Merge lp:~ajite/openobject-addons/elico-7.0-add-purchase_landed_costs_extended into lp:~openerp-community/openobject-addons/elico-7.0

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
Reviewer Review Type Date Requested Status
Elico Corp (www.elico-corp.com) Pending
Review via email: mp+223668@code.launchpad.net

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','&lt;&gt;','draft'),('state','&lt;&gt;','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.