[MERGE] merged csn branch with purchase and purchase tender improvements (workflow, features...)

bzr revid: qdp-launchpad@openerp.com-20130731171625-o8gun3jrobzpvu62
This commit is contained in:
Quentin (OpenERP) 2013-07-31 19:16:25 +02:00
commit 6ad84ce1ad
20 changed files with 762 additions and 231 deletions

View File

@ -18,7 +18,7 @@
<data noupdate="1">
<!--Email template -->
<record id="email_template_edi_purchase" model="email.template">
<field name="name">Purchase Order - Send by mail</field>
<field name="name">RFQ - Send by Email</field>
<field name="email_from">${object.validator.email or ''}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="partner_to">${object.partner_id.id}</field>
@ -34,6 +34,80 @@
<p>Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}: </p>
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
&nbsp;&nbsp;<strong>REFERENCES</strong><br />
&nbsp;&nbsp;RFQ number: <strong>${object.name}</strong><br />
&nbsp;&nbsp;RFQ date: ${object.date_order}<br />
% if object.origin:
&nbsp;&nbsp;RFQ reference: ${object.origin}<br />
% endif
% if object.partner_ref:
&nbsp;&nbsp;Your reference: ${object.partner_ref}<br />
% endif
% if object.validator:
&nbsp;&nbsp;Your contact: <a href="mailto:${object.validator.email or ''}?subject=Order%20${object.name}">${object.validator.name}</a>
% endif
</p>
<br/>
<p>If you have any question, do not hesitate to contact us.</p>
<p>Thank you!</p>
<br/>
<br/>
<div style="width: 375px; margin: 0px; padding: 0px; background-color: #8E0000; border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; background-repeat: repeat no-repeat;">
<h3 style="margin: 0px; padding: 2px 14px; font-size: 12px; color: #DDD;">
<strong style="text-transform:uppercase;">${object.company_id.name}</strong></h3>
</div>
<div style="width: 347px; margin: 0px; padding: 5px 14px; line-height: 16px; background-color: #F2F2F2;">
<span style="color: #222; margin-bottom: 5px; display: block; ">
% if object.company_id.street:
${object.company_id.street}<br/>
% endif
% if object.company_id.street2:
${object.company_id.street2}<br/>
% endif
% if object.company_id.city or object.company_id.zip:
${object.company_id.zip} ${object.company_id.city}<br/>
% 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 ''}<br/>
% endif
</span>
% if object.company_id.phone:
<div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">
Phone:&nbsp; ${object.company_id.phone}
</div>
% endif
% if object.company_id.website:
<div>
Web :&nbsp;<a href="${object.company_id.website}">${object.company_id.website}</a>
</div>
%endif
<p></p>
</div>
</div>
]]></field>
</record>
<!--Email template -->
<record id="email_template_edi_purchase_done" model="email.template">
<field name="name">Purchase Order - Send by Email</field>
<field name="email_from">${object.validator.email or ''}</field>
<field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field>
<field name="email_recipients">${object.partner_id.id}</field>
<field name="model_id" ref="purchase.model_purchase_order"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="report_purchase_order"/>
<field name="report_name">PO_${(object.name or '').replace('/','_')}</field>
<field name="lang">${object.partner_id.lang}</field>
<field name="body_html"><![CDATA[
<div style="font-family: 'Lucica Grande', Ubuntu, Arial, Verdana, sans-serif; font-size: 12px; color: rgb(34, 34, 34); background-color: #FFF; ">
<p>Hello ${object.partner_id.name},</p>
<p>Here is a ${object.state in ('draft', 'sent') and 'request for quotation' or 'purchase order confirmation'} from ${object.company_id.name}: </p>
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
&nbsp;&nbsp;<strong>REFERENCES</strong><br />
&nbsp;&nbsp;Order number: <strong>${object.name}</strong><br />

View File

@ -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)

View File

@ -159,28 +159,29 @@
<field name="arch" type="xml">
<form string="Purchase Order" version="7.0">
<header>
<button name="wkf_send_rfq" states="draft" string="Send by Email" type="object" context="{'send_rfq':True}" class="oe_highlight"/>
<button name="wkf_send_rfq" states="sent" string="Send by Email" type="object" context="{'send_rfq':True}"/>
<button name="print_quotation" string="Print" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
<button name="print_quotation" string="Print" type="object" states="sent" groups="base.group_user"/>
<button name="purchase_confirm" states="draft" string="Confirm Order"/>
<button name="purchase_confirm" states="sent" string="Confirm Order" class="oe_highlight"/>
<button name="wkf_send_rfq" states="confirmed" string="Resend Purchase Order" type="object" class="oe_highlight"/>
<button name="action_cancel" states="approved,except_picking,except_invoice" string="Cancel Order" type="object" />
<button name="bid_received" states="sent" string="Bid Received" class="oe_highlight"/>
<button name="wkf_send_rfq" states="draft" string="Send RFQ by Email" type="object" context="{'send_rfq':True}" class="oe_highlight"/>
<button name="wkf_send_rfq" states="sent" string="Re-Send RFQ by Email" type="object" context="{'send_rfq':True}"/>
<button name="print_quotation" string="Print RFQ" type="object" states="draft" class="oe_highlight" groups="base.group_user"/>
<button name="print_quotation" string="Re-Print RFQ" type="object" states="sent" groups="base.group_user"/>
<button name="purchase_confirm" states="draft" string="Confirm Order" id="draft_confirm"/>
<button name="purchase_confirm" states="bid" string="Confirm Order" class="oe_highlight" id="bid_confirm"/>
<button name="picking_ok" states="except_picking" string="Manually Corrected"/>
<button name="invoice_ok" states="except_invoice" string="Manually Corrected"/>
<button name="purchase_approve" states="confirmed" string="Approve Order" class="oe_highlight" groups="purchase.group_purchase_manager"/>
<button name="wkf_send_rfq" states="approved" string="Send PO by Email" type="object" context="{'send_rfq':False}"/>
<button name="view_picking" string="Receive Products" type="object" attrs="{'invisible': ['|', ('shipped','=',True), ('state','!=', 'approved')]}" class="oe_highlight"/>
<button name="view_invoice" string="Receive Invoice" type="object" attrs="{'invisible': ['|', ('invoice_method','=','picking'), '|', ('state','!=', 'approved'), ('invoiced','=',True) ]}" class="oe_highlight"/>
<button name="action_cancel_draft" states="cancel,sent,confirmed" string="Set to Draft" type="object" />
<button name="purchase_cancel" states="draft,confirmed,sent" string="Cancel Order"/>
<field name="state" widget="statusbar" statusbar_visible="draft,sent,approved,done" statusbar_colors='{"except_picking":"red","except_invoice":"red","confirmed":"blue"}' readonly="1"/>
<button name="purchase_cancel" states="draft,confirmed,sent,bid" string="Cancel"/>
<button name="action_cancel" states="approved,except_picking,except_invoice" string="Cancel" type="object" />
<field name="state" widget="statusbar" statusbar_visible="draft,sent,bid,approved,done" statusbar_colors='{"except_picking":"red","except_invoice":"red","confirmed":"blue"}' readonly="1"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<label string="Request for Quotation " attrs="{'invisible': [('state','not in',('draft','sent'))]}"/>
<label string="Purchase Order " attrs="{'invisible': [('state','in',('draft','sent'))]}"/>
<label string="Request for Quotation " attrs="{'invisible': [('state','not in',('draft','sent','bid'))]}"/>
<label string="Purchase Order " attrs="{'invisible': [('state','in',('draft','sent','bid'))]}"/>
<field name="name" class="oe_inline" readonly="1"/>
</h1>
</div>
@ -200,16 +201,16 @@
</group>
</group>
<notebook>
<page string="Purchase Order">
<page string="Products">
<field name="order_line">
<tree string="Purchase Order Lines" editable="bottom">
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,parent.state,context)"/>
<field name="name"/>
<field name="date_planned"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="account_analytic_id" groups="purchase.group_analytic_accounting" domain="[('type','not in',('view','template'))]"/>
<field name="product_qty" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id,parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<field name="product_uom" groups="product.group_uom" on_change="onchange_product_uom(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<field name="product_qty" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id,parent.date_order,parent.fiscal_position,date_planned,name,price_unit,parent.state,context)"/>
<field name="product_uom" groups="product.group_uom" on_change="onchange_product_uom(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,parent.state,context)"/>
<field name="price_unit"/>
<field name="taxes_id" widget="many2many_tags" domain="[('parent_id','=',False),('type_tax_use','!=','sale')]"/>
<field name="price_subtotal"/>
@ -228,7 +229,15 @@
<div class="oe_clear"/>
<field name="notes" class="oe_inline" placeholder="Terms and conditions..."/>
</page>
<page string="Incoming Shipments &amp; Invoices">
<page string="RFQ &amp; Bid">
<group>
<field name="payment_term_id" widget="selection"/>
<field name="incoterm_id"/>
<field name="bid_date"/>
<field name="bid_validity"/>
</group>
</page>
<page string="Deliveries &amp; Invoices">
<group>
<group>
<field name="dest_address_id" string="Customer Address" on_change="onchange_dest_address_id(dest_address_id)" groups="stock.group_locations"/>
@ -329,11 +338,11 @@
</record>
<record id="purchase_rfq" model="ir.actions.act_window">
<field name="name">Quotations</field>
<field name="name">Requests for Quotation</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">purchase.order</field>
<field name="context">{}</field>
<field name="domain">[('state','in',('draft','sent','cancel', 'confirmed'))]</field>
<field name="domain">[('state','in',('draft','sent','bid','cancel', 'confirmed'))]</field>
<field name="view_mode">tree,form,graph,calendar</field>
<field name="search_view_id" ref="view_purchase_order_filter"/>
<field name="help" type="html">
@ -359,7 +368,7 @@
<field name="res_model">purchase.order</field>
<field name="view_mode">tree,form,graph,calendar</field>
<field name="context">{}</field>
<field name="domain">[('state','not in',('draft','sent','confirmed'))]</field>
<field name="domain">[('state','not in',('draft','sent','bid', 'confirmed'))]</field>
<field name="search_view_id" ref="view_purchase_order_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
@ -382,11 +391,11 @@
<sheet>
<group>
<group>
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)"/>
<field name="product_id" on_change="onchange_product_id(parent.pricelist_id,product_id,0,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,'draft',context)"/>
<label for="product_qty"/>
<div>
<field name="product_qty" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id,parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)" class="oe_inline"/>
<field name="product_uom" groups="product.group_uom" on_change="onchange_product_uom(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,context)" class="oe_inline"/>
<field name="product_qty" on_change="onchange_product_id(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id,parent.date_order,parent.fiscal_position,date_planned,name,price_unit,'draft',context)" class="oe_inline"/>
<field name="product_uom" groups="product.group_uom" on_change="onchange_product_uom(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id, parent.date_order,parent.fiscal_position,date_planned,name,price_unit,'draft',context)" class="oe_inline"/>
</div>
<field name="price_unit"/>
</group>
@ -429,6 +438,7 @@
</tree>
</field>
</record>
<record id="purchase_order_line_form2" model="ir.ui.view">
<field name="name">purchase.order.line.form2</field>
<field name="model">purchase.order.line</field>
@ -480,9 +490,10 @@
<field name="order_id"/>
<field name="product_id"/>
<field name="partner_id" string="Supplier" filter_domain="[('partner_id', 'child_of', self)]"/>
<filter name="hide_cancelled" string="Hide cancelled lines" domain="[('state', '!=', 'cancel')]"/>
<group expand="0" string="Group By...">
<filter string="Supplier" icon="terp-partner" domain="[]" context="{'group_by' : 'partner_id'}" />
<filter string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by' : 'product_id'}" />
<filter name="groupby_supplier" string="Supplier" icon="terp-partner" domain="[]" context="{'group_by' : 'partner_id'}" />
<filter name="groupby_product" string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by' : 'product_id'}" />
<filter icon="terp-gtk-jump-to-rtl" string="Order Reference" domain="[]" context="{'group_by' :'order_id'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by' : 'state'}" />
</group>
@ -507,6 +518,7 @@
</p>
</field>
</record>
<record id="purchase_line_form_action_tree2" model="ir.actions.act_window.view">
<field eval="1" name="sequence"/>
<field name="view_mode">tree</field>

