Variants
diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py
index 36e428a7730..f9250471af0 100644
--- a/addons/purchase/models/purchase.py
+++ b/addons/purchase/models/purchase.py
@@ -601,59 +601,70 @@ class PurchaseOrderLine(models.Model):
price_unit = order.currency_id.compute(price_unit, order.company_id.currency_id, round=False)
return price_unit
+ @api.multi
+ def _prepare_stock_moves(self, picking):
+ """ Prepare the stock moves data for one order line. This function returns a list of
+ dictionary ready to be used in stock.move's create()
+ """
+ self.ensure_one()
+ res = []
+ if self.product_id.type not in ['product', 'consu']:
+ return res
+ qty = 0.0
+ price_unit = self._get_stock_move_price_unit()
+ for move in self.move_ids.filtered(lambda x: x.state != 'cancel'):
+ qty += move.product_qty
+ template = {
+ 'name': self.name or '',
+ 'product_id': self.product_id.id,
+ 'product_uom': self.product_uom.id,
+ 'date': self.order_id.date_order,
+ 'date_expected': self.date_planned,
+ 'location_id': self.order_id.partner_id.property_stock_supplier.id,
+ 'location_dest_id': self.order_id._get_destination_location(),
+ 'picking_id': picking.id,
+ 'partner_id': self.order_id.dest_address_id.id,
+ 'move_dest_id': False,
+ 'state': 'draft',
+ 'purchase_line_id': self.id,
+ 'company_id': self.order_id.company_id.id,
+ 'price_unit': price_unit,
+ 'picking_type_id': self.order_id.picking_type_id.id,
+ 'group_id': self.order_id.group_id.id,
+ 'procurement_id': False,
+ 'origin': self.order_id.name,
+ 'route_ids': self.order_id.picking_type_id.warehouse_id and [(6, 0, [x.id for x in self.order_id.picking_type_id.warehouse_id.route_ids])] or [],
+ 'warehouse_id': self.order_id.picking_type_id.warehouse_id.id,
+ }
+ # Fullfill all related procurements with this po line
+ diff_quantity = self.product_qty - qty
+ for procurement in self.procurement_ids:
+ # If the procurement has some moves already, we should deduct their quantity
+ sum_existing_moves = sum(x.product_qty for x in procurement.move_ids if x.state != 'cancel')
+ existing_proc_qty = procurement.product_id.uom_id._compute_quantity(sum_existing_moves, procurement.product_uom)
+ procurement_qty = procurement.product_uom._compute_quantity(procurement.product_qty, self.product_uom) - existing_proc_qty
+ if float_compare(procurement_qty, 0.0, precision_rounding=procurement.product_uom.rounding) > 0 and float_compare(diff_quantity, 0.0, precision_rounding=self.product_uom.rounding) > 0:
+ tmp = template.copy()
+ tmp.update({
+ 'product_uom_qty': min(procurement_qty, diff_quantity),
+ 'move_dest_id': procurement.move_dest_id.id, # move destination is same as procurement destination
+ 'procurement_id': procurement.id,
+ 'propagate': procurement.rule_id.propagate,
+ })
+ res.append(tmp)
+ diff_quantity -= min(procurement_qty, diff_quantity)
+ if float_compare(diff_quantity, 0.0, precision_rounding=self.product_uom.rounding) > 0:
+ template['product_uom_qty'] = diff_quantity
+ res.append(template)
+ return res
+
@api.multi
def _create_stock_moves(self, picking):
moves = self.env['stock.move']
done = self.env['stock.move'].browse()
for line in self:
- if line.product_id.type not in ['product', 'consu']:
- continue
- qty = 0.0
- price_unit = line._get_stock_move_price_unit()
- for move in line.move_ids.filtered(lambda x: x.state != 'cancel'):
- qty += move.product_qty
- template = {
- 'name': line.name or '',
- 'product_id': line.product_id.id,
- 'product_uom': line.product_uom.id,
- 'date': line.order_id.date_order,
- 'date_expected': line.date_planned,
- 'location_id': line.order_id.partner_id.property_stock_supplier.id,
- 'location_dest_id': line.order_id._get_destination_location(),
- 'picking_id': picking.id,
- 'partner_id': line.order_id.dest_address_id.id,
- 'move_dest_id': False,
- 'state': 'draft',
- 'purchase_line_id': line.id,
- 'company_id': line.order_id.company_id.id,
- 'price_unit': price_unit,
- 'picking_type_id': line.order_id.picking_type_id.id,
- 'group_id': line.order_id.group_id.id,
- 'procurement_id': False,
- 'origin': line.order_id.name,
- 'route_ids': line.order_id.picking_type_id.warehouse_id and [(6, 0, [x.id for x in line.order_id.picking_type_id.warehouse_id.route_ids])] or [],
- 'warehouse_id':line.order_id.picking_type_id.warehouse_id.id,
- }
- # Fullfill all related procurements with this po line
- diff_quantity = line.product_qty - qty
- for procurement in line.procurement_ids:
- # If the procurement has some moves already, we should deduct their quantity
- sum_existing_moves = sum(x.product_qty for x in procurement.move_ids if x.state != 'cancel')
- existing_proc_qty = procurement.product_id.uom_id._compute_quantity(sum_existing_moves, procurement.product_uom)
- procurement_qty = procurement.product_uom._compute_quantity(procurement.product_qty, line.product_uom) - existing_proc_qty
- if float_compare(procurement_qty, 0.0, precision_rounding=procurement.product_uom.rounding) > 0 and float_compare(diff_quantity, 0.0, precision_rounding=line.product_uom.rounding) > 0:
- tmp = template.copy()
- tmp.update({
- 'product_uom_qty': min(procurement_qty, diff_quantity),
- 'move_dest_id': procurement.move_dest_id.id, #move destination is same as procurement destination
- 'procurement_id': procurement.id,
- 'propagate': procurement.rule_id.propagate,
- })
- done += moves.create(tmp)
- diff_quantity -= min(procurement_qty, diff_quantity)
- if float_compare(diff_quantity, 0.0, precision_rounding=line.product_uom.rounding) > 0:
- template['product_uom_qty'] = diff_quantity
- done += moves.create(template)
+ for val in line._prepare_stock_moves(picking):
+ done += moves.create(val)
return done
@api.multi
diff --git a/addons/purchase/views/purchase_views.xml b/addons/purchase/views/purchase_views.xml
index 47122f20599..df918b60dd1 100644
--- a/addons/purchase/views/purchase_views.xml
+++ b/addons/purchase/views/purchase_views.xml
@@ -210,6 +210,7 @@
+
diff --git a/addons/resource/models/resource.py b/addons/resource/models/resource.py
index 582716e4801..c661b0c5151 100644
--- a/addons/resource/models/resource.py
+++ b/addons/resource/models/resource.py
@@ -300,34 +300,34 @@ class ResourceCalendar(models.Model):
# Computes start_dt, end_dt (with default values if not set) + off-interval work limits
work_limits = []
if start_dt is None and end_dt is not None:
- start_dt = end_dt.replace(hour=0, minute=0, second=0)
+ start_dt = end_dt.replace(hour=0, minute=0, second=0, microsecond=0)
elif start_dt is None:
- start_dt = datetime.datetime.now().replace(hour=0, minute=0, second=0)
+ start_dt = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
else:
- work_limits.append((start_dt.replace(hour=0, minute=0, second=0), start_dt))
+ work_limits.append((start_dt.replace(hour=0, minute=0, second=0, microsecond=0), start_dt))
if end_dt is None:
- end_dt = start_dt.replace(hour=23, minute=59, second=59)
+ end_dt = start_dt.replace(hour=23, minute=59, second=59, microsecond=999999)
else:
- work_limits.append((end_dt, end_dt.replace(hour=23, minute=59, second=59)))
+ work_limits.append((end_dt, end_dt.replace(hour=23, minute=59, second=59, microsecond=999999)))
assert start_dt.date() == end_dt.date(), 'get_working_intervals_of_day is restricted to one day'
intervals = []
- work_dt = start_dt.replace(hour=0, minute=0, second=0)
+ work_dt = start_dt.replace(hour=0, minute=0, second=0, microsecond=0)
# no calendar: try to use the default_interval, then return directly
if not self:
working_interval = []
if default_interval:
- working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0),
- start_dt.replace(hour=default_interval[1], minute=0, second=0))
+ working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0, microsecond=0),
+ start_dt.replace(hour=default_interval[1], minute=0, second=0, microsecond=0))
intervals = self.interval_remove_leaves(working_interval, work_limits)
return intervals
working_intervals = []
tz_info = fields.Datetime.context_timestamp(self, work_dt).tzinfo
for calendar_working_day in self.get_attendances_for_weekday(start_dt):
- dt_f = work_dt.replace(hour=0, minute=0, second=0) + timedelta(seconds=(calendar_working_day.hour_from * 3600))
- dt_t = work_dt.replace(hour=0, minute=0, second=0) + timedelta(seconds=(calendar_working_day.hour_to * 3600))
+ dt_f = work_dt.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(seconds=(calendar_working_day.hour_from * 3600))
+ dt_t = work_dt.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(seconds=(calendar_working_day.hour_to * 3600))
# adapt tz
working_interval = (
@@ -369,12 +369,12 @@ class ResourceCalendar(models.Model):
resource_id=None, default_interval=None):
hours = 0.0
for day in rrule.rrule(rrule.DAILY, dtstart=start_dt,
- until=(end_dt + timedelta(days=1)).replace(hour=0, minute=0, second=0),
+ until=(end_dt + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0),
byweekday=self.get_weekdays()):
- day_start_dt = day.replace(hour=0, minute=0, second=0)
+ day_start_dt = day.replace(hour=0, minute=0, second=0, microsecond=0)
if start_dt and day.date() == start_dt.date():
day_start_dt = start_dt
- day_end_dt = day.replace(hour=23, minute=59, second=59)
+ day_end_dt = day.replace(hour=23, minute=59, second=59, microsecond=999999)
if end_dt and day.date() == end_dt.date():
day_end_dt = end_dt
hours += self.get_working_hours_of_date(
@@ -528,7 +528,7 @@ class ResourceCalendar(models.Model):
intervals = []
planned_days = 0
iterations = 0
- current_datetime = day_date.replace(hour=0, minute=0, second=0)
+ current_datetime = day_date.replace(hour=0, minute=0, second=0, microsecond=0)
while planned_days < days and iterations < 100:
working_intervals = self.get_working_intervals_of_day(
@@ -585,7 +585,7 @@ class ResourceCalendar(models.Model):
saas-3, counts leave hours instead of all-day leaves."""
return self.schedule_hours(
hours * -1.0,
- day_dt=dt_from.replace(minute=0, second=0),
+ day_dt=dt_from.replace(minute=0, second=0, microsecond=0),
compute_leaves=True, resource_id=resource,
default_interval=(8, 16)
)
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index 18fdb4a44f5..5f3fef9b915 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -664,7 +664,7 @@ class SaleOrderLine(models.Model):
vals = line._prepare_order_line_procurement(group_id=line.order_id.procurement_group_id.id)
vals['product_qty'] = line.product_uom_qty - qty
- new_proc = self.env["procurement.order"].create(vals)
+ new_proc = self.env["procurement.order"].with_context(procurement_autorun_defer=True).create(vals)
new_proc.message_post_with_view('mail.message_origin_link',
values={'self': new_proc, 'origin': line.order_id},
subtype_id=self.env.ref('mail.mt_note').id)
diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml
index 794ed8ea5b4..1c5cc4f086d 100644
--- a/addons/sale/views/sale_views.xml
+++ b/addons/sale/views/sale_views.xml
@@ -290,6 +290,7 @@
+
diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py
index 46a586774c2..65bbdf51a07 100644
--- a/addons/sale_stock/models/sale_order.py
+++ b/addons/sale_stock/models/sale_order.py
@@ -111,8 +111,9 @@ class SaleOrderLine(models.Model):
def _compute_qty_delivered_updateable(self):
for line in self:
if line.product_id.type not in ('consu', 'product'):
- return super(SaleOrderLine, self)._compute_qty_delivered_updateable()
- line.qty_delivered_updateable = False
+ super(SaleOrderLine, line)._compute_qty_delivered_updateable()
+ else:
+ line.qty_delivered_updateable = False
@api.onchange('product_id')
def _onchange_product_id_set_customer_lead(self):
diff --git a/addons/sale_stock/models/stock.py b/addons/sale_stock/models/stock.py
index 4c05ef79156..e0e96a32213 100644
--- a/addons/sale_stock/models/stock.py
+++ b/addons/sale_stock/models/stock.py
@@ -65,11 +65,12 @@ class StockPicking(models.Model):
res = super(StockPicking, self)._create_backorder(backorder_moves)
for picking in self.filtered(lambda pick: pick.picking_type_id.code == 'outgoing'):
backorder = picking.search([('backorder_id', '=', picking.id)])
- order = self.env['sale.order'].search([('procurement_group_id', '=', backorder.group_id.id)])
- backorder.message_post_with_view(
- 'mail.message_origin_link',
- values={'self': backorder, 'origin': order},
- subtype_id=self.env.ref('mail.mt_note').id)
+ if backorder.group_id: # origin from a sale
+ order = self.env['sale.order'].search([('procurement_group_id', '=', backorder.group_id.id)])
+ backorder.message_post_with_view(
+ 'mail.message_origin_link',
+ values={'self': backorder, 'origin': order},
+ subtype_id=self.env.ref('mail.mt_note').id)
return res
diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py
index e7cfb8ccf4a..8baec91e569 100644
--- a/addons/stock/models/stock_warehouse.py
+++ b/addons/stock/models/stock_warehouse.py
@@ -527,7 +527,7 @@ class Warehouse(models.Model):
routes = self.env['stock.location.route'].search([('supplier_wh_id', '=', self.id)])
pulls = Pull.search(['&', ('route_id', 'in', routes.ids), ('location_id.usage', '=', 'transit')])
pulls.write({
- 'location_src_id': new_location,
+ 'location_src_id': new_location.id,
'procure_method': change_to_multiple and "make_to_order" or "make_to_stock"})
if not change_to_multiple:
# If single delivery we should create the necessary MTO rules for the resupply
diff --git a/addons/stock/views/stock_pack_operation_views.xml b/addons/stock/views/stock_pack_operation_views.xml
index 1f79f6fd758..5b6919dab83 100644
--- a/addons/stock/views/stock_pack_operation_views.xml
+++ b/addons/stock/views/stock_pack_operation_views.xml
@@ -21,8 +21,8 @@