diff --git a/addons/purchase_requisition/__manifest__.py b/addons/purchase_requisition/__manifest__.py
index 44f79864b4a..1aedefb2675 100644
--- a/addons/purchase_requisition/__manifest__.py
+++ b/addons/purchase_requisition/__manifest__.py
@@ -11,12 +11,14 @@ Manage calls for tenders and blanket orders. Calls for tenders are used to get
competing offers from different vendors and select the best ones. Blanket orders
are agreements you have with vendors to benefit from a predetermined pricing.
""",
- 'depends' : ['purchase_stock'],
+ 'depends': ['purchase_stock'],
'demo': ['data/purchase_requisition_demo.xml'],
'data': [
- 'security/purchase_tender.xml',
+ 'security/purchase_requisition_security.xml',
'security/ir.model.access.csv',
'data/purchase_requisition_data.xml',
+ 'views/product_views.xml',
+ 'views/purchase_views.xml',
'views/purchase_requisition_views.xml',
'views/res_config_settings_views.xml',
'report/purchase_requisition_report.xml',
diff --git a/addons/purchase_requisition/models/__init__.py b/addons/purchase_requisition/models/__init__.py
index 028784383c7..a45382e1957 100644
--- a/addons/purchase_requisition/models/__init__.py
+++ b/addons/purchase_requisition/models/__init__.py
@@ -1,3 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import purchase
+from . import product
from . import purchase_requisition
+from . import stock
diff --git a/addons/purchase_requisition/models/product.py b/addons/purchase_requisition/models/product.py
new file mode 100644
index 00000000000..ef94ad283b7
--- /dev/null
+++ b/addons/purchase_requisition/models/product.py
@@ -0,0 +1,34 @@
+# -*- encoding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+
+class SupplierInfo(models.Model):
+ _inherit = 'product.supplierinfo'
+ _order = 'sequence, purchase_requisition_id desc, min_qty desc, price'
+
+ purchase_requisition_id = fields.Many2one('purchase.requisition', related='purchase_requisition_line_id.requisition_id', string='Blanket order', readonly=False)
+ purchase_requisition_line_id = fields.Many2one('purchase.requisition.line')
+
+
+class ProductProduct(models.Model):
+ _inherit = 'product.product'
+
+ def _prepare_sellers(self, params):
+ if params and params.get('order_id'):
+ return self.seller_ids.filtered(lambda s: not s.purchase_requisition_id or s.purchase_requisition_id == params['order_id'].requisition_id)
+ else:
+ return self.seller_ids
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ purchase_requisition = fields.Selection(
+ [('rfq', 'Create a draft purchase order'),
+ ('tenders', 'Propose a call for tenders')],
+ string='Procurement', default='rfq',
+ help="Create a draft purchase order: Based on your product configuration, the system will create a draft "
+ "purchase order.Propose a call for tender : If the 'purchase_requisition' module is installed and this option "
+ "is selected, the system will create a draft call for tender.")
diff --git a/addons/purchase_requisition/models/purchase.py b/addons/purchase_requisition/models/purchase.py
new file mode 100644
index 00000000000..7e4d5359e2c
--- /dev/null
+++ b/addons/purchase_requisition/models/purchase.py
@@ -0,0 +1,129 @@
+# -*- encoding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class PurchaseOrder(models.Model):
+ _inherit = 'purchase.order'
+
+ requisition_id = fields.Many2one('purchase.requisition', string='Purchase Agreement', copy=False)
+ is_quantity_copy = fields.Selection(related='requisition_id.is_quantity_copy', readonly=False)
+
+ @api.onchange('requisition_id')
+ def _onchange_requisition_id(self):
+ if not self.requisition_id:
+ return
+
+ requisition = self.requisition_id
+ if self.partner_id:
+ partner = self.partner_id
+ else:
+ partner = requisition.vendor_id
+ payment_term = partner.property_supplier_payment_term_id
+
+ FiscalPosition = self.env['account.fiscal.position']
+ fpos = FiscalPosition.get_fiscal_position(partner.id)
+ fpos = FiscalPosition.browse(fpos)
+
+ self.partner_id = partner.id
+ self.fiscal_position_id = fpos.id
+ self.payment_term_id = payment_term.id,
+ self.company_id = requisition.company_id.id
+ self.currency_id = requisition.currency_id.id
+ if not self.origin or requisition.name not in self.origin.split(', '):
+ if self.origin:
+ if requisition.name:
+ self.origin = self.origin + ', ' + requisition.name
+ else:
+ self.origin = requisition.name
+ self.notes = requisition.description
+ self.date_order = requisition.date_end or fields.Datetime.now()
+ self.picking_type_id = requisition.picking_type_id.id
+
+ if requisition.type_id.line_copy != 'copy':
+ return
+
+ # 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 = fpos.map_tax(line.product_id.supplier_taxes_id.filtered(lambda tax: tax.company_id == requisition.company_id)).ids
+ else:
+ taxes_ids = line.product_id.supplier_taxes_id.filtered(lambda tax: tax.company_id == requisition.company_id).ids
+
+ # Compute quantity and price_unit
+ if line.product_uom_id != line.product_id.uom_po_id:
+ product_qty = line.product_uom_id._compute_quantity(line.product_qty, line.product_id.uom_po_id)
+ price_unit = line.product_uom_id._compute_price(line.price_unit, line.product_id.uom_po_id)
+ else:
+ product_qty = line.product_qty
+ price_unit = line.price_unit
+
+ if requisition.type_id.quantity_copy != 'copy':
+ product_qty = 0
+
+ # Create PO line
+ order_line_values = line._prepare_purchase_order_line(
+ name=name, product_qty=product_qty, price_unit=price_unit,
+ taxes_ids=taxes_ids)
+ order_lines.append((0, 0, order_line_values))
+ self.order_line = order_lines
+
+ @api.multi
+ def button_confirm(self):
+ res = super(PurchaseOrder, self).button_confirm()
+ for po in self:
+ if not po.requisition_id:
+ continue
+ if 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()
+ return res
+
+ @api.model
+ def create(self, vals):
+ purchase = super(PurchaseOrder, self).create(vals)
+ if purchase.requisition_id:
+ purchase.message_post_with_view('mail.message_origin_link',
+ values={'self': purchase, 'origin': purchase.requisition_id},
+ subtype_id=self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'))
+ return purchase
+
+ @api.multi
+ def write(self, vals):
+ result = super(PurchaseOrder, self).write(vals)
+ if vals.get('requisition_id'):
+ self.message_post_with_view('mail.message_origin_link',
+ values={'self': self, 'origin': self.requisition_id, 'edit': True},
+ subtype_id=self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'))
+ return result
+
+
+class PurchaseOrderLine(models.Model):
+ _inherit = 'purchase.order.line'
+
+ @api.onchange('product_qty', 'product_uom')
+ def _onchange_quantity(self):
+ res = super(PurchaseOrderLine, self)._onchange_quantity()
+ if self.order_id.requisition_id:
+ for line in self.order_id.requisition_id.line_ids:
+ if line.product_id == self.product_id:
+ if line.product_uom_id != self.product_uom:
+ self.price_unit = line.product_uom_id._compute_price(
+ line.price_unit, self.product_uom)
+ else:
+ self.price_unit = line.price_unit
+ break
+ return res
diff --git a/addons/purchase_requisition/models/purchase_requisition.py b/addons/purchase_requisition/models/purchase_requisition.py
index 7e899b2e928..ce4f9c77ac9 100644
--- a/addons/purchase_requisition/models/purchase_requisition.py
+++ b/addons/purchase_requisition/models/purchase_requisition.py
@@ -180,13 +180,6 @@ class PurchaseRequisition(models.Model):
self.mapped('line_ids').unlink()
return super(PurchaseRequisition, self).unlink()
-class SupplierInfo(models.Model):
- _inherit = "product.supplierinfo"
- _order = 'sequence, purchase_requisition_id desc, min_qty desc, price'
-
- purchase_requisition_id = fields.Many2one('purchase.requisition', related='purchase_requisition_line_id.requisition_id', string='Blanket order', readonly=False)
- purchase_requisition_line_id = fields.Many2one('purchase.requisition.line')
-
class PurchaseRequisitionLine(models.Model):
_name = "purchase.requisition.line"
@@ -290,217 +283,3 @@ class PurchaseRequisitionLine(models.Model):
'analytic_tag_ids': self.analytic_tag_ids.ids,
'move_dest_ids': self.move_dest_id and [(4, self.move_dest_id.id)] or []
}
-
-
-class PurchaseOrder(models.Model):
- _inherit = "purchase.order"
-
- requisition_id = fields.Many2one('purchase.requisition', string='Purchase Agreement', copy=False)
- is_quantity_copy = fields.Selection(related='requisition_id.is_quantity_copy', readonly=False)
-
- @api.onchange('requisition_id')
- def _onchange_requisition_id(self):
- if not self.requisition_id:
- return
-
- requisition = self.requisition_id
- if self.partner_id:
- partner = self.partner_id
- else:
- partner = requisition.vendor_id
- payment_term = partner.property_supplier_payment_term_id
-
- FiscalPosition = self.env['account.fiscal.position']
- fpos = FiscalPosition.get_fiscal_position(partner.id)
- fpos = FiscalPosition.browse(fpos)
-
- self.partner_id = partner.id
- self.fiscal_position_id = fpos.id
- self.payment_term_id = payment_term.id
- self.company_id = requisition.company_id.id
- self.currency_id = requisition.currency_id.id
- if not self.origin or requisition.name not in self.origin.split(', '):
- if self.origin:
- if requisition.name:
- self.origin = self.origin + ', ' + requisition.name
- else:
- self.origin = requisition.name
- self.notes = requisition.description
- self.date_order = requisition.date_end or fields.Datetime.now()
- self.picking_type_id = requisition.picking_type_id.id
-
- if requisition.type_id.line_copy != 'copy':
- return
-
- # 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 = fpos.map_tax(line.product_id.supplier_taxes_id.filtered(lambda tax: tax.company_id == requisition.company_id)).ids
- else:
- taxes_ids = line.product_id.supplier_taxes_id.filtered(lambda tax: tax.company_id == requisition.company_id).ids
-
- # Compute quantity and price_unit
- if line.product_uom_id != line.product_id.uom_po_id:
- product_qty = line.product_uom_id._compute_quantity(line.product_qty, line.product_id.uom_po_id)
- price_unit = line.product_uom_id._compute_price(line.price_unit, line.product_id.uom_po_id)
- else:
- product_qty = line.product_qty
- price_unit = line.price_unit
-
- if requisition.type_id.quantity_copy != 'copy':
- product_qty = 0
-
- # Create PO line
- order_line_values = line._prepare_purchase_order_line(
- name=name, product_qty=product_qty, price_unit=price_unit,
- taxes_ids=taxes_ids)
- order_lines.append((0, 0, order_line_values))
- self.order_line = order_lines
-
- @api.multi
- def button_confirm(self):
- res = super(PurchaseOrder, self).button_confirm()
- for po in self:
- if not po.requisition_id:
- continue
- if 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()
- return res
-
- @api.model
- def create(self, vals):
- purchase = super(PurchaseOrder, self).create(vals)
- if purchase.requisition_id:
- purchase.message_post_with_view('mail.message_origin_link',
- values={'self': purchase, 'origin': purchase.requisition_id},
- subtype_id=self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'))
- return purchase
-
- @api.multi
- def write(self, vals):
- result = super(PurchaseOrder, self).write(vals)
- if vals.get('requisition_id'):
- self.message_post_with_view('mail.message_origin_link',
- values={'self': self, 'origin': self.requisition_id, 'edit': True},
- subtype_id=self.env['ir.model.data'].xmlid_to_res_id('mail.mt_note'))
- return result
-
-
-class PurchaseOrderLine(models.Model):
- _inherit = "purchase.order.line"
-
- @api.onchange('product_qty', 'product_uom')
- def _onchange_quantity(self):
- res = super(PurchaseOrderLine, self)._onchange_quantity()
- if self.order_id.requisition_id:
- for line in self.order_id.requisition_id.line_ids:
- if line.product_id == self.product_id:
- if line.product_uom_id != self.product_uom:
- self.price_unit = line.product_uom_id._compute_price(
- line.price_unit, self.product_uom)
- else:
- self.price_unit = line.price_unit
- break
- return res
-
-
-class ProductProduct(models.Model):
- _inherit = 'product.product'
-
- def _prepare_sellers(self, params):
- if params and params.get('order_id'):
- return self.seller_ids.filtered(lambda s: not s.purchase_requisition_id or s.purchase_requisition_id == params['order_id'].requisition_id)
- else:
- return self.seller_ids
-
-
-class ProductTemplate(models.Model):
- _inherit = 'product.template'
-
- purchase_requisition = fields.Selection(
- [('rfq', 'Create a draft purchase order'),
- ('tenders', 'Propose a call for tenders')],
- string='Procurement', default='rfq',
- help="Create a draft purchase order: Based on your product configuration, the system will create a draft "
- "purchase order.Propose a call for tender : If the 'purchase_requisition' module is installed and this option "
- "is selected, the system will create a draft call for tender.")
-
-
-class StockMove(models.Model):
- _inherit = "stock.move"
-
- requistion_line_ids = fields.One2many('purchase.requisition.line', 'move_dest_id')
-
-
-class ProcurementGroup(models.Model):
- _inherit = 'procurement.group'
-
- @api.model
- def _get_exceptions_domain(self):
- return super(ProcurementGroup, self)._get_exceptions_domain() + [('requistion_line_ids', '=', False)]
-
-
-class StockRule(models.Model):
- _inherit = 'stock.rule'
-
- @api.multi
- def _run_buy(self, product_id, product_qty, product_uom, location_id, name, origin, values):
- if product_id.purchase_requisition != 'tenders':
- return super(StockRule, self)._run_buy(product_id, product_qty, product_uom, location_id, name, origin, values)
- values = self.env['purchase.requisition']._prepare_tender_values(product_id, product_qty, product_uom, location_id, name, origin, values)
- values['picking_type_id'] = self.picking_type_id.id
- self.env['purchase.requisition'].create(values)
- return True
-
- def _prepare_purchase_order(self, product_id, product_qty, product_uom, origin, values, partner):
- res = super(StockRule, self)._prepare_purchase_order(product_id, product_qty, product_uom, origin, values, partner)
- res['partner_ref'] = values['supplier'].purchase_requisition_id.name
- res['requisition_id'] = values['supplier'].purchase_requisition_id.id
- if values['supplier'].purchase_requisition_id.currency_id:
- res['currency_id'] = values['supplier'].purchase_requisition_id.currency_id.id
- return res
-
- def _make_po_get_domain(self, values, partner):
- domain = super(StockRule, self)._make_po_get_domain(values, partner)
- if 'supplier' in values and values['supplier'].purchase_requisition_id:
- domain += (
- ('requisition_id', '=', values['supplier'].purchase_requisition_id.id),
- )
- return domain
-
-
-class StockMove(models.Model):
- _inherit = 'stock.move'
-
- requisition_line_ids = fields.One2many('purchase.requisition.line', 'move_dest_id')
-
- def _get_upstream_documents_and_responsibles(self, visited):
- if self.requisition_line_ids:
- return [(requisition_line.requisition_id, requisition_line.requisition_id.user_id, visited) for requisition_line in self.requisition_line_ids if requisition_line.state not in ('done', 'cancel')]
- else:
- return super(StockMove, self)._get_upstream_documents_and_responsibles(visited)
-
-
-class Orderpoint(models.Model):
- _inherit = "stock.warehouse.orderpoint"
-
- def _quantity_in_progress(self):
- res = super(Orderpoint, self)._quantity_in_progress()
- for op in self:
- for pr in self.env['purchase.requisition'].search([('state','=','draft'),('origin','=',op.name)]):
- for prline in pr.line_ids.filtered(lambda l: l.product_id.id == op.product_id.id):
- res[op.id] += prline.product_uom_id._compute_quantity(prline.product_qty, op.product_uom, round=False)
- return res
diff --git a/addons/purchase_requisition/models/stock.py b/addons/purchase_requisition/models/stock.py
new file mode 100644
index 00000000000..b41b674fb4a
--- /dev/null
+++ b/addons/purchase_requisition/models/stock.py
@@ -0,0 +1,65 @@
+# -*- encoding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class ProcurementGroup(models.Model):
+ _inherit = 'procurement.group'
+
+ @api.model
+ def _get_exceptions_domain(self):
+ return super(ProcurementGroup, self)._get_exceptions_domain() + [('requistion_line_ids', '=', False)]
+
+
+class StockRule(models.Model):
+ _inherit = 'stock.rule'
+
+ @api.multi
+ def _run_buy(self, product_id, product_qty, product_uom, location_id, name, origin, values):
+ if product_id.purchase_requisition != 'tenders':
+ return super(StockRule, self)._run_buy(product_id, product_qty, product_uom, location_id, name, origin, values)
+ values = self.env['purchase.requisition']._prepare_tender_values(product_id, product_qty, product_uom, location_id, name, origin, values)
+ values['picking_type_id'] = self.picking_type_id.id
+ self.env['purchase.requisition'].create(values)
+ return True
+
+ def _prepare_purchase_order(self, product_id, product_qty, product_uom, origin, values, partner):
+ res = super(StockRule, self)._prepare_purchase_order(product_id, product_qty, product_uom, origin, values, partner)
+ res['partner_ref'] = values['supplier'].purchase_requisition_id.name
+ res['requisition_id'] = values['supplier'].purchase_requisition_id.id
+ if values['supplier'].purchase_requisition_id.currency_id:
+ res['currency_id'] = values['supplier'].purchase_requisition_id.currency_id.id
+ return res
+
+ def _make_po_get_domain(self, values, partner):
+ domain = super(StockRule, self)._make_po_get_domain(values, partner)
+ if 'supplier' in values and values['supplier'].purchase_requisition_id:
+ domain += (
+ ('requisition_id', '=', values['supplier'].purchase_requisition_id.id),
+ )
+ return domain
+
+
+class StockMove(models.Model):
+ _inherit = 'stock.move'
+
+ requisition_line_ids = fields.One2many('purchase.requisition.line', 'move_dest_id')
+
+ def _get_upstream_documents_and_responsibles(self, visited):
+ if self.requisition_line_ids:
+ return [(requisition_line.requisition_id, requisition_line.requisition_id.user_id, visited) for requisition_line in self.requisition_line_ids if requisition_line.state not in ('done', 'cancel')]
+ else:
+ return super(StockMove, self)._get_upstream_documents_and_responsibles(visited)
+
+
+class Orderpoint(models.Model):
+ _inherit = "stock.warehouse.orderpoint"
+
+ def _quantity_in_progress(self):
+ res = super(Orderpoint, self)._quantity_in_progress()
+ for op in self:
+ for pr in self.env['purchase.requisition'].search([('state','=','draft'),('origin','=',op.name)]):
+ for prline in pr.line_ids.filtered(lambda l: l.product_id.id == op.product_id.id):
+ res[op.id] += prline.product_uom_id._compute_quantity(prline.product_qty, op.product_uom, round=False)
+ return res
diff --git a/addons/purchase_requisition/security/purchase_tender.xml b/addons/purchase_requisition/security/purchase_requisition_security.xml
similarity index 100%
rename from addons/purchase_requisition/security/purchase_tender.xml
rename to addons/purchase_requisition/security/purchase_requisition_security.xml
diff --git a/addons/purchase_requisition/views/product_views.xml b/addons/purchase_requisition/views/product_views.xml
new file mode 100644
index 00000000000..53c80d48231
--- /dev/null
+++ b/addons/purchase_requisition/views/product_views.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ product.template.product.form.inherit
+ product.supplierinfo
+
+
+
+
+
+
+
+
+
+ product.supplierinfo.requisition.view
+ product.supplierinfo
+
+
+
+
+
+
+
+
+
+ product.template.form.inherit
+ product.template
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/purchase_requisition/views/purchase_requisition_views.xml b/addons/purchase_requisition/views/purchase_requisition_views.xml
index 93030beeb2c..304ed3e75a4 100644
--- a/addons/purchase_requisition/views/purchase_requisition_views.xml
+++ b/addons/purchase_requisition/views/purchase_requisition_views.xml
@@ -73,22 +73,6 @@
-
- purchase.order.form.inherit
- purchase.order
-
-
-
-
-
-
-
-
-
-
-
-
product.supplierinfo.requisition.view
product.supplierinfo
@@ -102,17 +86,6 @@
-
- purchase.order.list.select.inherit
- purchase.order
-
-
-
-
-
-
-
-
Request for Quotation
ir.actions.act_window
@@ -330,37 +303,5 @@
parent="purchase.menu_procurement_management"
action="action_purchase_requisition"/>
-
- product.template.form.inherit
- product.template
-
-
-
-
-
-
-
-
-
-
-
- product.template.product.form.inherit
- product.supplierinfo
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/purchase_requisition/views/purchase_views.xml b/addons/purchase_requisition/views/purchase_views.xml
new file mode 100644
index 00000000000..2ef157538e6
--- /dev/null
+++ b/addons/purchase_requisition/views/purchase_views.xml
@@ -0,0 +1,30 @@
+
+
+
+
+ purchase.order.form.inherit
+ purchase.order
+
+
+
+
+
+
+
+
+
+
+
+
+
+ purchase.order.list.select.inherit
+ purchase.order
+
+
+
+
+
+
+
+
+