diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml
index b9c3c3ab95d..311cb4603ae 100644
--- a/addons/purchase/edi/purchase_order_action_data.xml
+++ b/addons/purchase/edi/purchase_order_action_data.xml
@@ -18,7 +18,7 @@
- Purchase Order - Send by mail
+ RFQ - Send by Email${object.validator.email or ''}${object.company_id.name} Order (Ref ${object.name or 'n/a' })${object.partner_id.id}
@@ -34,6 +34,80 @@
Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}:
+
+ REFERENCES
+ RFQ number: ${object.name}
+ RFQ date: ${object.date_order}
+ % if object.origin:
+ RFQ reference: ${object.origin}
+ % endif
+ % if object.partner_ref:
+ Your reference: ${object.partner_ref}
+ % endif
+ % if object.validator:
+ Your contact: ${object.validator.name}
+ % endif
+
+
+
+
If you have any question, do not hesitate to contact us.
+
Thank you!
+
+
+
+
+ ${object.company_id.name}
+
+
+
+ % if object.company_id.street:
+ ${object.company_id.street}
+ % endif
+ % if object.company_id.street2:
+ ${object.company_id.street2}
+ % endif
+ % if object.company_id.city or object.company_id.zip:
+ ${object.company_id.zip} ${object.company_id.city}
+ % endif
+ % if object.company_id.country_id:
+ ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif
+
+ % if object.company_id.phone:
+
+
+ ]]>
+
+
+
+
+
+ Purchase Order - Send by Email
+ ${object.validator.email or ''}
+ ${object.company_id.name} Order (Ref ${object.name or 'n/a' })
+ ${object.partner_id.id}
+
+
+
+ PO_${(object.name or '').replace('/','_')}
+ ${object.partner_id.lang}
+
+
+
Hello ${object.partner_id.name},
+
+
Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}:
+
REFERENCES
Order number: ${object.name}
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index 01643ee44c6..d13b2cbf21a 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -153,8 +153,9 @@ class purchase_order(osv.osv):
STATE_SELECTION = [
('draft', 'Draft PO'),
('sent', 'RFQ Sent'),
+ ('bid', 'Bid Received'),
('confirmed', 'Waiting Approval'),
- ('approved', 'Purchase Order'),
+ ('approved', 'Purchase Confirmed'),
('except_picking', 'Shipping Exception'),
('except_invoice', 'Invoice Exception'),
('done', 'Done'),
@@ -173,7 +174,7 @@ class purchase_order(osv.osv):
help="Reference of the document that generated this purchase order request; a sales order or an internal procurement request."
),
'partner_ref': fields.char('Supplier Reference', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, size=64,
- help="Reference of the sales order or quotation sent by your supplier. It's mainly used to do the matching when you receive the products as this reference is usually written on the delivery order sent by your supplier."),
+ help="Reference of the sales order or bid sent by your supplier. It's mainly used to do the matching when you receive the products as this reference is usually written on the delivery order sent by your supplier."),
'date_order':fields.date('Order Date', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, select=True, help="Date on which this document has been created."),
'date_approve':fields.date('Date Approved', readonly=1, select=True, help="Date on which purchase order has been approved"),
'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]},
@@ -187,7 +188,7 @@ class purchase_order(osv.osv):
'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ),
'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."),
'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}),
- 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The status of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' status. Then the order has to be confirmed by the user, the status switch to 'Confirmed'. Then the supplier must confirm the order to change the status to 'Approved'. When the purchase order is paid and received, the status becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the status becomes in exception.", select=True),
+ 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The status of the purchase order or the quotation request. A request for quotation is a purchase order in a 'Draft' status. Then the order has to be confirmed by the user, the status switch to 'Confirmed'. Then the supplier must confirm the order to change the status to 'Approved'. When the purchase order is paid and received, the status becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the status becomes in exception.", select=True),
'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order Lines', states={'approved':[('readonly',True)],'done':[('readonly',True)]}),
'validator' : fields.many2one('res.users', 'Validated by', readonly=True),
'notes': fields.text('Terms and Conditions'),
@@ -222,10 +223,13 @@ class purchase_order(osv.osv):
}, multi="sums",help="The total amount"),
'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position'),
'payment_term_id': fields.many2one('account.payment.term', 'Payment Term'),
+ 'incoterm_id': fields.many2one('stock.incoterms', 'Incoterm', help="International Commercial Terms are a series of predefined commercial terms used in international transactions."),
'product_id': fields.related('order_line','product_id', type='many2one', relation='product.product', string='Product'),
'create_uid': fields.many2one('res.users', 'Responsible'),
'company_id': fields.many2one('res.company','Company',required=True,select=1, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}),
'journal_id': fields.many2one('account.journal', 'Journal'),
+ 'bid_date': fields.date('Bid Received On', readonly=True, help="Date on which the bid was received"),
+ 'bid_validity': fields.date('Bid Valid Until', help="Date on which the bid expired"),
}
_defaults = {
'date_order': fields.date.context_today,
@@ -271,6 +275,13 @@ class purchase_order(osv.osv):
return super(purchase_order, self).unlink(cr, uid, unlink_ids, context=context)
+ def set_order_line_status(self, cr, uid, ids, status, context=None):
+ line = self.pool.get('purchase.order.line')
+ for order in self.browse(cr, uid, ids, context=context):
+ order_line_ids = [order_line.id for order_line in order.order_line]
+ line.write(cr, uid, order_line_ids, {'state': status}, context=context)
+ return True
+
def button_dummy(self, cr, uid, ids, context=None):
return True
@@ -398,6 +409,9 @@ class purchase_order(osv.osv):
self.write(cr, uid, ids, {'state': 'approved', 'date_approve': fields.date.context_today(self,cr,uid,context=context)})
return True
+ def wkf_bid_received(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state':'bid', 'bid_date': fields.date.context_today(self,cr,uid,context=context)})
+
def print_confirm(self,cr,uid,ids,context=None):
print "Confirmed"
@@ -411,9 +425,14 @@ class purchase_order(osv.osv):
'''
This function opens a window to compose an email, with the edi purchase template message loaded by default
'''
+ if not context:
+ context= {}
ir_model_data = self.pool.get('ir.model.data')
try:
- template_id = ir_model_data.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1]
+ if context.get('send_rfq', False):
+ template_id = ir_model_data.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase')[1]
+ else:
+ template_id = ir_model_data.get_object_reference(cr, uid, 'purchase', 'email_template_edi_purchase_done')[1]
except ValueError:
template_id = False
try:
@@ -506,12 +525,17 @@ class purchase_order(osv.osv):
if not len(ids):
return False
self.write(cr, uid, ids, {'state':'draft','shipped':0})
+ self.set_order_line_status(cr, uid, ids, 'draft', context=context)
for p_id in ids:
# Deleting the existing instance of workflow for PO
self.delete_workflow(cr, uid, [p_id]) # TODO is it necessary to interleave the calls?
self.create_workflow(cr, uid, [p_id])
return True
+ def wkf_po_done(self, cr, uid, ids, context=None):
+ self.write(cr, uid, ids, {'state': 'done'}, context=context)
+ self.set_order_line_status(cr, uid, ids, 'done', context=context)
+
def action_invoice_create(self, cr, uid, ids, context=None):
"""Generates invoice for given ids of purchase orders and links that invoice ID to purchase order.
:param ids: list of ids of purchase orders.
@@ -577,6 +601,10 @@ class purchase_order(osv.osv):
return True
return False
+ def wkf_action_cancel(self, cr, uid, ids, context=None):
+ self.write(cr,uid,ids,{'state':'cancel'})
+ self.set_order_line_status(cr, uid, ids, 'cancel', context=context)
+
def action_cancel(self, cr, uid, ids, context=None):
for purchase in self.browse(cr, uid, ids, context=context):
for pick in purchase.picking_ids:
@@ -594,7 +622,7 @@ class purchase_order(osv.osv):
self.pool.get('account.invoice') \
.signal_invoice_cancel(cr, uid, map(attrgetter('id'), purchase.invoice_ids))
self.write(cr,uid,ids,{'state':'cancel'})
-
+ self.set_order_line_status(cr, uid, ids, 'cancel', context=context)
self.signal_purchase_cancel(cr, uid, ids)
return True
@@ -755,6 +783,7 @@ class purchase_order(osv.osv):
'invoiced':False,
'invoice_ids': [],
'picking_ids': [],
+ 'origin' : '',
'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'),
})
return super(purchase_order, self).copy(cr, uid, id, default, context)
@@ -909,7 +938,6 @@ class purchase_order_line(osv.osv):
'invoiced': fields.boolean('Invoiced', readonly=True),
'partner_id': fields.related('order_id','partner_id',string='Partner',readonly=True,type="many2one", relation="res.partner", store=True),
'date_order': fields.related('order_id','date_order',string='Order Date',readonly=True,type="date"),
-
}
_defaults = {
'product_qty': lambda *a: 1.0,
@@ -928,7 +956,7 @@ class purchase_order_line(osv.osv):
def onchange_product_uom(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
- name=False, price_unit=False, context=None):
+ name=False, price_unit=False, state='draft', context=None):
"""
onchange handler of product_uom.
"""
@@ -936,7 +964,7 @@ class purchase_order_line(osv.osv):
return {'value': {'price_unit': price_unit or 0.0, 'name': name or '', 'product_uom' : uom_id or False}}
return self.onchange_product_id(cr, uid, ids, pricelist_id, product_id, qty, uom_id,
partner_id, date_order=date_order, fiscal_position_id=fiscal_position_id, date_planned=date_planned,
- name=name, price_unit=price_unit, context=context)
+ name=name, price_unit=price_unit, state=state, context=context)
def _get_date_planned(self, cr, uid, supplier_info, date_order_str, context=None):
"""Return the datetime value to use as Schedule Date (``date_planned``) for
@@ -961,7 +989,7 @@ class purchase_order_line(osv.osv):
def onchange_product_id(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
- name=False, price_unit=False, context=None):
+ name=False, price_unit=False, state='draft', context=None):
"""
onchange handler of product_id.
"""
@@ -1035,12 +1063,14 @@ class purchase_order_line(osv.osv):
if qty:
res['value'].update({'product_qty': qty})
- # - determine price_unit and taxes_id
- if pricelist_id:
- price = product_pricelist.price_get(cr, uid, [pricelist_id],
- product.id, qty or 1.0, partner_id or False, {'uom': uom_id, 'date': date_order})[pricelist_id]
- else:
- price = product.standard_price
+ price = price_unit
+ if state not in ('sent','bid'):
+ # - determine price_unit and taxes_id
+ if pricelist_id:
+ price = product_pricelist.price_get(cr, uid, [pricelist_id],
+ product.id, qty or 1.0, partner_id or False, {'uom': uom_id, 'date': date_order})[pricelist_id]
+ else:
+ price = product.standard_price
taxes = account_tax.browse(cr, uid, map(lambda x: x.id, product.supplier_taxes_id))
fpos = fiscal_position_id and account_fiscal_position.browse(cr, uid, fiscal_position_id, context=context) or False
@@ -1050,7 +1080,7 @@ class purchase_order_line(osv.osv):
return res
product_id_change = onchange_product_id
- product_uom_change = onchange_product_uom
+ product_uom_change = onchange_product_uom
def action_confirm(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'confirmed'}, context=context)
@@ -1128,6 +1158,7 @@ class procurement_order(osv.osv):
# res = res.values()
# return len(res) and res[0] or 0 #TO CHECK: why workflow is generated error if return not integer value
+
def create_procurement_purchase_order(self, cr, uid, procurement, po_vals, line_vals, context=None):
"""Create the purchase order from the procurement, using
the provided field values, after adding the given purchase
@@ -1188,8 +1219,8 @@ class procurement_order(osv.osv):
warehouse_obj = self.pool.get('stock.warehouse')
for procurement in self.browse(cr, uid, ids, context=context):
res_id = procurement.move_id.id
- #TODO: so if the seller does not exist, it will just crash...
partner = procurement.product_id.seller_id # Taken Main Supplier of Product of Procurement.
+ assert partner, ('There is no supplier associated to product %s', (procurements.product_id.name))
seller_qty = procurement.product_id.seller_qty
partner_id = partner.id
address_id = partner_obj.address_get(cr, uid, [partner_id], ['delivery'])['delivery']
@@ -1258,7 +1289,9 @@ class mail_mail(osv.Model):
def _postprocess_sent_message(self, cr, uid, mail, context=None):
if mail.model == 'purchase.order':
- self.pool.get('purchase.order').signal_send_rfq(cr, uid, [mail.res_id])
+ obj = self.pool.get('purchase.order').browse(cr, uid, mail.res_id, context=context)
+ if obj.state == 'draft':
+ self.pool.get('purchase.order').signal_send_rfq(cr, uid, [mail.res_id])
return super(mail_mail, self)._postprocess_sent_message(cr, uid, mail=mail, context=context)
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index 6fc61724e2a..24317b9fa9c 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -159,28 +159,29 @@
+
tree
diff --git a/addons/purchase/purchase_workflow.xml b/addons/purchase/purchase_workflow.xml
index f2459ad12a1..294dad28d92 100644
--- a/addons/purchase/purchase_workflow.xml
+++ b/addons/purchase/purchase_workflow.xml
@@ -19,6 +19,12 @@
functionwrite({'state':'sent'})
+
+
+ bid
+ function
+ wkf_bid_received()
+ confirmed
@@ -31,7 +37,7 @@
cancelfunctionTrue
- write({'state':'cancel'})
+ wkf_action_cancel()
@@ -88,7 +94,7 @@
done
- write({'state':'done'})
+ wkf_po_done()functionTrueAND
@@ -104,16 +110,26 @@
send_rfq
-
-
+
+ purchase_confirm
+
+
+
+ bid_received
+ purchase_cancel
+
+
+
+ purchase_cancel
+
@@ -221,7 +237,7 @@
- check_buy() and check_supplier_info()
+ check_buy() and check_supplier_info() and not check_product_requisition()
diff --git a/addons/purchase/res_config.py b/addons/purchase/res_config.py
index ffbbbfd974e..95f2454f3f0 100644
--- a/addons/purchase/res_config.py
+++ b/addons/purchase/res_config.py
@@ -49,10 +49,14 @@ Example: Product: this product is deprecated, do not purchase more than 5.
'module_purchase_double_validation': fields.boolean("Force two levels of approvals",
help="""Provide a double validation mechanism for purchases exceeding minimum amount.
This installs the module purchase_double_validation."""),
- 'module_purchase_requisition': fields.boolean("Manage purchase requisitions",
- help="""Purchase Requisitions are used when you want to request quotations from several suppliers for a given set of products.
+ 'module_purchase_requisition': fields.boolean("Manage calls for bids",
+ help="""Calls for bids are used when you want to generate requests for quotations to several suppliers for a given set of products.
You can configure per product if you directly do a Request for Quotation
- to one supplier or if you want a purchase requisition to negotiate with several suppliers."""),
+ to one supplier or if you want a Call for Bids to compare offers from several suppliers."""),
+ 'group_advance_purchase_requisition': fields.boolean("Choose from several bids in a call for bids",
+ implied_group='purchase.group_advance_bidding',
+ help="""In the process of a public bidding, you can compare the bid lines and choose for each requested product from which bid you
+ buy which quantity"""),
'module_purchase_analytic_plans': fields.boolean('Use multiple analytic accounts on purchase orders',
help ="""Allows the user to maintain several analysis plans. These let you split lines on a purchase order between several accounts and analytic plans.
This installs the module purchase_analytic_plans."""),
diff --git a/addons/purchase/res_config_view.xml b/addons/purchase/res_config_view.xml
index 276dc892392..6983acb854a 100644
--- a/addons/purchase/res_config_view.xml
+++ b/addons/purchase/res_config_view.xml
@@ -66,6 +66,10 @@
+
+
+
+
diff --git a/addons/purchase/security/purchase_security.xml b/addons/purchase/security/purchase_security.xml
index 4e1cf8b6107..d8b2d320fb1 100644
--- a/addons/purchase/security/purchase_security.xml
+++ b/addons/purchase/security/purchase_security.xml
@@ -21,6 +21,11 @@
+
+ Advance bidding process
+
+
+
diff --git a/addons/purchase_requisition/__openerp__.py b/addons/purchase_requisition/__openerp__.py
index 2ba0ad95e09..bbae04b024b 100644
--- a/addons/purchase_requisition/__openerp__.py
+++ b/addons/purchase_requisition/__openerp__.py
@@ -33,19 +33,24 @@ related requisition. This new object will regroup and will allow you to easily
keep track and order all your purchase orders.
""",
'depends' : ['purchase'],
+ 'js': [
+ 'static/src/js/web_addons.js',
+ ],
'demo': ['purchase_requisition_demo.xml'],
'data': ['security/purchase_tender.xml',
'wizard/purchase_requisition_partner_view.xml',
+ 'wizard/bid_line_qty_view.xml',
'purchase_requisition_data.xml',
'purchase_requisition_view.xml',
'purchase_requisition_report.xml',
+ 'purchase_requisition_workflow.xml',
'security/ir.model.access.csv','purchase_requisition_sequence.xml'
],
'auto_install': False,
'test': [
'test/purchase_requisition_demo.yml',
- 'test/purchase_requisition.yml',
'test/cancel_purchase_requisition.yml',
+ 'test/purchase_requisition.yml',
],
'installable': True,
}
diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py
index c5d32136a7a..abdf43d3d97 100644
--- a/addons/purchase_requisition/purchase_requisition.py
+++ b/addons/purchase_requisition/purchase_requisition.py
@@ -23,96 +23,161 @@ from datetime import datetime
from dateutil.relativedelta import relativedelta
import time
-from openerp.osv import fields,osv
+from openerp.osv import fields, osv
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
class purchase_requisition(osv.osv):
_name = "purchase.requisition"
- _description="Purchase Requisition"
+ _description = "Purchase Requisition"
_inherit = ['mail.thread', 'ir.needaction_mixin']
+
+ def _get_po_line(self, cr, uid, ids, field_names, arg=None, context=None):
+ result = {}.fromkeys(ids, [])
+ for element in self.browse(cr, uid, ids, context=context):
+ for po in element.purchase_ids:
+ result[element.id] += [po_line.id for po_line in po.order_line]
+ return result
+
_columns = {
- 'name': fields.char('Requisition Reference', size=32,required=True),
+ 'name': fields.char('Call for Bids Reference', size=32, required=True),
'origin': fields.char('Source Document', size=32),
- 'date_start': fields.datetime('Requisition Date'),
- 'date_end': fields.datetime('Requisition Deadline'),
+ 'ordering_date': fields.date('Scheduled Ordering Date'),
+ 'date_end': fields.datetime('Bid Submission Deadline'),
+ 'schedule_date': fields.date('Scheduled Date', select=True, help="The expected and scheduled date where all the products are received"),
'user_id': fields.many2one('res.users', 'Responsible'),
- 'exclusive': fields.selection([('exclusive','Purchase Requisition (exclusive)'),('multiple','Multiple Requisitions')],'Requisition Type', required=True, help="Purchase Requisition (exclusive): On the confirmation of a purchase order, it cancels the remaining purchase order.\nPurchase Requisition(Multiple): It allows to have multiple purchase orders.On confirmation of a purchase order it does not cancel the remaining orders"""),
+ 'exclusive': fields.selection([('exclusive', 'Select only one RFQ (exclusive)'), ('multiple', 'Select multiple RFQ')], 'Bid Selection Type', required=True, help="Select only one RFQ (exclusive): On the confirmation of a purchase order, it cancels the remaining purchase order.\nSelect multiple RFQ: It allows to have multiple purchase orders.On confirmation of a purchase order it does not cancel the remaining orders"""),
'description': fields.text('Description'),
'company_id': fields.many2one('res.company', 'Company', required=True),
- 'purchase_ids' : fields.one2many('purchase.order','requisition_id','Purchase Orders',states={'done': [('readonly', True)]}),
- 'line_ids' : fields.one2many('purchase.requisition.line','requisition_id','Products to Purchase',states={'done': [('readonly', True)]}),
- 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
- 'state': fields.selection([('draft','New'),('in_progress','Sent to Suppliers'),('cancel','Cancelled'),('done','Purchase Done')],
- 'Status', track_visibility='onchange', required=True)
+ 'purchase_ids': fields.one2many('purchase.order', 'requisition_id', 'Purchase Orders', states={'done': [('readonly', True)]}),
+ 'po_line_ids': fields.function(_get_po_line, method=True, type='one2many', relation='purchase.order.line', string='Products by supplier'),
+ 'line_ids': fields.one2many('purchase.requisition.line', 'requisition_id', 'Products to Purchase', states={'done': [('readonly', True)]}),
+ 'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'),
+ 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
+ 'state': fields.selection([('draft', 'Draft'), ('in_progress', 'Confirmed'), ('open', 'Bid Selection'), ('done', 'PO Created'), ('cancel', 'Cancelled')],
+ 'Status', track_visibility='onchange', required=True),
+ 'multiple_rfq_per_supplier': fields.boolean('Multiple RFQ per supplier'),
+ 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
}
_defaults = {
- 'date_start': lambda *args: time.strftime('%Y-%m-%d %H:%M:%S'),
'state': 'draft',
'exclusive': 'multiple',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.requisition', context=c),
- 'user_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).id ,
+ 'user_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).id,
'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order.requisition'),
}
def copy(self, cr, uid, id, default=None, context=None):
- if not default:
- default = {}
+ default = default or {}
default.update({
- 'state':'draft',
- 'purchase_ids':[],
+ 'state': 'draft',
+ 'purchase_ids': [],
'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order.requisition'),
})
return super(purchase_requisition, self).copy(cr, uid, id, default, context)
-
+
def tender_cancel(self, cr, uid, ids, context=None):
purchase_order_obj = self.pool.get('purchase.order')
- for purchase in self.browse(cr, uid, ids, context=context):
- for purchase_id in purchase.purchase_ids:
- if str(purchase_id.state) in('draft'):
- purchase_order_obj.action_cancel(cr,uid,[purchase_id.id])
+ #try to set all associated quotations to cancel state
+ purchase_ids = []
+ for tender in self.browse(cr, uid, ids, context=context):
+ purchase_ids += [purchase.id for purchase in tender.purchase_ids]
+ if purchase_ids:
+ purchase_order_obj.action_cancel(cr, uid, purchase_ids)
+ purchase_order_obj.message_post(cr, uid, purchase_ids, body=_('Cancelled by the tender associated to this quotation.'), context=context)
return self.write(cr, uid, ids, {'state': 'cancel'})
def tender_in_progress(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state':'in_progress'} ,context=context)
+ return self.write(cr, uid, ids, {'state': 'in_progress'}, context=context)
+
+ def tender_open(self, cr, uid, ids, context=None):
+ return self.write(cr, uid, ids, {'state': 'open'}, context=context)
def tender_reset(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state': 'draft'})
+ self.write(cr, uid, ids, {'state': 'draft'})
+ for p_id in ids:
+ # Deleting the existing instance of workflow for PO
+ self.delete_workflow(cr, uid, [p_id])
+ self.create_workflow(cr, uid, [p_id])
+ return True
def tender_done(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state':'done', 'date_end':time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
+ return self.write(cr, uid, ids, {'state': 'done'}, context=context)
- def _planned_date(self, requisition, delay=0.0):
- company = requisition.company_id
- date_planned = False
- if requisition.date_start:
- date_planned = datetime.strptime(requisition.date_start, '%Y-%m-%d %H:%M:%S') - relativedelta(days=company.po_lead)
+ def open_product_line(self, cr, uid, ids, context=None):
+ """ This opens product line view to view all lines from the different quotations, groupby default by product and partner to show comparaison
+ between supplier price
+ @return: the product line tree view
+ """
+ if context is None:
+ context = {}
+ res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'purchase_requisition', 'purchase_line_tree', context=context)
+ res['context'] = context
+ po_ids_browse = self.browse(cr, uid, ids, context=context)[0].po_line_ids
+ po_ids = []
+ for po in po_ids_browse:
+ po_ids.append(po.id)
+ res['context'].update({
+ 'search_default_groupby_product': True,
+ 'search_default_hide_cancelled': True,
+ })
+ res['domain'] = [('id', 'in', po_ids)]
+ return res
+
+ def open_rfq(self, cr, uid, ids, context=None):
+ """ This opens rfq view to view all quotations associated to the call for bids
+ @return: the RFQ tree view
+ """
+ if context is None:
+ context = {}
+ res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'purchase', 'purchase_rfq', context=context)
+ res['context'] = context
+ po_ids = [po.id for po in self.browse(cr, uid, ids, context=context)[0].purchase_ids]
+ res['domain'] = [('id', 'in', po_ids)]
+ return res
+
+ def _prepare_purchase_order(self, cr, uid, requisition, supplier, context=None):
+ if not requisition.warehouse_id:
+ warehouse_obj = self.pool.get('stock.warehouse')
+
+ warehouse_id = warehouse_obj.search(cr, uid, [('company_id', '=', requisition.company_id.id)], context=context)
+ if not warehouse_id:
+ raise osv.except_osv(_('Warning!'), _('No warehouse found for this company.'))
+ location_id = warehouse_obj.browse(cr, uid, warehouse_id, context=context)[0].lot_input_id.id
else:
- date_planned = datetime.today() - relativedelta(days=company.po_lead)
- if delay:
- date_planned -= relativedelta(days=delay)
- return date_planned and date_planned.strftime('%Y-%m-%d %H:%M:%S') or False
+ location_id = requisition.warehouse_id.lot_input_id.id
+ supplier_pricelist = supplier.property_product_pricelist_purchase and supplier.property_product_pricelist_purchase.id or False
+ return {
+ 'origin': requisition.name,
+ 'date_order': requisition.date_end or fields.date.context_today,
+ 'partner_id': supplier.id,
+ 'pricelist_id': supplier_pricelist,
+ 'location_id': location_id,
+ 'company_id': requisition.company_id.id,
+ 'fiscal_position': supplier.property_account_position and supplier.property_account_position.id or False,
+ 'requisition_id': requisition.id,
+ 'notes': requisition.description,
+ 'warehouse_id': requisition.warehouse_id.id if requisition.warehouse_id else False,
+ }
- def _seller_details(self, cr, uid, requisition_line, supplier, context=None):
+ def _prepare_purchase_order_line(self, cr, uid, requisition, requisition_line, purchase_id, supplier, context=None):
+ po_line_obj = self.pool.get('purchase.order.line')
product_uom = self.pool.get('product.uom')
- pricelist = self.pool.get('product.pricelist')
- supplier_info = self.pool.get("product.supplierinfo")
product = requisition_line.product_id
default_uom_po_id = product.uom_po_id.id
+ date_order = requisition.ordering_date or fields.date.context_today(self, cr, uid, context=context)
qty = product_uom._compute_qty(cr, uid, requisition_line.product_uom_id.id, requisition_line.product_qty, default_uom_po_id)
- seller_delay = 0.0
- seller_price = False
- seller_qty = False
- for product_supplier in product.seller_ids:
- if supplier.id == product_supplier.name and qty >= product_supplier.qty:
- seller_delay = product_supplier.delay
- seller_qty = product_supplier.qty
- supplier_pricelist = supplier.property_product_pricelist_purchase or False
- seller_price = pricelist.price_get(cr, uid, [supplier_pricelist.id], product.id, qty, False, {'uom': default_uom_po_id})[supplier_pricelist.id]
- if seller_qty:
- qty = max(qty,seller_qty)
- date_planned = self._planned_date(requisition_line.requisition_id, seller_delay)
- return seller_price, qty, default_uom_po_id, date_planned
+ supplier_pricelist = supplier.property_product_pricelist_purchase and supplier.property_product_pricelist_purchase.id or False
+ vals = po_line_obj.onchange_product_id(cr, uid, [], supplier_pricelist, product.id, qty, default_uom_po_id,
+ supplier.id, date_order=date_order, fiscal_position_id=supplier.property_account_position, date_planned=requisition_line.schedule_date,
+ name=False, price_unit=False, state='draft', context=context)['value']
+ vals.update({
+ 'order_id': purchase_id,
+ 'product_id': product.id,
+ 'move_dest_id': requisition.move_dest_id.id,
+ 'account_analytic_id': requisition_line.account_analytic_id.id,
+ })
+ return vals
def make_purchase_order(self, cr, uid, ids, partner_id, context=None):
"""
@@ -124,62 +189,114 @@ class purchase_requisition(osv.osv):
purchase_order = self.pool.get('purchase.order')
purchase_order_line = self.pool.get('purchase.order.line')
res_partner = self.pool.get('res.partner')
- fiscal_position = self.pool.get('account.fiscal.position')
supplier = res_partner.browse(cr, uid, partner_id, context=context)
- supplier_pricelist = supplier.property_product_pricelist_purchase or False
res = {}
for requisition in self.browse(cr, uid, ids, context=context):
- if supplier.id in filter(lambda x: x, [rfq.state <> 'cancel' and rfq.partner_id.id or None for rfq in requisition.purchase_ids]):
- raise osv.except_osv(_('Warning!'), _('You have already one %s purchase order for this partner, you must cancel this purchase order to create a new quotation.') % rfq.state)
- location_id = requisition.warehouse_id.lot_input_id.id
+ if not requisition.multiple_rfq_per_supplier and supplier.id in filter(lambda x: x, [rfq.state != 'cancel' and rfq.partner_id.id or None for rfq in requisition.purchase_ids]):
+ raise osv.except_osv(_('Warning!'), _('You have already one %s purchase order for this partner, you must cancel this purchase order to create a new quotation.') % rfq.state)
context.update({'mail_create_nolog': True})
- purchase_id = purchase_order.create(cr, uid, {
- 'origin': requisition.name,
- 'partner_id': supplier.id,
- 'pricelist_id': supplier_pricelist.id,
- 'location_id': location_id,
- 'company_id': requisition.company_id.id,
- 'fiscal_position': supplier.property_account_position and supplier.property_account_position.id or False,
- 'requisition_id':requisition.id,
- 'notes':requisition.description,
- 'warehouse_id':requisition.warehouse_id.id ,
- })
+ purchase_id = purchase_order.create(cr, uid, self._prepare_purchase_order(cr, uid, requisition, supplier, context=context), context=context)
purchase_order.message_post(cr, uid, [purchase_id], body=_("RFQ created"), context=context)
res[requisition.id] = purchase_id
for line in requisition.line_ids:
- product = line.product_id
- seller_price, qty, default_uom_po_id, date_planned = self._seller_details(cr, uid, line, supplier, context=context)
- taxes_ids = product.supplier_taxes_id
- taxes = fiscal_position.map_tax(cr, uid, supplier.property_account_position, taxes_ids)
- purchase_order_line.create(cr, uid, {
- 'order_id': purchase_id,
- 'name': product.partner_ref,
- 'product_qty': qty,
- 'product_id': product.id,
- 'product_uom': default_uom_po_id,
- 'price_unit': seller_price,
- 'date_planned': date_planned,
- 'taxes_id': [(6, 0, taxes)],
- }, context=context)
-
+ purchase_order_line.create(cr, uid, self._prepare_purchase_order_line(cr, uid, requisition, line, purchase_id, supplier, context=context), context=context)
return res
+ def check_valid_quotation(self, cr, uid, quotation, context=None):
+ """
+ Check if a quotation has all his order lines bid in order to confirm it if its the case
+ return True if all order line have been selected during bidding process, else return False
+
+ args : 'quotation' must be a browse record
+ """
+ for line in quotation.order_line:
+ if line.state != 'confirmed' or line.product_qty != line.quantity_bid:
+ return False
+ return True
+
+ def generate_po(self, cr, uid, ids, context=None):
+ """
+ Generate all purchase order based on selected lines, should only be called on one tender at a time
+ """
+ if context is None:
+ contex = {}
+ po = self.pool.get('purchase.order')
+ poline = self.pool.get('purchase.order.line')
+ id_per_supplier = {}
+ for tender in self.browse(cr, uid, ids, context=context):
+ if tender.state == 'done':
+ raise osv.except_osv(_('Warning!'), _('You have already generate the purchase order(s).'))
+
+ confirm = False
+ #check that we have at least confirm one line
+ for po_line in tender.po_line_ids:
+ if po_line.state == 'confirmed':
+ confirm = True
+ break
+ if not confirm:
+ raise osv.except_osv(_('Warning!'), _('You have no line selected for buying.'))
+
+ #check for complete RFQ
+ for quotation in tender.purchase_ids:
+ if (self.check_valid_quotation(cr, uid, quotation, context=context)):
+ #use workflow to set PO state to confirm
+ po.signal_purchase_confirm(cr, uid, [quotation.id])
+
+ #get other confirmed lines per supplier
+ for po_line in tender.po_line_ids:
+ #only take into account confirmed line that does not belong to already confirmed purchase order
+ if po_line.state == 'confirmed' and po_line.order_id.state in ['draft', 'sent', 'bid']:
+ if id_per_supplier.get(po_line.partner_id.id):
+ id_per_supplier[po_line.partner_id.id].append(po_line)
+ else:
+ id_per_supplier[po_line.partner_id.id] = [po_line]
+
+ #generate po based on supplier and cancel all previous RFQ
+ ctx = context.copy()
+ ctx['force_requisition_id'] = True
+ for supplier, product_line in id_per_supplier.items():
+ #copy a quotation for this supplier and change order_line then validate it
+ quotation_id = po.search(cr, uid, [('requisition_id', '=', tender.id), ('partner_id', '=', supplier)], limit=1)[0]
+ new_po = po.copy(cr, uid, quotation_id, default={'order_line': [], 'requisition_id': tender.id, 'origin': tender.name}, context=ctx)
+ #duplicate po_line and change product_qty if needed and associate them to newly created PO
+ for line in product_line:
+ poline.copy(cr, uid, line.id, default={'product_qty': line.quantity_bid, 'order_id': new_po}, context=context)
+ #use workflow to set new PO state to confirm
+ po.signal_purchase_confirm(cr, uid, [new_po])
+
+ #cancel other orders
+ self.cancel_unconfirmed_quotations(cr, uid, tender, context=context)
+
+ #set tender to state done
+ self.signal_done(cr, uid, [tender.id])
+ return True
+
+ def cancel_unconfirmed_quotations(self, cr, uid, tender, context=None):
+ #cancel other orders
+ po = self.pool.get('purchase.order')
+ for quotation in tender.purchase_ids:
+ if quotation.state in ['draft', 'sent', 'bid']:
+ self.pool.get('purchase.order').signal_purchase_cancel(cr, uid, [quotation.id])
+ po.message_post(cr, uid, [quotation.id], body=_('Cancelled by the call for bids associated to this request for quotation.'), context=context)
+ return True
+
class purchase_requisition_line(osv.osv):
-
_name = "purchase.requisition.line"
- _description="Purchase Requisition Line"
+ _description = "Purchase Requisition Line"
_rec_name = 'product_id'
_columns = {
- 'product_id': fields.many2one('product.product', 'Product' ),
+ 'product_id': fields.many2one('product.product', 'Product'),
'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure')),
- 'requisition_id' : fields.many2one('purchase.requisition','Purchase Requisition', ondelete='cascade'),
- 'company_id': fields.related('requisition_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True),
+ 'requisition_id': fields.many2one('purchase.requisition', 'Call for Bids', ondelete='cascade'),
+ 'company_id': fields.related('requisition_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
+ 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account',),
+ 'schedule_date': fields.date('Scheduled Date'),
}
- def onchange_product_id(self, cr, uid, ids, product_id, product_uom_id, context=None):
+ def onchange_product_id(self, cr, uid, ids, product_id, product_uom_id, parent_analytic_account, analytic_account, parent_date, date, context=None):
""" Changes UoM and name if product_id changes.
@param name: Name of the field
@param product_id: Changed product_id
@@ -188,7 +305,11 @@ class purchase_requisition_line(osv.osv):
value = {'product_uom_id': ''}
if product_id:
prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
- value = {'product_uom_id': prod.uom_id.id,'product_qty':1.0}
+ value = {'product_uom_id': prod.uom_id.id, 'product_qty': 1.0}
+ if not analytic_account:
+ value.update({'account_analytic_id': parent_analytic_account})
+ if not date:
+ value.update({'schedule_date': parent_date})
return {'value': value}
_defaults = {
@@ -197,66 +318,90 @@ class purchase_requisition_line(osv.osv):
class purchase_order(osv.osv):
_inherit = "purchase.order"
+
_columns = {
- 'requisition_id' : fields.many2one('purchase.requisition','Purchase Requisition')
+ 'requisition_id': fields.many2one('purchase.requisition', 'Call for Bids'),
}
def wkf_confirm_order(self, cr, uid, ids, context=None):
res = super(purchase_order, self).wkf_confirm_order(cr, uid, ids, context=context)
proc_obj = self.pool.get('procurement.order')
for po in self.browse(cr, uid, ids, context=context):
- if po.requisition_id and (po.requisition_id.exclusive=='exclusive'):
+ if po.requisition_id and (po.requisition_id.exclusive == 'exclusive'):
for order in po.requisition_id.purchase_ids:
if order.id != po.id:
proc_ids = proc_obj.search(cr, uid, [('purchase_id', '=', order.id)])
- if proc_ids and po.state=='confirmed':
+ if proc_ids and po.state == 'confirmed':
proc_obj.write(cr, uid, proc_ids, {'purchase_id': po.id})
self.signal_purchase_cancel(cr, uid, [order.id])
po.requisition_id.tender_done(context=context)
return res
+ def copy(self, cr, uid, id, default=None, context=None):
+ if context is None:
+ context = {}
+ if not context.get('force_requisition_id'):
+ default = default or {}
+ default.update({'requisition_id': False})
+ return super(purchase_order, self).copy(cr, uid, id, default=default, context=context)
+
+
+class purchase_order_line(osv.osv):
+ _inherit = 'purchase.order.line'
+
+ _columns = {
+ 'quantity_bid': fields.float('Quantity Bid', digits_compute=dp.get_precision('Product Unit of Measure'), help="Technical field for not loosing the initial information about the quantity proposed in the bid"),
+ }
+
+ def action_draft(self, cr, uid, ids, context=None):
+ self.write(cr, uid, ids, {'state': 'draft'}, context=context)
+
+ 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:
+ self.write(cr, uid, ids, {'quantity_bid': element.product_qty}, context=context)
+ return True
+
+ def generate_po(self, cr, uid, tender_id, context=None):
+ #call generate_po from tender with active_id. Called from js widget
+ return self.pool.get('purchase.requisition').generate_po(cr, uid, [tender_id], context=context)
+
class product_product(osv.osv):
_inherit = 'product.product'
_columns = {
- 'purchase_requisition': fields.boolean('Purchase Requisition', help="Check this box to generates purchase requisition instead of generating requests for quotation from procurement.")
- }
- _defaults = {
- 'purchase_requisition': False
+ 'purchase_requisition': fields.boolean('Call for Bids', help="Check this box to generate Call for Bids instead of generating requests for quotation from procurement.")
}
class procurement_order(osv.osv):
-
_inherit = 'procurement.order'
_columns = {
- 'requisition_id' : fields.many2one('purchase.requisition','Latest Requisition')
+ 'requisition_id': fields.many2one('purchase.requisition', 'Latest Requisition')
}
- def make_po(self, cr, uid, ids, context=None):
- res = {}
+
+ def _run(self, cr, uid, procurement, context=None):
requisition_obj = self.pool.get('purchase.requisition')
warehouse_obj = self.pool.get('stock.warehouse')
- procurement = self.browse(cr, uid, ids, context=context)[0]
if procurement.product_id.purchase_requisition:
- warehouse_id = warehouse_obj.search(cr, uid, [('company_id', '=', procurement.company_id.id or company.id)], context=context)
- res[procurement.id] = requisition_obj.create(cr, uid,
- {
- 'origin': procurement.origin,
- 'date_end': procurement.date_planned,
- 'warehouse_id':warehouse_id and warehouse_id[0] or False,
- 'company_id':procurement.company_id.id,
- 'line_ids': [(0,0,{
- 'product_id': procurement.product_id.id,
- 'product_uom_id': procurement.product_uom.id,
- 'product_qty': procurement.product_qty
+ warehouse_id = warehouse_obj.search(cr, uid, [('company_id', '=', procurement.company_id.id)], context=context)
+ requisition_id = requisition_obj.create(cr, uid, {
+ 'origin': procurement.origin,
+ 'date_end': procurement.date_planned,
+ 'warehouse_id': warehouse_id and warehouse_id[0] or False,
+ 'company_id': procurement.company_id.id,
+ 'move_dest_id': procurement.move_id.id,
+ 'line_ids': [(0, 0, {
+ 'product_id': procurement.product_id.id,
+ 'product_uom_id': procurement.product_uom.id,
+ 'product_qty': procurement.product_qty
- })],
- })
- self.write(cr,uid,[procurement.id],{'state': 'running','requisition_id': res[procurement.id]},context=context)
- else:
- res = super(procurement_order, self).make_po(cr, uid, ids, context=context)
- return res
+ })],
+ })
+ return self.write(cr, uid, [procurement.id], {'requisition_id': requisition_id}, context=context)
+ return super(procurement_order, self)._run(cr, uid, procurement, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/purchase_requisition/purchase_requisition_report.xml b/addons/purchase_requisition/purchase_requisition_report.xml
index 97d510fe752..f0767dc6da4 100644
--- a/addons/purchase_requisition/purchase_requisition_report.xml
+++ b/addons/purchase_requisition/purchase_requisition_report.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/addons/purchase_requisition/purchase_requisition_view.xml b/addons/purchase_requisition/purchase_requisition_view.xml
index ac6d8a251fe..298a16e9fb6 100644
--- a/addons/purchase_requisition/purchase_requisition_view.xml
+++ b/addons/purchase_requisition/purchase_requisition_view.xml
@@ -7,7 +7,7 @@
purchase.order
-
+
@@ -28,49 +28,62 @@
purchase.requisition.formpurchase.requisition
-
diff --git a/addons/purchase_requisition/purchase_requisition_workflow.xml b/addons/purchase_requisition/purchase_requisition_workflow.xml
new file mode 100644
index 00000000000..fb2524562c1
--- /dev/null
+++ b/addons/purchase_requisition/purchase_requisition_workflow.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+ Purchase Requisition Basic Workflow
+ purchase.requisition
+ True
+
+
+
+
+ True
+ draft
+
+
+
+ sent
+ function
+ tender_in_progress()
+
+
+
+ open
+ function
+ tender_open()
+
+
+
+ cancel
+ function
+ True
+ tender_cancel()
+
+
+
+ done
+ tender_done()
+ function
+ True
+
+
+
+
+
+ sent_suppliers
+
+
+
+
+ open_bid
+
+
+
+
+ cancel_requisition
+
+
+
+
+ done
+
+
+
+
+ cancel_requisition
+
+
+
diff --git a/addons/purchase_requisition/report/purchase_requisition.rml b/addons/purchase_requisition/report/purchase_requisition.rml
index 95c5d19ba31..78b2d0818d4 100644
--- a/addons/purchase_requisition/report/purchase_requisition.rml
+++ b/addons/purchase_requisition/report/purchase_requisition.rml
@@ -1,6 +1,6 @@
-
-
+
+
@@ -50,6 +50,7 @@
+
@@ -57,6 +58,7 @@
+
@@ -132,20 +134,20 @@
- Purchase for Requisitions [[ requisition.name ]]
+ Call for Bid [[ requisition.name ]]
Description [[ requisition.line_ids==[] and removeParentNode('blockTable') ]]
@@ -183,11 +185,14 @@
Product UoM
+
+ Scheduled Date
+
[[ requisition.line_ids==[] and removeParentNode('section') ]] [[ repeatIn(requisition.line_ids,'line_ids') ]]
-
+
[ [[ (line_ids.product_id and line_ids.product_id.code) or '' ]] ] [[ (line_ids.product_id and line_ids.product_id.name) or '' ]]
@@ -198,23 +203,26 @@
[[ (line_ids.product_uom_id and line_ids.product_uom_id.category_id and line_ids.product_uom_id.category_id.name) or '' ]]
- Quotation Detail [[ requisition.purchase_ids ==[] and removeParentNode('para') ]]
+ Requests for Quotation [[ requisition.purchase_ids ==[] and removeParentNode('para') ]]
Supplier [[ requisition.purchase_ids ==[] and removeParentNode('blockTable') ]]
- Date Ordered
+ Date
- Order Reference
+ Reference
@@ -236,4 +244,4 @@
-
\ No newline at end of file
+
diff --git a/addons/purchase_requisition/static/src/js/web_addons.js b/addons/purchase_requisition/static/src/js/web_addons.js
new file mode 100644
index 00000000000..693df43c751
--- /dev/null
+++ b/addons/purchase_requisition/static/src/js/web_addons.js
@@ -0,0 +1,27 @@
+openerp.purchase_requisition = function(instance) {
+ var QWeb = instance.web.qweb,
+ _t = instance.web._t;
+
+ instance.web.purchase_requisition = instance.web.purchase_requisition || {};
+
+ instance.web.views.add('tree_purchase_order_line_compare', 'instance.web.purchase_requisition.CompareListView');
+ instance.web.purchase_requisition.CompareListView = instance.web.ListView.extend({
+ init: function() {
+ var self = this;
+ this._super.apply(this, arguments);
+ this.on('list_view_loaded', this, function() {
+ if(self.__parentedParent.$el.find('.oe_generate_po').length == 0){
+ var button = $("")
+ .click(this.proxy('generate_purchase_order'));
+ self.__parentedParent.$el.find('.oe_list_buttons').append(button);
+ }
+ });
+ },
+ generate_purchase_order: function () {
+ var self = this;
+ new instance.web.Model(self.dataset.model).call("generate_po",[self.dataset.context.active_id,self.dataset.context]).then(function(result) {
+ self.ViewManager.ActionManager.history_back();
+ });
+ },
+ });
+}
diff --git a/addons/purchase_requisition/test/purchase_requisition.yml b/addons/purchase_requisition/test/purchase_requisition.yml
index 791dc02f895..458acc30750 100644
--- a/addons/purchase_requisition/test/purchase_requisition.yml
+++ b/addons/purchase_requisition/test/purchase_requisition.yml
@@ -31,10 +31,20 @@
assert line.product_uom_id.id == procurement.product_uom.id, "UOM is not correspond."
assert line.product_qty == procurement.product_qty, "Quantity is not correspond."
-
- I open another requisition.
+ I send the purchase order associated to the requisition.
+-
+ !python {model: purchase.requisition}: |
+ po = self.pool.get('purchase.order')
+ for element in self.browse(cr, uid, [ref("requisition1")], context=context):
+ for purchase in element.purchase_ids:
+ po.write(cr, uid, purchase.id, {'state': 'sent'}, context=context)
+
+-
+ I open another requisition and set tender state to choose bidding line.
-
!python {model: purchase.requisition}: |
self.tender_in_progress(cr, uid, [ref("requisition1")], context=context)
+ self.tender_open(cr, uid, [ref("requisition1")], context=context)
-
Supplier send one RFQ so I create requisition request of that supplier.
-
diff --git a/addons/purchase_requisition/wizard/__init__.py b/addons/purchase_requisition/wizard/__init__.py
index a9691cb6e25..e19f2efb14f 100644
--- a/addons/purchase_requisition/wizard/__init__.py
+++ b/addons/purchase_requisition/wizard/__init__.py
@@ -20,6 +20,7 @@
##############################################################################
import purchase_requisition_partner
+import bid_line_qty
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/purchase_requisition/wizard/bid_line_qty.py b/addons/purchase_requisition/wizard/bid_line_qty.py
new file mode 100644
index 00000000000..7bae7755414
--- /dev/null
+++ b/addons/purchase_requisition/wizard/bid_line_qty.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# 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 .
+#
+##############################################################################
+
+from openerp.osv import fields, osv
+import openerp.addons.decimal_precision as dp
+
+class bid_line_qty(osv.osv_memory):
+ _name = "bid.line.qty"
+ _description = "Change Bid line quantity"
+ _columns = {
+ 'qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
+ }
+
+ def change_qty(self, cr, uid, ids, context=None):
+ active_ids = context and context.get('active_ids', [])
+ data = self.browse(cr, uid, ids, context=context)[0]
+ self.pool.get('purchase.order.line').write(cr, uid, active_ids, {'quantity_bid': data.qty})
+ return {'type': 'ir.actions.act_window_close'}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/purchase_requisition/wizard/bid_line_qty_view.xml b/addons/purchase_requisition/wizard/bid_line_qty_view.xml
new file mode 100644
index 00000000000..0bc8ad2ac5f
--- /dev/null
+++ b/addons/purchase_requisition/wizard/bid_line_qty_view.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ Quantity to Purchase
+ bid.line.qty
+
+
+
+
+
+
+
+
+
+
+
+ Bid Line Qty
+ ir.actions.act_window
+ bid.line.qty
+ form
+ form
+ {'record_id' : active_id}
+ new
+
+
+
diff --git a/addons/purchase_requisition/wizard/purchase_requisition_partner.py b/addons/purchase_requisition/wizard/purchase_requisition_partner.py
index 0fee8745931..e161756dbe6 100644
--- a/addons/purchase_requisition/wizard/purchase_requisition_partner.py
+++ b/addons/purchase_requisition/wizard/purchase_requisition_partner.py
@@ -38,7 +38,7 @@ class purchase_requisition_partner(osv.osv_memory):
record_id = context and context.get('active_id', False) or False
tender = self.pool.get('purchase.requisition').browse(cr, uid, record_id, context=context)
if not tender.line_ids:
- raise osv.except_osv(_('Error!'), _('No Product in Tender.'))
+ raise osv.except_osv(_('Error!'), _('No product in call for bids.'))
return res
def create_order(self, cr, uid, ids, context=None):
@@ -48,5 +48,4 @@ class purchase_requisition_partner(osv.osv_memory):
return {'type': 'ir.actions.act_window_close'}
-
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/purchase_requisition/wizard/purchase_requisition_partner_view.xml b/addons/purchase_requisition/wizard/purchase_requisition_partner_view.xml
index f5f0367ad4b..3db4ca3ae83 100644
--- a/addons/purchase_requisition/wizard/purchase_requisition_partner_view.xml
+++ b/addons/purchase_requisition/wizard/purchase_requisition_partner_view.xml
@@ -2,15 +2,15 @@
- Purchase Requisition
+ Choose Supplierpurchase.requisition.partner
-