diff --git a/addons/purchase_requisition/models/purchase_requisition.py b/addons/purchase_requisition/models/purchase_requisition.py index ec8cf2d826e..dffda14ab6e 100644 --- a/addons/purchase_requisition/models/purchase_requisition.py +++ b/addons/purchase_requisition/models/purchase_requisition.py @@ -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') diff --git a/addons/purchase_requisition/tests/test_purchase_requisition.py b/addons/purchase_requisition/tests/test_purchase_requisition.py index 1ddbed67bf8..537d1121e24 100644 --- a/addons/purchase_requisition/tests/test_purchase_requisition.py +++ b/addons/purchase_requisition/tests/test_purchase_requisition.py @@ -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') diff --git a/addons/purchase_requisition/views/purchase_requisition_views.xml b/addons/purchase_requisition/views/purchase_requisition_views.xml index 1134250cfcd..e1f17b6dc5b 100644 --- a/addons/purchase_requisition/views/purchase_requisition_views.xml +++ b/addons/purchase_requisition/views/purchase_requisition_views.xml @@ -85,6 +85,19 @@ + + product.supplierinfo.requisition.view + product.supplierinfo + + + + + + + + purchase.order.list.select.inherit purchase.order @@ -324,6 +337,17 @@ + + product.template.product.form.inherit + product.supplierinfo + + + + + + + +