[REF] purchase_requisition: blanket order generate sellerinfo

The blanket order is a contract between you and a supplier on a
different
price (usually lower than normal) for a big quantity of some specific
products.

The goal is to generate a supplier info for each product of the blanket
order and
remove them when the BO is either closed or cancel.
This commit is contained in:
William Henroin
2018-02-01 15:11:15 +01:00
committed by Pierre Masereel
parent 7fea2292ce
commit 87af05ae76
3 changed files with 140 additions and 5 deletions
@@ -50,9 +50,9 @@ class PurchaseRequisition(models.Model):
order_count = fields.Integer(compute='_compute_orders_number', string='Number of Orders')
vendor_id = fields.Many2one('res.partner', string="Vendor")
type_id = fields.Many2one('purchase.requisition.type', string="Agreement Type", required=True, default=_get_type_id)
ordering_date = fields.Date(string="Ordering Date")
date_end = fields.Datetime(string='Agreement Deadline')
schedule_date = fields.Date(string='Delivery Date', index=True, help="The expected and scheduled delivery date where all the products are received")
ordering_date = fields.Date(string="Ordering Date", track_visibility='onchange')
date_end = fields.Datetime(string='Agreement Deadline', track_visibility='onchange')
schedule_date = fields.Date(string='Delivery Date', index=True, help="The expected and scheduled delivery date where all the products are received", track_visibility='onchange')
user_id = fields.Many2one('res.users', string='Purchase Representative', default= lambda self: self.env.user)
description = fields.Text()
company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env['res.company']._company_default_get('purchase.requisition'))
@@ -104,6 +104,8 @@ class PurchaseRequisition(models.Model):
def action_cancel(self):
# try to set all associated quotations to cancel state
for requisition in self:
for requisition_line in requisition.line_ids:
requisition_line.supplier_info_ids.unlink()
requisition.purchase_ids.button_cancel()
for po in requisition.purchase_ids:
po.message_post(body=_('Cancelled by the agreement associated to this quotation.'))
@@ -112,7 +114,15 @@ class PurchaseRequisition(models.Model):
@api.multi
def action_in_progress(self):
if not all(obj.line_ids for obj in self):
raise UserError(_('You cannot confirm call because there is no product line.'))
raise UserError(_('You cannot confirm agreement because there is no product line.'))
for requisition in self:
if requisition.type_id.quantity_copy == 'none' and requisition.vendor_id:
for requisition_line in requisition.line_ids:
if requisition_line.price_unit <= 0.0:
raise UserError(_('You cannot confirm the blanket order without price.'))
if requisition_line.product_qty <= 0.0:
raise UserError(_('You cannot confirm the blanket order without quantity.'))
requisition_line.create_supplier_info()
self.write({'state': 'in_progress'})
@api.multi
@@ -130,6 +140,9 @@ class PurchaseRequisition(models.Model):
"""
if any(purchase_order.state in ['draft', 'sent', 'to approve'] for purchase_order in self.mapped('purchase_ids')):
raise UserError(_('You have to cancel or validate every RfQ before closing the purchase requisition.'))
for requisition in self:
for requisition_line in requisition.line_ids:
requisition_line.supplier_info_ids.unlink()
self.write({'state': 'done'})
def _prepare_tender_values(self, product_id, product_qty, product_uom, location_id, name, origin, values):
@@ -146,6 +159,18 @@ class PurchaseRequisition(models.Model):
})],
}
def unlink(self):
if any(requisition.state not in ('draft', 'cancel') for requisition in self):
raise UserError(_('You can only delete draft requisitions.'))
# Draft requisitions could have some requisition lines.
self.mapped('requisition_line_ids').unlink()
return super(PurchaseRequisition, self).unlink()
class SupplierInfo(models.Model):
_inherit = "product.supplierinfo"
purchase_requisition_id = fields.Many2one('purchase.requisition', related='purchase_requisition_line_id.requisition_id', string='Blanket order')
purchase_requisition_line_id = fields.Many2one('purchase.requisition.line')
class PurchaseRequisitionLine(models.Model):
_name = "purchase.requisition.line"
@@ -157,11 +182,50 @@ class PurchaseRequisitionLine(models.Model):
product_qty = fields.Float(string='Quantity', digits=dp.get_precision('Product Unit of Measure'))
price_unit = fields.Float(string='Unit Price', digits=dp.get_precision('Product Price'))
qty_ordered = fields.Float(compute='_compute_ordered_qty', string='Ordered Quantities')
requisition_id = fields.Many2one('purchase.requisition', string='Purchase Agreement', ondelete='cascade')
requisition_id = fields.Many2one('purchase.requisition', required=True, string='Purchase Agreement', ondelete='cascade')
company_id = fields.Many2one('res.company', related='requisition_id.company_id', string='Company', store=True, readonly=True, default= lambda self: self.env['res.company']._company_default_get('purchase.requisition.line'))
account_analytic_id = fields.Many2one('account.analytic.account', string='Analytic Account')
schedule_date = fields.Date(string='Scheduled Date')
move_dest_id = fields.Many2one('stock.move', 'Downstream Move')
supplier_info_ids = fields.One2many('product.supplierinfo', 'purchase_requisition_line_id')
@api.multi
def create(self,vals):
res = super(PurchaseRequisitionLine, self).create(vals)
if res.requisition_id.state not in ['draft', 'cancel', 'done'] and res.requisition_id.is_quantity_copy == 'none':
supplier_infos = self.env['product.supplierinfo'].search([
('product_id', '=', vals.get('product_id')),
('name', '=', res.requisition_id.vendor_id.id),
])
if not [s.requisition_id for s in supplier_infos]:
res.create_supplier_info()
return res
@api.multi
def write(self, vals):
res = super(PurchaseRequisitionLine, self).write(vals)
if 'price_unit' in vals:
# If the price is updated, we have to update the related SupplierInfo
self.supplier_info_ids.write({'price': vals['price_unit']})
return res
def unlink(self):
if self.requisition_id.state not in ['draft', 'cancel', 'done']:
self.supplier_info_ids.unlink()
return super(PurchaseRequisitionLine, self).unlink()
def create_supplier_info(self):
purchase_requisition = self.requisition_id
if purchase_requisition.type_id.quantity_copy == 'none' and purchase_requisition.vendor_id:
# create a supplier_info only in case of blanket order
self.env['product.supplierinfo'].create({
'name': purchase_requisition.vendor_id.id,
'product_id': self.product_id.id,
'product_tmpl_id': self.product_id.product_tmpl_id.id,
'price': self.price_unit,
'purchase_requisition_id': purchase_requisition.id,
'purchase_requisition_line_id': self.id,
})
@api.multi
@api.depends('requisition_id.purchase_ids.state')
@@ -72,3 +72,50 @@ class TestPurchaseRequisition(common.TransactionCase):
po_dict = purchase_order._convert_to_write({name: purchase_order[name] for name in purchase_order._cache})
self.po_requisition = PurchaseOrder.create(po_dict)
self.assertEqual(len(self.po_requisition.order_line), 1, 'Purchase order should have one line')
def test_03_purchase_requisition(self):
price_product09 = 34
price_product13 = 62
quantity = 26
# Create a pruchase requisition with type blanket order and two product
line1 = (0, 0, {'product_id': self.product_09_id, 'product_qty': quantity, 'product_uom_id': self.product_09_uom_id, 'price_unit': price_product09})
self.product_13_uom_id = self.ref('product.product_uom_unit')
line2 = (0, 0, {'product_id': self.product_13_id, 'product_qty': quantity, 'product_uom_id': self.product_13_uom_id, 'price_unit': price_product13})
requisition_type = self.env['purchase.requisition.type'].create({
'name': 'Blanket test',
'quantity_copy': 'none'
})
requisition_blanket = self.env['purchase.requisition'].create({
'line_ids': [line1, line2],
'type_id': requisition_type.id,
'vendor_id': self.res_partner_1_id
})
# confirm the requisition
requisition_blanket.action_in_progress()
# Check for both product that the new supplier info(purchase.requisition.vendor_id) is added to the puchase tab
# and check the quantity
seller_partner1 = self.env['res.partner'].browse(self.res_partner_1_id)
supplierinfo09 = self.env['product.supplierinfo'].search([
('name', '=', seller_partner1.id),
('product_id', '=', self.product_09_id)
])
self.assertEqual(supplierinfo09.name, seller_partner1, 'The supplierinfo is not the good one')
self.assertEqual(supplierinfo09.price, price_product09, 'The supplierinfo is not the good one')
supplierinfo13 = self.env['product.supplierinfo'].search([
('name', '=', seller_partner1.id),
('product_id', '=', self.product_13_id)
])
self.assertEqual(supplierinfo13.name, seller_partner1, 'The supplierinfo is not the good one')
self.assertEqual(supplierinfo13.price, price_product13, 'The supplierinfo is not the good one')
# Put the requisition in done Status
requisition_blanket.action_in_progress()
requisition_blanket.action_done()
self.assertFalse(self.env['product.supplierinfo'].search([('id', '=', supplierinfo09.id)]), 'The supplier info should be removed')
self.assertFalse(self.env['product.supplierinfo'].search([('id', '=', supplierinfo13.id)]), 'The supplier info should be removed')
@@ -85,6 +85,19 @@
</field>
</record>
<record model="ir.ui.view" id="supplier_info_form_inherit">
<field name="name">product.supplierinfo.requisition.view</field>
<field name="model">product.supplierinfo</field>
<field name="inherit_id" ref="product.product_supplierinfo_form_view"/>
<field name="arch" type="xml">
<field name="product_code" position="after">
<field name="purchase_requisition_id"
attrs="{'invisible': [('purchase_requisition_id', '=', False)]}"
/>
</field>
</field>
</record>
<record model="ir.ui.view" id="purchase_order_search_inherit">
<field name="name">purchase.order.list.select.inherit</field>
<field name="model">purchase.order</field>
@@ -324,6 +337,17 @@
</field>
</record>
<record model="ir.ui.view" id="product_supplierinfo_tree_view_inherit">
<field name="name">product.template.product.form.inherit</field>
<field name="model">product.supplierinfo</field>
<field name="inherit_id" ref="product.product_supplierinfo_tree_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='min_qty']" position="before">
<field name="purchase_requisition_id"/>
</xpath>
</field>
</record>
<act_window
domain="[('requisition_id', '=', active_id)]"
context="{'default_requisition_id': active_id}"