[IMP] stock,mrp,purchase: split _run method and move it to rules
Previously mrp, stock and purchase used _run methon on procurement group and start with a if checking for the action type. This commit instead launch a specific method depending the rule's action's type Also _run method and their submethod used in procurement group was always called with a rule. Thus we choose to move this method on the rule object himself.
This commit is contained in:
@@ -11,67 +11,57 @@ class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
action = fields.Selection(selection_add=[('manufacture', 'Manufacture')])
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = 'procurement.group'
|
||||
@api.multi
|
||||
def _run_manufacture(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
Production = self.env['mrp.production']
|
||||
ProductionSudo = Production.sudo().with_context(force_company=values['company_id'].id)
|
||||
bom = self._get_matching_bom(product_id, values)
|
||||
if not bom:
|
||||
msg = _('No Bill of Material found for product %s.') % (product_id.display_name,)
|
||||
raise UserError(msg)
|
||||
|
||||
# create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
|
||||
production = ProductionSudo.create(self._prepare_mo_vals(product_id, product_qty, product_uom, location_id, name, origin, values, bom))
|
||||
origin_production = values.get('move_dest_ids') and values['move_dest_ids'][0].raw_material_production_id or False
|
||||
orderpoint = values.get('orderpoint_id')
|
||||
if orderpoint:
|
||||
production.message_post_with_view('mail.message_origin_link',
|
||||
values={'self': production, 'origin': orderpoint.id},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
if origin_production:
|
||||
production.message_post_with_view('mail.message_origin_link',
|
||||
values={'self': production, 'origin': origin_production},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def _run(self, values, rule, doraise=True):
|
||||
if rule.action == 'manufacture':
|
||||
Production = self.env['mrp.production']
|
||||
ProductionSudo = Production.sudo().with_context(force_company=values['company_id'].id)
|
||||
bom = self._get_matching_bom(values, rule)
|
||||
if not bom:
|
||||
msg = _('No Bill of Material found for product %s.') % (values['product_id'].display_name,)
|
||||
if doraise:
|
||||
raise UserError(msg)
|
||||
else:
|
||||
self.log_next_activity(values['product_id'], msg)
|
||||
return False
|
||||
|
||||
# create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
|
||||
production = ProductionSudo.create(self._prepare_mo_vals(values, rule, bom))
|
||||
origin_production = values.get('move_dest_ids') and values ['move_dest_ids'][0].raw_material_production_id or False
|
||||
orderpoint = values.get('orderpoint_id')
|
||||
if orderpoint:
|
||||
production.message_post_with_view('mail.message_origin_link',
|
||||
values={'self': production, 'origin': orderpoint.id},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
if origin_production:
|
||||
production.message_post_with_view('mail.message_origin_link',
|
||||
values={'self': production, 'origin': origin_production},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
return True
|
||||
return super(ProcurementGroup, self)._run(values, rule, doraise)
|
||||
|
||||
@api.multi
|
||||
def _get_matching_bom(self, values, rule):
|
||||
def _get_matching_bom(self, product_id, values):
|
||||
if values.get('bom_id', False):
|
||||
return values['bom_id']
|
||||
return self.env['mrp.bom'].with_context(
|
||||
company_id=values['company_id'].id, force_company=values['company_id'].id
|
||||
)._bom_find(product=values['product_id'], picking_type=rule.picking_type_id) # TDE FIXME: context bullshit
|
||||
)._bom_find(product=product_id, picking_type=self.picking_type_id) # TDE FIXME: context bullshit
|
||||
|
||||
def _get_date_planned(self, values, rule):
|
||||
format_date_planned = fields.Datetime.from_string(values['date_planned'])
|
||||
date_planned = format_date_planned - relativedelta(days=values['product_id'].produce_delay or 0.0)
|
||||
date_planned = date_planned - relativedelta(days=values['company_id'].manufacturing_lead)
|
||||
return date_planned
|
||||
|
||||
def _prepare_mo_vals(self, values, rule, bom):
|
||||
def _prepare_mo_vals(self, product_id, product_qty, product_uom, location_id, name, origin, values, bom):
|
||||
return {
|
||||
'origin': values['origin'],
|
||||
'product_id': values['product_id'].id,
|
||||
'product_qty': values['product_qty'],
|
||||
'product_uom_id': values['product_uom'].id,
|
||||
'location_src_id': rule.location_src_id.id or values['location_id'].id,
|
||||
'location_dest_id': values['location_id'].id,
|
||||
'origin': origin,
|
||||
'product_id': product_id.id,
|
||||
'product_qty': product_qty,
|
||||
'product_uom_id': product_uom.id,
|
||||
'location_src_id': self.location_src_id.id or location_id.id,
|
||||
'location_dest_id': location_id.id,
|
||||
'bom_id': bom.id,
|
||||
'date_planned_start': fields.Datetime.to_string(self._get_date_planned(values, rule)),
|
||||
'date_planned_start': fields.Datetime.to_string(self._get_date_planned(product_id, values)),
|
||||
'date_planned_finished': values['date_planned'],
|
||||
'procurement_group_id': values.get('group_id').id if values.get('group_id', False) else False,
|
||||
'propagate': rule.propagate,
|
||||
'picking_type_id': rule.picking_type_id.id or values['warehouse_id'].manu_type_id.id,
|
||||
'propagate': self.propagate,
|
||||
'picking_type_id': self.picking_type_id.id or values['warehouse_id'].manu_type_id.id,
|
||||
'company_id': values['company_id'].id,
|
||||
'move_dest_ids': values.get('move_dest_ids') and [(4, x.id) for x in values['move_dest_ids']] or False,
|
||||
}
|
||||
|
||||
def _get_date_planned(self, product_id, values):
|
||||
format_date_planned = fields.Datetime.from_string(values['date_planned'])
|
||||
date_planned = format_date_planned - relativedelta(days=product_id.produce_delay or 0.0)
|
||||
date_planned = date_planned - relativedelta(days=values['company_id'].manufacturing_lead)
|
||||
return date_planned
|
||||
|
||||
@@ -8,8 +8,8 @@ class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
@api.multi
|
||||
def _action_procurement_create(self):
|
||||
res = super(SaleOrderLine, self)._action_procurement_create()
|
||||
def _action_launch_procurement_rule(self):
|
||||
res = super(SaleOrderLine, self)._action_launch_procurement_rule()
|
||||
orders = list(set(x.order_id for x in self))
|
||||
for order in orders:
|
||||
reassign = order.picking_ids.filtered(lambda x: x.state=='confirmed' or ((x.state in ['partially_available', 'waiting']) and not x.printed))
|
||||
|
||||
@@ -747,6 +747,12 @@ class PurchaseOrderLine(models.Model):
|
||||
else:
|
||||
return datetime.today() + relativedelta(days=seller.delay if seller else 0)
|
||||
|
||||
def _merge_in_existing_line(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
""" This function purpose is to be override with the purpose to forbide _run_buy method
|
||||
to merge a new po line in an existing one.
|
||||
"""
|
||||
return True
|
||||
|
||||
@api.onchange('product_id')
|
||||
def onchange_product_id(self):
|
||||
result = {}
|
||||
@@ -842,62 +848,62 @@ class PurchaseOrderLine(models.Model):
|
||||
self.product_qty = 1.0
|
||||
|
||||
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
|
||||
action = fields.Selection(selection_add=[('buy', 'Buy')])
|
||||
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = 'procurement.group'
|
||||
|
||||
@api.model
|
||||
def _get_exceptions_domain(self):
|
||||
return super(ProcurementGroup, self)._get_exceptions_domain() + [('created_purchase_line_id', '=', False)]
|
||||
|
||||
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
action = fields.Selection(selection_add=[('buy', 'Buy')])
|
||||
|
||||
@api.multi
|
||||
def _run(self, values, rule, doraise=True):
|
||||
if rule.action == 'buy':
|
||||
cache = {}
|
||||
suppliers = values['product_id'].seller_ids\
|
||||
.filtered(lambda r: (not r.company_id or r.company_id == values['company_id']) and (not r.product_id or r.product_id == values['product_id']))
|
||||
if not suppliers:
|
||||
msg = _('No vendor associated to product %s. Unable to generate the purchase order.') % (values['product_id'].display_name,)
|
||||
if doraise:
|
||||
raise UserError(msg)
|
||||
def _run_buy(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
cache = {}
|
||||
suppliers = product_id.seller_ids\
|
||||
.filtered(lambda r: (not r.company_id or r.company_id == values['company_id']) and (not r.product_id or r.product_id == product_id))
|
||||
if not suppliers:
|
||||
msg = _('No vendor associated to product %s. Unable to generate the purchase order.') % (product_id.display_name,)
|
||||
raise UserError(msg)
|
||||
|
||||
supplier = self._make_po_select_supplier(values, suppliers)
|
||||
partner = supplier.name
|
||||
|
||||
domain = self._make_po_get_domain(values, partner)
|
||||
|
||||
if domain in cache:
|
||||
po = cache[domain]
|
||||
else:
|
||||
po = self.env['purchase.order'].search([dom for dom in domain])
|
||||
po = po[0] if po else False
|
||||
cache[domain] = po
|
||||
if not po:
|
||||
vals = self._prepare_purchase_order(product_id, product_qty, product_uom, origin, values, partner)
|
||||
po = self.env['purchase.order'].create(vals)
|
||||
cache[domain] = po
|
||||
elif not po.origin or origin not in po.origin.split(', '):
|
||||
if po.origin:
|
||||
if origin:
|
||||
po.write({'origin': po.origin + ', ' + origin})
|
||||
else:
|
||||
self._log_next_activity(values['product_id'], msg)
|
||||
return False
|
||||
supplier = self._make_po_select_supplier(values, rule, suppliers)
|
||||
partner = supplier.name
|
||||
|
||||
domain = self._make_po_get_domain(values, rule, partner)
|
||||
|
||||
if domain in cache:
|
||||
po = cache[domain]
|
||||
po.write({'origin': po.origin})
|
||||
else:
|
||||
po = self.env['purchase.order'].search([dom for dom in domain])
|
||||
po = po[0] if po else False
|
||||
cache[domain] = po
|
||||
if not po:
|
||||
vals = self._prepare_purchase_order(values, rule, partner)
|
||||
po = self.env['purchase.order'].create(vals)
|
||||
cache[domain] = po
|
||||
elif not po.origin or values['origin'] not in po.origin.split(', '):
|
||||
if po.origin:
|
||||
if values['origin']:
|
||||
po.write({'origin': po.origin + ', ' + values['origin']})
|
||||
else:
|
||||
po.write({'origin': po.origin})
|
||||
else:
|
||||
po.write({'origin': values['origin']})
|
||||
po.write({'origin': origin})
|
||||
|
||||
# Create Line
|
||||
po_line = False
|
||||
for line in po.order_line:
|
||||
if line.product_id == values['product_id'] and line.product_uom == values['product_id'].uom_po_id:
|
||||
procurement_uom_po_qty = values['product_uom']._compute_quantity(values['product_qty'], values['product_id'].uom_po_id)
|
||||
seller = values['product_id']._select_seller(
|
||||
# Create Line
|
||||
po_line = False
|
||||
for line in po.order_line:
|
||||
if line.product_id == product_id and line.product_uom == product_id.uom_po_id:
|
||||
if line._merge_in_existing_line(product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
procurement_uom_po_qty = product_uom._compute_quantity(product_qty, product_id.uom_po_id)
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity = line.product_qty + procurement_uom_po_qty,
|
||||
date = po.date_order and po.date_order[:10],
|
||||
uom_id=values['product_id'].uom_po_id)
|
||||
quantity=line.product_qty + procurement_uom_po_qty,
|
||||
date=po.date_order and po.date_order[:10],
|
||||
uom_id=product_id.uom_po_id)
|
||||
|
||||
price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, line.product_id.supplier_taxes_id, line.taxes_id) if seller else 0.0
|
||||
if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id:
|
||||
@@ -909,50 +915,48 @@ class ProcurementGroup(models.Model):
|
||||
'move_dest_ids': [(4, x.id) for x in values.get('move_dest_ids', [])]
|
||||
})
|
||||
break
|
||||
if not po_line:
|
||||
vals = self._prepare_purchase_order_line(values, rule, po, supplier)
|
||||
self.env['purchase.order.line'].create(vals)
|
||||
return True
|
||||
return super(ProcurementGroup, self)._run(values, rule, doraise=True)
|
||||
if not po_line:
|
||||
vals = self._prepare_purchase_order_line(product_id, product_qty, product_uom, values, po, supplier)
|
||||
self.env['purchase.order.line'].create(vals)
|
||||
|
||||
def _get_purchase_schedule_date(self, values, rule):
|
||||
def _get_purchase_schedule_date(self, values):
|
||||
"""Return the datetime value to use as Schedule Date (``date_planned``) for the
|
||||
Purchase Order Lines created to satisfy the given procurement. """
|
||||
procurement_date_planned = datetime.strptime(values['date_planned'], DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
procurement_date_planned = fields.Datetime.from_string(values['date_planned'])
|
||||
schedule_date = (procurement_date_planned - relativedelta(days=values['company_id'].po_lead))
|
||||
return schedule_date
|
||||
|
||||
def _get_purchase_order_date(self, values, rule, partner, schedule_date):
|
||||
def _get_purchase_order_date(self, product_id, product_qty, product_uom, values, partner, schedule_date):
|
||||
"""Return the datetime value to use as Order Date (``date_order``) for the
|
||||
Purchase Order created to satisfy the given procurement. """
|
||||
seller = values['product_id']._select_seller(
|
||||
seller = product_id._select_seller(
|
||||
partner_id=partner,
|
||||
quantity=values['product_qty'],
|
||||
quantity=product_qty,
|
||||
date=fields.Date.to_string(schedule_date),
|
||||
uom_id=values['product_uom'])
|
||||
uom_id=product_uom)
|
||||
|
||||
return schedule_date - relativedelta(days=int(seller.delay))
|
||||
|
||||
@api.model
|
||||
def _prepare_purchase_order_line(self, values, rule, po, supplier):
|
||||
procurement_uom_po_qty = values['product_uom']._compute_quantity(values['product_qty'], values['product_id'].uom_po_id)
|
||||
seller = values['product_id']._select_seller(
|
||||
@api.multi
|
||||
def _prepare_purchase_order_line(self, product_id, product_qty, product_uom, values, po, supplier):
|
||||
procurement_uom_po_qty = product_uom._compute_quantity(product_qty, product_id.uom_po_id)
|
||||
seller = product_id._select_seller(
|
||||
partner_id=supplier.name,
|
||||
quantity=procurement_uom_po_qty,
|
||||
date=po.date_order and po.date_order[:10],
|
||||
uom_id=values['product_id'].uom_po_id)
|
||||
uom_id=product_id.uom_po_id)
|
||||
|
||||
taxes = values['product_id'].supplier_taxes_id
|
||||
taxes = product_id.supplier_taxes_id
|
||||
fpos = po.fiscal_position_id
|
||||
taxes_id = fpos.map_tax(taxes) if fpos else taxes
|
||||
if taxes_id:
|
||||
taxes_id = taxes_id.filtered(lambda x: x.company_id.id == values['company_id'].id)
|
||||
|
||||
price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, values['product_id'].supplier_taxes_id, taxes_id) if seller else 0.0
|
||||
price_unit = self.env['account.tax']._fix_tax_included_price(seller.price, product_id.supplier_taxes_id, taxes_id) 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)
|
||||
|
||||
product_lang = values['product_id'].with_context({
|
||||
product_lang = product_id.with_context({
|
||||
'lang': supplier.name.lang,
|
||||
'partner_id': supplier.name.id,
|
||||
})
|
||||
@@ -965,8 +969,8 @@ class ProcurementGroup(models.Model):
|
||||
return {
|
||||
'name': name,
|
||||
'product_qty': procurement_uom_po_qty,
|
||||
'product_id': values['product_id'].id,
|
||||
'product_uom': values['product_id'].uom_po_id.id,
|
||||
'product_id': product_id.id,
|
||||
'product_uom': product_id.uom_po_id.id,
|
||||
'price_unit': price_unit,
|
||||
'date_planned': date_planned,
|
||||
'orderpoint_id': values.get('orderpoint_id', False) and values.get('orderpoint_id').id,
|
||||
@@ -975,56 +979,50 @@ class ProcurementGroup(models.Model):
|
||||
'move_dest_ids': [(4, x.id) for x in values.get('move_dest_ids', [])],
|
||||
}
|
||||
|
||||
def _prepare_purchase_order(self, values, rule, partner):
|
||||
schedule_date = self._get_purchase_schedule_date(values, rule)
|
||||
purchase_date = self._get_purchase_order_date(values, rule, partner, schedule_date)
|
||||
def _prepare_purchase_order(self, product_id, product_qty, product_uom, origin, values, partner):
|
||||
schedule_date = self._get_purchase_schedule_date(values)
|
||||
purchase_date = self._get_purchase_order_date(product_id, product_qty, product_uom, values, partner, schedule_date)
|
||||
fpos = self.env['account.fiscal.position'].with_context(company_id=values['company_id'].id).get_fiscal_position(partner.id)
|
||||
|
||||
gpo = rule.group_propagation_option
|
||||
group = (gpo == 'fixed' and rule.group_id.id) or \
|
||||
gpo = self.group_propagation_option
|
||||
group = (gpo == 'fixed' and self.group_id.id) or \
|
||||
(gpo == 'propagate' and values['group_id'].id) or False
|
||||
|
||||
return {
|
||||
'partner_id': partner.id,
|
||||
'picking_type_id': rule.picking_type_id.id,
|
||||
'picking_type_id': self.picking_type_id.id,
|
||||
'company_id': values['company_id'].id,
|
||||
'currency_id': partner.property_purchase_currency_id.id or self.env.user.company_id.currency_id.id,
|
||||
'dest_address_id': values.get('partner_dest_id', False) and values['partner_dest_id'].id,
|
||||
'origin': values['origin'],
|
||||
'origin': origin,
|
||||
'payment_term_id': partner.property_supplier_payment_term_id.id,
|
||||
'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'fiscal_position_id': fpos,
|
||||
'group_id': group
|
||||
}
|
||||
|
||||
def _make_po_select_supplier(self, values, rule, suppliers):
|
||||
def _make_po_select_supplier(self, values, suppliers):
|
||||
""" Method intended to be overridden by customized modules to implement any logic in the
|
||||
selection of supplier.
|
||||
"""
|
||||
return suppliers[0]
|
||||
|
||||
def _make_po_get_domain(self, values, rule, partner):
|
||||
domain = super(ProcurementGroup, self)._make_po_get_domain(values, partner)
|
||||
gpo = rule.group_propagation_option
|
||||
group = (gpo == 'fixed' and rule.group_id) or \
|
||||
def _make_po_get_domain(self, values, partner):
|
||||
domain = super(ProcurementRule, self)._make_po_get_domain(values, partner)
|
||||
gpo = self.group_propagation_option
|
||||
group = (gpo == 'fixed' and self.group_id) or \
|
||||
(gpo == 'propagate' and values['group_id']) or False
|
||||
|
||||
domain += (
|
||||
('partner_id', '=', partner.id),
|
||||
('state', '=', 'draft'),
|
||||
('picking_type_id', '=', rule.picking_type_id.id),
|
||||
('picking_type_id', '=', self.picking_type_id.id),
|
||||
('company_id', '=', values['company_id'].id),
|
||||
)
|
||||
if group:
|
||||
domain += (('group_id', '=', group.id),)
|
||||
if values.get('date_planned', False):
|
||||
domain += (('date_planned', '=', values.get('date_planned')),)
|
||||
return domain
|
||||
|
||||
@api.model
|
||||
def _get_exceptions_domain(self):
|
||||
return super(ProcurementGroup, self)._get_exceptions_domain() + [('created_purchase_line_id', '=', False)]
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_name = 'product.template'
|
||||
|
||||
@@ -10,17 +10,11 @@ class TestPurchase(TestStockCommon):
|
||||
def _create_make_procurement(self, product, product_qty, date_planned=False):
|
||||
ProcurementGroup = self.env['procurement.group']
|
||||
order_values = {
|
||||
'name': product.name,
|
||||
'product_id': product,
|
||||
'product_qty': product_qty,
|
||||
'product_uom': self.uom_unit,
|
||||
'warehouse_id': self.warehouse_1,
|
||||
'date_planned': date_planned or fields.Datetime.to_string(fields.datetime.now() + timedelta(days=10)), # 10 days added to current date of procurement to get future schedule date and order date of purchase order.
|
||||
'location_id': self.warehouse_1.lot_stock_id,
|
||||
'origin': '/',
|
||||
'group_id': self.env['procurement.group'],
|
||||
}
|
||||
return ProcurementGroup.run(order_values)
|
||||
return ProcurementGroup.run(product, product_qty, self.uom_unit, self.warehouse_1.lot_stock_id, product.name, '/', order_values)
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
||||
@@ -104,22 +104,16 @@ class TestPurchaseLeadTime(TestPurchase):
|
||||
|
||||
date_planned = fields.Datetime.to_string(fields.datetime.now() + timedelta(days=10))
|
||||
# Create procurement order of product_1
|
||||
self.env['procurement.group'].run({
|
||||
'product_id': self.product_1,
|
||||
'product_qty': 5.000,
|
||||
'name': 'Test scheduler for RFQ',
|
||||
'product_uom': self.uom_unit,
|
||||
self.env['procurement.group'].run(self.product_1, 5.000, self.uom_unit, self.warehouse_1.lot_stock_id, 'Test scheduler for RFQ', '/', {
|
||||
'warehouse_id': self.warehouse_1,
|
||||
'location_id': self.warehouse_1.lot_stock_id,
|
||||
'date_planned': date_planned, # 10 days added to current date of procurement to get future schedule date and order date of purchase order.
|
||||
'rule_id': self.warehouse_1.buy_pull_id,
|
||||
'origin': '/',
|
||||
'group_id': False, #self.env['procurement.group'],
|
||||
'group_id': False,
|
||||
'route_ids': [],
|
||||
})
|
||||
|
||||
# Confirm purchase order
|
||||
|
||||
|
||||
purchase = self.env['purchase.order.line'].search([('product_id', '=', self.product_1.id)], limit=1).order_id
|
||||
purchase.button_confirm()
|
||||
|
||||
|
||||
@@ -105,6 +105,20 @@ class PurchaseRequisition(models.Model):
|
||||
raise UserError(_('You have to cancel or validate every RfQ before closing the purchase requisition.'))
|
||||
self.write({'state': 'done'})
|
||||
|
||||
def _prepare_tender_values(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
return{
|
||||
'origin': origin,
|
||||
'date_end': values['date_planned'],
|
||||
'warehouse_id': values.get('warehouse_id') and values['warehouse_id'].id or False,
|
||||
'company_id': values['company_id'].id,
|
||||
'line_ids': [(0, 0, {
|
||||
'product_id': product_id.id,
|
||||
'product_uom_id': product_uom.id,
|
||||
'product_qty': product_qty,
|
||||
'move_dest_id': values.get('move_dest_ids') and values['move_dest_ids'][0].id or False,
|
||||
})],
|
||||
}
|
||||
|
||||
|
||||
class PurchaseRequisitionLine(models.Model):
|
||||
_name = "purchase.requisition.line"
|
||||
@@ -296,24 +310,14 @@ class ProductTemplate(models.Model):
|
||||
string='Procurement', default='rfq')
|
||||
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = 'procurement.group'
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
|
||||
@api.multi
|
||||
def _run_buy(self, product_id, product_qty, product_uom, location_id, name, origin, values, log_activites=False):
|
||||
def _run_buy(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
if product_id.purchase_requisition != 'tenders':
|
||||
return super(ProcurementGroup, self)._run_buy(product_id, product_qty, product_uom, location_id, name, origin, values, log_activites)
|
||||
self.env['purchase.requisition'].create({
|
||||
'origin': origin,
|
||||
'date_end': values['date_planned'],
|
||||
'warehouse_id': values.get('warehouse_id') and values['warehouse_id'].id or False,
|
||||
'company_id': values['company_id'].id,
|
||||
'picking_type_id': self.picking_type_id.id,
|
||||
'line_ids': [(0, 0, {
|
||||
'product_id': product_id.id,
|
||||
'product_uom_id': product_uom.id,
|
||||
'product_qty': product_qty,
|
||||
'move_dest_id': values.get('move_dest_ids') and values['move_dest_ids'][0].id or False,
|
||||
})],
|
||||
})
|
||||
return super(ProcurementRule, 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
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import common
|
||||
from odoo.tools import float_compare
|
||||
from odoo import fields
|
||||
|
||||
|
||||
@@ -48,16 +47,12 @@ class TestPurchaseRequisition(common.TransactionCase):
|
||||
warehouse = self.env['stock.warehouse'].browse(self.ref('stock.warehouse0'))
|
||||
product = self.env['product.product'].browse(self.product_13_id)
|
||||
product.write({'route_ids': [(4, self.ref('purchase.route_warehouse0_buy'))]})
|
||||
self.env['procurement.group'].run({'product_id': product,
|
||||
'product_qty': 14,
|
||||
'product_uom': self.env['product.uom'].browse(self.ref('product.product_uom_unit')),
|
||||
'warehouse_id': warehouse,
|
||||
'location_id': warehouse.lot_stock_id,
|
||||
'name': '/',
|
||||
'origin': '/',
|
||||
'date_planned': date_planned,
|
||||
})
|
||||
|
||||
self.env['procurement.group'].run(product, 14, self.env['product.uom'].browse(self.ref('product.product_uom_unit')), warehouse.lot_stock_id, '/', '/',
|
||||
{
|
||||
'warehouse_id': warehouse,
|
||||
'date_planned': date_planned,
|
||||
})
|
||||
|
||||
# Check requisition details which created after run procurement.
|
||||
line = self.env['purchase.requisition.line'].search([('product_id', '=', self.product_13_id), ('product_qty', '=', 14.0)])
|
||||
requisition = line[0].requisition_id
|
||||
|
||||
@@ -916,6 +916,14 @@ class SaleOrderLine(models.Model):
|
||||
invoice_lines |= self.env['account.invoice.line'].create(vals)
|
||||
return invoice_lines
|
||||
|
||||
@api.multi
|
||||
def _prepare_procurement_values(self, group_id=False):
|
||||
""" Prepare specific key for moves or other components that will be created from a procurement rule
|
||||
comming from a sale order line. This method could be override in order to add other custom key that could
|
||||
be used in move/po creation.
|
||||
"""
|
||||
return {}
|
||||
|
||||
@api.multi
|
||||
def _get_display_price(self, product):
|
||||
# TO DO: move me in master/saas-16 on sale.order
|
||||
|
||||
@@ -66,8 +66,8 @@ class SaleOrderLine(models.Model):
|
||||
_inherit = 'sale.order.line'
|
||||
|
||||
@api.multi
|
||||
def _prepare_order_line_procurement(self, group_id):
|
||||
vals = super(SaleOrderLine, self)._prepare_order_line_procurement(group_id=group_id)
|
||||
def _prepare_procurement_values(self, group_id):
|
||||
vals = super(SaleOrderLine, self)._prepare_procurement_values(group_id=group_id)
|
||||
for line in self.filtered("order_id.requested_date"):
|
||||
date_planned = fields.Datetime.from_string(line.order_id.requested_date) - timedelta(days=line.order_id.company_id.security_lead)
|
||||
vals.update({
|
||||
|
||||
@@ -37,7 +37,7 @@ class SaleOrder(models.Model):
|
||||
def action_confirm(self):
|
||||
result = super(SaleOrder, self).action_confirm()
|
||||
for order in self:
|
||||
order.order_line._action_procurement_create()
|
||||
order.order_line._action_launch_procurement_rule()
|
||||
return result
|
||||
|
||||
@api.depends('picking_ids')
|
||||
@@ -89,7 +89,7 @@ class SaleOrderLine(models.Model):
|
||||
def create(self, values):
|
||||
line = super(SaleOrderLine, self).create(values)
|
||||
if line.state == 'sale':
|
||||
line._action_procurement_create()
|
||||
line._action_launch_procurement_rule()
|
||||
return line
|
||||
|
||||
@api.multi
|
||||
@@ -101,7 +101,7 @@ class SaleOrderLine(models.Model):
|
||||
lambda r: r.state == 'sale' and float_compare(r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) == -1)
|
||||
res = super(SaleOrderLine, self).write(values)
|
||||
if lines:
|
||||
lines._action_procurement_create()
|
||||
lines._action_launch_procurement_rule()
|
||||
return res
|
||||
|
||||
|
||||
@@ -186,31 +186,32 @@ class SaleOrderLine(models.Model):
|
||||
return {}
|
||||
|
||||
@api.multi
|
||||
def _prepare_order_line_procurement(self, group_id=False):
|
||||
def _prepare_procurement_values(self, group_id=False):
|
||||
""" Prepare specific key for moves or other components that will be created from a procurement rule
|
||||
comming from a sale order line. This method could be override in order to add other custom key that could
|
||||
be used in move/po creation.
|
||||
"""
|
||||
values = super(SaleOrderLine, self)._prepare_procurement_values(group_id)
|
||||
self.ensure_one()
|
||||
date_planned = datetime.strptime(self.order_id.confirmation_date, DEFAULT_SERVER_DATETIME_FORMAT)\
|
||||
+ timedelta(days=self.customer_lead or 0.0) - timedelta(days=self.order_id.company_id.security_lead)
|
||||
return {
|
||||
'name': self.name,
|
||||
'origin': self.order_id.name,
|
||||
'product_id': self.product_id,
|
||||
'product_qty': self.product_uom_qty,
|
||||
'product_uom': self.product_uom,
|
||||
values.update({
|
||||
'company_id': self.order_id.company_id,
|
||||
'group_id': group_id,
|
||||
'sale_line_id': self.id,
|
||||
'date_planned': date_planned.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
'location_id': self.order_id.partner_shipping_id.property_stock_customer,
|
||||
'route_ids': self.route_id,
|
||||
'warehouse_id': self.order_id.warehouse_id or False,
|
||||
'partner_dest_id': self.order_id.partner_shipping_id
|
||||
}
|
||||
})
|
||||
return values
|
||||
|
||||
@api.multi
|
||||
def _action_procurement_create(self):
|
||||
def _action_launch_procurement_rule(self):
|
||||
"""
|
||||
Create procurements based on quantity ordered. If the quantity is increased, new
|
||||
procurements are created. If the quantity is decreased, no automated action is taken.
|
||||
Launch procurement group run method with required/custom fields genrated by a
|
||||
sale order line. procurement group will launch '_run_move', '_run_buy' or '_run_manufacture'
|
||||
depending on the sale order line product rule.
|
||||
"""
|
||||
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
|
||||
for line in self:
|
||||
@@ -223,17 +224,16 @@ class SaleOrderLine(models.Model):
|
||||
continue
|
||||
|
||||
if not line.order_id.procurement_group_id:
|
||||
line.order_id.procurement_group_id = self.env["procurement.group"].create({
|
||||
line.order_id.procurement_group_id = self.env['procurement.group'].create({
|
||||
'name': line.order_id.name, 'move_type': line.order_id.picking_policy,
|
||||
'sale_id': line.order_id.id
|
||||
})
|
||||
vals = line._prepare_order_line_procurement(group_id=line.order_id.procurement_group_id)
|
||||
vals['product_qty'] = line.product_uom_qty - qty
|
||||
values = line._prepare_procurement_values(group_id=line.order_id.procurement_group_id)
|
||||
product_qty = line.product_uom_qty - qty
|
||||
|
||||
new_proc = self.env["procurement.group"].run(vals)
|
||||
self.env['procurement.group'].run(line.product_id, product_qty, line.product_uom, line.order_id.partner_shipping_id.property_stock_customer, line.name, line.order_id.name, values)
|
||||
return True
|
||||
|
||||
|
||||
@api.multi
|
||||
def _get_delivered_qty(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -20,17 +20,6 @@ class StockMove(models.Model):
|
||||
line.qty_delivered = line._get_delivered_qty()
|
||||
return result
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = 'procurement.group'
|
||||
|
||||
sale_id = fields.Many2one('sale.order', 'Sale Order')
|
||||
|
||||
def _get_stock_move_values(self, values, rule, group_id):
|
||||
result = super(ProcurementGroup, self)._get_stock_move_values(values, rule, group_id)
|
||||
if values.get('sale_line_id', False):
|
||||
result['sale_line_id'] = values['sale_line_id']
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
res = super(StockMove, self).write(vals)
|
||||
@@ -43,10 +32,25 @@ class ProcurementGroup(models.Model):
|
||||
return res
|
||||
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = 'procurement.group'
|
||||
|
||||
sale_id = fields.Many2one('sale.order', 'Sale Order')
|
||||
|
||||
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
|
||||
def _get_stock_move_values(self, product_id, product_qty, product_uom, location_id, name, origin, values, group_id):
|
||||
result = super(ProcurementRule, self)._get_stock_move_values(product_id, product_qty, product_uom, location_id, name, origin, values, group_id)
|
||||
if values.get('sale_line_id', False):
|
||||
result['sale_line_id'] = values['sale_line_id']
|
||||
return result
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
|
||||
sale_id = fields.Many2one(related="group_id.sale_id", string="Sales Order", store=True)
|
||||
|
||||
@api.multi
|
||||
@@ -59,4 +63,4 @@ class StockPicking(models.Model):
|
||||
'mail.message_origin_link',
|
||||
values={'self': backorder, 'origin': backorder.sale_id},
|
||||
subtype_id=self.env.ref('mail.mt_note').id)
|
||||
return res
|
||||
return res
|
||||
|
||||
+103
-100
@@ -62,6 +62,82 @@ class ProcurementRule(models.Model):
|
||||
'stock.warehouse', 'Warehouse to Propagate',
|
||||
help="The warehouse to propagate on the created move/procurement, which can be different of the warehouse this rule is for (e.g for resupplying rules from another warehouse)")
|
||||
|
||||
@api.multi
|
||||
def _run_move(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
if not self.location_src_id:
|
||||
msg = _('No source location defined on procurement rule: %s!') % (self.name, )
|
||||
raise UserError(msg)
|
||||
|
||||
# create the move as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
|
||||
# Search if picking with move for it exists already:
|
||||
group_id = False
|
||||
if self.group_propagation_option == 'propagate':
|
||||
group_id = values.get('group_id', False) and values['group_id'].id
|
||||
elif self.group_propagation_option == 'fixed':
|
||||
group_id = self.group_id.id
|
||||
|
||||
data = self._get_stock_move_values(product_id, product_qty, product_uom, location_id, name, origin, values, group_id)
|
||||
# Since action_confirm launch following procurement_group we should activate it.
|
||||
move = self.env['stock.move'].sudo().create(data)
|
||||
move.assign_picking()
|
||||
move.picking_id.action_confirm()
|
||||
return True
|
||||
|
||||
def _get_stock_move_values(self, product_id, product_qty, product_uom, location_id, name, origin, values, group_id):
|
||||
''' Returns a dictionary of values that will be used to create a stock move from a procurement.
|
||||
This function assumes that the given procurement has a rule (action == 'move') set on it.
|
||||
|
||||
:param procurement: browse record
|
||||
:rtype: dictionary
|
||||
'''
|
||||
date_expected = (datetime.strptime(values['date_planned'], DEFAULT_SERVER_DATETIME_FORMAT) - relativedelta(days=self.delay or 0)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
# it is possible that we've already got some move done, so check for the done qty and create
|
||||
# a new move with the correct qty
|
||||
qty_left = product_qty
|
||||
return {
|
||||
'name': name[:2000],
|
||||
'company_id': self.company_id.id or self.location_src_id.company_id.id or self.location_id.company_id.id or values['company_id'].id,
|
||||
'product_id': product_id.id,
|
||||
'product_uom': product_uom.id,
|
||||
'product_uom_qty': qty_left,
|
||||
'partner_id': self.partner_address_id.id or (values.get('group_id', False) and values['group_id'].partner_id.id) or False,
|
||||
'location_id': self.location_src_id.id,
|
||||
'location_dest_id': location_id.id,
|
||||
'move_dest_ids': values.get('move_dest_ids', False) and [(4, x.id) for x in values['move_dest_ids']] or [],
|
||||
'rule_id': self.id,
|
||||
'procure_method': self.procure_method,
|
||||
'origin': origin,
|
||||
'picking_type_id': self.picking_type_id.id,
|
||||
'group_id': group_id,
|
||||
'route_ids': [(4, route.id) for route in values.get('route_ids', [])],
|
||||
'warehouse_id': self.propagate_warehouse_id.id or self.warehouse_id.id,
|
||||
'date': date_expected,
|
||||
'date_expected': date_expected,
|
||||
'propagate': self.propagate,
|
||||
'priority': values.get('priority', "1"),
|
||||
}
|
||||
|
||||
def _log_next_activity(self, product_id, note):
|
||||
existing_activity = self.env['mail.activity'].search([('res_id', '=', product_id.product_tmpl_id.id), ('res_model_id', '=', self.env.ref('product.model_product_template').id),
|
||||
('note', '=', note)])
|
||||
if not existing_activity:
|
||||
# If the user deleted todo activity type.
|
||||
try:
|
||||
activity_type_id = self.env.ref('mail.mail_activity_data_todo').id
|
||||
except:
|
||||
activity_type_id = False
|
||||
self.env['mail.activity'].create({
|
||||
'activity_type_id': activity_type_id,
|
||||
'note': note,
|
||||
'user_id': product_id.responsible_id.id,
|
||||
'res_id': product_id.product_tmpl_id.id,
|
||||
'res_model_id': self.env.ref('product.model_product_template').id,
|
||||
})
|
||||
|
||||
def _make_po_get_domain(self, values, partner):
|
||||
return ()
|
||||
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
"""
|
||||
The procurement group class is used to group products together
|
||||
@@ -99,44 +175,21 @@ class ProcurementGroup(models.Model):
|
||||
('one', 'All at once')], string='Delivery Type', default='direct',
|
||||
required=True)
|
||||
|
||||
def _make_po_get_domain(self, values, partner):
|
||||
return ()
|
||||
|
||||
def _log_next_activity(self, product_id, note):
|
||||
existing_activity = self.env['mail.activity'].search([('res_id', '=', product_id.product_tmpl_id.id), ('res_model_id', '=', self.env.ref('product.model_product_template').id),
|
||||
('note', '=', note)])
|
||||
if not existing_activity:
|
||||
self.env['mail.activity'].create({
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
|
||||
'note': note,
|
||||
'user_id': product_id.responsible_id.id,
|
||||
'res_id': product_id.product_tmpl_id.id,
|
||||
'res_model_id': self.env.ref('product.model_product_template').id,
|
||||
})
|
||||
|
||||
@api.model
|
||||
def run(self, values, doraise=True):
|
||||
for key in ('product_id', 'product_uom', 'product_qty', 'location_id', 'name', 'origin'):
|
||||
assert key in values
|
||||
def run(self, product_id, product_qty, product_uom, location_id, name, origin, values):
|
||||
values.setdefault('company_id', self.env['res.company']._company_default_get('procurement.group'))
|
||||
values.setdefault('priority', '1')
|
||||
values.setdefault('date_planned', fields.Datetime.now())
|
||||
rule = self._get_rule(values)
|
||||
rule = self._get_rule(product_id, location_id, values)
|
||||
|
||||
if not rule:
|
||||
if doraise:
|
||||
raise UserError(_('No procurement rule found.'))
|
||||
else:
|
||||
msg = _('No procurement rule found. Update the inventory tab of the product form.')
|
||||
if values.get('orderpoint_id', False):
|
||||
msg = _('No procurement rule found for orderpoint %s. Update the inventory tab of the product form.') % (values['orderpoint_id'].name,)
|
||||
self._log_next_activity(values['product_id'], msg)
|
||||
return False
|
||||
self._run(values, rule, doraise)
|
||||
raise UserError(_('No procurement rule found.'))
|
||||
|
||||
getattr(rule, '_run_%s' % rule.action)(product_id, product_qty, product_uom, location_id, name, origin, values)
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def _search_rule(self, values, domain):
|
||||
def _search_rule(self, product_id, values, domain):
|
||||
""" First find a rule among the ones defined on the procurement
|
||||
group; then try on the routes defined for the product; finally fallback
|
||||
on the default behavior """
|
||||
@@ -147,7 +200,7 @@ class ProcurementGroup(models.Model):
|
||||
if values.get('route_ids', False):
|
||||
res = Pull.search(expression.AND([[('route_id', 'in', values['route_ids'].ids)], domain]), order='route_sequence, sequence', limit=1)
|
||||
if not res:
|
||||
product_routes = values['product_id'].route_ids | values['product_id'].categ_id.total_route_ids
|
||||
product_routes = product_id.route_ids | product_id.categ_id.total_route_ids
|
||||
if product_routes:
|
||||
res = Pull.search(expression.AND([[('route_id', 'in', product_routes.ids)], domain]), order='route_sequence, sequence', limit=1)
|
||||
if not res:
|
||||
@@ -157,51 +210,17 @@ class ProcurementGroup(models.Model):
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _get_rule(self, values):
|
||||
def _get_rule(self, product_id, location_id, values):
|
||||
result = False
|
||||
location = values['location_id']
|
||||
location = location_id
|
||||
while (not result) and location:
|
||||
result = self._search_rule(values, [('location_id', '=', location.id)])
|
||||
result = self._search_rule(product_id, values, [('location_id', '=', location.id)])
|
||||
location = location.location_id
|
||||
return result
|
||||
|
||||
def _get_stock_move_values(self, values, rule, group_id):
|
||||
''' Returns a dictionary of values that will be used to create a stock move from a procurement.
|
||||
This function assumes that the given procurement has a rule (action == 'move') set on it.
|
||||
|
||||
:param procurement: browse record
|
||||
:rtype: dictionary
|
||||
'''
|
||||
date_expected = (datetime.strptime(values['date_planned'], DEFAULT_SERVER_DATETIME_FORMAT) - relativedelta(days=rule.delay or 0)).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
# it is possible that we've already got some move done, so check for the done qty and create
|
||||
# a new move with the correct qty
|
||||
qty_left = values['product_qty']
|
||||
return {
|
||||
'name': values['name'][:2000],
|
||||
'company_id': rule.company_id.id or rule.location_src_id.company_id.id or rule.location_id.company_id.id or values['company_id'].id,
|
||||
'product_id': values['product_id'].id,
|
||||
'product_uom': values['product_uom'].id,
|
||||
'product_uom_qty': qty_left,
|
||||
'partner_id': rule.partner_address_id.id or (values['group_id'] and values['group_id'].partner_id.id) or False,
|
||||
'location_id': rule.location_src_id.id,
|
||||
'location_dest_id': values['location_id'].id,
|
||||
'move_dest_ids': values.get('move_dest_ids', False) and [(4, x.id) for x in values['move_dest_ids']] or [],
|
||||
'rule_id': rule.id,
|
||||
'procure_method': rule.procure_method,
|
||||
'origin': values['origin'],
|
||||
'picking_type_id': rule.picking_type_id.id,
|
||||
'group_id': group_id,
|
||||
'route_ids': [(4, route.id) for route in values.get('route_ids', [])],
|
||||
'warehouse_id': rule.propagate_warehouse_id.id or rule.warehouse_id.id,
|
||||
'date': date_expected,
|
||||
'date_expected': date_expected,
|
||||
'propagate': rule.propagate,
|
||||
'priority': values.get('priority', "1"),
|
||||
}
|
||||
|
||||
def _merge_domain(self, values, rule, group_id):
|
||||
return [
|
||||
('group_id', '=', group_id), #extra logic?
|
||||
('group_id', '=', group_id), # extra logic?
|
||||
('location_id', '=', rule.location_src_id.id),
|
||||
('location_dest_id', '=', values['location_id'].id),
|
||||
('picking_type_id', '=', rule.picking_type_id.id),
|
||||
@@ -210,33 +229,6 @@ class ProcurementGroup(models.Model):
|
||||
('picking_id.backorder_id', '=', False),
|
||||
('product_id', '=', values['product_id'].id)]
|
||||
|
||||
@api.multi
|
||||
def _run(self, values, rule, doraise=True):
|
||||
if rule.action == 'move':
|
||||
if not rule.location_src_id:
|
||||
msg = _('No source location defined on procurement rule: %s!') % (rule.name, )
|
||||
if doraise:
|
||||
raise UserError(msg)
|
||||
else:
|
||||
self._log_next_activity(values['product_id'], msg)
|
||||
return False
|
||||
|
||||
# create the move as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example)
|
||||
# Search if picking with move for it exists already:
|
||||
group_id = False
|
||||
if rule.group_propagation_option == 'propagate':
|
||||
group_id = values.get('group_id', False) and values['group_id'].id
|
||||
elif rule.group_propagation_option == 'fixed':
|
||||
group_id = rule.group_id.id
|
||||
|
||||
data = self._get_stock_move_values(values, rule, group_id)
|
||||
# Since action_confirm launch following procurement_group we should activate it.
|
||||
move = self.env['stock.move'].sudo().create(data)
|
||||
move.assign_picking()
|
||||
move.picking_id.action_confirm()
|
||||
return True
|
||||
return False
|
||||
|
||||
@api.model
|
||||
def _get_exceptions_domain(self):
|
||||
return [('procure_method', '=', 'make_to_order'), ('move_orig_ids', '=', False)]
|
||||
@@ -260,10 +252,17 @@ class ProcurementGroup(models.Model):
|
||||
self.env['stock.move'].browse(moves_chunk).action_assign()
|
||||
if use_new_cursor:
|
||||
self._cr.commit()
|
||||
|
||||
|
||||
exception_moves = self.env['stock.move'].search(self._get_exceptions_domain())
|
||||
for move in exception_moves:
|
||||
self.env['procurement.group'].run(move._prepare_procurement_from_move(), doraise=False)
|
||||
values = move._prepare_procurement_values()
|
||||
try:
|
||||
with self._cr.savepoint():
|
||||
origin = (move.group_id and (move.group_id.name + ":") or "") + (move.rule_id and move.rule_id.name or move.origin or move.picking_id.name or "/")
|
||||
self.run(move.product_id, move.product_uom_qty, move.product_uom, move.location_id, move.rule_id and move.rule_id.name or "/", origin, values)
|
||||
except UserError as error:
|
||||
self.env['procurement.rule']._log_next_activity(move.product_id, error.name)
|
||||
|
||||
if use_new_cursor:
|
||||
self._cr.commit()
|
||||
finally:
|
||||
@@ -355,8 +354,13 @@ class ProcurementGroup(models.Model):
|
||||
qty -= substract_quantity[orderpoint.id]
|
||||
qty_rounded = float_round(qty, precision_rounding=orderpoint.product_uom.rounding)
|
||||
if qty_rounded > 0:
|
||||
self.env['procurement.group'].run(
|
||||
orderpoint._prepare_procurement_values(qty_rounded, **group['procurement_values']), doraise=False)
|
||||
values = orderpoint._prepare_procurement_values(qty_rounded, **group['procurement_values'])
|
||||
try:
|
||||
with self._cr.savepoint():
|
||||
self.env['procurement.group'].run(orderpoint.product_id, qty_rounded, orderpoint.product_uom, orderpoint.location_id,
|
||||
orderpoint.name, orderpoint.name, values)
|
||||
except UserError as error:
|
||||
self.env['procurement.rule']._log_next_activity(orderpoint.product_id, error.name)
|
||||
self._procurement_from_orderpoint_post_process([orderpoint.id])
|
||||
if use_new_cursor:
|
||||
cr.commit()
|
||||
@@ -384,4 +388,3 @@ class ProcurementGroup(models.Model):
|
||||
cr.close()
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
@@ -589,7 +589,10 @@ class StockMove(models.Model):
|
||||
|
||||
# create procurements for make to order moves
|
||||
for move in move_create_proc:
|
||||
self.env['procurement.group'].run(move._prepare_procurement_from_move())
|
||||
values = move._prepare_procurement_values()
|
||||
origin = (move.group_id and (move.group_id.name + ":") or "") + (move.rule_id and move.rule_id.name or move.origin or move.picking_id.name or "/")
|
||||
self.env['procurement.group'].run(move.product_id, move.product_uom_qty, move.product_uom, move.location_id, move.rule_id and move.rule_id.name or "/", origin,
|
||||
values)
|
||||
|
||||
move_to_confirm.write({'state': 'confirmed'})
|
||||
(move_waiting | move_create_proc).write({'state': 'waiting'})
|
||||
@@ -600,9 +603,12 @@ class StockMove(models.Model):
|
||||
self._push_apply()
|
||||
return self
|
||||
|
||||
def _prepare_procurement_from_move(self):
|
||||
def _prepare_procurement_values(self):
|
||||
""" Prepare specific key for moves or other componenets that will be created from a procurement rule
|
||||
comming from a stock move. This method could be override in order to add other custom key that could
|
||||
be used in move/po creation.
|
||||
"""
|
||||
self.ensure_one()
|
||||
origin = (self.group_id and (self.group_id.name + ":") or "") + (self.rule_id and self.rule_id.name or self.origin or self.picking_id.name or "/")
|
||||
group_id = self.group_id or False
|
||||
if self.rule_id:
|
||||
if self.rule_id.group_propagation_option == 'fixed' and self.rule_id.group_id:
|
||||
@@ -610,14 +616,8 @@ class StockMove(models.Model):
|
||||
elif self.rule_id.group_propagation_option == 'none':
|
||||
group_id = False
|
||||
return {
|
||||
'name': self.rule_id and self.rule_id.name or "/",
|
||||
'origin': origin,
|
||||
'company_id': self.company_id,
|
||||
'date_planned': self.date,
|
||||
'product_id': self.product_id,
|
||||
'product_qty': self.product_uom_qty,
|
||||
'product_uom': self.product_uom,
|
||||
'location_id': self.location_id,
|
||||
'move_dest_ids': self,
|
||||
'group_id': group_id,
|
||||
'route_ids': self.route_ids,
|
||||
|
||||
@@ -824,15 +824,12 @@ class Orderpoint(models.Model):
|
||||
|
||||
@api.multi
|
||||
def _prepare_procurement_values(self, product_qty, date=False, group=False):
|
||||
""" Prepare specific key for moves or other components that will be created from a procurement rule
|
||||
comming from an orderpoint. This method could be override in order to add other custom key that could
|
||||
be used in move/po creation.
|
||||
"""
|
||||
return {
|
||||
'name': self.name,
|
||||
'date_planned': date or self._get_date_planned(product_qty, datetime.today()),
|
||||
'product_id': self.product_id,
|
||||
'product_qty': product_qty,
|
||||
'company_id': self.company_id,
|
||||
'product_uom': self.product_uom,
|
||||
'location_id': self.location_id,
|
||||
'origin': self.name,
|
||||
'warehouse_id': self.warehouse_id,
|
||||
'orderpoint_id': self,
|
||||
'group_id': group or self.group_id,
|
||||
|
||||
@@ -17,12 +17,12 @@ class PurchaseOrderLine(models.Model):
|
||||
return res
|
||||
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = 'procurement.group'
|
||||
class ProcurementRule(models.Model):
|
||||
_inherit = 'procurement.rule'
|
||||
|
||||
@api.model
|
||||
def _prepare_purchase_order_line(self, values, rule, po, supplier):
|
||||
res = super(ProcurementGroup, self)._prepare_purchase_order_line(values, rule, po, supplier)
|
||||
def _prepare_purchase_order_line(self, product_id, product_qty, product_uom, values, po, supplier):
|
||||
res = super(ProcurementRule, self)._prepare_purchase_order_line(product_id, product_qty, product_uom, values, po, supplier)
|
||||
res['sale_line_id'] = values.get('sale_line_id', False)
|
||||
return res
|
||||
|
||||
|
||||
Reference in New Issue
Block a user