[MERGE] forward port branch saas-17 up to bfd8224dd4

This commit is contained in:
Christophe Simonis
2017-08-24 21:36:04 +02:00
11 changed files with 65 additions and 41 deletions
@@ -306,7 +306,7 @@ class AccountBankStatement(models.Model):
#try to assign partner to bank_statement_line
stl_to_assign = st_lines_left.filtered(lambda stl: not stl.partner_id)
refs = {stl_to_assign.mapped('name')}
refs = set(stl_to_assign.mapped('name'))
if stl_to_assign and refs\
and st_lines_left[0].journal_id.default_credit_account_id\
and st_lines_left[0].journal_id.default_debit_account_id:
+11 -6
View File
@@ -1653,17 +1653,20 @@ class AccountPartialReconcile(models.Model):
return self
#check if the reconcilation is full
#first, gather all journal items involved in the reconciliation just created
partial_rec_set = OrderedDict.fromkeys([x for x in self])
aml_set = aml_to_balance = self.env['account.move.line']
total_debit = 0
total_credit = 0
total_amount_currency = 0
#make sure that all partial reconciliations share the same secondary currency otherwise it's not
#possible to compute the exchange difference entry and it has to be done manually.
currency = list(partial_rec_set)[0].currency_id
currency = self[0].currency_id
maxdate = '0000-00-00'
for partial_rec in partial_rec_set:
seen = set()
todo = set(self)
while todo:
partial_rec = todo.pop()
seen.add(partial_rec)
if partial_rec.currency_id != currency:
#no exchange rate entry will be created
currency = None
@@ -1682,13 +1685,15 @@ class AccountPartialReconcile(models.Model):
#currency is recorded on the partial rec and in order to check if the reconciliation is total, we need to convert the
#aml.balance in that foreign currency
total_amount_currency += aml.company_id.currency_id.with_context(date=aml.date).compute(aml.balance, partial_rec.currency_id)
for x in aml.matched_debit_ids | aml.matched_credit_ids:
partial_rec_set[x] = None
partial_rec_ids = [x.id for x in partial_rec_set]
if x not in seen:
todo.add(x)
partial_rec_ids = [x.id for x in seen]
aml_ids = aml_set.ids
#then, if the total debit and credit are equal, or the total amount in currency is 0, the reconciliation is full
digits_rounding_precision = aml_set[0].company_id.currency_id.rounding
if (currency and float_is_zero(total_amount_currency, precision_rounding=currency.rounding)) or float_compare(total_debit, total_credit, precision_rounding=digits_rounding_precision) == 0:
exchange_move_id = False
if currency and aml_to_balance:
@@ -238,7 +238,7 @@
<field name="number" class="oe_inline" attrs="{'invisible': [('state', '=', 'draft')]}"/>
<div attrs="{'invisible': [('sequence_number_next_prefix','=',False)]}">
<field name="sequence_number_next_prefix" class="oe_inline"/>
<field name="sequence_number_next" class="oe_inline" style="width: 80px !important;"/>
<field name="sequence_number_next" class="oe_inline"/>
</div>
</h1>
</div>
@@ -378,7 +378,7 @@
<div attrs="{'invisible': [('sequence_number_next_prefix','=',False)]}">
<label string="Invoice Number:"/><br/>
<field name="sequence_number_next_prefix" class="oe_inline"/>
<field name="sequence_number_next" class="oe_inline" style="width: 80px !important;"/>
<field name="sequence_number_next" class="oe_inline"/>
</div>
</h1>
<field name="type" invisible="1"/>
+3 -3
View File
@@ -196,7 +196,7 @@ class MrpWorkorder(models.Model):
move_lots[0].qty_done = new_qty
elif move.product_id.tracking == 'serial':
# Create extra pseudo record
qty_todo = new_qty - sum(move_lots.mapped('quantity'))
qty_todo = new_qty - sum(move_lots.mapped('qty_done'))
if float_compare(qty_todo, 0.0, precision_rounding=move.product_uom.rounding) > 0:
while float_compare(qty_todo, 0.0, precision_rounding=move.product_uom.rounding) > 0:
self.active_move_line_ids += self.env['stock.move.line'].new({
@@ -217,8 +217,8 @@ class MrpWorkorder(models.Model):
for move_lot in move_lots:
if qty_todo <= 0:
break
if not move_lot.lot_id and qty_todo >= move_lot.quantity:
qty_todo = qty_todo - move_lot.quantity
if not move_lot.lot_id and qty_todo >= move_lot.qty_done:
qty_todo = qty_todo - move_lot.qty_done
self.active_move_line_ids -= move_lot # Difference operator
else:
#move_lot.product_qty = move_lot.product_qty - qty_todo
+3
View File
@@ -152,6 +152,9 @@
<field name="product_qty" readonly="1"/>
<field name="lot_id" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}"/>
<field name="qty_done"/>
<field name="product_uom_id" invisible="1"/>
<field name="location_id" invisible="1"/>
<field name="location_dest_id" invisible="1"/>
<field name="move_id" invisible="1"/>
<field name="done_wo" invisible="1"/>
</tree>
+1 -1
View File
@@ -556,7 +556,7 @@ class PurchaseOrderLine(models.Model):
qty -= inv_line.uom_id._compute_quantity(inv_line.quantity, line.product_uom)
line.qty_invoiced = qty
@api.depends('order_id.state', 'move_ids.state')
@api.depends('order_id.state', 'move_ids.state', 'move_ids.product_uom_qty')
def _compute_qty_received(self):
for line in self:
if line.order_id.state not in ['purchase', 'done']:
+11
View File
@@ -36,6 +36,17 @@ class StockMove(models.Model):
subtype_id=self.env.ref('mail.mt_note').id)
return result
@api.multi
def write(self, vals):
res = super(StockMove, self).write(vals)
if 'product_uom_qty' in vals:
for move in self:
if move.state == 'done':
sale_order_lines = self.filtered(lambda move: move.procurement_id.sale_line_id and move.product_id.expense_policy == 'no').mapped('procurement_id.sale_line_id')
for line in sale_order_lines:
line.qty_delivered = line._get_delivered_qty()
return res
class StockPicking(models.Model):
_inherit = 'stock.picking'
+13 -20
View File
@@ -741,9 +741,13 @@ class StockMove(models.Model):
# the reserved quantity on the quants, convert it here in
# `product_id.uom_id` (the UOM of the quants is the UOM of the product).
move_lines_in = move.move_orig_ids.filtered(lambda m: m.state == 'done').mapped('move_line_ids')
keys_in = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id']
keys_in_groupby = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id']
def _keys_in_sorted(ml):
return (ml.location_dest_id.id, ml.lot_id.id, ml.result_package_id.id, ml.owner_id.id)
grouped_move_lines_in = {}
for k, g in groupby(sorted(move_lines_in, key=itemgetter(*keys_in)), key=itemgetter(*keys_in)):
for k, g in groupby(sorted(move_lines_in, key=_keys_in_sorted), key=itemgetter(*keys_in_groupby)):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
@@ -757,14 +761,18 @@ class StockMove(models.Model):
moves_out_siblings_to_consider = moves_out_siblings & (assigned_moves + partially_available_moves)
reserved_moves_out_siblings = moves_out_siblings.filtered(lambda m: m.state in ['partially_available', 'assigned'])
move_lines_out_reserved = (reserved_moves_out_siblings | moves_out_siblings_to_consider).mapped('move_line_ids')
keys_out = ['location_id', 'lot_id', 'package_id', 'owner_id']
keys_out_groupby = ['location_id', 'lot_id', 'package_id', 'owner_id']
def _keys_out_sorted(ml):
return (ml.location_id.id, ml.lot_id.id, ml.package_id.id, ml.owner_id.id)
grouped_move_lines_out = {}
for k, g in groupby(sorted(move_lines_out_done, key=itemgetter(*keys_out)), key=itemgetter(*keys_out)):
for k, g in groupby(sorted(move_lines_out_done, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)):
qty_done = 0
for ml in g:
qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id)
grouped_move_lines_out[k] = qty_done
for k, g in groupby(sorted(move_lines_out_reserved, key=itemgetter(*keys_out)), key=itemgetter(*keys_out)):
for k, g in groupby(sorted(move_lines_out_reserved, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)):
grouped_move_lines_out[k] = sum(self.env['stock.move.line'].concat(*list(g)).mapped('product_qty'))
available_move_lines = {key: grouped_move_lines_in[key] - grouped_move_lines_out.get(key, 0) for key in grouped_move_lines_in.keys()}
# pop key if the quantity available amount to 0
@@ -983,18 +991,3 @@ class StockMove(models.Model):
move.state = 'waiting'
else:
move.state = 'confirmed'
@api.multi
def action_show_picking(self):
view = self.env.ref('stock.view_picking_form')
return {
'name': _('Transfer'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'stock.picking',
'views': [(view.id, 'form')],
'view_id': view.id,
'target': 'new',
'res_id': self.id}
show_picking = action_show_picking
+8
View File
@@ -230,6 +230,14 @@ class StockMoveLine(models.Model):
# Unreserve and reserve following move in order to have the real reserved quantity on move_line.
next_moves |= ml.move_id.move_dest_ids.filtered(lambda move: move.state not in ('done', 'cancel'))
res = super(StockMoveLine, self).write(vals)
# As stock_account values according to a move's `product_uom_qty`, we consider that any
# done stock move should have its `quantity_done` equals to its `product_uom_qty`, and
# this is what move's `action_done` will do. So, we replicate the behavior here.
if updates or 'qty_done' in vals:
moves = self.filtered(lambda ml: ml.move_id.state == 'done').mapped('move_id')
for move in moves:
move.product_uom_qty = move.quantity_done
next_moves.do_unreserve()
next_moves.action_assign()
return res
+7
View File
@@ -2016,6 +2016,7 @@ class StockMove(TransactionCase):
move1.move_line_ids.qty_done = 1
move1.action_done()
self.assertEqual(move1.product_uom_qty, 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
@@ -2024,6 +2025,7 @@ class StockMove(TransactionCase):
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), -1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), -1.0)
self.assertEqual(move1.product_uom_qty, 2.0)
def test_edit_done_move_line_9(self):
""" Test that editing a done stock move line linked to an untracked product correctly and
@@ -2052,12 +2054,14 @@ class StockMove(TransactionCase):
move1.move_line_ids.qty_done = 1
move1.action_done()
self.assertEqual(move1.product_uom_qty, 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 0.0)
# edit once done, we actually moved 2 products
move1.move_line_ids.qty_done = 0
self.assertEqual(move1.product_uom_qty, 0.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, shelf1_location), 1.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 1.0)
@@ -2087,6 +2091,7 @@ class StockMove(TransactionCase):
quant = self.env['stock.quant']._gather(self.product1, self.stock_location)
self.assertEqual(len(quant), 0.0)
self.assertEqual(move1.product_uom_qty, 0.0)
def test_edit_done_move_line_11(self):
""" Add a move line and check if the quant is updated
@@ -2112,6 +2117,7 @@ class StockMove(TransactionCase):
picking.action_assign()
move1.move_line_ids.qty_done = 10
picking.action_done()
self.assertEqual(move1.product_uom_qty, 10.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 10.0)
self.env['stock.move.line'].create({
'picking_id': move1.move_line_ids.picking_id.id,
@@ -2124,6 +2130,7 @@ class StockMove(TransactionCase):
})
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 20.0)
move1.move_line_ids[1].qty_done = 5
self.assertEqual(move1.product_uom_qty, 15.0)
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product1, self.stock_location), 15.0)
def test_immediate_validate_1(self):
+5 -8
View File
@@ -257,19 +257,16 @@
<tree editable="bottom" decoration-muted="result_package_id" decoration-danger="qty_done&gt;product_uom_qty" decoration-success="qty_done==product_uom_qty and state!='done' and not result_package_id">
<field name="product_id" required="1"/>
<field name="product_uom_id" attrs="{'readonly': [('state', '!=', 'draft')]}" groups="product.group_uom"/>
<field name="from_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="to_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="package_id" groups="stock.group_tracking_lot" invisible="1"/>
<field name="location_id" domain="[('id', 'child_of', parent.location_id)]" invisible="1"/>
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'default_location_id': location_dest_id}" invisible="1"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="location_id" groups="stock.group_stock_multi_locations,stock.group_tracking_lot" domain="[('id', 'child_of', parent.location_id)]"/>
<field name="location_dest_id" groups="stock.group_stock_multi_locations,stock.group_tracking_lot" domain="[('id', 'child_of', parent.location_dest_id)]"/>
<field name="result_package_id" groups="stock.group_tracking_lot"/>
<field name="lots_visible" invisible="1"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="state" invisible="1"/>
<field name="lot_id" attrs="{'invisible': [('lots_visible', '=', False)]}" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}"/>
<field name="lot_id" groups="stock.group_production_lot" attrs="{'invisible': [('lots_visible', '=', False)]}" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}"/>
<field name="product_uom_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="qty_done"/>
<button name="show_details" string="Modify" type="object" icon="fa-pencil" groups="stock.group_stock_multi_locations" states="confirmed,assigned,waiting,partially_available"/>
</tree>
</field>
<button class="oe_link oe_right" name="put_in_pack" type="object" string="&#8627;Put in Pack" attrs="{'invisible': [('state', 'in', ('done', 'cancel'))]}" groups="stock.group_tracking_lot"/>