View File

@ -19,6 +19,12 @@
<field name="kind">function</field>
<field name="action">write({'state':'sent'})</field>
</record>
<record id="act_bid" model="workflow.activity">
<field name="wkf_id" ref="purchase_order"/>
<field name="name">bid</field>
<field name="kind">function</field>
<field name="action">wkf_bid_received()</field>
</record>
<record id="act_confirmed" model="workflow.activity">
<field name="wkf_id" ref="purchase_order"/>
<field name="name">confirmed</field>
@ -31,7 +37,7 @@
<field name="name">cancel</field>
<field name="kind">function</field>
<field name="flow_stop">True</field>
<field name="action">write({'state':'cancel'})</field>
<field name="action">wkf_action_cancel()</field>
</record>
<record id="act_except_invoice" model="workflow.activity">
<field name="wkf_id" ref="purchase_order"/>
@ -88,7 +94,7 @@
<record id="act_done" model="workflow.activity">
<field name="wkf_id" ref="purchase_order"/>
<field name="name">done</field>
<field name="action">write({'state':'done'})</field>
<field name="action">wkf_po_done()</field>
<field name="kind">function</field>
<field name="flow_stop">True</field>
<field name="join_mode">AND</field>
@ -104,16 +110,26 @@
<field name="act_to" ref="act_sent"/>
<field name="signal">send_rfq</field>
</record>
<record id="trans_sent_confirmed" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<record id="trans_bid_confirmed" model="workflow.transition">
<field name="act_from" ref="act_bid"/>
<field name="act_to" ref="act_confirmed"/>
<field name="signal">purchase_confirm</field>
</record>
<record id="trans_sent_bid" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<field name="act_to" ref="act_bid"/>
<field name="signal">bid_received</field>
</record>
<record id="trans_sent_cancel" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">purchase_cancel</field>
</record>
<record id="trans_bid_cancel" model="workflow.transition">
<field name="act_from" ref="act_bid"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">purchase_cancel</field>
</record>
<record id="trans_confirmed_cancel" model="workflow.transition">
<field name="act_from" ref="act_confirmed"/>
@ -221,7 +237,7 @@
<record id="trans_confirm_mto_purchase" model="workflow.transition">
<field name="act_from" ref="procurement.act_confirm_mto"/>
<field name="act_to" ref="act_buy"/>
<field name="condition">check_buy() and check_supplier_info()</field>
<field name="condition">check_buy() and check_supplier_info() and not check_product_requisition()</field>
</record>
<record id="trans_buy_make_done" model="workflow.transition">

