[MERGE] forward port branch saas-17 up to bfd8224dd4
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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']:
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -257,19 +257,16 @@
|
||||
<tree editable="bottom" decoration-muted="result_package_id" decoration-danger="qty_done>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="↳Put in Pack" attrs="{'invisible': [('state', 'in', ('done', 'cancel'))]}" groups="stock.group_tracking_lot"/>
|
||||
|
||||
Reference in New Issue
Block a user