最近有需求需要调用库存补货规则,流程稍微有点复杂,在这里简单整理下
# 1. 订单确认调用
@api.multi
def _action_confirm(self):
for order in self:
order.order_line._action_launch_stock_rule() # 调用规则了
super(SaleOrder, self)._action_confirm()
# 2.调用规则
@api.multi
def _action_launch_stock_rule(self):
"""
Launch procurement group run method with required/custom fields genrated by a
sale order line. procurement group will launch '_run_pull', '_run_buy' or '_run_manufacture'
depending on the sale order line product rule.
"""
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
errors = []
for line in self:
if line.state != 'sale' or not line.product_id.type in ('consu','product'):
continue
qty = line._get_qty_procurement()
if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0:
continue
group_id = line.order_id.procurement_group_id
if not group_id:
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,
'partner_id': line.order_id.partner_shipping_id.id,
})
line.order_id.procurement_group_id = group_id
else:
# In case the procurement group is already created and the order was
# cancelled, we need to update certain values of the group.
updated_vals = {}
if group_id.partner_id != line.order_id.partner_shipping_id:
updated_vals.update({'partner_id': line.order_id.partner_shipping_id.id})
if group_id.move_type != line.order_id.picking_policy:
updated_vals.update({'move_type': line.order_id.picking_policy})
if updated_vals:
group_id.write(updated_vals)
values = line._prepare_procurement_values(group_id=group_id)
product_qty = line.product_uom_qty - qty
procurement_uom = line.product_uom
quant_uom = line.product_id.uom_id
get_param = self.env['ir.config_parameter'].sudo().get_param
if procurement_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1':
product_qty = line.product_uom._compute_quantity(product_qty, quant_uom, rounding_method='HALF-UP')
procurement_uom = quant_uom
try:
self.env['procurement.group'].run(line.product_id, product_qty, procurement_uom, line.order_id.partner_shipping_id.property_stock_customer, line.name, line.order_id.name, values) # 这块代码执行玩就生成出库单了
except UserError as error:
errors.append(error.name)
if errors:
raise UserError('
'.join(errors))
return True
# 3. 获取规则
@api.model
def run(self, product_id, product_qty, product_uom, location_id, name, origin, values):
""" Method used in a procurement case. The purpose is to supply the
product passed as argument in the location also given as an argument.
In order to be able to find a suitable location that provide the product
it will search among stock.rule.
"""
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(product_id, location_id, values) # 获取规则记录
if not rule:
raise UserError(_('No procurement rule found in location "%s" for product "%s".
Check routes configuration.') % (location_id.display_name, product_id.display_name))
action = 'pull' if rule.action == 'pull_push' else rule.action
if hasattr(rule, '_run_%s' % action):
getattr(rule, '_run_%s' % action)(product_id, product_qty, product_uom, location_id, name, origin, values) # 使用规则生成调波单
else:
_logger.error("The method _run_%s doesn't exist on the procument rules" % action)
return True
# 4. 使用规则生成调拨单
def _run_pull(self, product_id, product_qty, product_uom, location_id, name, origin, values):
if not self.location_src_id:
msg = _('No source location defined on stock 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().with_context(force_company=data.get('company_id', False)).create(data) # 在这里创建stock_move记录
move._action_confirm() # stock.move 在确认时创建stock.picking
return True
# 5. stock.move 确认并创建picking
def _action_confirm(self, merge=True, merge_into=False):
""" Confirms stock move or put it in waiting if it's linked to another move.
:param: merge: According to this boolean, a newly confirmed move will be merged
in another move of the same picking sharing its characteristics.
"""
move_create_proc = self.env['stock.move']
move_to_confirm = self.env['stock.move']
move_waiting = self.env['stock.move']
to_assign = {}
for move in self:
# if the move is preceeded, then it's waiting (if preceeding move is done, then action_assign has been called already and its state is already available)
if move.move_orig_ids:
move_waiting |= move
else:
if move.procure_method == 'make_to_order':
move_create_proc |= move
else:
move_to_confirm |= move
if move._should_be_assigned():
key = (move.group_id.id, move.location_id.id, move.location_dest_id.id)
if key not in to_assign:
to_assign[key] = self.env['stock.move']
to_assign[key] |= move
# create procurements for make to order moves
for move in move_create_proc:
values = move._prepare_procurement_values()
origin = (move.group_id and move.group_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'})
# assign picking in batch for all confirmed move that share the same details
for moves in to_assign.values():
moves._assign_picking()
self._push_apply()
if merge:
return self._merge_moves(merge_into=merge_into)
return self