openerp-community-reviewer team mailing list archive
-
openerp-community-reviewer team
-
Mailing list archive
-
Message #05322
[Merge] lp:~camptocamp/openerp-humanitarian-ngo/ngo-addons-add_other_procurement_method-nbi into lp:openerp-humanitarian-ngo
Nicolas Bessi - Camptocamp has proposed merging lp:~camptocamp/openerp-humanitarian-ngo/ngo-addons-add_other_procurement_method-nbi into lp:openerp-humanitarian-ngo.
Commit message:
[ADD] support of "other" sourcing method.
Allows to create a call for bid or an agreement purchase order from many source lines including service source lines.
[Add] multi-currency:
Allows to have a currency (pricelist) defined in on logisitc request and have sourcing process (call for bid or agreement PO) in an other currency.
[IMP] separate budget in an external module
Requested reviews:
OpenERP for Humanitarian Core Editors (humanitarian-core-editors)
For more details, see:
https://code.launchpad.net/~camptocamp/openerp-humanitarian-ngo/ngo-addons-add_other_procurement_method-nbi/+merge/205155
[ADD] support of "other" sourcing method.
Allows to create a call for bid or an agreement purchase order from many source lines including service source lines.
[Add] multi-currency:
Allows to have a currency (pricelist) defined in on logisitc request and have sourcing process (call for bid or agreement PO) in an other currency.
[IMP] separate budget in an external module
Depends on https://code.launchpad.net/~camptocamp/openerp-humanitarian-ngo/ngo-addons-add_agreement_sourcing-nbi/+merge/196676
--
https://code.launchpad.net/~camptocamp/openerp-humanitarian-ngo/ngo-addons-add_other_procurement_method-nbi/+merge/205155
Your team OpenERP for Humanitarian Core Editors is requested to review the proposed merge of lp:~camptocamp/openerp-humanitarian-ngo/ngo-addons-add_other_procurement_method-nbi into lp:openerp-humanitarian-ngo.
=== added directory 'framework_agreement_requisition'
=== added file 'framework_agreement_requisition/__init__.py'
--- framework_agreement_requisition/__init__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import model
=== added file 'framework_agreement_requisition/__openerp__.py'
--- framework_agreement_requisition/__openerp__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/__openerp__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+{'name': 'Framework Agreement Negociation',
+ 'version': '0.1',
+ 'author': 'Camptocamp',
+ 'maintainer': 'Camptocamp',
+ 'category': 'NGO',
+ 'complexity': 'normal',
+ 'depends': ['purchase_requisition',
+ 'purchase_requisition_extended',
+ 'framework_agreement'],
+ 'description': """
+Negociate framework agreement using tender process
+==================================================
+
+This will allows you to use "The calls for Bids" model
+ to negociate agreement.
+
+To to so you have too check the box "Negociate Agreement".
+
+The module add a state "Agreement selected" on tender and PO.
+
+
+These will be the final state once you have choosen
+the agreement that fit your needs the best.
+
+Once the selection is done juste use the button "Agreement selected" on tender
+That will close flow of tender and related PO accordingly.
+
+""",
+ 'website': 'http://www.camptocamp.com',
+ 'data': ['requisition_workflow.xml',
+ 'purchase_workflow.xml',
+ 'view/purchase_requisition_view.xml'],
+ 'demo': [],
+ 'test': ['test/agreement_requisition.yml'],
+ 'installable': True,
+ 'auto_install': False,
+ 'license': 'AGPL-3',
+ 'application': False,
+ }
=== added directory 'framework_agreement_requisition/model'
=== added file 'framework_agreement_requisition/model/__init__.py'
--- framework_agreement_requisition/model/__init__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/model/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import purchase_requisition
+from . import purchase
=== added file 'framework_agreement_requisition/model/purchase.py'
--- framework_agreement_requisition/model/purchase.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/model/purchase.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp import netsvc
+from openerp.osv import orm
+
+SELECTED_STATE = ('agreement_selected', 'Agreement selected')
+AGR_SELECT = 'agreement_selected'
+
+
+class purchase_order(orm.Model):
+ """Add workflow behavior"""
+
+ _inherit = "purchase.order"
+
+ def __init__(self, pool, cr):
+ """Add a new state value using PO class property"""
+ if SELECTED_STATE not in super(purchase_order, self).STATE_SELECTION:
+ super(purchase_order, self).STATE_SELECTION.append(SELECTED_STATE)
+ return super(purchase_order, self).__init__(pool, cr)
+
+ def select_agreement(self, cr, uid, agr_id, context=None):
+ """Pass PO in state 'Agreement selected'"""
+ if isinstance(agr_id, (list, tuple)):
+ assert len(agr_id) == 1
+ agr_id = agr_id[0]
+ wf_service = netsvc.LocalService("workflow")
+ return wf_service.trg_validate(uid, 'purchase.order',
+ agr_id, 'select_agreement', cr)
+
+ def po_tender_agreement_selected(self, cr, uid, ids, context=None):
+ """Workflow function that write state 'Agreement selected'"""
+ return self.write(cr, uid, ids, {'state': AGR_SELECT},
+ context=context)
+
+
+class purchase_order_line(orm.Model):
+ """Add make_agreement function"""
+
+ _inherit = "purchase.order.line"
+
+ # Did you know a good way to supress SQL constraint to add
+ # Python constraint...
+ _sql_constraints = [
+ ('quantity_bid', 'CHECK(true)',
+ 'Selected quantity must be less or equal than the quantity in the bid'),
+ ]
+
+ def _check_quantity_bid(self, cr, uid, ids, context=None):
+ for line in self.browse(cr, uid, ids, context=context):
+ if line.order_id.framework_agreement_id:
+ continue
+ if line.product_id.type == 'product' and not line.quantity_bid <= line.product_qty:
+ return False
+ return True
+
+ _constraints = [
+ (_check_quantity_bid,
+ 'Selected quantity must be less or equal than the quantity in the bid',
+ [])
+ ]
+ def _agreement_data(self, cr, uid, po_line, origin, context=None):
+ """Get agreement values from PO line
+
+ :param po_line: Po line records
+
+ :returns: agreement dict to be used by orm.Model.create
+ """
+ vals = {}
+ vals['supplier_id'] = po_line.order_id.partner_id.id
+ vals['product_id'] = po_line.product_id.id
+ vals['quantity'] = po_line.product_qty
+ vals['delay'] = po_line.product_lead_time
+ vals['origin'] = origin if origin else False
+ return vals
+
+ def make_agreement(self, cr, uid, line_id, origin, context=None):
+ """ generate a draft framework agreement
+
+ :returns: a record of LTA
+
+ """
+ agr_model = self.pool['framework.agreement']
+ if isinstance(line_id, (list, tuple)):
+ assert len(line_id) == 1
+ line_id = line_id[0]
+ current = self.browse(cr, uid, line_id, context=context)
+ vals = self._agreement_data(cr, uid, current, origin, context=context)
+ agr_id = agr_model.create(cr, uid, vals, context=context)
+ return agr_model.browse(cr, uid, agr_id, context=context)
=== added file 'framework_agreement_requisition/model/purchase_requisition.py'
--- framework_agreement_requisition/model/purchase_requisition.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/model/purchase_requisition.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,120 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from itertools import chain
+from openerp import netsvc
+from openerp.osv import orm, fields
+from openerp.tools.translate import _
+from .purchase import AGR_SELECT as PO_AGR_SELECT
+
+SELECTED_STATE = ('agreement_selected', 'Agreement selected')
+AGR_SELECT = 'agreement_selected'
+
+
+class purchase_requisition(orm.Model):
+ """Add support to negociate LTA using tender process"""
+
+ def __init__(self, pool, cr):
+ """Nasty hack to add fields to select fields
+
+ We do this in order not to compromising other state added
+ by other addons that are not in inheritance chain...
+
+ """
+ sel = super(purchase_requisition, self)._columns['state']
+ if SELECTED_STATE not in sel.selection:
+ sel.selection.append(SELECTED_STATE)
+ return super(purchase_requisition, self).__init__(pool, cr)
+
+ _inherit = "purchase.requisition"
+ _columns = {
+ 'framework_agreement_tender': fields.boolean('Negociate Agreement'),
+ }
+
+ def tender_agreement_selected(self, cr, uid, ids, context=None):
+ """Workflow function that write state 'Agreement selected'"""
+ return self.write(cr, uid, ids, {'state': AGR_SELECT},
+ context=context)
+
+ def select_agreement(self, cr, uid, agr_id, context=None):
+ """Pass tender to state 'Agreement selected'"""
+ if isinstance(agr_id, (list, tuple)):
+ assert len(agr_id) == 1
+ agr_id = agr_id[0]
+ wf_service = netsvc.LocalService("workflow")
+ return wf_service.trg_validate(uid, 'purchase.requisition',
+ agr_id, 'select_agreement', cr)
+
+ def _agreement_selected(self, cr, uid, ids, context=None):
+ """Tells tender that an agreement has been selected"""
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+ generated = []
+ for req in self.browse(cr, uid, ids, context=context):
+ if not req.framework_agreement_tender:
+ raise orm.except_orm(_('Invalid tender'),
+ _('Request is not of type agreement'))
+ self.select_agreement(cr, uid, req.id, context=context)
+ req.refresh()
+ if req.state != AGR_SELECT:
+ raise RuntimeError('requisiton %s does not pass to state'
+ ' agreement_selected' %
+ req.name)
+ rfqs = chain.from_iterable(req_line.purchase_line_ids
+ for req_line in req.line_ids)
+ po_to_select = []
+ po_to_cancel = []
+ for rfq in rfqs:
+ if rfq.state == 'confirmed':
+ agr_record = rfq.make_agreement(req.name)
+ generated.append(agr_record)
+ po_to_select.append(rfq.order_id)
+ else:
+ po_to_cancel.append(rfq.order_id)
+
+ if not po_to_select:
+ raise orm.except_orm(_('No confirmed RFQ related to tender'),
+ _('Please choose at least one'))
+
+ for p_order in set(po_to_select):
+ p_order.select_agreement()
+ p_order.refresh()
+ if p_order.state != PO_AGR_SELECT:
+ raise RuntimeError('Purchase order %s does not pass to %s' %
+ (p_order.name, PO_AGR_SELECT))
+ wf_service = netsvc.LocalService("workflow")
+ for p_order in set(po_to_cancel):
+ wf_service.trg_validate(uid, 'purchase.order', p_order.id,
+ 'purchase_cancel', cr)
+ return generated
+
+ def agreement_selected(self, cr, uid, ids, context=None):
+ agrements = self._agreement_selected(cr, uid, ids, context=context)
+ a_ids = [x.id for x in agrements]
+ return {
+ 'name': _('Generated Agreements'),
+ 'view_mode': 'tree,form',
+ 'res_model': 'framework.agreement',
+ 'domain': [('id', 'in', a_ids)],
+ 'target': 'current',
+ 'view_id': False,
+ 'context': {},
+ 'type': 'ir.actions.act_window',
+ }
=== added file 'framework_agreement_requisition/purchase_workflow.xml'
--- framework_agreement_requisition/purchase_workflow.xml 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/purchase_workflow.xml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="act_po_agreement_selected" model="workflow.activity">
+ <field name="wkf_id" ref="purchase.purchase_order"/>
+ <field name="name">Agreement selected</field>
+ <field name="kind">function</field>
+ <field name="action">po_tender_agreement_selected()</field>
+ <field name="flow_stop">True</field>
+ </record>
+ <record id="trans_po_agreement_selected" model="workflow.transition">
+ <field name="act_from" ref="purchase.act_bid"/>
+ <field name="act_to" ref="act_po_agreement_selected"/>
+ <field name="signal">select_agreement</field>
+ </record>
+ </data>
+</openerp>
=== added file 'framework_agreement_requisition/requisition_workflow.xml'
--- framework_agreement_requisition/requisition_workflow.xml 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/requisition_workflow.xml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="act_agreement_selected" model="workflow.activity">
+ <field name="wkf_id" ref="purchase_requisition.purchase_requisition_workflow"/>
+ <field name="name">Agreement selected</field>
+ <field name="kind">function</field>
+ <field name="action">tender_agreement_selected()</field>
+ <field name="flow_stop">True</field>
+ </record>
+
+ <record id="trans_agreement_selected" model="workflow.transition">
+ <field name="act_from" ref="purchase_requisition_extended.act_closed"/>
+ <field name="act_to" ref="act_agreement_selected"/>
+ <field name="signal">select_agreement</field>
+ </record>
+ </data>
+</openerp>
=== added directory 'framework_agreement_requisition/test'
=== added file 'framework_agreement_requisition/test/agreement_requisition.yml'
--- framework_agreement_requisition/test/agreement_requisition.yml 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/test/agreement_requisition.yml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,98 @@
+-
+ Standard flow of a Call for agreement Bids in mode open
+-
+ Create Call for Bids
+-
+ !record {model: purchase.requisition, id: purchase_requisition_agreement}:
+ date_start: '2013-08-02 00:00:00'
+ date_end: '2013-08-30 00:00:00'
+ bid_tendering_mode: 'open'
+ schedule_date: '2013-09-30'
+ req_validity: '2013-09-10'
+ framework_agreement_tender: True
+ line_ids:
+ - product_id: product.product_product_15
+ product_qty: 2500.0
+-
+ Confirm Call
+-
+ !python {model: purchase.requisition}: |
+ import netsvc
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'purchase.requisition', ref("purchase_requisition_agreement"), 'sent_suppliers', cr)
+-
+ Create RFQ1. I run the 'Request a quotation' wizard. I fill the supplier.
+-
+ !record {model: purchase.requisition.partner, id: purchase_requisition_agreement_partner1_create}:
+ partner_id: base.res_partner_2
+-
+ Create RFQ1. I confirm the wizard.
+-
+ !python {model: purchase.requisition.partner}: |
+ self.create_order(cr, uid, [ref("purchase_requisition_agreement_partner1_create")],{
+ 'active_model': 'purchase.requisition',
+ 'active_id': ref("purchase_requisition_agreement"),
+ 'active_ids': [ref("purchase_requisition_agreement")],
+ })
+-
+ I encode the bid. I set a 300 price on the line.
+-
+ !python {model: purchase.requisition}: |
+ purchase_req = self.browse(cr, uid, ref("purchase_requisition_agreement"))
+ assert len(purchase_req.purchase_ids) == 1, "There must be 1 RFQs linked to this Call for bids"
+ price = 300
+ for rfq in purchase_req.purchase_ids:
+ for line in rfq.order_line:
+ self.pool.get('purchase.order.line').write(cr, uid, [line.id], {'price_unit': price})
+
+-
+ I send the RFQ. For this, I print the RFQ.
+-
+ !python {model: purchase.requisition}: |
+ purchase_req = self.browse(cr, uid, ref("purchase_requisition_agreement"))
+ for rfq in purchase_req.purchase_ids:
+ self.pool.get('purchase.order').print_quotation(cr, uid, [rfq.id])
+-
+ I run the 'Bid encoded' wizard of bid1. I fill the date.
+-
+ !record {model: purchase.action_modal_datetime, id: purchase_requisition_agreement_bid1_bidencoded}:
+ datetime: '2013-08-13 00:00:00'
+-
+ I launch wizard action.
+-
+ !python {model: purchase.action_modal_datetime}: |
+ purchase_req = self.pool['purchase.requisition'].browse(cr, uid, ref("purchase_requisition_agreement"))
+ po_id = purchase_req.purchase_ids[0].id
+ self.action(cr, uid, [ref('purchase_requisition_agreement_bid1_bidencoded')],
+ {'action': 'bid_received_ok',
+ 'active_id': po_id,
+ 'active_ids': [po_id],
+ 'active_model': 'purchase.order',
+ 'default_datetime': '2013-08-13 00:00:00',
+ 'uid': 1})
+-
+ I close the Call for bids and move to bids selection
+-
+ !python {model: purchase.requisition}: |
+ import netsvc
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'purchase.requisition', ref("purchase_requisition_agreement"), 'open_bid', cr)
+
+-
+ In the bids selection, I confirm line 1 of bid 1
+-
+ !python {model: purchase.requisition}: |
+ purchase_req = self.browse(cr, uid, ref("purchase_requisition_agreement"))
+ self.pool.get('purchase.order.line').action_confirm(cr, uid, [purchase_req.purchase_ids[0].order_line[0].id])
+-
+ I close the call for bids
+-
+ !python {model: purchase.requisition}: |
+ self.close_callforbids(cr, uid, [ref("purchase_requisition_agreement")])
+-
+ I mark the tender as agreement selected
+-
+ !python {model: purchase.requisition}: |
+ self.agreement_selected(cr, uid, ref("purchase_requisition_agreement"))
+ purchase_req = self.browse(cr, uid, ref("purchase_requisition_agreement"))
+ assert purchase_req.state == 'agreement_selected'
=== added directory 'framework_agreement_requisition/view'
=== added file 'framework_agreement_requisition/view/purchase_requisition_view.xml'
--- framework_agreement_requisition/view/purchase_requisition_view.xml 1970-01-01 00:00:00 +0000
+++ framework_agreement_requisition/view/purchase_requisition_view.xml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data noupdate="0">
+ <record model="ir.ui.view" id="view_purchase_requisition_form_agreement">
+ <field name="name">purchase.requisition.form.inherit.aggrement.button</field>
+ <field name="model">purchase.requisition</field>
+ <field name="inherit_id" ref="purchase_requisition.view_purchase_requisition_form"/>
+ <field name="arch" type="xml">
+ <field name="multiple_rfq_per_supplier"
+ position="after">
+ <field name="framework_agreement_tender"/>
+ </field>
+ <button name="cancel_requisition" position="after">
+ <button name="agreement_selected"
+ type="object"
+ class="po_buttons oe_form_buttons"
+ attrs="{'invisible': ['|', ('framework_agreement_tender', '=', False), ('state', '!=', 'closed')]}"
+ string="Framework agreement selected"/>
+ </button>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_purchase_requisition_filter">
+ <field name="name">purchase.requisition.form.inherit.agreement.filter</field>
+ <field name="model">purchase.requisition</field>
+ <field name="inherit_id" ref="purchase_requisition.view_purchase_requisition_filter"/>
+ <field name="arch" type="xml">
+ <filter name="draft"
+ position="after">
+ <filter icon="terp-document-new"
+ name="framework_agreement"
+ string="Framework Agreement?"
+ domain="[(framework_agreement_tender,'=',True)]"/>
+ </filter>
+ </field>
+ </record>
+
+ </data>
+</openerp>
=== added directory 'framework_agreement_sourcing'
=== added file 'framework_agreement_sourcing/__init__.py'
--- framework_agreement_sourcing/__init__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import model
+from . import wizard
=== added file 'framework_agreement_sourcing/__openerp__.py'
--- framework_agreement_sourcing/__openerp__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/__openerp__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+{'name': 'Framework agreement integration in sourcing',
+ 'version': '0.1',
+ 'author': 'Camptocamp',
+ 'maintainer': 'Camptocamp',
+ 'category': 'NGO',
+ 'complexity': 'normal',
+ 'depends': ['framework_agreement', 'logistic_requisition'],
+ 'description': """
+Automatically source logistic order from framework agreement
+============================================================
+
+If you have a framework agreement negociated for the current product in
+your logistic requisition. If the date and state of agreement are OK,
+agreement will be used as source for the concerned source lines
+of your request.
+
+In this case tender flow is byassed and confirmed PO will be generated
+when logistic requisition is confirmed.
+
+When confirming Logistic request sourcing lines are generating.
+Generation process will look up all agreements with remaining quantity
+and use them one after the other.
+
+We will first choose cheapest agreements with price in negociated currency even
+if they are cheaper in other currences.
+
+Then we will choose remaining agreements ordered
+by price converted in company currency.
+
+You can prevent this behavior by forcing only one agreement per product at
+the same time in company.
+
+""",
+ 'website': 'http://www.camptocamp.com',
+ 'data': [
+ 'view/requisition_view.xml',
+ 'wizard/logistic_requisition_source_create_po_view.xml',
+ 'security/ir.model.access.csv'
+ ],
+ 'demo': [],
+ 'test': [],
+ 'installable': True,
+ 'auto_install': False,
+ 'license': 'AGPL-3',
+ 'application': False,
+ }
=== added directory 'framework_agreement_sourcing/i18n'
=== added file 'framework_agreement_sourcing/i18n/en_US.po'
--- framework_agreement_sourcing/i18n/en_US.po 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/i18n/en_US.po 2014-03-10 15:39:56 +0000
@@ -0,0 +1,220 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * framework_agreement_sourcing
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-02-04 10:39+0000\n"
+"PO-Revision-Date: 2014-02-04 10:39+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:72
+#, python-format
+msgid "Please cancel uneeded one"
+msgstr "Please cancel uneeded one"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_cost_estimate.py:64
+#, python-format
+msgid "Please add one"
+msgstr "Please add one"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:240
+#, python-format
+msgid "Source line must be of type other or agreement"
+msgstr "Source line must be of type other or agreement"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_cost_estimate.py:63
+#, python-format
+msgid "No stockable product in related PO"
+msgstr "No stockable product in related PO"
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_source_create_agr_po
+msgid "logistic.requisition.source.create.agr.po"
+msgstr "logistic.requisition.source.create.agr.po"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Which pricelist / currency you want ?"
+msgstr "Which pricelist / currency you want ?"
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_line
+msgid "Logistic Requisition Line"
+msgstr "Logistic Requisition Line"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "{'invisible': [('procurement_method', '=', 'fw_agreement')]}"
+msgstr "{'invisible': [('procurement_method', '=', 'fw_agreement')]}"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "onchange_sourcing_method(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)"
+msgstr "onchange_sourcing_method(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)"
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_cost_estimate
+msgid "Create cost estimate of logistic requisition lines"
+msgstr "Create cost estimate of logistic requisition lines"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:71
+#, python-format
+msgid "Many Purchase order lines found for %s"
+msgstr "Many Purchase order lines found for %s"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:241
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:246
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:249
+#, python-format
+msgid "Please correct selection"
+msgstr "Please correct selection"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:248
+#, python-format
+msgid "There should be at least one agreement line"
+msgstr "There should be at least one agreement line"
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source,framework_agreement_id:0
+msgid "Agreement"
+msgstr "Agreement"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:245
+#, python-format
+msgid "There should be only one agreement line"
+msgstr "There should be only one agreement line"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/wizard/logistic_requisition_source_create_po.py:85
+#, python-format
+msgid "User Error"
+msgstr "User Error"
+
+#. module: framework_agreement_sourcing
+#: model:ir.actions.act_window,name:framework_agreement_sourcing.action_view_create_agr_po_from_source
+msgid "Create Agreement Purchase Order"
+msgstr "Create Agreement Purchase Order"
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source.create.agr.po,pricelist_id:0
+msgid "Pricelist / Currency"
+msgstr "Pricelist / Currency"
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source.create.agr.po,framework_currency_ids:0
+msgid "Available Currency"
+msgstr "Available Currency"
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source,supplier_id:0
+msgid "Agreement Supplier"
+msgstr "Agreement Supplier"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid " Please note that: \n"
+" \n"
+" Requisition will only be created if: \n"
+" * Lines belong to the same company \n"
+" * There is only one agreement line in selection \n"
+" * Products are define on all selected lines \n"
+" * Non agreement line are of type other \n"
+" \n"
+" \n"
+" "
+msgstr " Please note that: \n"
+" \n"
+" Requisition will only be created if: \n"
+" * Lines belong to the same company \n"
+" * There is only one agreement line in selection \n"
+" * Products are define on all selected lines \n"
+" * Non agreement line are of type other \n"
+" \n"
+" \n"
+" "
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_purchase_order
+msgid "Purchase Order"
+msgstr "Purchase Order"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:327
+#, python-format
+msgid "You have ask for a quantity of %s \n"
+" but there is only %s available for current agreement"
+msgstr "You have ask for a quantity of %s \n"
+" but there is only %s available for current agreement"
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_source
+msgid "Logistic Requisition Source"
+msgstr "Logistic Requisition Source"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "You can only chose a pricelist with a currency that is included in the framewrok agreement you have chosen."
+msgstr "You can only chose a pricelist with a currency that is included in the framewrok agreement you have chosen."
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "onchange_quantity(procurement_method, requisition_line_id, proposed_qty, proposed_product_id)"
+msgstr "onchange_quantity(procurement_method, requisition_line_id, proposed_qty, proposed_product_id)"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Create Purchase Order"
+msgstr "Create Purchase Order"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/wizard/logistic_requisition_source_create_po.py:85
+#, python-format
+msgid "You must chose a pricelist that is in the same currency than one of the available in the framework agreement."
+msgstr "You must chose a pricelist that is in the same currency than one of the available in the framework agreement."
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source,framework_agreement_po_id:0
+msgid "Agreement Purchase"
+msgstr "Agreement Purchase"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Cancel"
+msgstr "Cancel"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "onchange_product_id(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)"
+msgstr "onchange_product_id(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Are you sure you want to create a Purchase Order from those lines ?"
+msgstr "Are you sure you want to create a Purchase Order from those lines ?"
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "or"
+msgstr "or"
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_sale_order_line
+msgid "Sales Order Line"
+msgstr "Sales Order Line"
+
=== added file 'framework_agreement_sourcing/i18n/framework_agreement_sourcing.pot'
--- framework_agreement_sourcing/i18n/framework_agreement_sourcing.pot 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/i18n/framework_agreement_sourcing.pot 2014-03-10 15:39:56 +0000
@@ -0,0 +1,209 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * framework_agreement_sourcing
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-02-04 10:37+0000\n"
+"PO-Revision-Date: 2014-02-04 10:37+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:72
+#, python-format
+msgid "Please cancel uneeded one"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_cost_estimate.py:64
+#, python-format
+msgid "Please add one"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:240
+#, python-format
+msgid "Source line must be of type other or agreement"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_cost_estimate.py:63
+#, python-format
+msgid "No stockable product in related PO"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_source_create_agr_po
+msgid "logistic.requisition.source.create.agr.po"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Which pricelist / currency you want ?"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_line
+msgid "Logistic Requisition Line"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "{'invisible': [('procurement_method', '=', 'fw_agreement')]}"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "onchange_sourcing_method(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_cost_estimate
+msgid "Create cost estimate of logistic requisition lines"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:71
+#, python-format
+msgid "Many Purchase order lines found for %s"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:241
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:246
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:249
+#, python-format
+msgid "Please correct selection"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:248
+#, python-format
+msgid "There should be at least one agreement line"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source,framework_agreement_id:0
+msgid "Agreement"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:245
+#, python-format
+msgid "There should be only one agreement line"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/wizard/logistic_requisition_source_create_po.py:85
+#, python-format
+msgid "User Error"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: model:ir.actions.act_window,name:framework_agreement_sourcing.action_view_create_agr_po_from_source
+msgid "Create Agreement Purchase Order"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source.create.agr.po,pricelist_id:0
+msgid "Pricelist / Currency"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source.create.agr.po,framework_currency_ids:0
+msgid "Available Currency"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source,supplier_id:0
+msgid "Agreement Supplier"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid " Please note that: \n"
+" \n"
+" Requisition will only be created if: \n"
+" * Lines belong to the same company \n"
+" * There is only one agreement line in selection \n"
+" * Products are define on all selected lines \n"
+" * Non agreement line are of type other \n"
+" \n"
+" \n"
+" "
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_purchase_order
+msgid "Purchase Order"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/model/logistic_requisition_source.py:327
+#, python-format
+msgid "You have ask for a quantity of %s \n"
+" but there is only %s available for current agreement"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_logistic_requisition_source
+msgid "Logistic Requisition Source"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "You can only chose a pricelist with a currency that is included in the framewrok agreement you have chosen."
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "onchange_quantity(procurement_method, requisition_line_id, proposed_qty, proposed_product_id)"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Create Purchase Order"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: code:addons/framework_agreement_sourcing/wizard/logistic_requisition_source_create_po.py:85
+#, python-format
+msgid "You must chose a pricelist that is in the same currency than one of the available in the framework agreement."
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: field:logistic.requisition.source,framework_agreement_po_id:0
+msgid "Agreement Purchase"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Cancel"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source:0
+msgid "onchange_product_id(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "Are you sure you want to create a Purchase Order from those lines ?"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: view:logistic.requisition.source.create.agr.po:0
+msgid "or"
+msgstr ""
+
+#. module: framework_agreement_sourcing
+#: model:ir.model,name:framework_agreement_sourcing.model_sale_order_line
+msgid "Sales Order Line"
+msgstr ""
=== added directory 'framework_agreement_sourcing/model'
=== added file 'framework_agreement_sourcing/model/__init__.py'
--- framework_agreement_sourcing/model/__init__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/model/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import logistic_requisition
+from . import logistic_requisition_source
+from . import purchase
+from . import sale_order
+from . import logistic_requisition_cost_estimate
=== added file 'framework_agreement_sourcing/model/logistic_requisition.py'
--- framework_agreement_sourcing/model/logistic_requisition.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/model/logistic_requisition.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,243 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from collections import namedtuple
+from openerp.tools.translate import _
+from openerp.osv import orm
+from .logistic_requisition_source import AGR_PROC
+
+
+class logistic_requisition_line(orm.Model):
+ """Override to enable generation of source line"""
+
+ _inherit = "logistic.requisition.line"
+
+ def _prepare_line_source(self, cr, uid, line,
+ qty=None, agreement=None,
+ context=None):
+ """Prepare data dict for source line creation. If an agreement
+ is given, the procurement_method will be an LTA (AGR_PROC).
+ Otherwise, if it's a stockable product we'll go to tender
+ by setting procurement_method as 'procurement'. Finally marke the
+ rest as 'other'. Those are default value that can be changed afterward
+ by the user.
+
+ :params line: browse record of origin requistion.line
+ :params agreement: browse record of origin agreement
+ :params qty: quantity to be set on source line
+
+ :returns: dict to be used by Model.create
+
+ """
+ res = {}
+ res['proposed_product_id'] = line.product_id.id
+ res['requisition_line_id'] = line.id
+ res['proposed_uom_id'] = line.requested_uom_id.id
+ res['unit_cost'] = 0.0
+ res['proposed_qty'] = qty
+ res['framework_agreement_id'] = False
+ if agreement:
+ if not agreement.product_id.id == line.product_id.id:
+ raise ValueError("Product mismatch for agreement and requisition line")
+ res['framework_agreement_id'] = agreement.id
+ res['procurement_method'] = AGR_PROC
+ else:
+ if line.product_id.type == 'product':
+ res['procurement_method'] = 'procurement'
+ else:
+ res['procurement_method'] = 'other'
+ return res
+
+ def _sort_agreements(self, cr, uid, agreements, qty, currency=None,
+ context=None):
+ """Sort agreements to be proposed
+
+ Agreement with negociated currency will first be taken in account
+ then they will be choosen by price converted in currency company
+
+ :param agreements: list of agreements to be sorted
+ :param currency: prefered currrency
+
+ :returns: sorted agreements list
+
+ """
+ if not agreements:
+ return agreements
+
+ def _best_company_price(cr, uid, agreement, qty):
+ """Returns the best price in company currency
+
+ For given agreement and price
+
+ """
+ comp_id = self.pool['framework.agreement']._company_get(cr, uid)
+ comp_obj = self.pool['res.company']
+ currency_obj = self.pool['res.currency']
+ comp_currency = comp_obj.browse(cr, uid, comp_id,
+ context=context).currency_id
+ prices = []
+ for pl in agreement.framework_agreement_pricelist_ids:
+ price = agreement.get_price(qty, currency=pl.currency_id)
+ comp_price = currency_obj.compute(cr, uid,
+ pl.currency_id.id,
+ comp_currency.id,
+ price, False)
+ prices.append(comp_price)
+ return min(prices)
+
+ firsts = []
+ if currency:
+ firsts = [x for x in agreements if x.has_currency(currency)]
+ lasts = [x for x in agreements if not x.has_currency(currency)]
+ firsts.sort(key=lambda x: x.get_price(qty, currency=currency))
+ lasts.sort(key=lambda x: _best_company_price(cr, uid, x, qty))
+ return firsts + lasts
+ else:
+ agreements.sort(key=lambda x: _best_company_price(cr, uid, x, qty))
+ return agreements
+
+ def _generate_lines_from_agreements(self, cr, uid, container, line,
+ agreements, qty, currency=None, context=None):
+ """Generate 1/n source line(s) for one requisition line.
+
+ This is done using available agreements.
+ We first look for cheapeast agreement.
+ Then if no more quantity are available and there is still remaining
+ needs we look for next cheapest agreement or return remaining qty.
+ we prefer to use agreement with negociated currency first even
+ if they are cheaper in other currences. Then it will choose remaining
+ agreements ordered by price converted in company currency
+
+ :param container: list of agreements browse
+ :param qty: quantity to be sourced
+ :param line: origin requisition line
+
+ :returns: remaining quantity to source
+
+ """
+ agreements = agreements if agreements is not None else []
+ agreements = self._sort_agreements(cr, uid, agreements, qty,
+ currency=currency)
+ if not agreements:
+ return qty
+ current_agr = agreements.pop(0)
+ avail = current_agr.available_quantity
+ if not avail:
+ return qty
+ avail_sold = avail - qty
+ to_consume = qty if avail_sold >= 0 else avail
+
+ source_id = self.make_source_line(cr, uid, line, force_qty=to_consume,
+ agreement=current_agr, context=context)
+ container.append(source_id)
+ difference = qty - to_consume
+ if difference:
+ return self._generate_lines_from_agreements(cr, uid, container, line,
+ agreements, difference,
+ context=context)
+ else:
+ return 0
+
+ def _source_lines_for_agreements(self, cr, uid, line, agreements, currency=None, context=None):
+ """Generate 1/n source line(s) for one requisition line
+
+ This is done using available agreements.
+ We first look for cheapeast agreement.
+ Then if no more quantity are available and there is still remaining needs
+ we look for next cheapest agreement or we create a tender source line
+
+ :param line: requisition line browse record
+ :returns: (generated line ids, remaining qty not covered by agreement)
+
+ """
+ Sourced = namedtuple('Sourced', ['generated', 'remaining'])
+ qty = line.requested_qty
+ generated = []
+ remaining_qty = self._generate_lines_from_agreements(cr, uid, generated,
+ line, agreements, qty,
+ currency=currency, context=context)
+ return Sourced(generated, remaining_qty)
+
+ def make_source_line(self, cr, uid, line, force_qty=None, agreement=None, context=None):
+ """Generate a source line from a requisition line, see
+ _prepare_line_source for details.
+
+ :param line: browse record of origin logistic.request
+ :param force_qty: if set this quantity will be used instead
+ of requested quantity
+ :returns: id of generated source line
+
+ """
+ qty = force_qty if force_qty else line.requested_qty
+ src_obj = self.pool['logistic.requisition.source']
+ vals = self._prepare_line_source(cr, uid, line,
+ qty=qty,
+ agreement=agreement,
+ context=None)
+ return src_obj.create(cr, uid, vals, context=context)
+
+ def _generate_source_line(self, cr, uid, line, context=None):
+ """Generate one or n source line(s) per requisition line.
+
+ Depending on the available resources. If there is framework agreement(s)
+ running we generate one or n source line using agreements otherwise we generate one
+ source line using tender process
+
+ :param line: browse record of origin logistic.request
+
+ :returns: list of generated source line ids
+
+ """
+ if line.source_ids:
+ return None
+ agr_obj = self.pool['framework.agreement']
+ date = line.requisition_id.date
+ currency = line.currency_id
+ product_id = line.product_id.id
+ agreements = agr_obj.get_all_product_agreements(cr, uid, product_id, date,
+ context=context)
+ generated_lines = []
+ if agreements:
+ line_ids, missing_qty = self._source_lines_for_agreements(cr, uid, line,
+ agreements, currency=currency)
+ generated_lines.extend(line_ids)
+ if missing_qty:
+ generated_lines.append(self.make_source_line(cr, uid, line,
+ force_qty=missing_qty))
+ else:
+ generated_lines.append(self.make_source_line(cr, uid, line))
+
+ return generated_lines
+
+ def _do_confirm(self, cr, uid, ids, context=None):
+ """Override to generate source lines from requision line.
+
+ Please refer to _generate_source_line documentation
+
+ """
+ # TODO refactor
+ # this should probably be in logistic_requisition module
+ # providing a mechanism to allow each type of sourcing method
+ # to generate source line
+ res = super(logistic_requisition_line, self)._do_confirm(cr, uid, ids,
+ context=context)
+ for line_br in self.browse(cr, uid, ids, context=context):
+ self._generate_source_line(cr, uid, line_br, context=context)
+ return res
=== added file 'framework_agreement_sourcing/model/logistic_requisition_cost_estimate.py'
--- framework_agreement_sourcing/model/logistic_requisition_cost_estimate.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/model/logistic_requisition_cost_estimate.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import orm
+from openerp.tools.translate import _
+from .logistic_requisition_source import AGR_PROC
+
+
+class logistic_requisition_cost_estimate(orm.Model):
+ """Add update of agreement price"""
+
+ _inherit = "logistic.requisition.cost.estimate"
+
+
+ def _prepare_cost_estimate_line(self, cr, uid, sourcing, context=None):
+ """Override in order to update agreement source line
+
+ """
+ res = super(logistic_requisition_cost_estimate,
+ self)._prepare_cost_estimate_line(cr, uid, sourcing,
+ context=context)
+
+ if sourcing.procurement_method == AGR_PROC:
+ res['type'] = 'make_to_order'
+ res['sale_flow'] = 'direct_delivery'
+ return res
+
+ def _link_po_lines_to_so_lines(self, cr, uid, so, sources, context=None):
+ """Naive implementation to link all PO lines to SO lines.
+
+ For our actuall need we want to link all service line
+ to SO real product lines.
+
+ There should not be twice the same product on differents
+ Agreement PO line so this case in not handled
+
+ """
+
+ so_lines = [x for x in so.order_line if x.product_id]
+ po_lines = set(x.purchase_line_id for x in sources
+ if x.purchase_line_id)
+ product_dict = dict((x.product_id.id, x.id) for x in so_lines
+ if x.product_id)
+ default = product_dict[product_dict.keys()[0]]
+ if not product_dict:
+ raise orm.except_orm(_('No stockable product in related PO'),
+ _('Please add one'))
+ for po_line in po_lines:
+ key = po_line.product_id.id if po_line.product_id else False
+ po_line.write({'sale_order_line_id': product_dict.get(key, default)})
+
+ def cost_estimate(self, cr, uid, ids, context=None):
+ """Override to link PO to cost_estimate$
+
+ In a normal flow, when you chose a bid as the winning one, the bid is
+ dupplicated to generate the draft PO. On this action, it link the LRS
+ to the generated PO line.
+
+ In a tender flow, we don't dupplicate the bid, it's only a PO. The link
+ between the LRS and the PO line should then be created here.
+
+ This is for the drop shipping to work propely cause in that case, SO
+ and PO are linked together.
+ """
+ so_model = self.pool['sale.order']
+ po_model = self.pool['purchase.order']
+ res = super(logistic_requisition_cost_estimate,
+ self).cost_estimate(cr, uid, ids, context=context)
+ so_id = res['res_id']
+ order = so_model.browse(cr, uid, so_id, context=context)
+ # Can be optimized with a SQL or a search but
+ # gain of perfo will not worth readability loss
+ # for such small data set
+ sources = [x.logistic_requisition_source_id for x in order.order_line
+ if x and x.logistic_requisition_source_id.procurement_method == AGR_PROC]
+ po_ids = set(x.purchase_line_id.order_id.id for x in sources
+ if x.purchase_line_id)
+ po_model.write(cr, uid, list(po_ids),
+ {'sale_id': so_id,
+ 'sale_flow': 'direct_delivery'})
+ self._link_po_lines_to_so_lines(cr, uid, order, sources, context=context)
+ return res
=== added file 'framework_agreement_sourcing/model/logistic_requisition_source.py'
--- framework_agreement_sourcing/model/logistic_requisition_source.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/model/logistic_requisition_source.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,389 @@
+ # -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from itertools import chain
+from openerp.osv import orm, fields
+from openerp.tools.translate import _
+from openerp.addons.framework_agreement.model.framework_agreement import\
+ FrameworkAgreementObservable
+from openerp.addons.framework_agreement.utils import id_boilerplate
+
+
+AGR_PROC = 'fw_agreement'
+
+
+class logistic_requisition_source(orm.Model, FrameworkAgreementObservable):
+ """Adds support of framework agreement to source line"""
+
+ _inherit = "logistic.requisition.source"
+
+ _columns = {'framework_agreement_id': fields.many2one('framework.agreement',
+ 'Agreement'),
+ 'framework_agreement_po_id': fields.many2one('purchase.order',
+ 'Agreement Purchase'),
+ 'supplier_id': fields.related('framework_agreement_id', 'supplier_id',
+ type='many2one', relation='res.partner',
+ string='Agreement Supplier')}
+
+ def _get_procur_method_hook(self, cr, uid, context=None):
+ """Adds framework agreement as a procurement method in selection field"""
+ res = super(logistic_requisition_source, self)._get_procur_method_hook(cr, uid,
+ context=context)
+ res.append((AGR_PROC, 'Framework agreement'))
+ return res
+
+ def _get_purchase_line_id(self, cr, uid, ids, field_name, arg, context=None):
+ """For each source line, get the related purchase order line
+
+ For more detail please refer to function fields documentation
+
+ """
+ po_line_model = self.pool['purchase.order.line']
+ res = super(logistic_requisition_source, self)._get_purchase_line_id(cr, uid, ids,
+ field_name,
+ arg,
+ context=context)
+ for line in self.browse(cr, uid, ids, context=context):
+ if line.procurement_method == AGR_PROC:
+ po_l_ids = po_line_model.search(cr, uid,
+ [('lr_source_line_id', '=', line.id),
+ ('state', '!=', 'cancel')],
+ context=context)
+ if po_l_ids:
+ if len(po_l_ids) > 1:
+ raise orm.except_orm(_('Many Purchase order lines found for %s') % line.name,
+ _('Please cancel uneeded one'))
+ res[line.id] = po_l_ids[0]
+ else:
+ res[line.id] = False
+ return res
+
+ #------------------ adapting source line to po -----------------------------
+ def _company(self, cr, uid, context):
+ """Return company id
+
+ :returns: company id
+
+ """
+ return self.pool['res.company']._company_default_get(cr, uid, self._name,
+ context=context)
+
+ def _prepare_purchase_order(self, cr, uid, line, po_pricelist, context=None):
+ """Prepare the dict of values to create the PO from a
+ source line.
+
+ :param browse_record line: logistic.requisition.source
+ :param browse_record pricelist: product.pricelist
+ :returns: data dict to be used by orm.Model.create
+
+ """
+ supplier = line.framework_agreement_id.supplier_id
+ add = line.requisition_id.consignee_shipping_id
+ term = supplier.property_supplier_payment_term
+ term = term.id if term else False
+ position = supplier.property_account_position
+ position = position.id if position else False
+ requisition = line.requisition_id
+ data = {}
+ data['framework_agreement_id'] = line.framework_agreement_id.id
+ data['partner_id'] = supplier.id
+ data['company_id'] = self._company(cr, uid, context)
+ data['pricelist_id'] = po_pricelist.id
+ data['dest_address_id'] = add.id
+ data['location_id'] = add.property_stock_customer.id
+ data['payment_term_id'] = term
+ data['fiscal_position'] = position
+ data['origin'] = requisition.name
+ data['date_order'] = requisition.date
+ # data['name'] = requisition.name
+ data['consignee_id'] = requisition.consignee_id.id
+ data['incoterm_id'] = requisition.incoterm_id.id
+ data['incoterm_address'] = requisition.incoterm_address
+ data['type'] = 'purchase'
+ return data
+
+ def _prepare_purchase_order_line(self, cr, uid, po_id, line,
+ po_supplier, po_pricelist, context=None):
+ """Prepare the dict of values to create the PO Line from args.
+
+ :param integer po_id: ids of purchase.order
+ :param browse_record line: logistic.requisition.source
+ :param browse_record po_supplier: res.partner
+ :param browse_record po_pricelist: product.pricelist
+ :returns: data dict to be used by orm.Model.create
+
+ """
+ data = {}
+ acc_pos_obj = self.pool['account.fiscal.position']
+ pl_model = self.pool['product.pricelist']
+ currency = po_pricelist.currency_id
+
+ if line.framework_agreement_id:
+ price = line.framework_agreement_id.get_price(line.proposed_qty, currency=currency)
+ lead_time = line.framework_agreement_id.delay
+ supplier = line.framework_agreement_id.supplier_id
+ data['framework_agreement_id'] = line.framework_agreement_id.id
+ else:
+ supplier = po_supplier
+ lead_time = 0
+ price = 0.0
+ if po_pricelist:
+ price = pl_model.price_get(cr, uid,
+ [po_pricelist.id],
+ line.proposed_product_id.id,
+ line.proposed_qty or 1.0,
+ po_supplier.id,
+ {'uom': line.proposed_uom_id.id})[po_pricelist.id]
+
+ if not price:
+ price = line.proposed_product_id.standard_price or 1.00
+ taxes_ids = line.proposed_product_id.supplier_taxes_id
+ taxes = acc_pos_obj.map_tax(cr, uid, supplier.property_account_position,
+ taxes_ids)
+
+ data['order_id'] = po_id
+ data['product_qty'] = line.proposed_qty
+ data['product_id'] = line.proposed_product_id.id
+ data['product_uom'] = line.proposed_uom_id.id
+ data['lr_source_line_id']= line.id
+ data['product_lead_time'] = lead_time
+ data['price_unit'] = price
+ data['name'] = line.proposed_product_id.name
+ data['date_planned'] = line.requisition_id.date_delivery
+ data['taxes_id'] = [(6, 0, taxes)]
+ return data
+
+ def _make_po_from_source_lines(self, cr, uid, main_source, other_sources,
+ pricelist, context=None):
+ """Create a purchase order from a source line. After creating it,
+ it'll update the unit_cost of the source line accoring to the PO
+ price. We do this because the currency of the PO may not be the same
+ than the LRS so it may happends that value vary because of exchange
+ rate.
+
+ :param browse_record main_source: logistic.requisition.source of
+ type LTA from which you want to generate to PO
+ :param browse_record other_sources: logistic.requisition.source of
+ type other to indlue in the PO
+ :param browse_record pricelist: product.pricelist to be used in PO to
+ know the currency mainly (as the prices will be computed from LTA)
+ :returns integer : generated PO id
+
+ """
+ if context is None:
+ context = {}
+ context['draft_po'] = True
+ currency_obj = self.pool['res.currency']
+ po_obj = self.pool['purchase.order']
+ po_l_obj = self.pool['purchase.order.line']
+ supplier = main_source.framework_agreement_id.supplier_id
+ to_curr = pricelist.currency_id.id
+ po_vals = self._prepare_purchase_order(cr, uid, main_source,
+ pricelist, context=context)
+ po_id = po_obj.create(cr, uid, po_vals, context=context)
+ other_sources = other_sources if other_sources else []
+ for source in chain([main_source], other_sources):
+ line_vals = self._prepare_purchase_order_line(cr, uid, po_id,
+ source, supplier,
+ pricelist, context=context)
+ po_l_obj.create(cr, uid, line_vals, context=context)
+ # TODO: Update LRS unit_cost from po line, with currency conversion
+ from_curr = source.requisition_id.currency_id.id
+ # Compute from bid currency to LRS currency
+ price = currency_obj.compute(cr, uid, from_curr, to_curr,
+ line_vals['price_unit'], False)
+ source.write({'framework_agreement_po_id': po_id, 'unit_cost':price})
+ return po_id
+
+ def make_purchase_order(self, cr, uid, ids, pricelist, context=None):
+ """Create a purchase order from the LRS ids list. This method will
+ create one PO with all lines. Between them, you'll have line of type
+ LTA (framewrok agreement) and line of type other.
+ Currently, only one line of type LTA is accepted at a time.
+
+ We'll raise an error if other types are selected here.
+ We accept line of type other here to include products not included
+ in the LTA for example : you order Product A under LTA + the transport
+ as a LRS of type other.
+
+ :param integer list ids: ids of logistic.requisition.source
+ :param browse_record pricelist: product.pricelist
+ :returns integer : generated PO id
+
+ """
+ sources = self.browse(cr, uid, ids, context=context)
+ # LRS of type LTA (framework agreement)
+ agreement_sources = []
+ # LRS of type other
+ other_sources = []
+ for source in sources:
+ if source.procurement_method == AGR_PROC:
+ agreement_sources.append(source)
+ elif source.procurement_method == 'other':
+ other_sources.append(source)
+ else:
+ raise orm.except_orm(_('Source line must be of type other or agreement'),
+ _('Please correct selection'))
+
+ main_source = agreement_sources[0] if agreement_sources else False
+ if len(agreement_sources) > 1:
+ raise orm.except_orm(_('There should be only one agreement line'),
+ _('Please correct selection'))
+ if not main_source:
+ raise orm.except_orm(_('There should be at least one agreement line'),
+ _('Please correct selection'))
+ fback = main_source.framework_agreement_id.supplier_id.property_product_pricelist_purchase
+ pricelist = pricelist if pricelist else fback
+ po_id = self._make_po_from_source_lines(cr, uid, main_source,
+ other_sources, pricelist, context=None)
+ return po_id
+
+ def _is_sourced_other(self, cr, uid, source, context=None):
+ """Predicate function to test if line on other
+ method are sourced"""
+ tender_ok = self._is_sourced_procurement(cr, uid, source,
+ context=context)
+ agr_ok = self._is_sourced_fw_agreement(cr, uid, source,
+ context=context)
+ return (tender_ok or agr_ok)
+
+ def _is_sourced_fw_agreement(self, cr, uid, source, context=None):
+ """Predicate that tells if source line of type agreement are sourced
+
+ :retuns: boolean True if sourced
+
+ """
+ po_line_obj = self.pool['purchase.order.line']
+ sources_ids = po_line_obj.search(cr, uid,
+ [('lr_source_line_id', '=', source.id)],
+ context=context)
+ # predicate
+ return bool(sources_ids)
+
+ #---------------OpenERP tedious onchange management ------------------------
+
+ def _get_date(self, cr, uid, requision_line_id, context=None):
+ """helper to retrive date to be used by framework agreement
+ when in source line context
+
+ :param source_id: requisition.line.source id that should
+ provide date
+
+ :returns: date/datetime string
+
+ """
+ req_obj = self.pool['logistic.requisition.line']
+ current = req_obj.browse(cr, uid, requision_line_id, context=context)
+ now = fields.datetime.now()
+ return current.requisition_id.date or now
+
+ @id_boilerplate
+ def onchange_sourcing_method(self, cr, uid, ids, method, req_line_id, proposed_product_id,
+ proposed_qty=0, context=None):
+ """
+ Called when source method is set on a source line.
+
+ If sourcing method is framework agreement
+ it will set price, agreement and supplier if possible
+ and raise quantity warning.
+
+ """
+ line_source = self.browse(cr, uid, ids, context=context)
+ res = {'value': {'framework_agreement_id': False}}
+ if (method != AGR_PROC or not proposed_product_id):
+ return res
+ currency = line_source.currency_id
+ agreement_obj = self.pool['framework.agreement']
+ date = self._get_date(cr, uid, req_line_id, context=context)
+ agreement, enough_qty = agreement_obj.get_cheapest_agreement_for_qty(cr, uid,
+ proposed_product_id,
+ date,
+ proposed_qty,
+ currency=currency,
+ context=context)
+ if not agreement:
+ return res
+ price = agreement.get_price(proposed_qty, currency=currency)
+ res['value'] = {'framework_agreement_id': agreement.id,
+ 'unit_cost': price,
+ 'total_cost': price * proposed_qty,
+ 'supplier_id': agreement.supplier_id.id}
+ if not enough_qty:
+ msg = _("You have ask for a quantity of %s \n"
+ " but there is only %s available"
+ " for current agreement") % (proposed_qty,
+ agreement.available_quantity)
+ res['warning'] = msg
+ return res
+
+ @id_boilerplate
+ def onchange_quantity(self, cr, uid, ids, method, req_line_id, qty,
+ proposed_product_id, context=None):
+ """Raise a warning if agreed qty is not sufficient"""
+ line_source = self.browse(cr, uid, ids, context=context)
+ if (method != AGR_PROC or not proposed_product_id):
+ return {}
+ currency = line_source.currency_id
+ date = self._get_date(cr, uid, req_line_id, context=context)
+ return self.onchange_quantity_obs(cr, uid, ids, qty, date,
+ proposed_product_id,
+ currency=currency,
+ price_field='dummy',
+ context=context)
+
+ @id_boilerplate
+ def onchange_product_id(self, cr, uid, ids, method, req_line_id,
+ proposed_product_id, proposed_qty,
+ context=None):
+ """Call when product is set on a source line.
+
+ If sourcing method is framework agreement
+ it will set price, agreement and supplier if possible
+ and raise quantity warning.
+
+ """
+ if method != AGR_PROC:
+ if proposed_product_id:
+ value = {'proposed_uom_id': ''}
+ if proposed_product_id:
+ prod_obj = self.pool.get('product.product')
+ prod = prod_obj.browse(cr, uid, proposed_product_id, context=context)
+ value = {
+ 'proposed_uom_id': prod.uom_id.id,
+ }
+ return {'value': value}
+ return {}
+
+ return self.onchange_sourcing_method(cr, uid, ids, method, req_line_id,
+ proposed_product_id,
+ proposed_qty=proposed_qty,
+ context=context)
+
+ @id_boilerplate
+ def onchange_agreement(self, cr, uid, ids, agreement_id, req_line_id, qty,
+ proposed_product_id, context=None):
+ line_source = self.browse(cr, uid, ids, context=context)
+ if not proposed_product_id or not agreement_id:
+ return {}
+ currency = line_source.currency_id
+ date = self._get_date(cr, uid, req_line_id, context=context)
+ return self.onchange_agreement_obs(cr, uid, ids, agreement_id, qty,
+ date, proposed_product_id,
+ currency=currency, price_field='dummy')
=== added file 'framework_agreement_sourcing/model/purchase.py'
--- framework_agreement_sourcing/model/purchase.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/model/purchase.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import orm
+
+class purchase_order(orm.Model):
+ """ Override action_confirm to set quantity bid if LTA
+ """
+
+ _inherit = "purchase.order"
+
+
+ def action_confirm(self, cr, uid, ids, context=None):
+ super(purchase_order_line, self).action_confirm(cr, uid, ids, context=context)
+ for element in self.browse(cr, uid, ids, context=context):
+ if not element.quantity_bid and not element.framework_agreement_id:
+ self.write(cr, uid, ids, {'quantity_bid': element.product_qty}, context=context)
+ return True
+
=== added file 'framework_agreement_sourcing/model/sale_order.py'
--- framework_agreement_sourcing/model/sale_order.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/model/sale_order.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp import netsvc
+from openerp.osv import orm
+from .logistic_requisition_source import AGR_PROC
+
+
+class sale_order_line(orm.Model):
+ """Pass agreement PO into state confirmed when SO is confirmed"""
+
+ _inherit = "sale.order.line"
+
+ def button_confirm(self, cr, uid, ids, context=None):
+ """Override confirmation of request of cotation to support LTA
+
+ Related PO generated by agreement source line will be passed to state confirm.
+
+ """
+ def source_valid(source):
+ if source and source.procurement_method == AGR_PROC:
+ return True
+ return False
+ result = super(sale_order_line, self).button_confirm(cr, uid, ids,
+ context=context)
+ po_line_model = self.pool['purchase.order.line']
+ po_model = self.pool['purchase.order']
+
+ lines = self.browse(cr, uid, ids, context=context)
+ source_ids = [x.logistic_requisition_source_id.id for x in lines
+ if source_valid(x.logistic_requisition_source_id)]
+ po_line_ids = po_line_model.search(cr, uid,
+ [('lr_source_line_id', 'in', source_ids)],
+ context=context)
+ po_lines = po_line_model.read(cr, uid, po_line_ids, ['order_id'],
+ load='_classic_write')
+ po_ids = set(x['order_id'] for x in po_lines)
+ wf_service = netsvc.LocalService("workflow")
+ for po in po_model.browse(cr, uid, list(po_ids), context=context):
+ wf_service.trg_validate(uid, 'purchase.order', po.id,
+ 'draft_po', cr)
+ return result
=== added directory 'framework_agreement_sourcing/security'
=== added file 'framework_agreement_sourcing/security/ir.model.access.csv'
--- framework_agreement_sourcing/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/security/ir.model.access.csv 2014-03-10 15:39:56 +0000
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_po_creator,po_creator,model_logistic_requisition_source_create_agr_po,purchase.group_purchase_user,1,1,1,1
=== added directory 'framework_agreement_sourcing/tests'
=== added file 'framework_agreement_sourcing/tests/__init__.py'
--- framework_agreement_sourcing/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/tests/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import common
+from . import test_logistic_order_line_to_source_line
+from . import test_agreement_souce_line_to_po
+checks = [test_logistic_order_line_to_source_line,
+ test_agreement_souce_line_to_po]
=== added file 'framework_agreement_sourcing/tests/common.py'
--- framework_agreement_sourcing/tests/common.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/tests/common.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from datetime import timedelta
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+import openerp.tests.common as test_common
+from openerp.addons.logistic_requisition.tests import logistic_requisition
+from openerp.addons.framework_agreement.tests.common import BaseAgreementTestMixin
+
+
+class CommonSourcingSetUp(test_common.TransactionCase, BaseAgreementTestMixin):
+
+ def setUp(self):
+ """
+ Setup a standard configuration for test
+ """
+ super(CommonSourcingSetUp, self).setUp()
+ self.commonsetUp()
+ self.requisition_model = self.registry('logistic.requisition')
+ self.requisition_line_model = self.registry('logistic.requisition.line')
+ self.source_line_model = self.registry('logistic.requisition.source')
+ self.make_common_agreements()
+ self.make_common_requisition()
+
+ def make_common_requisition(self):
+ """Create a standard logistic requisition"""
+ start_date = self.now + timedelta(days=12)
+ start_date = start_date.strftime(DEFAULT_SERVER_DATE_FORMAT)
+ req = {
+ 'partner_id': self.ref('base.res_partner_1'),
+ 'consignee_id': self.ref('base.res_partner_3'),
+ 'date_delivery': start_date,
+ 'date': start_date,
+ 'user_id': self.uid,
+ 'budget_holder_id': self.uid,
+ 'finance_officer_id': self.uid,
+ 'pricelist_id': self.ref('product.list0'),
+ }
+ agr_line = {
+ 'product_id': self.product_id,
+ 'requested_qty': 100,
+ 'requested_uom_id': self.ref('product.product_uom_unit'),
+ 'date_delivery': self.now.strftime(DEFAULT_SERVER_DATE_FORMAT),
+ 'budget_tot_price': 100000000,
+ }
+ product_line = {
+ 'product_id': self.ref('product.product_product_7'),
+ 'requested_qty': 10,
+ 'requested_uom_id': self.ref('product.product_uom_unit'),
+ 'date_delivery': self.now.strftime(DEFAULT_SERVER_DATE_FORMAT),
+ 'budget_tot_price': 100000000,
+ }
+
+ other_line = {
+ 'product_id': self.ref('logistic_requisition.product_transport'),
+ 'requested_qty': 1,
+ 'requested_uom_id': self.ref('product.product_uom_unit'),
+ 'date_delivery': self.now.strftime(DEFAULT_SERVER_DATE_FORMAT),
+ 'budget_tot_price': 100000000,
+ }
+
+
+ requisition_id = logistic_requisition.create(self, req)
+ logistic_requisition.add_line(self, requisition_id,
+ agr_line)
+ logistic_requisition.add_line(self, requisition_id,
+ product_line)
+ logistic_requisition.add_line(self, requisition_id,
+ other_line)
+ self.requisition = self.requisition_model.browse(self.cr, self.uid, requisition_id)
+
+ def make_common_agreements(self):
+ """Create two default agreements.
+
+ We have two agreement for same product but using
+ different suppliers
+
+ One supplier has a better price for lower qty the other
+ has better price for higher qty
+
+ We also create one requisition with one line of agreement product
+ And one line of other product
+
+ """
+
+ cr, uid = self.cr, self.uid
+ start_date = self.now + timedelta(days=10)
+ start_date = start_date.strftime(DEFAULT_SERVER_DATE_FORMAT)
+ end_date = self.now + timedelta(days=20)
+ end_date = end_date.strftime(DEFAULT_SERVER_DATE_FORMAT)
+ # Agreement 1
+ agr_id = self.agreement_model.create(cr, uid,
+ {'supplier_id': self.supplier_id,
+ 'product_id': self.product_id,
+ 'start_date': start_date,
+ 'end_date': end_date,
+ 'draft': False,
+ 'delay': 5,
+ 'quantity': 2000})
+
+ pl_id = self.agreement_pl_model.create(cr, uid,
+ {'framework_agreement_id': agr_id,
+ 'currency_id': self.ref('base.EUR')})
+ self.agreement_line_model.create(cr, uid,
+ {'framework_agreement_pricelist_id': pl_id,
+ 'quantity': 0,
+ 'price': 77.0})
+
+ self.agreement_line_model.create(cr, uid,
+ {'framework_agreement_pricelist_id': pl_id,
+ 'quantity': 1000,
+ 'price': 30.0})
+
+ self.cheap_on_high_agreement = self.agreement_model.browse(cr, uid, agr_id)
+
+ # Agreement 2
+ agr_id = self.agreement_model.create(cr, uid,
+ {'supplier_id': self.ref('base.res_partner_3'),
+ 'product_id': self.product_id,
+ 'start_date': start_date,
+ 'end_date': end_date,
+ 'draft': False,
+ 'delay': 5,
+ 'quantity': 1200})
+
+ pl_id = self.agreement_pl_model.create(cr, uid,
+ {'framework_agreement_id': agr_id,
+ 'currency_id': self.ref('base.EUR')})
+
+ self.agreement_line_model.create(cr, uid,
+ {'framework_agreement_pricelist_id': pl_id,
+ 'quantity': 0,
+ 'price': 50.0})
+ self.agreement_line_model.create(cr, uid,
+ {'framework_agreement_pricelist_id': pl_id,
+ 'quantity': 1000,
+ 'price': 45.0})
+ self.cheap_on_low_agreement = self.agreement_model.browse(cr, uid, agr_id)
=== added file 'framework_agreement_sourcing/tests/test_agreement_souce_line_to_po.py'
--- framework_agreement_sourcing/tests/test_agreement_souce_line_to_po.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/tests/test_agreement_souce_line_to_po.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from .common import CommonSourcingSetUp
+
+
+class TestSourceToPo(CommonSourcingSetUp):
+
+ def setUp(self):
+ # we generate a source line
+ super(TestSourceToPo, self).setUp()
+ cr, uid = self.cr, self.uid
+ lines = self.requisition.line_ids
+ agr_line = None
+ self.wiz_model = self.registry('logistic.requisition.source.create.agr.po')
+ for line in lines:
+ if line.product_id == self.cheap_on_low_agreement.product_id:
+ agr_line = line
+ break
+ self.assertTrue(agr_line)
+ agr_line.write({'requested_qty': 400})
+ agr_line.refresh()
+ source_ids = []
+ for line in lines:
+ if (line.product_id.id == self.product_id or
+ line.product_id.type == 'service'):
+ lid = self.requisition_line_model._generate_source_line(cr, uid, line)
+ source_ids += lid
+ self.assertTrue(len(source_ids) == 2)
+ self.source_lines = self.source_line_model.browse(cr, uid, source_ids)
+ self.lta_source = next(x for x in self.source_lines
+ if x.procurement_method == 'fw_agreement')
+ self.other_source = next(x for x in self.source_lines
+ if x.procurement_method == 'other')
+
+ def test_01_transform_source_to_agreement(self):
+ """Test transformation of an agreement source line into PO"""
+ cr, uid = self.cr, self.uid
+ self.assertTrue(self.lta_source)
+ self.lta_source.refresh()
+ active_ids = [x.id for x in self.source_lines]
+ wiz_id = self.wiz_model.create(self.cr, self.uid, {},
+ context={'active_ids': active_ids})
+
+ wiz = self.wiz_model.browse(self.cr, self.uid, wiz_id)
+ po_id = wiz.action_create_agreement_po_requisition(
+ context={'active_ids': active_ids}
+ )['res_id']
+ self.assertTrue(po_id)
+ supplier = self.lta_source.framework_agreement_id.supplier_id
+ add = self.lta_source.requisition_id.consignee_shipping_id
+ consignee = self.lta_source.requisition_id.consignee_id
+ po = self.registry('purchase.order').browse(cr, uid, po_id)
+ date_order = self.lta_source.requisition_id.date
+ date_delivery = self.lta_source.requisition_id.date_delivery
+ self.assertEqual(po.partner_id, supplier)
+ self.assertEqual(po.pricelist_id, supplier.property_product_pricelist_purchase)
+ self.assertEqual(po.date_order, date_order)
+ self.assertEqual(po.dest_address_id, add)
+ self.assertEqual(po.consignee_id, consignee)
+ self.assertEqual(po.state, 'draftpo')
+
+ self.assertEqual(len(po.order_line), 2)
+
+ po_line = next(x for x in po.order_line
+ if x.product_id == self.lta_source.framework_agreement_id.product_id)
+ self.assertEqual(po_line.product_qty, self.lta_source.proposed_qty)
+ self.assertEqual(po_line.product_id, self.lta_source.proposed_product_id)
+ self.assertEqual(po_line.product_qty, self.lta_source.proposed_qty)
+ self.assertEqual(po_line.product_uom, self.lta_source.proposed_uom_id)
+ self.assertAlmostEqual(po_line.price_unit, 50.0)
+ self.assertEqual(po_line.lr_source_line_id, self.lta_source)
+ self.assertEqual(po_line.date_planned, date_delivery)
+
+ po_line = next(x for x in po.order_line
+ if x.product_id == self.other_source.proposed_product_id)
+ self.assertEqual(po_line.product_qty, self.other_source.proposed_qty)
+ self.assertEqual(po_line.product_id, self.other_source.proposed_product_id)
+ self.assertEqual(po_line.product_qty, self.other_source.proposed_qty)
+ self.assertEqual(po_line.product_uom, self.other_source.proposed_uom_id)
+ self.assertAlmostEqual(po_line.price_unit, 1.0)
+ self.assertEqual(po_line.lr_source_line_id, self.other_source)
=== added file 'framework_agreement_sourcing/tests/test_logistic_order_line_to_source_line.py'
--- framework_agreement_sourcing/tests/test_logistic_order_line_to_source_line.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/tests/test_logistic_order_line_to_source_line.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from ..model.logistic_requisition_source import AGR_PROC
+from .common import CommonSourcingSetUp
+
+
+class TestTransformation(CommonSourcingSetUp):
+
+ def test_01_enough_qty_on_first_agr(self):
+ """Test that we can source a line with one agreement and low qty"""
+ cr, uid = self.cr, self.uid
+ lines = self.requisition.line_ids
+ agr_line = None
+ for line in lines:
+ if line.product_id == self.cheap_on_low_agreement.product_id:
+ agr_line = line
+ break
+ self.assertTrue(agr_line)
+ agr_line.write({'requested_qty': 400})
+ agr_line.refresh()
+ to_validate_ids = self.requisition_line_model._generate_source_line(cr, uid, agr_line)
+ self.assertTrue(len(to_validate_ids) == 1)
+ to_validate = self.source_line_model.browse(cr, uid, to_validate_ids[0])
+ self.assertEqual(to_validate.procurement_method, AGR_PROC)
+ self.assertAlmostEqual(to_validate.unit_cost, 0.0)
+ self.assertEqual(to_validate.proposed_qty, 400)
+
+ def test_02_enough_qty_on_high_agr(self):
+ """Test that we can source a line correctly on both agreement"""
+ cr, uid = self.cr, self.uid
+ lines = self.requisition.line_ids
+ agr_line = None
+ for line in lines:
+ if line.product_id == self.cheap_on_high_agreement.product_id:
+ agr_line = line
+ break
+ self.assertTrue(agr_line)
+ agr_line.write({'requested_qty': 1500})
+ agr_line.refresh()
+ to_validate_ids = self.requisition_line_model._generate_source_line(cr, uid, agr_line)
+ self.assertTrue(len(to_validate_ids) == 1)
+ to_validate = self.source_line_model.browse(cr, uid, to_validate_ids[0])
+ self.assertEqual(to_validate.procurement_method, AGR_PROC)
+ self.assertAlmostEqual(to_validate.unit_cost, 0.0)
+ self.assertEqual(to_validate.proposed_qty, 1500)
+
+ def test_03_not_enough_qty_on_high_agreement(self):
+ """Test that we can source a line with one agreement and high qty"""
+ cr, uid = self.cr, self.uid
+ lines = self.requisition.line_ids
+ agr_line = None
+ for line in lines:
+ if line.product_id == self.cheap_on_high_agreement.product_id:
+ agr_line = line
+ break
+ self.assertTrue(agr_line)
+ agr_line.write({'requested_qty': 2400})
+ agr_line.refresh()
+ to_validate_ids = self.requisition_line_model._generate_source_line(cr, uid, agr_line)
+ self.assertTrue(len(to_validate_ids) == 2)
+ # We validate generated line
+ to_validates = self.source_line_model.browse(cr, uid, to_validate_ids)
+ # high_line
+ # idiom taken from Python cookbook
+ high_line = next((x for x in to_validates
+ if x.framework_agreement_id == self.cheap_on_high_agreement), None)
+ self.assertTrue(high_line, msg="High agreement was not used")
+ self.assertEqual(high_line.procurement_method, AGR_PROC)
+ self.assertEqual(high_line.proposed_qty, 2000)
+ self.assertAlmostEqual(high_line.unit_cost, 0.0)
+
+ # low_line
+ low_line = next((x for x in to_validates
+ if x.framework_agreement_id == self.cheap_on_low_agreement), None)
+ self.assertTrue(low_line, msg="Low agreement was not used")
+ self.assertEqual(low_line.procurement_method, AGR_PROC)
+ self.assertEqual(low_line.proposed_qty, 400)
+ self.assertAlmostEqual(low_line.unit_cost, 0.0)
+
+ def test_03_not_enough_qty_on_all_agreemenst(self):
+ """Test that we have generate correct line when not enough qty on all agreements
+
+ That means last source line must be of type other or procurement
+ """
+ cr, uid = self.cr, self.uid
+ lines = self.requisition.line_ids
+ agr_line = None
+ for line in lines:
+ if line.product_id == self.cheap_on_high_agreement.product_id:
+ agr_line = line
+ break
+ self.assertTrue(agr_line)
+ agr_line.write({'requested_qty': 5000})
+ agr_line.refresh()
+ to_validate_ids = self.requisition_line_model._generate_source_line(cr, uid, agr_line)
+ self.assertTrue(len(to_validate_ids) == 3)
+ # We validate generated line
+ to_validates = self.source_line_model.browse(cr, uid, to_validate_ids)
+ # high_line
+ # idiom taken from Python cookbook
+ high_line = next((x for x in to_validates
+ if x.framework_agreement_id == self.cheap_on_high_agreement), None)
+ self.assertTrue(high_line, msg="High agreement was not used")
+ self.assertEqual(high_line.procurement_method, AGR_PROC)
+ self.assertEqual(high_line.proposed_qty, 2000)
+ self.assertAlmostEqual(high_line.unit_cost, 0.0)
+
+ # low_line
+ low_line = next((x for x in to_validates
+ if x.framework_agreement_id == self.cheap_on_low_agreement), None)
+ self.assertTrue(low_line, msg="Low agreement was not used")
+ self.assertEqual(low_line.procurement_method, AGR_PROC)
+ self.assertEqual(low_line.proposed_qty, 1200)
+ self.assertAlmostEqual(low_line.unit_cost, 0.0)
+
+ # Tender line
+ tender_line = next((x for x in to_validates
+ if not x.framework_agreement_id), None)
+ self.assertTrue(tender_line, msg="Tender line was not generated")
+ self.assertNotEqual(tender_line.procurement_method, AGR_PROC)
=== added directory 'framework_agreement_sourcing/view'
=== added file 'framework_agreement_sourcing/view/requisition_view.xml'
--- framework_agreement_sourcing/view/requisition_view.xml 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/view/requisition_view.xml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="add_supplier_and_agreement_on_source_line" model="ir.ui.view">
+ <field name="name">add supplier and agrement on source line</field>
+ <field name="model">logistic.requisition.source</field>
+ <field name="inherit_id" ref="logistic_requisition.view_logistic_requisition_source_form"/>
+ <field name="arch" type="xml">
+ <field name="unit_cost"
+ position="before">
+ <field name="framework_agreement_id"
+ domain="[('draft', '=', False)]"
+ attrs="{'required': [('procurement_method', '=', 'fw_agreement')],
+ 'invisible': [('procurement_method', '!=', 'fw_agreement')]}"
+ on_change="onchange_agreement(framework_agreement_id, requisition_line_id, proposed_qty, proposed_product_id, context)"/>
+ <field name="framework_agreement_po_id"
+ readonly="1"
+ attrs="{'invisible': [('procurement_method', 'not in', ['fw_agreement','other'])]}"
+
+ />
+
+ <field name="supplier_id"
+ invisible="1"/>
+ </field>
+ <field name="proposed_uom_id"
+ position="attributes">
+ <attribute name="attrs">{'invisible': [('procurement_method', '=', 'fw_agreement')]}</attribute>
+ </field>
+ <field name="unit_cost"
+ position="attributes">
+ <attribute name="attrs">{'invisible': [('procurement_method', '=', 'fw_agreement')]}</attribute>
+ </field>
+ <field name="total_cost"
+ position="attributes">
+ <attribute name="attrs">{'invisible': [('procurement_method', '=', 'fw_agreement')]}</attribute>
+ </field>
+ <field name="price_is"
+ position="attributes">
+ <attribute name="attrs">{'invisible': [('procurement_method', '=', 'fw_agreement')]}</attribute>
+ </field>
+ </field>
+ </record>
+
+ <record id="addd_agreement_source_line_onchange" model="ir.ui.view">
+ <field name="name">ad agreement source line onchange</field>
+ <field name="model">logistic.requisition.source</field>
+ <field name="inherit_id" ref="logistic_requisition.view_logistic_requisition_source_form"/>
+ <field name="arch" type="xml">
+ <data>
+ <field name="procurement_method"
+ position="attributes">
+ <attribute name="on_change">onchange_sourcing_method(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)</attribute>
+ </field>
+
+ <field name="proposed_qty"
+ position="attributes">
+ <attribute name="on_change">onchange_quantity(procurement_method, requisition_line_id, proposed_qty, proposed_product_id)</attribute>
+ </field>
+ <field name="proposed_product_id"
+ position="attributes">
+ <attribute name="on_change">onchange_product_id(procurement_method, requisition_line_id, proposed_product_id, proposed_qty)</attribute>
+ </field>
+
+ </data>
+ </field>
+ </record>
+
+ </data>
+</openerp>
=== added directory 'framework_agreement_sourcing/wizard'
=== added file 'framework_agreement_sourcing/wizard/__init__.py'
--- framework_agreement_sourcing/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/wizard/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: joel Grand-Guillaume
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import logistic_requisition_source_create_po
=== added file 'framework_agreement_sourcing/wizard/logistic_requisition_source_create_po.py'
--- framework_agreement_sourcing/wizard/logistic_requisition_source_create_po.py 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/wizard/logistic_requisition_source_create_po.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Nicolas Bessi
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import orm, fields
+from openerp.tools.translate import _
+
+class logistic_requisition_source_po_creator(orm.TransientModel):
+
+ _name = 'logistic.requisition.source.create.agr.po'
+
+ _columns = {
+ 'pricelist_id': fields.many2one('product.pricelist',
+ string='Pricelist / Currency',
+ required=True),
+ 'framework_currency_ids': fields.many2many('framework.agreement.pricelist',
+ rel='framework_agr_id_po_create_rel',
+ string='Available Currency',
+ readonly=True)
+ }
+
+ def default_get(self, cr, uid, fields_list, context=None):
+ """ Take the pricelist of the lrs by default. Show the
+ available choice for the user.
+ """
+ if context is None:
+ context = {}
+ defaults = super(logistic_requisition_source_po_creator, self).\
+ default_get(cr, uid, fields_list, context=context)
+ line_obj = self.pool.get('logistic.requisition.source')
+ fmwk_price_obj = self.pool.get('framework.agreement.pricelist')
+ line_ids = context['active_ids']
+ pricelist_id = None
+ line = next((x for x in line_obj.browse(cr, uid, line_ids, context=context)
+ if x.framework_agreement_id), None)
+ if not line:
+ raise orm.except_orm(_('No sourcing line with agreement selected'),
+ _('Please correct selection'))
+
+ pricelist_id = line_obj._get_purchase_pricelist_from_currency(
+ cr,
+ uid,
+ line.requisition_id.pricelist_id.currency_id.id,
+ context=context
+ )
+ defaults['pricelist_id'] = pricelist_id
+
+ frwk_ids = fmwk_price_obj.search(
+ cr, uid,
+ [('framework_agreement_id', '=', line.framework_agreement_id.id)],
+ context=context
+ )
+ defaults['framework_currency_ids'] = frwk_ids
+ return defaults
+
+ def _make_purchase_order(self, cr, uid, pricelist, source_ids, context=None):
+ """Create PO from source line ids"""
+ lr_model = self.pool['logistic.requisition.source']
+ po_id = lr_model.make_purchase_order(cr, uid, source_ids,
+ pricelist, context=context)
+ return po_id
+
+ def action_create_agreement_po_requisition(self, cr, uid, ids, context=None):
+ """ Implement buttons that create PO from selected source lines"""
+ act_obj = self.pool['ir.actions.act_window']
+ source_ids = context['active_ids']
+ form = self.browse(cr, uid, ids, context=context)[0]
+ pricelist=form.pricelist_id
+
+ available_currency = [x.currency_id for x in form.framework_currency_ids]
+ if available_currency and pricelist.currency_id not in available_currency:
+ raise orm.except_orm(_('User Error'), _(
+ 'You must chose a pricelist that is in the same currency '
+ 'than one of the available in the framework agreement.'))
+ po_id = self._make_purchase_order(cr, uid, pricelist, source_ids,
+ context=context)
+ # TODO : update LRS price from PO depending on the chosen currency
+
+ res = act_obj.for_xml_id(cr, uid,
+ 'purchase', 'purchase_rfq', context=context)
+ res.update({'domain': [('id', '=', po_id)],
+ 'res_id': po_id,
+ 'context': '{}',
+ 'search_view_id': False,
+ })
+ return res
=== added file 'framework_agreement_sourcing/wizard/logistic_requisition_source_create_po_view.xml'
--- framework_agreement_sourcing/wizard/logistic_requisition_source_create_po_view.xml 1970-01-01 00:00:00 +0000
+++ framework_agreement_sourcing/wizard/logistic_requisition_source_create_po_view.xml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="view_create_agr_po_from_source" model="ir.ui.view">
+ <field name="name">Create Purchase Order From Requisition Source</field>
+ <field name="model">logistic.requisition.source.create.agr.po</field>
+ <field name="arch" type="xml">
+ <form string="Create Purchase Order" version="7.0">
+ <separator string="Which pricelist / currency you want ?"/>
+ <label string="You can only chose a pricelist with a currency that is included
+ in the framewrok agreement you have chosen." colspan="4"/>
+ <field name="framework_currency_ids"/>
+ <field name="pricelist_id" domain="[('type','=','purchase')]"/>
+
+ <group>
+ <separator string="Are you sure you want to create a Purchase Order from those lines ?"/>
+ <label string="
+ Please note that:
+ Requisition will only be created if:
+ * Lines belong to the same company
+ * There is only one agreement line in selection
+ * Products are define on all selected lines
+ * Non agreement line are of type other
+
+ " colspan="4"/>
+ </group>
+ <footer>
+ <button name="action_create_agreement_po_requisition"
+ string="Create Purchase Order"
+ type="object" class="oe_highlight"/>
+ or
+ <button string="Cancel"
+ class="oe_link"
+ special="cancel" />
+ </footer>
+ </form>
+ </field>
+ </record>
+
+ <act_window
+ name="Create Agreement Purchase Order"
+ res_model="logistic.requisition.source.create.agr.po"
+ src_model="logistic.requisition.source"
+ view_mode="form"
+ target="new"
+ multi="True"
+ key2="client_action_multi"
+ id="action_view_create_agr_po_from_source"/>
+
+ </data>
+</openerp>
=== modified file 'logistic_order/__openerp__.py'
--- logistic_order/__openerp__.py 2014-02-06 16:20:00 +0000
+++ logistic_order/__openerp__.py 2014-03-10 15:39:56 +0000
@@ -35,9 +35,15 @@
"sale_validity",
"transport_plan",
"delivery",
+<<<<<<< TREE
"stock_location_ownership",
"sale_sourced_by_line",
"sale_dropshipping",
+=======
+ 'stock_location_ownership',
+ 'sale_sourced_by_line',
+ 'sale_dropshipping',
+>>>>>>> MERGE-SOURCE
"sale_order_webkit",
],
"demo": [],
=== modified file 'logistic_requisition/__openerp__.py'
--- logistic_requisition/__openerp__.py 2013-09-20 07:12:14 +0000
+++ logistic_requisition/__openerp__.py 2014-03-10 15:39:56 +0000
@@ -31,8 +31,38 @@
This module allows you to manage your Logistic Requisitions.
============================================================
-A Logistic requisition express a need that is requested somewhere.
-
+A Logistic requisition express a need that is requested somewhere. It allows to
+manage the sourcing of the needs before making a cost estimate to the requestor.
+
+This invert the logic that is in standard in OpenERP in the way that the sourcing
+of the procuremnt is made before the order confirmation and we built the link incase
+of validation. In standard, th SO confirmation generate the procurement then it
+generate the needed procurement.
+
+The sourcing can be of various type:
+
+ * Warehouse dispatch: You will deliver the requested goods from one of your
+ stock location
+ * Procurement : You will go for tender to chose the best supplier for the
+ requiredgoods
+ * Framework agreement : You will use the existing Framework agreement
+ * Other : Nothing of those choices. In that case, the line can be selected
+and included in either a PO or a tender
+
+The simple process is the following:
+
+ * LR are recorded as the to represent the need of the customer/requestor
+ * LR are confirm and sourced of different way (dispatch, tender, ..)
+ * If sourced from tender, you need to go all along the tendering process (CBA).
+ Once you chose one bid, the system will automatically put back the prices
+ information on the sourcing line of the LR.
+ * You can create a cost estimate from sourced LR lines for your
+ resquestor/customer
+ * If the requestor accept the cost estimate, his validation will automatically:
+ * Create the link between the chosen winning bid and the line sourced this way.
+ A new draft PO will be generatedand managed as drop shipping.
+ * Lines sourced as a dispatch will create a delivery order.
+ * Lines sourced a LTA will create a PO of type LTA
""",
"depends": ["transport_plan",
"purchase",
=== modified file 'logistic_requisition/i18n/en_US.po'
--- logistic_requisition/i18n/en_US.po 2013-08-29 09:53:01 +0000
+++ logistic_requisition/i18n/en_US.po 2014-03-10 15:39:56 +0000
@@ -6,8 +6,8 @@
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-08-29 09:51+0000\n"
-"PO-Revision-Date: 2013-08-29 09:51+0000\n"
+"POT-Creation-Date: 2014-02-04 13:11+0000\n"
+"PO-Revision-Date: 2014-02-04 13:11+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -21,14 +21,14 @@
msgstr "Logistic Specialist"
#. module: logistic_requisition
-#: field:logistic.requisition.source,offer_ids:0
-msgid "Sales Quotation Lines"
-msgstr "Sales Quotation Lines"
+#: field:logistic.requisition.line,amount_total:0
+msgid "Total Amount"
+msgstr "Total Amount"
#. module: logistic_requisition
-#: field:logistic.requisition,finance_officer_id:0
-msgid "Finance Officer"
-msgstr "Finance Officer"
+#: view:logistic.requisition.line:0
+msgid "View Stock"
+msgstr "View Stock"
#. module: logistic_requisition
#: view:logistic.requisition:0
@@ -49,6 +49,11 @@
msgstr "Shipping / Transport"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:140
+msgid "Preferred transport"
+msgstr "Preferred transport"
+
+#. module: logistic_requisition
#: model:ir.actions.act_window,name:logistic_requisition.act_window_requisition_source_transport
#: model:ir.actions.act_window,name:logistic_requisition.action_requisition_source_transport
msgid "Create Transport Plan"
@@ -70,10 +75,20 @@
msgstr "View Lines"
#. module: logistic_requisition
-#: selection:logistic.requisition.source,price_is:0
-#: selection:sale.order.line,price_is:0
-msgid "Fixed"
-msgstr "Fixed"
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:119
+msgid "Consignee address:"
+msgstr "Consignee address:"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:634
+#, python-format
+msgid "This new requisition concerns %s and is due for %s."
+msgstr "This new requisition concerns %s and is due for %s."
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:139
+msgid "Desired delivery date"
+msgstr "Desired delivery date"
#. module: logistic_requisition
#: field:logistic.requisition,message_unread:0
@@ -93,6 +108,11 @@
msgstr "Company"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:149
+msgid "No"
+msgstr "No"
+
+#. module: logistic_requisition
#: selection:logistic.requisition.source,procurement_method:0
msgid "Framework Agreement"
msgstr "Framework Agreement"
@@ -114,15 +134,11 @@
msgstr "Just for Quotation"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Requestor is External or National Societe"
-msgstr "Requestor is External or National Societe"
-
-#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/transport_plan.py:154
#: model:ir.model,name:logistic_requisition.model_transport_plan
#: view:logistic.requisition.source:0
#: field:logistic.requisition.source,transport_plan_id:0
+#, python-format
msgid "Transport Plan"
msgstr "Transport Plan"
@@ -138,9 +154,14 @@
msgstr "Delivery Address"
#. module: logistic_requisition
-#: field:logistic.requisition.line,budget_unit_price:0
-msgid "Budget Unit Price"
-msgstr "Budget Unit Price"
+#: help:logistic.requisition.line.assign,logistic_user_id:0
+msgid "Logistic Specialist in charge of the Logistic Requisition Line"
+msgstr "Logistic Specialist in charge of the Logistic Requisition Line"
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:163
+msgid "UoM"
+msgstr "UoM"
#. module: logistic_requisition
#: view:logistic.requisition:0
@@ -149,6 +170,12 @@
msgstr "Business Unit Officer"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:262
+#, python-format
+msgid "The cost estimate cannot be created, because no lines are sourced."
+msgstr "The cost estimate cannot be created, because no lines are sourced."
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
#: field:logistic.requisition.line,note:0
msgid "Notes"
@@ -162,9 +189,9 @@
msgstr "Messages"
#. module: logistic_requisition
-#: constraint:logistic.requisition.source:0
-msgid "The total cost cannot be more than the total budget."
-msgstr "The total cost cannot be more than the total budget."
+#: view:logistic.requisition.line:0
+msgid "Total Proposed"
+msgstr "Total Proposed"
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_purchase_order
@@ -172,12 +199,19 @@
msgstr "Purchase Order"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:142
#: view:logistic.requisition:0
#: field:logistic.requisition,analytic_id:0
msgid "Project"
msgstr "Project"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:522
+#, python-format
+msgid "Please create source ressource using various source line actions"
+msgstr "Please create source ressource using various source line actions"
+
+#. module: logistic_requisition
#: help:logistic.requisition.source,date_eta:0
msgid "Estimated Date of Arrival"
msgstr "Estimated Date of Arrival"
@@ -202,11 +236,10 @@
" sourced logistic requisition lines."
#. module: logistic_requisition
-#: selection:logistic.requisition,requester_type:0
-#: selection:logistic.requisition.line,requester_type:0
-#: selection:res.partner,requester_type:0
-msgid "External Organization"
-msgstr "External Organization"
+#: selection:logistic.requisition.source,price_is:0
+#: selection:sale.order.line,price_is:0
+msgid "Fixed"
+msgstr "Fixed"
#. module: logistic_requisition
#: help:logistic.requisition,message_unread:0
@@ -221,9 +254,28 @@
msgstr "Incoterm Place"
#. module: logistic_requisition
-#: field:logistic.requisition,date_budget_holder:0
-msgid "Budget Holder Validation Date"
-msgstr "Budget Holder Validation Date"
+#: code:addons/logistic_requisition/model/purchase.py:174
+#: code:addons/logistic_requisition/model/purchase.py:194
+#, python-format
+msgid "UserError"
+msgstr "UserError"
+
+#. module: logistic_requisition
+#: field:logistic.requisition.source,unit_cost:0
+msgid "Unit Cost"
+msgstr "Unit Cost"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:588
+#, python-format
+msgid "Prices for location"
+msgstr "Prices for location"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1067
+#, python-format
+msgid "The sourcing lines do not have the same delivery address."
+msgstr "The sourcing lines do not have the same delivery address."
#. module: logistic_requisition
#: field:logistic.requisition,name:0
@@ -244,11 +296,6 @@
msgstr "Desired Delivery Date"
#. module: logistic_requisition
-#: view:logistic.requisition.line:0
-msgid "View Stock"
-msgstr "View Stock"
-
-#. module: logistic_requisition
#: field:logistic.requisition,consignee_id:0
msgid "Consignee"
msgstr "Consignee"
@@ -293,19 +340,17 @@
msgstr "Partner"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/purchase.py:178
+#, python-format
+msgid "You cannot change the informations because this PO line is already linked to a Logistic Requsition Line %s marked as sourced or quoted."
+msgstr "You cannot change the informations because this PO line is already linked to a Logistic Requsition Line %s marked as sourced or quoted."
+
+#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_sale_order
msgid "Sales Order"
msgstr "Sales Order"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: field:logistic.requisition,amount_total:0
-#: view:logistic.requisition.line:0
-#: view:logistic.requisition.source:0
-msgid "Total Budget"
-msgstr "Total Budget"
-
-#. module: logistic_requisition
#: help:logistic.requisition,incoterm_id:0
msgid "International Commercial Terms are a series of predefined commercial terms used in international transactions."
msgstr "International Commercial Terms are a series of predefined commercial terms used in international transactions."
@@ -365,6 +410,11 @@
msgstr "State"
#. module: logistic_requisition
+#: view:logistic.requisition.source.create.requisition:0
+msgid "Which pricelist / currency you want ?"
+msgstr "Which pricelist / currency you want ?"
+
+#. module: logistic_requisition
#: field:logistic.requisition,message_follower_ids:0
#: field:logistic.requisition.line,message_follower_ids:0
#: field:logistic.requisition.source,message_follower_ids:0
@@ -393,17 +443,13 @@
msgstr "Unassigned Requisition"
#. module: logistic_requisition
-#: selection:logistic.requisition,requester_type:0
-#: selection:logistic.requisition.line,requester_type:0
-#: selection:res.partner,requester_type:0
-msgid "National Society"
-msgstr "National Society"
-
-#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1146
+#: code:addons/logistic_requisition/wizard/logistic_line_create_requisition.py:63
#: model:ir.model,name:logistic_requisition.model_purchase_requisition
#: view:logistic.requisition.line:0
#: view:logistic.requisition.source:0
#: field:logistic.requisition.source,po_requisition_id:0
+#, python-format
msgid "Purchase Requisition"
msgstr "Call for Bids"
@@ -418,14 +464,9 @@
msgstr "Skipped"
#. module: logistic_requisition
-#: field:logistic.requisition.source,selected_po_id:0
-msgid "Selected Purchase Order"
-msgstr "Selected Purchase Order"
-
-#. module: logistic_requisition
-#: constraint:logistic.requisition.source:0
-msgid "A purchase requisition cannot be linked to lines of different logistics requisitions."
-msgstr "A call for bids cannot be linked to lines of different logistics requisitions."
+#: help:logistic.requisition.line,logistic_user_id:0
+msgid "User in charge of the Logistic Requisition Line"
+msgstr "User in charge of the Logistic Requisition Line"
#. module: logistic_requisition
#: model:logistic.requisition.cancel.reason,name:logistic_requisition.cancel_reason_other_provider
@@ -443,9 +484,27 @@
msgstr "Skipped Lines"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-msgid "View Sourcing Lines"
-msgstr "View Sourcing Lines"
+#: field:logistic.requisition.source,selected_bid_id:0
+msgid "Selected Bid"
+msgstr "Selected Bid"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:566
+#, python-format
+msgid "Stock by Location"
+msgstr "Stock by Location"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1092
+#, python-format
+msgid "The logistic requisition sourcing line %s is already linked to a Purchase Requisition."
+msgstr "The logistic requisition sourcing line %s is already linked to a Purchase Requisition."
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:219
+#, python-format
+msgid "Cannot create a cost estimate because:"
+msgstr "Cannot create a cost estimate because:"
#. module: logistic_requisition
#: field:logistic.requisition.source,price_is:0
@@ -459,6 +518,12 @@
msgstr "Reset to Draft"
#. module: logistic_requisition
+#: view:logistic.requisition:0
+#: view:logistic.requisition.line:0
+msgid "Show cost estimate only"
+msgstr "Show cost estimate only"
+
+#. module: logistic_requisition
#: view:logistic.requisition.cost.estimate:0
msgid "The lines in the Skipped section will be ignored\n"
" because they are not sourced or are already linked\n"
@@ -468,11 +533,22 @@
" to a cost estimate."
#. module: logistic_requisition
+#: field:logistic.requisition,pricelist_id:0
+msgid "Pricelist"
+msgstr "Pricelist"
+
+#. module: logistic_requisition
#: field:logistic.requisition.source,dispatch_location_id:0
msgid "Dispatch From"
msgstr "Dispatch From"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1113
+#, python-format
+msgid "There should be at least one selectedline with procurement method Procurement"
+msgstr "There should be at least one selectedline with procurement method Procurement"
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
#: field:logistic.requisition.line,logistic_user_id:0
msgid "Assigned To"
@@ -489,10 +565,25 @@
msgstr "draft,confirmed,exception"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Show cost estimate only"
-msgstr "Show cost estimate only"
+#: field:logistic.requisition.source,transport_applicable:0
+msgid "Transport Applicable"
+msgstr "Transport Applicable"
+
+#. module: logistic_requisition
+#: field:logistic.requisition.source,default_source_address:0
+msgid "Default source"
+msgstr "Default source"
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:141
+msgid "Cost estimate only"
+msgstr "Cost estimate only"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/purchase.py:198
+#, python-format
+msgid "You cannot delete this PO line because it is already linked to a Logistic Requsition Line %s marked as sourced or quoted."
+msgstr "You cannot delete this PO line because it is already linked to a Logistic Requsition Line %s marked as sourced or quoted."
#. module: logistic_requisition
#: view:logistic.requisition:0
@@ -521,6 +612,11 @@
msgstr "Logistic Requisition Reference must be unique!"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:192
+msgid "Signature"
+msgstr "Signature"
+
+#. module: logistic_requisition
#: view:logistic.requisition.line:0
msgid "Country:"
msgstr "Country:"
@@ -531,14 +627,21 @@
msgstr "Your assigned Requisition"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:196
+#, python-format
+msgid "Sourcing %s: no account code has been stored"
+msgstr "Sourcing %s: no account code has been stored"
+
+#. module: logistic_requisition
#: field:logistic.requisition.source,proposed_qty:0
msgid "Proposed Qty"
msgstr "Proposed Qty"
#. module: logistic_requisition
-#: field:purchase.order.line,po_line_from_bid_ids:0
-msgid "Lines generated by the bid"
-msgstr "Lines generated by the bid"
+#: selection:logistic.requisition.source,price_is:0
+#: selection:sale.order.line,price_is:0
+msgid "Estimated"
+msgstr "Estimated"
#. module: logistic_requisition
#: model:ir.actions.act_window,name:logistic_requisition.action_requisition_line_assign
@@ -552,19 +655,32 @@
msgstr "Purchase Cancel Reasons"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1045
+#, python-format
+msgid "The lines are not assigned to the same User."
+msgstr "The lines are not assigned to the same User."
+
+#. module: logistic_requisition
#: constraint:logistic.requisition.cost.estimate:0
msgid "All the lines should belong to the same requisition."
msgstr "All the lines should belong to the same requisition."
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1118
+#, python-format
+msgid "Selected line procurement method should be procurement or other"
+msgstr "Selected line procurement method should be procurement or other"
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
msgid "Delivery Date"
msgstr "Delivery Date"
#. module: logistic_requisition
-#: field:logistic.requisition,budget_holder_id:0
-msgid "Budget Holder"
-msgstr "Budget Holder"
+#: model:ir.actions.act_window,name:logistic_requisition.action_logistic_requisition_cancel
+#: view:logistic.requisition.cancel:0
+msgid "Reason for the cancellation"
+msgstr "Reason for the cancellation"
#. module: logistic_requisition
#: constraint:logistic.requisition.source:0
@@ -573,7 +689,6 @@
#. module: logistic_requisition
#: view:logistic.requisition.source:0
-#: field:purchase.requisition,logistic_requisition_source_ids:0
msgid "Logistic Requisition Sourcing Lines"
msgstr "Logistic Requisition Sourcing Lines"
@@ -583,8 +698,11 @@
msgstr "Line's State"
#. module: logistic_requisition
+#: view:logistic.requisition:0
#: field:logistic.requisition,currency_id:0
+#: view:logistic.requisition.line:0
#: field:logistic.requisition.line,currency_id:0
+#: field:logistic.requisition.source,currency_id:0
msgid "Currency"
msgstr "Currency"
@@ -600,36 +718,53 @@
msgstr "Incoterm Place of Delivery. International Commercial Terms are a series of predefined commercial terms used in international transactions."
#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:191
+#, python-format
+msgid "Sourcing %s: no quantity has been proposed"
+msgstr "Sourcing %s: no quantity has been proposed"
+
+#. module: logistic_requisition
#: model:ir.actions.act_window,name:logistic_requisition.action_logistic_requisition
#: model:ir.ui.menu,name:logistic_requisition.menu_logistic_requisition
msgid "Requisitions"
msgstr "Requisitions"
#. module: logistic_requisition
-#: field:logistic.requisition,date_finance_officer:0
-msgid "Finance Officer Validation Date"
-msgstr "Finance Officer Validation Date"
-
-#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Internal (Federation)"
-msgstr "Internal (Federation)"
-
-#. module: logistic_requisition
-#: field:logistic.requisition.source,transport_applicable:0
-msgid "Transport Applicable"
-msgstr "Transport Applicable"
-
-#. module: logistic_requisition
-#: field:logistic.requisition.source,purchase_requisition_line_ids:0
-msgid "Purchase Requisition Lines"
-msgstr "Call for Bids Lines"
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:149
+msgid "Yes"
+msgstr "Yes"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1115
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1120
+#, python-format
+msgid "Please correct selection"
+msgstr "Please correct selection"
+
+#. module: logistic_requisition
+#: field:logistic.requisition.source,offer_ids:0
+msgid "Sales Quotation Lines"
+msgstr "Sales Quotation Lines"
+
+#. module: logistic_requisition
+#: constraint:logistic.requisition.source:0
+msgid "A purchase requisition cannot be linked to lines of different logistics requisitions."
+msgstr "A call for bids cannot be linked to lines of different logistics requisitions."
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:136
+msgid "Logistic requisition"
+msgstr "Logistic requisition"
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:190
+msgid "Requesting entity"
+msgstr "Requesting entity"
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_purchase_order_line
-#: field:logistic.requisition.source,bid_line_id:0
#: field:logistic.requisition.source,purchase_line_id:0
+#: field:logistic.requisition.source,selected_bid_line_id:0
msgid "Purchase Order Line"
msgstr "Purchase Order Line"
@@ -644,6 +779,12 @@
msgstr "Assigned Specialist"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:521
+#, python-format
+msgid "line %s is not sourced"
+msgstr "line %s is not sourced"
+
+#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_logistic_requisition_source_transport_plan
msgid "Create a transport plan for logistic requisition source lines"
msgstr "Create a transport plan for logistic requisition source lines"
@@ -703,18 +844,13 @@
msgstr "Activity Code"
#. module: logistic_requisition
+#: field:purchase.requisition.line,logistic_requisition_source_ids:0
#: view:transport.plan:0
#: field:transport.plan,logistic_requisition_source_ids:0
msgid "Logistic Requisition Source Lines"
msgstr "Logistic Requisition Source Lines"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "External (NS, Others)"
-msgstr "External (NS, Others)"
-
-#. module: logistic_requisition
#: field:logistic.requisition.source.transport.plan,transport_estimated_cost:0
msgid "Transportation Estimated Costs"
msgstr "Transportation Estimated Costs"
@@ -750,16 +886,6 @@
msgstr "offers"
#. module: logistic_requisition
-#: field:purchase.requisition.line,logistic_requisition_source_id:0
-msgid "Logistic Requisition Source Line"
-msgstr "Logistic Requisition Source Line"
-
-#. module: logistic_requisition
-#: view:logistic.requisition:0
-msgid "Validation"
-msgstr "Validation"
-
-#. module: logistic_requisition
#: help:logistic.requisition.source.transport.plan,date_eta:0
msgid "Estimated Date of Arrival if not set requisition ETA will be used"
msgstr "Estimated Date of Arrival if not set requisition ETA will be used"
@@ -772,9 +898,15 @@
msgstr "Is a Follower"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-msgid " validated on "
-msgstr " validated on "
+#: code:addons/logistic_requisition/model/sale_order.py:45
+#, python-format
+msgid "The logistic requisition line %s has no purchase order line."
+msgstr "The logistic requisition line %s has no purchase order line."
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:187
+msgid "Approval"
+msgstr "Approval"
#. module: logistic_requisition
#: help:logistic.requisition.source,date_etd:0
@@ -788,6 +920,11 @@
msgstr "Create Call for Bids From Requisition Source"
#. module: logistic_requisition
+#: help:logistic.requisition,pricelist_id:0
+msgid "Pricelist that represent the currency for current logistic request."
+msgstr "Pricelist that represent the currency for current logistic request."
+
+#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_stock_incoterms
msgid "Incoterms"
msgstr "Incoterms"
@@ -813,7 +950,6 @@
#. module: logistic_requisition
#: view:logistic.requisition:0
-#: field:logistic.requisition,partner_id:0
msgid "Requesting Entity"
msgstr "Requesting Entity"
@@ -848,6 +984,17 @@
msgstr "View Prices"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1044
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1052
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1059
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1066
+#: code:addons/logistic_requisition/model/sale_order.py:44
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:261
+#, python-format
+msgid "Error"
+msgstr "Error"
+
+#. module: logistic_requisition
#: model:logistic.requisition.cancel.reason,name:logistic_requisition.cancel_reason_no_service_needed
msgid "No service needed anymore"
msgstr "No service needed anymore"
@@ -859,11 +1006,7 @@
msgstr "Assign"
#. module: logistic_requisition
-#: field:logistic.requisition.source,unit_cost:0
-msgid "Unit Cost"
-msgstr "Unit Cost"
-
-#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:162
#: view:logistic.requisition:0
#: field:logistic.requisition.line,requested_qty:0
msgid "Quantity"
@@ -891,12 +1034,9 @@
msgstr "Line Sourced"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: field:logistic.requisition,requester_type:0
-#: view:logistic.requisition.line:0
-#: field:logistic.requisition.line,requester_type:0
-msgid "Type of Requestor"
-msgstr "Type of Requestor"
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:160
+msgid "Number"
+msgstr "Number"
#. module: logistic_requisition
#: view:logistic.requisition.source.create.requisition:0
@@ -904,6 +1044,12 @@
msgstr "Create Requisition"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1091
+#, python-format
+msgid "Existing"
+msgstr "Existing"
+
+#. module: logistic_requisition
#: view:logistic.requisition.line:0
#: selection:logistic.requisition.line,state:0
#: selection:logistic.requisition.source,state:0
@@ -911,9 +1057,10 @@
msgstr "Assigned"
#. module: logistic_requisition
-#: field:logistic.requisition.line,budget_tot_price:0
-msgid "Budget Total Price"
-msgstr "Budget Total Price"
+#: view:logistic.requisition.line:0
+#: view:logistic.requisition.source:0
+msgid "Total Budget"
+msgstr "Total Budget"
#. module: logistic_requisition
#: view:logistic.requisition:0
@@ -947,16 +1094,9 @@
msgstr "Create Cost Estimate"
#. module: logistic_requisition
-#: selection:logistic.requisition.source,price_is:0
-#: selection:sale.order.line,price_is:0
-msgid "Estimated"
-msgstr "Estimated"
-
-#. module: logistic_requisition
-#: model:ir.actions.act_window,name:logistic_requisition.action_logistic_requisition_cancel
-#: view:logistic.requisition.cancel:0
-msgid "Reason for the cancellation"
-msgstr "Reason for the cancellation"
+#: field:purchase.order.line,po_line_from_bid_ids:0
+msgid "Lines generated by the bid"
+msgstr "Lines generated by the bid"
#. module: logistic_requisition
#: field:logistic.requisition.line,name:0
@@ -979,18 +1119,16 @@
msgstr "Your assigned Requisition Line"
#. module: logistic_requisition
-#: selection:logistic.requisition,requester_type:0
-#: selection:logistic.requisition.line,requester_type:0
-#: selection:res.partner,requester_type:0
-msgid "Federation (internal)"
-msgstr "Federation (internal)"
-
-#. module: logistic_requisition
#: view:logistic.requisition.line:0
msgid "Req. Date:"
msgstr "Req. Date:"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:182
+msgid "General Remarks"
+msgstr "General Remarks"
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
#: field:logistic.requisition,cost_estimate_only:0
#: view:logistic.requisition.line:0
@@ -1014,9 +1152,9 @@
msgstr "Supplier Address"
#. module: logistic_requisition
-#: field:res.partner,requester_type:0
-msgid "Requester Type"
-msgstr "Requester Type"
+#: selection:logistic.requisition.source,procurement_method:0
+msgid "Other"
+msgstr "Other"
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_logistic_requisition_line
@@ -1042,6 +1180,7 @@
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_purchase_requisition_line
+#: field:logistic.requisition.source,purchase_requisition_line_id:0
msgid "Purchase Requisition Line"
msgstr "Call for Bids Line"
@@ -1058,6 +1197,7 @@
msgstr "Product"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:161
#: field:logistic.requisition.line,description:0
msgid "Description"
msgstr "Description"
@@ -1069,10 +1209,10 @@
msgstr "Procurement"
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Requestor is the Federation"
-msgstr "Requestor is the Federation"
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1053
+#, python-format
+msgid "The sourcing lines do not belong to the same company."
+msgstr "The sourcing lines do not belong to the same company."
#. module: logistic_requisition
#: help:transport.plan,product_id:0
@@ -1080,6 +1220,12 @@
msgstr "Product used for the transport, will be used in the cost estimate"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1097
+#, python-format
+msgid "The sourcing line %d does not have any product defined, please choose one."
+msgstr "The sourcing line %d does not have any product defined, please choose one."
+
+#. module: logistic_requisition
#: field:logistic.requisition.source.transport.plan,transport_mode_id:0
msgid "Transport by"
msgstr "Transport by"
@@ -1090,9 +1236,15 @@
msgstr "Cancellation reason:"
#. module: logistic_requisition
-#: help:logistic.requisition.line,logistic_user_id:0
-msgid "User in charge of the Logistic Requisition Line"
-msgstr "User in charge of the Logistic Requisition Line"
+#: code:addons/logistic_requisition/model/logistic_requisition.py:632
+#, python-format
+msgid "Logistic Requisition Line %s Assigned"
+msgstr "Logistic Requisition Line %s Assigned"
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:191
+msgid "Requested date"
+msgstr "Requested date"
#. module: logistic_requisition
#: view:logistic.requisition.line:0
@@ -1100,9 +1252,9 @@
msgstr "My Requisition Line"
#. module: logistic_requisition
-#: field:logistic.requisition,allowed_budget:0
-msgid "Allowed Budget"
-msgstr "Allowed Budget"
+#: field:logistic.requisition,partner_id:0
+msgid "Customer"
+msgstr "Customer"
#. module: logistic_requisition
#: model:ir.actions.act_window,name:logistic_requisition.action_logistic_requisition_source
@@ -1110,11 +1262,6 @@
msgstr "Requisitions Sourcing Lines"
#. module: logistic_requisition
-#: help:logistic.requisition.line.assign,logistic_user_id:0
-msgid "Logistic Specialist in charge of the Logistic Requisition Line"
-msgstr "Logistic Specialist in charge of the Logistic Requisition Line"
-
-#. module: logistic_requisition
#: field:logistic.requisition,line_ids:0
msgid "Products to Purchase"
msgstr "Products to Purchase"
@@ -1125,6 +1272,7 @@
msgstr "Mobilization Officer or Logistic Coordinator in charge of the Logistic Requisition"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:143
#: view:logistic.requisition:0
#: field:logistic.requisition,country_id:0
#: view:logistic.requisition.line:0
@@ -1133,8 +1281,11 @@
msgstr "Country"
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:693
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:293
#: view:logistic.requisition.cost.estimate:0
#: field:logistic.requisition.line,cost_estimate_id:0
+#, python-format
msgid "Cost Estimate"
msgstr "Cost Estimate"
@@ -1154,17 +1305,13 @@
msgstr "Product UoM"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:144
#: view:logistic.requisition:0
#: field:logistic.requisition,date:0
msgid "Requisition Date"
msgstr "Requisition Date"
#. module: logistic_requisition
-#: view:logistic.requisition.line:0
-msgid "Total Proposed"
-msgstr "Total Proposed"
-
-#. module: logistic_requisition
#: view:logistic.requisition.cancel:0
msgid "Choose the reason for the cancellation of the\n"
" logistic requisition."
@@ -1172,6 +1319,11 @@
" logistic requisition."
#. module: logistic_requisition
+#: field:logistic.requisition.source,proposed_product_id:0
+msgid "Proposed Product"
+msgstr "Proposed Product"
+
+#. module: logistic_requisition
#: view:logistic.requisition.line.assign:0
msgid "Select the assignee"
msgstr "Select the assignee"
@@ -1179,6 +1331,7 @@
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_logistic_requisition_source
#: view:logistic.requisition.source:0
+#: field:purchase.order.line,lr_source_line_id:0
msgid "Logistic Requisition Source"
msgstr "Logistic Requisition Source"
@@ -1197,6 +1350,11 @@
msgstr "Messages and communication history"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:102
+msgid "Shipping address:"
+msgstr "Shipping address:"
+
+#. module: logistic_requisition
#: view:logistic.requisition.source.create.requisition:0
msgid " Please note that: \n"
" \n"
@@ -1224,7 +1382,13 @@
msgstr "Sourced"
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:178
+msgid "Delivery Remarks"
+msgstr "Delivery Remarks"
+
+#. module: logistic_requisition
#: field:logistic.requisition.line,account_code:0
+#: field:sale.order.line,account_code:0
msgid "Account Code"
msgstr "Account Code"
@@ -1234,6 +1398,17 @@
msgstr "Generated from bid"
#. module: logistic_requisition
+#: field:logistic.requisition.source.create.requisition,pricelist_id:0
+msgid "Pricelist / Currency"
+msgstr "Pricelist / Currency"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1060
+#, python-format
+msgid "The sourcing lines do not have the same consignee."
+msgstr "The sourcing lines do not have the same consignee."
+
+#. module: logistic_requisition
#: view:logistic.requisition.cancel:0
#: view:logistic.requisition.cost.estimate:0
#: view:logistic.requisition.line.assign:0
@@ -1243,7 +1418,24 @@
msgstr "or"
#. module: logistic_requisition
+#: view:logistic.requisition:0
+msgid "View Sourcing Lines"
+msgstr "View Sourcing Lines"
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1096
+#, python-format
+msgid "Missing information"
+msgstr "Missing information"
+
+#. module: logistic_requisition
#: view:logistic.requisition.line:0
msgid "Open Cost Estimate"
msgstr "Open Cost Estimate"
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:105
+#, python-format
+msgid "Transport from %s to %s by %s (Ref. %s)"
+msgstr "Transport from %s to %s by %s (Ref. %s)"
+
=== modified file 'logistic_requisition/i18n/logistic_requisition.pot'
--- logistic_requisition/i18n/logistic_requisition.pot 2013-08-29 09:53:01 +0000
+++ logistic_requisition/i18n/logistic_requisition.pot 2014-03-10 15:39:56 +0000
@@ -1,13 +1,13 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
-# * logistic_requisition
+# * logistic_requisition
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 7.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-08-29 09:50+0000\n"
-"PO-Revision-Date: 2013-08-29 09:50+0000\n"
+"POT-Creation-Date: 2014-02-04 13:09+0000\n"
+"PO-Revision-Date: 2014-02-04 13:09+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -21,13 +21,13 @@
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition.source,offer_ids:0
-msgid "Sales Quotation Lines"
+#: field:logistic.requisition.line,amount_total:0
+msgid "Total Amount"
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition,finance_officer_id:0
-msgid "Finance Officer"
+#: view:logistic.requisition.line:0
+msgid "View Stock"
msgstr ""
#. module: logistic_requisition
@@ -49,6 +49,11 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:140
+msgid "Preferred transport"
+msgstr ""
+
+#. module: logistic_requisition
#: model:ir.actions.act_window,name:logistic_requisition.act_window_requisition_source_transport
#: model:ir.actions.act_window,name:logistic_requisition.action_requisition_source_transport
msgid "Create Transport Plan"
@@ -70,9 +75,19 @@
msgstr ""
#. module: logistic_requisition
-#: selection:logistic.requisition.source,price_is:0
-#: selection:sale.order.line,price_is:0
-msgid "Fixed"
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:119
+msgid "Consignee address:"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:634
+#, python-format
+msgid "This new requisition concerns %s and is due for %s."
+msgstr ""
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:139
+msgid "Desired delivery date"
msgstr ""
#. module: logistic_requisition
@@ -93,6 +108,11 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:149
+msgid "No"
+msgstr ""
+
+#. module: logistic_requisition
#: selection:logistic.requisition.source,procurement_method:0
msgid "Framework Agreement"
msgstr ""
@@ -114,15 +134,11 @@
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Requestor is External or National Societe"
-msgstr ""
-
-#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/transport_plan.py:154
#: model:ir.model,name:logistic_requisition.model_transport_plan
#: view:logistic.requisition.source:0
#: field:logistic.requisition.source,transport_plan_id:0
+#, python-format
msgid "Transport Plan"
msgstr ""
@@ -138,8 +154,13 @@
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition.line,budget_unit_price:0
-msgid "Budget Unit Price"
+#: help:logistic.requisition.line.assign,logistic_user_id:0
+msgid "Logistic Specialist in charge of the Logistic Requisition Line"
+msgstr ""
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:163
+msgid "UoM"
msgstr ""
#. module: logistic_requisition
@@ -149,6 +170,12 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:262
+#, python-format
+msgid "The cost estimate cannot be created, because no lines are sourced."
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
#: field:logistic.requisition.line,note:0
msgid "Notes"
@@ -162,8 +189,8 @@
msgstr ""
#. module: logistic_requisition
-#: constraint:logistic.requisition.source:0
-msgid "The total cost cannot be more than the total budget."
+#: view:logistic.requisition.line:0
+msgid "Total Proposed"
msgstr ""
#. module: logistic_requisition
@@ -172,12 +199,19 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:142
#: view:logistic.requisition:0
#: field:logistic.requisition,analytic_id:0
msgid "Project"
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:522
+#, python-format
+msgid "Please create source ressource using various source line actions"
+msgstr ""
+
+#. module: logistic_requisition
#: help:logistic.requisition.source,date_eta:0
msgid "Estimated Date of Arrival"
msgstr ""
@@ -201,10 +235,9 @@
msgstr ""
#. module: logistic_requisition
-#: selection:logistic.requisition,requester_type:0
-#: selection:logistic.requisition.line,requester_type:0
-#: selection:res.partner,requester_type:0
-msgid "External Organization"
+#: selection:logistic.requisition.source,price_is:0
+#: selection:sale.order.line,price_is:0
+msgid "Fixed"
msgstr ""
#. module: logistic_requisition
@@ -220,8 +253,27 @@
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition,date_budget_holder:0
-msgid "Budget Holder Validation Date"
+#: code:addons/logistic_requisition/model/purchase.py:174
+#: code:addons/logistic_requisition/model/purchase.py:194
+#, python-format
+msgid "UserError"
+msgstr ""
+
+#. module: logistic_requisition
+#: field:logistic.requisition.source,unit_cost:0
+msgid "Unit Cost"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:588
+#, python-format
+msgid "Prices for location"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1067
+#, python-format
+msgid "The sourcing lines do not have the same delivery address."
msgstr ""
#. module: logistic_requisition
@@ -243,11 +295,6 @@
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition.line:0
-msgid "View Stock"
-msgstr ""
-
-#. module: logistic_requisition
#: field:logistic.requisition,consignee_id:0
msgid "Consignee"
msgstr ""
@@ -292,19 +339,17 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/purchase.py:178
+#, python-format
+msgid "You cannot change the informations because this PO line is already linked to a Logistic Requsition Line %s marked as sourced or quoted."
+msgstr ""
+
+#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_sale_order
msgid "Sales Order"
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: field:logistic.requisition,amount_total:0
-#: view:logistic.requisition.line:0
-#: view:logistic.requisition.source:0
-msgid "Total Budget"
-msgstr ""
-
-#. module: logistic_requisition
#: help:logistic.requisition,incoterm_id:0
msgid "International Commercial Terms are a series of predefined commercial terms used in international transactions."
msgstr ""
@@ -364,6 +409,11 @@
msgstr ""
#. module: logistic_requisition
+#: view:logistic.requisition.source.create.requisition:0
+msgid "Which pricelist / currency you want ?"
+msgstr ""
+
+#. module: logistic_requisition
#: field:logistic.requisition,message_follower_ids:0
#: field:logistic.requisition.line,message_follower_ids:0
#: field:logistic.requisition.source,message_follower_ids:0
@@ -392,17 +442,13 @@
msgstr ""
#. module: logistic_requisition
-#: selection:logistic.requisition,requester_type:0
-#: selection:logistic.requisition.line,requester_type:0
-#: selection:res.partner,requester_type:0
-msgid "National Society"
-msgstr ""
-
-#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1146
+#: code:addons/logistic_requisition/wizard/logistic_line_create_requisition.py:63
#: model:ir.model,name:logistic_requisition.model_purchase_requisition
#: view:logistic.requisition.line:0
#: view:logistic.requisition.source:0
#: field:logistic.requisition.source,po_requisition_id:0
+#, python-format
msgid "Purchase Requisition"
msgstr ""
@@ -417,13 +463,8 @@
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition.source,selected_po_id:0
-msgid "Selected Purchase Order"
-msgstr ""
-
-#. module: logistic_requisition
-#: constraint:logistic.requisition.source:0
-msgid "A purchase requisition cannot be linked to lines of different logistics requisitions."
+#: help:logistic.requisition.line,logistic_user_id:0
+msgid "User in charge of the Logistic Requisition Line"
msgstr ""
#. module: logistic_requisition
@@ -442,8 +483,26 @@
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-msgid "View Sourcing Lines"
+#: field:logistic.requisition.source,selected_bid_id:0
+msgid "Selected Bid"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:566
+#, python-format
+msgid "Stock by Location"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1092
+#, python-format
+msgid "The logistic requisition sourcing line %s is already linked to a Purchase Requisition."
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:219
+#, python-format
+msgid "Cannot create a cost estimate because:"
msgstr ""
#. module: logistic_requisition
@@ -458,6 +517,12 @@
msgstr ""
#. module: logistic_requisition
+#: view:logistic.requisition:0
+#: view:logistic.requisition.line:0
+msgid "Show cost estimate only"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition.cost.estimate:0
msgid "The lines in the Skipped section will be ignored\n"
" because they are not sourced or are already linked\n"
@@ -465,11 +530,22 @@
msgstr ""
#. module: logistic_requisition
+#: field:logistic.requisition,pricelist_id:0
+msgid "Pricelist"
+msgstr ""
+
+#. module: logistic_requisition
#: field:logistic.requisition.source,dispatch_location_id:0
msgid "Dispatch From"
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1113
+#, python-format
+msgid "There should be at least one selectedline with procurement method Procurement"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
#: field:logistic.requisition.line,logistic_user_id:0
msgid "Assigned To"
@@ -486,9 +562,24 @@
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Show cost estimate only"
+#: field:logistic.requisition.source,transport_applicable:0
+msgid "Transport Applicable"
+msgstr ""
+
+#. module: logistic_requisition
+#: field:logistic.requisition.source,default_source_address:0
+msgid "Default source"
+msgstr ""
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:141
+msgid "Cost estimate only"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/purchase.py:198
+#, python-format
+msgid "You cannot delete this PO line because it is already linked to a Logistic Requsition Line %s marked as sourced or quoted."
msgstr ""
#. module: logistic_requisition
@@ -518,6 +609,11 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:192
+msgid "Signature"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition.line:0
msgid "Country:"
msgstr ""
@@ -528,13 +624,20 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:196
+#, python-format
+msgid "Sourcing %s: no account code has been stored"
+msgstr ""
+
+#. module: logistic_requisition
#: field:logistic.requisition.source,proposed_qty:0
msgid "Proposed Qty"
msgstr ""
#. module: logistic_requisition
-#: field:purchase.order.line,po_line_from_bid_ids:0
-msgid "Lines generated by the bid"
+#: selection:logistic.requisition.source,price_is:0
+#: selection:sale.order.line,price_is:0
+msgid "Estimated"
msgstr ""
#. module: logistic_requisition
@@ -549,18 +652,31 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1045
+#, python-format
+msgid "The lines are not assigned to the same User."
+msgstr ""
+
+#. module: logistic_requisition
#: constraint:logistic.requisition.cost.estimate:0
msgid "All the lines should belong to the same requisition."
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1118
+#, python-format
+msgid "Selected line procurement method should be procurement or other"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
msgid "Delivery Date"
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition,budget_holder_id:0
-msgid "Budget Holder"
+#: model:ir.actions.act_window,name:logistic_requisition.action_logistic_requisition_cancel
+#: view:logistic.requisition.cancel:0
+msgid "Reason for the cancellation"
msgstr ""
#. module: logistic_requisition
@@ -570,7 +686,6 @@
#. module: logistic_requisition
#: view:logistic.requisition.source:0
-#: field:purchase.requisition,logistic_requisition_source_ids:0
msgid "Logistic Requisition Sourcing Lines"
msgstr ""
@@ -580,8 +695,11 @@
msgstr ""
#. module: logistic_requisition
+#: view:logistic.requisition:0
#: field:logistic.requisition,currency_id:0
+#: view:logistic.requisition.line:0
#: field:logistic.requisition.line,currency_id:0
+#: field:logistic.requisition.source,currency_id:0
msgid "Currency"
msgstr ""
@@ -597,36 +715,53 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:191
+#, python-format
+msgid "Sourcing %s: no quantity has been proposed"
+msgstr ""
+
+#. module: logistic_requisition
#: model:ir.actions.act_window,name:logistic_requisition.action_logistic_requisition
#: model:ir.ui.menu,name:logistic_requisition.menu_logistic_requisition
msgid "Requisitions"
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition,date_finance_officer:0
-msgid "Finance Officer Validation Date"
-msgstr ""
-
-#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Internal (Federation)"
-msgstr ""
-
-#. module: logistic_requisition
-#: field:logistic.requisition.source,transport_applicable:0
-msgid "Transport Applicable"
-msgstr ""
-
-#. module: logistic_requisition
-#: field:logistic.requisition.source,purchase_requisition_line_ids:0
-msgid "Purchase Requisition Lines"
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:149
+msgid "Yes"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1115
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1120
+#, python-format
+msgid "Please correct selection"
+msgstr ""
+
+#. module: logistic_requisition
+#: field:logistic.requisition.source,offer_ids:0
+msgid "Sales Quotation Lines"
+msgstr ""
+
+#. module: logistic_requisition
+#: constraint:logistic.requisition.source:0
+msgid "A call for bids cannot be linked to lines of different logistics requisitions."
+msgstr ""
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:136
+msgid "Logistic requisition"
+msgstr ""
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:190
+msgid "Requesting entity"
msgstr ""
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_purchase_order_line
-#: field:logistic.requisition.source,bid_line_id:0
#: field:logistic.requisition.source,purchase_line_id:0
+#: field:logistic.requisition.source,selected_bid_line_id:0
msgid "Purchase Order Line"
msgstr ""
@@ -641,6 +776,12 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:521
+#, python-format
+msgid "line %s is not sourced"
+msgstr ""
+
+#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_logistic_requisition_source_transport_plan
msgid "Create a transport plan for logistic requisition source lines"
msgstr ""
@@ -695,18 +836,13 @@
msgstr ""
#. module: logistic_requisition
+#: field:purchase.requisition.line,logistic_requisition_source_ids:0
#: view:transport.plan:0
#: field:transport.plan,logistic_requisition_source_ids:0
msgid "Logistic Requisition Source Lines"
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "External (NS, Others)"
-msgstr ""
-
-#. module: logistic_requisition
#: field:logistic.requisition.source.transport.plan,transport_estimated_cost:0
msgid "Transportation Estimated Costs"
msgstr ""
@@ -742,16 +878,6 @@
msgstr ""
#. module: logistic_requisition
-#: field:purchase.requisition.line,logistic_requisition_source_id:0
-msgid "Logistic Requisition Source Line"
-msgstr ""
-
-#. module: logistic_requisition
-#: view:logistic.requisition:0
-msgid "Validation"
-msgstr ""
-
-#. module: logistic_requisition
#: help:logistic.requisition.source.transport.plan,date_eta:0
msgid "Estimated Date of Arrival if not set requisition ETA will be used"
msgstr ""
@@ -764,8 +890,14 @@
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-msgid " validated on "
+#: code:addons/logistic_requisition/model/sale_order.py:45
+#, python-format
+msgid "The logistic requisition line %s has no purchase order line."
+msgstr ""
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:187
+msgid "Approval"
msgstr ""
#. module: logistic_requisition
@@ -780,6 +912,11 @@
msgstr ""
#. module: logistic_requisition
+#: help:logistic.requisition,pricelist_id:0
+msgid "Pricelist that represent the currency for current logistic request."
+msgstr ""
+
+#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_stock_incoterms
msgid "Incoterms"
msgstr ""
@@ -805,7 +942,6 @@
#. module: logistic_requisition
#: view:logistic.requisition:0
-#: field:logistic.requisition,partner_id:0
msgid "Requesting Entity"
msgstr ""
@@ -840,6 +976,17 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1044
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1052
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1059
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1066
+#: code:addons/logistic_requisition/model/sale_order.py:44
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:261
+#, python-format
+msgid "Error"
+msgstr ""
+
+#. module: logistic_requisition
#: model:logistic.requisition.cancel.reason,name:logistic_requisition.cancel_reason_no_service_needed
msgid "No service needed anymore"
msgstr ""
@@ -851,11 +998,7 @@
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition.source,unit_cost:0
-msgid "Unit Cost"
-msgstr ""
-
-#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:162
#: view:logistic.requisition:0
#: field:logistic.requisition.line,requested_qty:0
msgid "Quantity"
@@ -883,11 +1026,8 @@
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: field:logistic.requisition,requester_type:0
-#: view:logistic.requisition.line:0
-#: field:logistic.requisition.line,requester_type:0
-msgid "Type of Requestor"
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:160
+msgid "Number"
msgstr ""
#. module: logistic_requisition
@@ -896,6 +1036,12 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1091
+#, python-format
+msgid "Existing"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition.line:0
#: selection:logistic.requisition.line,state:0
#: selection:logistic.requisition.source,state:0
@@ -903,8 +1049,9 @@
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition.line,budget_tot_price:0
-msgid "Budget Total Price"
+#: view:logistic.requisition.line:0
+#: view:logistic.requisition.source:0
+msgid "Total Budget"
msgstr ""
#. module: logistic_requisition
@@ -939,15 +1086,8 @@
msgstr ""
#. module: logistic_requisition
-#: selection:logistic.requisition.source,price_is:0
-#: selection:sale.order.line,price_is:0
-msgid "Estimated"
-msgstr ""
-
-#. module: logistic_requisition
-#: model:ir.actions.act_window,name:logistic_requisition.action_logistic_requisition_cancel
-#: view:logistic.requisition.cancel:0
-msgid "Reason for the cancellation"
+#: field:purchase.order.line,po_line_from_bid_ids:0
+msgid "Lines generated by the bid"
msgstr ""
#. module: logistic_requisition
@@ -971,18 +1111,16 @@
msgstr ""
#. module: logistic_requisition
-#: selection:logistic.requisition,requester_type:0
-#: selection:logistic.requisition.line,requester_type:0
-#: selection:res.partner,requester_type:0
-msgid "Federation (internal)"
-msgstr ""
-
-#. module: logistic_requisition
#: view:logistic.requisition.line:0
msgid "Req. Date:"
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:182
+msgid "General Remarks"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition:0
#: field:logistic.requisition,cost_estimate_only:0
#: view:logistic.requisition.line:0
@@ -1006,8 +1144,8 @@
msgstr ""
#. module: logistic_requisition
-#: field:res.partner,requester_type:0
-msgid "Requester Type"
+#: selection:logistic.requisition.source,procurement_method:0
+msgid "Other"
msgstr ""
#. module: logistic_requisition
@@ -1034,6 +1172,7 @@
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_purchase_requisition_line
+#: field:logistic.requisition.source,purchase_requisition_line_id:0
msgid "Purchase Requisition Line"
msgstr ""
@@ -1050,6 +1189,7 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:161
#: field:logistic.requisition.line,description:0
msgid "Description"
msgstr ""
@@ -1061,9 +1201,9 @@
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition:0
-#: view:logistic.requisition.line:0
-msgid "Requestor is the Federation"
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1053
+#, python-format
+msgid "The sourcing lines do not belong to the same company."
msgstr ""
#. module: logistic_requisition
@@ -1072,6 +1212,12 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1097
+#, python-format
+msgid "The sourcing line %d does not have any product defined, please choose one."
+msgstr ""
+
+#. module: logistic_requisition
#: field:logistic.requisition.source.transport.plan,transport_mode_id:0
msgid "Transport by"
msgstr ""
@@ -1082,8 +1228,14 @@
msgstr ""
#. module: logistic_requisition
-#: help:logistic.requisition.line,logistic_user_id:0
-msgid "User in charge of the Logistic Requisition Line"
+#: code:addons/logistic_requisition/model/logistic_requisition.py:632
+#, python-format
+msgid "Logistic Requisition Line %s Assigned"
+msgstr ""
+
+#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:191
+msgid "Requested date"
msgstr ""
#. module: logistic_requisition
@@ -1092,8 +1244,8 @@
msgstr ""
#. module: logistic_requisition
-#: field:logistic.requisition,allowed_budget:0
-msgid "Allowed Budget"
+#: field:logistic.requisition,partner_id:0
+msgid "Customer"
msgstr ""
#. module: logistic_requisition
@@ -1102,11 +1254,6 @@
msgstr ""
#. module: logistic_requisition
-#: help:logistic.requisition.line.assign,logistic_user_id:0
-msgid "Logistic Specialist in charge of the Logistic Requisition Line"
-msgstr ""
-
-#. module: logistic_requisition
#: field:logistic.requisition,line_ids:0
msgid "Products to Purchase"
msgstr ""
@@ -1117,6 +1264,7 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:143
#: view:logistic.requisition:0
#: field:logistic.requisition,country_id:0
#: view:logistic.requisition.line:0
@@ -1125,8 +1273,11 @@
msgstr ""
#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:693
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:293
#: view:logistic.requisition.cost.estimate:0
#: field:logistic.requisition.line,cost_estimate_id:0
+#, python-format
msgid "Cost Estimate"
msgstr ""
@@ -1146,23 +1297,24 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:144
#: view:logistic.requisition:0
#: field:logistic.requisition,date:0
msgid "Requisition Date"
msgstr ""
#. module: logistic_requisition
-#: view:logistic.requisition.line:0
-msgid "Total Proposed"
-msgstr ""
-
-#. module: logistic_requisition
#: view:logistic.requisition.cancel:0
msgid "Choose the reason for the cancellation of the\n"
" logistic requisition."
msgstr ""
#. module: logistic_requisition
+#: field:logistic.requisition.source,proposed_product_id:0
+msgid "Proposed Product"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition.line.assign:0
msgid "Select the assignee"
msgstr ""
@@ -1170,6 +1322,7 @@
#. module: logistic_requisition
#: model:ir.model,name:logistic_requisition.model_logistic_requisition_source
#: view:logistic.requisition.source:0
+#: field:purchase.order.line,lr_source_line_id:0
msgid "Logistic Requisition Source"
msgstr ""
@@ -1188,6 +1341,11 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:102
+msgid "Shipping address:"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition.source.create.requisition:0
msgid " Please note that: \n"
" \n"
@@ -1208,7 +1366,13 @@
msgstr ""
#. module: logistic_requisition
+#: report:addons/logistic_requisition/report/logistic_requisition.mako:178
+msgid "Delivery Remarks"
+msgstr ""
+
+#. module: logistic_requisition
#: field:logistic.requisition.line,account_code:0
+#: field:sale.order.line,account_code:0
msgid "Account Code"
msgstr ""
@@ -1218,6 +1382,17 @@
msgstr ""
#. module: logistic_requisition
+#: field:logistic.requisition.source.create.requisition,pricelist_id:0
+msgid "Pricelist / Currency"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1060
+#, python-format
+msgid "The sourcing lines do not have the same consignee."
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition.cancel:0
#: view:logistic.requisition.cost.estimate:0
#: view:logistic.requisition.line.assign:0
@@ -1227,7 +1402,24 @@
msgstr ""
#. module: logistic_requisition
+#: view:logistic.requisition:0
+msgid "View Sourcing Lines"
+msgstr ""
+
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/model/logistic_requisition.py:1096
+#, python-format
+msgid "Missing information"
+msgstr ""
+
+#. module: logistic_requisition
#: view:logistic.requisition.line:0
msgid "Open Cost Estimate"
msgstr ""
+#. module: logistic_requisition
+#: code:addons/logistic_requisition/wizard/cost_estimate.py:105
+#, python-format
+msgid "Transport from %s to %s by %s (Ref. %s)"
+msgstr ""
+
=== modified file 'logistic_requisition/model/logistic_requisition.py'
--- logistic_requisition/model/logistic_requisition.py 2013-11-01 09:32:02 +0000
+++ logistic_requisition/model/logistic_requisition.py 2014-03-10 15:39:56 +0000
@@ -158,40 +158,22 @@
readonly=True,
required=True
),
- 'amount_total': fields.function(
- lambda self, *args, **kwargs: self._get_amount(*args, **kwargs),
- digits_compute=dp.get_precision('Account'),
- string='Total Budget',
- store={
- 'logistic.requisition': (
- lambda self, cr, uid, ids, c=None: ids,
- ['line_ids'], 20),
- 'logistic.requisition.line': (
- lambda self, *a, **kw: self._store_get_requisition_ids(*a, **kw),
- ['requested_qty', 'budget_unit_price', 'budget_tot_price', 'requisition_id'], 20),
- }),
'sourced': fields.function(
lambda self, *args, **kwargs: self._get_sourced(*args, **kwargs),
string='Sourced',
type='float'
),
- 'allowed_budget': fields.boolean('Allowed Budget'),
- 'currency_id': fields.related('company_id',
+ 'pricelist_id': fields.many2one('product.pricelist',
+ 'Pricelist',
+ required=True,
+ states=REQ_STATES,
+ help="Pricelist that represent the currency for current logistic request."),
+ 'currency_id': fields.related('pricelist_id',
'currency_id',
type='many2one',
relation='res.currency',
string='Currency',
readonly=True),
- 'budget_holder_id': fields.many2one(
- 'res.users',
- string='Budget Holder'),
- 'date_budget_holder': fields.datetime(
- 'Budget Holder Validation Date'),
- 'finance_officer_id': fields.many2one(
- 'res.users',
- string='Finance Officer'),
- 'date_finance_officer': fields.datetime(
- 'Finance Officer Validation Date'),
'cancel_reason_id': fields.many2one(
'logistic.requisition.cancel.reason',
string='Reason for Cancellation',
@@ -214,13 +196,6 @@
'Logistic Requisition Reference must be unique!'),
]
- def _get_amount(self, cr, uid, ids, name, args, context=None):
- res = {}
- for requisition in self.browse(cr, uid, ids, context=context):
- res[requisition.id] = sum(line.budget_tot_price for line
- in requisition.line_ids)
- return res
-
def _get_sourced(self, cr, uid, ids, name, args, context=None):
res = {}
for requisition in self.browse(cr, uid, ids, context=context):
@@ -268,10 +243,6 @@
line_obj = self.pool.get('logistic.requisition.line')
line_obj._do_draft(cr, uid, line_ids, context=context)
vals = {'state': 'draft',
- 'budget_holder_id': False,
- 'date_budget_holder': False,
- 'finance_officer_id': False,
- 'date_finance_officer': False,
'cancel_reason_id': False,
}
self.write(cr, uid, ids, vals, context=context)
@@ -296,13 +267,24 @@
default.update({
'state': 'draft',
'name': '/',
- 'budget_holder_id': False,
- 'date_budget_holder': False,
- 'finance_officer_id': False,
- 'date_finance_officer': False,
})
return super(logistic_requisition, self).copy(cr, uid, id, default=default, context=context)
+ def onchange_partner_id(self, cr, uid, ids, part, context=None):
+ """We take the pricelist of the chosen partner"""
+ values = {'pricelist_id': False}
+ if not part:
+ return {'value': values}
+
+ part = self.pool.get('res.partner').browse(cr, uid, part,
+ context=context)
+ pricelist = part.property_product_pricelist
+ pricelist = pricelist.id if pricelist else False
+ values = {}
+ if pricelist:
+ values['pricelist_id'] = pricelist
+ return {'value': values}
+
def onchange_consignee_id(self, cr, uid, ids, consignee_id, context=None):
values = {'consignee_shipping_id': False}
if not consignee_id:
@@ -425,13 +407,9 @@
'Product UoM',
states=REQUEST_STATES,
required=True),
- 'budget_tot_price': fields.float(
- 'Budget Total Price',
- states=REQUEST_STATES,
- digits_compute=dp.get_precision('Account')),
- 'budget_unit_price': fields.function(
- lambda self, *args, **kwargs: self._get_unit_amount_line(*args, **kwargs),
- string='Budget Unit Price',
+ 'amount_total': fields.function(
+ lambda self, *args, **kwargs: self._get_total_cost(*args, **kwargs),
+ string='Total Amount',
type="float",
digits_compute=dp.get_precision('Account'),
store=True),
@@ -572,11 +550,13 @@
context=context, load='_classic_write')
return list(set([x['requisition_id'] for x in reqs]))
- def _get_unit_amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict, context=None):
+ def _get_total_cost(self, cr, uid, ids, name, args, context=None):
res = {}
+ for i in ids:
+ res[i] = 0.0
for line in self.browse(cr, uid, ids, context=context):
- price = line.budget_tot_price / line.requested_qty
- res[line.id] = price
+ for source_line in line.source_ids:
+ res[line.id] += source_line.total_cost
return res
def view_stock_by_location(self, cr, uid, ids, context=None):
@@ -749,25 +729,6 @@
'quoted': [('readonly', True)]
}
- def _purchase_line_id(self, cr, uid, ids, field_name, arg, context=None):
- """ For each line, returns the generated purchase line from the
- purchase requisition.
- """
- result = {}
- for line in self.browse(cr, uid, ids, context=context):
- result[line.id] = False
- bid_line = line.bid_line_id
- if not bid_line:
- continue
- po_lines = bid_line.po_line_from_bid_ids
- if not po_lines:
- continue
- assert len(po_lines) == 1, (
- "We should not have several purchase order lines "
- "for a logistic requisition line")
- result[line.id] = po_lines[0].id if po_lines else False
- return result
-
def _default_source_address(self, cr, uid, ids, field_name, arg, context=None):
"""Return the default source address depending of the procurment method"""
res = {}
@@ -818,6 +779,7 @@
[('procurement', 'Procurement'),
('wh_dispatch', 'Warehouse Dispatch'),
('fw_agreement', 'Framework Agreement'),
+ ('other', 'Other'),
],
string='Procurement Method',
required=True,
@@ -859,6 +821,12 @@
type='float',
digits_compute=dp.get_precision('Account'),
store=True),
+ 'currency_id': fields.related('requisition_id',
+ 'currency_id',
+ type='many2one',
+ relation='res.currency',
+ string='Currency',
+ readonly=True),
'transport_applicable': fields.boolean(
'Transport Applicable',
states=SOURCED_STATES),
@@ -928,6 +896,8 @@
'transport_applicable': False,
'price_is': 'fixed',
'name': '/',
+ 'procurement_method': 'other',
+ 'proposed_qty': 1
}
_constraints = [
(lambda self, *a, **kw: self._check_transport_plan(*a, **kw),
@@ -938,9 +908,6 @@
"A transport plan cannot be linked to lines of different "
"logistic requisitions.",
['transport_plan_id', 'requisition_id']),
- (lambda self, *a, **kw: self._check_source_lines_total_amount(*a, **kw),
- 'The total cost cannot be more than the total budget.',
- ['proposed_qty', 'unit_cost', 'requisition_line_id']),
(lambda self, *a, **kw: self._check_purchase_requisition_unique(*a, **kw),
"A call for bids cannot be linked to lines of different "
"logistics requisitions.",
@@ -955,10 +922,16 @@
return False
return True
+ def _is_sourced_other(self, cr, uid, source, context=None):
+ """Predicate function to test if line on other
+ method are sourced"""
+ return self._is_sourced_procurement(cr, uid, source,
+ context=context)
+
def _is_sourced_wh_dispatch(self, cr, uid, source, context=None):
- """Predicate function to test if line on warehouse
- method are sourced"""
- return True
+ """Predicate function to test if line on warehouse
+ method are sourced"""
+ return True
def _is_sourced(self, cr, uid, source_id, context=None):
""" check if line is source using predicate function
@@ -1011,32 +984,32 @@
return False
return True
- def _check_source_lines_total_amount(self, cr, uid, ids, context=None):
- for source in self.browse(cr, uid, ids, context=context):
- line = source.requisition_line_id
- total = sum(source.unit_cost * source.proposed_qty
- for source in line.source_ids)
- if total > line.budget_tot_price:
- return False
- return True
-
def _get_purchase_line_id(self, cr, uid, ids, field_name, arg, context=None):
""" For each line, returns the generated purchase line from the
purchase requisition.
"""
result = {}
+ po_line_model = self.pool['purchase.order.line']
+ po_lines = None
for line in self.browse(cr, uid, ids, context=context):
result[line.id] = False
- bid_line = line.selected_bid_line_id
- if not bid_line:
- continue
- po_lines = bid_line.po_line_from_bid_ids
- if not po_lines:
- continue
+ if line.selected_bid_line_id:
+ bid_line = line.selected_bid_line_id
+ if not bid_line:
+ continue
+ po_lines = [x.id for x in bid_line.po_line_from_bid_ids]
+ if not po_lines:
+ continue
+ else:
+ po_lines = po_line_model.search(cr, uid,
+ [('lr_source_line_id', '=', line.id),
+ ('state', '!=', 'cancel')],
+ context=context)
assert len(po_lines) == 1, (
"We should not have several purchase order lines "
"for a logistic requisition line")
- result[line.id] = po_lines[0].id if po_lines else False
+ result[line.id] = po_lines[0] if po_lines else False
+
return result
def create(self, cr, uid, vals, context=None):
@@ -1046,7 +1019,18 @@
return super(logistic_requisition_source, self).create(cr, uid, vals,
context=context)
- def _prepare_po_requisition(self, cr, uid, sources, purch_req_lines, context=None):
+ def _get_purchase_pricelist_from_currency(self, cr, uid, currency_id, context=None):
+ """ This method will look for a pricelist of type 'purchase' using
+ the same currency than than the given one.
+ return : ID of product.pricelist type Integer
+ """
+ pricelist_obj = self.pool.get('product.pricelist')
+ pricelist_id = pricelist_obj.search(cr, uid,
+ [('currency_id','=',currency_id),('type','=','purchase')], limit=1)
+ return pricelist_id[0]
+
+ def _prepare_po_requisition(self, cr, uid, sources, purch_req_lines,
+ pricelist=None, context=None):
company_id = None
user_id = None
consignee_id = None
@@ -1084,12 +1068,24 @@
_('Error'),
_('The sourcing lines do not have the '
'same delivery address.'))
+ line_pricelist_id = self._get_purchase_pricelist_from_currency(
+ cr,
+ uid,
+ line.requisition_id.pricelist_id.currency_id.id,
+ context=context
+ )
+ if pricelist is None:
+ pricelist = line_pricelist_id
return {'user_id': user_id or uid,
'company_id': company_id,
'consignee_id': consignee_id,
'dest_address_id': dest_address_id,
'line_ids': [(0, 0, rline) for rline in purch_req_lines],
'origin': ", ".join(origin),
+ 'req_incoterm_id': line.requisition_id.incoterm_id.id,
+ 'req_incoterm_address': line.requisition_id.incoterm_address,
+ 'pricelist_id': pricelist,
+ 'schedule_date': line.requisition_id.date_delivery,
}
def _prepare_po_requisition_line(self, cr, uid, line, context=None):
@@ -1111,17 +1107,27 @@
'logistic_requisition_source_ids': [(4, line.id)],
}
- def _action_create_po_requisition(self, cr, uid, ids, context=None):
+ def _action_create_po_requisition(self, cr, uid, ids, pricelist=None, context=None):
purch_req_obj = self.pool.get('purchase.requisition')
purch_req_lines = []
lines = self.browse(cr, uid, ids, context=context)
+ if not next((x for x in lines
+ if x.procurement_method == 'procurement'), None):
+ raise orm.except_orm(_('There should be at least one selected'
+ ' line with procurement method Procurement'),
+ _('Please correct selection'))
for line in lines:
+ if line.procurement_method not in ('other', 'procurement'):
+ raise orm.except_orm(_('Selected line procurement method should'
+ ' be procurement or other'),
+ _('Please correct selection'))
vals = self._prepare_po_requisition_line(cr, uid, line,
context=context)
purch_req_lines.append(vals)
vals = self._prepare_po_requisition(cr, uid,
lines,
purch_req_lines,
+ pricelist=pricelist,
context=context)
purch_req_id = purch_req_obj.create(cr, uid, vals, context=context)
self.write(cr, uid, ids,
@@ -1129,8 +1135,10 @@
context=context)
return purch_req_id
- def action_create_po_requisition(self, cr, uid, ids, context=None):
- self._action_create_po_requisition(cr, uid, ids, context=context)
+ def action_create_po_requisition(self, cr, uid, ids,
+ pricelist=None, context=None):
+ self._action_create_po_requisition(cr, uid, ids,
+ pricelist=pricelist, context=context)
return self.action_open_po_requisition(cr, uid, ids, context=context)
def action_open_po_requisition(self, cr, uid, ids, context=None):
=== modified file 'logistic_requisition/model/purchase.py'
--- logistic_requisition/model/purchase.py 2013-09-20 07:12:14 +0000
+++ logistic_requisition/model/purchase.py 2014-03-10 15:39:56 +0000
@@ -21,11 +21,35 @@
from openerp.osv import orm, fields
from openerp import netsvc
+from openerp.tools.translate import _
class purchase_order(orm.Model):
_inherit = 'purchase.order'
+ def validate_service_product_procurement(self, cr, uid, ids, context=None):
+ """ As action_picking_create only take care of non-service product
+ by looping on the moves, we need then to pass through all line with
+ product of type service and confirm them.
+ This way all procurements will reach the done state once the picking
+ related to the PO will be done and in the mean while the SO will be
+ then marked as delivered.
+ """
+ wf_service = netsvc.LocalService("workflow")
+ proc_obj = self.pool.get('procurement.order')
+ # Proc product of type service should be confirm at this
+ # stage, otherwise, when picking of related PO is created
+ # then done, it stay blocked at running stage
+ proc_ids = proc_obj.search(cr, uid, [('purchase_id', 'in', ids)],
+ context=context)
+ for proc in proc_obj.browse(cr, uid, proc_ids, context=context):
+ if proc.product_id.type == 'service':
+ wf_service.trg_validate(uid, 'procurement.order',
+ proc.id, 'button_confirm', cr)
+ wf_service.trg_validate(uid, 'procurement.order',
+ proc.id, 'button_check', cr)
+ return True
+
def action_picking_create(self, cr, uid, ids, context=None):
""" When the picking is created, we'll:
@@ -62,7 +86,7 @@
if purchase_line is not None:
wf_service.trg_validate(uid, 'procurement.order',
procurement.id, 'button_check', cr)
-
+ self.validate_service_product_procurement(cr, uid, ids, context)
return picking_id
@@ -85,3 +109,93 @@
'Lines generated by the bid',
readonly=True),
}
+
+ def _prepare_lrs_update_from_po_line(self, cr, uid, vals,
+ po_line, context=None):
+ """ Take the vals dict from po line and return a vals dict for LRS
+
+ :param dict vals: value of to be written in new po line
+ :param browse_record po_line: purchase.order.line
+ :returns dict : vals to be written on logistic.requisition.source
+
+ """
+ lrs_vals = {}
+ if vals.get('product_qty'):
+ lrs_vals['proposed_qty'] = vals.get('product_qty')
+ if vals.get('product_id'):
+ lrs_vals['proposed_product_id'] = vals.get('product_id')
+ if vals.get('product_uom'):
+ lrs_vals['proposed_uom_id'] = vals.get('product_uom')
+ if vals.get('price_unit'):
+ currency_obj = self.pool['res.currency']
+ to_curr = po_line.lr_source_line_id.requisition_id.currency_id.id
+ from_curr = po_line.order_id.pricelist_id.currency_id.id
+ price = currency_obj.compute(cr, uid, from_curr, to_curr,
+ vals.get('price_unit'), False)
+ lrs_vals['unit_cost'] = price
+ if vals.get('date_planned'):
+ if po_line.lr_source_line_id.transport_applicable:
+ if pr_bid_line.order_id.transport == 'included':
+ lrs_vals['date_etd'] = False
+ lrs_vals['date_eta'] = vals.get('date_planned')
+
+ else:
+ lrs_vals['date_etd'] = vals.get('date_planned')
+ lrs_vals['date_eta'] = False
+ else:
+ lrs_vals['date_etd'] = vals.get('date_planned')
+ lrs_vals['date_eta'] = vals.get('date_planned')
+ return lrs_vals
+
+ def write(self, cr, uid, ids, vals, context=None):
+ """ Here we implement something to allow the update of LRS when some
+ information are changed in PO line. It should be possible to do it when :
+ PO is still in draft
+ LRL is not marked as sourced
+ Once done, nobody should be able to change the PO line infos
+ """
+ if context is None:
+ context = {}
+ if not ids:
+ return True
+ #We have to enforce list as it is called by function_inv
+ if not isinstance(ids, list):
+ ids = [ids]
+ if (vals.get('product_qty') or vals.get('product_id')
+ or vals.get('product_uom')
+ or vals.get('price_unit')
+ or vals.get('date_planned')):
+ lrs_obj = self.pool.get('logistic.requisition.source')
+ for line in self.browse(cr, uid, ids, context=context):
+ if line.lr_source_line_id:
+ if (line.lr_source_line_id.requisition_line_id in
+ ('sourced', 'quoted')):
+ raise osv.except_osv(
+ _('UserError'),
+ _(
+ "You cannot change the informations because this PO line "
+ "is already linked to a Logistic Requsition Line %s marked "
+ "as sourced or quoted." % (line.lr_source_line_id.name)
+ )
+ )
+ else:
+ lrs_vals = self._prepare_lrs_update_from_po_line(cr,
+ uid, vals, line, context=context)
+ lrs_obj.write(cr, uid, [line.lr_source_line_id.id],
+ lrs_vals, context=context)
+ return super(purchase_order_line, self).write(cr, uid, ids, vals,
+ context=context)
+ def unlink(self, cr, uid, ids, context=None):
+ for line in self.browse(cr, uid, ids, context=context):
+ if line.lr_source_line_id:
+ if (line.lr_source_line_id.requisition_line_id in
+ ('sourced', 'quoted')):
+ raise osv.except_osv(
+ _('UserError'),
+ _(
+ "You cannot delete this PO line because it is "
+ "already linked to a Logistic Requsition Line %s marked "
+ "as sourced or quoted." % (line.lr_source_line_id.name)
+ )
+ )
+ return super(purchase_order_line, self).unlink(cr, uid, ids, context=context)
=== modified file 'logistic_requisition/model/purchase_requisition.py'
--- logistic_requisition/model/purchase_requisition.py 2013-11-01 09:05:44 +0000
+++ logistic_requisition/model/purchase_requisition.py 2014-03-10 15:39:56 +0000
@@ -31,6 +31,7 @@
For each selected bid line, we ensure there is a corresponding source
(one2one relation) and we update the source data with the bid line data.
"""
+ currency_obj = self.pool['res.currency']
if isinstance(id, (tuple, list)):
assert len(id) == 1, (
"_split_requisition_source_lines() accepts only 1 ID, "
@@ -43,16 +44,24 @@
# this call for bid line has been added manually
continue
source = pr_line.logistic_requisition_source_ids[0]
+ has_requisition = source.requisition_id
+ if not has_requisition:
+ continue
+ to_curr = source.requisition_id.currency_id.id
set_sources = set()
# Look for po lines of this purchase_requisition line
for pr_bid_line in pr_line.purchase_line_ids:
+ # Compute from bid currency to LRS currency
+ from_curr = pr_bid_line.order_id.pricelist_id.currency_id.id
+ price = currency_obj.compute(cr, uid, from_curr, to_curr,
+ pr_bid_line.price_unit, False)
vals = {
'price_is': 'fixed',
'proposed_qty': pr_bid_line.quantity_bid,
'proposed_product_id': pr_bid_line.product_id.id,
'proposed_uom_id': pr_bid_line.product_uom.id,
'selected_bid_line_id': pr_bid_line.id,
- 'unit_cost': pr_bid_line.price_unit,
+ 'unit_cost': price,
#FIXME: we need to take care of the scheduled date
# set eta or etd depending if transport is included
}
@@ -119,19 +128,6 @@
vals['from_bid_line_id'] = line.id
return vals
- #FIXME: we should not do that, the location must be known from the beginning of the RFQ-Bid process
- #def _prepare_purchase_order(self, cr, uid, requisition, supplier, context=None):
- # vals = super(purchase_requisition, self)._prepare_purchase_order(
- # cr, uid, requisition, supplier, context=context)
- # if requisition.logistic_requisition_source_ids:
- # # comes from a logistic.requisition -> generate direct
- # # delivery purchases, so we put the location customer
- # # of the first consignee_id (same consignee on all lines)
- # source = requisition.logistic_requisition_source_ids[0]
- # req = source.requisition_id
- # vals['location_id'] = req.consignee_id.property_stock_customer.id
- # return vals
-
class purchase_requisition_line(orm.Model):
_inherit = 'purchase.requisition.line'
=== modified file 'logistic_requisition/model/sale_order.py'
--- logistic_requisition/model/sale_order.py 2013-09-10 11:08:34 +0000
+++ logistic_requisition/model/sale_order.py 2014-03-10 15:39:56 +0000
@@ -72,7 +72,6 @@
# with it
vals['purchase_id'] = purchase_line.order_id.id
proc_id = proc_obj.create(cr, uid, vals, context=context)
-
sale_line.write({'procurement_id': proc_id})
# We do not confirm the procurement. It will stay in 'draft'
# without reservation move. At the moment when the picking
@@ -80,6 +79,8 @@
# the id of the picking's move in this procurement and
# confirm the procurement
# (see in purchase_order.action_picking_create())
+ # In there, we'll also take care and confirm all procurements
+ # with product of type service.
# set the purchases to direct delivery
purchase_obj = self.pool.get('purchase.order')
@@ -133,6 +134,14 @@
cr, uid, order, list(lines), picking_id=False, context=context)
return True
+ def copy(self, cr, uid, id, default=None, context=None):
+ if not default:
+ default = {}
+ default['invoice_ids'] = False
+ default['requisition_id'] = False
+ return super(sale_order, self).copy(cr, uid, id,
+ default=default, context=context)
+
class sale_order_line(orm.Model):
_inherit = "sale.order.line"
=== modified file 'logistic_requisition/report/logistic_requisition.mako'
--- logistic_requisition/report/logistic_requisition.mako 2013-08-14 14:26:08 +0000
+++ logistic_requisition/report/logistic_requisition.mako 2014-03-10 15:39:56 +0000
@@ -197,29 +197,6 @@
<td> </td>
</tr>
</table>
- <p><b>${_("Budget")}</b></p>
- <p>${_("Budget limit:")} ${formatLang(requisition.allowed_budget)}</p>
- <table class="basic_table" width="100%">
- <tr>
- <td style="font-weight:bold;width:40%">${_("Budget/financial holder")}</td>
- <td style="font-weight:bold;width:30%">${_("Validation date")}</td>
- <td style="font-weight:bold;">${_("Signature")}</td>
- </tr>
- %if requisition.budget_holder_id:
- <tr>
- <td>${requisition.budget_holder_id.name}</td>
- <td>${requisition.date_budget_holder if requisition.date_budget_holder else 'N/A'}</td>
- <td> </td>
- </tr>
- %endif
- %if requisition.finance_officer_id:
- <tr>
- <td>${requisition.finance_officer_id.name}</td>
- <td>${requisition.date_finance_officer if requisition.date_finance_officer else 'N/A'}</td>
- <td> </td>
- </tr>
- %endif
- </table>
<p style="page-break-after: always"/>
%endfor
</body>
=== modified file 'logistic_requisition/security/ir.model.access.csv'
--- logistic_requisition/security/ir.model.access.csv 2013-08-06 14:04:56 +0000
+++ logistic_requisition/security/ir.model.access.csv 2014-03-10 15:39:56 +0000
@@ -3,3 +3,5 @@
access_logistic_requisition_line_purchase_user,logistic.requisition.line,model_logistic_requisition_line,purchase.group_purchase_user,1,1,1,1
access_logreq_cancel_reason_user,access_logistic_requisition_cancel_reason user,model_logistic_requisition_cancel_reason,purchase.group_purchase_user,1,0,0,0
access_logreq_cancel_reason_manager,access_logistic_requisition_cancel_reason manager,model_logistic_requisition_cancel_reason,purchase.group_purchase_manager,1,1,1,1
+access_log_cost_create_user,logistic.requisition.cost.estimate,model_logistic_requisition_cost_estimate,base.group_sale_salesman,1,1,1,1
+access_log_cost_create_manager,logistic.requisition.cost.estimate,model_logistic_requisition_cost_estimate,base.group_sale_manager,1,1,1,1
=== modified file 'logistic_requisition/test/line_assigned.yml'
--- logistic_requisition/test/line_assigned.yml 2013-08-26 18:20:48 +0000
+++ logistic_requisition/test/line_assigned.yml 2014-03-10 15:39:56 +0000
@@ -6,6 +6,7 @@
consignee_id: base.res_partner_3
date_delivery: !eval "time.strftime('%Y-%m-%d')"
user_id: base.user_demo
+ pricelist_id: product.list0
-
And I add a line with an assignee
-
@@ -14,7 +15,6 @@
product_id: product.product_product_7
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a line without assignee
@@ -24,7 +24,6 @@
product_id: product.product_product_8
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
-
When I confirm the logistic requisition
-
=== modified file 'logistic_requisition/test/logistic_requisition_report_test.yml'
--- logistic_requisition/test/logistic_requisition_report_test.yml 2013-08-27 14:33:41 +0000
+++ logistic_requisition/test/logistic_requisition_report_test.yml 2014-03-10 15:39:56 +0000
@@ -8,11 +8,11 @@
consignee_id: base.res_partner_3
date_delivery: !eval "time.strftime('%Y-%m-%d')"
user_id: base.user_demo
+ pricelist_id: product.list0
line_ids:
- product_id: product.product_product_7
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
account_code: 'Shazam!'
-
=== modified file 'logistic_requisition/test/requisition_cancel_reason.yml'
--- logistic_requisition/test/requisition_cancel_reason.yml 2013-08-06 14:04:56 +0000
+++ logistic_requisition/test/requisition_cancel_reason.yml 2014-03-10 15:39:56 +0000
@@ -12,6 +12,7 @@
consignee_id: base.res_partner_3
date_delivery: !eval "time.strftime('%Y-%m-%d')"
user_id: base.user_demo
+ pricelist_id: product.list0
-
When I cancel it with the wizard asking for the reason
-
=== modified file 'logistic_requisition/test/requisition_create_cost_estimate.yml'
--- logistic_requisition/test/requisition_create_cost_estimate.yml 2013-11-01 11:17:26 +0000
+++ logistic_requisition/test/requisition_create_cost_estimate.yml 2014-03-10 15:39:56 +0000
@@ -9,6 +9,7 @@
incoterm_id: stock.incoterm_FCA
incoterm_address: incoterm address as text
analytic_id: account.analytic_consultancy
+ pricelist_id: product.list0
-
And I add a line 1
-
@@ -18,7 +19,6 @@
requested_qty: 100
requested_uom_id: product.product_uom_unit
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a source line to the line 1
@@ -42,7 +42,6 @@
requested_qty: 100
requested_uom_id: product.product_uom_unit
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a source line to the line 2
@@ -88,8 +87,6 @@
try:
self.cost_estimate(cr, uid, wizard_id)
except orm.except_orm as err:
- assert 'NO_BUDGET_VALID' in err.error_codes, (
- 'Expected an error because the budget holder is missing')
assert 'NO_ACCOUNT_CODE' in err.error_codes, (
'Expected an error because the account code is missing')
else:
@@ -97,9 +94,6 @@
-
I set some data to fulfill the business rules
-
- !record {model: logistic.requisition, id: logistic_requisition_cost_estimate_01}:
- budget_holder_id: base.user_demo
--
!record {model: logistic.requisition.line, id: logistic_requisition_line_cost_estimate_01}:
account_code: 'XXXX'
-
=== modified file 'logistic_requisition/test/transport_plan.yml'
--- logistic_requisition/test/transport_plan.yml 2013-11-01 11:17:26 +0000
+++ logistic_requisition/test/transport_plan.yml 2014-03-10 15:39:56 +0000
@@ -23,6 +23,7 @@
consignee_id: base.res_partner_3
date_delivery: !eval "time.strftime('%Y-%m-%d')"
user_id: base.user_demo
+ pricelist_id: product.list0
-
And I add a line 1
-
@@ -31,7 +32,6 @@
product_id: product.product_product_7
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a source line 1
@@ -52,7 +52,6 @@
product_id: product.product_product_8
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a source line 2
@@ -93,6 +92,7 @@
consignee_id: base.res_partner_3
date_delivery: !eval "time.strftime('%Y-%m-%d')"
user_id: base.user_demo
+ pricelist_id: product.list0
-
With a line
-
@@ -101,7 +101,6 @@
product_id: product.product_product_7
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a source line
=== modified file 'logistic_requisition/test/transport_plan_from_lr_line_wizard.yml'
--- logistic_requisition/test/transport_plan_from_lr_line_wizard.yml 2013-11-01 11:17:26 +0000
+++ logistic_requisition/test/transport_plan_from_lr_line_wizard.yml 2014-03-10 15:39:56 +0000
@@ -7,6 +7,7 @@
consignee_id: base.res_partner_3
date_delivery: !eval "time.strftime('%Y-%m-%d')"
user_id: base.user_demo
+ pricelist_id: product.list0
-
And I add a first line with an assignee
-
@@ -15,7 +16,6 @@
product_id: product.product_product_7
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a first source line
@@ -34,7 +34,6 @@
product_id: product.product_product_8
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
-
And I add a second source line
=== modified file 'logistic_requisition/test/transport_plan_to_cost_estimate.yml'
--- logistic_requisition/test/transport_plan_to_cost_estimate.yml 2013-11-01 11:17:26 +0000
+++ logistic_requisition/test/transport_plan_to_cost_estimate.yml 2014-03-10 15:39:56 +0000
@@ -27,8 +27,7 @@
consignee_id: base.res_partner_3
date_delivery: !eval "time.strftime('%Y-%m-%d')"
user_id: base.user_demo
- budget_holder_id: base.user_demo
- finance_officer_id: base.user_demo
+ pricelist_id: product.list0
-
And I add a line 1
-
@@ -37,7 +36,6 @@
product_id: product.product_product_7
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
account_code: 'XXXX'
-
@@ -60,7 +58,6 @@
product_id: product.product_product_8
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
account_code: 'YYYY'
-
@@ -83,7 +80,6 @@
product_id: product.product_product_8
requested_qty: 100
date_delivery: !eval "time.strftime('%Y-%m-%d')"
- budget_tot_price: 1000
logistic_user_id: base.user_demo
account_code: 'ZZZZ'
-
=== modified file 'logistic_requisition/tests/__init__.py'
--- logistic_requisition/tests/__init__.py 2013-08-27 14:13:33 +0000
+++ logistic_requisition/tests/__init__.py 2014-03-10 15:39:56 +0000
@@ -22,6 +22,7 @@
from . import test_sale_order_from_lr_confirm
from . import test_mto_workflow
from . import transport_plan_date
+from . import test_mutlicurrency_update_po_line
fast_suite = [
@@ -32,4 +33,5 @@
test_sale_order_from_lr_confirm,
test_mto_workflow,
transport_plan_date,
+ test_mutlicurrency_update_po_line,
]
=== modified file 'logistic_requisition/tests/logistic_requisition.py'
--- logistic_requisition/tests/logistic_requisition.py 2013-11-01 11:17:26 +0000
+++ logistic_requisition/tests/logistic_requisition.py 2014-03-10 15:39:56 +0000
@@ -43,20 +43,6 @@
log_req_obj.onchange_consignee_id(
cr, uid, [], vals.get('consignee_id'))['value']
)
- vals.update(
- log_req_obj.onchange_validate(
- cr, uid, [],
- vals.get('budget_holder_id'),
- False,
- 'date_budget_holder')['value']
- )
- vals.update(
- log_req_obj.onchange_validate(
- cr, uid, [],
- vals.get('finance_officer_id'),
- False,
- 'date_finance_officer')['value']
- )
return log_req_obj.create(cr, uid, vals)
@@ -135,6 +121,24 @@
log_req_line_obj.button_sourced(test.cr, test.uid, line_ids)
+def check_line_unit_cost(test, line_id, bid_price, bid_pricelist):
+ cr, uid = test.cr, test.uid
+ log_req_source_obj = test.registry('logistic.requisition.source')
+ pricelist_obj = test.registry('product.pricelist')
+ currency_obj = test.registry('res.currency')
+ lrs = log_req_source_obj.browse(cr, uid, line_id)
+ pricelist = pricelist_obj.browse(cr, uid, bid_pricelist)
+ to_curr = lrs.requisition_id.currency_id.id
+ from_curr = pricelist.currency_id.id
+ price = currency_obj.compute(cr, uid, from_curr, to_curr,
+ bid_price, False)
+ test.assertEquals(lrs.unit_cost,
+ price,
+ "The unit cost of the LRS should be the selected "
+ "bid value converted regarding the different currency")
+
+
+
def create_quotation(test, requisition_id, line_ids):
""" Create the quotation / cost estimate (sale.order)
@@ -174,10 +178,10 @@
return sale_id, sale_line_ids
-def create_purchase_requisition(test, source_id):
+def create_purchase_requisition(test, source_id, pricelist=None):
""" Create a purchase requisition for a logistic requisition line """
log_req_source_obj = test.registry('logistic.requisition.source')
purch_req_id = log_req_source_obj._action_create_po_requisition(
- test.cr, test.uid, [source_id])
+ test.cr, test.uid, [source_id], pricelist=pricelist)
assert purch_req_id
return purch_req_id
=== modified file 'logistic_requisition/tests/purchase_requisition.py'
--- logistic_requisition/tests/purchase_requisition.py 2013-08-27 14:13:33 +0000
+++ logistic_requisition/tests/purchase_requisition.py 2014-03-10 15:39:56 +0000
@@ -45,6 +45,12 @@
purch_req_obj.close_callforbids_ok(test.cr, test.uid,
[purchase_requisition_id])
+def change_pricelist(test, purchase_requisition_id, pricelist_id):
+ """ Change the pricelist """
+ purch_req_obj = test.registry('purchase.requisition')
+ purch_req_obj.write(test.cr, test.uid, [purchase_requisition_id],
+ {'pricelist_id': pricelist_id})
+
def create_draft_purchase_order(test, purchase_requisition_id, partner_id):
""" Create a draft purchase order for a purchase requisition.
=== modified file 'logistic_requisition/tests/test_mto_workflow.py'
--- logistic_requisition/tests/test_mto_workflow.py 2013-11-01 11:17:26 +0000
+++ logistic_requisition/tests/test_mto_workflow.py 2014-03-10 15:39:56 +0000
@@ -80,20 +80,19 @@
self.product_7 = self.ref('product.product_product_7')
self.product_8 = self.ref('product.product_product_8')
self.product_uom_pce = self.ref('product.product_uom_unit')
+ self.pricelist_sale = self.ref('product.list0')
self.vals = {
'partner_id': self.partner_4,
'consignee_id': self.partner_3,
'date_delivery': time.strftime(D_FMT),
'user_id': self.user_demo,
- 'budget_holder_id': self.user_demo,
- 'finance_officer_id': self.user_demo,
+ 'pricelist_id' : self.pricelist_sale,
}
self.line = {
'product_id': self.product_7,
'requested_qty': 100,
'requested_uom_id': self.product_uom_pce,
'date_delivery': time.strftime(D_FMT),
- 'budget_tot_price': 1000,
'account_code': 'a code',
'source_ids': [{'proposed_qty': 100,
'proposed_product_id': self.product_7,
@@ -223,7 +222,6 @@
'requested_qty': 200,
'requested_uom_id': self.product_uom_pce,
'date_delivery': time.strftime(D_FMT),
- 'budget_tot_price': 4000,
'account_code': 'a code',
'source_ids': [{'proposed_qty': 200,
'proposed_product_id': self.product_8,
=== added file 'logistic_requisition/tests/test_mutlicurrency_update_po_line.py'
--- logistic_requisition/tests/test_mutlicurrency_update_po_line.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition/tests/test_mutlicurrency_update_po_line.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Guewen Baconnier
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import time
+import unittest2
+from functools import partial
+
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as D_FMT
+import openerp.tests.common as common
+from . import logistic_requisition
+from . import purchase_requisition
+from . import purchase_order
+
+
+class test_sale_order_from_lr_confirm(common.TransactionCase):
+ """ Test the multi-currency in the LR -> PO -> LRS process
+ and ensure that updating the PO line update properly the LRS values
+ while there still not marked as sourced or quoted.
+ """
+
+ def setUp(self):
+ super(test_sale_order_from_lr_confirm, self).setUp()
+ self.purch_req_model = self.registry('purchase.requisition')
+ self.log_req_model = self.registry('logistic.requisition')
+ self.log_req_line_model = self.registry('logistic.requisition.line')
+ self.sale_model = self.registry('sale.order')
+ self.purchase_model = self.registry('purchase.order')
+ data_model = self.registry('ir.model.data')
+ self.get_ref = partial(data_model.get_object_reference,
+ self.cr, self.uid)
+ __, self.partner_1 = self.get_ref('base', 'res_partner_1')
+ __, self.partner_3 = self.get_ref('base', 'res_partner_3')
+ __, self.partner_4 = self.get_ref('base', 'res_partner_4')
+ __, self.user_demo = self.get_ref('base', 'user_demo')
+ __, self.lr_currency_usd = self.get_ref('base', 'USD')
+ __, self.purchase_pricelist_eur = self.get_ref('purchase', 'list0')
+ __, self.pricelist_sale = self.get_ref('product', 'list0')
+ # Computer Case: make_to_order
+ __, self.product_16 = self.get_ref('product', 'product_product_16')
+ __, self.product_uom_pce = self.get_ref('product', 'product_uom_unit')
+ self.vals = {
+ 'partner_id': self.partner_4,
+ 'consignee_id': self.partner_3,
+ 'date_delivery': time.strftime(D_FMT),
+ 'user_id': self.user_demo,
+ 'currency_id': self.lr_currency_usd,
+ 'pricelist_id': self.pricelist_sale,
+ }
+
+ self.line1 = {
+ 'product_id': self.product_16, # MTO
+ 'requested_qty': 100,
+ 'requested_uom_id': self.product_uom_pce,
+ 'date_delivery': time.strftime(D_FMT),
+ 'account_code': '1234',
+ }
+ self.source1 = {
+ 'proposed_qty': 100,
+ 'proposed_product_id': self.product_16,
+ 'proposed_uom_id': self.product_uom_pce,
+ 'unit_cost': 10,
+ 'transport_applicable': 0,
+ 'procurement_method': 'procurement',
+ 'price_is': 'estimated',
+ }
+
+ def test_lrs_price_from_po_price_multicurrency(self):
+ """ The purchase requisition must generate the purchase orders on confirmation of sale order.
+
+ When a logistic requisition creates a sale order with MTO lines,
+ the confirmation of the lines should generates the purchase
+ orders on the purchase requisition linked to the logistic
+ requisition lines.
+
+ The price from USD to EUr should be respected.
+ """
+ cr, uid = self.cr, self.uid
+ requisition_id = logistic_requisition.create(self, self.vals)
+ line_id = logistic_requisition.add_line(self, requisition_id, self.line1)
+ source_id = logistic_requisition.add_source(self, line_id, self.source1)
+ logistic_requisition.confirm(self, requisition_id)
+ logistic_requisition.assign_lines(self, [line_id], self.user_demo)
+ purch_req_id = logistic_requisition.create_purchase_requisition(
+ self, source_id)
+ purchase_requisition.change_pricelist(self, purch_req_id,
+ self.purchase_pricelist_eur)
+ purchase_requisition.confirm_call(self, purch_req_id)
+ bid, bid_line = purchase_requisition.create_draft_purchase_order(
+ self, purch_req_id, self.partner_1)
+ bid_line.write({'price_unit': 10})
+ purchase_order.select_line(self, bid_line.id, 100)
+ purchase_order.bid_encoded(self, bid.id)
+ purchase_requisition.close_call(self, purch_req_id)
+ purchase_requisition.bids_selected(self, purch_req_id)
+
+ logistic_requisition.check_line_unit_cost(self, source_id, 10,
+ self.purchase_pricelist_eur)
+ # Change po value to check
+
+ logistic_requisition.source_lines(self, [line_id])
+
+ # Try to change again
+ sale_id, __ = logistic_requisition.create_quotation(
+ self, requisition_id, [line_id])
+ # the confirmation of the sale order should generate the
+ # purchase order of the purchase requisition
+ self.sale_model.action_button_confirm(cr, uid, [sale_id])
+ purch_req = self.purch_req_model.browse(cr, uid, purch_req_id)
+ self.assertEquals(purch_req.state,
+ 'done',
+ "The purchase requisition should be in 'done' state.")
+ self.assertEquals(len(purch_req.purchase_ids), 1,
+ "We should have only 1 purchase order.")
=== modified file 'logistic_requisition/tests/test_purchase_split_requisition.py'
--- logistic_requisition/tests/test_purchase_split_requisition.py 2013-11-01 11:17:26 +0000
+++ logistic_requisition/tests/test_purchase_split_requisition.py 2014-03-10 15:39:56 +0000
@@ -71,20 +71,20 @@
__, self.user_demo = self.get_ref('base', 'user_demo')
__, self.product_7 = self.get_ref('product', 'product_product_7')
__, self.product_uom_pce = self.get_ref('product', 'product_uom_unit')
+ __, self.pricelist_sale = self.get_ref('product','list0')
+
vals = {
'partner_id': self.partner_4,
'consignee_id': self.partner_3,
'date_delivery': time.strftime(D_FMT),
'user_id': self.user_demo,
- 'budget_holder_id': self.user_demo,
- 'finance_officer_id': self.user_demo,
+ 'pricelist_id': self.pricelist_sale,
}
line = {
'product_id': self.product_7,
'requested_qty': 100,
'requested_uom_id': self.product_uom_pce,
'date_delivery': time.strftime(D_FMT),
- 'budget_tot_price': 1000,
}
source = {
'proposed_qty': 100,
@@ -141,7 +141,8 @@
self.assertEquals(source.unit_cost,
bid_line.price_unit,
"The requisition line should have the price "
- "proposed on the purchase order line. ")
+ "proposed on the purchase order line as long as "
+ "their currency are the same ")
def test_split_1_line_selected(self):
""" Create a call for bids from the logistic requisition, 1 po line choosed """
@@ -214,38 +215,6 @@
"We should have 2 lines linked with the purchase "
"lines and no remaining line.")
- def test_split_too_many_products_selected_budget_exceeded(self):
- """ Create a call for bids from the logistic requisition, 2 po line choosed (budget exceeded)
-
- 30 items in a first purchase order and 80 items in a second one,
- for a total of 110 items. That means 110 products have been ordered
- but 100 only have been ordered at the origin.
-
- The total cost is greater than the requested budget.
- We should not be able to propose more than requested financially.
- """
- # create a first draft bid and select a part of the line
- purchase1, bid_line1 = purchase_requisition.create_draft_purchase_order(
- self, self.purchase_requisition.id, self.partner_1)
- bid_line1.write({'price_unit': 15})
- purchase_order.select_line(self, bid_line1.id, 30)
- purchase_order.bid_encoded(self, purchase1.id)
-
- # create a second draft bid and select a part of the line
- purchase2, bid_line2 = purchase_requisition.create_draft_purchase_order(
- self, self.purchase_requisition.id, self.partner_12)
- bid_line2.write({'price_unit': 13})
- purchase_order.select_line(self, bid_line2.id, 80)
- purchase_order.bid_encoded(self, purchase2.id)
-
- # close the call for bids
- purchase_requisition.close_call(self, self.purchase_requisition.id)
- # selection of bids will trigger the split of lines
- # the generation of po fails because the budget is exceeded
- with self.assertRaises(orm.except_orm):
- purchase_requisition.bids_selected(self,
- self.purchase_requisition.id)
-
def test_split_too_many_products_selected(self):
""" Create a call for bids from the logistic requisition, 2 po line choosed (too many)
=== modified file 'logistic_requisition/tests/test_sale_order_from_lr_confirm.py'
--- logistic_requisition/tests/test_sale_order_from_lr_confirm.py 2013-11-01 11:17:26 +0000
+++ logistic_requisition/tests/test_sale_order_from_lr_confirm.py 2014-03-10 15:39:56 +0000
@@ -55,13 +55,13 @@
# Computer Case: make_to_order
__, self.product_16 = self.get_ref('product', 'product_product_16')
__, self.product_uom_pce = self.get_ref('product', 'product_uom_unit')
+ __, self.pricelist_sale = self.get_ref('product', 'list0')
self.vals = {
'partner_id': self.partner_4,
'consignee_id': self.partner_3,
'date_delivery': time.strftime(D_FMT),
'user_id': self.user_demo,
- 'budget_holder_id': self.user_demo,
- 'finance_officer_id': self.user_demo,
+ 'pricelist_id': self.pricelist_sale,
}
self.line1 = {
@@ -69,7 +69,6 @@
'requested_qty': 100,
'requested_uom_id': self.product_uom_pce,
'date_delivery': time.strftime(D_FMT),
- 'budget_tot_price': 10000,
'account_code': '1234',
}
self.source1 = {
=== modified file 'logistic_requisition/view/logistic_requisition.xml'
--- logistic_requisition/view/logistic_requisition.xml 2014-02-07 17:21:33 +0000
+++ logistic_requisition/view/logistic_requisition.xml 2014-03-10 15:39:56 +0000
@@ -57,7 +57,7 @@
</div>
<group>
<group>
- <field name="partner_id"/>
+ <field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
<field name="consignee_id"
on_change="onchange_consignee_id(consignee_id)"
attrs="{'required': [('cost_estimate_only', '=', False)]}"/>
@@ -66,6 +66,8 @@
context="{'default_type':'delivery'}"
attrs="{'required': [('cost_estimate_only', '=', False)]}"/>
<field name="analytic_id"/>
+ <field name="pricelist_id" domain="[('type','=','sale')]"/>
+ <field name="currency_id"/>
</group>
<group>
<field name="date"/>
@@ -91,46 +93,12 @@
<field name="requested_qty" string="Quantity"/>
<field name="requested_uom_id"/>
<field name="date_delivery"/>
- <field name="budget_unit_price"/>
- <field name="budget_tot_price"/>
<field name="account_id"/>
<field name="account_code"/>
<field name="state"/>
<field name="logistic_user_id" string="Assigned To"/>
</tree>
- </field>
- <group class="oe_subtotal_footer oe_right">
- <field name="currency_id" invisible="1"/>
- <field name="amount_total"
- class="oe_subtotal_footer_separator"
- widget="monetary"
- options="{'currency_field': 'currency_id'}"/>
- </group>
- <div class="oe_clear"/>
- <group string="Validation">
- <div colspan="4">
- <label for="budget_holder_id"/>
- <field name="budget_holder_id"
- on_change="onchange_validate(budget_holder_id, date_budget_holder, 'date_budget_holder')"
- class="oe_inline"/>
- <label string=" validated on "
- attrs="{'invisible': [('budget_holder_id', '=', False)]}" />
- <field name="date_budget_holder"
- attrs="{'invisible': [('budget_holder_id', '=', False)]}"
- class="oe_inline"/>
- </div>
- <div colspan="4">
- <label for="finance_officer_id"/>
- <field name="finance_officer_id"
- on_change="onchange_validate(finance_officer_id, date_finance_officer, 'date_finance_officer')"
- class="oe_inline"/>
- <label string=" validated on "
- attrs="{'invisible': [('finance_officer_id', '=', False)]}"/>
- <field name="date_finance_officer"
- attrs="{'invisible': [('finance_officer_id', '=', False)]}"
- class="oe_inline"/>
- </div>
- </group>
+ </field>
</page>
<page string="Transportation and Delivery">
<group>
@@ -172,7 +140,6 @@
<field name="date_delivery"/>
<field name="country_id"/>
<field name="analytic_id"/>
- <field name="amount_total" sum="Total Budget"/>
<field name="currency_id"/>
<field name="state"/>
<field name="sourced" widget="progressbar"/>
@@ -209,11 +176,13 @@
<field name="user_id" />
<field name="partner_id" />
<field name="analytic_id" />
+ <field name="currency_id" />
<group expand="0" string="Group By...">
<filter string="Business Unit Officer" domain="[]" context="{'group_by':'user_id'}"/>
<filter name="partner_id" string="Requesting Entity" domain="[]" context="{'group_by':'partner_id'}"/>
<filter string="Country" domain="[]" context="{'group_by':'country_id'}"/>
<filter string="Project" domain="[]" context="{'group_by':'analytic_id'}"/>
+ <filter string="Currency" domain="[]" context="{'group_by':'currency_id'}"/>
<separator/>
<filter string="State" domain="[]" context="{'group_by':'state'}"/>
<separator/>
@@ -254,7 +223,7 @@
<field name="requested_qty"/>
<field name="requested_uom_id"/>
<field name="logistic_user_id"/>
- <field name="budget_tot_price" sum="Total Budget"/>
+ <field name="currency_id"/>
<field name="state"/>
<field name="country_id" invisible="1"/>
</tree>
@@ -370,21 +339,17 @@
</div>
<field name="date_delivery"/>
<field name="currency_id" invisible="1"/>
- <field name="budget_unit_price" widget="monetary"
- options="{'currency_field': 'currency_id'}"/>
- <field name="budget_tot_price" widget="monetary"
- options="{'currency_field': 'currency_id'}"/>
</group>
<group>
<field name="cost_estimate_only"/>
<field name="logistic_user_id"/>
<field name="account_id"/>
<field name="account_code"/>
+ <field name="currency_id"/>
</group>
</group>
<notebook>
<page string="Sourcing">
- <group>
<field name="source_ids" nolabel="1"
options='{"reload_on_button": true}'
context="{'default_requisition_line_id': active_id,
@@ -398,17 +363,22 @@
<field name="proposed_uom_id"/>
<field name="proposed_qty" sum="Total Proposed"/>
<field name="unit_cost"/>
- <field name="total_cost" sum="Total Budget"/>
+ <field name="total_cost" sum="Total Budget"
+ class="oe_subtotal_footer_separator"
+ widget="monetary"
+ options="{'currency_field': 'parent.currency_id'}"/>
<field name="po_requisition_id" invisible="1"/>
- <button name="action_create_po_requisition" icon="gtk-execute"
- string="Purchase Requisition" type="object"
- attrs="{'invisible': ['|', '|', ('procurement_method', 'not in', ['procurement', 'fw_agreement']), ('state', '!=', 'assigned'), ('po_requisition_id', '!=', False)]}" />
<button name="action_open_po_requisition" icon="terp-stock_zoom"
string="Purchase Requisition" type="object"
attrs="{'invisible': [('po_requisition_id', '=', False)]}" />
</tree>
</field>
- </group>
+ <group class="oe_subtotal_footer oe_right">
+ <field name="amount_total"
+ class="oe_subtotal_footer_separator"
+ widget="monetary"
+ options="{'currency_field': 'currency_id'}"/>
+ </group>
</page>
</notebook>
<group>
@@ -461,6 +431,7 @@
<field name="name"/>
<field name="description" />
<field name="logistic_user_id" />
+ <field name="currency_id" />
<group expand="0" string="Group By...">
<filter name="groupby_logistic_user_id" string="Assigned Specialist"
@@ -475,6 +446,8 @@
domain="[]" context="{'group_by':'product_id'}"/>
<filter name="groupby_country_id" string="Country"
domain="[]" context="{'group_by':'country_id'}"/>
+ <filter name="groupby_currency_id" string="Currency"
+ domain="[]" context="{'group_by':'currency_id'}"/>
</group>
</search>
</field>
@@ -510,6 +483,7 @@
<field name="proposed_qty"/>
<field name="proposed_uom_id"/>
<field name="total_cost" sum="Total Budget"/>
+ <field name="currency_id"/>
<field name="po_requisition_id"/>
<button name="action_create_po_requisition" icon="gtk-execute"
string="Purchase Requisition" type="object"
@@ -546,6 +520,7 @@
<field name="proposed_qty" class="oe_inline"/>
<field name="proposed_uom_id" class="oe_inline"/>
</div>
+ <field name="currency_id"/>
<field name="unit_cost" widget="monetary"
options="{'currency_field': 'currency_id'}"/>
<field name="total_cost" widget="monetary"
@@ -555,14 +530,11 @@
</group>
<group>
<group string="Purchase Requisition"
- attrs="{'invisible': [('procurement_method', 'not in', ['procurement', 'fw_agreement'])]}"
+ attrs="{'invisible': [('procurement_method', 'not in', ['procurement','other'])]}"
colspan="4">
<label for="po_requisition_id"/>
<div>
<field name="po_requisition_id" class="oe_inline"/>
- <button name="action_create_po_requisition"
- string="Purchase Requisition" type="object"
- attrs="{'invisible': [('po_requisition_id', '!=', False)]}" />
</div>
<field name="selected_bid_id"
on_change="onchange_selected_bid_id(selected_bid_id)"/>
@@ -571,8 +543,8 @@
colspan="4"
attrs="{'invisible': [('procurement_method', '!=', 'wh_dispatch')]}">
<field name="dispatch_location_id"
- on_change="onchange_dispatch_location_id(dispatch_location_id)"
- attrs="{'required': [('procurement_method', '=', 'wh_dispatch')]}"
+ on_change="onchange_dispatch_location_id(dispatch_location_id)"
+ attrs="{'required': [('procurement_method', '=', 'wh_dispatch')]}"
domain="[('usage', '!=', 'view')]"/>
<field name="stock_owner" />
</group>
@@ -624,7 +596,7 @@
<field name="res_model">logistic.requisition.source</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
- <field name="context">{"search_default_groupby_procurement_method" : True}</field>
+ <field name="context">{}</field>
<field name="search_view_id" ref="view_logistic_requisition_source_filter"/>
<field name="help"></field>
</record>
=== modified file 'logistic_requisition/wizard/cost_estimate.py'
--- logistic_requisition/wizard/cost_estimate.py 2013-10-31 15:46:50 +0000
+++ logistic_requisition/wizard/cost_estimate.py 2014-03-10 15:39:56 +0000
@@ -149,17 +149,17 @@
}
if sourcing.dispatch_location_id:
vals['location_id'] = sourcing.dispatch_location_id.id
- if sourcing.procurement_method == 'wh_dispatch':
+ if sourcing.procurement_method in ('wh_dispatch'):
vals['type'] = 'make_to_stock'
else:
- vals['type'] = 'make_to_order'
+ vals['type'] = sourcing.proposed_product_id.procure_method
vals['sale_flow'] = 'direct_delivery'
requisition = sourcing.requisition_line_id.requisition_id
onchange_vals = sale_line_obj.product_id_change(
cr, uid, [],
requisition.consignee_id.property_product_pricelist.id,
- sourcing.requisition_line_id.product_id.id,
+ sourcing.proposed_product_id.id,
partner_id=requisition.consignee_id.id,
qty=sourcing.proposed_qty,
uom=sourcing.proposed_uom_id.id).get('value', {})
@@ -178,11 +178,6 @@
:returns: list of tuples ('message, 'error_code')
"""
errors = []
- if not requisition.budget_holder_id:
- error = (_('The requisition must be validated '
- 'by the Budget Holder.'),
- 'NO_BUDGET_VALID')
- errors.append(error)
return errors
def _check_line(self, cr, uid, line, context=None):
@@ -243,12 +238,13 @@
'incoterm': requisition.incoterm_id.id,
'incoterm_address': requisition.incoterm_address,
'requisition_id': requisition.id,
+ 'origin': requisition.name,
'project_id': requisition.analytic_id.id if requisition.analytic_id else False,
}
-
onchange_vals = sale_obj.onchange_partner_id(
cr, uid, [], partner_id, context=context).get('value', {})
vals.update(onchange_vals)
+ vals.update({'pricelist_id':requisition.pricelist_id.id})
return vals
def cost_estimate(self, cr, uid, ids, context=None):
=== modified file 'logistic_requisition/wizard/logistic_line_create_requisition.py'
--- logistic_requisition/wizard/logistic_line_create_requisition.py 2013-08-23 13:34:36 +0000
+++ logistic_requisition/wizard/logistic_line_create_requisition.py 2014-03-10 15:39:56 +0000
@@ -19,18 +19,46 @@
#
##############################################################################
-from openerp.osv import orm
from openerp.tools.translate import _
+from openerp.osv import fields, orm
class LogisticRequisitionSourceCreateRequisition(orm.TransientModel):
_name = "logistic.requisition.source.create.requisition"
_description = "Create Purchase Requisition From Requisition Source"
+
+ _columns = {
+ 'pricelist_id': fields.many2one('product.pricelist',
+ string='Pricelist / Currency',
+ required=True),
+ }
+
+ def default_get(self, cr, uid, fields_list, context=None):
+ """Take the first line pricelist as default"""
+ if context is None:
+ context = {}
+ defaults = super(LogisticRequisitionSourceCreateRequisition, self).\
+ default_get(cr, uid, fields_list, context=context)
+ line_obj = self.pool.get('logistic.requisition.source')
+ line_ids = context['active_ids']
+ pricelist_id = None
+ line = line_obj.browse(cr, uid, line_ids, context=context)[0]
+ pricelist_id = line_obj._get_purchase_pricelist_from_currency(
+ cr,
+ uid,
+ line.requisition_id.pricelist_id.currency_id.id,
+ context=context
+ )
+ defaults['pricelist_id'] = pricelist_id
+ return defaults
+
def create_po_requisition(self, cr, uid, ids, context=None):
+ form = self.browse(cr, uid, ids, context=context)[0]
source_obj = self.pool.get('logistic.requisition.source')
requisition_id = source_obj._action_create_po_requisition(
- cr, uid, context.get('active_ids', []), context=context)
+ cr, uid, context.get('active_ids', []),
+ pricelist=form.pricelist_id.id, context=context)
return {
'name': _('Purchase Requisition'),
'view_type': 'form',
=== modified file 'logistic_requisition/wizard/logistic_line_create_requisition_view.xml'
--- logistic_requisition/wizard/logistic_line_create_requisition_view.xml 2013-08-23 13:34:36 +0000
+++ logistic_requisition/wizard/logistic_line_create_requisition_view.xml 2014-03-10 15:39:56 +0000
@@ -6,6 +6,8 @@
<field name="model">logistic.requisition.source.create.requisition</field>
<field name="arch" type="xml">
<form string="Create Requisition" version="7.0">
+ <separator string="Which pricelist / currency you want ?"/>
+ <field name="pricelist_id" domain="[('type','=','purchase')]"/>
<group>
<separator string="Are you sure you want to create a requisition from those lines ?"/>
<label string="
=== added directory 'logistic_requisition_budget'
=== added file 'logistic_requisition_budget/__init__.py'
--- logistic_requisition_budget/__init__.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Romain Deheele
+# Copyright 2014 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more description.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import model
+from . import wizard
=== added file 'logistic_requisition_budget/__openerp__.py'
--- logistic_requisition_budget/__openerp__.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/__openerp__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Joël Grand-Guillaume, Jacques-Etienne Baudoux, Guewen Baconnier
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more description.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+{"name": "Logistic Requisition Budget",
+ "version": "0.1",
+ "author": "Camptocamp",
+ "license": "AGPL-3",
+ "category": "Purchase Management",
+ 'complexity': "normal",
+ "images": [],
+ "website": "http://www.camptocamp.com",
+ "description": """
+This logisitc requisiton budget
+===============================
+
+This module add a notion of budget on logistic requisition.
+Each requisition lines have now a budget holder and a budget Value.
+
+Requisiton must be approves by budget manager.
+
+If budget is exceeded requisiton flow is block unitl adaptation of price
+or budget.
+
+""",
+ "depends": ["logistic_requisition",
+ ],
+ "demo": [],
+ "data": ["view/logistic_requisition.xml",
+ "report/logistic_requisition_report.xml",
+ ],
+ "auto_install": False,
+ "test": ['test/requisition_create_cost_estimate.yml',
+ ],
+ "installable": True,
+ }
=== added directory 'logistic_requisition_budget/i18n'
=== added file 'logistic_requisition_budget/i18n/en_US.po'
--- logistic_requisition_budget/i18n/en_US.po 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/i18n/en_US.po 2014-03-10 15:39:56 +0000
@@ -0,0 +1,227 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * logistic_requisition_budget
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-02-04 09:22+0000\n"
+"PO-Revision-Date: 2014-02-04 09:22+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:149
+msgid "No"
+msgstr "No"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:182
+msgid "General Remarks"
+msgstr "General Remarks"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:136
+msgid "Logistic requisition"
+msgstr "Logistic requisition"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:192
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:206
+msgid "Signature"
+msgstr "Signature"
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,finance_officer_id:0
+msgid "Finance Officer"
+msgstr "Finance Officer"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:102
+msgid "Shipping address:"
+msgstr "Shipping address:"
+
+#. module: logistic_requisition_budget
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition_line
+msgid "Logistic Requisition Line"
+msgstr "Logistic Requisition Line"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:140
+msgid "Preferred transport"
+msgstr "Preferred transport"
+
+#. module: logistic_requisition_budget
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition_cost_estimate
+msgid "Create cost estimate of logistic requisition lines"
+msgstr "Create cost estimate of logistic requisition lines"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:119
+msgid "Consignee address:"
+msgstr "Consignee address:"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:161
+msgid "Description"
+msgstr "Description"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:139
+msgid "Desired delivery date"
+msgstr "Desired delivery date"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:205
+msgid "Validation date"
+msgstr "Validation date"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:201
+msgid "Budget limit:"
+msgstr "Budget limit:"
+
+#. module: logistic_requisition_budget
+#: model:ir.actions.report.xml,name:logistic_requisition_budget.report_logistic_requisition
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition
+msgid "Logistic Requisition"
+msgstr "Logistic Requisition"
+
+#. module: logistic_requisition_budget
+#: code:addons/logistic_requisition_budget/wizard/cost_estimate.py:36
+#, python-format
+msgid "The requisition must be validated by the Budget Holder."
+msgstr "The requisition must be validated by the Budget Holder."
+
+#. module: logistic_requisition_budget
+#: view:logistic.requisition:0
+msgid " validated on "
+msgstr " validated on "
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:141
+msgid "Cost estimate only"
+msgstr "Cost estimate only"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:187
+msgid "Approval"
+msgstr "Approval"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:191
+msgid "Requested date"
+msgstr "Requested date"
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition.line,budget_unit_price:0
+msgid "Budget Unit Price"
+msgstr "Budget Unit Price"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:163
+msgid "UoM"
+msgstr "UoM"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:143
+msgid "Country"
+msgstr "Country"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:200
+msgid "Budget"
+msgstr "Budget"
+
+#. module: logistic_requisition_budget
+#: constraint:logistic.requisition.source:0
+msgid "The total cost cannot be more than the total budget."
+msgstr "The total cost cannot be more than the total budget."
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:142
+msgid "Project"
+msgstr "Project"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:144
+msgid "Requisition Date"
+msgstr "Requisition Date"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:190
+msgid "Requesting entity"
+msgstr "Requesting entity"
+
+#. module: logistic_requisition_budget
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition_source
+msgid "Logistic Requisition Source"
+msgstr "Logistic Requisition Source"
+
+#. module: logistic_requisition_budget
+#: view:logistic.requisition:0
+msgid "Validation"
+msgstr "Validation"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:162
+msgid "Quantity"
+msgstr "Quantity"
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,date_budget_holder:0
+msgid "Budget Holder Validation Date"
+msgstr "Budget Holder Validation Date"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:204
+msgid "Budget/financial holder"
+msgstr "Budget/financial holder"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:178
+msgid "Delivery Remarks"
+msgstr "Delivery Remarks"
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition.line,budget_tot_price:0
+msgid "Budget Total Price"
+msgstr "Budget Total Price"
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,budget_holder_id:0
+msgid "Budget Holder"
+msgstr "Budget Holder"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:160
+msgid "Number"
+msgstr "Number"
+
+#. module: logistic_requisition_budget
+#: view:logistic.requisition:0
+#: field:logistic.requisition,amount_total:0
+#: view:logistic.requisition.line:0
+msgid "Total Budget"
+msgstr "Total Budget"
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,allowed_budget:0
+msgid "Allowed Budget"
+msgstr "Allowed Budget"
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,date_finance_officer:0
+msgid "Finance Officer Validation Date"
+msgstr "Finance Officer Validation Date"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:149
+msgid "Yes"
+msgstr "Yes"
+
=== added file 'logistic_requisition_budget/i18n/logistic_requisition_budget.pot'
--- logistic_requisition_budget/i18n/logistic_requisition_budget.pot 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/i18n/logistic_requisition_budget.pot 2014-03-10 15:39:56 +0000
@@ -0,0 +1,227 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+# * logistic_requisition_budget
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 7.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-02-04 09:22+0000\n"
+"PO-Revision-Date: 2014-02-04 09:22+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:149
+msgid "No"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:182
+msgid "General Remarks"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:136
+msgid "Logistic requisition"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:192
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:206
+msgid "Signature"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,finance_officer_id:0
+msgid "Finance Officer"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:102
+msgid "Shipping address:"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition_line
+msgid "Logistic Requisition Line"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:140
+msgid "Preferred transport"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition_cost_estimate
+msgid "Create cost estimate of logistic requisition lines"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:119
+msgid "Consignee address:"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:161
+msgid "Description"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:139
+msgid "Desired delivery date"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:205
+msgid "Validation date"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:201
+msgid "Budget limit:"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: model:ir.actions.report.xml,name:logistic_requisition_budget.report_logistic_requisition
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition
+msgid "Logistic Requisition"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: code:addons/logistic_requisition_budget/wizard/cost_estimate.py:36
+#, python-format
+msgid "The requisition must be validated by the Budget Holder."
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: view:logistic.requisition:0
+msgid " validated on "
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:141
+msgid "Cost estimate only"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:187
+msgid "Approval"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:191
+msgid "Requested date"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition.line,budget_unit_price:0
+msgid "Budget Unit Price"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:163
+msgid "UoM"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:143
+msgid "Country"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:200
+msgid "Budget"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: constraint:logistic.requisition.source:0
+msgid "The total cost cannot be more than the total budget."
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:142
+msgid "Project"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:144
+msgid "Requisition Date"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:190
+msgid "Requesting entity"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: model:ir.model,name:logistic_requisition_budget.model_logistic_requisition_source
+msgid "Logistic Requisition Source"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: view:logistic.requisition:0
+msgid "Validation"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:162
+msgid "Quantity"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,date_budget_holder:0
+msgid "Budget Holder Validation Date"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:204
+msgid "Budget/financial holder"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:178
+msgid "Delivery Remarks"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition.line,budget_tot_price:0
+msgid "Budget Total Price"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,budget_holder_id:0
+msgid "Budget Holder"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:160
+msgid "Number"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: view:logistic.requisition:0
+#: field:logistic.requisition,amount_total:0
+#: view:logistic.requisition.line:0
+msgid "Total Budget"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,allowed_budget:0
+msgid "Allowed Budget"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: field:logistic.requisition,date_finance_officer:0
+msgid "Finance Officer Validation Date"
+msgstr ""
+
+#. module: logistic_requisition_budget
+#: report:addons/logistic_requisition_budget/report/logistic_requisition.mako:149
+msgid "Yes"
+msgstr ""
+
=== added directory 'logistic_requisition_budget/model'
=== added file 'logistic_requisition_budget/model/__init__.py'
--- logistic_requisition_budget/model/__init__.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/model/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Joël Grand-Guillaume, Jacques-Etienne Baudoux, Guewen Baconnier,
+# Romain Deheele
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more description.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from . import logistic_requisition
=== added file 'logistic_requisition_budget/model/logistic_requisition.py'
--- logistic_requisition_budget/model/logistic_requisition.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/model/logistic_requisition.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Romain Deheele
+# Copyright 2014 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more description.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import logging
+
+from openerp.osv import fields, orm
+import openerp.addons.decimal_precision as dp
+
+_logger = logging.getLogger(__name__)
+
+
+class logistic_requisition(orm.Model):
+ _inherit = "logistic.requisition"
+
+ REQ_STATES = {'confirmed': [('readonly', True)],
+ 'done': [('readonly', True)]
+ }
+
+ _columns = {
+ 'amount_total': fields.function(
+ lambda self, *args, **kwargs: self._get_amount(*args, **kwargs),
+ digits_compute=dp.get_precision('Account'),
+ string='Total Budget',
+ store={
+ 'logistic.requisition': (
+ lambda self, cr, uid, ids, c=None: ids,
+ ['line_ids'], 20),
+ 'logistic.requisition.line': (
+ lambda self, *a, **kw: self._store_get_requisition_ids(*a, **kw),
+ ['requested_qty', 'budget_unit_price', 'budget_tot_price', 'requisition_id'], 20),
+ }),
+ 'allowed_budget': fields.boolean('Allowed Budget'),
+ 'budget_holder_id': fields.many2one(
+ 'res.users',
+ string='Budget Holder'),
+ 'date_budget_holder': fields.datetime(
+ 'Budget Holder Validation Date'),
+ 'finance_officer_id': fields.many2one(
+ 'res.users',
+ string='Finance Officer'),
+ 'date_finance_officer': fields.datetime(
+ 'Finance Officer Validation Date'),
+ }
+
+ def _get_amount(self, cr, uid, ids, name, args, context=None):
+ """Compute the requisiton total budget"""
+ res = {}
+ for requisition in self.browse(cr, uid, ids, context=context):
+ res[requisition.id] = sum(line.budget_tot_price for line
+ in requisition.line_ids)
+ return res
+
+ def _do_draft(self, cr, uid, ids, context=None):
+ """Cancel LR and related budget"""
+ super(logistic_requisition, self)._do_draft(cr, uid, ids,
+ context=context)
+ vals = {'state': 'draft',
+ 'budget_holder_id': False,
+ 'date_budget_holder': False,
+ 'finance_officer_id': False,
+ 'date_finance_officer': False,
+ 'cancel_reason_id': False,
+ }
+ self.write(cr, uid, ids, vals, context=context)
+
+ def copy(self, cr, uid, id, default=None, context=None):
+ if not default:
+ default = {}
+ default.update({
+ 'budget_holder_id': False,
+ 'date_budget_holder': False,
+ 'finance_officer_id': False,
+ 'date_finance_officer': False,
+ })
+ return super(logistic_requisition, self).copy(cr, uid, id, default=default, context=context)
+
+
+class logistic_requisition_line(orm.Model):
+ _inherit = "logistic.requisition.line"
+
+ REQUEST_STATES = {'assigned': [('readonly', True)],
+ 'sourced': [('readonly', True)],
+ 'quoted': [('readonly', True)],
+ }
+ STATES = [('draft', 'Draft'),
+ ('confirmed', 'Confirmed'),
+ ('assigned', 'Assigned'),
+ ('sourced', 'Sourced'),
+ ('quoted', 'Quoted'),
+ ('cancel', 'Cancelled')
+ ]
+
+ _columns = {
+ 'budget_tot_price': fields.float(
+ 'Budget Total Price',
+ states=REQUEST_STATES,
+ digits_compute=dp.get_precision('Account')),
+ 'budget_unit_price': fields.function(
+ lambda self, *args, **kwargs: self._get_unit_amount_line(*args, **kwargs),
+ string='Budget Unit Price',
+ type="float",
+ digits_compute=dp.get_precision('Account'),
+ store=True),
+ }
+
+ def _get_unit_amount_line(self, cr, uid, ids, prop, unknow_none,
+ unknow_dict, context=None):
+ res = {}
+ for line in self.browse(cr, uid, ids, context=context):
+ price = line.budget_tot_price / line.requested_qty
+ res[line.id] = price
+ return res
+
+
+class logistic_requisition_source(orm.Model):
+ _inherit = "logistic.requisition.source"
+
+ _constraints = [
+ (lambda self, *a, **kw: self._check_source_lines_total_amount(*a, **kw),
+ 'The total cost cannot be more than the total budget.',
+ ['proposed_qty', 'unit_cost', 'requisition_line_id']),
+ ]
+
+ def _check_source_lines_total_amount(self, cr, uid, ids, context=None):
+ for source in self.browse(cr, uid, ids, context=context):
+ line = source.requisition_line_id
+ total = sum(source.unit_cost * source.proposed_qty
+ for source in line.source_ids)
+ if total > line.budget_tot_price:
+ return False
+ return True
=== added directory 'logistic_requisition_budget/report'
=== added file 'logistic_requisition_budget/report/logistic_requisition.mako'
--- logistic_requisition_budget/report/logistic_requisition.mako 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/report/logistic_requisition.mako 2014-03-10 15:39:56 +0000
@@ -0,0 +1,226 @@
+<html>
+<head>
+ <style type="text/css">
+ ${css}
+
+.list_sale_table {
+ border:thin solid #E3E4EA;
+ text-align:center;
+ border-collapse: collapse;
+}
+.list_sale_table th {
+ background-color: #EEEEEE;
+ border: thin solid #000000;
+ text-align:center;
+ font-size:12;
+ font-weight:bold;
+ padding-right:3px;
+ padding-left:3px;
+}
+.list_sale_table td {
+ border-top: thin solid #EEEEEE;
+ text-align:left;
+ font-size:12;
+ padding-right:3px;
+ padding-left:3px;
+ padding-top:3px;
+ padding-bottom:3px;
+}
+.list_sale_table thead {
+ display:table-header-group;
+}
+
+td.formatted_note {
+ text-align:left;
+ border-right:thin solid #EEEEEE;
+ border-left:thin solid #EEEEEE;
+ border-top:thin solid #EEEEEE;
+ padding-left:10px;
+ font-size:11;
+}
+
+
+
+.no_bloc {
+ border-top: thin solid #ffffff ;
+}
+
+.right_table {
+ right: 4cm;
+ width:"100%";
+}
+
+.std_text {
+ font-size:12;
+}
+
+tfoot.totals tr:first-child td{
+ padding-top: 15px;
+}
+
+
+td.amount, th.amount {
+ text-align: right;
+ white-space: nowrap;
+}
+
+
+.address .recipient .shipping .invoice {
+ font-size: 12px;
+}
+
+ </style>
+</head>
+<body>
+ <%page expression_filter="entity"/>
+ <%
+ def carriage_returns(text):
+ return text.replace('\n', '<br />')
+
+ %>
+ %for requisition in objects:
+ <% setLang(requisition.partner_id.lang) %>
+
+ <div class="address">
+ <table class="recipient">
+ %if requisition.partner_id.parent_id:
+ <tr><td class="name">${requisition.partner_id.parent_id.name or ''}</td></tr>
+ <tr><td>${requisition.partner_id.title.name if requisition.partner_id.title else ''} ${requisition.partner_id.name }</td></tr>
+ <% address_lines = requisition.partner_id.contact_address.split("\n")[1:] %>
+ %else:
+ <tr><td class="name">${requisition.partner_id.title.name if requisition.partner_id.title else ''} ${requisition.partner_id.name }</td></tr>
+ <% address_lines = requisition.partner_id.contact_address.split("\n") %>
+ %endif
+ %for part in address_lines:
+ %if part:
+ <tr><td>${part}</td></tr>
+ %endif
+ %endfor
+ </table>
+
+ <table class="shipping">
+ <tr><td class="address_title">${_("Shipping address:")}</td></tr>
+ %if requisition.consignee_shipping_id.parent_id:
+ <tr><td>${requisition.consignee_shipping_id.parent_id.name or ''}</td></tr>
+ <tr><td>${requisition.consignee_shipping_id.title.name if requisition.consignee_shipping_id.title else ''} ${requisition.consignee_shipping_id.name }</td></tr>
+ <% address_lines = requisition.consignee_shipping_id.contact_address.split("\n")[1:] %>
+ %else:
+ <tr><td>${requisition.consignee_shipping_id.title and requisition.consignee_shipping_id.title.name or ''} ${requisition.consignee_shipping_id.name }</td></tr>
+ <% address_lines = requisition.consignee_shipping_id.contact_address.split("\n") %>
+ %endif
+ %for part in address_lines:
+ %if part:
+ <tr><td>${part}</td></tr>
+ %endif
+ %endfor
+ </table>
+
+ <table class="invoice">
+ <tr><td class="address_title">${_("Consignee address:")}</td></tr>
+ %if requisition.consignee_id.parent_id:
+ <tr><td>${requisition.consignee_id.parent_id.name or ''}</td></tr>
+ <tr><td>${requisition.consignee_id.title.name if requisition.consignee_id.title else ''} ${requisition.consignee_id.name }</td></tr>
+ <% address_lines = requisition.consignee_id.contact_address.split("\n")[1:] %>
+ %else:
+ <tr><td>${requisition.consignee_id.title.name if requisition.consignee_id.title else ''} ${requisition.consignee_id.name }</td></tr>
+ <% address_lines = requisition.consignee_id.contact_address.split("\n") %>
+ %endif
+ %for part in address_lines:
+ %if part:
+ <tr><td>${part}</td></tr>
+ %endif
+ %endfor
+ </table>
+ </div>
+
+ <h1 style="clear:both;">${_('Logistic requisition')} ${requisition.name}</h1>
+ <table class="basic_table" width="100%">
+ <tr>
+ <td style="font-weight:bold;">${_("Desired delivery date")}</td>
+ <td style="font-weight:bold;">${_("Preferred transport")}</td>
+ <td style="font-weight:bold;">${_("Cost estimate only")}</td>
+ <td style="font-weight:bold;">${_("Project")}</td>
+ <td style="font-weight:bold;">${_("Country")}</td>
+ <td style="font-weight:bold;">${_("Requisition Date")}</td>
+ </tr>
+ <tr>
+ <td>${formatLang(requisition.date_delivery, date=True)}</td>
+ <td>${requisition.preferred_transport.name if requisition.preferred_transport else ''}</td>
+ <td>${_("Yes") if requisition.cost_estimate_only else _("No")}</td>
+ <td>${requisition.analytic_id.name if requisition.analytic_id else ''}</td>
+ <td>${requisition.country_id.name}</td>
+ <td>${formatLang(requisition.date, date=True)}</td>
+ </tr>
+ </table>
+ <br/>
+
+ <table class="list_sale_table" width="100%" style="margin-top: 20px;">
+ <thead>
+ <tr>
+ <th>${_("Number")}</th>
+ <th>${_("Description")}</th>
+ <th class="amount">${_("Quantity")}</th>
+ <th class="amount">${_("UoM")}</th>
+ </tr>
+ </thead>
+ <tbody>
+ %for line in requisition.line_ids:
+ <tr class="line">
+ <td style="text-align:left; " >${line.name}</td>
+ <td style="text-align:left; " >${(line.product_id.code or '') if line.product_id else ''} ${line.product_id.name if line.product_id else ''}</td>
+
+ <td class="amount" width="15%">${formatLang(line.requested_qty)}</td>
+ <td style="text-align:center;">${line.requested_uom_id.category_id.name}</td>
+ %endfor
+ </tbody>
+ </table>
+ %if requisition.shipping_note :
+ <p><b>${_('Delivery Remarks')}</b></p>
+ <p class="std_text">${requisition.note | carriage_returns}</p>
+ %endif
+ %if requisition.note :
+ <p><b>${_("General Remarks")}</b></p>
+ <p class="std_text">${requisition.note | carriage_returns}</p>
+ %endif
+ <br/>
+ <br/>
+ <p><b>${_("Approval")}</b></p>
+ <table class="basic_table" width="100%">
+ <tr>
+ <td style="font-weight:bold;width:40%">${_("Requesting entity")}</td>
+ <td style="font-weight:bold;width:30%">${_("Requested date")}</td>
+ <td style="font-weight:bold;">${_("Signature")}</td>
+ </tr>
+ <tr>
+ <td>${requisition.partner_id.name}</td>
+ <td>${requisition.date or 'N/A'}</td>
+ <td> </td>
+ </tr>
+ </table>
+ <p><b>${_("Budget")}</b></p>
+ <p>${_("Budget limit:")} ${formatLang(requisition.allowed_budget)}</p>
+ <table class="basic_table" width="100%">
+ <tr>
+ <td style="font-weight:bold;width:40%">${_("Budget/financial holder")}</td>
+ <td style="font-weight:bold;width:30%">${_("Validation date")}</td>
+ <td style="font-weight:bold;">${_("Signature")}</td>
+ </tr>
+ %if requisition.budget_holder_id:
+ <tr>
+ <td>${requisition.budget_holder_id.name}</td>
+ <td>${requisition.date_budget_holder if requisition.date_budget_holder else 'N/A'}</td>
+ <td> </td>
+ </tr>
+ %endif
+ %if requisition.finance_officer_id:
+ <tr>
+ <td>${requisition.finance_officer_id.name}</td>
+ <td>${requisition.date_finance_officer if requisition.date_finance_officer else 'N/A'}</td>
+ <td> </td>
+ </tr>
+ %endif
+ </table>
+ <p style="page-break-after: always"/>
+ %endfor
+</body>
+</html>
=== added file 'logistic_requisition_budget/report/logistic_requisition_report.xml'
--- logistic_requisition_budget/report/logistic_requisition_report.xml 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/report/logistic_requisition_report.xml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <report id="report_logistic_requisition"
+ auto="False"
+ name="logistic.requisition"
+ model="logistic.requisition"
+ file="logistic_requisition_with_budget/report/logistic_requisition.mako"
+ webkit_header="base_headers_webkit.base_reports_portrait_header"
+ report_type="webkit"
+ string="Logistic Requisition"/>
+ </data>
+</openerp>
=== added directory 'logistic_requisition_budget/test'
=== added file 'logistic_requisition_budget/test/requisition_create_cost_estimate.yml'
--- logistic_requisition_budget/test/requisition_create_cost_estimate.yml 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/test/requisition_create_cost_estimate.yml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,197 @@
+-
+ Given I create a logistic requisition
+-
+ !record {model: logistic.requisition, id: logistic_requisition_cost_estimate_01}:
+ partner_id: base.res_partner_4
+ consignee_id: base.res_partner_3
+ date_delivery: !eval "time.strftime('%Y-%m-%d')"
+ user_id: base.user_demo
+ incoterm_id: stock.incoterm_FCA
+ incoterm_address: incoterm address as text
+ analytic_id: account.analytic_consultancy
+ pricelist_id: product.list0
+-
+ And I add a line 1
+-
+ !record {model: logistic.requisition.line, id: logistic_requisition_line_cost_estimate_01}:
+ requisition_id: logistic_requisition_cost_estimate_01
+ product_id: product.product_product_7
+ requested_qty: 100
+ requested_uom_id: product.product_uom_unit
+ date_delivery: !eval "time.strftime('%Y-%m-%d')"
+ budget_tot_price: 1000
+ logistic_user_id: base.user_demo
+-
+ And I add a source line to the line 1
+-
+ !record {model: logistic.requisition.source, id: logistic_requisition_source_cost_estimate_01}:
+ requisition_line_id: logistic_requisition_line_cost_estimate_01
+ proposed_qty: 110
+ proposed_product_id: product.product_product_7
+ proposed_uom_id: product.product_uom_unit
+ unit_cost: 8
+ transport_applicable: 0
+ procurement_method: wh_dispatch
+ price_is: fixed
+ dispatch_location_id: stock.stock_location_shop1
+-
+ And I add a line 2
+-
+ !record {model: logistic.requisition.line, id: logistic_requisition_line_cost_estimate_02}:
+ requisition_id: logistic_requisition_cost_estimate_01
+ product_id: product.product_product_8
+ requested_qty: 100
+ requested_uom_id: product.product_uom_unit
+ date_delivery: !eval "time.strftime('%Y-%m-%d')"
+ budget_tot_price: 1000
+ logistic_user_id: base.user_demo
+-
+ And I add a source line to the line 2
+-
+ !record {model: logistic.requisition.source, id: logistic_requisition_source_cost_estimate_02}:
+ requisition_line_id: logistic_requisition_line_cost_estimate_02
+ proposed_qty: 90
+ proposed_product_id: product.product_product_8
+ proposed_uom_id: product.product_uom_unit
+ unit_cost: 8
+ transport_applicable: 0
+ procurement_method: wh_dispatch
+ price_is: estimated
+ dispatch_location_id: stock.stock_location_shop1
+-
+ When I confirm the logistic requisition
+-
+ !python {model: logistic.requisition}: |
+ req = self.browse(cr, uid, ref("logistic_requisition_cost_estimate_01"))
+ req.button_confirm()
+-
+ And I source the lines
+-
+ !python {model: logistic.requisition.line}: |
+ line1_id = ref("logistic_requisition_line_cost_estimate_01")
+ line2_id = ref("logistic_requisition_line_cost_estimate_02")
+ ids = [line1_id, line2_id]
+ # The initial yaml does not set it to false, probably
+ # a bug related to the application of the default values
+ # However, we need to not have this flag to source the lines
+ self.write(cr, uid, ids, {'transport_applicable': False})
+ self.button_sourced(cr, uid, ids)
+-
+ I open the wizard to create a cost estimate from the logistic requisition,
+ but it fails because the business rules are not satisfied.
+-
+ !python {model: logistic.requisition.cost.estimate}: |
+ from openerp.osv import orm
+ context = {'active_model': 'logistic.requisition',
+ 'active_ids': [ref('logistic_requisition_cost_estimate_01')],
+ }
+ wizard_id = self.create(cr, uid, {}, context=context)
+ try:
+ self.cost_estimate(cr, uid, wizard_id)
+ except orm.except_orm as err:
+ assert 'NO_BUDGET_VALID' in err.error_codes, (
+ 'Expected an error because the budget holder is missing')
+ assert 'NO_ACCOUNT_CODE' in err.error_codes, (
+ 'Expected an error because the account code is missing')
+ else:
+ raise AssertionError('Expected an orm.except_orm error, got nothing')
+-
+ I set some data to fulfill the business rules
+-
+ !record {model: logistic.requisition, id: logistic_requisition_cost_estimate_01}:
+ budget_holder_id: base.user_demo
+-
+ !record {model: logistic.requisition.line, id: logistic_requisition_line_cost_estimate_01}:
+ account_code: 'XXXX'
+-
+ !record {model: logistic.requisition.line, id: logistic_requisition_line_cost_estimate_02}:
+ account_code: 'YYYY'
+-
+ I open the wizard to create a cost estimate from the logistic requisition
+-
+ !python {model: logistic.requisition.cost.estimate}: |
+ context = {'active_model': 'logistic.requisition',
+ 'active_ids': [ref('logistic_requisition_cost_estimate_01')],
+ }
+ wizard_id = self.create(cr, uid, {}, context=context)
+ open_action = self.cost_estimate(cr, uid, wizard_id)
+ cost_estimate_id = open_action['res_id']
+ cost_estimate_obj = self.pool.get('sale.order')
+ cost_estimate = cost_estimate_obj.browse(cr, uid, cost_estimate_id)
+ assert len(cost_estimate.order_line) == 2, (
+ "Cost Estimate should have 2 lines, 1 per logistic requisition line")
+-
+ I check if the information of the logistic requisition are propagated correctly
+-
+ !python {model: logistic.requisition}: |
+ req = self.browse(cr, uid, ref("logistic_requisition_cost_estimate_01"))
+ sale_obj = self.pool.get('sale.order')
+ sale_ids = sale_obj.search(cr, uid, [('requisition_id', '=', req.id)])
+ assert len(sale_ids) == 1
+ sale = sale_obj.browse(cr, uid, sale_ids[0])
+ assert sale.partner_id == req.partner_id
+ assert sale.partner_invoice_id == req.partner_id
+ assert sale.consignee_id == req.consignee_id
+ assert sale.incoterm == req.incoterm_id
+ assert sale.incoterm_address == req.incoterm_address
+ assert sale.project_id == req.analytic_id
+-
+ I check if the information of the lines is correct
+-
+ !python {model: logistic.requisition.source}: |
+ def check_line(self, cr, uid, line_id):
+ # the upper scope is not accessible here,
+ # that's why self, cr, uid are passed as arguments
+ sale_line_obj = self.pool.get('sale.order.line')
+ sale_line_ids = sale_line_obj.search(
+ cr, uid, [('logistic_requisition_source_id', '=', line_id)])
+ assert len(sale_line_ids) == 1
+ sale_line = sale_line_obj.browse(cr, uid, sale_line_ids[0])
+ source = self.browse(cr, uid, line_id)
+ line = source.requisition_line_id
+ assert sale_line.product_id == source.proposed_product_id, (
+ "product_id should be %s, received %s" %
+ (source.proposed_product_id, sale_line.product_id))
+
+ assert sale_line.name == line.description, (
+ "name should be %s, received %s" %
+ (line.description, sale_line.name))
+
+ assert sale_line.product_uom_qty == source.proposed_qty, (
+ "qty should be %s, received %s" %
+ (source.proposed_qty, sale_line.product_uom_qty))
+
+ assert sale_line.product_uom == source.proposed_uom_id, (
+ "unit of measure should be %s, received %s" %
+ (source.proposed_uom_id, sale_line.product_uom))
+
+ assert sale_line.price_unit == source.unit_cost, (
+ "price_unit should be %s, received %s" %
+ (source.unit_cost, sale_line.price_unit))
+
+ assert sale_line.price_is == source.price_is, (
+ "price_is should be %s, received %s" %
+ (source.price_is, sale_line.price_is))
+ assert_type = ('make_to_stock'
+ if source.procurement_method == 'wh_dispatch'
+ else 'make_to_order')
+ assert sale_line.type == assert_type, (
+ "Type should be %s, received: %s" % (assert_type, sale_line.type))
+
+ if source.procurement_method != 'wh_dispatch':
+ assert sale_line.sale_flow == 'direct_delivery', (
+ "A line not in 'Warehouse dispatch' should be in direct delivery")
+
+ if source.dispatch_location_id:
+ assert sale_line.location_id == source.dispatch_location_id, (
+ "dispatch should be %s, received %s" %
+ (source.dispatch_location_id, sale_line.location_id))
+ else:
+ assert not sale_line.location_id, (
+ "No dispatch location on requisition line, but received a "
+ "location in sale order line")
+
+ source1_id = ref("logistic_requisition_source_cost_estimate_01")
+ source2_id = ref("logistic_requisition_source_cost_estimate_02")
+ check_line(self, cr, uid, source1_id)
+ check_line(self, cr, uid, source2_id)
=== added directory 'logistic_requisition_budget/tests'
=== added file 'logistic_requisition_budget/tests/__init__.py'
--- logistic_requisition_budget/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/tests/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Romain Deheele
+# Copyright 2014 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import test_purchase_split_requisition
+
+
+fast_suite = [
+]
+
+checks = [
+ test_purchase_split_requisition,
+]
=== added file 'logistic_requisition_budget/tests/test_purchase_split_requisition.py'
--- logistic_requisition_budget/tests/test_purchase_split_requisition.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/tests/test_purchase_split_requisition.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Guewen Baconnier
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import time
+import unittest2
+from functools import partial
+
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as D_FMT
+from openerp.osv import orm
+import openerp.tests.common as common
+from openerp import SUPERUSER_ID
+from openerp.addons.logistic_requisition.tests import logistic_requisition
+from openerp.addons.logistic_requisition.tests import purchase_requisition
+from openerp.addons.logistic_requisition.tests import purchase_order
+
+
+class test_purchase_split_requisition(common.TransactionCase):
+ """ Test the split of the logistics requisition lines
+ according to the purchase order lines choosed during the
+ purchase requisition process.
+
+ A bit of context:
+
+ We create a logistics requisition with several lines.
+ For each line, we create a purchase requisition.
+ During the purchase requisition process, we'll send requests
+ for quotations to several suppliers. Once the call is closed,
+ we'll have a few RfQ with different amounts. We'll choose
+ which lines we want to confirm.
+ A purchase order will be generated with the final choice, at this
+ point, for 1 logistics requisition line, we may have several
+ purchase order line. We have to split the logistics requisition
+ lines in order to have the same quantities than the purchase lines.
+
+ The purpose of the tests here is to check if the split is done
+ correctly.
+ """
+
+ def setUp(self):
+ super(test_purchase_split_requisition, self).setUp()
+ cr, uid = self.cr, self.uid
+ self.ir_model_data = self.registry('ir.model.data')
+ self.log_req = self.registry('logistic.requisition')
+ self.log_req_line = self.registry('logistic.requisition.line')
+ self.purchase_order = self.registry('purchase.order')
+ self.get_ref = partial(self.ir_model_data.get_object_reference,
+ self.cr, self.uid)
+
+ __, self.partner_1 = self.get_ref('base', 'res_partner_1')
+ __, self.partner_3 = self.get_ref('base', 'res_partner_3')
+ __, self.partner_4 = self.get_ref('base', 'res_partner_4')
+ __, self.partner_12 = self.get_ref('base', 'res_partner_12')
+ __, self.user_demo = self.get_ref('base', 'user_demo')
+ __, self.product_7 = self.get_ref('product', 'product_product_7')
+ __, self.product_uom_pce = self.get_ref('product', 'product_uom_unit')
+ __, self.pricelist_sale = self.get_ref('product','list0')
+
+ vals = {
+ 'partner_id': self.partner_4,
+ 'consignee_id': self.partner_3,
+ 'date_delivery': time.strftime(D_FMT),
+ 'user_id': self.user_demo,
+ 'pricelist_id': self.pricelist_sale,
+ }
+ line = {
+ 'product_id': self.product_7,
+ 'requested_qty': 100,
+ 'requested_uom_id': self.product_uom_pce,
+ 'date_delivery': time.strftime(D_FMT),
+ }
+ source = {
+ 'proposed_qty': 100,
+ 'proposed_product_id': self.product_7,
+ 'proposed_uom_id': self.product_uom_pce,
+ 'transport_applicable': 0,
+ 'procurement_method': 'procurement',
+ 'price_is': 'estimated',
+ }
+ self.requisition_id = logistic_requisition.create(self, vals)
+ self.line_id = logistic_requisition.add_line(self, self.requisition_id,
+ line)
+ self.source_id = logistic_requisition.add_source(self, self.line_id,
+ source)
+ logistic_requisition.confirm(self, self.requisition_id)
+ logistic_requisition.assign_lines(self, [self.line_id], self.user_demo)
+ purch_req_id = logistic_requisition.create_purchase_requisition(
+ self, self.source_id)
+ purchase_requisition.confirm_call(self, purch_req_id)
+ purch_req_model = self.registry('purchase.requisition')
+ self.purchase_requisition = purch_req_model.browse(
+ cr, uid, purch_req_id)
+ dp_obj = self.registry('decimal.precision')
+ self.uom_precision = dp_obj.precision_get(cr, SUPERUSER_ID,
+ 'Product Unit of Measure')
+
+ def test_split_too_many_products_selected_budget_exceeded(self):
+ """ Create a call for bids from the logistic requisition, 2 po line choosed (budget exceeded)
+
+ 30 items in a first purchase order and 80 items in a second one,
+ for a total of 110 items. That means 110 products have been ordered
+ but 100 only have been ordered at the origin.
+
+ The total cost is greater than the requested budget.
+ We should not be able to propose more than requested financially.
+ """
+ # create a first draft bid and select a part of the line
+ purchase1, bid_line1 = purchase_requisition.create_draft_purchase_order(
+ self, self.purchase_requisition.id, self.partner_1)
+ bid_line1.write({'price_unit': 15})
+ purchase_order.select_line(self, bid_line1.id, 30)
+ purchase_order.bid_encoded(self, purchase1.id)
+
+ # create a second draft bid and select a part of the line
+ purchase2, bid_line2 = purchase_requisition.create_draft_purchase_order(
+ self, self.purchase_requisition.id, self.partner_12)
+ bid_line2.write({'price_unit': 13})
+ purchase_order.select_line(self, bid_line2.id, 80)
+ purchase_order.bid_encoded(self, purchase2.id)
+
+ # close the call for bids
+ purchase_requisition.close_call(self, self.purchase_requisition.id)
+ # selection of bids will trigger the split of lines
+ # the generation of po fails because the budget is exceeded
+ with self.assertRaises(orm.except_orm):
+ purchase_requisition.bids_selected(self,
+ self.purchase_requisition.id)
=== added directory 'logistic_requisition_budget/view'
=== added file 'logistic_requisition_budget/view/logistic_requisition.xml'
--- logistic_requisition_budget/view/logistic_requisition.xml 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/view/logistic_requisition.xml 2014-03-10 15:39:56 +0000
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record model="ir.ui.view" id="view_logistic_requisition_budget_form">
+ <field name="name">logistic.requisition.budget.form</field>
+ <field name="model">logistic.requisition</field>
+ <field name="inherit_id" ref="logistic_requisition.view_logistic_requisition_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//page[@string='Requisition Lines']/field[@name='line_ids']/tree/field[@name='date_delivery']" position="after">
+ <field name="budget_unit_price"/>
+ <field name="budget_tot_price"/>
+ </xpath>
+ <xpath expr="//page[@string='Requisition Lines']/field[@name='line_ids']" position="after">
+ <group class="oe_subtotal_footer oe_right">
+ <field name="amount_total"
+ class="oe_subtotal_footer_separator"
+ widget="monetary"
+ options="{'currency_field': 'currency_id'}"/>
+ </group>
+ <div class="oe_clear"/>
+ <group string="Validation">
+ <div colspan="4">
+ <label for="budget_holder_id"/>
+ <field name="budget_holder_id"
+ on_change="onchange_validate(budget_holder_id, date_budget_holder, 'date_budget_holder')"
+ class="oe_inline"/>
+ <label string=" validated on "
+ attrs="{'invisible': [('budget_holder_id', '=', False)]}" />
+ <field name="date_budget_holder"
+ attrs="{'invisible': [('budget_holder_id', '=', False)]}"
+ class="oe_inline"/>
+ </div>
+ <div colspan="4">
+ <label for="finance_officer_id"/>
+ <field name="finance_officer_id"
+ on_change="onchange_validate(finance_officer_id, date_finance_officer, 'date_finance_officer')"
+ class="oe_inline"/>
+ <label string=" validated on "
+ attrs="{'invisible': [('finance_officer_id', '=', False)]}"/>
+ <field name="date_finance_officer"
+ attrs="{'invisible': [('finance_officer_id', '=', False)]}"
+ class="oe_inline"/>
+ </div>
+ </group>
+ </xpath>
+ </field>
+ </record>
+
+
+ <record model="ir.ui.view" id="view_logistic_requisition_budget_tree">
+ <field name="name">logistic.requisition.budget.tree</field>
+ <field name="model">logistic.requisition</field>
+ <field name="inherit_id" ref="logistic_requisition.view_logistic_requisition_tree"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='analytic_id']" position="after">
+ <field name="amount_total" sum="Total Budget"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_logistic_requisition_line_budget_tree">
+ <field name="name">logistic.requisition.line.budget.tree</field>
+ <field name="model">logistic.requisition.line</field>
+ <field name="inherit_id" ref="logistic_requisition.view_logistic_requisition_line_tree"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='logistic_user_id']" position="after">
+ <field name="budget_tot_price" sum="Total Budget"/>
+ </xpath>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_logistic_requisition_line_budget_form">
+ <field name="name">logistic.requisition.line.budget.form</field>
+ <field name="model">logistic.requisition.line</field>
+ <field name="inherit_id" ref="logistic_requisition.view_logistic_requisition_line_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//form[@string='Logistic Requisition Line']/sheet/group/group/field[@name='currency_id']" position="after">
+ <field name="budget_unit_price" widget="monetary"
+ options="{'currency_field': 'currency_id'}"/>
+ <field name="budget_tot_price" widget="monetary"
+ options="{'currency_field': 'currency_id'}"/>
+ </xpath>
+ </field>
+ </record>
+
+
+ </data>
+</openerp>
=== added directory 'logistic_requisition_budget/wizard'
=== added file 'logistic_requisition_budget/wizard/__init__.py'
--- logistic_requisition_budget/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/wizard/__init__.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Joël Grand-Guillaume
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more description.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from . import cost_estimate
=== added file 'logistic_requisition_budget/wizard/cost_estimate.py'
--- logistic_requisition_budget/wizard/cost_estimate.py 1970-01-01 00:00:00 +0000
+++ logistic_requisition_budget/wizard/cost_estimate.py 2014-03-10 15:39:56 +0000
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Joël Grand-Guillaume
+# Copyright 2013 Camptocamp SA
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more description.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+from openerp.osv import fields, orm
+from openerp.tools.translate import _
+
+
+class logistic_requisition_cost_estimate(orm.TransientModel):
+ _inherit = 'logistic.requisition.cost.estimate'
+
+ def _check_requisition(self, cr, uid, requisition, context=None):
+ """ Check the rules to create a cost estimate from the
+ requisition
+
+ :returns: list of tuples ('message, 'error_code')
+ """
+ errors = []
+ if not requisition.budget_holder_id:
+ error = (_('The requisition must be validated '
+ 'by the Budget Holder.'),
+ 'NO_BUDGET_VALID')
+ errors.append(error)
+ return errors
Follow ups