View File

@ -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."""),

View File

@ -66,6 +66,10 @@
<field name="module_purchase_requisition" class="oe_inline"/>
<label for="module_purchase_requisition"/>
</div>
<div attrs="{'invisible': [('module_purchase_requisition', '=', False)]}">
<field name="group_advance_purchase_requisition" class="oe_inline"/>
<label for="group_advance_purchase_requisition"/>
</div>
<div>
<field name="module_purchase_analytic_plans" on_change="onchange_purchase_analytic_plans(module_purchase_analytic_plans, context)" class="oe_inline"/>
<label for="module_purchase_analytic_plans"/>

View File

@ -21,6 +21,11 @@
<field name="implied_ids" eval="[(4, ref('analytic.group_analytic_accounting'))]"/>
</record>
<record id="group_advance_bidding" model="res.groups">
<field name="name">Advance bidding process</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
</data>
<data noupdate="1">

View File

@ -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,
}

View File

@ -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:

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<report auto="False" id="report_purchase_requisition" model="purchase.requisition" name="purchase.requisition" rml="purchase_requisition/report/purchase_requisition.rml" string="Purchase Requisition"/>
<report auto="False" id="report_purchase_requisition" model="purchase.requisition" name="purchase.requisition" rml="purchase_requisition/report/purchase_requisition.rml" string="Call for Bids"/>
</data>
</openerp>

View File

@ -7,7 +7,7 @@
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<field name="validator" position="before">
<field name="payment_term_id" position="before">
<field name="requisition_id"/>
</field>
</field>
@ -28,49 +28,62 @@
<field name="name">purchase.requisition.form</field>
<field name="model">purchase.requisition</field>
<field name="arch" type="xml">
<form string="Purchase Requisition" version="7.0">
<header>
<button name="tender_in_progress" states="draft" string="Send to Suppliers" type="object" class="oe_highlight"/>
<button name="tender_reset" states="done,cancel" string="Reset to Draft" type="object" />
<button name="tender_done" states="in_progress" string="Purchase Done" type="object" class="oe_highlight"/>
<button name="tender_cancel" states="draft,in_progress" string="Cancel Requisition" type="object" />
<field name="state" widget="statusbar" statusbar_visible="draft,in_progress,done" statusbar_colors='{"in_progress":"blue"}'/>
<form string="Call for Bids" version="7.0">
<header>
<button name="sent_suppliers" states="draft" string="Confirm Call" class="oe_highlight"/>
<button name="open_bid" states="in_progress" string="Close Call for Bids" class="oe_highlight"/>
<button name="tender_reset" states="cancel" string="Reset to Draft" type="object" />
<button name="open_product_line" states="open" string="Choose product lines" type="object" class="oe_highlight" groups="purchase.group_advance_bidding"/>
<button name="generate_po" states="open" string="Done" type="object" class="oe_highlight"/>
<button name="cancel_requisition" states="draft,in_progress" string="Cancel Call"/>
<field name="state" widget="statusbar" statusbar_visible="draft,in_progress,open,done" statusbar_colors='{"in_progress":"blue"}'/>
</header>
<sheet>
<div class="oe_right oe_button_box" style="margin-top: 10px">
<button name="open_product_line" type="object" string="View Products Lines"
attrs="{'invisible': ['|', ('state', 'not in', ('open','done')), ('exclusive', '=', 'exclusive')]}" groups="purchase.group_advance_bidding"/>
</div>
<div class="oe_edit_only">
<label for="name" class="oe_inline"/>
<label for="origin" class="oe_inline"/>
</div>
<h1>
<field name="name" class="oe_inline"/>
<label string="," attrs="{'invisible':[('origin','=',False)]}"/>
<field name="origin" class="oe_inline" placeholder="e.g. PO0025"/>
<field name="name" class="oe_inline" attrs="{'readonly': [('state','not in',('draft'))]}"/>
</h1>
<group>
<group>
<field name="user_id"/>
<field name="exclusive"/>
<field name="user_id" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="exclusive" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="account_analytic_id" colspan="2" groups="purchase.group_analytic_accounting" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="multiple_rfq_per_supplier" attrs="{'readonly': [('state','not in',('draft'))]}"/>
</group>
<group>
<field name="date_start"/>
<field name="date_end"/>
<field name="warehouse_id" widget="selection" groups="stock.group_locations"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="date_end" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="ordering_date" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="schedule_date" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="origin" placeholder="e.g. OP0025" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="warehouse_id" widget="selection" groups="stock.group_locations" attrs="{'readonly': [('state','not in',('draft'))]}"/>
<field name="company_id" groups="base.group_multi_company" widget="selection" attrs="{'readonly': [('state','not in',('draft'))]}"/>
</group>
</group>
<notebook>
<page string="Products">
<field name="line_ids">
<field name="line_ids" attrs="{'readonly': [('state','not in',('draft'))]}">
<tree string="Products" editable="bottom">
<field name="product_id" on_change="onchange_product_id(product_id,product_uom_id)"/>
<field name="product_id" on_change="onchange_product_id(product_id,product_uom_id,parent.account_analytic_id,account_analytic_id,parent.schedule_date,schedule_date)"/>
<field name="product_qty"/>
<field name="product_uom_id" groups="product.group_uom"/>
<field name="schedule_date"/>
<field name="account_analytic_id" groups="purchase.group_analytic_accounting"/>
</tree>
<form string="Products" version="7.0">
<group>
<field name="product_id" />
<field name="product_qty"/>
<field name="product_uom_id" />
<field name="schedule_date"/>
<field name="account_analytic_id" groups="purchase.group_analytic_accounting"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
</form>
@ -78,26 +91,28 @@
<div class="oe_right oe_button_box" style="margin-top: 10px">
<button name="%(action_purchase_requisition_partner)d" type="action"
string="Request a Quotation" icon="gtk-execute"
attrs="{'invisible': [('line_ids','=',False),('state', 'not in', ('in_progress'))]}"/>
attrs="{'invisible': ['|', ('line_ids','=',[]),('state', 'in', ('draft' ,'open','done'))]}"/>
<button name="open_rfq" type="object" string="View RFQs/Bids" attrs="{'invisible': [('state', 'in', ('draft'))]}"/>
</div>
<separator string="Quotations"/>
<field name="purchase_ids" readonly="1">
<separator string="Requests for Quotation"/>
<field name="purchase_ids">
<tree string="Purchase Order">
<field name="name" string="Reference"/>
<field name="date_order" string="Order Date"/>
<field name="partner_id"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="minimum_planned_date"/>
<field name="minimum_planned_date" invisible="1"/>
<field name="origin"/>
<field name="state"/>
<button name="purchase_cancel" states="draft,confirmed,wait_auth" string="Cancel Purchase Order" icon="gtk-cancel"/>
<button name="purchase_confirm" states="draft" string="Confirm Purchase Order" icon="gtk-apply"/>
<button name="purchase_approve" states="confirmed" string="Approved by Supplier" icon="gtk-ok"/>
<button name="wkf_send_rfq" states="draft" string="Send RFQ by Email" type="object" context="{'send_rfq':True}" icon="gtk-network"/>
</tree>
</field>
<separator string="Terms and Conditions"/>
<field name="description"/>
<field name="description" attrs="{'readonly': [('state','not in',('draft'))]}"/>
</page>
</notebook>
</sheet>
@ -112,10 +127,10 @@
<field name="name">purchase.requisition.tree</field>
<field name="model">purchase.requisition</field>
<field name="arch" type="xml">
<tree fonts="bold:message_unread==True" colors="grey:state == 'cancel';red:date_end and date_end&lt;current_date;black:date_end&gt;current_date;" string="Purchase Requisition">
<tree fonts="bold:message_unread==True" colors="grey:state == 'cancel';red:date_end and date_end&lt;current_date;black:date_end&gt;current_date;" string="Call for Bids">
<field name="message_unread" invisible="1"/>
<field name="name"/>
<field name="date_start"/>
<field name="ordering_date"/>
<field name="user_id"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
<field name="date_end"/>
@ -129,12 +144,12 @@
<field name="name">purchase.requisition.list.select</field>
<field name="model">purchase.requisition</field>
<field name="arch" type="xml">
<search string="Search Purchase Requisition">
<field name="name" string="Purchase Requisition"/>
<search string="Search Call for Bids">
<field name="name" string="Call for Bids"/>
<field name="exclusive" />
<filter icon="terp-document-new" name="draft" string="New" domain="[('state','=','draft')]" help="New Purchase Requisition"/>
<filter icon="terp-camera_test" string="Sent to Suppliers" domain="[('state','=','in_progress')]" help="Purchase Requisition in negociation"/>
<filter icon="terp-dialog-close" string="Purchase Done" domain="[('state','=','done')]" help="Current Purchase Requisition"/>
<filter icon="terp-document-new" name="draft" string="New" domain="[('state','=','draft')]" help="New Call for Bids"/>
<filter icon="terp-camera_test" string="Confirmed" domain="[('state','=','in_progress')]" help="Call for Bids in negociation"/>
<filter icon="terp-dialog-close" string="Closed Bids" domain="[('state','=','open')]" help="Call for Bids where bids are closed"/>
<separator/>
<filter icon="terp-personal-" string="Unassigned" domain="[('user_id','=', False)]" help="Unassigned Requisition"/>
<field name="user_id" />
@ -142,7 +157,7 @@
<filter string="Responsible" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Source" icon="terp-gtk-jump-to-rtl" domain="[]" context="{'group_by':'origin'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<filter string="Start Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}"/>
<filter string="Start Date" icon="terp-go-month" domain="[]" context="{'group_by':'ordering_date'}"/>
<filter string="End Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_end'}"/>
</group>
</search>
@ -151,7 +166,7 @@
<record model="ir.actions.act_window" id="action_purchase_requisition">
<field name="name">Purchase Requisitions</field>
<field name="name">Calls for Bids</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">purchase.requisition</field>
<field name="view_type">form</field>
@ -160,13 +175,13 @@
<field name="search_view_id" ref="view_purchase_requisition_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to start a new purchase requisition process.
Click to start a new Call for Bids process.
</p><p>
A purchase requisition is the step before a request for quotation.
In a purchase requisition (or purchase tender), you can record the
products you need to buy and trigger the creation of RfQs to
suppliers. After the negotiation, once you have reviewed all the
supplier's offers, you can validate some and cancel others.
A Call for Bids is a procedure for generating competing offers from
different bidders. In the call for bids, you can record the
products you need to buy and generate the creation of RfQs to
suppliers. Once the bids have been registered, you can review and
compare them and you can validate some and cancel others.
</p>
</field>
</record>
@ -206,5 +221,39 @@
res_model="purchase.order"
src_model="purchase.requisition"/>
<record id="purchase_order_line_tree_tender" model="ir.ui.view">
<field name="name">purchase.order.line.tree.tender</field>
<field name="model">purchase.order.line</field>
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<tree string="Purchase Order Lines" create="false" colors="blue:state == 'confirmed';gray:state == 'cancel'">
<field name="name"/>
<field name="partner_id" string="Supplier" />
<field name="product_id"/>
<field name="price_unit"/>
<field name="product_qty"/>
<field name="quantity_bid"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="price_subtotal"/>
<field name="state" invisible="1"/>
<field name="invoiced" invisible="1"/>
<button name="action_draft" states="confirmed" type="object" string="Cancel Choice" icon="gtk-cancel"/>
<button name="%(action_bid_line_qty)d" type="action" states="draft" string="Change Quantity" icon="gtk-ok"/>
<button name="action_confirm" states="draft" type="object" string="Confirm Order" icon="gtk-apply"/>
</tree>
</field>
</record>
<record id="purchase_line_tree" model="ir.actions.act_window">
<field name="name">Bid Lines</field>
<field name="res_model">purchase.order.line</field>
<field name="context">{"search_default_groupby_product" : True,}</field>
<field name="view_type">form</field>
<field name="view_mode">tree_purchase_order_line_compare</field>
<field name="view_id" ref="purchase_order_line_tree_tender"/>
<field name="search_view_id" ref="purchase.purchase_order_line_search"/>
</record>
</data>
</openerp>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="purchase_requisition_workflow" model="workflow">
<field name="name">Purchase Requisition Basic Workflow</field>
<field name="osv">purchase.requisition</field>
<field name="on_create">True</field>
</record>
<record id="act_draft" model="workflow.activity">
<field name="wkf_id" ref="purchase_requisition_workflow"/>
<field name="flow_start">True</field>
<field name="name">draft</field>
</record>
<record id="act_sent" model="workflow.activity">
<field name="wkf_id" ref="purchase_requisition_workflow"/>
<field name="name">sent</field>
<field name="kind">function</field>
<field name="action">tender_in_progress()</field>
</record>
<record id="act_open" model="workflow.activity">
<field name="wkf_id" ref="purchase_requisition_workflow"/>
<field name="name">open</field>
<field name="kind">function</field>
<field name="action">tender_open()</field>
</record>
<record id="act_cancel" model="workflow.activity">
<field name="wkf_id" ref="purchase_requisition_workflow"/>
<field name="name">cancel</field>
<field name="kind">function</field>
<field name="flow_stop">True</field>
<field name="action">tender_cancel()</field>
</record>
<record id="act_done" model="workflow.activity">
<field name="wkf_id" ref="purchase_requisition_workflow"/>
<field name="name">done</field>
<field name="action">tender_done()</field>
<field name="kind">function</field>
<field name="flow_stop">True</field>
</record>
<record id="trans_draft_sent" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_sent"/>
<field name="signal">sent_suppliers</field>
</record>
<record id="trans_sent_open" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<field name="act_to" ref="act_open"/>
<field name="signal">open_bid</field>
</record>
<record id="trans_draft_cancel" model="workflow.transition">
<field name="act_from" ref="act_draft"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">cancel_requisition</field>
</record>
<record id="trans_open_done" model="workflow.transition">
<field name="act_from" ref="act_open"/>
<field name="act_to" ref="act_done"/>
<field name="signal">done</field>
</record>
<record id="trans_sent_cancel" model="workflow.transition">
<field name="act_from" ref="act_sent"/>
<field name="act_to" ref="act_cancel"/>
<field name="signal">cancel_requisition</field>
</record>
</data>
</openerp>

View File

@ -1,6 +1,6 @@
<?xml version="1.0"?>
<document filename="Purchase for Requisitions.pdf">
<template title="Purchase for Requisitions" author="OpenERP S.A.(sales@openerp.com)" allowSplitting="20">
<document filename="Call for Bids.pdf">
<template title="Call for Bids" author="OpenERP S.A.(sales@openerp.com)" allowSplitting="20">
<pageTemplate id="first">
<frame id="first" x1="34.0" y1="28.0" width="530" height="786"/>
</pageTemplate>
@ -50,6 +50,7 @@
<lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/>
<lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/>
<lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/>
<lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/>
</blockTableStyle>
<blockTableStyle id="Table5">
<blockAlignment value="LEFT"/>
@ -57,6 +58,7 @@
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/>
</blockTableStyle>
<blockTableStyle id="Table6">
<blockAlignment value="LEFT"/>
@ -132,20 +134,20 @@
<para style="terp_default_8">
<font color="white"> </font>
</para>
<para style="terp_header">Purchase for Requisitions [[ requisition.name ]]</para>
<para style="terp_header">Call for Bid [[ requisition.name ]]</para>
<para style="terp_default_8">
<font color="white"> </font>
</para>
<blockTable colWidths="159.0,159.0,106.0,106.0" style="Table2">
<tr>
<td>
<para style="terp_tblheader_General_Centre">Requisition Reference</para>
<para style="terp_tblheader_General_Centre">Call for Bid Reference</para>
</td>
<td>
<para style="terp_tblheader_General_Centre">Requisition Date</para>
<para style="terp_tblheader_General_Centre">Date</para>
</td>
<td>
<para style="terp_tblheader_General_Centre">Type</para>
<para style="terp_tblheader_General_Centre">Selection Type</para>
</td>
<td>
<para style="terp_tblheader_General_Centre">Source</para>
@ -171,8 +173,8 @@
<para style="terp_default_8">
<font color="white"> </font>
</para>
<para style="terp_tblheader_Details">Product Detail [[ requisition.line_ids==[] and removeParentNode('para') ]]</para>
<blockTable colWidths="375.0,71.0,84.0" style="Table4">
<para style="terp_tblheader_Details">Products [[ requisition.line_ids==[] and removeParentNode('para') ]]</para>
<blockTable colWidths="275.0,71.0,84.0,100.0" style="Table4">
<tr>
<td>
<para style="terp_tblheader_Details">Description [[ requisition.line_ids==[] and removeParentNode('blockTable') ]]</para>
@ -183,11 +185,14 @@
<td>
<para style="terp_tblheader_Details_Centre">Product UoM</para>
</td>
<td>
<para style="terp_tblheader_Details_Centre">Scheduled Date</para>
</td>
</tr>
</blockTable>
<section>
<para style="terp_default_2">[[ requisition.line_ids==[] and removeParentNode('section') ]] [[ repeatIn(requisition.line_ids,'line_ids') ]]</para>
<blockTable colWidths="375.0,71.0,84.0" style="Table5">
<blockTable colWidths="275.0,71.0,84.0,100.0" style="Table5">
<tr>
<td>
<para style="terp_default_9">[ [[ (line_ids.product_id and line_ids.product_id.code) or '' ]] ] [[ (line_ids.product_id and line_ids.product_id.name) or '' ]]</para>
@ -198,23 +203,26 @@
<td>
<para style="terp_default_Centre_9">[[ (line_ids.product_uom_id and line_ids.product_uom_id.category_id and line_ids.product_uom_id.category_id.name) or '' ]]</para>
</td>
<td>
<para style="terp_default_Centre_9">[[ formatLang(line_ids.schedule_date,date='True') ]]</para>
</td>
</tr>
</blockTable>
</section>
<para style="terp_default_8">
<font color="white"> </font>
</para>
<para style="terp_tblheader_Details">Quotation Detail [[ requisition.purchase_ids ==[] and removeParentNode('para') ]]</para>
<para style="terp_tblheader_Details">Requests for Quotation [[ requisition.purchase_ids ==[] and removeParentNode('para') ]]</para>
<blockTable colWidths="337.0,96.0,97.0" style="Table6">
<tr>
<td>
<para style="terp_tblheader_Details">Supplier [[ requisition.purchase_ids ==[] and removeParentNode('blockTable') ]]</para>
</td>
<td>
<para style="terp_tblheader_Details_Centre">Date Ordered</para>
<para style="terp_tblheader_Details_Centre">Date</para>
</td>
<td>
<para style="terp_tblheader_Details_Centre">Order Reference</para>
<para style="terp_tblheader_Details_Centre">Reference</para>
</td>
</tr>
</blockTable>
@ -236,4 +244,4 @@
</section>
</pto>
</story>
</document>
</document>

View File

@ -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 = $("<button type='button' class='oe_button oe_highlight oe_generate_po'>Generate PO</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();
});
},
});
}

View File

@ -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.
-

View File

@ -20,6 +20,7 @@
##############################################################################
import purchase_requisition_partner
import bid_line_qty
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 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:

View File

@ -0,0 +1,31 @@
<openerp>
<data>
<record id="view_bid_line_qty" model="ir.ui.view">
<field name="name">Quantity to Purchase</field>
<field name="model">bid.line.qty</field>
<field name="arch" type="xml">
<form string="Quantity" version="7.0">
<group>
<field name="qty" context="{'default_qty': 0}"/>
</group>
<footer>
<button name="change_qty" string="Change Quantity" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_bid_line_qty" model="ir.actions.act_window">
<field name="name">Bid Line Qty</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">bid.line.qty</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="context">{'record_id' : active_id}</field>
<field name="target">new</field>
</record>
</data>
</openerp>

View File

@ -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:

View File

@ -2,15 +2,15 @@
<data>
<record id="view_purchase_requisition_partner" model="ir.ui.view">
<field name="name">Purchase Requisition</field>
<field name="name">Choose Supplier</field>
<field name="model">purchase.requisition.partner</field>
<field name="arch" type="xml">
<form string="Purchase Requisition" version="7.0">
<form string="Choose Supplier" version="7.0">
<group>
<field name="partner_id" context="{'default_supplier': 1, 'default_customer': 0}"/>
</group>
<footer>
<button name="create_order" string="Create Quotation" type="object" class="oe_highlight"/>
<button name="create_order" string="Create Request for Quotation" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
@ -19,7 +19,7 @@
</record>
<record id="action_purchase_requisition_partner" model="ir.actions.act_window">
<field name="name">Purchase Requisition</field>
<field name="name">Choose Supplier</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">purchase.requisition.partner</field>
<field name="view_type">form</field>
@ -27,5 +27,6 @@
<field name="context">{'record_id' : active_id}</field>
<field name="target">new</field>
</record>
</data>
</openerp>