Merge lp:~ajite/openobject-addons/elico-7.0-add-purchase_landed_costs_extended into lp:~openerp-community/openobject-addons/elico-7.0
- elico-7.0-add-purchase_landed_costs_extended
- Merge into elico-7.0
Proposed by
Augustin Cisterne-Kaas - www.elico-corp.com
Status: | Needs review |
---|---|
Proposed branch: | lp:~ajite/openobject-addons/elico-7.0-add-purchase_landed_costs_extended |
Merge into: | lp:~openerp-community/openobject-addons/elico-7.0 |
Diff against target: |
5110 lines (+4930/-0) 33 files modified
cron_watcher/__init__.py (+22/-0) cron_watcher/__openerp__.py (+41/-0) cron_watcher/cron.py (+65/-0) cron_watcher/cron_data.xml (+22/-0) purchase_landed_costs/__init__.py (+29/-0) purchase_landed_costs/__openerp__.py (+117/-0) purchase_landed_costs/i18n/fr.po (+418/-0) purchase_landed_costs/i18n/purchase_landed_costs.pot (+374/-0) purchase_landed_costs/product.py (+76/-0) purchase_landed_costs/purchase.py (+632/-0) purchase_landed_costs/purchase.py.save (+631/-0) purchase_landed_costs/purchase_landed_costs_data.yml (+20/-0) purchase_landed_costs/purchase_view.xml (+213/-0) purchase_landed_costs/security/ir.model.access.csv (+5/-0) purchase_landed_costs/security/landed_cost_security.xml (+13/-0) purchase_landed_costs/stock.py (+71/-0) purchase_landed_costs/test/landed_costs_based_on_quantity.yml (+156/-0) purchase_landed_costs/test/landed_costs_based_on_value.yml (+141/-0) purchase_landed_costs/test/landed_costs_multicurrency_company.yml (+220/-0) purchase_landed_costs/test/landed_costs_multicurrency_pricelist.yml (+219/-0) purchase_landed_costs/test/landed_costs_multicurrency_pricetype.yml (+219/-0) purchase_landed_costs/test/landed_costs_on_qty_by_line_and_order.yml (+144/-0) purchase_landed_costs_extended/__init__.py (+24/-0) purchase_landed_costs_extended/__openerp__.py (+39/-0) purchase_landed_costs_extended/purchase.py (+167/-0) purchase_landed_costs_extended/purchase_view.xml (+173/-0) purchase_landed_costs_extended/report/__init__.py (+22/-0) purchase_landed_costs_extended/report/purchase_report.py (+399/-0) purchase_landed_costs_extended/report/purchase_report_view.xml (+144/-0) purchase_landed_costs_extended/security/ir.model.access.csv (+3/-0) purchase_landed_costs_extended/wizard/__init__.py (+22/-0) purchase_landed_costs_extended/wizard/landed_cost_position_invoice.py (+57/-0) purchase_landed_costs_extended/wizard/landed_cost_position_invoice_view.xml (+32/-0) |
To merge this branch: | bzr merge lp:~ajite/openobject-addons/elico-7.0-add-purchase_landed_costs_extended |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Elico Corp (www.elico-corp.com) | Pending | ||
Review via email: mp+223668@code.launchpad.net |
Commit message
Description of the change
Added a new module for landed costs to handle different currencies.
(SQL report is still in progress since it does not handle the multi currency)
To post a comment you must log in.
Unmerged revisions
- 38. By Augustin Cisterne-Kaas - www.elico-corp.com
-
[ADD] Purchase landed costs extended
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'cron_watcher' |
2 | === added file 'cron_watcher/__init__.py' |
3 | --- cron_watcher/__init__.py 1970-01-01 00:00:00 +0000 |
4 | +++ cron_watcher/__init__.py 2014-06-19 02:29:14 +0000 |
5 | @@ -0,0 +1,22 @@ |
6 | +# -*- coding: utf-8 -*- |
7 | +############################################################################## |
8 | +# |
9 | +# OpenERP, Open Source Management Solution |
10 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
11 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
12 | +# |
13 | +# This program is free software: you can redistribute it and/or modify |
14 | +# it under the terms of the GNU Affero General Public License as |
15 | +# published by the Free Software Foundation, either version 3 of the |
16 | +# License, or (at your option) any later version. |
17 | +# |
18 | +# This program is distributed in the hope that it will be useful, |
19 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 | +# GNU Affero General Public License for more details. |
22 | +# |
23 | +# You should have received a copy of the GNU Affero General Public License |
24 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
25 | +# |
26 | +############################################################################## |
27 | +import cron |
28 | |
29 | === added file 'cron_watcher/__openerp__.py' |
30 | --- cron_watcher/__openerp__.py 1970-01-01 00:00:00 +0000 |
31 | +++ cron_watcher/__openerp__.py 2014-06-19 02:29:14 +0000 |
32 | @@ -0,0 +1,41 @@ |
33 | +# -*- coding: utf-8 -*- |
34 | +############################################################################## |
35 | +# |
36 | +# OpenERP, Open Source Management Solution |
37 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
38 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
39 | +# |
40 | +# This program is free software: you can redistribute it and/or modify |
41 | +# it under the terms of the GNU Affero General Public License as |
42 | +# published by the Free Software Foundation, either version 3 of the |
43 | +# License, or (at your option) any later version. |
44 | +# |
45 | +# This program is distributed in the hope that it will be useful, |
46 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
47 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
48 | +# GNU Affero General Public License for more details. |
49 | +# |
50 | +# You should have received a copy of the GNU Affero General Public License |
51 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
52 | +# |
53 | +############################################################################## |
54 | +{'name': 'Cron Watcher', |
55 | + 'version': '0.1', |
56 | + 'category': 'Tools', |
57 | + 'depends': ['base', 'mail'], |
58 | + 'author': 'Elico Corp', |
59 | + 'license': 'AGPL-3', |
60 | + 'website': 'https://www.elico-corp.com', |
61 | + 'description': """ |
62 | +Sends notification to a group named "Cron Watcher" when a cron job has not run |
63 | +for X minutes. |
64 | + |
65 | +X can be defined in "Settings", "Scheduler Actions", "Cron Watcher", |
66 | +"Technical Data", "Arguments" |
67 | + * 5 minutes = (5,) |
68 | + * 10 minutes = (10,) |
69 | + * etc... |
70 | +""", |
71 | + 'data': ['cron_data.xml'], |
72 | + 'installable': True, |
73 | + 'application': False} |
74 | |
75 | === added file 'cron_watcher/cron.py' |
76 | --- cron_watcher/cron.py 1970-01-01 00:00:00 +0000 |
77 | +++ cron_watcher/cron.py 2014-06-19 02:29:14 +0000 |
78 | @@ -0,0 +1,65 @@ |
79 | +# -*- coding: utf-8 -*- |
80 | +############################################################################## |
81 | +# |
82 | +# OpenERP, Open Source Management Solution |
83 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
84 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
85 | +# |
86 | +# This program is free software: you can redistribute it and/or modify |
87 | +# it under the terms of the GNU Affero General Public License as |
88 | +# published by the Free Software Foundation, either version 3 of the |
89 | +# License, or (at your option) any later version. |
90 | +# |
91 | +# This program is distributed in the hope that it will be useful, |
92 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
93 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
94 | +# GNU Affero General Public License for more details. |
95 | +# |
96 | +# You should have received a copy of the GNU Affero General Public License |
97 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
98 | +# |
99 | +############################################################################## |
100 | +from openerp.osv import orm |
101 | +import time |
102 | +from datetime import datetime |
103 | +from openerp.tools.translate import _ |
104 | + |
105 | + |
106 | +class ir_cron(orm.Model): |
107 | + _inherit = 'ir.cron' |
108 | + |
109 | + def _scheduler_cron_watcher(self, cr, uid, interval, |
110 | + domain=None, context=None): |
111 | + interval *= 60 |
112 | + m = self.pool.get('ir.model.data') |
113 | + cron_ids = self.search(cr, uid, [], context=context) |
114 | + date_format = "%Y-%m-%d %H:%M:%S" |
115 | + current_date = time.strftime(date_format) |
116 | + current_date = datetime.strptime(current_date, date_format) |
117 | + message_pool = self.pool.get('mail.message') |
118 | + |
119 | + group = m.get_object( |
120 | + cr, uid, 'cron_watcher', 'res_groups_cron_watcher') |
121 | + admin_user = m.get_object( |
122 | + cr, uid, 'base', 'user_root') |
123 | + partner_ids = [(4, user.partner_id.id) for user in group.users] |
124 | + for cron in self.browse(cr, uid, cron_ids, context=context): |
125 | + if not cron.nextcall: |
126 | + continue |
127 | + cron_date = datetime.strptime(cron.nextcall, date_format) |
128 | + delay = (current_date - cron_date).total_seconds() |
129 | + delay_minutes = round(int(delay) / 60) |
130 | + email = { |
131 | + 'type': 'notification', |
132 | + 'author_id': admin_user.partner_id.id, |
133 | + 'partner_ids': partner_ids, |
134 | + 'notified_partner_ids': partner_ids, |
135 | + 'model': 'ir.cron', |
136 | + 'res_id': cron.id, |
137 | + 'subject': _('Cron Watcher Alert (%s)') % cron.name, |
138 | + 'body': _( |
139 | + 'The cron job named "%s" has been delayed for\ |
140 | + more than %s minutes') % (cron.name, delay_minutes) |
141 | + } |
142 | + if delay > interval: |
143 | + message_pool.create(cr, uid, email, context=context) |
144 | |
145 | === added file 'cron_watcher/cron_data.xml' |
146 | --- cron_watcher/cron_data.xml 1970-01-01 00:00:00 +0000 |
147 | +++ cron_watcher/cron_data.xml 2014-06-19 02:29:14 +0000 |
148 | @@ -0,0 +1,22 @@ |
149 | +<?xml version="1.0" encoding="utf-8"?> |
150 | +<openerp> |
151 | + <data noupdate="1"> |
152 | + <record forcecreate="True" id="ir_cron_watcher" model="ir.cron"> |
153 | + <field name="name">Cron Watcher</field> |
154 | + <field eval="True" name="active"/> |
155 | + <field name="user_id" ref="base.user_root"/> |
156 | + <field name="interval_number">1</field> |
157 | + <field name="interval_type">hours</field> |
158 | + <field name="numbercall">-1</field> |
159 | + <field eval="False" name="doall"/> |
160 | + <field eval="'ir.cron'" name="model"/> |
161 | + <field eval="'_scheduler_cron_watcher'" name="function"/> |
162 | + <field eval="'(15,)'" name="args"/> |
163 | + </record> |
164 | + <record forcecreate="True" id="res_groups_cron_watcher" model="res.groups"> |
165 | + <field name="name">Cron Watcher</field> |
166 | + <field eval="True" name="active"/> |
167 | + <field name="users" eval="[(4, ref('base.user_root'))]" /> |
168 | + </record> |
169 | + </data> |
170 | +</openerp> |
171 | \ No newline at end of file |
172 | |
173 | === added directory 'purchase_landed_costs' |
174 | === added file 'purchase_landed_costs/__init__.py' |
175 | --- purchase_landed_costs/__init__.py 1970-01-01 00:00:00 +0000 |
176 | +++ purchase_landed_costs/__init__.py 2014-06-19 02:29:14 +0000 |
177 | @@ -0,0 +1,29 @@ |
178 | +# -*- coding: utf-8 -*- |
179 | +############################################################################## |
180 | +# |
181 | +# OpenERP, Open Source Management Solution |
182 | +# Copyright (C) 2010-2013 Camptocamp (<http://www.camptocamp.com>) |
183 | +# Authors: Ferdinand Gasauer, Joel Grand-Guillaume |
184 | +# |
185 | +# This program is free software: you can redistribute it and/or modify |
186 | +# it under the terms of the GNU Affero General Public License as |
187 | +# published by the Free Software Foundation, either version 3 of the |
188 | +# License, or (at your option) any later version. |
189 | +# |
190 | +# This program is distributed in the hope that it will be useful, |
191 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
192 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
193 | +# GNU Affero General Public License for more details. |
194 | +# |
195 | +# You should have received a copy of the GNU Affero General Public License |
196 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
197 | +# |
198 | +############################################################################## |
199 | + |
200 | + |
201 | +from . import product |
202 | +from . import purchase |
203 | +from . import stock |
204 | + |
205 | + |
206 | +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
207 | |
208 | === added file 'purchase_landed_costs/__openerp__.py' |
209 | --- purchase_landed_costs/__openerp__.py 1970-01-01 00:00:00 +0000 |
210 | +++ purchase_landed_costs/__openerp__.py 2014-06-19 02:29:14 +0000 |
211 | @@ -0,0 +1,117 @@ |
212 | +# -*- coding: utf-8 -*- |
213 | +############################################################################## |
214 | +# |
215 | +# OpenERP, Open Source Management Solution |
216 | +# Copyright (C) 2010-2013 Camptocamp (<http://www.camptocamp.com>) |
217 | +# Authors: Ferdinand Gasauer, Joel Grand-Guillaume |
218 | +# |
219 | +# This program is free software: you can redistribute it and/or modify |
220 | +# it under the terms of the GNU Affero General Public License as |
221 | +# published by the Free Software Foundation, either version 3 of the |
222 | +# License, or (at your option) any later version. |
223 | +# |
224 | +# This program is distributed in the hope that it will be useful, |
225 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
226 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
227 | +# GNU Affero General Public License for more details. |
228 | +# |
229 | +# You should have received a copy of the GNU Affero General Public License |
230 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
231 | +# |
232 | +############################################################################## |
233 | + |
234 | + |
235 | +{ |
236 | + 'name': 'Purchase Landed Costs', |
237 | + 'version': '1.0.1', |
238 | + 'category': 'Warehouse Management', |
239 | + 'description': """ |
240 | +Purchase Landed Costs |
241 | +===================== |
242 | + |
243 | +This module adds the possibility to include estimated landed costs to the average price computation. To define those landed costs, |
244 | +create products for every landed costs and affect them a distribution type. Don't forget to as well assign them a specific |
245 | +financial account (the one which will record the real cost) in order to compare at the end of the year the estimation with real |
246 | +accounting entries (see stock valuation). The landed costs is defined in purchase orders. These costs will be distributed |
247 | +according to the distribution type defined in landed cost: |
248 | + |
249 | + * value - example custom fees |
250 | + * quantity - example freight |
251 | + |
252 | +Note : Products used to define landed cost must have a default "Distribution Type" set (Value/Quantity). |
253 | + |
254 | +For each landed cost position (=line) define in a PO, a draft invoice can be pre-created at PO validation (an option need to |
255 | +be checked). Doing so will allow you to see those invoices using the view invoice button directly from the PO. You can define |
256 | +landed cost relative to a whole PO or by PO line (or both) and the system will distribute them to each line according to the |
257 | +chosen distribution type. |
258 | + |
259 | +Note that the landed cost is always expressed in company currency. |
260 | + |
261 | +Find all landed cost here : Reporting -> Purchase -> Landed costs |
262 | + |
263 | +Stock valuation: |
264 | +---------------- |
265 | +As the average price is also used for the stock valuation and because the computation is based on estimation of landed cost |
266 | +in the PO (done at incoming shipment reception), you will have a difference with the actual accounting bookings of landed cost. |
267 | +Stock valuation will have to be adjusted with a manual journal entry. In order to correct that amount, make a sum of |
268 | +estimated landed cost (landed cost position) by account and compare with the real account chart value. You can access those |
269 | +informations through this menu: Reporting -> Purchase -> Landed costs |
270 | + |
271 | +Warning: |
272 | +-------- |
273 | + |
274 | + * Average price will be computed based on the estimation made on the PO - not from real cost. This is due to the way OpenERP |
275 | + compute average stock : it stores the updated value at every input, no history, so no way to redefine the value afterwards. |
276 | + i.e. |
277 | + - incomming 01: 100 product A at 50.- AVG = 50.-, stock = 100 |
278 | + - incomming 02: 100 product A at 60.- AVG = 55.-, stock = 200 |
279 | + - delivery 01: 50 product A AVG = 55.-, stock = 150 |
280 | + - Receive the real landed cost of 10.- for incomming 01 |
281 | + => cannot compute back because no historical price was store for every transaction. Moreover, in OpenERP I can even |
282 | + set another average price for a product using the update wizard. |
283 | + |
284 | + |
285 | + * As the price type of OpenERP is not really well handled, we need to be sure that price type of cost price in product form |
286 | + is the same as the company one. Otherwise, when computing the AVG price, it make the convertion in company currency |
287 | + from the price type currency. This is not related to this module, but from the core of OpenERP. |
288 | + If you use this module in multi-company and different currency between company, you'll have to not share the product |
289 | + between them, even if product are the same (bug: https://bugs.launchpad.net/ocb-addons/+bug/1238525). |
290 | + |
291 | + |
292 | +TODO/Ideas: |
293 | +----------- |
294 | + * Manage multi-currencies landed costs in PO |
295 | + * Have the shipped date in landed cost instead of PO date for a better analysis |
296 | + * Compute a average purchase price per products while keep cost price as it is now |
297 | + |
298 | + |
299 | +""", |
300 | + 'author': 'Camptocamp', |
301 | + 'depends': ['purchase' ], |
302 | + 'website': 'http://www.camptocamp.com', |
303 | + 'data': ['security/ir.model.access.csv', |
304 | + 'security/landed_cost_security.xml', |
305 | + 'purchase_view.xml', |
306 | + 'purchase_landed_costs_data.yml', |
307 | + ], |
308 | + 'test': [ |
309 | + 'test/landed_costs_based_on_quantity.yml', |
310 | + 'test/landed_costs_based_on_value.yml', |
311 | + 'test/landed_costs_on_qty_by_line_and_order.yml', |
312 | + 'test/landed_costs_multicurrency_pricelist.yml', |
313 | + |
314 | + # those 2 tests here fails because of the bug regarding the price_type |
315 | + # (https://bugs.launchpad.net/ocb-addons/+bug/1238525) and average price |
316 | + # computation in OpenERP. I'll keep them because |
317 | + # The bug is happening when the company has a different currency that |
318 | + # the price_type of the standard_price field |
319 | + # Unless you didn't have to do that, everything work fine. |
320 | + # they should be sovled by a way or another. |
321 | + 'test/landed_costs_multicurrency_company.yml', |
322 | + 'test/landed_costs_multicurrency_pricetype.yml', |
323 | + ], |
324 | + 'demo': [], |
325 | + 'installable': True, |
326 | + 'active': False, |
327 | +} |
328 | +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
329 | |
330 | === added directory 'purchase_landed_costs/i18n' |
331 | === added file 'purchase_landed_costs/i18n/fr.po' |
332 | --- purchase_landed_costs/i18n/fr.po 1970-01-01 00:00:00 +0000 |
333 | +++ purchase_landed_costs/i18n/fr.po 2014-06-19 02:29:14 +0000 |
334 | @@ -0,0 +1,418 @@ |
335 | +# Translation of OpenERP Server. |
336 | +# This file contains the translation of the following modules: |
337 | +# * purchase_landed_costs |
338 | +# |
339 | +msgid "" |
340 | +msgstr "" |
341 | +"Project-Id-Version: OpenERP Server 7.0\n" |
342 | +"Report-Msgid-Bugs-To: \n" |
343 | +"POT-Creation-Date: 2013-10-17 09:49+0000\n" |
344 | +"PO-Revision-Date: 2014-02-06 12:55+0000\n" |
345 | +"Last-Translator: Nicolas Bessi - Camptocamp <Unknown>\n" |
346 | +"Language-Team: \n" |
347 | +"MIME-Version: 1.0\n" |
348 | +"Content-Type: text/plain; charset=UTF-8\n" |
349 | +"Content-Transfer-Encoding: 8bit\n" |
350 | +"X-Launchpad-Export-Date: 2014-02-26 07:32+0000\n" |
351 | +"X-Generator: Launchpad (build 16935)\n" |
352 | + |
353 | +#. module: purchase_landed_costs |
354 | +#: model:ir.model,name:purchase_landed_costs.model_purchase_order_line |
355 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_purchase_order_line_id |
356 | +#: field:landed.cost.position,purchase_order_line_id:0 |
357 | +msgid "Purchase Order Line" |
358 | +msgstr "Ligne de commande d'achat" |
359 | + |
360 | +#. module: purchase_landed_costs |
361 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount_company_currency |
362 | +#: field:landed.cost.position,amount_company_currency:0 |
363 | +msgid "Amount Company Currency" |
364 | +msgstr "Montant (Devise société)" |
365 | + |
366 | +#. module: purchase_landed_costs |
367 | +#: selection:product.template,landed_cost_type:0 |
368 | +msgid "None" |
369 | +msgstr "Aucun" |
370 | + |
371 | +#. module: purchase_landed_costs |
372 | +#: view:landed.cost.position:0 |
373 | +msgid "Group By..." |
374 | +msgstr "Grouper Par..." |
375 | + |
376 | +#. module: purchase_landed_costs |
377 | +#: help:product.template,landed_cost_type:0 |
378 | +msgid "" |
379 | +"Used if this product is landed costs: If landed costs are defined for " |
380 | +"purchase orders or pickings, this indicates how the costs are distributed to " |
381 | +"the lines" |
382 | +msgstr "" |
383 | +"Utilisé si ce produit est un coût d'acquisition: Si des coûts d'acquisition " |
384 | +"sont définis sur les commandes d'achat ou les bons de livraison, ceci " |
385 | +"indique la répartion de ces coûts sur les lignes" |
386 | + |
387 | +#. module: purchase_landed_costs |
388 | +#: view:landed.cost.position:0 |
389 | +msgid "Supplier" |
390 | +msgstr "Fournisseurs" |
391 | + |
392 | +#. module: purchase_landed_costs |
393 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_generate_invoice |
394 | +#: field:landed.cost.position,generate_invoice:0 |
395 | +msgid "Generate Invoice" |
396 | +msgstr "Auto-Générer facture" |
397 | + |
398 | +#. module: purchase_landed_costs |
399 | +#: model:ir.model,name:purchase_landed_costs.model_landed_cost_position |
400 | +msgid "landed.cost.position" |
401 | +msgstr "landed.cost.position" |
402 | + |
403 | +#. module: purchase_landed_costs |
404 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_account_id |
405 | +#: field:landed.cost.position,account_id:0 |
406 | +msgid "Fiscal Account" |
407 | +msgstr "Compte" |
408 | + |
409 | +#. module: purchase_landed_costs |
410 | +#: help:landed.cost.position,partner_id:0 |
411 | +msgid "The supplier of this cost component." |
412 | +msgstr "Fournisseur lié à ce coût d'acquisition" |
413 | + |
414 | +#. module: purchase_landed_costs |
415 | +#: help:landed.cost.position,amount_total:0 |
416 | +msgid "" |
417 | +"This field represent the total amount of this position regarding a whole " |
418 | +"order. By summing it, you'll have the total landed cost for the order (in " |
419 | +"his currency)" |
420 | +msgstr "" |
421 | +"Ce champs représente de montant total de ce poste par rapport à l'ensemble " |
422 | +"du bon de commande. En l'additionnant, vous obtiendrez le total des coûts " |
423 | +"d'acquisition pour ce bon de commande." |
424 | + |
425 | +#. module: purchase_landed_costs |
426 | +#: model:ir.model,name:purchase_landed_costs.model_product_product |
427 | +msgid "Product" |
428 | +msgstr "Article" |
429 | + |
430 | +#. module: purchase_landed_costs |
431 | +#: code:addons/purchase_landed_costs/product.py:66 |
432 | +#, python-format |
433 | +msgid "Define expense account for this company: \"%s\" (id:%d)." |
434 | +msgstr "Détermine le compte de charge pour cette société: \"%s\" (id:%d)." |
435 | + |
436 | +#. module: purchase_landed_costs |
437 | +#: code:addons/purchase_landed_costs/purchase.py:493 |
438 | +#, python-format |
439 | +msgid "Define purchase journal for this company: \"%s\" (id:%d)." |
440 | +msgstr "Détermine le journal d'achat pour cette société: \"%s\" (id:%d)." |
441 | + |
442 | +#. module: purchase_landed_costs |
443 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost_line_ids |
444 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landed_costs |
445 | +#: view:landed.cost.position:0 |
446 | +#: field:purchase.order,landed_cost_line_ids:0 |
447 | +#: field:purchase.order.line,landed_costs:0 |
448 | +msgid "Landed Costs" |
449 | +msgstr "Coûts d'acquisition" |
450 | + |
451 | +#. module: purchase_landed_costs |
452 | +#: help:landed.cost.position,amount_company_currency:0 |
453 | +msgid "" |
454 | +"Landed cost for stock valuation (expressed in company currency). It will be " |
455 | +"added to the price of the supplier price." |
456 | +msgstr "" |
457 | +"Coût d'acquisition pour la valorisation des stocks (exprimés en devise de la " |
458 | +"société). S'ajoutera au coût de revient." |
459 | + |
460 | +#. module: purchase_landed_costs |
461 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_company_id |
462 | +#: field:landed.cost.position,company_id:0 |
463 | +msgid "Company" |
464 | +msgstr "Société" |
465 | + |
466 | +#. module: purchase_landed_costs |
467 | +#: help:landed.cost.position,amount:0 |
468 | +msgid "Landed cost expressed in PO currency used to fullfil landed cost." |
469 | +msgstr "" |
470 | +"Coûts d'acquisition exprimés dans la même devise que la commande fournisseur." |
471 | + |
472 | +#. module: purchase_landed_costs |
473 | +#: selection:landed.cost.distribution.type,apply_on:0 |
474 | +msgid "Order" |
475 | +msgstr "Commande" |
476 | + |
477 | +#. module: purchase_landed_costs |
478 | +#: model:ir.model,name:purchase_landed_costs.model_landed_cost_distribution_type |
479 | +msgid "landed.cost.distribution.type" |
480 | +msgstr "landed.cost.distribution.type" |
481 | + |
482 | +#. module: purchase_landed_costs |
483 | +#: model:ir.actions.act_window,name:purchase_landed_costs.act_po_2_landed_costs |
484 | +msgid "Related Landed Costs" |
485 | +msgstr "Coûts d'acquisition liés" |
486 | + |
487 | +#. module: purchase_landed_costs |
488 | +#: help:landed.cost.distribution.type,apply_on:0 |
489 | +msgid "Defines if this distribution type Applied on order or line level." |
490 | +msgstr "Defines if this distribution type Applied on order or line level." |
491 | + |
492 | +#. module: purchase_landed_costs |
493 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_date_po |
494 | +#: view:landed.cost.position:0 |
495 | +#: field:landed.cost.position,date_po:0 |
496 | +msgid "Date" |
497 | +msgstr "Date" |
498 | + |
499 | +#. module: purchase_landed_costs |
500 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost_base_value |
501 | +#: field:purchase.order,landed_cost_base_value:0 |
502 | +msgid "Landed Costs Base Value" |
503 | +msgstr "Coûts d'acquisition valeur de base" |
504 | + |
505 | +#. module: purchase_landed_costs |
506 | +#: selection:landed.cost.distribution.type,apply_on:0 |
507 | +msgid "Line" |
508 | +msgstr "Ligne" |
509 | + |
510 | +#. module: purchase_landed_costs |
511 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_stock_move_price_unit_net |
512 | +#: field:stock.move,price_unit_net:0 |
513 | +msgid "Purchase Price" |
514 | +msgstr "Prix d'achat" |
515 | + |
516 | +#. module: purchase_landed_costs |
517 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landing_cost_lines |
518 | +#: field:purchase.order,landing_cost_lines:0 |
519 | +msgid "Landing Cost Lines" |
520 | +msgstr "Lignes de coût d'acquisition" |
521 | + |
522 | +#. module: purchase_landed_costs |
523 | +#: view:landed.cost.position:0 |
524 | +msgid "Purchase" |
525 | +msgstr "Achat" |
526 | + |
527 | +#. module: purchase_landed_costs |
528 | +#: view:landed.cost.position:0 |
529 | +msgid "Account" |
530 | +msgstr "Compte" |
531 | + |
532 | +#. module: purchase_landed_costs |
533 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_distribution_type_name |
534 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_distribution_type_id |
535 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_product_template_landed_cost_type |
536 | +#: field:landed.cost.distribution.type,name:0 |
537 | +#: view:landed.cost.position:0 |
538 | +#: field:landed.cost.position,distribution_type_id:0 |
539 | +#: field:product.template,landed_cost_type:0 |
540 | +msgid "Distribution Type" |
541 | +msgstr "Type de Distribution" |
542 | + |
543 | +#. module: purchase_landed_costs |
544 | +#: view:landed.cost.position:0 |
545 | +msgid "Total amount" |
546 | +msgstr "Montant Total" |
547 | + |
548 | +#. module: purchase_landed_costs |
549 | +#: model:ir.actions.act_window,help:purchase_landed_costs.action_landed_cost_report_all |
550 | +msgid "" |
551 | +"Landed cost Analysis allows you to easily check and analyse your estimated " |
552 | +"landed costs." |
553 | +msgstr "" |
554 | +"L'analyse des coûts d'acquisition permet de contrôler l'ensemble des " |
555 | +"estimations de coût d'acquisition des commandes d'achat." |
556 | + |
557 | +#. module: purchase_landed_costs |
558 | +#: selection:landed.cost.distribution.type,landed_cost_type:0 |
559 | +#: selection:product.template,landed_cost_type:0 |
560 | +msgid "Value" |
561 | +msgstr "Valeur" |
562 | + |
563 | +#. module: purchase_landed_costs |
564 | +#: view:landed.cost.position:0 |
565 | +msgid "Purchase Line" |
566 | +msgstr "Ligne d'achat" |
567 | + |
568 | +#. module: purchase_landed_costs |
569 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_distribution_type_landed_cost_type |
570 | +#: field:landed.cost.distribution.type,landed_cost_type:0 |
571 | +msgid "Product Landed Cost Type" |
572 | +msgstr "Type de Côut d'acquisition" |
573 | + |
574 | +#. module: purchase_landed_costs |
575 | +#: model:ir.model,name:purchase_landed_costs.model_purchase_order |
576 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_purchase_order_id |
577 | +#: field:landed.cost.position,purchase_order_id:0 |
578 | +msgid "Purchase Order" |
579 | +msgstr "Bon de commande" |
580 | + |
581 | +#. module: purchase_landed_costs |
582 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landed_cost_line_ids |
583 | +#: field:purchase.order.line,landed_cost_line_ids:0 |
584 | +msgid "Landed Costs Positions" |
585 | +msgstr "Poste de coûts d'acquisition" |
586 | + |
587 | +#. module: purchase_landed_costs |
588 | +#: code:addons/purchase_landed_costs/product.py:65 |
589 | +#: code:addons/purchase_landed_costs/purchase.py:492 |
590 | +#, python-format |
591 | +msgid "Error!" |
592 | +msgstr "Erreur!" |
593 | + |
594 | +#. module: purchase_landed_costs |
595 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount |
596 | +#: field:landed.cost.position,amount:0 |
597 | +msgid "Amount" |
598 | +msgstr "Montant" |
599 | + |
600 | +#. module: purchase_landed_costs |
601 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_quantity_total |
602 | +#: field:purchase.order,quantity_total:0 |
603 | +msgid "Total Quantity" |
604 | +msgstr "Quantité totale" |
605 | + |
606 | +#. module: purchase_landed_costs |
607 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount_total_comp_currency |
608 | +#: field:landed.cost.position,amount_total_comp_currency:0 |
609 | +msgid "Amount Total Company Currency" |
610 | +msgstr "Montant Total (Devise société)" |
611 | + |
612 | +#. module: purchase_landed_costs |
613 | +#: model:ir.model,name:purchase_landed_costs.model_stock_partial_picking |
614 | +msgid "Partial Picking Processing Wizard" |
615 | +msgstr "Assistant de livraison partielle" |
616 | + |
617 | +#. module: purchase_landed_costs |
618 | +#: help:landed.cost.position,date_po:0 |
619 | +msgid "Date of the related PO" |
620 | +msgstr "Date du bon de commande lié" |
621 | + |
622 | +#. module: purchase_landed_costs |
623 | +#: help:landed.cost.position,generate_invoice:0 |
624 | +msgid "" |
625 | +"If ticked, this will generate a draft invoice at the PO confirmation for " |
626 | +"this landed cost position from the related partner. If not, no invoice will " |
627 | +"be generated, but the cost will be included for the average price " |
628 | +"computation." |
629 | +msgstr "" |
630 | +"Si coché, une facture brouillon (donc modifiable) sera pré-générée lors de " |
631 | +"la confirmation du bon de commande pour cette position de coût d'acquisition " |
632 | +"graĉe au fournisseur lié. Si non cochée, aucune facture ne sera pré-générée " |
633 | +"mais le coût de revient sera calculé en tenant compte des coûts d'acquisiton " |
634 | +"tout de même." |
635 | + |
636 | +#. module: purchase_landed_costs |
637 | +#: help:landed.cost.position,distribution_type_id:0 |
638 | +msgid "" |
639 | +"Defines if the amount is to be calculated for each quantity or an absolute " |
640 | +"value" |
641 | +msgstr "" |
642 | +"Détermine si le montant du coût d'acquisition se calcule par quantité ou si " |
643 | +"il s'agit d'une simple valeur à ajouter" |
644 | + |
645 | +#. module: purchase_landed_costs |
646 | +#: model:ir.actions.act_window,name:purchase_landed_costs.action_landed_cost_report_all |
647 | +#: model:ir.ui.menu,name:purchase_landed_costs.menu_action_landed_cost_report_all |
648 | +msgid "Landed Costs Analysis" |
649 | +msgstr "Analyse des coûts d'acquisiton" |
650 | + |
651 | +#. module: purchase_landed_costs |
652 | +#: selection:landed.cost.distribution.type,landed_cost_type:0 |
653 | +#: selection:product.template,landed_cost_type:0 |
654 | +msgid "Quantity" |
655 | +msgstr "Quantité" |
656 | + |
657 | +#. module: purchase_landed_costs |
658 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_product_id |
659 | +#: view:landed.cost.position:0 |
660 | +#: field:landed.cost.position,product_id:0 |
661 | +msgid "Landed Cost Name" |
662 | +msgstr "Nom du coût d'acquisiton" |
663 | + |
664 | +#. module: purchase_landed_costs |
665 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost_base_quantity |
666 | +#: field:purchase.order,landed_cost_base_quantity:0 |
667 | +msgid "Landed Costs Base Quantity" |
668 | +msgstr "Quantité de base du coûts d'acquisiton" |
669 | + |
670 | +#. module: purchase_landed_costs |
671 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landing_costs_order |
672 | +#: field:purchase.order.line,landing_costs_order:0 |
673 | +msgid "Landing Costs from Order" |
674 | +msgstr "Coûts d'acquisiton de la commande" |
675 | + |
676 | +#. module: purchase_landed_costs |
677 | +#: model:ir.model,name:purchase_landed_costs.model_stock_move |
678 | +msgid "Stock Move" |
679 | +msgstr "Mouvement de stock" |
680 | + |
681 | +#. module: purchase_landed_costs |
682 | +#: model:ir.model,name:purchase_landed_costs.model_product_template |
683 | +msgid "Product Template" |
684 | +msgstr "Modèle d'article" |
685 | + |
686 | +#. module: purchase_landed_costs |
687 | +#: help:landed.cost.distribution.type,landed_cost_type:0 |
688 | +msgid "Refer to the product landed cost type." |
689 | +msgstr "En référence au type de coût d'acquisition." |
690 | + |
691 | +#. module: purchase_landed_costs |
692 | +#: help:stock.move,price_unit_net:0 |
693 | +msgid "" |
694 | +"This is the net purchase price, without landed cost as the price include " |
695 | +"landed price has been stored in price_unit field" |
696 | +msgstr "" |
697 | +"Il s'agit du prix d'achat net (sans coûts d'acquisiton). Le coût de revient " |
698 | +"(avec coûts d'acquisiton) est stocké dans le champs Prix Unitaire" |
699 | + |
700 | +#. module: purchase_landed_costs |
701 | +#: help:landed.cost.position,amount_total_comp_currency:0 |
702 | +msgid "" |
703 | +"This field represent the total amount of this position regarding a whole " |
704 | +"order. By summing it, you'll have the total landed cost for the order (in " |
705 | +"company reference currency)." |
706 | +msgstr "" |
707 | +"Représente le montant total de ce poste au niveau de la commande (donc " |
708 | +"ignore ce poste au niveau des lignes de commande). Il est exprimé en devise " |
709 | +"de la société." |
710 | + |
711 | +#. module: purchase_landed_costs |
712 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_distribution_type_apply_on |
713 | +#: field:landed.cost.distribution.type,apply_on:0 |
714 | +msgid "Applied on" |
715 | +msgstr "S'applique sur" |
716 | + |
717 | +#. module: purchase_landed_costs |
718 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_partner_id |
719 | +#: field:landed.cost.position,partner_id:0 |
720 | +msgid "Partner" |
721 | +msgstr "Partenaire" |
722 | + |
723 | +#. module: purchase_landed_costs |
724 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost |
725 | +#: field:purchase.order,landed_cost:0 |
726 | +msgid "Landed Costs Total Untaxed" |
727 | +msgstr "Coûts d'acquisiton H.T total" |
728 | + |
729 | +#. module: purchase_landed_costs |
730 | +#: view:purchase.order:0 |
731 | +msgid "Open All Landed costs" |
732 | +msgstr "Ouvrir tout les coûts d'acquisition" |
733 | + |
734 | +#. module: purchase_landed_costs |
735 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landing_costs |
736 | +#: view:landed.cost.position:0 |
737 | +#: view:purchase.order:0 |
738 | +#: view:purchase.order.line:0 |
739 | +#: field:purchase.order.line,landing_costs:0 |
740 | +msgid "Landing Costs" |
741 | +msgstr "Coûts d'acquisiton" |
742 | + |
743 | +#. module: purchase_landed_costs |
744 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount_total |
745 | +#: field:landed.cost.position,amount_total:0 |
746 | +msgid "Amount Total" |
747 | +msgstr "Montant total" |
748 | + |
749 | +#. module: purchase_landed_costs |
750 | +#: view:landed.cost.position:0 |
751 | +msgid "Purchase Orders" |
752 | +msgstr "Bons de commande" |
753 | |
754 | === added file 'purchase_landed_costs/i18n/purchase_landed_costs.pot' |
755 | --- purchase_landed_costs/i18n/purchase_landed_costs.pot 1970-01-01 00:00:00 +0000 |
756 | +++ purchase_landed_costs/i18n/purchase_landed_costs.pot 2014-06-19 02:29:14 +0000 |
757 | @@ -0,0 +1,374 @@ |
758 | +# Translation of OpenERP Server. |
759 | +# This file contains the translation of the following modules: |
760 | +# * purchase_landed_costs |
761 | +# |
762 | +msgid "" |
763 | +msgstr "" |
764 | +"Project-Id-Version: OpenERP Server 7.0\n" |
765 | +"Report-Msgid-Bugs-To: \n" |
766 | +"POT-Creation-Date: 2013-10-17 09:49+0000\n" |
767 | +"PO-Revision-Date: 2013-10-17 09:49+0000\n" |
768 | +"Last-Translator: <>\n" |
769 | +"Language-Team: \n" |
770 | +"MIME-Version: 1.0\n" |
771 | +"Content-Type: text/plain; charset=UTF-8\n" |
772 | +"Content-Transfer-Encoding: \n" |
773 | +"Plural-Forms: \n" |
774 | + |
775 | +#. module: purchase_landed_costs |
776 | +#: model:ir.model,name:purchase_landed_costs.model_purchase_order_line |
777 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_purchase_order_line_id |
778 | +#: field:landed.cost.position,purchase_order_line_id:0 |
779 | +msgid "Purchase Order Line" |
780 | +msgstr "" |
781 | + |
782 | +#. module: purchase_landed_costs |
783 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount_company_currency |
784 | +#: field:landed.cost.position,amount_company_currency:0 |
785 | +msgid "Amount Company Currency" |
786 | +msgstr "" |
787 | + |
788 | +#. module: purchase_landed_costs |
789 | +#: selection:product.template,landed_cost_type:0 |
790 | +msgid "None" |
791 | +msgstr "" |
792 | + |
793 | +#. module: purchase_landed_costs |
794 | +#: view:landed.cost.position:0 |
795 | +msgid "Group By..." |
796 | +msgstr "" |
797 | + |
798 | +#. module: purchase_landed_costs |
799 | +#: help:product.template,landed_cost_type:0 |
800 | +msgid "Used if this product is landed costs: If landed costs are defined for purchase orders or pickings, this indicates how the costs are distributed to the lines" |
801 | +msgstr "" |
802 | + |
803 | +#. module: purchase_landed_costs |
804 | +#: view:landed.cost.position:0 |
805 | +msgid "Supplier" |
806 | +msgstr "" |
807 | + |
808 | +#. module: purchase_landed_costs |
809 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_generate_invoice |
810 | +#: field:landed.cost.position,generate_invoice:0 |
811 | +msgid "Generate Invoice" |
812 | +msgstr "" |
813 | + |
814 | +#. module: purchase_landed_costs |
815 | +#: model:ir.model,name:purchase_landed_costs.model_landed_cost_position |
816 | +msgid "landed.cost.position" |
817 | +msgstr "" |
818 | + |
819 | +#. module: purchase_landed_costs |
820 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_account_id |
821 | +#: field:landed.cost.position,account_id:0 |
822 | +msgid "Fiscal Account" |
823 | +msgstr "" |
824 | + |
825 | +#. module: purchase_landed_costs |
826 | +#: help:landed.cost.position,partner_id:0 |
827 | +msgid "The supplier of this cost component." |
828 | +msgstr "" |
829 | + |
830 | +#. module: purchase_landed_costs |
831 | +#: help:landed.cost.position,amount_total:0 |
832 | +msgid "This field represent the total amount of this position regarding a whole order. By summing it, you'll have the total landed cost for the order (in his currency)" |
833 | +msgstr "" |
834 | + |
835 | +#. module: purchase_landed_costs |
836 | +#: model:ir.model,name:purchase_landed_costs.model_product_product |
837 | +msgid "Product" |
838 | +msgstr "" |
839 | + |
840 | +#. module: purchase_landed_costs |
841 | +#: code:addons/purchase_landed_costs/product.py:66 |
842 | +#, python-format |
843 | +msgid "Define expense account for this company: \"%s\" (id:%d)." |
844 | +msgstr "" |
845 | + |
846 | +#. module: purchase_landed_costs |
847 | +#: code:addons/purchase_landed_costs/purchase.py:493 |
848 | +#, python-format |
849 | +msgid "Define purchase journal for this company: \"%s\" (id:%d)." |
850 | +msgstr "" |
851 | + |
852 | +#. module: purchase_landed_costs |
853 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost_line_ids |
854 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landed_costs |
855 | +#: view:landed.cost.position:0 |
856 | +#: field:purchase.order,landed_cost_line_ids:0 |
857 | +#: field:purchase.order.line,landed_costs:0 |
858 | +msgid "Landed Costs" |
859 | +msgstr "" |
860 | + |
861 | +#. module: purchase_landed_costs |
862 | +#: help:landed.cost.position,amount_company_currency:0 |
863 | +msgid "Landed cost for stock valuation (expressed in company currency). It will be added to the price of the supplier price." |
864 | +msgstr "" |
865 | + |
866 | +#. module: purchase_landed_costs |
867 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_company_id |
868 | +#: field:landed.cost.position,company_id:0 |
869 | +msgid "Company" |
870 | +msgstr "" |
871 | + |
872 | +#. module: purchase_landed_costs |
873 | +#: help:landed.cost.position,amount:0 |
874 | +msgid "Landed cost expressed in PO currency used to fullfil landed cost." |
875 | +msgstr "" |
876 | + |
877 | +#. module: purchase_landed_costs |
878 | +#: selection:landed.cost.distribution.type,apply_on:0 |
879 | +msgid "Order" |
880 | +msgstr "" |
881 | + |
882 | +#. module: purchase_landed_costs |
883 | +#: model:ir.model,name:purchase_landed_costs.model_landed_cost_distribution_type |
884 | +msgid "landed.cost.distribution.type" |
885 | +msgstr "" |
886 | + |
887 | +#. module: purchase_landed_costs |
888 | +#: model:ir.actions.act_window,name:purchase_landed_costs.act_po_2_landed_costs |
889 | +msgid "Related Landed Costs" |
890 | +msgstr "" |
891 | + |
892 | +#. module: purchase_landed_costs |
893 | +#: help:landed.cost.distribution.type,apply_on:0 |
894 | +msgid "Defines if this distribution type Applied on order or line level." |
895 | +msgstr "" |
896 | + |
897 | +#. module: purchase_landed_costs |
898 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_date_po |
899 | +#: view:landed.cost.position:0 |
900 | +#: field:landed.cost.position,date_po:0 |
901 | +msgid "Date" |
902 | +msgstr "" |
903 | + |
904 | +#. module: purchase_landed_costs |
905 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost_base_value |
906 | +#: field:purchase.order,landed_cost_base_value:0 |
907 | +msgid "Landed Costs Base Value" |
908 | +msgstr "" |
909 | + |
910 | +#. module: purchase_landed_costs |
911 | +#: selection:landed.cost.distribution.type,apply_on:0 |
912 | +msgid "Line" |
913 | +msgstr "" |
914 | + |
915 | +#. module: purchase_landed_costs |
916 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_stock_move_price_unit_net |
917 | +#: field:stock.move,price_unit_net:0 |
918 | +msgid "Purchase Price" |
919 | +msgstr "" |
920 | + |
921 | +#. module: purchase_landed_costs |
922 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landing_cost_lines |
923 | +#: field:purchase.order,landing_cost_lines:0 |
924 | +msgid "Landing Cost Lines" |
925 | +msgstr "" |
926 | + |
927 | +#. module: purchase_landed_costs |
928 | +#: view:landed.cost.position:0 |
929 | +msgid "Purchase" |
930 | +msgstr "" |
931 | + |
932 | +#. module: purchase_landed_costs |
933 | +#: view:landed.cost.position:0 |
934 | +msgid "Account" |
935 | +msgstr "" |
936 | + |
937 | +#. module: purchase_landed_costs |
938 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_distribution_type_name |
939 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_distribution_type_id |
940 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_product_template_landed_cost_type |
941 | +#: field:landed.cost.distribution.type,name:0 |
942 | +#: view:landed.cost.position:0 |
943 | +#: field:landed.cost.position,distribution_type_id:0 |
944 | +#: field:product.template,landed_cost_type:0 |
945 | +msgid "Distribution Type" |
946 | +msgstr "" |
947 | + |
948 | +#. module: purchase_landed_costs |
949 | +#: view:landed.cost.position:0 |
950 | +msgid "Total amount" |
951 | +msgstr "" |
952 | + |
953 | +#. module: purchase_landed_costs |
954 | +#: model:ir.actions.act_window,help:purchase_landed_costs.action_landed_cost_report_all |
955 | +msgid "Landed cost Analysis allows you to easily check and analyse your estimated landed costs." |
956 | +msgstr "" |
957 | + |
958 | +#. module: purchase_landed_costs |
959 | +#: selection:landed.cost.distribution.type,landed_cost_type:0 |
960 | +#: selection:product.template,landed_cost_type:0 |
961 | +msgid "Value" |
962 | +msgstr "" |
963 | + |
964 | +#. module: purchase_landed_costs |
965 | +#: view:landed.cost.position:0 |
966 | +msgid "Purchase Line" |
967 | +msgstr "" |
968 | + |
969 | +#. module: purchase_landed_costs |
970 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_distribution_type_landed_cost_type |
971 | +#: field:landed.cost.distribution.type,landed_cost_type:0 |
972 | +msgid "Product Landed Cost Type" |
973 | +msgstr "" |
974 | + |
975 | +#. module: purchase_landed_costs |
976 | +#: model:ir.model,name:purchase_landed_costs.model_purchase_order |
977 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_purchase_order_id |
978 | +#: field:landed.cost.position,purchase_order_id:0 |
979 | +msgid "Purchase Order" |
980 | +msgstr "" |
981 | + |
982 | +#. module: purchase_landed_costs |
983 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landed_cost_line_ids |
984 | +#: field:purchase.order.line,landed_cost_line_ids:0 |
985 | +msgid "Landed Costs Positions" |
986 | +msgstr "" |
987 | + |
988 | +#. module: purchase_landed_costs |
989 | +#: code:addons/purchase_landed_costs/product.py:65 |
990 | +#: code:addons/purchase_landed_costs/purchase.py:492 |
991 | +#, python-format |
992 | +msgid "Error!" |
993 | +msgstr "" |
994 | + |
995 | +#. module: purchase_landed_costs |
996 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount |
997 | +#: field:landed.cost.position,amount:0 |
998 | +msgid "Amount" |
999 | +msgstr "" |
1000 | + |
1001 | +#. module: purchase_landed_costs |
1002 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_quantity_total |
1003 | +#: field:purchase.order,quantity_total:0 |
1004 | +msgid "Total Quantity" |
1005 | +msgstr "" |
1006 | + |
1007 | +#. module: purchase_landed_costs |
1008 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount_total_comp_currency |
1009 | +#: field:landed.cost.position,amount_total_comp_currency:0 |
1010 | +msgid "Amount Total Company Currency" |
1011 | +msgstr "" |
1012 | + |
1013 | +#. module: purchase_landed_costs |
1014 | +#: model:ir.model,name:purchase_landed_costs.model_stock_partial_picking |
1015 | +msgid "Partial Picking Processing Wizard" |
1016 | +msgstr "" |
1017 | + |
1018 | +#. module: purchase_landed_costs |
1019 | +#: help:landed.cost.position,date_po:0 |
1020 | +msgid "Date of the related PO" |
1021 | +msgstr "" |
1022 | + |
1023 | +#. module: purchase_landed_costs |
1024 | +#: help:landed.cost.position,generate_invoice:0 |
1025 | +msgid "If ticked, this will generate a draft invoice at the PO confirmation for this landed cost position from the related partner. If not, no invoice will be generated, but the cost will be included for the average price computation." |
1026 | +msgstr "" |
1027 | + |
1028 | +#. module: purchase_landed_costs |
1029 | +#: help:landed.cost.position,distribution_type_id:0 |
1030 | +msgid "Defines if the amount is to be calculated for each quantity or an absolute value" |
1031 | +msgstr "" |
1032 | + |
1033 | +#. module: purchase_landed_costs |
1034 | +#: model:ir.actions.act_window,name:purchase_landed_costs.action_landed_cost_report_all |
1035 | +#: model:ir.ui.menu,name:purchase_landed_costs.menu_action_landed_cost_report_all |
1036 | +msgid "Landed Costs Analysis" |
1037 | +msgstr "" |
1038 | + |
1039 | +#. module: purchase_landed_costs |
1040 | +#: selection:landed.cost.distribution.type,landed_cost_type:0 |
1041 | +#: selection:product.template,landed_cost_type:0 |
1042 | +msgid "Quantity" |
1043 | +msgstr "" |
1044 | + |
1045 | +#. module: purchase_landed_costs |
1046 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_product_id |
1047 | +#: view:landed.cost.position:0 |
1048 | +#: field:landed.cost.position,product_id:0 |
1049 | +msgid "Landed Cost Name" |
1050 | +msgstr "" |
1051 | + |
1052 | +#. module: purchase_landed_costs |
1053 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost_base_quantity |
1054 | +#: field:purchase.order,landed_cost_base_quantity:0 |
1055 | +msgid "Landed Costs Base Quantity" |
1056 | +msgstr "" |
1057 | + |
1058 | +#. module: purchase_landed_costs |
1059 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landing_costs_order |
1060 | +#: field:purchase.order.line,landing_costs_order:0 |
1061 | +msgid "Landing Costs from Order" |
1062 | +msgstr "" |
1063 | + |
1064 | +#. module: purchase_landed_costs |
1065 | +#: model:ir.model,name:purchase_landed_costs.model_stock_move |
1066 | +msgid "Stock Move" |
1067 | +msgstr "" |
1068 | + |
1069 | +#. module: purchase_landed_costs |
1070 | +#: model:ir.model,name:purchase_landed_costs.model_product_template |
1071 | +msgid "Product Template" |
1072 | +msgstr "" |
1073 | + |
1074 | +#. module: purchase_landed_costs |
1075 | +#: help:landed.cost.distribution.type,landed_cost_type:0 |
1076 | +msgid "Refer to the product landed cost type." |
1077 | +msgstr "" |
1078 | + |
1079 | +#. module: purchase_landed_costs |
1080 | +#: help:stock.move,price_unit_net:0 |
1081 | +msgid "This is the net purchase price, without landed cost as the price include landed price has been stored in price_unit field" |
1082 | +msgstr "" |
1083 | + |
1084 | +#. module: purchase_landed_costs |
1085 | +#: help:landed.cost.position,amount_total_comp_currency:0 |
1086 | +msgid "This field represent the total amount of this position regarding a whole order. By summing it, you'll have the total landed cost for the order (in company reference currency)." |
1087 | +msgstr "" |
1088 | + |
1089 | +#. module: purchase_landed_costs |
1090 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_distribution_type_apply_on |
1091 | +#: field:landed.cost.distribution.type,apply_on:0 |
1092 | +msgid "Applied on" |
1093 | +msgstr "" |
1094 | + |
1095 | +#. module: purchase_landed_costs |
1096 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_partner_id |
1097 | +#: field:landed.cost.position,partner_id:0 |
1098 | +msgid "Partner" |
1099 | +msgstr "" |
1100 | + |
1101 | +#. module: purchase_landed_costs |
1102 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_landed_cost |
1103 | +#: field:purchase.order,landed_cost:0 |
1104 | +msgid "Landed Costs Total Untaxed" |
1105 | +msgstr "" |
1106 | + |
1107 | +#. module: purchase_landed_costs |
1108 | +#: view:purchase.order:0 |
1109 | +msgid "Open All Landed costs" |
1110 | +msgstr "" |
1111 | + |
1112 | +#. module: purchase_landed_costs |
1113 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_purchase_order_line_landing_costs |
1114 | +#: view:landed.cost.position:0 |
1115 | +#: view:purchase.order:0 |
1116 | +#: view:purchase.order.line:0 |
1117 | +#: field:purchase.order.line,landing_costs:0 |
1118 | +msgid "Landing Costs" |
1119 | +msgstr "" |
1120 | + |
1121 | +#. module: purchase_landed_costs |
1122 | +#: model:ir.model.fields,field_description:purchase_landed_costs.field_landed_cost_position_amount_total |
1123 | +#: field:landed.cost.position,amount_total:0 |
1124 | +msgid "Amount Total" |
1125 | +msgstr "" |
1126 | + |
1127 | +#. module: purchase_landed_costs |
1128 | +#: view:landed.cost.position:0 |
1129 | +msgid "Purchase Orders" |
1130 | +msgstr "" |
1131 | + |
1132 | |
1133 | === added file 'purchase_landed_costs/product.py' |
1134 | --- purchase_landed_costs/product.py 1970-01-01 00:00:00 +0000 |
1135 | +++ purchase_landed_costs/product.py 2014-06-19 02:29:14 +0000 |
1136 | @@ -0,0 +1,76 @@ |
1137 | +# -*- coding: utf-8 -*- |
1138 | +############################################################################## |
1139 | +# |
1140 | +# OpenERP, Open Source Management Solution |
1141 | +# Copyright (C) 2010-2013 Camptocamp (<http://www.camptocamp.com>) |
1142 | +# Authors: Ferdinand Gasauer, Joel Grand-Guillaume |
1143 | +# |
1144 | +# This program is free software: you can redistribute it and/or modify |
1145 | +# it under the terms of the GNU Affero General Public License as |
1146 | +# published by the Free Software Foundation, either version 3 of the |
1147 | +# License, or (at your option) any later version. |
1148 | +# |
1149 | +# This program is distributed in the hope that it will be useful, |
1150 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1151 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1152 | +# GNU Affero General Public License for more details. |
1153 | +# |
1154 | +# You should have received a copy of the GNU Affero General Public License |
1155 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1156 | +# |
1157 | +############################################################################## |
1158 | + |
1159 | +from openerp.osv import orm, fields |
1160 | +from openerp.tools.translate import _ |
1161 | + |
1162 | + |
1163 | +class product_template(orm.Model): |
1164 | + _inherit = "product.template" |
1165 | + |
1166 | + _columns = { |
1167 | + 'landed_cost_type': fields.selection( |
1168 | + [('value', 'Value'), |
1169 | + ('per_unit', 'Quantity'), |
1170 | + ('none', 'None')], |
1171 | + 'Distribution Type', |
1172 | + help="Used if this product is landed costs: " |
1173 | + "If landed costs are defined for purchase orders or pickings, " |
1174 | + "this indicates how the costs are distributed to the lines"), |
1175 | + } |
1176 | + |
1177 | + _defaults = { |
1178 | + 'landed_cost_type': lambda self, cr, uid, context: context.get('landed_cost_type') |
1179 | + } |
1180 | + |
1181 | + |
1182 | +class product_product(orm.Model): |
1183 | + _inherit = "product.product" |
1184 | + |
1185 | + def _choose_exp_account_from(self, cr, uid, product, fiscal_position=False, |
1186 | + context=None): |
1187 | + """ Method to compute the expense account to chose based on product and |
1188 | + fiscal position. |
1189 | + |
1190 | + Used in invoice creation and on_change of landed costs. |
1191 | + Taken from method : _choose_account_from_po_line of purchase.py in |
1192 | + purchase module. |
1193 | + |
1194 | + """ |
1195 | + fiscal_obj = self.pool.get('account.fiscal.position') |
1196 | + property_obj = self.pool.get('ir.property') |
1197 | + if product: |
1198 | + acc_id = product.property_account_expense.id |
1199 | + if not acc_id: |
1200 | + acc_id = product.categ_id.property_account_expense_categ.id |
1201 | + if not acc_id: |
1202 | + raise orm.except_orm( |
1203 | + _('Error!'), |
1204 | + _('Define expense account for this company: "%s" (id:%d).') |
1205 | + % (product.name, product.id,)) |
1206 | + else: |
1207 | + acc_id = property_obj.get(cr, uid, |
1208 | + 'property_account_expense_categ', |
1209 | + 'product.category').id |
1210 | + return fiscal_obj.map_account(cr, uid, fiscal_position, acc_id) |
1211 | + |
1212 | + |
1213 | |
1214 | === added file 'purchase_landed_costs/purchase.py' |
1215 | --- purchase_landed_costs/purchase.py 1970-01-01 00:00:00 +0000 |
1216 | +++ purchase_landed_costs/purchase.py 2014-06-19 02:29:14 +0000 |
1217 | @@ -0,0 +1,632 @@ |
1218 | +# -*- coding: utf-8 -*- |
1219 | +############################################################################## |
1220 | +# |
1221 | +# OpenERP, Open Source Management Solution |
1222 | +# Copyright (C) 2010-2013 Camptocamp (<http://www.camptocamp.com>) |
1223 | +# Authors: Ferdinand Gasauer, Joel Grand-Guillaume |
1224 | +# |
1225 | +# This program is free software: you can redistribute it and/or modify |
1226 | +# it under the terms of the GNU Affero General Public License as |
1227 | +# published by the Free Software Foundation, either version 3 of the |
1228 | +# License, or (at your option) any later version. |
1229 | +# |
1230 | +# This program is distributed in the hope that it will be useful, |
1231 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1232 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1233 | +# GNU Affero General Public License for more details. |
1234 | +# |
1235 | +# You should have received a copy of the GNU Affero General Public License |
1236 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1237 | +# |
1238 | +############################################################################## |
1239 | + |
1240 | +from openerp.osv import orm, fields |
1241 | +import openerp.addons.decimal_precision as dp |
1242 | +from openerp.tools.translate import _ |
1243 | +import logging |
1244 | + |
1245 | +_logger = logging.getLogger(__name__) |
1246 | + |
1247 | + |
1248 | +class landed_cost_distribution_type(orm.Model): |
1249 | + """ This is a model to give how we should distribute the amount given |
1250 | + for a landed costs. At the begining we use a selection field, but it |
1251 | + was impossible to filter it depending on the context (in a line or |
1252 | + on order). So we replaced it by this object, adding is_* method to |
1253 | + deal with. Base distribution are defined in YML file. |
1254 | + |
1255 | + """ |
1256 | + |
1257 | + _name = "landed.cost.distribution.type" |
1258 | + |
1259 | + _columns = { |
1260 | + 'name': fields.char('Distribution Type', required=True), |
1261 | + 'apply_on': fields.selection( |
1262 | + [('line', 'Line'), |
1263 | + ('order', 'Order')], |
1264 | + 'Applied on', |
1265 | + required=True, |
1266 | + help="Defines if this distribution type Applied " |
1267 | + "on order or line level."), |
1268 | + 'landed_cost_type': fields.selection( |
1269 | + [('value', 'Value'), |
1270 | + ('per_unit', 'Quantity')], |
1271 | + 'Product Landed Cost Type', |
1272 | + help="Refer to the product landed cost type."), |
1273 | + } |
1274 | + |
1275 | + |
1276 | +class landed_cost_position(orm.Model): |
1277 | + """ The landed cost position represent a direct cost for the delivery |
1278 | + of the goods puchased. It can be from a different partner than the |
1279 | + original supplier, like transport. Cost will be re-affected to each |
1280 | + PO line in respect of the distribution method selected. The average |
1281 | + price computation for the product will take those direct costs into |
1282 | + account. |
1283 | + |
1284 | + """ |
1285 | + |
1286 | + _name = "landed.cost.position" |
1287 | + |
1288 | + def _get_company_currency_from_landed_cost(self, cr, uid, landed_cost, |
1289 | + amount, context=None): |
1290 | + """ Return the amount in company currency by looking at the po. |
1291 | + |
1292 | + Always return a value, even if company currency = PO one. |
1293 | + |
1294 | + :param browse_record landed_cost: Landed cost position browse record |
1295 | + :param float value to convert |
1296 | + :return: Float value amount in company currency converted at po date |
1297 | + |
1298 | + """ |
1299 | + cur_obj = self.pool.get('res.currency') |
1300 | + result = amount |
1301 | + # In some cases, po is not set, we must take it back from po_line |
1302 | + if landed_cost.purchase_order_id: |
1303 | + po = landed_cost.purchase_order_id |
1304 | + else: |
1305 | + po = landed_cost.purchase_order_line_id.order_id |
1306 | + if po: |
1307 | + cmp_cur_id = po.company_id.currency_id.id |
1308 | + po_cur_id = po.pricelist_id.currency_id.id |
1309 | + if cmp_cur_id != po_cur_id: |
1310 | + ctx = context.copy() |
1311 | + ctx['date'] = landed_cost.date_po or False |
1312 | + result = cur_obj.compute(cr, uid, |
1313 | + po_cur_id, |
1314 | + cmp_cur_id, |
1315 | + amount, |
1316 | + context=ctx) |
1317 | + return result |
1318 | + |
1319 | + def _get_total_amount(self, cr, uid, landed_cost, context=None): |
1320 | + """ We should have a field that is the computed value (total |
1321 | + costs that land) e.g. if it's related to a line and per_unit => |
1322 | + I want for the reporting the total line landed cost and multiply |
1323 | + the quantity by given amount. |
1324 | + |
1325 | + :param browse_record landed_cost: Landed cost position browse record |
1326 | + :return total value of this landed cost position |
1327 | + |
1328 | + """ |
1329 | + vals_po_currency = 0.0 |
1330 | + if (landed_cost.purchase_order_line_id and |
1331 | + landed_cost.distribution_type_id.landed_cost_type == 'per_unit'): |
1332 | + vals_po_currency = (landed_cost.amount * |
1333 | + landed_cost.purchase_order_line_id.product_qty) |
1334 | + else: |
1335 | + vals_po_currency = landed_cost.amount |
1336 | + return vals_po_currency |
1337 | + |
1338 | + def _get_amounts(self, cr, uid, ids, field_name, arg, context=None): |
1339 | + if not ids: |
1340 | + return {} |
1341 | + result = {} |
1342 | + for landed_cost in self.browse(cr, uid, ids, context=context): |
1343 | + val_comp_currency = self._get_company_currency_from_landed_cost( |
1344 | + cr, uid, landed_cost, landed_cost.amount, context=context) |
1345 | + val_total = self._get_total_amount(cr, uid, landed_cost, |
1346 | + context=context) |
1347 | + val_total_comp_currency = self._get_company_currency_from_landed_cost( |
1348 | + cr, uid, landed_cost, val_total, context=context) |
1349 | + amounts = { |
1350 | + 'amount_company_currency': val_comp_currency, |
1351 | + 'amount_total': val_total, |
1352 | + 'amount_total_comp_currency': val_total_comp_currency |
1353 | + } |
1354 | + result[landed_cost.id] = amounts |
1355 | + return result |
1356 | + |
1357 | + def _get_po(self, cr, uid, ids, context=None): |
1358 | + landed_obj = self.pool.get('landed.cost.position') |
1359 | + return landed_obj.search(cr, uid, |
1360 | + [('purchase_order_id', 'in', ids)], |
1361 | + context=context) |
1362 | + |
1363 | + _columns = { |
1364 | + 'product_id': fields.many2one( |
1365 | + 'product.product', |
1366 | + 'Landed Cost Name', |
1367 | + required=True, |
1368 | + domain=[('landed_cost_type', '!=', False)]), |
1369 | + 'account_id': fields.many2one( |
1370 | + 'account.account', |
1371 | + 'Fiscal Account', |
1372 | + required=True,), |
1373 | + 'partner_id': fields.many2one( |
1374 | + 'res.partner', |
1375 | + 'Partner', |
1376 | + help="The supplier of this cost component.", |
1377 | + required=True), |
1378 | + 'distribution_type_id': fields.many2one( |
1379 | + 'landed.cost.distribution.type', |
1380 | + 'Distribution Type', |
1381 | + required=True, |
1382 | + help="Defines if the amount is to be calculated for each quantity " |
1383 | + "or an absolute value"), |
1384 | + 'purchase_order_line_id': fields.many2one( |
1385 | + 'purchase.order.line', |
1386 | + 'Purchase Order Line'), |
1387 | + 'purchase_order_id': fields.many2one('purchase.order', 'Purchase Order'), |
1388 | + 'generate_invoice': fields.boolean( |
1389 | + 'Generate Invoice', |
1390 | + help="If ticked, this will generate a draft invoice at the " |
1391 | + "PO confirmation for this landed cost position from the " |
1392 | + "related partner. If not, no invoice will be generated, " |
1393 | + "but the cost will be included for the average price " |
1394 | + "computation."), |
1395 | + 'amount': fields.float( |
1396 | + 'Amount', |
1397 | + required=True, |
1398 | + digits_compute=dp.get_precision('Purchase Price'), |
1399 | + help="Landed cost expressed in PO currency used " |
1400 | + "to fullfil landed cost."), |
1401 | + 'amount_company_currency': fields.function( |
1402 | + _get_amounts, |
1403 | + type="float", |
1404 | + multi='compute_amounts', |
1405 | + string='Amount Company Currency', |
1406 | + # Use Account as it's for comparison with financial accounting |
1407 | + digits_compute=dp.get_precision('Account'), |
1408 | + store={ |
1409 | + 'purchase.order': (_get_po, |
1410 | + ['pricelist_id', 'company_id'], 50), |
1411 | + 'landed.cost.position': (lambda self, cr, uid, ids, c=None: ids, |
1412 | + ['amount', 'purchase_order_id'], 10), |
1413 | + }, |
1414 | + help="Landed cost for stock valuation (expressed in company currency). " |
1415 | + "It will be added to the price of the supplier price."), |
1416 | + 'amount_total': fields.function( |
1417 | + _get_amounts, |
1418 | + type="float", |
1419 | + multi='compute_amounts', |
1420 | + digits_compute=dp.get_precision('Purchase Price'), |
1421 | + string='Amount Total', |
1422 | + help="This field represent the total amount of this position " |
1423 | + "regarding a whole order. By summing it, you'll have the total " |
1424 | + "landed cost for the order (in his currency)", |
1425 | + store={ |
1426 | + 'purchase.order': (_get_po, |
1427 | + ['pricelist_id', 'company_id'], 50), |
1428 | + 'landed.cost.position': (lambda self, cr, uid, ids, c=None: ids, |
1429 | + ['amount', 'purchase_order_id'], 10) |
1430 | + }), |
1431 | + 'amount_total_comp_currency': fields.function( |
1432 | + _get_amounts, |
1433 | + type="float", |
1434 | + multi='compute_amounts', |
1435 | + digits_compute=dp.get_precision('Account'), |
1436 | + string='Amount Total Company Currency', |
1437 | + help="This field represent the total amount of this position " |
1438 | + "regarding a whole order. By summing it, you'll have the total " |
1439 | + "landed cost for the order (in company reference currency).", |
1440 | + store={ |
1441 | + 'purchase.order': (_get_po, |
1442 | + ['pricelist_id', 'company_id'], 50), |
1443 | + 'landed.cost.position': (lambda self, cr, uid, ids, c=None: ids, |
1444 | + ['amount', 'purchase_order_id'], 10) |
1445 | + }), |
1446 | + 'date_po': fields.related( |
1447 | + 'purchase_order_id', 'date_order', |
1448 | + type='date', |
1449 | + string='Date', |
1450 | + store=True, |
1451 | + readonly=True, |
1452 | + help="Date of the related PO"), |
1453 | + 'company_id': fields.related( |
1454 | + 'purchase_order_id', 'company_id', |
1455 | + type='many2one', |
1456 | + relation='res.company', |
1457 | + string='Company', |
1458 | + store=True, |
1459 | + readonly=True), |
1460 | + } |
1461 | + |
1462 | + _default = { |
1463 | + 'generate_invoice': False, |
1464 | + } |
1465 | + |
1466 | + def write(self, cr, uid, ids, vals, context=None): |
1467 | + """ Add the purchase_order_id if only linked to a line """ |
1468 | + if vals.get('purchase_order_line_id'): |
1469 | + po_line_obj = self.pool.get('purchase.order.line') |
1470 | + line_id = vals['purchase_order_line_id'] |
1471 | + po = po_line_obj.browse(cr, uid, line_id, context=context).order_id |
1472 | + vals['purchase_order_id'] = po.id |
1473 | + return super(landed_cost_position, self).write( |
1474 | + cr, uid, ids, vals, context=context) |
1475 | + |
1476 | + def create(self, cr, uid, vals, context=None): |
1477 | + """ Add the purchase_order_id if only linked to a line """ |
1478 | + if vals.get('purchase_order_line_id'): |
1479 | + po_line_obj = self.pool.get('purchase.order.line') |
1480 | + line_id = vals['purchase_order_line_id'] |
1481 | + po = po_line_obj.browse(cr, uid, line_id, context=context).order_id |
1482 | + vals['purchase_order_id'] = po.id |
1483 | + return super(landed_cost_position, self).create( |
1484 | + cr, uid, vals, context=context) |
1485 | + |
1486 | + def onchange_product_id(self, cr, uid, ids, product_id, |
1487 | + purchase_order_id=False, context=None): |
1488 | + """ Give the default value for the distribution type depending |
1489 | + on the setting of the product and the use case: line or order |
1490 | + position. |
1491 | + |
1492 | + """ |
1493 | + res = {} |
1494 | + fiscal_position = False |
1495 | + landed_cost_type = False |
1496 | + # order or line depending on which view we are |
1497 | + if purchase_order_id: |
1498 | + apply_on = 'order' |
1499 | + po_obj = self.pool.get('purchase.order') |
1500 | + po = po_obj.browse(cr, uid, purchase_order_id, context=context) |
1501 | + fiscal_position = po.fiscal_position or False |
1502 | + else: |
1503 | + apply_on = 'line' |
1504 | + if not product_id: |
1505 | + return res |
1506 | + prod_obj = self.pool.get('product.product') |
1507 | + dist_type_obj = self.pool.get('landed.cost.distribution.type') |
1508 | + prod = prod_obj.browse(cr, uid, [product_id], context=context)[0] |
1509 | + account_id = prod_obj._choose_exp_account_from( |
1510 | + cr, uid, prod, fiscal_position=fiscal_position, context=context) |
1511 | + if prod.landed_cost_type in ('per_unit', 'value'): |
1512 | + landed_cost_type = dist_type_obj.search( |
1513 | + cr, uid, |
1514 | + [('apply_on', '=', apply_on), |
1515 | + ('landed_cost_type', '=', prod.landed_cost_type)], |
1516 | + context=context)[0] |
1517 | + value = { |
1518 | + 'distribution_type_id': landed_cost_type, |
1519 | + 'account_id': account_id, |
1520 | + 'partner_id': prod.seller_id and prod.seller_id.id or False |
1521 | + } |
1522 | + res = {'value': value} |
1523 | + return res |
1524 | + |
1525 | + |
1526 | +class purchase_order_line(orm.Model): |
1527 | + _inherit = "purchase.order.line" |
1528 | + |
1529 | + def _landing_cost(self, cr, uid, ids, name, args, context=None): |
1530 | + if not ids: |
1531 | + return {} |
1532 | + result = {} |
1533 | + # landed costs for the line |
1534 | + for line in self.browse(cr, uid, ids, context=context): |
1535 | + landed_costs = 0.0 |
1536 | + if line.landed_cost_line_ids: |
1537 | + for costs in line.landed_cost_line_ids: |
1538 | + if (costs.distribution_type_id.landed_cost_type == 'value' and |
1539 | + costs.distribution_type_id.apply_on == 'line'): |
1540 | + landed_costs += costs.amount |
1541 | + else: |
1542 | + landed_costs += costs.amount * line.product_qty |
1543 | + result[line.id] = landed_costs |
1544 | + return result |
1545 | + |
1546 | + def _landing_cost_order(self, cr, uid, ids, name, args, context=None): |
1547 | + if not ids: |
1548 | + return {} |
1549 | + result = {} |
1550 | + lines = self.browse(cr, uid, ids, context=context) |
1551 | + # Landed costs line by line |
1552 | + for line in lines: |
1553 | + landed_costs = 0.0 |
1554 | + order = line.order_id |
1555 | + # distribution of landed costs of PO |
1556 | + if order.landed_cost_line_ids: |
1557 | + # Base value (Absolute Value) |
1558 | + if order.landed_cost_base_value: |
1559 | + try: |
1560 | + landed_costs += (order.landed_cost_base_value / |
1561 | + order.amount_untaxed * |
1562 | + line.price_subtotal) |
1563 | + # We ignore the zero division error and doesn't sum |
1564 | + # matter of function filed computation order |
1565 | + except ZeroDivisionError: |
1566 | + pass |
1567 | + # Base quantity (Per Quantity) |
1568 | + if order.landed_cost_base_quantity: |
1569 | + try: |
1570 | + landed_costs += (order.landed_cost_base_quantity / |
1571 | + order.quantity_total * |
1572 | + line.product_qty) |
1573 | + # We ignore the zero division error and doesn't sum |
1574 | + # matter of function filed computation order |
1575 | + except ZeroDivisionError: |
1576 | + pass |
1577 | + result[line.id] = landed_costs |
1578 | + return result |
1579 | + |
1580 | + def _landed_cost(self, cr, uid, ids, name, args, context=None): |
1581 | + if not ids : return {} |
1582 | + result = {} |
1583 | + # landed costs for the line |
1584 | + for line in self.browse(cr, uid, ids, context=context): |
1585 | + landed_costs = 0.0 |
1586 | + landed_costs += (line.price_subtotal + |
1587 | + line.landing_costs + line.landing_costs_order) |
1588 | + result[line.id] = landed_costs |
1589 | + return result |
1590 | + |
1591 | + _columns = { |
1592 | + 'landed_cost_line_ids': fields.one2many( |
1593 | + 'landed.cost.position', |
1594 | + 'purchase_order_line_id', |
1595 | + 'Landed Costs Positions'), |
1596 | + 'landing_costs': fields.function( |
1597 | + _landing_cost, |
1598 | + digits_compute=dp.get_precision('Account'), |
1599 | + string='Landing Costs'), |
1600 | + 'landing_costs_order': fields.function( |
1601 | + _landing_cost_order, |
1602 | + digits_compute=dp.get_precision('Account'), |
1603 | + string='Landing Costs from Order'), |
1604 | + 'landed_costs': fields.function( |
1605 | + _landed_cost, |
1606 | + digits_compute=dp.get_precision('Account'), |
1607 | + string='Landed Costs'), |
1608 | + } |
1609 | + |
1610 | + |
1611 | +class purchase_order(orm.Model): |
1612 | + _inherit = "purchase.order" |
1613 | + |
1614 | + def _landed_cost_base_value(self, cr, uid, ids, name, args, context=None): |
1615 | + if not ids: |
1616 | + return {} |
1617 | + result = {} |
1618 | + landed_costs_base_value = 0.0 |
1619 | + for line in self.browse(cr, uid, ids, context=context): |
1620 | + if line.landed_cost_line_ids: |
1621 | + for costs in line.landed_cost_line_ids: |
1622 | + if (costs.distribution_type_id.landed_cost_type == 'value' and |
1623 | + costs.distribution_type_id.apply_on == 'order'): |
1624 | + landed_costs_base_value += costs.amount |
1625 | + result[line.id] = landed_costs_base_value |
1626 | + return result |
1627 | + |
1628 | + def _landed_cost_base_quantity(self, cr, uid, ids, name, args, context=None): |
1629 | + if not ids: |
1630 | + return {} |
1631 | + result = {} |
1632 | + landed_costs_base_quantity = 0.0 |
1633 | + for line in self.browse(cr, uid, ids, context=context): |
1634 | + if line.landed_cost_line_ids: |
1635 | + for costs in line.landed_cost_line_ids: |
1636 | + if (costs.distribution_type_id.landed_cost_type == 'per_unit' and |
1637 | + costs.distribution_type_id.apply_on == 'order'): |
1638 | + landed_costs_base_quantity += costs.amount |
1639 | + result[line.id] = landed_costs_base_quantity |
1640 | + return result |
1641 | + |
1642 | + def _quantity_total(self, cr, uid, ids, name, args, context=None): |
1643 | + if not ids: |
1644 | + return {} |
1645 | + result = {} |
1646 | + quantity_total = 0.0 |
1647 | + for line in self.browse(cr, uid, ids, context=context): |
1648 | + if line.order_line: |
1649 | + for pol in line.order_line: |
1650 | + if pol.product_qty > 0.0: |
1651 | + quantity_total += pol.product_qty |
1652 | + result[line.id] = quantity_total |
1653 | + return result |
1654 | + |
1655 | + def _landed_cost(self, cr, uid, ids, name, args, context=None): |
1656 | + if not ids: |
1657 | + return {} |
1658 | + result = {} |
1659 | + landed_costs = 0.0 |
1660 | + # landed costs for the line |
1661 | + for line in self.browse(cr, uid, ids, context=context): |
1662 | + landed_costs += (line.landing_cost_lines + |
1663 | + line.landed_cost_base_value + |
1664 | + line.landed_cost_base_quantity + |
1665 | + line.amount_untaxed) |
1666 | + result[line.id] = landed_costs |
1667 | + return result |
1668 | + |
1669 | + def _landing_cost_lines(self, cr, uid, ids, name, args, context=None): |
1670 | + if not ids: |
1671 | + return {} |
1672 | + result = {} |
1673 | + landed_cost_lines = 0.0 |
1674 | + for line in self.browse(cr, uid, ids, context=context): |
1675 | + if line.order_line: |
1676 | + for pol in line.order_line: |
1677 | + if pol.product_qty > 0.0: |
1678 | + landed_cost_lines += pol.landing_costs |
1679 | + result[line.id] = landed_cost_lines |
1680 | + return result |
1681 | + |
1682 | + _columns = { |
1683 | + 'landed_cost_line_ids': fields.one2many( |
1684 | + 'landed.cost.position', |
1685 | + 'purchase_order_id', |
1686 | + 'Landed Costs', |
1687 | + domain=[('purchase_order_line_id', '=', False)]), |
1688 | + 'landed_cost_base_value': fields.function( |
1689 | + _landed_cost_base_value, |
1690 | + digits_compute=dp.get_precision('Account'), |
1691 | + string='Landed Costs Base Value'), |
1692 | + 'landed_cost_base_quantity': fields.function( |
1693 | + _landed_cost_base_quantity, |
1694 | + digits_compute=dp.get_precision('Account'), |
1695 | + string='Landed Costs Base Quantity'), |
1696 | + 'landing_cost_lines': fields.function( |
1697 | + _landing_cost_lines, |
1698 | + digits_compute=dp.get_precision('Account'), |
1699 | + string='Landing Cost Lines'), |
1700 | + 'landed_cost': fields.function( |
1701 | + _landed_cost, |
1702 | + digits_compute=dp.get_precision('Account'), |
1703 | + string='Landed Costs Total Untaxed'), |
1704 | + 'quantity_total': fields.function( |
1705 | + _quantity_total, |
1706 | + digits_compute=dp.get_precision('Product UoM'), |
1707 | + string='Total Quantity'), |
1708 | + } |
1709 | + |
1710 | + def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, |
1711 | + context=None): |
1712 | + """ Here, the technical price_unit field will store the purchase |
1713 | + price + landed cost. The original purchase price is stored in |
1714 | + price_unit_net new field to keep record of it. |
1715 | + |
1716 | + """ |
1717 | + res = super(purchase_order,self)._prepare_order_line_move( |
1718 | + cr, uid, order, order_line, picking_id, context=context) |
1719 | + res['price_unit_net'] = res['price_unit'] |
1720 | + try: |
1721 | + res['price_unit'] = order_line.landed_costs / order_line.product_qty |
1722 | + except ZeroDivisionError: |
1723 | + pass |
1724 | + return res |
1725 | + |
1726 | + def _prepare_landed_cost_inv_line(self, cr, uid, account_id, inv_id, |
1727 | + landed_cost, context=None): |
1728 | + """ Collects require data from landed cost position that is used to |
1729 | + create invoice line for that particular position. |
1730 | + |
1731 | + If it comes from a PO line and Distribution type is per unit |
1732 | + the quantity of the invoice is the PO line quantity |
1733 | + |
1734 | + :param account_id: Expense account. |
1735 | + :param inv_id: Related invoice. |
1736 | + :param browse_record landed_cost: Landed cost position browse record |
1737 | + :return: Value for fields of invoice lines. |
1738 | + :rtype: dict |
1739 | + |
1740 | + """ |
1741 | + qty = 1.0 |
1742 | + if (landed_cost.purchase_order_line_id and |
1743 | + landed_cost.distribution_type_id.landed_cost_type == 'per_unit'): |
1744 | + qty = landed_cost.purchase_order_line_id.product_qty |
1745 | + line_tax_ids = [x.id for x in landed_cost.product_id.supplier_taxes_id] |
1746 | + return { |
1747 | + 'name': landed_cost.product_id.name, |
1748 | + 'account_id': account_id, |
1749 | + 'invoice_id' : inv_id, |
1750 | + 'price_unit': landed_cost.amount or 0.0, |
1751 | + 'quantity': qty, |
1752 | + 'product_id': landed_cost.product_id.id or False, |
1753 | + 'invoice_line_tax_id': [(6, 0, line_tax_ids)], |
1754 | + } |
1755 | + |
1756 | + def _prepare_landed_cost_inv(self, cr, uid, landed_cost, context=None): |
1757 | + """ Collects require data from landed cost position that is used to |
1758 | + create invoice for that particular position. |
1759 | + |
1760 | + Note that _landed can come from a line or at whole PO level. |
1761 | + |
1762 | + :param browse_record landed_cost: Landed cost position browse record |
1763 | + :return: Value for fields of invoice. |
1764 | + :rtype: dict |
1765 | + |
1766 | + """ |
1767 | + po = (landed_cost.purchase_order_id or |
1768 | + landed_cost.purchase_order_line_id.order_id) |
1769 | + currency_id = landed_cost.purchase_order_id.pricelist_id.currency_id.id |
1770 | + fiscal_position_id = po.fiscal_position.id if po.fiscal_position else False |
1771 | + journal_obj = self.pool.get('account.journal') |
1772 | + journal_ids = journal_obj.search( |
1773 | + cr, uid, |
1774 | + [('type', '=', 'purchase'), |
1775 | + ('company_id', '=', po.company_id.id)], |
1776 | + limit=1) |
1777 | + if not journal_ids: |
1778 | + raise orm.except_orm( |
1779 | + _('Error!'), |
1780 | + _('Define purchase journal for this company: "%s" (id: %d).') |
1781 | + % (po.company_id.name, po.company_id.id)) |
1782 | + return { |
1783 | + 'currency_id': currency_id, |
1784 | + 'partner_id': landed_cost.partner_id.id, |
1785 | + 'account_id': landed_cost.partner_id.property_account_payable.id, |
1786 | + 'type': 'in_invoice', |
1787 | + 'origin': po.name, |
1788 | + 'fiscal_position': fiscal_position_id, |
1789 | + 'company_id': po.company_id.id, |
1790 | + 'journal_id': len(journal_ids) and journal_ids[0] or False, |
1791 | + } |
1792 | + |
1793 | + def _generate_invoice_from_landed_cost(self, cr, uid, landed_cost, |
1794 | + context=None): |
1795 | + """ Generate an invoice from order landed costs (means generic |
1796 | + costs to a whole PO) or from a line landed costs. |
1797 | + |
1798 | + """ |
1799 | + invoice_obj = self.pool.get('account.invoice') |
1800 | + invoice_line_obj = self.pool.get('account.invoice.line') |
1801 | + prod_obj = self.pool.get('product.product') |
1802 | + po = (landed_cost.purchase_order_id or |
1803 | + landed_cost.purchase_order_line_id.order_id) |
1804 | + vals_inv = self._prepare_landed_cost_inv(cr, uid, landed_cost, |
1805 | + context=context) |
1806 | + inv_id = invoice_obj.create(cr, uid, vals_inv, context=context) |
1807 | + fiscal_position = (po.fiscal_position or False) |
1808 | + exp_account_id = prod_obj._choose_exp_account_from( |
1809 | + cr, uid, |
1810 | + landed_cost.product_id, |
1811 | + fiscal_position=fiscal_position, |
1812 | + context=context |
1813 | + ) |
1814 | + vals_line = self._prepare_landed_cost_inv_line( |
1815 | + cr, uid, exp_account_id, inv_id, |
1816 | + landed_cost, context=context |
1817 | + ) |
1818 | + inv_line_id = invoice_line_obj.create(cr, uid, vals_line, |
1819 | + context=context) |
1820 | + return inv_id |
1821 | + |
1822 | + def wkf_approve_order(self, cr, uid, ids, context=None): |
1823 | + """ On PO approval, generate all invoices for all landed cost position. |
1824 | + |
1825 | + Remember that only landed cost position with the checkbox |
1826 | + generate_invoice ticked are generated. |
1827 | + |
1828 | + """ |
1829 | + res = super(purchase_order,self).wkf_approve_order(cr, uid, ids, |
1830 | + context=context) |
1831 | + for order in self.browse(cr, uid, ids, context=context): |
1832 | + invoice_ids = [] |
1833 | + for order_cost in order.landed_cost_line_ids: |
1834 | + if order_cost.generate_invoice: |
1835 | + inv_id = self._generate_invoice_from_landed_cost( |
1836 | + cr, uid, order_cost, context=context) |
1837 | + invoice_ids.append(inv_id) |
1838 | + for po_line in order.order_line: |
1839 | + for line_cost in po_line.landed_cost_line_ids: |
1840 | + inv_id = self._generate_invoice_from_landed_cost( |
1841 | + cr, uid, line_cost, context=context) |
1842 | + invoice_ids.append(inv_id) |
1843 | + # Link this new invoice to related purchase order |
1844 | + # 4 in that list is "Add" mode in a many2many used here because |
1845 | + # the call to super() already add the main invoice |
1846 | + if invoice_ids: |
1847 | + commands = [(4, invoice_id) for invoice_id in invoice_ids] |
1848 | + order.write({'invoice_ids': commands}, context=context) |
1849 | + return res |
1850 | |
1851 | === added file 'purchase_landed_costs/purchase.py.save' |
1852 | --- purchase_landed_costs/purchase.py.save 1970-01-01 00:00:00 +0000 |
1853 | +++ purchase_landed_costs/purchase.py.save 2014-06-19 02:29:14 +0000 |
1854 | @@ -0,0 +1,631 @@ |
1855 | +# -*- coding: utf-8 -*- |
1856 | +############################################################################## |
1857 | +# |
1858 | +# OpenERP, Open Source Management Solution |
1859 | +# Copyright (C) 2010-2013 Camptocamp (<http://www.camptocamp.com>) |
1860 | +# Authors: Ferdinand Gasauer, Joel Grand-Guillaume |
1861 | +# |
1862 | +# This program is free software: you can redistribute it and/or modify |
1863 | +# it under the terms of the GNU Affero General Public License as |
1864 | +# published by the Free Software Foundation, either version 3 of the |
1865 | +# License, or (at your option) any later version. |
1866 | +# |
1867 | +# This program is distributed in the hope that it will be useful, |
1868 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1869 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1870 | +# GNU Affero General Public License for more details. |
1871 | +# |
1872 | +# You should have received a copy of the GNU Affero General Public License |
1873 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1874 | +# |
1875 | +############################################################################## |
1876 | + |
1877 | +from openerp.osv import orm, fields |
1878 | +import openerp.addons.decimal_precision as dp |
1879 | +from openerp.tools.translate import _ |
1880 | +import logging |
1881 | + |
1882 | +_logger = logging.getLogger(__name__) |
1883 | + |
1884 | + |
1885 | +class landed_cost_distribution_type(orm.Model): |
1886 | + """ This is a model to give how we should distribute the amount given |
1887 | + for a landed costs. At the begining we use a selection field, but it |
1888 | + was impossible to filter it depending on the context (in a line or |
1889 | + on order). So we replaced it by this object, adding is_* method to |
1890 | + deal with. Base distribution are defined in YML file. |
1891 | + |
1892 | + """ |
1893 | + |
1894 | + _name = "landed.cost.distribution.type" |
1895 | + |
1896 | + _columns = { |
1897 | + 'name': fields.char('Distribution Type', required=True), |
1898 | + 'apply_on': fields.selection( |
1899 | + [('line', 'Line'), |
1900 | + ('order', 'Order')], |
1901 | + 'Applied on', |
1902 | + required=True, |
1903 | + help="Defines if this distribution type Applied " |
1904 | + "on order or line level."), |
1905 | + 'landed_cost_type': fields.selection( |
1906 | + [('value', 'Value'), |
1907 | + ('per_unit', 'Quantity')], |
1908 | + 'Product Landed Cost Type', |
1909 | + help="Refer to the product landed cost type."), |
1910 | + } |
1911 | + |
1912 | + |
1913 | +class landed_cost_position(orm.Model): |
1914 | + """ The landed cost position represent a direct cost for the delivery |
1915 | + of the goods puchased. It can be from a different partner than the |
1916 | + original supplier, like transport. Cost will be re-affected to each |
1917 | + PO line in respect of the distribution method selected. The average |
1918 | + price computation for the product will take those direct costs into |
1919 | + account. |
1920 | + |
1921 | + """ |
1922 | + |
1923 | + _name = "landed.cost.position" |
1924 | + |
1925 | + def _get_company_currency_from_landed_cost(self, cr, uid, landed_cost, |
1926 | + amount, context=None): |
1927 | + """ Return the amount in company currency by looking at the po. |
1928 | + |
1929 | + Always return a value, even if company currency = PO one. |
1930 | + |
1931 | + :param browse_record landed_cost: Landed cost position browse record |
1932 | + :param float value to convert |
1933 | + :return: Float value amount in company currency converted at po date |
1934 | + |
1935 | + """ |
1936 | + cur_obj = self.pool.get('res.currency') |
1937 | + result = amount |
1938 | + # In some cases, po is not set, we must take it back from po_line |
1939 | + if landed_cost.purchase_order_id: |
1940 | + po = landed_cost.purchase_order_id |
1941 | + else: |
1942 | + po = landed_cost.purchase_order_line_id.order_id |
1943 | + if po: |
1944 | + cmp_cur_id = po.company_id.currency_id.id |
1945 | + po_cur_id = po.pricelist_id.currency_id.id |
1946 | + if cmp_cur_id != po_cur_id: |
1947 | + ctx = context.copy() |
1948 | + ctx['date'] = landed_cost.date_po or False |
1949 | + result = cur_obj.compute(cr, uid, |
1950 | + po_cur_id, |
1951 | + cmp_cur_id, |
1952 | + amount, |
1953 | + context=ctx) |
1954 | + return result |
1955 | + |
1956 | + def _get_total_amount(self, cr, uid, landed_cost, context=None): |
1957 | + """ We should have a field that is the computed value (total |
1958 | + costs that land) e.g. if it's related to a line and per_unit => |
1959 | + I want for the reporting the total line landed cost and multiply |
1960 | + the quantity by given amount. |
1961 | + |
1962 | + :param browse_record landed_cost: Landed cost position browse record |
1963 | + :return total value of this landed cost position |
1964 | + |
1965 | + """ |
1966 | + vals_po_currency = 0.0 |
1967 | + if (landed_cost.purchase_order_line_id and |
1968 | + landed_cost.distribution_type_id.landed_cost_type == 'per_unit'): |
1969 | + vals_po_currency = (landed_cost.amount * |
1970 | + landed_cost.purchase_order_line_id.product_qty) |
1971 | + else: |
1972 | + vals_po_currency = landed_cost.amount |
1973 | + return vals_po_currency |
1974 | + |
1975 | + def _get_amounts(self, cr, uid, ids, field_name, arg, context=None): |
1976 | + if not ids: |
1977 | + return {} |
1978 | + result = {} |
1979 | + for landed_cost in self.browse(cr, uid, ids, context=context): |
1980 | + val_comp_currency = self._get_company_currency_from_landed_cost( |
1981 | + cr, uid, landed_cost, landed_cost.amount, context=context) |
1982 | + val_total = self._get_total_amount(cr, uid, landed_cost, |
1983 | + context=context) |
1984 | + val_total_comp_currency = self._get_company_currency_from_landed_cost( |
1985 | + cr, uid, landed_cost, val_total, context=context) |
1986 | + amounts = { |
1987 | + 'amount_company_currency': val_comp_currency, |
1988 | + 'amount_total': val_total, |
1989 | + 'amount_total_comp_currency': val_total_comp_currency |
1990 | + } |
1991 | + result[landed_cost.id] = amounts |
1992 | + return result |
1993 | + |
1994 | + def _get_po(self, cr, uid, ids, context=None): |
1995 | + landed_obj = self.pool.get('landed.cost.position') |
1996 | + return landed_obj.search(cr, uid, |
1997 | + [('purchase_order_id', 'in', ids)], |
1998 | + context=context) |
1999 | + |
2000 | + _columns = { |
2001 | + 'product_id': fields.many2one( |
2002 | + 'product.product', |
2003 | + 'Landed Cost Name', |
2004 | + required=True, |
2005 | + domain=[('landed_cost_type', '!=', False)]), |
2006 | + 'account_id': fields.many2one( |
2007 | + 'account.account', |
2008 | + 'Fiscal Account', |
2009 | + required=True,), |
2010 | + 'partner_id': fields.many2one( |
2011 | + 'res.partner', |
2012 | + 'Partner', |
2013 | + help="The supplier of this cost component.", |
2014 | + required=True), |
2015 | + 'distribution_type_id': fields.many2one( |
2016 | + 'landed.cost.distribution.type', |
2017 | + 'Distribution Type', |
2018 | + required=True, |
2019 | + help="Defines if the amount is to be calculated for each quantity " |
2020 | + "or an absolute value"), |
2021 | + 'purchase_order_line_id': fields.many2one( |
2022 | + 'purchase.order.line', |
2023 | + 'Purchase Order Line'), |
2024 | + 'purchase_order_id': fields.many2one('purchase.order', 'Purchase Order'), |
2025 | + 'generate_invoice': fields.boolean( |
2026 | + 'Generate Invoice', |
2027 | + help="If ticked, this will generate a draft invoice at the " |
2028 | + "PO confirmation for this landed cost position from the " |
2029 | + "related partner. If not, no invoice will be generated, " |
2030 | + "but the cost will be included for the average price " |
2031 | + "computation."), |
2032 | + 'amount': fields.float( |
2033 | + 'Amount', |
2034 | + required=True, |
2035 | + digits_compute=dp.get_precision('Purchase Price'), |
2036 | + help="Landed cost expressed in PO currency used " |
2037 | + "to fullfil landed cost."), |
2038 | + 'amount_company_currency': fields.function( |
2039 | + _get_amounts, |
2040 | + type="float", |
2041 | + multi='compute_amounts', |
2042 | + string='Amount Company Currency', |
2043 | + # Use Account as it's for comparison with financial accounting |
2044 | + digits_compute=dp.get_precision('Account'), |
2045 | + store={ |
2046 | + 'purchase.order': (_get_po, |
2047 | + ['pricelist_id', 'company_id'], 50), |
2048 | + 'landed.cost.position': (lambda self, cr, uid, ids, c=None: ids, |
2049 | + ['amount', 'purchase_order_id'], 10), |
2050 | + }, |
2051 | + help="Landed cost for stock valuation (expressed in company currency). " |
2052 | + "It will be added to the price of the supplier price."), |
2053 | + 'amount_total': fields.function( |
2054 | + _get_amounts, |
2055 | + type="float", |
2056 | + multi='compute_amounts', |
2057 | + digits_compute=dp.get_precision('Purchase Price'), |
2058 | + string='Amount Total', |
2059 | + help="This field represent the total amount of this position " |
2060 | + "regarding a whole order. By summing it, you'll have the total " |
2061 | + "landed cost for the order (in his currency)", |
2062 | + store={ |
2063 | + 'purchase.order': (_get_po, |
2064 | + ['pricelist_id', 'company_id'], 50), |
2065 | + 'landed.cost.position': (lambda self, cr, uid, ids, c=None: ids, |
2066 | + ['amount', 'purchase_order_id'], 10) |
2067 | + }), |
2068 | + 'amount_total_comp_currency': fields.function( |
2069 | + _get_amounts, |
2070 | + type="float", |
2071 | + multi='compute_amounts', |
2072 | + digits_compute=dp.get_precision('Account'), |
2073 | + string='Amount Total Company Currency', |
2074 | + help="This field represent the total amount of this position " |
2075 | + "regarding a whole order. By summing it, you'll have the total " |
2076 | + "landed cost for the order (in company reference currency).", |
2077 | + store={ |
2078 | + 'purchase.order': (_get_po, |
2079 | + ['pricelist_id', 'company_id'], 50), |
2080 | + 'landed.cost.position': (lambda self, cr, uid, ids, c=None: ids, |
2081 | + ['amount', 'purchase_order_id'], 10) |
2082 | + }), |
2083 | + 'date_po': fields.related( |
2084 | + 'purchase_order_id', 'date_order', |
2085 | + type='date', |
2086 | + string='Date', |
2087 | + store=True, |
2088 | + readonly=True, |
2089 | + help="Date of the related PO"), |
2090 | + 'company_id': fields.related( |
2091 | + 'purchase_order_id', 'company_id', |
2092 | + type='many2one', |
2093 | + relation='res.company', |
2094 | + string='Company', |
2095 | + store=True, |
2096 | + readonly=True), |
2097 | + } |
2098 | + |
2099 | + _default = { |
2100 | + 'generate_invoice': False, |
2101 | + } |
2102 | + |
2103 | + def write(self, cr, uid, ids, vals, context=None): |
2104 | + """ Add the purchase_order_id if only linked to a line """ |
2105 | + if vals.get('purchase_order_line_id'): |
2106 | + po_line_obj = self.pool.get('purchase.order.line') |
2107 | + line_id = vals['purchase_order_line_id'] |
2108 | + po = po_line_obj.browse(cr, uid, line_id, context=context).order_id |
2109 | + vals['purchase_order_id'] = po.id |
2110 | + return super(landed_cost_position, self).write( |
2111 | + cr, uid, ids, vals, context=context) |
2112 | + |
2113 | + def create(self, cr, uid, vals, context=None): |
2114 | + """ Add the purchase_order_id if only linked to a line """ |
2115 | + if vals.get('purchase_order_line_id'): |
2116 | + po_line_obj = self.pool.get('purchase.order.line') |
2117 | + line_id = vals['purchase_order_line_id'] |
2118 | + po = po_line_obj.browse(cr, uid, line_id, context=context).order_id |
2119 | + vals['purchase_order_id'] = po.id |
2120 | + return super(landed_cost_position, self).create( |
2121 | + cr, uid, vals, context=context) |
2122 | + |
2123 | + def onchange_product_id(self, cr, uid, ids, product_id, |
2124 | + purchase_order_id=False, context=None): |
2125 | + """ Give the default value for the distribution type depending |
2126 | + on the setting of the product and the use case: line or order |
2127 | + position. |
2128 | + |
2129 | + """ |
2130 | + res = {} |
2131 | + fiscal_position = False |
2132 | + landed_cost_type = False |
2133 | + # order or line depending on which view we are |
2134 | + if purchase_order_id: |
2135 | + apply_on = 'order' |
2136 | + po_obj = self.pool.get('purchase.order') |
2137 | + po = po_obj.browse(cr, uid, purchase_order_id, context=context) |
2138 | +other/purchase_landed_costs else: |
2139 | + apply_on = 'line' |
2140 | + if not product_id: |
2141 | + return res |
2142 | + prod_obj = self.pool.get('product.product') |
2143 | + dist_type_obj = self.pool.get('landed.cost.distribution.type') |
2144 | + prod = prod_obj.browse(cr, uid, [product_id], context=context)[0] |
2145 | + account_id = prod_obj._choose_exp_account_from( |
2146 | + cr, uid, prod, fiscal_position=fiscal_position, context=context) |
2147 | + if prod.landed_cost_type in ('per_unit', 'value'): |
2148 | + landed_cost_type = dist_type_obj.search( |
2149 | + cr, uid, |
2150 | + [('apply_on', '=', apply_on), |
2151 | + ('landed_cost_type', '=', prod.landed_cost_type)], |
2152 | + context=context)[0] |
2153 | + value = { |
2154 | + 'distribution_type_id': landed_cost_type, |
2155 | + 'account_id': account_id, |
2156 | + 'partner_id': prod.seller_id and prod.seller_id.id or False |
2157 | + } |
2158 | + res = {'value': value} |
2159 | + return res |
2160 | + |
2161 | + |
2162 | +class purchase_order_line(orm.Model): |
2163 | + _inherit = "purchase.order.line" |
2164 | + |
2165 | + def _landing_cost(self, cr, uid, ids, name, args, context=None): |
2166 | + if not ids: |
2167 | + return {} |
2168 | + result = {} |
2169 | + # landed costs for the line |
2170 | + for line in self.browse(cr, uid, ids, context=context): |
2171 | + landed_costs = 0.0 |
2172 | + if line.landed_cost_line_ids: |
2173 | + for costs in line.landed_cost_line_ids: |
2174 | + if (costs.distribution_type_id.landed_cost_type == 'value' and |
2175 | + costs.distribution_type_id.apply_on == 'line'): |
2176 | + landed_costs += costs.amount |
2177 | + else: |
2178 | + landed_costs += costs.amount * line.product_qty |
2179 | + result[line.id] = landed_costs |
2180 | + return result |
2181 | + |
2182 | + def _landing_cost_order(self, cr, uid, ids, name, args, context=None): |
2183 | + if not ids: |
2184 | + return {} |
2185 | + result = {} |
2186 | + lines = self.browse(cr, uid, ids, context=context) |
2187 | + # Landed costs line by line |
2188 | + for line in lines: |
2189 | + landed_costs = 0.0 |
2190 | + order = line.order_id |
2191 | + # distribution of landed costs of PO |
2192 | + if order.landed_cost_line_ids: |
2193 | + # Base value (Absolute Value) |
2194 | + if order.landed_cost_base_value: |
2195 | + try: |
2196 | + landed_costs += (order.landed_cost_base_value / |
2197 | + order.amount_untaxed * |
2198 | + line.price_subtotal) |
2199 | + # We ignore the zero division error and doesn't sum |
2200 | + # matter of function filed computation order |
2201 | + except ZeroDivisionError: |
2202 | + pass |
2203 | + # Base quantity (Per Quantity) |
2204 | + if order.landed_cost_base_quantity: |
2205 | + try: |
2206 | + landed_costs += (order.landed_cost_base_quantity / |
2207 | + order.quantity_total * |
2208 | + line.product_qty) |
2209 | + # We ignore the zero division error and doesn't sum |
2210 | + # matter of function filed computation order |
2211 | + except ZeroDivisionError: |
2212 | + pass |
2213 | + result[line.id] = landed_costs |
2214 | + return result |
2215 | + |
2216 | + def _landed_cost(self, cr, uid, ids, name, args, context=None): |
2217 | + if not ids : return {} |
2218 | + result = {} |
2219 | + # landed costs for the line |
2220 | + for line in self.browse(cr, uid, ids, context=context): |
2221 | + landed_costs = 0.0 |
2222 | + landed_costs += (line.price_subtotal + |
2223 | + line.landing_costs + line.landing_costs_order) |
2224 | + result[line.id] = landed_costs |
2225 | + return result |
2226 | + |
2227 | + _columns = { |
2228 | + 'landed_cost_line_ids': fields.one2many( |
2229 | + 'landed.cost.position', |
2230 | + 'purchase_order_line_id', |
2231 | + 'Landed Costs Positions'), |
2232 | + 'landing_costs': fields.function( |
2233 | + _landing_cost, |
2234 | + digits_compute=dp.get_precision('Account'), |
2235 | + string='Landing Costs'), |
2236 | + 'landing_costs_order': fields.function( |
2237 | + _landing_cost_order, |
2238 | + digits_compute=dp.get_precision('Account'), |
2239 | + string='Landing Costs from Order'), |
2240 | + 'landed_costs': fields.function( |
2241 | + _landed_cost, |
2242 | + digits_compute=dp.get_precision('Account'), |
2243 | + string='Landed Costs'), |
2244 | + } |
2245 | + |
2246 | + |
2247 | +class purchase_order(orm.Model): |
2248 | + _inherit = "purchase.order" |
2249 | + |
2250 | + def _landed_cost_base_value(self, cr, uid, ids, name, args, context=None): |
2251 | + if not ids: |
2252 | + return {} |
2253 | + result = {} |
2254 | + landed_costs_base_value = 0.0 |
2255 | + for line in self.browse(cr, uid, ids, context=context): |
2256 | + if line.landed_cost_line_ids: |
2257 | + for costs in line.landed_cost_line_ids: |
2258 | + if (costs.distribution_type_id.landed_cost_type == 'value' and |
2259 | + costs.distribution_type_id.apply_on == 'order'): |
2260 | + landed_costs_base_value += costs.amount |
2261 | + result[line.id] = landed_costs_base_value |
2262 | + return result |
2263 | + |
2264 | + def _landed_cost_base_quantity(self, cr, uid, ids, name, args, context=None): |
2265 | + if not ids: |
2266 | + return {} |
2267 | + result = {} |
2268 | + landed_costs_base_quantity = 0.0 |
2269 | + for line in self.browse(cr, uid, ids, context=context): |
2270 | + if line.landed_cost_line_ids: |
2271 | + for costs in line.landed_cost_line_ids: |
2272 | + if (costs.distribution_type_id.landed_cost_type == 'per_unit' and |
2273 | + costs.distribution_type_id.apply_on == 'order'): |
2274 | + landed_costs_base_quantity += costs.amount |
2275 | + result[line.id] = landed_costs_base_quantity |
2276 | + return result |
2277 | + |
2278 | + def _quantity_total(self, cr, uid, ids, name, args, context=None): |
2279 | + if not ids: |
2280 | + return {} |
2281 | + result = {} |
2282 | + quantity_total = 0.0 |
2283 | + for line in self.browse(cr, uid, ids, context=context): |
2284 | + if line.order_line: |
2285 | + for pol in line.order_line: |
2286 | + if pol.product_qty > 0.0: |
2287 | + quantity_total += pol.product_qty |
2288 | + result[line.id] = quantity_total |
2289 | + return result |
2290 | + |
2291 | + def _landed_cost(self, cr, uid, ids, name, args, context=None): |
2292 | + if not ids: |
2293 | + return {} |
2294 | + result = {} |
2295 | + landed_costs = 0.0 |
2296 | + # landed costs for the line |
2297 | + for line in self.browse(cr, uid, ids, context=context): |
2298 | + landed_costs += (line.landing_cost_lines + |
2299 | + line.landed_cost_base_value + |
2300 | + line.landed_cost_base_quantity + |
2301 | + line.amount_untaxed) |
2302 | + result[line.id] = landed_costs |
2303 | + return result |
2304 | + |
2305 | + def _landing_cost_lines(self, cr, uid, ids, name, args, context=None): |
2306 | + if not ids: |
2307 | + return {} |
2308 | + result = {} |
2309 | + landed_cost_lines = 0.0 |
2310 | + for line in self.browse(cr, uid, ids, context=context): |
2311 | + if line.order_line: |
2312 | + for pol in line.order_line: |
2313 | + if pol.product_qty > 0.0: |
2314 | + landed_cost_lines += pol.landing_costs |
2315 | + result[line.id] = landed_cost_lines |
2316 | + return result |
2317 | + |
2318 | + _columns = { |
2319 | + 'landed_cost_line_ids': fields.one2many( |
2320 | + 'landed.cost.position', |
2321 | + 'purchase_order_id', |
2322 | + 'Landed Costs', |
2323 | + domain=[('purchase_order_line_id', '=', False)]), |
2324 | + 'landed_cost_base_value': fields.function( |
2325 | + _landed_cost_base_value, |
2326 | + digits_compute=dp.get_precision('Account'), |
2327 | + string='Landed Costs Base Value'), |
2328 | + 'landed_cost_base_quantity': fields.function( |
2329 | + _landed_cost_base_quantity, |
2330 | + digits_compute=dp.get_precision('Account'), |
2331 | + string='Landed Costs Base Quantity'), |
2332 | + 'landing_cost_lines': fields.function( |
2333 | + _landing_cost_lines, |
2334 | + digits_compute=dp.get_precision('Account'), |
2335 | + string='Landing Cost Lines'), |
2336 | + 'landed_cost': fields.function( |
2337 | + _landed_cost, |
2338 | + digits_compute=dp.get_precision('Account'), |
2339 | + string='Landed Costs Total Untaxed'), |
2340 | + 'quantity_total': fields.function( |
2341 | + _quantity_total, |
2342 | + digits_compute=dp.get_precision('Product UoM'), |
2343 | + string='Total Quantity'), |
2344 | + } |
2345 | + |
2346 | + def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, |
2347 | + context=None): |
2348 | + """ Here, the technical price_unit field will store the purchase |
2349 | + price + landed cost. The original purchase price is stored in |
2350 | + price_unit_net new field to keep record of it. |
2351 | + |
2352 | + """ |
2353 | + res = super(purchase_order,self)._prepare_order_line_move( |
2354 | + cr, uid, order, order_line, picking_id, context=context) |
2355 | + res['price_unit_net'] = res['price_unit'] |
2356 | + try: |
2357 | + res['price_unit'] = order_line.landed_costs / order_line.product_qty |
2358 | + except ZeroDivisionError: |
2359 | + pass |
2360 | + return res |
2361 | + |
2362 | + def _prepare_landed_cost_inv_line(self, cr, uid, account_id, inv_id, |
2363 | + landed_cost, context=None): |
2364 | + """ Collects require data from landed cost position that is used to |
2365 | + create invoice line for that particular position. |
2366 | + |
2367 | + If it comes from a PO line and Distribution type is per unit |
2368 | + the quantity of the invoice is the PO line quantity |
2369 | + |
2370 | + :param account_id: Expense account. |
2371 | + :param inv_id: Related invoice. |
2372 | + :param browse_record landed_cost: Landed cost position browse record |
2373 | + :return: Value for fields of invoice lines. |
2374 | + :rtype: dict |
2375 | + |
2376 | + """ |
2377 | + qty = 1.0 |
2378 | + if (landed_cost.purchase_order_line_id and |
2379 | + landed_cost.distribution_type_id.landed_cost_type == 'per_unit'): |
2380 | + qty = landed_cost.purchase_order_line_id.product_qty |
2381 | + line_tax_ids = [x.id for x in landed_cost.product_id.supplier_taxes_id] |
2382 | + return { |
2383 | + 'name': landed_cost.product_id.name, |
2384 | + 'account_id': account_id, |
2385 | + 'invoice_id' : inv_id, |
2386 | + 'price_unit': landed_cost.amount or 0.0, |
2387 | + 'quantity': qty, |
2388 | + 'product_id': landed_cost.product_id.id or False, |
2389 | + 'invoice_line_tax_id': [(6, 0, line_tax_ids)], |
2390 | + } |
2391 | + |
2392 | + def _prepare_landed_cost_inv(self, cr, uid, landed_cost, context=None): |
2393 | + """ Collects require data from landed cost position that is used to |
2394 | + create invoice for that particular position. |
2395 | + |
2396 | + Note that _landed can come from a line or at whole PO level. |
2397 | + |
2398 | + :param browse_record landed_cost: Landed cost position browse record |
2399 | + :return: Value for fields of invoice. |
2400 | + :rtype: dict |
2401 | + |
2402 | + """ |
2403 | + po = (landed_cost.purchase_order_id or |
2404 | + landed_cost.purchase_order_line_id.order_id) |
2405 | + currency_id = landed_cost.purchase_order_id.pricelist_id.currency_id.id |
2406 | + fiscal_position = po.fiscal_position or False |
2407 | + journal_obj = self.pool.get('account.journal') |
2408 | + journal_ids = journal_obj.search( |
2409 | + cr, uid, |
2410 | + [('type', '=', 'purchase'), |
2411 | + ('company_id', '=', po.company_id.id)], |
2412 | + limit=1) |
2413 | + if not journal_ids: |
2414 | + raise orm.except_orm( |
2415 | + _('Error!'), |
2416 | + _('Define purchase journal for this company: "%s" (id: %d).') |
2417 | + % (po.company_id.name, po.company_id.id)) |
2418 | + return { |
2419 | + 'currency_id': currency_id, |
2420 | + 'partner_id': landed_cost.partner_id.id, |
2421 | + 'account_id': landed_cost.partner_id.property_account_payable.id, |
2422 | + 'type': 'in_invoice', |
2423 | + 'origin': po.name, |
2424 | + 'fiscal_position': fiscal_position, |
2425 | + 'company_id': po.company_id.id, |
2426 | + 'journal_id': len(journal_ids) and journal_ids[0] or False, |
2427 | + } |
2428 | + |
2429 | + def _generate_invoice_from_landed_cost(self, cr, uid, landed_cost, |
2430 | + context=None): |
2431 | + """ Generate an invoice from order landed costs (means generic |
2432 | + costs to a whole PO) or from a line landed costs. |
2433 | + |
2434 | + """ |
2435 | + invoice_obj = self.pool.get('account.invoice') |
2436 | + invoice_line_obj = self.pool.get('account.invoice.line') |
2437 | + prod_obj = self.pool.get('product.product') |
2438 | + po = (landed_cost.purchase_order_id or |
2439 | + landed_cost.purchase_order_line_id.order_id) |
2440 | + vals_inv = self._prepare_landed_cost_inv(cr, uid, landed_cost, |
2441 | + context=context) |
2442 | + inv_id = invoice_obj.create(cr, uid, vals_inv, context=context) |
2443 | + fiscal_position = (po.fiscal_position or False) |
2444 | + exp_account_id = prod_obj._choose_exp_account_from( |
2445 | + cr, uid, |
2446 | + landed_cost.product_id, |
2447 | + fiscal_position=fiscal_position, |
2448 | + context=context |
2449 | + ) |
2450 | + vals_line = self._prepare_landed_cost_inv_line( |
2451 | + cr, uid, exp_account_id, inv_id, |
2452 | + landed_cost, context=context |
2453 | + ) |
2454 | + inv_line_id = invoice_line_obj.create(cr, uid, vals_line, |
2455 | + context=context) |
2456 | + return inv_id |
2457 | + |
2458 | + def wkf_approve_order(self, cr, uid, ids, context=None): |
2459 | + """ On PO approval, generate all invoices for all landed cost position. |
2460 | + |
2461 | + Remember that only landed cost position with the checkbox |
2462 | + generate_invoice ticked are generated. |
2463 | + |
2464 | + """ |
2465 | + res = super(purchase_order,self).wkf_approve_order(cr, uid, ids, |
2466 | + context=context) |
2467 | + for order in self.browse(cr, uid, ids, context=context): |
2468 | + invoice_ids = [] |
2469 | + for order_cost in order.landed_cost_line_ids: |
2470 | + if order_cost.generate_invoice: |
2471 | + inv_id = self._generate_invoice_from_landed_cost( |
2472 | + cr, uid, order_cost, context=context) |
2473 | + invoice_ids.append(inv_id) |
2474 | + for po_line in order.order_line: |
2475 | + for line_cost in po_line.landed_cost_line_ids: |
2476 | + inv_id = self._generate_invoice_from_landed_cost( |
2477 | + cr, uid, line_cost, context=context) |
2478 | + invoice_ids.append(inv_id) |
2479 | + # Link this new invoice to related purchase order |
2480 | + # 4 in that list is "Add" mode in a many2many used here because |
2481 | + # the call to super() already add the main invoice |
2482 | + if invoice_ids: |
2483 | + commands = [(4, invoice_id) for invoice_id in invoice_ids] |
2484 | + order.write({'invoice_ids': commands}, context=context) |
2485 | + return res |
2486 | |
2487 | === added file 'purchase_landed_costs/purchase_landed_costs_data.yml' |
2488 | --- purchase_landed_costs/purchase_landed_costs_data.yml 1970-01-01 00:00:00 +0000 |
2489 | +++ purchase_landed_costs/purchase_landed_costs_data.yml 2014-06-19 02:29:14 +0000 |
2490 | @@ -0,0 +1,20 @@ |
2491 | +- |
2492 | + !record {model: landed.cost.distribution.type, id: dist_unit}: |
2493 | + name: Distributed by Quantity |
2494 | + landed_cost_type: per_unit |
2495 | + apply_on: order |
2496 | +- |
2497 | + !record {model: landed.cost.distribution.type, id: dist_value}: |
2498 | + name: Distributed by Value |
2499 | + landed_cost_type: value |
2500 | + apply_on: order |
2501 | +- |
2502 | + !record {model: landed.cost.distribution.type, id: per_unit}: |
2503 | + name: Multiplied by Quantity |
2504 | + landed_cost_type: per_unit |
2505 | + apply_on: line |
2506 | +- |
2507 | + !record {model: landed.cost.distribution.type, id: value}: |
2508 | + name: Simple Value |
2509 | + landed_cost_type: value |
2510 | + apply_on: line |
2511 | \ No newline at end of file |
2512 | |
2513 | === added file 'purchase_landed_costs/purchase_view.xml' |
2514 | --- purchase_landed_costs/purchase_view.xml 1970-01-01 00:00:00 +0000 |
2515 | +++ purchase_landed_costs/purchase_view.xml 2014-06-19 02:29:14 +0000 |
2516 | @@ -0,0 +1,213 @@ |
2517 | +<?xml version="1.0" encoding="UTF-8"?> |
2518 | +<openerp> |
2519 | + <data> |
2520 | + |
2521 | + <record model="ir.ui.view" id="c2c_landed_cost_tree"> |
2522 | + <field name="name">c2clanded.cost.tree</field> |
2523 | + <field name="model">landed.cost.position</field> |
2524 | + <field name="priority">1</field> |
2525 | + <field name="type">tree</field> |
2526 | + <field name="arch" type="xml"> |
2527 | + <tree string="Landing Costs" editable="bottom"> |
2528 | + <field name="generate_invoice"/> |
2529 | + <field name="product_id" context="{'landed_cost_type':'per_unit'}" on_change="onchange_product_id(product_id)"/> |
2530 | + <field name="account_id" invisible="1"/> |
2531 | + <field name="partner_id"/> |
2532 | + <field name="amount"/> |
2533 | + <field name="distribution_type_id" domain="[('apply_on','=','line')]" widget="selection"/> |
2534 | + </tree> |
2535 | + </field> |
2536 | + </record> |
2537 | + |
2538 | + <record model="ir.ui.view" id="c2c_landed_cost_form"> |
2539 | + <field name="name">c2clanded.cost.form</field> |
2540 | + <field name="model">landed.cost.position</field> |
2541 | + <field name="priority">1</field> |
2542 | + <field name="type">form</field> |
2543 | + <field name="arch" type="xml"> |
2544 | + <form string="Landing Costs"> |
2545 | + <field name="generate_invoice"/> |
2546 | + <field name="product_id" on_change="onchange_product_id(product_id)"/> |
2547 | + <field name="account_id" invisible="1"/> |
2548 | + <field name="partner_id"/> |
2549 | + <field name="amount"/> |
2550 | + <field name="distribution_type_id" domain="[('apply_on','=','line')]" widget="selection"/> |
2551 | + </form> |
2552 | + </field> |
2553 | + </record> |
2554 | + |
2555 | + |
2556 | + <!-- ****************** |
2557 | + Landed cost definition in product form |
2558 | + ******************--> |
2559 | + <record model="ir.ui.view" id="c2c_product_landed_cost_view"> |
2560 | + <field name="name">c2c_product.landed.cost.view</field> |
2561 | + <field name="model">product.product</field> |
2562 | + <field name="inherit_id" ref="product.product_normal_form_view"/> |
2563 | + <field name="type">form</field> |
2564 | + <field name="arch" type="xml"> |
2565 | + <field name="description" position="before"> |
2566 | + <group> |
2567 | + <field name="landed_cost_type" groups="purchase.group_purchase_user"/> |
2568 | + </group> |
2569 | + </field> |
2570 | + </field> |
2571 | + </record> |
2572 | + |
2573 | + <!-- Analysis (placed before the view due to the action in c2c_purchase_order_landed_cost_view --> |
2574 | + <record model="ir.ui.view" id="c2c_landed_cost_tree_stat"> |
2575 | + <field name="name">c2clanded.cost.tree</field> |
2576 | + <field name="model">landed.cost.position</field> |
2577 | + <field name="priority">20</field> |
2578 | + <field name="type">tree</field> |
2579 | + <field name="arch" type="xml"> |
2580 | + <tree string="Landing Costs" create="false"> |
2581 | + <field name="date_po"/> |
2582 | + <field name="product_id"/> |
2583 | + <field name="account_id" /> |
2584 | + <field name="partner_id"/> |
2585 | + <field name="amount_total" sum="Total amount"/> |
2586 | + <field name="amount_total_comp_currency" sum="Total amount"/> |
2587 | + <field name="distribution_type_id" widget="selection"/> |
2588 | + <field name="purchase_order_id"/> |
2589 | + <field name="purchase_order_line_id"/> |
2590 | + </tree> |
2591 | + </field> |
2592 | + </record> |
2593 | + |
2594 | + <record id="act_po_2_landed_costs" model="ir.actions.act_window"> |
2595 | + <field name="name">Related Landed Costs</field> |
2596 | + <field name="res_model">landed.cost.position</field> |
2597 | + <field name="view_type">form</field> |
2598 | + <field name="view_mode">tree,form</field> |
2599 | + <field name="context">{'search_default_purchase_order_id': active_id}</field> |
2600 | + <field name="view_id" ref="c2c_landed_cost_tree_stat"></field> |
2601 | + </record> |
2602 | + |
2603 | + <!-- Landed costs Purchase Form--> |
2604 | + <record model="ir.ui.view" id="c2c_purchase_order_landed_cost_view"> |
2605 | + <field name="name">c2c_purchase.order.landed.cost.form.view</field> |
2606 | + <field name="model">purchase.order</field> |
2607 | + <field name="inherit_id" ref="purchase.purchase_order_form"/> |
2608 | + <field name="arch" type="xml"> |
2609 | + <data> |
2610 | + <field name="price_subtotal" position="after"> |
2611 | + <field name="landing_costs" invisible="1"/> |
2612 | + <field name="landing_costs_order"/> |
2613 | + <field name="landed_costs"/> |
2614 | + </field> |
2615 | + <notebook position="inside"> |
2616 | + <page string="Landing Costs" attrs="{'readonly':[('state','=','done')]}"> |
2617 | + <group> |
2618 | + <group> |
2619 | + <field name="quantity_total"/> |
2620 | + <field name="landed_cost_base_quantity" /> |
2621 | + <field name="landed_cost_base_value" /> |
2622 | + </group> |
2623 | + <group> |
2624 | + <field name="landing_cost_lines"/> |
2625 | + <field name="landed_cost"/> |
2626 | + <button name="%(act_po_2_landed_costs)d" type="action" |
2627 | + string="Open All Landed costs"/> |
2628 | + </group> |
2629 | + </group> |
2630 | + <field name="landed_cost_line_ids" colspan="4" nolabel="1" context="{'landed_cost_type':'per_unit'}" widget="one2many_list"> |
2631 | + <tree string="Landing Costs" editable="bottom"> |
2632 | + <field name="generate_invoice"/> |
2633 | + <field name="product_id" on_change="onchange_product_id(product_id,parent.id)"/> |
2634 | + <field name="account_id" invisible="1"/> |
2635 | + <field name="partner_id"/> |
2636 | + <field name="amount"/> |
2637 | + <field name="distribution_type_id" domain="[('apply_on','=','order')]" widget="selection"/> |
2638 | + </tree> |
2639 | + </field> |
2640 | + </page> |
2641 | + </notebook> |
2642 | + </data> |
2643 | + </field> |
2644 | + </record> |
2645 | + |
2646 | + <!-- inherited view to make the order lines list in the form non-editable--> |
2647 | + <record id="view_order_form_editable_list" model="ir.ui.view"> |
2648 | + <field name="name">purchase.order.landed.cost.form.view.form</field> |
2649 | + <field name="model">purchase.order</field> |
2650 | + <field name="inherit_id" ref="purchase.purchase_order_form"/> |
2651 | + <field name="arch" type="xml"> |
2652 | + <xpath expr="//field[@name='order_line']/tree" position="attributes"> |
2653 | + <attribute name="editable">True</attribute> |
2654 | + </xpath> |
2655 | + </field> |
2656 | + </record> |
2657 | + |
2658 | + <!-- Landed costs Purchase Line Form--> |
2659 | + <record model="ir.ui.view" id="purchase_oder_line_landed_cost_view"> |
2660 | + <field name="name">purchase.oder.line.landed.cost.view</field> |
2661 | + <field name="model">purchase.order.line</field> |
2662 | + <field name="inherit_id" ref="purchase.purchase_order_line_form"/> |
2663 | + <field name="type">form</field> |
2664 | + <field name="arch" type="xml"> |
2665 | + <notebook position="inside"> |
2666 | + <page string="Landing Costs" > |
2667 | + <group colspan="2" col="2"> |
2668 | + <field name="landing_costs"/> |
2669 | + <field name="landing_costs_order"/> |
2670 | + <field name="landed_costs"/> |
2671 | + <field name="landed_cost_line_ids" colspan="4" nolabel="1" widget="one2many_list"/> |
2672 | + </group> |
2673 | + </page> |
2674 | + </notebook> |
2675 | + </field> |
2676 | + </record> |
2677 | + |
2678 | + <record id="view_landed_cost_search" model="ir.ui.view"> |
2679 | + <field name="name">landed.cost.position.search</field> |
2680 | + <field name="model">landed.cost.position</field> |
2681 | + <field name="arch" type="xml"> |
2682 | + <search string="Purchase Orders"> |
2683 | + <field name="date_po"/> |
2684 | + <field name="partner_id"/> |
2685 | + <field name="account_id"/> |
2686 | + <field name="product_id"/> |
2687 | + <field name="distribution_type_id"/> |
2688 | + <field name="purchase_order_id"/> |
2689 | + <field name="purchase_order_line_id"/> |
2690 | + <newline/> |
2691 | + <group expand="1" string="Group By..."> |
2692 | + <filter string="Date" name="group_date_po" icon="terp-personal" context="{'group_by':'date_po'}"/> |
2693 | + <filter string="Supplier" name="group_partner_id" icon="terp-personal" context="{'group_by':'partner_id'}"/> |
2694 | + <filter string="Account" name="group_account_id" icon="terp-stock_symbol-selection" context="{'group_by':'account_id'}"/> |
2695 | + <filter string="Landed Cost Name" name="group_product_id" icon="terp-accessories-archiver" context="{'group_by':'product_id'}"/> |
2696 | + <filter string="Distribution Type" name="distribution_type_id" icon="terp-accessories-archiver" context="{'group_by':'distribution_type_id'}"/> |
2697 | + <filter string="Purchase" icon="terp-stock_effects-object-colorize" context="{'group_by':'purchase_order_id'}"/> |
2698 | + <filter string="Purchase Line" icon="terp-stock_effects-object-colorize" context="{'group_by':'purchase_order_line_id'}"/> |
2699 | + </group> |
2700 | + </search> |
2701 | + </field> |
2702 | + </record> |
2703 | + |
2704 | + <record model="ir.ui.view" id="landed_cost_position_graph"> |
2705 | + <field name="name">landed.cost.position.graph</field> |
2706 | + <field name="model">landed.cost.position</field> |
2707 | + <field name="type">graph</field> |
2708 | + <field name="arch" type="xml"> |
2709 | + <graph string="Landed Costs" type="bar"> |
2710 | + <field name="account_id"/> |
2711 | + <field name="amount_total_comp_currency" operator="+"/> |
2712 | + </graph> |
2713 | + </field> |
2714 | + </record> |
2715 | + |
2716 | + <record id="action_landed_cost_report_all" model="ir.actions.act_window"> |
2717 | + <field name="name">Landed Costs Analysis</field> |
2718 | + <field name="res_model">landed.cost.position</field> |
2719 | + <field name="view_type">form</field> |
2720 | + <field name="view_mode">tree,graph</field> |
2721 | + <field name="view_id" ref="c2c_landed_cost_tree_stat"></field> |
2722 | + <field name="context">{'search_default_group_account_id':1,'search_default_group_product_id': 1,'group_by':[]}</field> |
2723 | + <field name="help">Landed cost Analysis allows you to easily check and analyse your estimated landed costs.</field> |
2724 | + </record> |
2725 | + <menuitem action="action_landed_cost_report_all" id="menu_action_landed_cost_report_all" parent="base.next_id_73" sequence="3"/> |
2726 | + |
2727 | + |
2728 | + </data> |
2729 | +</openerp> |
2730 | |
2731 | === added directory 'purchase_landed_costs/security' |
2732 | === added file 'purchase_landed_costs/security/ir.model.access.csv' |
2733 | --- purchase_landed_costs/security/ir.model.access.csv 1970-01-01 00:00:00 +0000 |
2734 | +++ purchase_landed_costs/security/ir.model.access.csv 2014-06-19 02:29:14 +0000 |
2735 | @@ -0,0 +1,5 @@ |
2736 | +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" |
2737 | +access_landed_cost,landed.cost.position,model_landed_cost_position,purchase.group_purchase_user,1,1,1,1 |
2738 | +access_landed_cost_manager,landed.cost.position,model_landed_cost_position,purchase.group_purchase_manager,1,1,1,1 |
2739 | +access_landed_cost_distribution_type_user,landed.cost.distribution user,model_landed_cost_distribution_type,purchase.group_purchase_user,1,0,0,0 |
2740 | +access_landed_cost_distribution_type_manager,landed.cost.distribution manager.type,model_landed_cost_distribution_type,purchase.group_purchase_manager,1,1,1,1 |
2741 | |
2742 | === added file 'purchase_landed_costs/security/landed_cost_security.xml' |
2743 | --- purchase_landed_costs/security/landed_cost_security.xml 1970-01-01 00:00:00 +0000 |
2744 | +++ purchase_landed_costs/security/landed_cost_security.xml 2014-06-19 02:29:14 +0000 |
2745 | @@ -0,0 +1,13 @@ |
2746 | +<?xml version="1.0" encoding="utf-8"?> |
2747 | +<openerp> |
2748 | + |
2749 | +<data noupdate="1"> |
2750 | + <record id="landed_costs_comp_rule" model="ir.rule"> |
2751 | + <field name="name">Landed Costs</field> |
2752 | + <field name="model_id" ref="model_landed_cost_position"/> |
2753 | + <field name="global" eval="True"/> |
2754 | + <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field> |
2755 | + </record> |
2756 | +</data> |
2757 | + |
2758 | +</openerp> |
2759 | |
2760 | === added file 'purchase_landed_costs/stock.py' |
2761 | --- purchase_landed_costs/stock.py 1970-01-01 00:00:00 +0000 |
2762 | +++ purchase_landed_costs/stock.py 2014-06-19 02:29:14 +0000 |
2763 | @@ -0,0 +1,71 @@ |
2764 | +# -*- coding: utf-8 -*- |
2765 | +############################################################################## |
2766 | +# |
2767 | +# OpenERP, Open Source Management Solution |
2768 | +# Copyright (C) 2010-2013 Camptocamp (<http://www.camptocamp.com>) |
2769 | +# Authors: Ferdinand Gasauer, Joel Grand-Guillaume |
2770 | +# |
2771 | +# This program is free software: you can redistribute it and/or modify |
2772 | +# it under the terms of the GNU Affero General Public License as |
2773 | +# published by the Free Software Foundation, either version 3 of the |
2774 | +# License, or (at your option) any later version. |
2775 | +# |
2776 | +# This program is distributed in the hope that it will be useful, |
2777 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2778 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2779 | +# GNU Affero General Public License for more details. |
2780 | +# |
2781 | +# You should have received a copy of the GNU Affero General Public License |
2782 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2783 | +# |
2784 | +############################################################################## |
2785 | +from openerp.osv import orm, fields |
2786 | +import openerp.addons.decimal_precision as dp |
2787 | +import logging |
2788 | + |
2789 | +_logger = logging.getLogger(__name__) |
2790 | + |
2791 | + |
2792 | +class stock_move(orm.Model): |
2793 | + _inherit = "stock.move" |
2794 | + |
2795 | + _columns = { |
2796 | + 'price_unit_net' : fields.float( |
2797 | + 'Purchase Price', |
2798 | + digits_compute=dp.get_precision('Account'), |
2799 | + help="This is the net purchase price, without landed cost " |
2800 | + "as the price include landed price has been stored in " |
2801 | + "price_unit field"), |
2802 | + } |
2803 | + |
2804 | + |
2805 | +class stock_partial_picking(orm.TransientModel): |
2806 | + _inherit = "stock.partial.picking" |
2807 | + |
2808 | + def _product_cost_for_average_update(self, cr, uid, move): |
2809 | + # Be aware of an OpenERP Bug !! If your price_type |
2810 | + # IS NOT in your comapny currency, AVG price is wrong. |
2811 | + # Currently, the cost on the product form is supposed to be expressed |
2812 | + # in the currency of the company owning the product. OpenERP |
2813 | + # read the average price from price_get method, which |
2814 | + # convert the price to company currency. |
2815 | + # So, in case you have: |
2816 | + # Rate from CHF to EUR 1.2 |
2817 | + # Company in CHF |
2818 | + # Price type in EUR |
2819 | + # Product AVG price = 10.- |
2820 | + # Reception new product with cost 15.- (in CHF in price_unit |
2821 | + # of moves) |
2822 | + # The price_get will return the current average price in CHF of 12.- |
2823 | + # The price computed will be =(12 * qty + 15 * qty') / (qty + qty') |
2824 | + # in CHF. The new cost will be store as is in the procuct |
2825 | + # standard_price instead of converting the result in EUR |
2826 | + # Reference : https://bugs.launchpad.net/ocb-addons/+bug/1238525 |
2827 | + res = super(stock_partial_picking, self)._product_cost_for_average_update(cr, uid, move) |
2828 | + _logger.debug('Before res stock_partial_picking `%s`', res) |
2829 | + # Re-take the cost from the PO line landed_costs field |
2830 | + if move.purchase_line_id: |
2831 | + res['cost'] = (move.purchase_line_id.landed_costs / |
2832 | + move.purchase_line_id.product_qty) |
2833 | + _logger.debug('After res stock_partial_picking `%s`', res) |
2834 | + return res |
2835 | |
2836 | === added directory 'purchase_landed_costs/test' |
2837 | === added file 'purchase_landed_costs/test/landed_costs_based_on_quantity.yml' |
2838 | --- purchase_landed_costs/test/landed_costs_based_on_quantity.yml 1970-01-01 00:00:00 +0000 |
2839 | +++ purchase_landed_costs/test/landed_costs_based_on_quantity.yml 2014-06-19 02:29:14 +0000 |
2840 | @@ -0,0 +1,156 @@ |
2841 | +- |
2842 | + Ensure main company, price_type of standard_price and pricelist are in EUR |
2843 | +- |
2844 | + !record {model: res.company, id: base.main_company}: |
2845 | + currency_id: base.EUR |
2846 | +- |
2847 | + !record {model: product.price.type, id: product.standard_price}: |
2848 | + currency_id: base.EUR |
2849 | +- |
2850 | + !record {model: product.pricelist, id: purchase.list0}: |
2851 | + currency_id: base.EUR |
2852 | +- |
2853 | + Affect the admin user to the main company |
2854 | +- |
2855 | + !record {model: res.users, id: base.user_root}: |
2856 | + company_id: base.main_company |
2857 | +- |
2858 | + Create a stock location for the test |
2859 | +- |
2860 | + !record {model: stock.location, id: location_stock_01}: |
2861 | + name: Stock for PO |
2862 | + usage: internal |
2863 | +- |
2864 | + Create a warehouse for the test |
2865 | +- |
2866 | + !record {model: stock.warehouse, id: wh_stock_01}: |
2867 | + name: Warehouse for PO |
2868 | + lot_output_id: location_stock_01 |
2869 | + lot_stock_id: location_stock_01 |
2870 | + lot_input_id: location_stock_01 |
2871 | + company_id: base.main_company |
2872 | +- |
2873 | + Create a Supplier for PO |
2874 | +- |
2875 | + !record {model: res.partner, id: res_partner_supplier_01}: |
2876 | + name: Supplier 1 |
2877 | + supplier: 1 |
2878 | +- |
2879 | + Create a Supplier for landed cost |
2880 | +- |
2881 | + !record {model: res.partner, id: res_partner_supplier_02}: |
2882 | + name: Supplier 2 |
2883 | + supplier: 1 |
2884 | +- |
2885 | + Create a product with landed type Quantity |
2886 | +- |
2887 | + !record {model: product.product, id: product_product_lcost_01}: |
2888 | + categ_id: product.product_category_1 |
2889 | + cost_method: standard |
2890 | + landed_cost_type: per_unit |
2891 | + name: Transport Poste Express Quantity |
2892 | + standard_price: 50.0 |
2893 | + list_price: 75.0 |
2894 | + type: service |
2895 | + uom_id: product.product_uom_unit |
2896 | + uom_po_id: product.product_uom_unit |
2897 | + volume: 0.0 |
2898 | + warranty: 0.0 |
2899 | + weight: 0.0 |
2900 | + weight_net: 0.0 |
2901 | +- |
2902 | + Create a wine product A with an avg price of 100 |
2903 | +- |
2904 | + !record {model: product.product, id: product_product_a_avg_01}: |
2905 | + categ_id: product.product_category_1 |
2906 | + cost_method: standard |
2907 | + name: Wine A |
2908 | + standard_price: 100.0 |
2909 | + list_price: 150.0 |
2910 | + type: product |
2911 | + cost_method: average |
2912 | + uom_id: product.product_uom_unit |
2913 | + uom_po_id: product.product_uom_unit |
2914 | +- |
2915 | + Create a wine product B with an avg price of 200 |
2916 | +- |
2917 | + !record {model: product.product, id: product_product_b_avg_01}: |
2918 | + categ_id: product.product_category_1 |
2919 | + cost_method: standard |
2920 | + name: Wine B |
2921 | + standard_price: 200.0 |
2922 | + list_price: 250.0 |
2923 | + type: product |
2924 | + cost_method: average |
2925 | + uom_id: product.product_uom_unit |
2926 | + uom_po_id: product.product_uom_unit |
2927 | +- |
2928 | + Create a purchase order with two lines and one landed cost based on quantity |
2929 | +- |
2930 | + !record {model: purchase.order, id: purchase_order_lcost_01}: |
2931 | + partner_id: res_partner_supplier_01 |
2932 | + invoice_method: order |
2933 | + location_id: location_stock_01 |
2934 | + pricelist_id: purchase.list0 |
2935 | + order_line: |
2936 | + - product_id: product_product_a_avg_01 |
2937 | + price_unit: 100 |
2938 | + product_qty: 15.0 |
2939 | + - product_id: product_product_b_avg_01 |
2940 | + price_unit: 200 |
2941 | + product_qty: 5.0 |
2942 | + landed_cost_line_ids: |
2943 | + - product_id: product_product_lcost_01 |
2944 | + amount: 50 |
2945 | + partner_id: res_partner_supplier_02 |
2946 | + generate_invoice: 1 |
2947 | + distribution_type_id: dist_unit |
2948 | +- |
2949 | + Test the landed costs computation by lines |
2950 | +- |
2951 | + !python {model: purchase.order}: | |
2952 | + po = self.browse(cr, uid, ref('purchase_order_lcost_01')) |
2953 | + for line in po.order_line: |
2954 | + # Compute pro-rata of quantity for line |
2955 | + value = (line.product_qty / (15+5)) * 50 |
2956 | + assert line.landing_costs_order == value, "The landing cost based on quantity has not been computed correctly" |
2957 | +- |
2958 | + I confirm the order where invoice control is 'Bases on order'. |
2959 | +- |
2960 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_01} |
2961 | +- |
2962 | + I check that the landed cost invoice and PO one is generated from PO confirmation |
2963 | +- |
2964 | + !python {model: purchase.order}: | |
2965 | + purchase_order = self.browse(cr, uid, ref("purchase_order_lcost_01")) |
2966 | + assert len(purchase_order.invoice_ids) == 2, "2 invoices (PO + landed cost) should have been generated on order confirmation." |
2967 | +- |
2968 | + Reception is ready for process, make it and check moves value |
2969 | +- |
2970 | + !python {model: stock.partial.picking}: | |
2971 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_01")).picking_ids |
2972 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
2973 | + self.do_partial(cr, uid, [partial_id]) |
2974 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
2975 | + for move in picking.move_lines: |
2976 | + if move.product_id.name == 'Wine A': |
2977 | + assert move.price_unit == 102.5,"Technical field price_unit of Wine A stock move should record the landed_costs, not purchase price" |
2978 | + assert move.price_unit_net == 100.0,"Technical field price_unit of Wine A stock move should record the purchase price" |
2979 | + elif move.product_id.name == 'Wine B': |
2980 | + assert move.price_unit == 202.5,"Technical field price_unit of Wine B stock move should record the landed_costs, not purchase price" |
2981 | + assert move.price_unit_net == 200.0,"Technical field price_unit_net of Wine B stock move should record the purchase price" |
2982 | +- |
2983 | + I check that purchase order is shipped. |
2984 | +- |
2985 | + !python {model: purchase.order}: | |
2986 | + assert self.browse(cr, uid, ref("purchase_order_lcost_01")).shipped == True,"Purchase order should be delivered" |
2987 | +- |
2988 | + I check that avg price of products is computed with landed costs |
2989 | +- |
2990 | + !python {model: product.product}: | |
2991 | + # computed as : ((15/20) * 50 + 100 * 15) / 15 |
2992 | + value_a = 102.5 |
2993 | + # computed as : ((5/20) * 50 + 200 * 5) / 5 |
2994 | + value_b = 202.5 |
2995 | + assert self.browse(cr, uid, ref("product_product_a_avg_01")).standard_price == value_a,"Avg price for product Wine A is wrongly computed" |
2996 | + assert self.browse(cr, uid, ref("product_product_b_avg_01")).standard_price == value_b,"Avg price for product Wine B is wrongly computed" |
2997 | |
2998 | === added file 'purchase_landed_costs/test/landed_costs_based_on_value.yml' |
2999 | --- purchase_landed_costs/test/landed_costs_based_on_value.yml 1970-01-01 00:00:00 +0000 |
3000 | +++ purchase_landed_costs/test/landed_costs_based_on_value.yml 2014-06-19 02:29:14 +0000 |
3001 | @@ -0,0 +1,141 @@ |
3002 | +- |
3003 | + Ensure main company, price_type of standard_price and pricelist are in EUR |
3004 | +- |
3005 | + !record {model: res.company, id: base.main_company}: |
3006 | + currency_id: base.EUR |
3007 | +- |
3008 | + !record {model: product.price.type, id: product.standard_price}: |
3009 | + currency_id: base.EUR |
3010 | +- |
3011 | + !record {model: product.pricelist, id: purchase.list0}: |
3012 | + currency_id: base.EUR |
3013 | +- |
3014 | + Affect the admin user to the main company |
3015 | +- |
3016 | + !record {model: res.users, id: base.user_root}: |
3017 | + company_id: base.main_company |
3018 | +- |
3019 | + Create a Supplier for PO |
3020 | +- |
3021 | + !record {model: res.partner, id: res_partner_supplier_03}: |
3022 | + name: Supplier 3 |
3023 | + supplier: 1 |
3024 | +- |
3025 | + Create a Supplier for landed cost |
3026 | +- |
3027 | + !record {model: res.partner, id: res_partner_supplier_04}: |
3028 | + name: Supplier 4 |
3029 | + supplier: 1 |
3030 | +- |
3031 | + Create a product with landed type value |
3032 | +- |
3033 | + !record {model: product.product, id: product_product_lcost_02}: |
3034 | + categ_id: product.product_category_1 |
3035 | + cost_method: standard |
3036 | + landed_cost_type: value |
3037 | + name: Transport Poste Express Value |
3038 | + standard_price: 50.0 |
3039 | + list_price: 75.0 |
3040 | + type: service |
3041 | + uom_id: product.product_uom_unit |
3042 | + uom_po_id: product.product_uom_unit |
3043 | + volume: 0.0 |
3044 | + warranty: 0.0 |
3045 | + weight: 0.0 |
3046 | + weight_net: 0.0 |
3047 | +- |
3048 | + Create a wine product C with an avg price of 100 |
3049 | +- |
3050 | + !record {model: product.product, id: product_product_c_avg_01}: |
3051 | + categ_id: product.product_category_1 |
3052 | + cost_method: standard |
3053 | + name: Wine C |
3054 | + standard_price: 100.0 |
3055 | + list_price: 150.0 |
3056 | + type: product |
3057 | + cost_method: average |
3058 | + uom_id: product.product_uom_unit |
3059 | + uom_po_id: product.product_uom_unit |
3060 | +- |
3061 | + Create a wine product D with an avg price of 200 |
3062 | +- |
3063 | + !record {model: product.product, id: product_product_d_avg_01}: |
3064 | + categ_id: product.product_category_1 |
3065 | + cost_method: standard |
3066 | + name: Wine D |
3067 | + standard_price: 200.0 |
3068 | + list_price: 250.0 |
3069 | + type: product |
3070 | + cost_method: average |
3071 | + uom_id: product.product_uom_unit |
3072 | + uom_po_id: product.product_uom_unit |
3073 | +- |
3074 | + Create a purchase order with two lines and one landed cost based on value |
3075 | +- |
3076 | + !record {model: purchase.order, id: purchase_order_lcost_02}: |
3077 | + partner_id: res_partner_supplier_03 |
3078 | + invoice_method: order |
3079 | + location_id: location_stock_01 |
3080 | + pricelist_id: purchase.list0 |
3081 | + order_line: |
3082 | + - product_id: product_product_c_avg_01 |
3083 | + price_unit: 100 |
3084 | + product_qty: 15.0 |
3085 | + - product_id: product_product_d_avg_01 |
3086 | + price_unit: 200 |
3087 | + product_qty: 5.0 |
3088 | + landed_cost_line_ids: |
3089 | + - product_id: product_product_lcost_02 |
3090 | + amount: 50 |
3091 | + partner_id: res_partner_supplier_04 |
3092 | + generate_invoice: 1 |
3093 | + distribution_type_id: dist_value |
3094 | +- |
3095 | + Test the landed costs computation by lines |
3096 | +- |
3097 | + !python {model: purchase.order}: | |
3098 | + po = self.browse(cr, uid, ref('purchase_order_lcost_02')) |
3099 | + for line in po.order_line: |
3100 | + # Compute pro-rata of value for line |
3101 | + value = (line.price_subtotal / (15 * 100 + 5 * 200)) * 50 |
3102 | + assert line.landing_costs_order == value, "The landing cost based on value has not been computed correctly" |
3103 | +- |
3104 | + I confirm the order where invoice control is 'Bases on order'. |
3105 | +- |
3106 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_02} |
3107 | +- |
3108 | + I check that the landed cost invoice and PO one is generated from PO confirmation |
3109 | +- |
3110 | + !python {model: purchase.order}: | |
3111 | + purchase_order = self.browse(cr, uid, ref("purchase_order_lcost_02")) |
3112 | + assert len(purchase_order.invoice_ids) == 2, "2 invoices (PO + landed cost) should have been generated on order confirmation." |
3113 | +- |
3114 | + Reception is ready for process, make it and check moves value |
3115 | +- |
3116 | + !python {model: stock.partial.picking}: | |
3117 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_02")).picking_ids |
3118 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3119 | + self.do_partial(cr, uid, [partial_id]) |
3120 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3121 | + for move in picking.move_lines: |
3122 | + if move.product_id.name == 'Wine C': |
3123 | + assert move.price_unit == 102.0,"Technical field price_unit of Wine C stock move should record the landed_costs, not purchase price" |
3124 | + assert move.price_unit_net == 100.0,"Technical field price_unit of Wine C stock move should record the purchase price" |
3125 | + elif move.product_id.name == 'Wine D': |
3126 | + assert move.price_unit == 204.0,"Technical field price_unit of Wine D stock move should record the landed_costs, not purchase price" |
3127 | + assert move.price_unit_net == 200.0,"Technical field price_unit_net of Wine D stock move should record the purchase price" |
3128 | +- |
3129 | + I check that purchase order is shipped. |
3130 | +- |
3131 | + !python {model: purchase.order}: | |
3132 | + assert self.browse(cr, uid, ref("purchase_order_lcost_02")).shipped == True,"Purchase order should be delivered" |
3133 | +- |
3134 | + I check that avg price of products is computed with landed costs |
3135 | +- |
3136 | + !python {model: product.product}: | |
3137 | + # computed as : ((15/20) * 50 + 100 * 15) / 15 |
3138 | + value_c = 102.0 |
3139 | + # computed as : ((5/20) * 50 + 200 * 5) / 5 |
3140 | + value_d = 204.0 |
3141 | + assert self.browse(cr, uid, ref("product_product_c_avg_01")).standard_price == value_c,"Avg price for product Wine A is wrongly computed" |
3142 | + assert self.browse(cr, uid, ref("product_product_d_avg_01")).standard_price == value_d,"Avg price for product Wine B is wrongly computed" |
3143 | |
3144 | === added file 'purchase_landed_costs/test/landed_costs_multicurrency_company.yml' |
3145 | --- purchase_landed_costs/test/landed_costs_multicurrency_company.yml 1970-01-01 00:00:00 +0000 |
3146 | +++ purchase_landed_costs/test/landed_costs_multicurrency_company.yml 2014-06-19 02:29:14 +0000 |
3147 | @@ -0,0 +1,220 @@ |
3148 | +- |
3149 | + Setup the Company as CHF (rate 1.3086) while keeping pricelist and price_type as EUR |
3150 | +- |
3151 | + !record {model: res.currency.rate, id: base.rateCHF}: |
3152 | + rate: 1.3086 |
3153 | + currency_id: base.CHF |
3154 | + name: !eval time.strftime('%Y-01-01') |
3155 | +- |
3156 | + !record {model: res.currency.rate, id: base.rateEUR}: |
3157 | + rate: 1.0 |
3158 | + currency_id: base.EUR |
3159 | + name: !eval time.strftime('%Y-01-01') |
3160 | +- |
3161 | + !record {model: res.company, id: base.main_company}: |
3162 | + currency_id: base.CHF |
3163 | +- |
3164 | + !record {model: product.price.type, id: product.standard_price}: |
3165 | + currency_id: base.EUR |
3166 | +- |
3167 | + !record {model: product.pricelist, id: purchase.list0}: |
3168 | + currency_id: base.EUR |
3169 | +- |
3170 | + Affect the admin user to the main company |
3171 | +- |
3172 | + !record {model: res.users, id: base.user_root}: |
3173 | + company_id: base.main_company |
3174 | +- |
3175 | + Create a stock location for the test |
3176 | +- |
3177 | + !record {model: stock.location, id: location_stock_01}: |
3178 | + name: Stock for PO |
3179 | + usage: internal |
3180 | +- |
3181 | + Create a warehouse for the test |
3182 | +- |
3183 | + !record {model: stock.warehouse, id: wh_stock_01}: |
3184 | + name: Warehouse for PO |
3185 | + lot_output_id: location_stock_01 |
3186 | + lot_stock_id: location_stock_01 |
3187 | + lot_input_id: location_stock_01 |
3188 | + company_id: base.main_company |
3189 | +- |
3190 | + Create a Supplier for PO |
3191 | +- |
3192 | + !record {model: res.partner, id: res_partner_supplier_01}: |
3193 | + name: Supplier 1 |
3194 | + supplier: 1 |
3195 | +- |
3196 | + Create a Supplier for landed cost |
3197 | +- |
3198 | + !record {model: res.partner, id: res_partner_supplier_02}: |
3199 | + name: Supplier 2 |
3200 | + supplier: 1 |
3201 | +- |
3202 | + Create a product with landed type Quantity |
3203 | +- |
3204 | + !record {model: product.product, id: product_product_lcost_01}: |
3205 | + categ_id: product.product_category_1 |
3206 | + cost_method: standard |
3207 | + landed_cost_type: per_unit |
3208 | + name: Transport Poste Express Quantity |
3209 | + standard_price: 50.0 |
3210 | + list_price: 75.0 |
3211 | + type: service |
3212 | + uom_id: product.product_uom_unit |
3213 | + uom_po_id: product.product_uom_unit |
3214 | + volume: 0.0 |
3215 | + warranty: 0.0 |
3216 | + weight: 0.0 |
3217 | + weight_net: 0.0 |
3218 | +- |
3219 | + Create a wine product H with an avg price of 100 |
3220 | +- |
3221 | + !record {model: product.product, id: product_product_h_avg_01}: |
3222 | + categ_id: product.product_category_1 |
3223 | + cost_method: standard |
3224 | + name: Wine H |
3225 | + standard_price: 100.0 |
3226 | + list_price: 150.0 |
3227 | + type: product |
3228 | + cost_method: average |
3229 | + uom_id: product.product_uom_unit |
3230 | + uom_po_id: product.product_uom_unit |
3231 | +- |
3232 | + Create a wine product I with an avg price of 200 |
3233 | +- |
3234 | + !record {model: product.product, id: product_product_i_avg_01}: |
3235 | + categ_id: product.product_category_1 |
3236 | + cost_method: standard |
3237 | + name: Wine I |
3238 | + standard_price: 200.0 |
3239 | + list_price: 250.0 |
3240 | + type: product |
3241 | + cost_method: average |
3242 | + uom_id: product.product_uom_unit |
3243 | + uom_po_id: product.product_uom_unit |
3244 | +- |
3245 | + Create a purchase order with two lines and one landed cost based on quantity |
3246 | +- |
3247 | + !record {model: purchase.order, id: purchase_order_lcost_05}: |
3248 | + partner_id: res_partner_supplier_01 |
3249 | + invoice_method: order |
3250 | + location_id: location_stock_01 |
3251 | + pricelist_id: purchase.list0 |
3252 | + order_line: |
3253 | + - product_id: product_product_h_avg_01 |
3254 | + price_unit: 100 |
3255 | + product_qty: 15.0 |
3256 | + - product_id: product_product_i_avg_01 |
3257 | + price_unit: 200 |
3258 | + product_qty: 5.0 |
3259 | + landed_cost_line_ids: |
3260 | + - product_id: product_product_lcost_01 |
3261 | + amount: 50 |
3262 | + partner_id: res_partner_supplier_02 |
3263 | + generate_invoice: 1 |
3264 | + distribution_type_id: dist_unit |
3265 | +- |
3266 | + Test the landed costs computation by lines |
3267 | +- |
3268 | + !python {model: purchase.order}: | |
3269 | + po = self.browse(cr, uid, ref('purchase_order_lcost_05')) |
3270 | + for line in po.order_line: |
3271 | + # Compute pro-rata of quantity for line |
3272 | + value = (line.product_qty / (15+5)) * 50 |
3273 | + assert line.landing_costs_order == value, "The landing cost based on quantity has not been computed correctly" |
3274 | +- |
3275 | + I confirm the order where invoice control is 'Bases on order'. |
3276 | +- |
3277 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_05} |
3278 | +- |
3279 | + I check that the landed cost invoice and PO one is generated from PO confirmation |
3280 | +- |
3281 | + !python {model: purchase.order}: | |
3282 | + purchase_order = self.browse(cr, uid, ref("purchase_order_lcost_05")) |
3283 | + assert len(purchase_order.invoice_ids) == 2, "2 invoices (PO + landed cost) should have been generated on order confirmation." |
3284 | +- |
3285 | + Reception is ready for process, make it and check moves value |
3286 | +- |
3287 | + !python {model: stock.partial.picking}: | |
3288 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_05")).picking_ids |
3289 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3290 | + self.do_partial(cr, uid, [partial_id]) |
3291 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3292 | + for move in picking.move_lines: |
3293 | + if move.product_id.name == 'Wine H': |
3294 | + assert move.price_unit == 102.5,"Technical field price_unit of Wine H stock move should record the landed_costs, not purchase price" |
3295 | + assert move.price_unit_net == 100.0,"Technical field price_unit of Wine H stock move should record the purchase price" |
3296 | + elif move.product_id.name == 'Wine I': |
3297 | + assert move.price_unit == 202.5,"Technical field price_unit of Wine I stock move should record the landed_costs, not purchase price" |
3298 | + assert move.price_unit_net == 200.0,"Technical field price_unit_net of Wine I stock move should record the purchase price" |
3299 | +- |
3300 | + I check that purchase order is shipped. |
3301 | +- |
3302 | + !python {model: purchase.order}: | |
3303 | + assert self.browse(cr, uid, ref("purchase_order_lcost_05")).shipped == True,"Purchase order should be delivered" |
3304 | +- |
3305 | + I check that avg price of products is computed with landed costs |
3306 | +- |
3307 | + !python {model: product.product}: | |
3308 | + # computed as : ((15/20) * 50 + 100 * 15) / 15 |
3309 | + value_a = 102.5 |
3310 | + # computed as : ((5/20) * 50 + 200 * 5) / 5 |
3311 | + value_b = 202.5 |
3312 | + print self.browse(cr, uid, ref("product_product_h_avg_01")).standard_price |
3313 | + print self.browse(cr, uid, ref("product_product_i_avg_01")).standard_price |
3314 | + assert self.browse(cr, uid, ref("product_product_h_avg_01")).standard_price == value_a,"Avg price for product Wine H is wrongly computed" |
3315 | + assert self.browse(cr, uid, ref("product_product_i_avg_01")).standard_price == value_b,"Avg price for product Wine I is wrongly computed" |
3316 | +- |
3317 | + Create a purchase order with two lines |
3318 | +- |
3319 | + !record {model: purchase.order, id: purchase_order_lcost_05bis}: |
3320 | + partner_id: res_partner_supplier_01 |
3321 | + invoice_method: order |
3322 | + location_id: location_stock_01 |
3323 | + pricelist_id: purchase.list0 |
3324 | + order_line: |
3325 | + - product_id: product_product_h_avg_01 |
3326 | + price_unit: 200 |
3327 | + product_qty: 15.0 |
3328 | + - product_id: product_product_i_avg_01 |
3329 | + price_unit: 400 |
3330 | + product_qty: 5.0 |
3331 | +- |
3332 | + I confirm the order where invoice control is 'Bases on order'. |
3333 | +- |
3334 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_05bis} |
3335 | +- |
3336 | + Reception is ready for process, make it and check moves value |
3337 | +- |
3338 | + !python {model: stock.partial.picking}: | |
3339 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_05bis")).picking_ids |
3340 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3341 | + self.do_partial(cr, uid, [partial_id]) |
3342 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3343 | + for move in picking.move_lines: |
3344 | + if move.product_id.name == 'Wine H': |
3345 | + assert move.price_unit == 200.0,"Technical field price_unit of Wine H stock move should record the purchase price" |
3346 | + elif move.product_id.name == 'Wine I': |
3347 | + assert move.price_unit == 400.0,"Technical field price_unit_net of Wine I stock move should record the purchase price" |
3348 | +- |
3349 | + I check that purchase order is shipped. |
3350 | +- |
3351 | + !python {model: purchase.order}: | |
3352 | + assert self.browse(cr, uid, ref("purchase_order_lcost_05")).shipped == True,"Purchase order should be delivered" |
3353 | +- |
3354 | + I check that avg price of products is computed correctly |
3355 | +- |
3356 | + !python {model: product.product}: | |
3357 | + xchg_rate_chf = 1.3086 |
3358 | + # Value in stock in EUR |
3359 | + value_a = 102.5 |
3360 | + value_b = 202.5 |
3361 | + # computed as : (value_a * 15 + (200) * 15) / 30 |
3362 | + value_abis = round((value_a * 15 + (200) * 15) / 30, 2) |
3363 | + # computed as : (value_b * 5 + (400) * 5) / 10 |
3364 | + value_bbis = round((value_b * 5 + (400) * 5) / 10, 2) |
3365 | + |
3366 | + assert self.browse(cr, uid, ref("product_product_h_avg_01")).standard_price == value_abis,"Avg price for product Wine H is wrongly computed" |
3367 | + assert self.browse(cr, uid, ref("product_product_i_avg_01")).standard_price == value_bbis,"Avg price for product Wine I is wrongly computed" |
3368 | |
3369 | === added file 'purchase_landed_costs/test/landed_costs_multicurrency_pricelist.yml' |
3370 | --- purchase_landed_costs/test/landed_costs_multicurrency_pricelist.yml 1970-01-01 00:00:00 +0000 |
3371 | +++ purchase_landed_costs/test/landed_costs_multicurrency_pricelist.yml 2014-06-19 02:29:14 +0000 |
3372 | @@ -0,0 +1,219 @@ |
3373 | +- |
3374 | + Setup the pricelist as CHF (rate 1.3086) while keeping company and price_type as EUR |
3375 | +- |
3376 | + !record {model: res.currency.rate, id: base.rateCHF}: |
3377 | + rate: 1.3086 |
3378 | + currency_id: base.CHF |
3379 | + name: !eval time.strftime('%Y-01-01') |
3380 | +- |
3381 | + !record {model: res.currency.rate, id: base.rateEUR}: |
3382 | + rate: 1.0 |
3383 | + currency_id: base.EUR |
3384 | + name: !eval time.strftime('%Y-01-01') |
3385 | +- |
3386 | + !record {model: res.company, id: base.main_company}: |
3387 | + currency_id: base.EUR |
3388 | +- |
3389 | + !record {model: product.price.type, id: product.standard_price}: |
3390 | + currency_id: base.EUR |
3391 | +- |
3392 | + !record {model: product.pricelist, id: purchase.list0}: |
3393 | + currency_id: base.CHF |
3394 | +- |
3395 | + Affect the admin user to the main company |
3396 | +- |
3397 | + !record {model: res.users, id: base.user_root}: |
3398 | + company_id: base.main_company |
3399 | +- |
3400 | + Create a stock location for the test |
3401 | +- |
3402 | + !record {model: stock.location, id: location_stock_01}: |
3403 | + name: Stock for PO |
3404 | + usage: internal |
3405 | +- |
3406 | + Create a warehouse for the test |
3407 | +- |
3408 | + !record {model: stock.warehouse, id: wh_stock_01}: |
3409 | + name: Warehouse for PO |
3410 | + lot_output_id: location_stock_01 |
3411 | + lot_stock_id: location_stock_01 |
3412 | + lot_input_id: location_stock_01 |
3413 | + company_id: base.main_company |
3414 | +- |
3415 | + Create a Supplier for PO |
3416 | +- |
3417 | + !record {model: res.partner, id: res_partner_supplier_01}: |
3418 | + name: Supplier 1 |
3419 | + supplier: 1 |
3420 | +- |
3421 | + Create a Supplier for landed cost |
3422 | +- |
3423 | + !record {model: res.partner, id: res_partner_supplier_02}: |
3424 | + name: Supplier 2 |
3425 | + supplier: 1 |
3426 | +- |
3427 | + Create a product with landed type Quantity |
3428 | +- |
3429 | + !record {model: product.product, id: product_product_lcost_01}: |
3430 | + categ_id: product.product_category_1 |
3431 | + cost_method: standard |
3432 | + landed_cost_type: per_unit |
3433 | + name: Transport Poste Express Quantity |
3434 | + standard_price: 50.0 |
3435 | + list_price: 75.0 |
3436 | + type: service |
3437 | + uom_id: product.product_uom_unit |
3438 | + uom_po_id: product.product_uom_unit |
3439 | + volume: 0.0 |
3440 | + warranty: 0.0 |
3441 | + weight: 0.0 |
3442 | + weight_net: 0.0 |
3443 | +- |
3444 | + Create a wine product F with an avg price of 100 |
3445 | +- |
3446 | + !record {model: product.product, id: product_product_f_avg_01}: |
3447 | + categ_id: product.product_category_1 |
3448 | + cost_method: standard |
3449 | + name: Wine F |
3450 | + standard_price: 100.0 |
3451 | + list_price: 150.0 |
3452 | + type: product |
3453 | + cost_method: average |
3454 | + uom_id: product.product_uom_unit |
3455 | + uom_po_id: product.product_uom_unit |
3456 | +- |
3457 | + Create a wine product G with an avg price of 200 |
3458 | +- |
3459 | + !record {model: product.product, id: product_product_g_avg_01}: |
3460 | + categ_id: product.product_category_1 |
3461 | + cost_method: standard |
3462 | + name: Wine G |
3463 | + standard_price: 200.0 |
3464 | + list_price: 250.0 |
3465 | + type: product |
3466 | + cost_method: average |
3467 | + uom_id: product.product_uom_unit |
3468 | + uom_po_id: product.product_uom_unit |
3469 | +- |
3470 | + Create a purchase order with two lines and one landed cost based on quantity |
3471 | +- |
3472 | + !record {model: purchase.order, id: purchase_order_lcost_04}: |
3473 | + partner_id: res_partner_supplier_01 |
3474 | + invoice_method: order |
3475 | + location_id: location_stock_01 |
3476 | + pricelist_id: purchase.list0 |
3477 | + order_line: |
3478 | + - product_id: product_product_f_avg_01 |
3479 | + price_unit: 100 |
3480 | + product_qty: 15.0 |
3481 | + - product_id: product_product_g_avg_01 |
3482 | + price_unit: 200 |
3483 | + product_qty: 5.0 |
3484 | + landed_cost_line_ids: |
3485 | + - product_id: product_product_lcost_01 |
3486 | + amount: 50 |
3487 | + partner_id: res_partner_supplier_02 |
3488 | + generate_invoice: 1 |
3489 | + distribution_type_id: dist_unit |
3490 | +- |
3491 | + Test the landed costs computation by lines |
3492 | +- |
3493 | + !python {model: purchase.order}: | |
3494 | + po = self.browse(cr, uid, ref('purchase_order_lcost_04')) |
3495 | + for line in po.order_line: |
3496 | + # Compute pro-rata of quantity for line |
3497 | + value = (line.product_qty / (15+5)) * 50 |
3498 | + assert line.landing_costs_order == value, "The landing cost based on quantity has not been computed correctly" |
3499 | +- |
3500 | + I confirm the order where invoice control is 'Bases on order'. |
3501 | +- |
3502 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_04} |
3503 | +- |
3504 | + I check that the landed cost invoice and PO one is generated from PO confirmation |
3505 | +- |
3506 | + !python {model: purchase.order}: | |
3507 | + purchase_order = self.browse(cr, uid, ref("purchase_order_lcost_04")) |
3508 | + assert len(purchase_order.invoice_ids) == 2, "2 invoices (PO + landed cost) should have been generated on order confirmation." |
3509 | +- |
3510 | + Reception is ready for process, make it and check moves value |
3511 | +- |
3512 | + !python {model: stock.partial.picking}: | |
3513 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_04")).picking_ids |
3514 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3515 | + self.do_partial(cr, uid, [partial_id]) |
3516 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3517 | + for move in picking.move_lines: |
3518 | + if move.product_id.name == 'Wine A': |
3519 | + assert move.price_unit == 102.5,"Technical field price_unit of Wine F stock move should record the landed_costs, not purchase price" |
3520 | + assert move.price_unit_net == 100.0,"Technical field price_unit of Wine F stock move should record the purchase price" |
3521 | + elif move.product_id.name == 'Wine B': |
3522 | + assert move.price_unit == 202.5,"Technical field price_unit of Wine G stock move should record the landed_costs, not purchase price" |
3523 | + assert move.price_unit_net == 200.0,"Technical field price_unit_net of Wine G stock move should record the purchase price" |
3524 | +- |
3525 | + I check that purchase order is shipped. |
3526 | +- |
3527 | + !python {model: purchase.order}: | |
3528 | + assert self.browse(cr, uid, ref("purchase_order_lcost_04")).shipped == True,"Purchase order should be delivered" |
3529 | +- |
3530 | + I check that avg price of products is computed with landed costs |
3531 | +- |
3532 | + !python {model: product.product}: | |
3533 | + xchg_rate_chf = 1.3086 |
3534 | + # computed as : ((15/20) * 50 + 100 * 15) / 15 |
3535 | + value_a = round(102.5 / xchg_rate_chf, 2) |
3536 | + # computed as : ((5/20) * 50 + 200 * 5) / 5 |
3537 | + value_b = round(202.5 / xchg_rate_chf, 2) |
3538 | + assert self.browse(cr, uid, ref("product_product_f_avg_01")).standard_price == value_a,"Avg price for product Wine F is wrongly computed" |
3539 | + assert self.browse(cr, uid, ref("product_product_g_avg_01")).standard_price == value_b,"Avg price for product Wine G is wrongly computed" |
3540 | +- |
3541 | + Create a purchase order with two lines |
3542 | +- |
3543 | + !record {model: purchase.order, id: purchase_order_lcost_04bis}: |
3544 | + partner_id: res_partner_supplier_01 |
3545 | + invoice_method: order |
3546 | + location_id: location_stock_01 |
3547 | + pricelist_id: purchase.list0 |
3548 | + order_line: |
3549 | + - product_id: product_product_f_avg_01 |
3550 | + price_unit: 200 |
3551 | + product_qty: 15.0 |
3552 | + - product_id: product_product_g_avg_01 |
3553 | + price_unit: 400 |
3554 | + product_qty: 5.0 |
3555 | +- |
3556 | + I confirm the order where invoice control is 'Bases on order'. |
3557 | +- |
3558 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_04bis} |
3559 | +- |
3560 | + Reception is ready for process, make it and check moves value |
3561 | +- |
3562 | + !python {model: stock.partial.picking}: | |
3563 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_04bis")).picking_ids |
3564 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3565 | + self.do_partial(cr, uid, [partial_id]) |
3566 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3567 | + for move in picking.move_lines: |
3568 | + if move.product_id.name == 'Wine A': |
3569 | + assert move.price_unit == 200.0,"Technical field price_unit of Wine F stock move should record the purchase price" |
3570 | + elif move.product_id.name == 'Wine B': |
3571 | + assert move.price_unit == 400.0,"Technical field price_unit_net of Wine G stock move should record the purchase price" |
3572 | +- |
3573 | + I check that purchase order is shipped. |
3574 | +- |
3575 | + !python {model: purchase.order}: | |
3576 | + assert self.browse(cr, uid, ref("purchase_order_lcost_04bis")).shipped == True,"Purchase order should be delivered" |
3577 | +- |
3578 | + I check that avg price of products is computed correctly |
3579 | +- |
3580 | + !python {model: product.product}: | |
3581 | + xchg_rate_chf = 1.3086 |
3582 | + # Value in stock in EUR |
3583 | + value_a = round(102.5 / xchg_rate_chf, 2) |
3584 | + value_b = round(202.5 / xchg_rate_chf, 2) |
3585 | + # computed as : (value_a * 15 + (200 / xchg_rate_chf) * 15) / 30 |
3586 | + value_abis = round((value_a * 15 + (200 / xchg_rate_chf) * 15) / 30, 2) #152.84 |
3587 | + # computed as : (value_b * 5 + (400 / xchg_rate_chf) * 5) / 10 |
3588 | + value_bbis = round((value_b * 5 + (400 / xchg_rate_chf) * 5) / 10, 2) |
3589 | + |
3590 | + assert self.browse(cr, uid, ref("product_product_f_avg_01")).standard_price == value_abis,"Avg price for product Wine F is wrongly computed" |
3591 | + assert self.browse(cr, uid, ref("product_product_g_avg_01")).standard_price == value_bbis,"Avg price for product Wine G is wrongly computed" |
3592 | \ No newline at end of file |
3593 | |
3594 | === added file 'purchase_landed_costs/test/landed_costs_multicurrency_pricetype.yml' |
3595 | --- purchase_landed_costs/test/landed_costs_multicurrency_pricetype.yml 1970-01-01 00:00:00 +0000 |
3596 | +++ purchase_landed_costs/test/landed_costs_multicurrency_pricetype.yml 2014-06-19 02:29:14 +0000 |
3597 | @@ -0,0 +1,219 @@ |
3598 | +- |
3599 | + Setup the Company as CHF (rate 1.3086) while keeping pricelist and price_type as EUR |
3600 | +- |
3601 | + !record {model: res.currency.rate, id: base.rateCHF}: |
3602 | + rate: 1.3086 |
3603 | + currency_id: base.CHF |
3604 | + name: !eval time.strftime('%Y-01-01') |
3605 | +- |
3606 | + !record {model: res.currency.rate, id: base.rateEUR}: |
3607 | + rate: 1.0 |
3608 | + currency_id: base.EUR |
3609 | + name: !eval time.strftime('%Y-01-01') |
3610 | +- |
3611 | + !record {model: res.company, id: base.main_company}: |
3612 | + currency_id: base.EUR |
3613 | +- |
3614 | + !record {model: product.price.type, id: product.standard_price}: |
3615 | + currency_id: base.CHF |
3616 | +- |
3617 | + !record {model: product.pricelist, id: purchase.list0}: |
3618 | + currency_id: base.EUR |
3619 | +- |
3620 | + Affect the admin user to the main company |
3621 | +- |
3622 | + !record {model: res.users, id: base.user_root}: |
3623 | + company_id: base.main_company |
3624 | +- |
3625 | + Create a stock location for the test |
3626 | +- |
3627 | + !record {model: stock.location, id: location_stock_01}: |
3628 | + name: Stock for PO |
3629 | + usage: internal |
3630 | +- |
3631 | + Create a warehouse for the test |
3632 | +- |
3633 | + !record {model: stock.warehouse, id: wh_stock_01}: |
3634 | + name: Warehouse for PO |
3635 | + lot_output_id: location_stock_01 |
3636 | + lot_stock_id: location_stock_01 |
3637 | + lot_input_id: location_stock_01 |
3638 | + company_id: base.main_company |
3639 | +- |
3640 | + Create a Supplier for PO |
3641 | +- |
3642 | + !record {model: res.partner, id: res_partner_supplier_01}: |
3643 | + name: Supplier 1 |
3644 | + supplier: 1 |
3645 | +- |
3646 | + Create a Supplier for landed cost |
3647 | +- |
3648 | + !record {model: res.partner, id: res_partner_supplier_02}: |
3649 | + name: Supplier 2 |
3650 | + supplier: 1 |
3651 | +- |
3652 | + Create a product with landed type Quantity |
3653 | +- |
3654 | + !record {model: product.product, id: product_product_lcost_01}: |
3655 | + categ_id: product.product_category_1 |
3656 | + cost_method: standard |
3657 | + landed_cost_type: per_unit |
3658 | + name: Transport Poste Express Quantity |
3659 | + standard_price: 50.0 |
3660 | + list_price: 75.0 |
3661 | + type: service |
3662 | + uom_id: product.product_uom_unit |
3663 | + uom_po_id: product.product_uom_unit |
3664 | + volume: 0.0 |
3665 | + warranty: 0.0 |
3666 | + weight: 0.0 |
3667 | + weight_net: 0.0 |
3668 | +- |
3669 | + Create a wine product J with an avg price of 100 |
3670 | +- |
3671 | + !record {model: product.product, id: product_product_j_avg_01}: |
3672 | + categ_id: product.product_category_1 |
3673 | + cost_method: standard |
3674 | + name: Wine J |
3675 | + standard_price: 100.0 |
3676 | + list_price: 150.0 |
3677 | + type: product |
3678 | + cost_method: average |
3679 | + uom_id: product.product_uom_unit |
3680 | + uom_po_id: product.product_uom_unit |
3681 | +- |
3682 | + Create a wine product K with an avg price of 200 |
3683 | +- |
3684 | + !record {model: product.product, id: product_product_k_avg_01}: |
3685 | + categ_id: product.product_category_1 |
3686 | + cost_method: standard |
3687 | + name: Wine K |
3688 | + standard_price: 200.0 |
3689 | + list_price: 250.0 |
3690 | + type: product |
3691 | + cost_method: average |
3692 | + uom_id: product.product_uom_unit |
3693 | + uom_po_id: product.product_uom_unit |
3694 | +- |
3695 | + Create a purchase order with two lines and one landed cost based on quantity |
3696 | +- |
3697 | + !record {model: purchase.order, id: purchase_order_lcost_06}: |
3698 | + partner_id: res_partner_supplier_01 |
3699 | + invoice_method: order |
3700 | + location_id: location_stock_01 |
3701 | + pricelist_id: purchase.list0 |
3702 | + order_line: |
3703 | + - product_id: product_product_j_avg_01 |
3704 | + price_unit: 100 |
3705 | + product_qty: 15.0 |
3706 | + - product_id: product_product_k_avg_01 |
3707 | + price_unit: 200 |
3708 | + product_qty: 5.0 |
3709 | + landed_cost_line_ids: |
3710 | + - product_id: product_product_lcost_01 |
3711 | + amount: 50 |
3712 | + partner_id: res_partner_supplier_02 |
3713 | + generate_invoice: 1 |
3714 | + distribution_type_id: dist_unit |
3715 | +- |
3716 | + Test the landed costs computation by lines |
3717 | +- |
3718 | + !python {model: purchase.order}: | |
3719 | + po = self.browse(cr, uid, ref('purchase_order_lcost_06')) |
3720 | + for line in po.order_line: |
3721 | + # Compute pro-rata of quantity for line |
3722 | + value = (line.product_qty / (15+5)) * 50 |
3723 | + assert line.landing_costs_order == value, "The landing cost based on quantity has not been computed correctly" |
3724 | +- |
3725 | + I confirm the order where invoice control is 'Bases on order'. |
3726 | +- |
3727 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_06} |
3728 | +- |
3729 | + I check that the landed cost invoice and PO one is generated from PO confirmation |
3730 | +- |
3731 | + !python {model: purchase.order}: | |
3732 | + purchase_order = self.browse(cr, uid, ref("purchase_order_lcost_06")) |
3733 | + assert len(purchase_order.invoice_ids) == 2, "2 invoices (PO + landed cost) should have been generated on order confirmation." |
3734 | +- |
3735 | + Reception is ready for process, make it and check moves value |
3736 | +- |
3737 | + !python {model: stock.partial.picking}: | |
3738 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_06")).picking_ids |
3739 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3740 | + self.do_partial(cr, uid, [partial_id]) |
3741 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3742 | + for move in picking.move_lines: |
3743 | + if move.product_id.name == 'Wine J': |
3744 | + assert move.price_unit == 102.5,"Technical field price_unit of Wine J stock move should record the landed_costs, not purchase price" |
3745 | + assert move.price_unit_net == 100.0,"Technical field price_unit of Wine J stock move should record the purchase price" |
3746 | + elif move.product_id.name == 'Wine K': |
3747 | + assert move.price_unit == 202.5,"Technical field price_unit of Wine K stock move should record the landed_costs, not purchase price" |
3748 | + assert move.price_unit_net == 200.0,"Technical field price_unit_net of Wine K stock move should record the purchase price" |
3749 | +- |
3750 | + I check that purchase order is shipped. |
3751 | +- |
3752 | + !python {model: purchase.order}: | |
3753 | + assert self.browse(cr, uid, ref("purchase_order_lcost_06")).shipped == True,"Purchase order should be delivered" |
3754 | +- |
3755 | + I check that avg price of products is computed with landed costs |
3756 | +- |
3757 | + !python {model: product.product}: | |
3758 | + xchg_rate_chf = 1.3086 |
3759 | + # computed as : ((15/20) * 50 + 100 * 15) / 15 |
3760 | + value_a = round(102.5 * xchg_rate_chf, 2) |
3761 | + # computed as : ((5/20) * 50 + 200 * 5) / 5 |
3762 | + value_b = round(202.5 * xchg_rate_chf, 2) |
3763 | + assert self.browse(cr, uid, ref("product_product_j_avg_01")).standard_price == value_a,"Avg price for product Wine J is wrongly computed" |
3764 | + assert self.browse(cr, uid, ref("product_product_k_avg_01")).standard_price == value_b,"Avg price for product Wine K is wrongly computed" |
3765 | +- |
3766 | + Create a purchase order with two lines |
3767 | +- |
3768 | + !record {model: purchase.order, id: purchase_order_lcost_06bis}: |
3769 | + partner_id: res_partner_supplier_01 |
3770 | + invoice_method: order |
3771 | + location_id: location_stock_01 |
3772 | + pricelist_id: purchase.list0 |
3773 | + order_line: |
3774 | + - product_id: product_product_j_avg_01 |
3775 | + price_unit: 200 |
3776 | + product_qty: 15.0 |
3777 | + - product_id: product_product_k_avg_01 |
3778 | + price_unit: 400 |
3779 | + product_qty: 5.0 |
3780 | +- |
3781 | + I confirm the order where invoice control is 'Bases on order'. |
3782 | +- |
3783 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_06bis} |
3784 | +- |
3785 | + Reception is ready for process, make it and check moves value |
3786 | +- |
3787 | + !python {model: stock.partial.picking}: | |
3788 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_06bis")).picking_ids |
3789 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3790 | + self.do_partial(cr, uid, [partial_id]) |
3791 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3792 | + for move in picking.move_lines: |
3793 | + if move.product_id.name == 'Wine J': |
3794 | + assert move.price_unit == 200.0,"Technical field price_unit of Wine J stock move should record the purchase price" |
3795 | + elif move.product_id.name == 'Wine K': |
3796 | + assert move.price_unit == 400.0,"Technical field price_unit_net of Wine K stock move should record the purchase price" |
3797 | +- |
3798 | + I check that purchase order is shipped. |
3799 | +- |
3800 | + !python {model: purchase.order}: | |
3801 | + assert self.browse(cr, uid, ref("purchase_order_lcost_06bis")).shipped == True,"Purchase order should be delivered" |
3802 | +- |
3803 | + I check that avg price of products is computed with landed costs |
3804 | +- |
3805 | + !python {model: product.product}: | |
3806 | + xchg_rate_chf = 1.3086 |
3807 | + # Value in stock in CHF |
3808 | + value_a = round(102.5 * xchg_rate_chf, 4) |
3809 | + value_b = round(202.5 * xchg_rate_chf, 4) |
3810 | + # computed as : (value_a * 15 + (200 * xchg_rate_chf) * 15) / 30 |
3811 | + value_abis = round((value_a * 15 + (200 * xchg_rate_chf) * 15) / 30, 2) |
3812 | + # computed as : (value_b * 5 + (400 * xchg_rate_chf) * 5) / 10 |
3813 | + value_bbis = round((value_b * 5 + (400 * xchg_rate_chf) * 5) / 10, 2) |
3814 | + |
3815 | + assert self.browse(cr, uid, ref("product_product_j_avg_01")).standard_price == value_abis,"Avg price for product Wine J is wrongly computed" |
3816 | + assert self.browse(cr, uid, ref("product_product_k_avg_01")).standard_price == value_bbis,"Avg price for product Wine K is wrongly computed" |
3817 | |
3818 | === added file 'purchase_landed_costs/test/landed_costs_on_qty_by_line_and_order.yml' |
3819 | --- purchase_landed_costs/test/landed_costs_on_qty_by_line_and_order.yml 1970-01-01 00:00:00 +0000 |
3820 | +++ purchase_landed_costs/test/landed_costs_on_qty_by_line_and_order.yml 2014-06-19 02:29:14 +0000 |
3821 | @@ -0,0 +1,144 @@ |
3822 | +- |
3823 | + Ensure main company, price_type of standard_price and pricelist are in EUR |
3824 | +- |
3825 | + !record {model: res.company, id: base.main_company}: |
3826 | + currency_id: base.EUR |
3827 | +- |
3828 | + !record {model: product.price.type, id: product.standard_price}: |
3829 | + currency_id: base.EUR |
3830 | +- |
3831 | + !record {model: product.pricelist, id: purchase.list0}: |
3832 | + currency_id: base.EUR |
3833 | +- |
3834 | + Affect the admin user to the main company |
3835 | +- |
3836 | + !record {model: res.users, id: base.user_root}: |
3837 | + company_id: base.main_company |
3838 | +- |
3839 | + Create a product with landed type for lines |
3840 | +- |
3841 | + !record {model: product.product, id: product_product_lcost_03}: |
3842 | + categ_id: product.product_category_1 |
3843 | + cost_method: standard |
3844 | + landed_cost_type: per_unit |
3845 | + name: Line fees |
3846 | + standard_price: 20.0 |
3847 | + list_price: 20.0 |
3848 | + type: service |
3849 | + uom_id: product.product_uom_unit |
3850 | + uom_po_id: product.product_uom_unit |
3851 | + volume: 0.0 |
3852 | + warranty: 0.0 |
3853 | + weight: 0.0 |
3854 | + weight_net: 0.0 |
3855 | +- |
3856 | + Create a wine product E with an avg price of 100 |
3857 | +- |
3858 | + !record {model: product.product, id: product_product_e_avg_01}: |
3859 | + categ_id: product.product_category_1 |
3860 | + cost_method: standard |
3861 | + name: Wine E |
3862 | + standard_price: 100.0 |
3863 | + list_price: 150.0 |
3864 | + type: product |
3865 | + cost_method: average |
3866 | + uom_id: product.product_uom_unit |
3867 | + uom_po_id: product.product_uom_unit |
3868 | +- |
3869 | + Create a purchase order with two lines and one landed cost based on quantity |
3870 | + And one landed cost for the first line |
3871 | +- |
3872 | + !record {model: purchase.order, id: purchase_order_lcost_03}: |
3873 | + partner_id: res_partner_supplier_01 |
3874 | + invoice_method: order |
3875 | + location_id: location_stock_01 |
3876 | + pricelist_id: purchase.list0 |
3877 | + order_line: |
3878 | + - product_id: product_product_e_avg_01 |
3879 | + price_unit: 100 |
3880 | + product_qty: 15.0 |
3881 | + landed_cost_line_ids: |
3882 | + - product_id: product_product_lcost_03 |
3883 | + amount: 20 |
3884 | + partner_id: res_partner_supplier_02 |
3885 | + generate_invoice: 1 |
3886 | + distribution_type_id: per_unit |
3887 | + - product_id: product_product_b_avg_01 |
3888 | + price_unit: 200 |
3889 | + product_qty: 5.0 |
3890 | + landed_cost_line_ids: |
3891 | + - product_id: product_product_lcost_01 |
3892 | + amount: 50 |
3893 | + partner_id: res_partner_supplier_02 |
3894 | + generate_invoice: 1 |
3895 | + distribution_type_id: dist_unit |
3896 | +- |
3897 | + Test the landed costs computation for whole order |
3898 | +- |
3899 | + !python {model: purchase.order}: | |
3900 | + po = self.browse(cr, uid, ref('purchase_order_lcost_03')) |
3901 | + for line in po.order_line: |
3902 | + # Compute pro-rata of quantity for line |
3903 | + value = (line.product_qty / (15+5)) * 50 |
3904 | + assert line.landing_costs_order == value, "The landing cost based on quantity has not been computed correctly" |
3905 | +- |
3906 | + Test the landed costs computation of the lines |
3907 | +- |
3908 | + !python {model: purchase.order}: | |
3909 | + po = self.browse(cr, uid, ref('purchase_order_lcost_03')) |
3910 | + for line in po.order_line: |
3911 | + if line.product_id.name == 'Wine E': |
3912 | + # Value for the first line should be equal to landed cost of the line * qty |
3913 | + value_landing = 20 * line.product_qty |
3914 | + # value is landing value + landing cost order + price_subtotal of po line |
3915 | + value_landed = value_landing + (line.product_qty / (15+5)) * 50 + line.price_subtotal |
3916 | + assert line.landing_costs == value_landing, "The landing cost of the line has not been computed correctly" |
3917 | + assert line.landed_costs == value_landed, "The landed costs of the line has not been computed correctly" |
3918 | + elif line.product_id.name == 'Wine B': |
3919 | + # Value for the first line should be equal to landed cost of the line * qty |
3920 | + value_landing = 0 * line.product_qty |
3921 | + # value is landing value + landing cost order + price_subtotal of po line |
3922 | + value_landed = value_landing + (line.product_qty / (15+5)) * 50 + line.price_subtotal |
3923 | + assert line.landing_costs == value_landing, "The landing cost of the line has not been computed correctly" |
3924 | + assert line.landed_costs == value_landed, "The landed costs of the line has not been computed correctly" |
3925 | +- |
3926 | + I confirm the order where invoice control is 'Bases on order'. |
3927 | +- |
3928 | + !workflow {model: purchase.order, action: purchase_confirm, ref: purchase_order_lcost_03} |
3929 | +- |
3930 | + I check that the landed cost invoice and PO one is generated from PO confirmation |
3931 | +- |
3932 | + !python {model: purchase.order}: | |
3933 | + purchase_order = self.browse(cr, uid, ref("purchase_order_lcost_03")) |
3934 | + assert len(purchase_order.invoice_ids) == 3, "3 invoices (PO + landed cost) should have been generated on order confirmation." |
3935 | +- |
3936 | + Reception is ready for process, make it and check moves value |
3937 | +- |
3938 | + !python {model: stock.partial.picking}: | |
3939 | + pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_lcost_03")).picking_ids |
3940 | + partial_id = self.create(cr, uid, {},context={'active_model': 'stock.picking','active_ids': [pick_ids[0].id]}) |
3941 | + self.do_partial(cr, uid, [partial_id]) |
3942 | + picking = self.pool.get('stock.picking').browse(cr, uid, [pick_ids[0].id])[0] |
3943 | + for move in picking.move_lines: |
3944 | + if move.product_id.name == 'Wine E': |
3945 | + assert move.price_unit == 122.5,"Technical field price_unit of Wine E stock move should record the landed_costs, not purchase price" |
3946 | + assert move.price_unit_net == 100.0,"Technical field price_unit of Wine E stock move should record the purchase price" |
3947 | + elif move.product_id.name == 'Wine B': |
3948 | + assert move.price_unit == 202.5,"Technical field price_unit of Wine B stock move should record the landed_costs, not purchase price" |
3949 | + assert move.price_unit_net == 200.0,"Technical field price_unit_net of Wine B stock move should record the purchase price" |
3950 | +- |
3951 | + I check that purchase order is shipped. |
3952 | +- |
3953 | + !python {model: purchase.order}: | |
3954 | + assert self.browse(cr, uid, ref("purchase_order_lcost_03")).shipped == True,"Purchase order should be delivered" |
3955 | +- |
3956 | + I check that avg price of products is computed with landed costs |
3957 | +- |
3958 | + !python {model: product.product}: | |
3959 | + # computed as : ((15/20) * 50 + 100 * 15) / 15 |
3960 | + value_a = 122.5 |
3961 | + # Here this scenario : landed_costs_based_on_quantity set the AVG price to 202.5; 5 PCE in stock |
3962 | + # computed as : ((5 * 202.5) + ( 5 * 202.5)) / 10 |
3963 | + value_b = 202.5 |
3964 | + assert self.browse(cr, uid, ref("product_product_e_avg_01")).standard_price == value_a,"Avg price for product Wine E is wrongly computed" |
3965 | + assert self.browse(cr, uid, ref("product_product_b_avg_01")).standard_price == value_b,"Avg price for product Wine B is wrongly computed" |
3966 | |
3967 | === added directory 'purchase_landed_costs/wizard' |
3968 | === added directory 'purchase_landed_costs_extended' |
3969 | === added file 'purchase_landed_costs_extended/__init__.py' |
3970 | --- purchase_landed_costs_extended/__init__.py 1970-01-01 00:00:00 +0000 |
3971 | +++ purchase_landed_costs_extended/__init__.py 2014-06-19 02:29:14 +0000 |
3972 | @@ -0,0 +1,24 @@ |
3973 | +# -*- coding: utf-8 -*- |
3974 | +############################################################################## |
3975 | +# |
3976 | +# OpenERP, Open Source Management Solution |
3977 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
3978 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
3979 | +# |
3980 | +# This program is free software: you can redistribute it and/or modify |
3981 | +# it under the terms of the GNU Affero General Public License as |
3982 | +# published by the Free Software Foundation, either version 3 of the |
3983 | +# License, or (at your option) any later version. |
3984 | +# |
3985 | +# This program is distributed in the hope that it will be useful, |
3986 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3987 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3988 | +# GNU Affero General Public License for more details. |
3989 | +# |
3990 | +# You should have received a copy of the GNU Affero General Public License |
3991 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3992 | +# |
3993 | +############################################################################## |
3994 | +import purchase |
3995 | +import report |
3996 | +import wizard |
3997 | |
3998 | === added file 'purchase_landed_costs_extended/__openerp__.py' |
3999 | --- purchase_landed_costs_extended/__openerp__.py 1970-01-01 00:00:00 +0000 |
4000 | +++ purchase_landed_costs_extended/__openerp__.py 2014-06-19 02:29:14 +0000 |
4001 | @@ -0,0 +1,39 @@ |
4002 | +# -*- coding: utf-8 -*- |
4003 | +############################################################################## |
4004 | +# |
4005 | +# OpenERP, Open Source Management Solution |
4006 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
4007 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
4008 | +# |
4009 | +# This program is free software: you can redistribute it and/or modify |
4010 | +# it under the terms of the GNU Affero General Public License as |
4011 | +# published by the Free Software Foundation, either version 3 of the |
4012 | +# License, or (at your option) any later version. |
4013 | +# |
4014 | +# This program is distributed in the hope that it will be useful, |
4015 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4016 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4017 | +# GNU Affero General Public License for more details. |
4018 | +# |
4019 | +# You should have received a copy of the GNU Affero General Public License |
4020 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4021 | +# |
4022 | +############################################################################## |
4023 | +{'name': 'Purchase Landed Costs Extended', |
4024 | + 'version': '0.1', |
4025 | + 'category': 'Warehouse Management', |
4026 | + 'depends': ['purchase_landed_costs'], |
4027 | + 'author': 'Elico Corp', |
4028 | + 'license': 'AGPL-3', |
4029 | + 'website': 'https://www.elico-corp.com', |
4030 | + 'description': """ |
4031 | + |
4032 | +""", |
4033 | + 'images': [], |
4034 | + 'demo': [], |
4035 | + 'data': ['purchase_view.xml', |
4036 | + 'report/purchase_report_view.xml', |
4037 | + 'wizard/landed_cost_position_invoice_view.xml', |
4038 | + 'security/ir.model.access.csv'], |
4039 | + 'installable': True, |
4040 | + 'application': False} |
4041 | |
4042 | === added file 'purchase_landed_costs_extended/purchase.py' |
4043 | --- purchase_landed_costs_extended/purchase.py 1970-01-01 00:00:00 +0000 |
4044 | +++ purchase_landed_costs_extended/purchase.py 2014-06-19 02:29:14 +0000 |
4045 | @@ -0,0 +1,167 @@ |
4046 | +# -*- coding: utf-8 -*- |
4047 | +############################################################################## |
4048 | +# |
4049 | +# OpenERP, Open Source Management Solution |
4050 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
4051 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
4052 | +# |
4053 | +# This program is free software: you can redistribute it and/or modify |
4054 | +# it under the terms of the GNU Affero General Public License as |
4055 | +# published by the Free Software Foundation, either version 3 of the |
4056 | +# License, or (at your option) any later version. |
4057 | +# |
4058 | +# This program is distributed in the hope that it will be useful, |
4059 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4060 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4061 | +# GNU Affero General Public License for more details. |
4062 | +# |
4063 | +# You should have received a copy of the GNU Affero General Public License |
4064 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4065 | +# |
4066 | +############################################################################## |
4067 | +from openerp.osv import orm, fields |
4068 | +import openerp.addons.decimal_precision as dp |
4069 | + |
4070 | + |
4071 | +class landed_cost_position(orm.Model): |
4072 | + _inherit = 'landed.cost.position' |
4073 | + |
4074 | + def onchange_partner_id(self, cr, uid, ids, partner_id, context=None): |
4075 | + if not partner_id: |
4076 | + return {} |
4077 | + partner = self.pool.get('res.partner').browse( |
4078 | + cr, uid, partner_id, context=context) |
4079 | + pricelist = partner.property_product_pricelist_purchase |
4080 | + return {'value': {'currency_id': pricelist.currency_id.id}} |
4081 | + |
4082 | + def onchange_amount_currency(self, cr, uid, ids, |
4083 | + amount_currency, currency_id, |
4084 | + date_po, context=None): |
4085 | + assert len(ids) < 2 |
4086 | + parent_currency_id = None |
4087 | + if ids: |
4088 | + landed_cost = self.browse(cr, uid, ids[0], context=context) |
4089 | + parent_currency_id = landed_cost.po_currency_id.id |
4090 | + else: |
4091 | + parent_currency_id = self._default_currency( |
4092 | + cr, uid, context=context) |
4093 | + if not parent_currency_id or not amount_currency or not currency_id: |
4094 | + return {} |
4095 | + cur_obj = self.pool.get('res.currency') |
4096 | + amount = amount_currency |
4097 | + if currency_id != parent_currency_id: |
4098 | + ctx = context.copy() |
4099 | + ctx['date'] = date_po or False |
4100 | + amount = cur_obj.compute(cr, uid, |
4101 | + currency_id, |
4102 | + parent_currency_id, |
4103 | + amount, |
4104 | + context=ctx) |
4105 | + return {'value': {'amount': amount}} |
4106 | + |
4107 | + def _default_currency(self, cr, uid, context=None): |
4108 | + context = context or {} |
4109 | + pricelist_id = context.get('pricelist_id', []) |
4110 | + pricelist = self.pool.get('product.pricelist').read( |
4111 | + cr, uid, pricelist_id, ['currency_id']) |
4112 | + parent_currency_id = None |
4113 | + if pricelist: |
4114 | + parent_currency_id = pricelist['currency_id'][0] |
4115 | + return parent_currency_id |
4116 | + |
4117 | + _columns = { |
4118 | + 'amount_currency': fields.float( |
4119 | + 'Currency Amount', |
4120 | + digits_compute=dp.get_precision('Purchase Price'), |
4121 | + help="Landed cost expressed in Landed Cost line currency"), |
4122 | + 'currency_id': fields.many2one( |
4123 | + 'res.currency', 'Currency'), |
4124 | + 'po_pricelist_id': fields.related( |
4125 | + 'purchase_order_id', 'pricelist_id', |
4126 | + type='many2one', |
4127 | + relation='product.pricelist', |
4128 | + string='PO Pricelist', |
4129 | + store=True, |
4130 | + readonly=True, |
4131 | + help="PO pricelist"), |
4132 | + 'po_currency_id': fields.related( |
4133 | + 'po_pricelist_id', 'currency_id', |
4134 | + type='many2one', |
4135 | + relation='res.currency', |
4136 | + string='PO Currency', |
4137 | + store=True, |
4138 | + readonly=True, |
4139 | + help="PO Currency"), |
4140 | + 'invoice_id': fields.many2one('account.invoice', 'Invoice'), |
4141 | + 'active': fields.boolean('Active') |
4142 | + } |
4143 | + |
4144 | + _defaults = { |
4145 | + 'currency_id': _default_currency, |
4146 | + 'active': True |
4147 | + } |
4148 | + |
4149 | + def open_invoice(self, cr, uid, ids, context=None): |
4150 | + assert len(ids) == 1 |
4151 | + lcp = self.browse(cr, uid, ids[0], context=context) |
4152 | + if not lcp.invoice_id: |
4153 | + return {} |
4154 | + return { |
4155 | + 'type': 'ir.actions.act_window', |
4156 | + 'name': 'Form heading', |
4157 | + 'view_mode': 'form', |
4158 | + 'view_type': 'form', |
4159 | + 'res_model': 'account.invoice', |
4160 | + 'nodestroy': True, |
4161 | + 'res_id': lcp.invoice_id.id, |
4162 | + 'context': context |
4163 | + } |
4164 | + |
4165 | + |
4166 | +class purchase_order(orm.Model): |
4167 | + _inherit = 'purchase.order' |
4168 | + |
4169 | + def _generate_invoice_from_landed_cost(self, cr, uid, landed_cost, |
4170 | + context=None): |
4171 | + if landed_cost.invoice_id: |
4172 | + return landed_cost.invoice_id.id |
4173 | + inv_id = super( |
4174 | + purchase_order, self |
4175 | + )._generate_invoice_from_landed_cost( |
4176 | + cr, uid, landed_cost, context=context) |
4177 | + landed_cost.write({'invoice_id': inv_id}, context=context) |
4178 | + return inv_id |
4179 | + |
4180 | + def _prepare_landed_cost_inv_line(self, cr, uid, account_id, inv_id, |
4181 | + landed_cost, context=None): |
4182 | + res = super(purchase_order, self)._prepare_landed_cost_inv_line( |
4183 | + cr, uid, account_id, inv_id, landed_cost, context=context) |
4184 | + res['price_unit'] = landed_cost.amount_currency |
4185 | + return res |
4186 | + |
4187 | + def _prepare_landed_cost_inv(self, cr, uid, landed_cost, context=None): |
4188 | + res = super(purchase_order, self)._prepare_landed_cost_inv( |
4189 | + cr, uid, landed_cost, context=context) |
4190 | + res['currency_id'] = landed_cost.currency_id.id |
4191 | + return res |
4192 | + |
4193 | + def wkf_approve_order(self, cr, uid, ids, context=None): |
4194 | + """ On PO approval, generate all invoices for all landed cost position. |
4195 | + |
4196 | + Remember that only landed cost position with the checkbox |
4197 | + generate_invoice ticked are generated. |
4198 | + |
4199 | + """ |
4200 | + lcp_pool = self.pool.get('landed.cost.position') |
4201 | + line_ids = [] |
4202 | + for order in self.browse(cr, uid, ids, context=context): |
4203 | + for po_line in order.order_line: |
4204 | + for line_cost in po_line.landed_cost_line_ids: |
4205 | + if not line_cost.generate_invoice or line_cost.invoice_id: |
4206 | + line_ids = line_cost.id |
4207 | + lcp_pool.write(cr, uid, line_ids, {'active': False}) |
4208 | + |
4209 | + res = super(purchase_order, self).wkf_approve_order(cr, uid, ids, |
4210 | + context=context) |
4211 | + lcp_pool.write(cr, uid, line_ids, {'active': True}) |
4212 | + return res |
4213 | |
4214 | === added file 'purchase_landed_costs_extended/purchase_view.xml' |
4215 | --- purchase_landed_costs_extended/purchase_view.xml 1970-01-01 00:00:00 +0000 |
4216 | +++ purchase_landed_costs_extended/purchase_view.xml 2014-06-19 02:29:14 +0000 |
4217 | @@ -0,0 +1,173 @@ |
4218 | +<?xml version="1.0" encoding="UTF-8"?> |
4219 | +<openerp> |
4220 | + <data> |
4221 | + |
4222 | + |
4223 | + |
4224 | + <record id="purchase_order_form" model="ir.ui.view"> |
4225 | + <field name="name">purchase.order.form</field> |
4226 | + <field name="model">purchase.order</field> |
4227 | + <field name="inherit_id" ref="purchase_landed_costs.c2c_purchase_order_landed_cost_view" /> |
4228 | + <field name="arch" type="xml" > |
4229 | + <field name="order_line" position="attributes"> |
4230 | + <attribute name="attrs">{'readonly': [('pricelist_id', '=', False)]}</attribute> |
4231 | + <attribute name="context">{'pricelist_id': pricelist_id}</attribute> |
4232 | + </field> |
4233 | + <xpath expr="//field[@name='order_line']/tree" position="attributes"> |
4234 | + <attribute name="editable">False</attribute> |
4235 | + </xpath> |
4236 | + <field name="landed_cost_line_ids" position="attributes"> |
4237 | + <attribute name="attrs">{'readonly': [('pricelist_id', '=', False)]}</attribute> |
4238 | + <attribute name="context">{'pricelist_id': pricelist_id}</attribute> |
4239 | + </field> |
4240 | + <xpath expr="//field[@name='landed_cost_line_ids']/tree/field[@name='amount']" position="before"> |
4241 | + <field |
4242 | + name="amount_currency" |
4243 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4244 | + /> |
4245 | + <field |
4246 | + name="currency_id" |
4247 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4248 | + /> |
4249 | + </xpath> |
4250 | + <xpath expr="//field[@name='landed_cost_line_ids']/tree/field[@name='amount']" position="after"> |
4251 | + <field name="invoice_id" readonly="True" /> |
4252 | + <button name="open_invoice" type="object" attrs="{'invisible': [('invoice_id', '=', False)]}" string="Invoice" icon="gtk-open" /> |
4253 | + |
4254 | + <field name="date_po" invisible="True" /> |
4255 | + </xpath> |
4256 | + |
4257 | + <xpath expr="//field[@name='landed_cost_line_ids']/tree/field[@name='partner_id']" position="attributes"> |
4258 | + <attribute name="on_change">onchange_partner_id(partner_id)</attribute> |
4259 | + </xpath> |
4260 | + </field> |
4261 | + </record> |
4262 | + |
4263 | + <record id="purchase_order_line_form" model="ir.ui.view"> |
4264 | + <field name="name">purchase.order.line.form</field> |
4265 | + <field name="model">purchase.order.line</field> |
4266 | + <field name="inherit_id" ref="purchase_landed_costs.purchase_oder_line_landed_cost_view" /> |
4267 | + <field name="arch" type="xml" > |
4268 | + <field name="product_id" position="attributes"> |
4269 | + <attribute name="attrs">{'readonly': [('state', '!=', 'draft')]}</attribute> |
4270 | + </field> |
4271 | + <field name="product_qty" position="attributes"> |
4272 | + <attribute name="attrs">{'readonly': [('state', '!=', 'draft')]}</attribute> |
4273 | + </field> |
4274 | + <field name="price_unit" position="attributes"> |
4275 | + <attribute name="attrs">{'readonly': [('state', '!=', 'draft')]}</attribute> |
4276 | + </field> |
4277 | + <field name="taxes_id" position="attributes"> |
4278 | + <attribute name="attrs">{'readonly': [('state', '!=', 'draft')]}</attribute> |
4279 | + </field> |
4280 | + <field name="date_planned" position="attributes"> |
4281 | + <attribute name="attrs">{'readonly': [('state', '!=', 'draft')]}</attribute> |
4282 | + </field> |
4283 | + <field name="name" position="attributes"> |
4284 | + <attribute name="attrs">{'readonly': [('state', '!=', 'draft')]}</attribute> |
4285 | + </field> |
4286 | + <field name="product_id" position="after"> |
4287 | + <field name="state" invisible="True" /> |
4288 | + </field> |
4289 | + <field name="landed_cost_line_ids" position="attributes"> |
4290 | + <attribute name="context">{'pricelist_id': context['pricelist_id']}</attribute> |
4291 | + </field> |
4292 | + </field> |
4293 | + </record> |
4294 | + |
4295 | + <record model="ir.ui.view" id="c2c_landed_cost_tree"> |
4296 | + <field name="name">c2clanded.cost.tree</field> |
4297 | + <field name="model">landed.cost.position</field> |
4298 | + <field name="inherit_id" ref="purchase_landed_costs.c2c_landed_cost_tree" /> |
4299 | + <field name="arch" type="xml"> |
4300 | + <field name="partner_id" position="attributes"> |
4301 | + <attribute name="on_change">onchange_partner_id(partner_id)</attribute> |
4302 | + </field> |
4303 | + <field name="amount" position="before"> |
4304 | + <field |
4305 | + name="amount_currency" |
4306 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4307 | + /> |
4308 | + <field |
4309 | + name="currency_id" |
4310 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4311 | + /> |
4312 | + </field> |
4313 | + <field name="amount" position="after"> |
4314 | + <field name="invoice_id" readonly="True" /> |
4315 | + <button name="open_invoice" type="object" attrs="{'invisible': [('invoice_id', '=', False)]}" string="Invoice" icon="gtk-open" /> |
4316 | + <field name="date_po" invisible="True" /> |
4317 | + </field> |
4318 | + </field> |
4319 | + </record> |
4320 | + |
4321 | + <record model="ir.ui.view" id="c2c_landed_cost_form"> |
4322 | + <field name="name">c2clanded.cost.form</field> |
4323 | + <field name="model">landed.cost.position</field> |
4324 | + <field name="inherit_id" ref="purchase_landed_costs.c2c_landed_cost_form" /> |
4325 | + <field name="arch" type="xml"> |
4326 | + <field name="partner_id" position="attributes"> |
4327 | + <attribute name="on_change">onchange_partner_id(partner_id)</attribute> |
4328 | + </field> |
4329 | + <field name="amount" position="before"> |
4330 | + <field |
4331 | + name="amount_currency" |
4332 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4333 | + /> |
4334 | + <field |
4335 | + name="currency_id" |
4336 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4337 | + /> |
4338 | + </field> |
4339 | + <field name="amount" position="after"> |
4340 | + <field name="invoice_id" readonly="True" /> |
4341 | + <button name="open_invoice" type="object" attrs="{'invisible': [('invoice_id', '=', False)]}" string="Invoice" icon="gtk-open" /> |
4342 | + <field name="date_po" invisible="True" /> |
4343 | + </field> |
4344 | + </field> |
4345 | + </record> |
4346 | + |
4347 | + |
4348 | + |
4349 | + <record model="ir.ui.view" id="landed_cost_tree"> |
4350 | + <field name="name">landed.cost.tree</field> |
4351 | + <field name="model">landed.cost.position</field> |
4352 | + <field name="priority">50</field> |
4353 | + <field name="type">tree</field> |
4354 | + <field name="arch" type="xml"> |
4355 | + <tree string="Landing Costs" editable="bottom" create="false"> |
4356 | + <field name="purchase_order_id"/> |
4357 | + <field name="generate_invoice"/> |
4358 | + <field name="product_id" context="{'landed_cost_type':'per_unit'}" on_change="onchange_product_id(product_id)"/> |
4359 | + <field name="account_id" invisible="1"/> |
4360 | + <field name="partner_id" on_change="onchange_partner_id(partner_id)" /> |
4361 | + <field |
4362 | + name="amount_currency" |
4363 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4364 | + /> |
4365 | + <field |
4366 | + name="currency_id" |
4367 | + on_change="onchange_amount_currency(amount_currency, currency_id, date_po, context)" |
4368 | + /> |
4369 | + <field name="amount"/> |
4370 | + <field name="po_currency_id"/> |
4371 | + <field name="distribution_type_id" domain="[('apply_on','=','line')]" widget="selection"/> |
4372 | + <field name="invoice_id" readonly="True" /> |
4373 | + <button name="open_invoice" type="object" attrs="{'invisible': [('invoice_id', '=', False)]}" string="Invoice" icon="gtk-open" /> |
4374 | + <field name="date_po" invisible="True" /> |
4375 | + </tree> |
4376 | + </field> |
4377 | + </record> |
4378 | + |
4379 | + |
4380 | + <record id="po_landed_costs_extended" model="ir.actions.act_window"> |
4381 | + <field name="name">Landed Costs</field> |
4382 | + <field name="res_model">landed.cost.position</field> |
4383 | + <field name="view_type">form</field> |
4384 | + <field name="view_mode">tree,form</field> |
4385 | + <field name="view_id" ref="landed_cost_tree"></field> |
4386 | + </record> |
4387 | + |
4388 | + <menuitem action="po_landed_costs_extended" id="menu_action_landed_cost" parent="purchase.menu_procurement_management_invoice" sequence="100"/> |
4389 | + </data> |
4390 | +</openerp> |
4391 | |
4392 | === added directory 'purchase_landed_costs_extended/report' |
4393 | === added file 'purchase_landed_costs_extended/report/__init__.py' |
4394 | --- purchase_landed_costs_extended/report/__init__.py 1970-01-01 00:00:00 +0000 |
4395 | +++ purchase_landed_costs_extended/report/__init__.py 2014-06-19 02:29:14 +0000 |
4396 | @@ -0,0 +1,22 @@ |
4397 | +# -*- coding: utf-8 -*- |
4398 | +############################################################################## |
4399 | +# |
4400 | +# OpenERP, Open Source Management Solution |
4401 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
4402 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
4403 | +# |
4404 | +# This program is free software: you can redistribute it and/or modify |
4405 | +# it under the terms of the GNU Affero General Public License as |
4406 | +# published by the Free Software Foundation, either version 3 of the |
4407 | +# License, or (at your option) any later version. |
4408 | +# |
4409 | +# This program is distributed in the hope that it will be useful, |
4410 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4411 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4412 | +# GNU Affero General Public License for more details. |
4413 | +# |
4414 | +# You should have received a copy of the GNU Affero General Public License |
4415 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4416 | +# |
4417 | +############################################################################## |
4418 | +import purchase_report |
4419 | |
4420 | === added file 'purchase_landed_costs_extended/report/purchase_report.py' |
4421 | --- purchase_landed_costs_extended/report/purchase_report.py 1970-01-01 00:00:00 +0000 |
4422 | +++ purchase_landed_costs_extended/report/purchase_report.py 2014-06-19 02:29:14 +0000 |
4423 | @@ -0,0 +1,399 @@ |
4424 | +# -*- coding: utf-8 -*- |
4425 | +############################################################################## |
4426 | +# |
4427 | +# OpenERP, Open Source Management Solution |
4428 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
4429 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
4430 | +# |
4431 | +# This program is free software: you can redistribute it and/or modify |
4432 | +# it under the terms of the GNU Affero General Public License as |
4433 | +# published by the Free Software Foundation, either version 3 of the |
4434 | +# License, or (at your option) any later version. |
4435 | +# |
4436 | +# This program is distributed in the hope that it will be useful, |
4437 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4438 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4439 | +# GNU Affero General Public License for more details. |
4440 | +# |
4441 | +# You should have received a copy of the GNU Affero General Public License |
4442 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4443 | +# |
4444 | +############################################################################## |
4445 | +# |
4446 | +# Please note that these reports are not multi-currency !!! |
4447 | +# |
4448 | +from openerp.osv import fields, orm |
4449 | +from openerp import tools |
4450 | + |
4451 | + |
4452 | +class purchase_landed_cost_report(orm.Model): |
4453 | + _name = "purchase.landed.cost.report" |
4454 | + _description = "Purchases Orders" |
4455 | + _auto = False |
4456 | + _columns = { |
4457 | + 'date': fields.date( |
4458 | + 'Order Date', readonly=True, |
4459 | + help="Date on which this document has been created"), |
4460 | + 'name': fields.char('Year', size=64, required=False, readonly=True), |
4461 | + 'day': fields.char('Day', size=128, readonly=True), |
4462 | + 'state': fields.selection([ |
4463 | + ('draft', 'Request for Quotation'), |
4464 | + ('confirmed', 'Waiting Supplier Ack'), |
4465 | + ('approved', 'Approved'), |
4466 | + ('except_picking', 'Shipping Exception'), |
4467 | + ('except_invoice', 'Invoice Exception'), |
4468 | + ('done', 'Done'), |
4469 | + ('cancel', 'Cancelled')], |
4470 | + 'Order Status', readonly=True), |
4471 | + 'product_id': fields.many2one( |
4472 | + 'product.product', 'Product', readonly=True), |
4473 | + 'warehouse_id': fields.many2one( |
4474 | + 'stock.warehouse', 'Warehouse', readonly=True), |
4475 | + 'location_id': fields.many2one( |
4476 | + 'stock.location', 'Destination', readonly=True), |
4477 | + 'partner_id': fields.many2one( |
4478 | + 'res.partner', 'Supplier', readonly=True), |
4479 | + 'pricelist_id': fields.many2one( |
4480 | + 'product.pricelist', 'Pricelist', readonly=True), |
4481 | + 'date_approve': fields.date( |
4482 | + 'Date Approved', readonly=True), |
4483 | + 'expected_date': fields.date( |
4484 | + 'Expected Date', readonly=True), |
4485 | + 'validator': fields.many2one( |
4486 | + 'res.users', 'Validated By', readonly=True), |
4487 | + 'product_uom': fields.many2one( |
4488 | + 'product.uom', 'Reference Unit of Measure', required=True), |
4489 | + 'company_id': fields.many2one( |
4490 | + 'res.company', 'Company', readonly=True), |
4491 | + 'user_id': fields.many2one( |
4492 | + 'res.users', 'Responsible', readonly=True), |
4493 | + 'quantity': fields.float('Quantity', readonly=True), |
4494 | + 'price_total': fields.float('Total Price', readonly=True), |
4495 | + 'price_average': fields.float( |
4496 | + 'Average Price', readonly=True, |
4497 | + group_operator="avg"), |
4498 | + 'negociation': fields.float( |
4499 | + 'Purchase-Standard Price', readonly=True, |
4500 | + group_operator="avg"), |
4501 | + 'price_standard': fields.float( |
4502 | + 'Products Value', readonly=True, |
4503 | + group_operator="sum"), |
4504 | + 'nbr': fields.integer('# of Lines', readonly=True), |
4505 | + 'month': fields.selection( |
4506 | + [ |
4507 | + ('01', 'January'), ('02', 'February'), ('03', 'March'), |
4508 | + ('04', 'April'), ('05', 'May'), ('06', 'June'), |
4509 | + ('07', 'July'), ('08', 'August'), ('09', 'September'), |
4510 | + ('10', 'October'), ('11', 'November'), ('12', 'December') |
4511 | + ], 'Month', readonly=True), |
4512 | + 'category_id': fields.many2one('product.category', 'Category', |
4513 | + readonly=True), |
4514 | + 'landed_cost_total': fields.float( |
4515 | + 'Landed cost total', readonly=True, |
4516 | + group_operator="sum"), |
4517 | + 'landed_cost_unit': fields.float( |
4518 | + 'Landed cost unit', readonly=True, |
4519 | + group_operator="sum"), |
4520 | + 'purchase_order_id': fields.many2one( |
4521 | + 'purchase.order', 'Purchase Order', readonly=True), |
4522 | + } |
4523 | + _order = 'name desc,price_total desc' |
4524 | + |
4525 | + def init(self, cr): |
4526 | + tools.sql.drop_view_if_exists(cr, 'purchase_landed_cost_report') |
4527 | + cr.execute(""" |
4528 | + CREATE OR REPLACE FUNCTION po_landed_cost_per_unit(int) |
4529 | + RETURNS numeric |
4530 | + AS ' |
4531 | + SELECT sum(subtotal) FROM (SELECT |
4532 | + |
4533 | + ((po_lcp.amount_company_currency |
4534 | + / (SELECT sum(product_qty) FROM |
4535 | + purchase_order_line |
4536 | + WHERE order_id = pol.order_id)) |
4537 | + * pol.product_qty |
4538 | + ) as subtotal |
4539 | + FROM purchase_order_line POL |
4540 | + LEFT JOIN landed_cost_position po_lcp |
4541 | + ON po_lcp.purchase_order_id = pol.order_id |
4542 | + LEFT JOIN landed_cost_distribution_type |
4543 | + po_lcdt |
4544 | + ON po_lcdt.id = po_lcp.distribution_type_id |
4545 | + WHERE pol.id = $1 |
4546 | + AND po_lcp.active = true |
4547 | + AND po_lcp.purchase_order_line_id is null |
4548 | + AND po_lcdt.landed_cost_type = ''per_unit'' |
4549 | + GROUP BY pol.id, po_lcp.id, po_lcdt.id) |
4550 | + as result' |
4551 | + LANGUAGE SQL; |
4552 | + |
4553 | + CREATE OR REPLACE FUNCTION po_landed_cost_per_value(int) |
4554 | + RETURNS numeric |
4555 | + AS ' |
4556 | + SELECT sum(subtotal) |
4557 | + FROM ( |
4558 | + SELECT ( |
4559 | + (po_lcp.amount_company_currency |
4560 | + * (pol.price_unit |
4561 | + * pol.product_qty)) / po.amount_untaxed |
4562 | + ) as subtotal |
4563 | + FROM purchase_order_line POL |
4564 | + LEFT JOIN purchase_order po |
4565 | + ON po.id = pol.order_id |
4566 | + LEFT JOIN landed_cost_position po_lcp |
4567 | + ON po_lcp.purchase_order_id = pol.order_id |
4568 | + LEFT JOIN landed_cost_distribution_type po_lcdt |
4569 | + ON po_lcdt.id = po_lcp.distribution_type_id |
4570 | + WHERE pol.id = $1 |
4571 | + AND po_lcp.active = true |
4572 | + AND po_lcp.purchase_order_line_id is null |
4573 | + AND po_lcdt.landed_cost_type = ''value'' |
4574 | + GROUP BY pol.id, po.id, po_lcp.id, po_lcdt.id) as |
4575 | + result' |
4576 | + LANGUAGE SQL; |
4577 | + |
4578 | + |
4579 | + CREATE OR REPLACE FUNCTION pol_landed_cost_per_unit(int) RETURNS |
4580 | + numeric |
4581 | + AS ' |
4582 | + SELECT sum(subtotal) |
4583 | + FROM ( |
4584 | + SELECT |
4585 | + pol.id as polid, |
4586 | + ( |
4587 | + pol_lcp.amount_company_currency |
4588 | + * pol.product_qty |
4589 | + ) as subtotal |
4590 | + FROM purchase_order_line POL |
4591 | + LEFT JOIN landed_cost_position pol_lcp |
4592 | + ON pol_lcp.purchase_order_line_id = pol.id |
4593 | + LEFT JOIN landed_cost_distribution_type |
4594 | + pol_lcdt |
4595 | + ON |
4596 | + pol_lcdt.id = pol_lcp.distribution_type_id |
4597 | + WHERE pol.id = $1 |
4598 | + AND pol_lcp.active = true |
4599 | + AND |
4600 | + pol_lcdt.landed_cost_type = ''per_unit'' |
4601 | + GROUP BY pol.id, pol_lcp.id, pol_lcdt.id) |
4602 | + as result' |
4603 | + LANGUAGE SQL; |
4604 | + |
4605 | + CREATE OR REPLACE FUNCTION pol_landed_cost_per_value(int) RETURNS |
4606 | + numeric |
4607 | + AS 'SELECT sum(subtotal) |
4608 | + FROM ( |
4609 | + SELECT |
4610 | + pol.id as polid, |
4611 | + pol_lcp.amount_company_currency as subtotal |
4612 | + FROM purchase_order_line POL |
4613 | + LEFT JOIN landed_cost_position pol_lcp |
4614 | + ON pol_lcp.purchase_order_line_id = pol.id |
4615 | + LEFT JOIN landed_cost_distribution_type |
4616 | + pol_lcdt |
4617 | + ON |
4618 | + pol_lcdt.id = pol_lcp.distribution_type_id |
4619 | + WHERE pol.id = $1 |
4620 | + AND pol_lcp.active = true |
4621 | + AND pol_lcdt.landed_cost_type = ''value'' |
4622 | + GROUP BY pol.id, pol_lcp.id, pol_lcdt.id) |
4623 | + as result' |
4624 | + LANGUAGE SQL; |
4625 | + |
4626 | + |
4627 | + CREATE OR REPLACE FUNCTION pol_landed_cost_total(int) RETURNS |
4628 | + numeric |
4629 | + AS 'SELECT sum(subtotal) |
4630 | + FROM (select po_landed_cost_per_unit as subtotal |
4631 | + from po_landed_cost_per_unit($1) UNION |
4632 | + select po_landed_cost_per_value as subtotal |
4633 | + from po_landed_cost_per_value($1) UNION |
4634 | + select pol_landed_cost_per_unit as subtotal |
4635 | + from pol_landed_cost_per_unit($1) UNION |
4636 | + select pol_landed_cost_per_value as subtotal |
4637 | + from pol_landed_cost_per_value($1)) as result' |
4638 | + LANGUAGE SQL; |
4639 | + |
4640 | + CREATE OR REPLACE FUNCTION single_pol_po_landed_cost_per_unit(int) |
4641 | + RETURNS numeric |
4642 | + AS ' |
4643 | + SELECT sum(subtotal) FROM (SELECT |
4644 | + |
4645 | + ((po_lcp.amount_company_currency |
4646 | + / (SELECT sum(product_qty) FROM |
4647 | + purchase_order_line WHERE |
4648 | + order_id = pol.order_id)) |
4649 | + ) as subtotal |
4650 | + FROM purchase_order_line POL |
4651 | + LEFT JOIN landed_cost_position po_lcp |
4652 | + ON po_lcp.purchase_order_id = pol.order_id |
4653 | + LEFT JOIN landed_cost_distribution_type |
4654 | + po_lcdt |
4655 | + ON po_lcdt.id = po_lcp.distribution_type_id |
4656 | + WHERE pol.id = $1 |
4657 | + AND po_lcp.active = true |
4658 | + AND po_lcp.purchase_order_line_id is null |
4659 | + AND po_lcdt.landed_cost_type = ''per_unit'' |
4660 | + GROUP BY pol.id, po_lcp.id, po_lcdt.id) |
4661 | + as result' |
4662 | + LANGUAGE SQL; |
4663 | + CREATE OR REPLACE FUNCTION single_pol_po_landed_cost_per_value(int) |
4664 | + RETURNS numeric |
4665 | + AS ' |
4666 | + SELECT sum(subtotal) |
4667 | + FROM ( |
4668 | + SELECT ( |
4669 | + (po_lcp.amount_company_currency * pol.price_unit) / |
4670 | + po.amount_untaxed |
4671 | + ) as subtotal |
4672 | + FROM purchase_order_line POL |
4673 | + LEFT JOIN purchase_order po |
4674 | + ON po.id = pol.order_id |
4675 | + LEFT JOIN landed_cost_position po_lcp |
4676 | + ON po_lcp.purchase_order_id = pol.order_id |
4677 | + LEFT JOIN landed_cost_distribution_type po_lcdt |
4678 | + ON po_lcdt.id = po_lcp.distribution_type_id |
4679 | + WHERE pol.id = $1 |
4680 | + AND po_lcp.active = true |
4681 | + AND po_lcp.purchase_order_line_id is null |
4682 | + AND po_lcdt.landed_cost_type = ''value'' |
4683 | + GROUP BY pol.id, po.id, po_lcp.id, po_lcdt.id) |
4684 | + as result' |
4685 | + LANGUAGE SQL; |
4686 | + |
4687 | + |
4688 | + CREATE OR REPLACE FUNCTION single_pol_landed_cost_per_unit(int) |
4689 | + RETURNS numeric |
4690 | + AS ' |
4691 | + SELECT sum(subtotal) |
4692 | + FROM ( |
4693 | + SELECT |
4694 | + pol.id as polid, |
4695 | + pol_lcp.amount_company_currency as subtotal |
4696 | + FROM purchase_order_line POL |
4697 | + LEFT JOIN landed_cost_position pol_lcp |
4698 | + ON pol_lcp.purchase_order_line_id = pol.id |
4699 | + LEFT JOIN landed_cost_distribution_type |
4700 | + pol_lcdt |
4701 | + ON |
4702 | + pol_lcdt.id = pol_lcp.distribution_type_id |
4703 | + WHERE pol.id = $1 |
4704 | + AND pol_lcp.active = true |
4705 | + AND |
4706 | + pol_lcdt.landed_cost_type = ''per_unit'' |
4707 | + GROUP BY pol.id, pol_lcp.id, pol_lcdt.id) |
4708 | + as result' |
4709 | + LANGUAGE SQL; |
4710 | + |
4711 | + CREATE OR REPLACE FUNCTION single_pol_landed_cost_per_value(int) RETURNS |
4712 | + numeric |
4713 | + AS 'SELECT sum(subtotal) |
4714 | + FROM ( |
4715 | + SELECT |
4716 | + pol.id as polid, |
4717 | + ( |
4718 | + pol_lcp.amount_company_currency / |
4719 | + pol.product_qty |
4720 | + )as subtotal |
4721 | + FROM purchase_order_line POL |
4722 | + LEFT JOIN landed_cost_position pol_lcp |
4723 | + ON pol_lcp.purchase_order_line_id = pol.id |
4724 | + LEFT JOIN landed_cost_distribution_type |
4725 | + pol_lcdt |
4726 | + ON |
4727 | + pol_lcdt.id = pol_lcp.distribution_type_id |
4728 | + WHERE pol.id = $1 |
4729 | + AND pol_lcp.active = true |
4730 | + AND pol_lcdt.landed_cost_type = ''value'' |
4731 | + GROUP BY pol.id, pol_lcp.id, pol_lcdt.id) |
4732 | + as result' |
4733 | + LANGUAGE SQL; |
4734 | + CREATE OR REPLACE FUNCTION pol_landed_cost_unit(int) |
4735 | + RETURNS numeric |
4736 | + AS 'SELECT sum(subtotal) |
4737 | + FROM (select single_pol_po_landed_cost_per_unit as subtotal |
4738 | + from single_pol_po_landed_cost_per_unit($1) UNION |
4739 | + select single_pol_po_landed_cost_per_value as subtotal |
4740 | + from single_pol_po_landed_cost_per_value($1) UNION |
4741 | + select single_pol_landed_cost_per_unit as subtotal |
4742 | + from single_pol_landed_cost_per_unit($1) UNION |
4743 | + select single_pol_landed_cost_per_value as subtotal |
4744 | + from single_pol_landed_cost_per_value($1)) as result' |
4745 | + LANGUAGE SQL; |
4746 | + create or replace view purchase_landed_cost_report as ( |
4747 | + select |
4748 | + min(l.id) as id, |
4749 | + s.id as purchase_order_id, |
4750 | + s.date_order as date, |
4751 | + to_char(s.date_order, 'YYYY') as name, |
4752 | + to_char(s.date_order, 'MM') as month, |
4753 | + to_char(s.date_order, 'YYYY-MM-DD') as day, |
4754 | + s.state, |
4755 | + s.date_approve, |
4756 | + s.minimum_planned_date as expected_date, |
4757 | + s.dest_address_id, |
4758 | + s.pricelist_id, |
4759 | + s.validator, |
4760 | + s.warehouse_id as warehouse_id, |
4761 | + s.partner_id as partner_id, |
4762 | + s.create_uid as user_id, |
4763 | + s.company_id as company_id, |
4764 | + l.product_id, |
4765 | + t.categ_id as category_id, |
4766 | + t.uom_id as product_uom, |
4767 | + s.location_id as location_id, |
4768 | + sum(l.product_qty/u.factor*u2.factor) as quantity, |
4769 | + count(*) as nbr, |
4770 | + sum(l.price_unit*l.product_qty)::decimal(16,2) |
4771 | + as price_total, |
4772 | + avg(100.0 * (l.price_unit*l.product_qty) / NULLIF( |
4773 | + t.standard_price*l.product_qty/ |
4774 | + u.factor*u2.factor, 0.0))::decimal(16,2) |
4775 | + as negociation, |
4776 | + sum(t.standard_price*l.product_qty/u.factor*u2.factor |
4777 | + )::decimal(16,2) as price_standard, |
4778 | + (sum(l.product_qty*l.price_unit)/NULLIF(sum( |
4779 | + l.product_qty/u.factor*u2.factor),0.0) |
4780 | + )::decimal(16,2) as price_average, |
4781 | + pol_landed_cost_total(l.id)::decimal(16,2) as |
4782 | + landed_cost_total, |
4783 | + pol_landed_cost_unit(l.id)::decimal(16,2) as |
4784 | + landed_cost_unit |
4785 | + from purchase_order_line l |
4786 | + join purchase_order s on (l.order_id=s.id) |
4787 | + left join product_product p on (l.product_id=p.id) |
4788 | + left join product_template t on ( |
4789 | + p.product_tmpl_id=t.id) |
4790 | + left join product_uom u on (u.id=l.product_uom) |
4791 | + left join product_uom u2 on (u2.id=t.uom_id) |
4792 | + group by |
4793 | + s.company_id, |
4794 | + s.create_uid, |
4795 | + s.partner_id, |
4796 | + u.factor, |
4797 | + s.location_id, |
4798 | + l.id, |
4799 | + s.id, |
4800 | + l.price_unit, |
4801 | + s.date_approve, |
4802 | + l.date_planned, |
4803 | + l.product_uom, |
4804 | + s.minimum_planned_date, |
4805 | + s.pricelist_id, |
4806 | + s.validator, |
4807 | + s.dest_address_id, |
4808 | + l.product_id, |
4809 | + t.categ_id, |
4810 | + s.date_order, |
4811 | + to_char(s.date_order, 'YYYY'), |
4812 | + to_char(s.date_order, 'MM'), |
4813 | + to_char(s.date_order, 'YYYY-MM-DD'), |
4814 | + s.state, |
4815 | + s.warehouse_id, |
4816 | + u.uom_type, |
4817 | + u.category_id, |
4818 | + t.uom_id, |
4819 | + u.id, |
4820 | + u2.factor |
4821 | + ) |
4822 | + """) |
4823 | |
4824 | === added file 'purchase_landed_costs_extended/report/purchase_report_view.xml' |
4825 | --- purchase_landed_costs_extended/report/purchase_report_view.xml 1970-01-01 00:00:00 +0000 |
4826 | +++ purchase_landed_costs_extended/report/purchase_report_view.xml 2014-06-19 02:29:14 +0000 |
4827 | @@ -0,0 +1,144 @@ |
4828 | +<?xml version="1.0" encoding="UTF-8"?> |
4829 | +<openerp> |
4830 | + <data> |
4831 | + <record model="ir.ui.view" id="view_purchase_order_landed_cost_graph"> |
4832 | + <field name="name">product.month.graph</field> |
4833 | + <field name="model">purchase.landed.cost.report</field> |
4834 | + <field name="arch" type="xml"> |
4835 | + <graph string="Purchase Orders Statistics" type="bar"> |
4836 | + <field name="product_id"/> |
4837 | + <field name="price_total" operator="+"/> |
4838 | + </graph> |
4839 | + </field> |
4840 | + </record> |
4841 | + |
4842 | + <record id="view_purchase_order_landed_cost_tree" model="ir.ui.view"> |
4843 | + <field name="name">purchase.order.landed.cost.tree</field> |
4844 | + <field name="model">purchase.landed.cost.report</field> |
4845 | + <field name="arch" type="xml"> |
4846 | + <tree string="Purchase Orders Statistics" create="false"> |
4847 | + <field name="date" invisible="1"/> |
4848 | + <field name="date_approve" invisible="1"/> |
4849 | + <field name="expected_date" invisible="1"/> |
4850 | + <field name="user_id" invisible="1"/> |
4851 | + <field name="partner_id" invisible="1"/> |
4852 | + <field name="product_id" invisible="1"/> |
4853 | + <field name="category_id" invisible="1"/> |
4854 | + <field name="product_uom" invisible="1"/> |
4855 | + <field name="day" invisible="1"/> |
4856 | + <field name="name" invisible="1"/> |
4857 | + <field name="month" invisible="1"/> |
4858 | + <field name="warehouse_id" invisible="1"/> |
4859 | + <field name="validator" invisible="1"/> |
4860 | + <field name="company_id" invisible="1"/> |
4861 | + <field name="state" invisible="1"/> |
4862 | + <field name="location_id" invisible="1"/> |
4863 | + <field name="nbr" sum="# of Lines"/> |
4864 | + <field name="quantity" sum="Quantity"/> |
4865 | + <field name="price_average" sum="Average Price"/> |
4866 | + <field name="price_total" sum="Total Price"/> |
4867 | + <field name="price_standard" sum="Products Value"/> |
4868 | + <field name="negociation" widget="progressbar"/> |
4869 | + <field name="landed_cost_unit" sum="Landed cost unit"/> |
4870 | + <field name="landed_cost_total" sum="Landed cost unit"/> |
4871 | + </tree> |
4872 | + </field> |
4873 | + </record> |
4874 | + |
4875 | + <record id="view_purchase_order_landed_cost_search" model="ir.ui.view"> |
4876 | + <field name="name">report.purchase.order.landed.cost.search</field> |
4877 | + <field name="model">purchase.landed.cost.report</field> |
4878 | + <field name="arch" type="xml"> |
4879 | + <search string="Purchase Orders"> |
4880 | + <filter icon="terp-document-new" string="Quotations" name="quotes" domain="[('state','=','draft')]"/> |
4881 | + <filter icon="terp-gtk-jump-to-rtl" string="Orders" name="orders" domain="[('state','<>','draft'),('state','<>','cancel')]"/> |
4882 | + <field name="partner_id"/> |
4883 | + <field name="product_id"/> |
4884 | + <field name="purchase_order_id"/> |
4885 | + <group expand="0" string="Extended Filters..."> |
4886 | + <field name="user_id"/> |
4887 | + <field name="validator"/> |
4888 | + <field name="location_id"/> |
4889 | + <field name="warehouse_id"/> |
4890 | + <field name="company_id" groups="base.group_multi_company"/> |
4891 | + <field name="date"/> |
4892 | + <field name="date_approve"/> |
4893 | + <field name="expected_date"/> |
4894 | + </group> |
4895 | + <newline/> |
4896 | + <group expand="1" string="Group By..."> |
4897 | + <filter string="Supplier" name="group_partner_id" icon="terp-personal" context="{'group_by':'partner_id'}"/> |
4898 | + <filter string="Responsible" name="Responsible" icon="terp-personal" context="{'group_by':'user_id'}"/> |
4899 | + <filter string="Validated by" icon="terp-personal" context="{'group_by':'validator'}"/> |
4900 | + <filter string="Product" name="group_product_id" icon="terp-accessories-archiver" context="{'group_by':'product_id'}"/> |
4901 | + <filter string="Order" name="group_purchase_order_id" icon="terp-accessories-archiver" context="{'group_by':'purchase_order_id'}"/> |
4902 | + <filter string="Category" name="group_category_id" icon="terp-stock_symbol-selection" context="{'group_by':'category_id'}"/> |
4903 | + <filter string="Warehouse" icon="terp-go-home" context="{'group_by':'warehouse_id'}"/> |
4904 | + <filter string="Reference UOM" name="group_product_uom" icon="terp-mrp" context="{'group_by':'product_uom'}"/> |
4905 | + <filter string="Destination" icon="terp-gtk-jump-to-ltr" context="{'group_by':'location_id'}"/> |
4906 | + <filter string="Status" icon="terp-stock_effects-object-colorize" context="{'group_by':'state'}"/> |
4907 | + <filter string="Company" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/> |
4908 | + <filter string="Day" icon="terp-go-today" context="{'group_by':'day'}" help="Order of Day"/> |
4909 | + <filter string="Month" icon="terp-go-month" context="{'group_by':'month'}" help="Order of Month"/> |
4910 | + <filter string="Year" icon="terp-go-year" context="{'group_by':'name'}" help="Order of Year"/> |
4911 | + </group> |
4912 | + </search> |
4913 | + </field> |
4914 | + </record> |
4915 | + |
4916 | + <record model="ir.ui.view" id="view_purchase_order_landed_cost_qty_amount_graph"> |
4917 | + <field name="name">purchase.order.landed.cost.qty.amount.graph</field> |
4918 | + <field name="model">purchase.landed.cost.report</field> |
4919 | + <field name="arch" type="xml"> |
4920 | + <graph string="Total Qty and Amount by month" type="bar"> |
4921 | + <field name="month"/> |
4922 | + <field name="quantity" operator="+"/> |
4923 | + <field name="price_total" operator="+"/> |
4924 | + </graph> |
4925 | + </field> |
4926 | + </record> |
4927 | + |
4928 | + <record model="ir.ui.view" id="view_purchase_order_landed_cost_by_user_graph"> |
4929 | + <field name="name">purchase.order.landed.cost.by.user.graph</field> |
4930 | + <field name="model">purchase.landed.cost.report</field> |
4931 | + <field name="arch" type="xml"> |
4932 | + <graph string="Total Orders Lines by User per month" orientation="vertical" type="bar"> |
4933 | + <field name="month" /> |
4934 | + <field name="nbr" operator="+"/> |
4935 | + <field name="user_id" group="True" /> |
4936 | + </graph> |
4937 | + </field> |
4938 | + </record> |
4939 | + |
4940 | + |
4941 | + <record id="action_purchase_order_landed_cost_landed_cost_report_all" model="ir.actions.act_window"> |
4942 | + <field name="name">Landed Cost Extended</field> |
4943 | + <field name="res_model">purchase.landed.cost.report</field> |
4944 | + <field name="view_type">form</field> |
4945 | + <field name="view_mode">tree,graph</field> |
4946 | + <field name="view_id" ref="view_purchase_order_landed_cost_tree"></field> |
4947 | + <field name="context">{'search_default_year':1,'search_default_month':1,'search_default_group_partner_id':1,'search_default_group_product_id': 1, 'search_default_group_purchase_order_id': 1, 'search_default_orders': 1, 'group_by_no_leaf':1,'group_by':[]}</field> |
4948 | + <field name="help">Purchase Analysis allows you to easily check and analyse your company purchase history and performance. From this menu you can track your negotiation performance, the delivery performance of your suppliers, etc.</field> |
4949 | + </record> |
4950 | + |
4951 | + <record id="action_purchase_order_landed_cost_landed_cost_report_graph" model="ir.actions.act_window"> |
4952 | + <field name="name">Total Qty and Amount by month</field> |
4953 | + <field name="res_model">purchase.landed.cost.report</field> |
4954 | + <field name="view_type">form</field> |
4955 | + <field name="view_mode">graph,tree</field> |
4956 | + <field name="view_id" ref="view_purchase_order_landed_cost_qty_amount_graph"></field> |
4957 | + </record> |
4958 | + |
4959 | + <record id="action_purchase_order_landed_cost_by_user_all" model="ir.actions.act_window"> |
4960 | + <field name="name">Total Orders by User per month</field> |
4961 | + <field name="res_model">purchase.landed.cost.report</field> |
4962 | + <field name="view_type">form</field> |
4963 | + <field name="view_mode">graph,tree</field> |
4964 | + <field name="view_id" ref="view_purchase_order_landed_cost_by_user_graph"></field> |
4965 | + </record> |
4966 | + |
4967 | + |
4968 | + <menuitem action="action_purchase_order_landed_cost_landed_cost_report_all" id="menu_action_purchase_order_landed_cost_landed_cost_report_all" parent="base.next_id_73" sequence="3"/> |
4969 | + |
4970 | + </data> |
4971 | +</openerp> |
4972 | \ No newline at end of file |
4973 | |
4974 | === added directory 'purchase_landed_costs_extended/security' |
4975 | === added file 'purchase_landed_costs_extended/security/ir.model.access.csv' |
4976 | --- purchase_landed_costs_extended/security/ir.model.access.csv 1970-01-01 00:00:00 +0000 |
4977 | +++ purchase_landed_costs_extended/security/ir.model.access.csv 2014-06-19 02:29:14 +0000 |
4978 | @@ -0,0 +1,3 @@ |
4979 | +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink |
4980 | +access_report_purchase_order_landed_cost,purchase_landed_costs_extended.purchase_landed_cost_report,model_purchase_landed_cost_report,purchase.group_purchase_manager,1,0,0,0 |
4981 | +access_report_purchase_order_landed_cost_user,purchase_landed_costs_extended.purchase_landed_cost_report user,model_purchase_landed_cost_report,purchase.group_purchase_user,1,0,0,0 |
4982 | \ No newline at end of file |
4983 | |
4984 | === added directory 'purchase_landed_costs_extended/wizard' |
4985 | === added file 'purchase_landed_costs_extended/wizard/__init__.py' |
4986 | --- purchase_landed_costs_extended/wizard/__init__.py 1970-01-01 00:00:00 +0000 |
4987 | +++ purchase_landed_costs_extended/wizard/__init__.py 2014-06-19 02:29:14 +0000 |
4988 | @@ -0,0 +1,22 @@ |
4989 | +# -*- coding: utf-8 -*- |
4990 | +############################################################################## |
4991 | +# |
4992 | +# OpenERP, Open Source Management Solution |
4993 | +# Copyright (c) 2010-2014 Elico Corp. All Rights Reserved. |
4994 | +# Augustin Cisterne-Kaas <augustin.cisterne-kaas@elico-corp.com> |
4995 | +# |
4996 | +# This program is free software: you can redistribute it and/or modify |
4997 | +# it under the terms of the GNU Affero General Public License as |
4998 | +# published by the Free Software Foundation, either version 3 of the |
4999 | +# License, or (at your option) any later version. |
5000 | +# |
The diff has been truncated for viewing.