[IMP] purchase_requisition: blanket orders and usability cleaning
Improved usability & code cleaning:
- Support for blanket orders, in addition of purchase tenders
("Purchase Agreements")
- Create a RfQ in one click, no wizard anymore
- Control of quantities really purchased
- Better control of side-effects thanks to purchase requisition types
- JS code removed (not necessary)
- Workflow removed
Closes #11666
This commit is contained in:
committed by
Nicolas Martinelli
parent
3938fb27e5
commit
ff110b0ed8
@@ -34,12 +34,6 @@ class purchase_config_settings(osv.osv_memory):
|
||||
help="Calls for tenders are used when you want to generate requests for quotations to several vendors for a given set of products.\n"
|
||||
"You can configure per product if you directly do a Request for Quotation "
|
||||
"to one vendor or if you want a Call for Tenders to compare offers from several vendors."),
|
||||
'group_advance_purchase_requisition': fields.selection([
|
||||
(0, 'Simple call for tender (only choose from one RFQ)'),
|
||||
(1, 'Advanced call for tender (choose products from different RFQ)')
|
||||
], "Advanced Calls for Tenders",
|
||||
implied_group='purchase.group_advance_bidding',
|
||||
help="""In the process of a public tendering, you can compare the tender lines and choose for each requested product which quantity you will buy from each bid."""),
|
||||
'group_warning': fields.selection([
|
||||
(0, 'All the products and the customers can be used in purchase orders'),
|
||||
(1, 'An informative or blocking warning can be set on a product or a customer')
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
<field name="group_uom" widget="radio"/>
|
||||
<field name="group_costing_method" widget="radio"/>
|
||||
<field name="module_purchase_requisition" widget="radio"/>
|
||||
<field name="group_advance_purchase_requisition" widget="radio"
|
||||
attrs="{'invisible': [('module_purchase_requisition', '=', 0)]}"/>
|
||||
<field name="group_manage_vendor_price" widget="radio"/>
|
||||
<field name="group_warning" widget="radio"/>
|
||||
</group>
|
||||
|
||||
@@ -25,11 +25,6 @@
|
||||
<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>
|
||||
|
||||
<record id="group_manage_vendor_price" model="res.groups">
|
||||
<field name="name">Manage Vendor Price</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import purchase_requisition
|
||||
import wizard
|
||||
|
||||
@@ -14,16 +14,13 @@ keep track and order all your purchase orders.
|
||||
""",
|
||||
'depends' : ['purchase'],
|
||||
'demo': ['purchase_requisition_demo.xml'],
|
||||
'data': ['views/purchase_requisition.xml',
|
||||
'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',
|
||||
'views/report_purchaserequisition.xml',
|
||||
'data': [
|
||||
'security/purchase_tender.xml',
|
||||
'purchase_requisition_data.xml',
|
||||
'purchase_requisition_view.xml',
|
||||
'purchase_requisition_report.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/report_purchaserequisition.xml',
|
||||
],
|
||||
'auto_install': False,
|
||||
'test': [
|
||||
@@ -32,8 +29,5 @@ keep track and order all your purchase orders.
|
||||
'test/cancel_purchase_requisition.yml',
|
||||
'test/purchase_requisition.yml',
|
||||
],
|
||||
'qweb': [
|
||||
'static/src/xml/purchase_requisition.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
|
||||
@@ -3,322 +3,153 @@
|
||||
|
||||
from datetime import datetime
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
from openerp.tools.translate import _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
class purchase_requisition_type(osv.osv):
|
||||
_name = "purchase.requisition.type"
|
||||
_description = "Purchase Agreement Type"
|
||||
_order = "sequence"
|
||||
_columns = {
|
||||
'name': fields.char('Agreement Type', required=True, translate=True),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'exclusive': fields.selection([
|
||||
('exclusive', 'Select only one RFQ (exclusive)'), ('multiple', 'Select multiple RFQ')],\
|
||||
'Agreement Selection Type', required=True,\
|
||||
help="""Select only one RFQ (exclusive): when a purchase order is confirmed, cancel the remaining purchase order.\n
|
||||
Select multiple RFQ: allows multiple purchase orders. On confirmation of a purchase order it does not cancel the remaining orders"""),
|
||||
'quantity_copy': fields.selection([
|
||||
('copy', 'Use quantities of agreement'), ('none', 'Set quantities manually')],\
|
||||
'Quantities', required=True),
|
||||
'line_copy': fields.selection([
|
||||
('copy', 'Use lines of agreement'), ('none', 'Do not create RfQ lines automatically')],\
|
||||
'Lines', required=True),
|
||||
}
|
||||
_defaults = {
|
||||
'exclusive': 'multiple',
|
||||
'quantity_copy': 'none',
|
||||
'line_copy': 'copy',
|
||||
'sequence': 1
|
||||
}
|
||||
|
||||
class purchase_requisition(osv.osv):
|
||||
_name = "purchase.requisition"
|
||||
_description = "Purchase Requisition"
|
||||
_inherit = ['mail.thread', 'ir.needaction_mixin']
|
||||
_order = "id desc"
|
||||
|
||||
def _get_po_line(self, cr, uid, ids, field_names, arg=None, context=None):
|
||||
result = dict((res_id, []) for res_id in 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]
|
||||
def _compute_orders_number(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict.fromkeys(ids, 0)
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
result[order.id] = len(order.purchase_ids)
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Call for Tenders Reference', required=True, copy=False),
|
||||
'name': fields.char('Agreement Reference', required=True, copy=False),
|
||||
'origin': fields.char('Source Document'),
|
||||
'ordering_date': fields.date('Scheduled Ordering Date'),
|
||||
'date_end': fields.datetime('Tender Closing Deadline'),
|
||||
'schedule_date': fields.date('Scheduled Date', select=True, help="The expected and scheduled delivery date where all the products are received"),
|
||||
'order_count': fields.function(_compute_orders_number, 'Number of Orders', type='integer'),
|
||||
'vendor_id': fields.many2one('res.partner', string="Vendor"),
|
||||
'type_id': fields.many2one('purchase.requisition.type', string="Agreement Type", required=True),
|
||||
'ordering_date': fields.date('Ordering Date'),
|
||||
'date_end': fields.datetime('Agreement Deadline'),
|
||||
'schedule_date': fields.date('Delivery Date', select=True, help="The expected and scheduled delivery date where all the products are received"),
|
||||
'user_id': fields.many2one('res.users', 'Responsible'),
|
||||
'exclusive': fields.selection([('exclusive', 'Select only one RFQ (exclusive)'), ('multiple', 'Select multiple RFQ')], 'Tender 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)]}),
|
||||
'po_line_ids': fields.function(_get_po_line, method=True, type='one2many', relation='purchase.order.line', string='Products by vendor'),
|
||||
'line_ids': fields.one2many('purchase.requisition.line', 'requisition_id', 'Products to Purchase', states={'done': [('readonly', True)]}, copy=True),
|
||||
'procurement_id': fields.many2one('procurement.order', 'Procurement', ondelete='set null', copy=False),
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse'),
|
||||
'state': fields.selection([('draft', 'Draft'), ('in_progress', 'Confirmed'),
|
||||
('open', 'Bid Selection'), ('done', 'PO Created'),
|
||||
('open', 'Bid Selection'), ('done', 'Done'),
|
||||
('cancel', 'Cancelled')],
|
||||
'Status', track_visibility='onchange', required=True,
|
||||
copy=False),
|
||||
'multiple_rfq_per_supplier': fields.boolean('Multiple RFQ per vendor'),
|
||||
'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', required=True),
|
||||
|
||||
}
|
||||
|
||||
def _get_picking_in(self, cr, uid, context=None):
|
||||
obj_data = self.pool.get('ir.model.data')
|
||||
return obj_data.get_object_reference(cr, uid, 'stock', 'picking_type_in')[1]
|
||||
|
||||
def _get_type_id(self, cr, uid, context=None):
|
||||
types = self.pool.get('purchase.requisition.type').search(cr, uid, [], context=context, limit=1)
|
||||
return types[0] if types else False
|
||||
|
||||
_defaults = {
|
||||
'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,
|
||||
'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').next_by_code(cr, uid, 'purchase.order.requisition'),
|
||||
'type_id': _get_type_id,
|
||||
'picking_type_id': _get_picking_in,
|
||||
}
|
||||
|
||||
def tender_cancel(self, cr, uid, ids, context=None):
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
purchase_order_obj = self.pool.get('purchase.order')
|
||||
# try to set all associated quotations to cancel state
|
||||
for tender in self.browse(cr, uid, ids, context=context):
|
||||
for purchase_order in tender.purchase_ids:
|
||||
purchase_order_obj.button_cancel(cr, uid, [purchase_order.id], context=context)
|
||||
purchase_order_obj.message_post(cr, uid, [purchase_order.id], body=_('Cancelled by the tender associated to this quotation.'), context=context)
|
||||
return self.write(cr, uid, ids, {'state': 'cancel'})
|
||||
for requisition in self.browse(cr, uid, ids, context=context):
|
||||
for po in requisition.purchase_ids:
|
||||
purchase_order_obj.button_cancel(cr, uid, [po.id], context=context)
|
||||
purchase_order_obj.message_post(cr, uid, [po.id], body=_('Cancelled by the agreement associated to this quotation.'), context=context)
|
||||
return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
|
||||
|
||||
def tender_in_progress(self, cr, uid, ids, context=None):
|
||||
def action_in_progress(self, cr, uid, ids, context=None):
|
||||
if not all(obj.line_ids for obj in self.pool['purchase.requisition'].browse(cr, uid, ids, context=context)):
|
||||
raise UserError(_('You can not confirm call because there is no product line.'))
|
||||
raise UserError(_('You cannot confirm call because there is no product line.'))
|
||||
return self.write(cr, uid, ids, {'state': 'in_progress'}, context=context)
|
||||
|
||||
def tender_open(self, cr, uid, ids, context=None):
|
||||
def action_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):
|
||||
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 action_draft(self, cr, uid, ids, context=None):
|
||||
return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
|
||||
|
||||
def tender_done(self, cr, uid, ids, context=None):
|
||||
def action_done(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Generate all purchase order based on selected lines, should only be called on one agreement at a time
|
||||
"""
|
||||
requisitions = self.browse(cr, uid, ids, context=context)
|
||||
if requisitions.mapped('purchase_ids') and any(r.state in ['draft', 'sent', 'to approve'] for r in requisitions.mapped('purchase_ids')):
|
||||
raise UserError(_('You have to cancel or validate every RfQ before closing the purchase requisition.'))
|
||||
return self.write(cr, uid, ids, {'state': 'done'}, context=context)
|
||||
|
||||
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 vendor 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_lines = self.browse(cr, uid, ids, context=context)[0].po_line_ids
|
||||
res['context'] = {
|
||||
'search_default_groupby_product': True,
|
||||
'search_default_hide_cancelled': True,
|
||||
'tender_id': ids[0],
|
||||
}
|
||||
res['domain'] = [('id', 'in', [line.id for line in po_lines])]
|
||||
return res
|
||||
|
||||
def open_rfq(self, cr, uid, ids, context=None):
|
||||
""" This opens rfq view to view all quotations associated to the call for tenders
|
||||
@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):
|
||||
return {
|
||||
'origin': requisition.name,
|
||||
'date_order': requisition.date_end or fields.datetime.now(),
|
||||
'partner_id': supplier.id,
|
||||
'currency_id': requisition.company_id and requisition.company_id.currency_id.id,
|
||||
'company_id': requisition.company_id.id,
|
||||
'fiscal_position_id': self.pool.get('account.fiscal.position').get_fiscal_position(cr, uid, supplier.id, context=context),
|
||||
'requisition_id': requisition.id,
|
||||
'notes': requisition.description,
|
||||
'picking_type_id': requisition.picking_type_id.id
|
||||
}
|
||||
|
||||
def _prepare_purchase_order_line(self, cr, uid, requisition, requisition_line, purchase_id, supplier, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
po_obj = self.pool.get('purchase.order')
|
||||
po_line_obj = self.pool.get('purchase.order.line')
|
||||
product_uom = self.pool.get('product.uom')
|
||||
product = requisition_line.product_id
|
||||
default_uom_po_id = product.uom_po_id.id
|
||||
ctx = context.copy()
|
||||
ctx['tz'] = requisition.user_id.tz
|
||||
date_order = requisition.ordering_date and fields.date.date_to_datetime(self, cr, uid, requisition.ordering_date, context=ctx) or fields.datetime.now()
|
||||
qty = product_uom._compute_qty(cr, uid, requisition_line.product_uom_id.id, requisition_line.product_qty, default_uom_po_id)
|
||||
|
||||
taxes = product.supplier_taxes_id
|
||||
fpos = supplier.property_account_position_id
|
||||
taxes_id = fpos.map_tax(taxes).ids if fpos else []
|
||||
|
||||
po = po_obj.browse(cr, uid, [purchase_id], context=context)
|
||||
seller = requisition_line.product_id._select_seller(
|
||||
requisition_line.product_id,
|
||||
partner_id=supplier,
|
||||
quantity=qty,
|
||||
date=date_order and date_order[:10],
|
||||
uom_id=product.uom_po_id)
|
||||
|
||||
price_unit = seller.price if seller else 0.0
|
||||
if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id:
|
||||
price_unit = seller.currency_id.compute(price_unit, po.currency_id)
|
||||
|
||||
date_planned = po_line_obj._get_date_planned(cr, uid, seller, po=po, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
product_lang = requisition_line.product_id.with_context({
|
||||
'lang': supplier.lang,
|
||||
'partner_id': supplier.id,
|
||||
})
|
||||
name = product_lang.display_name
|
||||
if product_lang.description_purchase:
|
||||
name += '\n' + product_lang.description_purchase
|
||||
|
||||
vals = {
|
||||
'name': name,
|
||||
'order_id': purchase_id,
|
||||
'product_qty': qty,
|
||||
'product_id': product.id,
|
||||
'product_uom': default_uom_po_id,
|
||||
'price_unit': price_unit,
|
||||
'date_planned': date_planned,
|
||||
'taxes_id': [(6, 0, taxes_id)],
|
||||
'account_analytic_id': requisition_line.account_analytic_id.id,
|
||||
}
|
||||
|
||||
return vals
|
||||
|
||||
def make_purchase_order(self, cr, uid, ids, partner_id, context=None):
|
||||
"""
|
||||
Create New RFQ for Vendor
|
||||
"""
|
||||
context = dict(context or {})
|
||||
assert partner_id, 'Vendor should be specified'
|
||||
purchase_order = self.pool.get('purchase.order')
|
||||
purchase_order_line = self.pool.get('purchase.order.line')
|
||||
res_partner = self.pool.get('res.partner')
|
||||
supplier = res_partner.browse(cr, uid, partner_id, context=context)
|
||||
res = {}
|
||||
for requisition in self.browse(cr, uid, ids, context=context):
|
||||
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 UserError(_('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, 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:
|
||||
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 tendering process, else return False
|
||||
|
||||
args : 'quotation' must be a browse record
|
||||
"""
|
||||
for line in quotation.order_line:
|
||||
if line.product_qty != line.quantity_tendered:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _prepare_po_from_tender(self, cr, uid, tender, context=None):
|
||||
""" Prepare the values to write in the purchase order
|
||||
created from a tender.
|
||||
|
||||
:param tender: the source tender from which we generate a purchase order
|
||||
"""
|
||||
return {'order_line': [],
|
||||
'requisition_id': tender.id,
|
||||
'origin': tender.name}
|
||||
|
||||
def _prepare_po_line_from_tender(self, cr, uid, tender, line, purchase_id, context=None):
|
||||
""" Prepare the values to write in the purchase order line
|
||||
created from a line of the tender.
|
||||
|
||||
:param tender: the source tender from which we generate a purchase order
|
||||
:param line: the source tender's line from which we generate a line
|
||||
:param purchase_id: the id of the new purchase
|
||||
"""
|
||||
return {'product_qty': line.quantity_tendered,
|
||||
'order_id': purchase_id}
|
||||
|
||||
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
|
||||
"""
|
||||
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 UserError(_('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.quantity_tendered > 0:
|
||||
confirm = True
|
||||
break
|
||||
if not confirm:
|
||||
raise UserError(_('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)):
|
||||
#Set PO state to confirm
|
||||
po.button_confirm(cr, uid, [quotation.id], context=context)
|
||||
|
||||
#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.quantity_tendered > 0 and po_line.order_id.state in ['draft', 'sent', 'to approve']:
|
||||
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 = dict(context or {}, 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]
|
||||
vals = self._prepare_po_from_tender(cr, uid, tender, context=context)
|
||||
new_po = po.copy(cr, uid, quotation_id, default=vals, context=context)
|
||||
#duplicate po_line and change product_qty if needed and associate them to newly created PO
|
||||
for line in product_line:
|
||||
vals = self._prepare_po_line_from_tender(cr, uid, tender, line, new_po, context=context)
|
||||
poline.copy(cr, uid, line.id, default=vals, context=context)
|
||||
#use workflow to set new PO state to confirm
|
||||
po.button_confirm(cr, uid, [new_po], context=context)
|
||||
|
||||
#cancel other orders
|
||||
self.cancel_unconfirmed_quotations(cr, uid, tender, context=context)
|
||||
|
||||
#set tender to state done
|
||||
self.signal_workflow(cr, uid, [tender.id], 'done')
|
||||
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', 'to approve']:
|
||||
self.pool.get('purchase.order').button_cancel(cr, uid, [quotation.id], context=context)
|
||||
po.message_post(cr, uid, [quotation.id], body=_('Cancelled by the call for tenders associated to this request for quotation.'), context=context)
|
||||
return True
|
||||
|
||||
|
||||
class purchase_requisition_line(osv.osv):
|
||||
_name = "purchase.requisition.line"
|
||||
_description = "Purchase Requisition Line"
|
||||
_rec_name = 'product_id'
|
||||
|
||||
def _compute_ordered_qty(self, cr, uid, ids, fieldnames, args, context=None):
|
||||
result = dict.fromkeys(ids, 0)
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
total = 0.0
|
||||
for po in line.requisition_id.purchase_ids.filtered(lambda r: r.state in ['purchase', 'done']):
|
||||
for po_line in po.order_line.filtered(lambda r: r.product_id == line.product_id):
|
||||
if po_line.product_uom != line.product_uom_id:
|
||||
total += self.pool.get('product.uom')._compute_qty_obj(po_line.product_uom, po_line.product_qty, line.product_uom_id)
|
||||
else:
|
||||
total += po_line.product_qty
|
||||
result[line.id] = total
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok', '=', True)]),
|
||||
'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok', '=', True)], required=True),
|
||||
'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', 'Call for Tenders', ondelete='cascade'),
|
||||
'price_unit': fields.float('Unit Price', digits_compute=dp.get_precision('Product Price')),
|
||||
'qty_ordered': fields.function(_compute_ordered_qty, string='Ordered Quantities', type='float'),
|
||||
'requisition_id': fields.many2one('purchase.requisition', 'Purchase Agreement', 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, parent_analytic_account, analytic_account, parent_date, date, 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
|
||||
@@ -338,62 +169,117 @@ class purchase_requisition_line(osv.osv):
|
||||
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.requisition.line', context=c),
|
||||
}
|
||||
|
||||
|
||||
class purchase_order(osv.osv):
|
||||
_inherit = "purchase.order"
|
||||
|
||||
_columns = {
|
||||
'requisition_id': fields.many2one('purchase.requisition', 'Call for Tenders', copy=False),
|
||||
'requisition_id': fields.many2one('purchase.requisition', 'Purchase Agreement', copy=False),
|
||||
}
|
||||
|
||||
def _onchange_requisition_id(self, cr, uid, ids, requisition_id, partner_id, context=None):
|
||||
if not requisition_id:
|
||||
return {}
|
||||
|
||||
requisition = self.pool.get('purchase.requisition').browse(cr, uid, [requisition_id], context=context)
|
||||
if partner_id:
|
||||
partner = self.pool.get('res.partner').browse(cr, uid, [partner_id], context=context)
|
||||
else:
|
||||
partner = requisition.vendor_id
|
||||
payment_term = partner.property_supplier_payment_term_id
|
||||
currency = partner.property_purchase_currency_id or requisition.company_id.currency_id
|
||||
|
||||
fpos_obj = self.pool.get('account.fiscal.position')
|
||||
fpos = fpos_obj.get_fiscal_position(cr, uid, partner.id, context=context)
|
||||
fpos = fpos_obj.browse(cr, uid, [fpos], context=context)
|
||||
|
||||
value = {
|
||||
'partner_id': partner.id,
|
||||
'fiscal_position_id': fpos.id,
|
||||
'payment_term_id': payment_term.id,
|
||||
'company_id': requisition.company_id.id,
|
||||
'currency_id': currency.id,
|
||||
'origin': requisition.name,
|
||||
'partner_ref': requisition.name, # to control vendor bill based on agreement reference
|
||||
'notes': requisition.description,
|
||||
'date_order': requisition.date_end or fields.datetime.now(),
|
||||
'picking_type_id': requisition.picking_type_id.id,
|
||||
}
|
||||
|
||||
if requisition.type_id.line_copy != 'copy':
|
||||
return {'value': value}
|
||||
|
||||
# Create PO lines if necessary
|
||||
order_lines = []
|
||||
for line in requisition.line_ids:
|
||||
# Compute name
|
||||
product_lang = line.product_id.with_context({
|
||||
'lang': partner.lang,
|
||||
'partner_id': partner.id,
|
||||
})
|
||||
name = product_lang.display_name
|
||||
if product_lang.description_purchase:
|
||||
name += '\n' + product_lang.description_purchase
|
||||
|
||||
# Compute taxes
|
||||
if fpos:
|
||||
taxes_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos,\
|
||||
line.product_id.supplier_taxes_id.filtered(lambda r: r.company_id == requisition.company_id), context=context)
|
||||
else:
|
||||
taxes_ids = line.product_id.supplier_taxes_id.filtered(lambda r: r.company_id == requisition.company_id).ids
|
||||
|
||||
# Compute quantity and price_unit
|
||||
if requisition.type_id.quantity_copy != 'copy':
|
||||
product_qty = 0
|
||||
price_unit = line.price_unit
|
||||
elif line.product_uom_id != line.product_id.uom_po_id:
|
||||
product_uom_obj = self.pool.get('product.uom')
|
||||
product_qty = product_uom_obj._compute_qty_obj(
|
||||
cr, uid, line.product_uom_id, line.product_qty, line.product_id.uom_po_id, context=context)
|
||||
price_unit = product_uom_obj._compute_price(
|
||||
cr, uid, line.product_uom_id.id, line.price_unit, to_uom_id=line.product_id.uom_po_id.id)
|
||||
else:
|
||||
product_qty = line.product_qty
|
||||
price_unit = line.price_unit
|
||||
|
||||
# Compute price_unit in appropriate currency
|
||||
if requisition.company_id.currency_id != currency:
|
||||
price_unit = requisition.company_id.currency_id.compute(price_unit, currency)
|
||||
|
||||
# Create PO line
|
||||
order_lines.append((0, 0, {
|
||||
'name': name,
|
||||
'product_id': line.product_id.id,
|
||||
'product_uom': line.product_id.uom_po_id.id,
|
||||
'product_qty': product_qty,
|
||||
'price_unit': price_unit,
|
||||
'taxes_id': [(6, 0, taxes_ids)],
|
||||
'date_planned': requisition.schedule_date or datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'procurement_ids': [(6, 0, [requisition.procurement_id.id])] if requisition.procurement_id else False,
|
||||
'account_analytic_id': line.account_analytic_id and line.account_analytic_id.id or False,
|
||||
}))
|
||||
value['order_line'] = order_lines
|
||||
|
||||
return {'value': value}
|
||||
|
||||
def button_confirm(self, cr, uid, ids, context=None):
|
||||
res = super(purchase_order, self).button_confirm(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'):
|
||||
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':
|
||||
proc_obj.write(cr, uid, proc_ids, {'purchase_id': po.id})
|
||||
order.button_cancel()
|
||||
po.requisition_id.tender_done(context=context)
|
||||
for element in po.order_line:
|
||||
if not element.quantity_tendered:
|
||||
element.write({'quantity_tendered': element.product_qty})
|
||||
if po.requisition_id and (po.requisition_id.type_id.exclusive == 'exclusive'):
|
||||
others_po = po.requisition_id.mapped('purchase_ids').filtered(lambda r: r.id != po.id)
|
||||
others_po.button_cancel()
|
||||
po.requisition_id.action_done(context=context)
|
||||
return res
|
||||
|
||||
|
||||
class purchase_order_line(osv.osv):
|
||||
_inherit = 'purchase.order.line'
|
||||
|
||||
_columns = {
|
||||
'quantity_tendered': fields.float('Quantity Tendered', digits_compute=dp.get_precision('Product Unit of Measure'), help="Technical field for not loosing the initial information about the quantity proposed in the tender", oldname='quantity_bid'),
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
def button_confirm(self, cr, uid, ids, context=None):
|
||||
for element in self.browse(cr, uid, ids, context=context):
|
||||
self.write(cr, uid, element.id, {'quantity_tendered': element.product_qty}, context=context)
|
||||
return True
|
||||
|
||||
def button_cancel(self, cr, uid, ids, context=None):
|
||||
self.write(cr, uid, ids, {'quantity_tendered': 0}, context=context)
|
||||
return True
|
||||
|
||||
|
||||
class product_template(osv.osv):
|
||||
_inherit = 'product.template'
|
||||
|
||||
_columns = {
|
||||
'purchase_requisition': fields.selection(
|
||||
[('rfq', 'Create a draft purchase order'),
|
||||
('tenders', 'Propose a call for tenders')],
|
||||
string='Procurement'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'purchase_requisition': 'rfq',
|
||||
}
|
||||
|
||||
@@ -6,5 +6,23 @@
|
||||
id="purchase_default_set"
|
||||
model="ir.values"
|
||||
name="set"/>
|
||||
|
||||
<record id="type_single" model="purchase.requisition.type">
|
||||
<field name="name">Blanket Order</field>
|
||||
<field name="sequence">3</field>
|
||||
<field name="quantity_copy">none</field>
|
||||
</record>
|
||||
<record id="type_multi" model="purchase.requisition.type">
|
||||
<field name="name">Purchase Tender</field>
|
||||
<field name="sequence">1</field>
|
||||
<field name="quantity_copy">copy</field>
|
||||
</record>
|
||||
|
||||
<record id="seq_purchase_requisition" model="ir.sequence">
|
||||
<field name="name">Purchase Requisition Order</field>
|
||||
<field name="code">purchase.order.requisition</field>
|
||||
<field name="prefix">TE</field>
|
||||
<field name="padding">5</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
</openerp>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
<record id="requisition1" model="purchase.requisition">
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="exclusive">exclusive</field>
|
||||
<field name="warehouse_id" ref="stock.stock_warehouse_shop0"/>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
<!-- Sequences for purchase.order -->
|
||||
<record id="seq_purchase_requisition" model="ir.sequence">
|
||||
<field name="name">Purchase Requisition Order</field>
|
||||
<field name="code">purchase.order.requisition</field>
|
||||
<field name="prefix">TE</field>
|
||||
<field name="padding">5</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -2,14 +2,66 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!-- Purchase Requisition Type -->
|
||||
|
||||
<record model="ir.ui.view" id="view_purchase_requisition_type_tree">
|
||||
<field name="name">purchase.requisition.type.tree</field>
|
||||
<field name="model">purchase.requisition.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Purchase Agreement Types">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="exclusive"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="view_purchase_requisition_type_form">
|
||||
<field name="name">purchase.requisition.type.form</field>
|
||||
<field name="model">purchase.requisition.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Purchase Agreement Types">
|
||||
<sheet>
|
||||
<group>
|
||||
<group string="Agreement Type">
|
||||
<field name="name"/>
|
||||
<field name="exclusive" widget="radio"/>
|
||||
</group>
|
||||
<group string="Data for new quotations">
|
||||
<field name="line_copy" widget="radio"/>
|
||||
<field name="quantity_copy" widget="radio"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="tender_type_action" model="ir.actions.act_window">
|
||||
<field name="name">Purchase Agreement Types</field>
|
||||
<field name="res_model">purchase.requisition.type</field>
|
||||
<field name="context">{}</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<menuitem
|
||||
id="menu_purchase_requisition_type"
|
||||
sequence="100"
|
||||
parent="purchase.menu_purchase_config"
|
||||
action="tender_type_action"/>
|
||||
|
||||
<!-- Purchase Orders -->
|
||||
|
||||
<record model="ir.ui.view" id="purchase_order_form_inherit">
|
||||
<field name="name">purchase.order.form.inherit</field>
|
||||
<field name="model">purchase.order</field>
|
||||
<field name="inherit_id" ref="purchase.purchase_order_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="payment_term_id" position="before">
|
||||
<field name="requisition_id"/>
|
||||
<field name="partner_ref" position="after">
|
||||
<field name="requisition_id" on_change="_onchange_requisition_id(requisition_id, partner_id)"
|
||||
domain="[('state', 'in', ('in_progress', 'open')), ('vendor_id', 'in', (partner_id, False))]"/>
|
||||
</field>
|
||||
<xpath expr="//field[@name='order_line']/tree" position="inside">
|
||||
<field name="procurement_ids" invisible="1"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -19,31 +71,55 @@
|
||||
<field name="inherit_id" ref="purchase.view_purchase_order_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//filter[@name='approved']" position="after">
|
||||
<filter string="Requisition" domain="[('requisition_id','!=',False)]" help="Purchase Orders with requisition"/>
|
||||
<filter string="Requisition" domain="[('requisition_id', '!=', False)]" help="Purchase Orders with requisition"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_purchase_requisition_to_so">
|
||||
<field name="name">Request for Quotation</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">purchase.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form,tree</field>
|
||||
<field name="domain">[('requisition_id','=',active_id)]</field>
|
||||
<field name="context">{"default_requisition_id":active_id}</field>
|
||||
</record>
|
||||
<record model="ir.actions.act_window" id="action_purchase_requisition_list">
|
||||
<field name="name">Request for Quotations</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">purchase.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('requisition_id','=',active_id)]</field>
|
||||
<field name="context">{"default_requisition_id":active_id}</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_purchase_requisition_form">
|
||||
<field name="name">purchase.requisition.form</field>
|
||||
<field name="model">purchase.requisition</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Call for Tenders">
|
||||
<header>
|
||||
<button name="sent_suppliers" states="draft" string="Confirm Call" class="oe_highlight"/>
|
||||
<button name="open_bid" states="in_progress" string="Close Call for Tenders" 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"}'/>
|
||||
<form string="Purchase Agreements">
|
||||
<header>
|
||||
<button name="%(action_purchase_requisition_to_so)d" type="action"
|
||||
string="New Quotation"
|
||||
attrs="{'invisible': [('state', '!=', 'open')]}"/>
|
||||
<button name="%(action_purchase_requisition_to_so)d" type="action"
|
||||
string="New Quotation" class="btn-primary"
|
||||
attrs="{'invisible': [('state', '!=', 'in_progress')]}"/>
|
||||
<button name="action_in_progress" states="draft" string="Confirm" type="object" class="btn-primary"/>
|
||||
<button name="action_open" states="in_progress" string="Validate" type="object" class="btn-primary"/>
|
||||
<button name="action_done" states="open" string="Done" type="object" class="btn-primary"/>
|
||||
<button name="action_draft" states="cancel" string="Reset to Draft" type="object"/>
|
||||
<button name="action_cancel" states="draft,in_progress" string="Cancel Call" type="object"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,in_progress,open,done"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="open_product_line" type="object" string="Product Lines" class="oe_stat_button" icon="fa-list"
|
||||
attrs="{'invisible': ['|', ('state', 'not in', ('open','done')), ('exclusive', '=', 'exclusive')]}" groups="purchase.group_advance_bidding"/>
|
||||
<button name="open_rfq" type="object" string="RFQs/Bids" class="oe_stat_button" icon="fa-list-alt"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}"/>
|
||||
<button name="%(action_purchase_requisition_list)d" type="action" class="oe_stat_button" icon="fa-list-alt"
|
||||
attrs="{'invisible': [('state', '=', 'draft')]}">
|
||||
<field name="order_count" widget="statinfo" string="RFQs/Orders"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
<label for="name" class="oe_edit_only oe_inline"/>
|
||||
@@ -53,15 +129,15 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="user_id" attrs="{'readonly': [('state','not in',('draft'))]}"/>
|
||||
<field name="exclusive" attrs="{'readonly': [('state','not in',('draft'))]}"/>
|
||||
<field name="user_id" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/>
|
||||
<field name="type_id" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}" widget="selection"/>
|
||||
<field name="vendor_id"/>
|
||||
<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_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="date_end" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/>
|
||||
<field name="ordering_date" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/>
|
||||
<field name="schedule_date" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/>
|
||||
<field name="origin" placeholder="e.g. PO0025" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="picking_type_id" widget="selection" groups="stock.group_stock_adv_location" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}" attrs="{'readonly': [('state','not in',('draft'))]}"/>
|
||||
@@ -69,19 +145,21 @@
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Products">
|
||||
<field name="line_ids" attrs="{'readonly': [('state','not in',('draft'))]}">
|
||||
<field name="line_ids">
|
||||
<tree string="Products" editable="bottom">
|
||||
<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_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="qty_ordered"/>
|
||||
<field name="product_uom_id" groups="product.group_uom"/>
|
||||
<field name="schedule_date"/>
|
||||
<field name="schedule_date" groups="base.group_no_one"/>
|
||||
<field name="account_analytic_id" groups="purchase.group_analytic_accounting"/>
|
||||
|
||||
<field name="price_unit"/>
|
||||
</tree>
|
||||
<form string="Products">
|
||||
<group>
|
||||
<field name="product_id" />
|
||||
<field name="product_qty"/>
|
||||
<field name="qty_ordered"/>
|
||||
<field name="product_uom_id" />
|
||||
<field name="schedule_date"/>
|
||||
<field name="account_analytic_id" groups="purchase.group_analytic_accounting"/>
|
||||
@@ -89,28 +167,8 @@
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
<separator string="Requests for Quotation" attrs="{'invisible': [('state', '=', 'draft')]}"/>
|
||||
<button name="%(action_purchase_requisition_partner)d" type="action"
|
||||
string="Request a Quotation" icon="fa-cogs"
|
||||
attrs="{'invisible': ['|', ('line_ids','=',[]),('state', 'in', ('draft' ,'open','done'))]}"/>
|
||||
<field name="purchase_ids" options="{'reload_on_button': true}" attrs="{'invisible': [('state', '=', 'draft')]}" readonly="1">
|
||||
<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" options="{'no_create': True}"/>
|
||||
<field name="dest_address_id" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="date_planned" invisible="1"/>
|
||||
<field name="origin"/>
|
||||
<field name="state"/>
|
||||
<button name="button_cancel" states="draft,sent,to approve,purchase" string="Cancel Purchase Order" type="object" icon="fa-times"/>
|
||||
<button name="button_confirm" states="draft" string="Confirm Purchase Order" type="object" icon="fa-check"/>
|
||||
<button name="button_approve" states="to approve" string="Approved by Vendor" type="object" icon="fa-check-square-o"/>
|
||||
<button name="action_rfq_send" states="draft" string="Send RFQ by Email" type="object" context="{'send_rfq':True}" icon="fa-envelope-o"/>
|
||||
</tree>
|
||||
</field>
|
||||
<separator string="Terms and Conditions"/>
|
||||
<field name="description" attrs="{'readonly': [('state','not in',('draft'))]}"/>
|
||||
<field name="description" attrs="{'readonly': [('state','not in',('draft','in_progress','open'))]}"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
@@ -125,12 +183,12 @@
|
||||
<field name="name">purchase.requisition.tree</field>
|
||||
<field name="model">purchase.requisition</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-bf="message_needaction==True" decoration-muted="state == 'cancel'" decoration-danger="date_end and date_end<current_date" string="Call for Tenders">
|
||||
<tree decoration-bf="message_needaction==True" decoration-muted="state == 'cancel'" decoration-danger="date_end and date_end<current_date" string="Purchase Agreements">
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="name"/>
|
||||
<field name="ordering_date"/>
|
||||
<field name="user_id"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
<field name="ordering_date"/>
|
||||
<field name="date_end"/>
|
||||
<field name="origin"/>
|
||||
<field name="state"/>
|
||||
@@ -142,12 +200,12 @@
|
||||
<field name="name">purchase.requisition.list.select</field>
|
||||
<field name="model">purchase.requisition</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Call for Tenders">
|
||||
<field name="name" string="Call for Tenders"/>
|
||||
<field name="exclusive" />
|
||||
<filter name="draft" string="New" domain="[('state','=','draft')]" help="New Call for Tenders"/>
|
||||
<filter string="Confirmed" domain="[('state','=','in_progress')]" help="Call for Tenders in negotiation"/>
|
||||
<filter string="Closed Tenders" domain="[('state','=','open')]" help="Call for Tenders where tenders are closed"/>
|
||||
<search string="Search Purchase Agreements">
|
||||
<field name="name" string="Purchase Agreements" filter_domain="['|', ('name','ilike',self), ('origin','ilike',self)]"/>
|
||||
<field name="type_id"/>
|
||||
<filter name="draft" string="Draft" domain="[('state','=','draft')]" help="New Agreements"/>
|
||||
<filter string="Confirmed" domain="[('state','in',('in_progress','open'))]" help="In negotiation"/>
|
||||
<filter string="Done" domain="[('state','=','done')]"/>
|
||||
<separator/>
|
||||
<filter string="Unassigned" domain="[('user_id','=', False)]" help="Unassigned Requisition"/>
|
||||
<field name="user_id" />
|
||||
@@ -164,7 +222,7 @@
|
||||
|
||||
|
||||
<record model="ir.actions.act_window" id="action_purchase_requisition">
|
||||
<field name="name">Purchase Tenders</field>
|
||||
<field name="name">Purchase Agreements</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">purchase.requisition</field>
|
||||
<field name="view_type">form</field>
|
||||
@@ -173,13 +231,18 @@
|
||||
<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 Call for Tenders process.
|
||||
Click to start a new purchase agreement.
|
||||
</p><p>
|
||||
A Call for Tenders is a procedure for generating competing offers from
|
||||
different bidders. In the call for tenders, you can record the
|
||||
products you need to buy and generate the creation of RfQs to
|
||||
vendors. Once the tenders have been registered, you can review and
|
||||
compare them and you can validate some and cancel others.
|
||||
Example of purchase agreements include call for tenders and blanket orders.
|
||||
</p><p>
|
||||
In a call for tenders, you can record the products you need to buy
|
||||
and generate the creation of RfQs to vendors. Once the tenders have
|
||||
been registered, you can review and compare them and you can
|
||||
validate some and cancel others.
|
||||
</p><p>
|
||||
For a blanket order, you can record an agreement for a specifc period
|
||||
(e.g. a year) and you order products within this agreement, benefiting
|
||||
from the negociated prices.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
@@ -219,40 +282,5 @@
|
||||
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" decoration-info="quantity_tendered>0">
|
||||
<field name="name"/>
|
||||
<field name="partner_id" string="Vendor"/>
|
||||
<field name="product_id"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="product_qty"/>
|
||||
<field name="quantity_tendered"/>
|
||||
<field name="product_uom" groups="product.group_uom"/>
|
||||
<field name="price_subtotal"/>
|
||||
<field name="order_id" invisible="1"/>
|
||||
<button name="button_cancel" type="object" string="Cancel Choice" icon="fa-times"
|
||||
attrs="{'invisible': [('quantity_tendered', '=', 0)]}"/>
|
||||
<button name="%(action_bid_line_qty)d" type="action" string="Change Quantity" icon="fa-plus-square-o"/>
|
||||
<button name="button_confirm" type="object" string="Confirm Order" icon="fa-check"
|
||||
attrs="{'invisible': [('quantity_tendered', '>', 0)]}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="purchase_line_tree" model="ir.actions.act_window">
|
||||
<field name="name">Tender 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>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,4 +1,6 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_purchase_requisition_type,purchase.requisition.type,model_purchase_requisition_type,purchase.group_purchase_user,1,0,0,0
|
||||
access_purchase_requisition_type_manager,purchase.requisition.type,model_purchase_requisition_type,purchase.group_purchase_manager,1,1,1,1
|
||||
access_purchase_requisition,purchase.requisition,model_purchase_requisition,purchase.group_purchase_user,1,1,1,1
|
||||
access_purchase_requisition_line_purchase_user,purchase.requisition.line,model_purchase_requisition_line,purchase.group_purchase_user,1,1,1,1
|
||||
access_purchase_requisition_manager,purchase.requisition manager,model_purchase_requisition,purchase.group_purchase_manager,1,0,0,0
|
||||
|
||||
|
@@ -1,38 +0,0 @@
|
||||
odoo.define('purchase_requisition.purchase_requisition', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var Model = require('web.DataModel');
|
||||
var ListView = require('web.ListView');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
|
||||
var CompareListView = ListView.extend({
|
||||
render_buttons: function ($node) {
|
||||
if (!this.$buttons) {
|
||||
this.$buttons = $(QWeb.render("CompareListView.buttons", {'widget': this}));
|
||||
|
||||
this.$buttons.find('.oe_generate_po').click(this.proxy('generate_purchase_order'));
|
||||
|
||||
$node = $node || this.options.$buttons;
|
||||
if ($node) {
|
||||
this.$buttons.appendTo($node);
|
||||
} else {
|
||||
this.$('.oe_list_buttons').replaceWith(this.$buttons);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
generate_purchase_order: function () {
|
||||
var self = this;
|
||||
new Model(self.dataset.model).call("generate_po",[self.dataset.context.tender_id,self.dataset.context]).then(function(result) {
|
||||
self.ViewManager.action_manager.history_back();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
core.view_registry.add('tree_purchase_order_line_compare', CompareListView);
|
||||
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
<t t-name="CompareListView.buttons">
|
||||
<div class="oe_list_buttons">
|
||||
<button type="button" class="btn btn-sm btn-primary oe_generate_po">
|
||||
Generate PO
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -7,7 +7,7 @@
|
||||
I cancel requisition.
|
||||
-
|
||||
!python {model: purchase.requisition}: |
|
||||
self.tender_cancel(cr, uid, [ref("requisition1")])
|
||||
self.action_cancel(cr, uid, [ref("requisition1")])
|
||||
-
|
||||
I check requisition after cancelled.
|
||||
-
|
||||
@@ -17,7 +17,7 @@
|
||||
I reset requisition as "New".
|
||||
-
|
||||
!python {model: purchase.requisition}: |
|
||||
self.tender_reset(cr, uid, [ref('requisition1')])
|
||||
self.action_draft(cr, uid, [ref('requisition1')])
|
||||
-
|
||||
I duplicate requisition.
|
||||
-
|
||||
|
||||
@@ -31,60 +31,3 @@
|
||||
assert line.product_id.id == procurement.product_id.id, "Product is not correspond."
|
||||
assert line.product_uom_id.id == procurement.product_uom.id, "UOM is not correspond."
|
||||
assert float_compare(line.product_qty, procurement.product_qty, precision_digits=2) == 0, "Quantity is not correspond."
|
||||
-
|
||||
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)
|
||||
|
||||
-
|
||||
Give access rights of Purchase Requisition User to open requisition
|
||||
-
|
||||
!context
|
||||
uid: 'res_users_purchase_requisition_user'
|
||||
-
|
||||
I open another requisition and set tender state to choose tendering line.
|
||||
-
|
||||
!python {model: purchase.requisition}: |
|
||||
self.tender_in_progress(cr, uid, [ref("requisition1")], context=context)
|
||||
self.tender_open(cr, uid, [ref("requisition1")], context=context)
|
||||
-
|
||||
Vendor send one RFQ so I create requisition request of that supplier.
|
||||
-
|
||||
!python {model: purchase.requisition.partner}: |
|
||||
context.update({"active_model": "purchase.requisition","active_ids": [ref("requisition1")],"active_id": ref("requisition1")})
|
||||
-
|
||||
!record {model: purchase.requisition.partner, id: requisition_partner_0}:
|
||||
partner_ids: [base.res_partner_12]
|
||||
-
|
||||
!python {model: purchase.requisition.partner}: |
|
||||
context.update({"mail_create_nolog" : True })
|
||||
self.create_order(cr, uid, [ref("requisition_partner_0")], context=context)
|
||||
-
|
||||
I check that the RFQ details which created for supplier.
|
||||
-
|
||||
!python {model: purchase.order}: |
|
||||
purchase_requisition = self.pool.get("purchase.requisition")
|
||||
purchase_ids = self.search(cr, uid, [('requisition_id','=',ref("requisition1"))])
|
||||
assert purchase_ids, "RFQ is not created."
|
||||
for rfq in self.browse(cr, uid, purchase_ids, context=context):
|
||||
if rfq.partner_id.id == ref('base.res_partner_12'):
|
||||
break
|
||||
else:
|
||||
assert False, "No PO found for res_partner_12."
|
||||
-
|
||||
I confirmed RFQ which has best price.
|
||||
-
|
||||
!python {model: purchase.order}: |
|
||||
purchase = self.browse(cr, uid, ref('rfq2'), context=context)
|
||||
purchase.signal_workflow('purchase_confirm')
|
||||
|
||||
-
|
||||
I check status of requisition after confirmed best RFQ.
|
||||
-
|
||||
!python {model: purchase.requisition}: |
|
||||
requisition = self.browse(cr, uid, ref('requisition1'), context=context)
|
||||
requisition.state == 'done', "Requisition should be closed."
|
||||
|
||||
@@ -5,11 +5,30 @@
|
||||
uid: 'res_users_purchase_requisition_user'
|
||||
-
|
||||
In order to test process of the purchase requisition ,I create requisition
|
||||
-
|
||||
-
|
||||
!record {model: purchase.requisition, id: requisition1}:
|
||||
exclusive: exclusive
|
||||
line_ids:
|
||||
- product_id: product.product_product_9
|
||||
product_qty: 10.0
|
||||
product_uom_id: product.product_uom_unit
|
||||
|
||||
-
|
||||
I create another requisition and set tender state to choose tendering line.
|
||||
-
|
||||
!record {model: purchase.requisition, id: requisition2}:
|
||||
line_ids:
|
||||
- product_id: product.product_product_9
|
||||
product_qty: 10.0
|
||||
product_uom_id: product.product_uom_unit
|
||||
-
|
||||
!python {model: purchase.requisition}: |
|
||||
self.action_in_progress(cr, uid, [ref("requisition2")], context=context)
|
||||
self.action_open(cr, uid, [ref("requisition2")], context=context)
|
||||
-
|
||||
Vendor send one RFQ so I create a RfQ of that agreement.
|
||||
-
|
||||
!record {model: purchase.order, id: po_requisition}:
|
||||
partner_id: base.res_partner_1
|
||||
requisition_id: purchase_requisition.requisition2
|
||||
-
|
||||
!python {model: purchase.order, id: po_requisition}: |
|
||||
assert len(self.order_line)==1, 'Purchase order should have one line'
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="assets_backend" name="purchase requisition assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/purchase_requisition/static/src/js/web_addons.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -21,8 +21,7 @@
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<strong>Selection Type:</strong><br/>
|
||||
<span t-if="o.exclusive=='exclusive'">Purchase Requisitions (exclusive)</span>
|
||||
<span t-if="o.exclusive=='multiple'">Multiple Requisitions</span>
|
||||
<span t-esc="o.type_id.name">Multiple Requisitions</span>
|
||||
</div>
|
||||
<div class="col-xs-3">
|
||||
<strong>Source:</strong><br/>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import purchase_requisition_partner
|
||||
import bid_line_qty
|
||||
@@ -1,18 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
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_tendered': data.qty})
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@@ -1,30 +0,0 @@
|
||||
<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">
|
||||
<group>
|
||||
<field name="qty" context="{'default_qty': 0}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="change_qty" string="Change Quantity" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-default" 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>
|
||||
@@ -1,31 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp.exceptions import UserError
|
||||
|
||||
class purchase_requisition_partner(osv.osv_memory):
|
||||
_name = "purchase.requisition.partner"
|
||||
_description = "Purchase Requisition Partner"
|
||||
_columns = {
|
||||
'partner_ids': fields.many2many('res.partner', 'purchase_requisition_supplier_rel', 'requisition_id', 'partner_id', string='Vendors', required=True, domain=[('supplier', '=', True)])
|
||||
}
|
||||
|
||||
def view_init(self, cr, uid, fields_list, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
res = super(purchase_requisition_partner, self).view_init(cr, uid, fields_list, context=context)
|
||||
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 UserError(_('Define product(s) you want to include in the call for tenders.'))
|
||||
return res
|
||||
|
||||
def create_order(self, cr, uid, ids, context=None):
|
||||
active_ids = context and context.get('active_ids', [])
|
||||
purchase_requisition = self.pool.get('purchase.requisition')
|
||||
for wizard in self.browse(cr, uid, ids, context=context):
|
||||
for partner_id in wizard.partner_ids:
|
||||
purchase_requisition.make_purchase_order(cr, uid, active_ids, partner_id.id, context=context)
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@@ -1,31 +0,0 @@
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="view_purchase_requisition_partner" model="ir.ui.view">
|
||||
<field name="name">Choose Vendor</field>
|
||||
<field name="model">purchase.requisition.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Choose Vendor">
|
||||
<group>
|
||||
<field name="partner_ids" widget="many2many_tags" context="{'default_supplier': 1, 'default_customer': 0}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="create_order" string="Create Request for Quotation" type="object" class="btn-primary"/>
|
||||
<button string="Cancel" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_purchase_requisition_partner" model="ir.actions.act_window">
|
||||
<field name="name">Choose Vendor</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">purchase.requisition.partner</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>
|
||||
Reference in New Issue
Block a user