'Basic stock operation on incoming and outgoing shipment.'

@mute_logger('odoo.addons.base.ir.ir_model', 'odoo.models')
def test_00_picking_create_and_transfer_quantity(self):

    LotObj = self.env['stock.production.lot']
    picking_in = self.PickingObj.create({'partner_id': self.partner_delta_id, 'picking_type_id': self.picking_type_in, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location})
    self.MoveObj.create({'name': self.productA.name, 'product_id': self.productA.id, 'product_uom_qty': 1, 'product_uom': self.productA.uom_id.id, 'picking_id': picking_in.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location})
    self.MoveObj.create({'name': self.productB.name, 'product_id': self.productB.id, 'product_uom_qty': 1, 'product_uom': self.productB.uom_id.id, 'picking_id': picking_in.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location})
    self.MoveObj.create({'name': self.productC.name, 'product_id': self.productC.id, 'product_uom_qty': 10, 'product_uom': self.productC.uom_id.id, 'picking_id': picking_in.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location})
    self.MoveObj.create({'name': self.productD.name, 'product_id': self.productD.id, 'product_uom_qty': 10, 'product_uom': self.productD.uom_id.id, 'picking_id': picking_in.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location})
    self.MoveObj.create({'name': self.productD.name, 'product_id': self.productD.id, 'product_uom_qty': 5, 'product_uom': self.productD.uom_id.id, 'picking_id': picking_in.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location})
    for move in picking_in.move_lines:
        self.assertEqual(move.state, 'draft', 'Wrong    state    of    move    line.')
    picking_in.action_confirm()
    for move in picking_in.move_lines:
        self.assertEqual(move.state, 'assigned', 'Wrong    state    of    move    line.')
    picking_in.do_prepare_partial()
    self.StockPackObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_in.id)]).write({'product_qty': 4.0})
    self.StockPackObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_in.id)]).write({'product_qty': 5.0})
    self.StockPackObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_in.id)]).write({'product_qty': 5.0})
    self.StockPackObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_in.id)]).write({'product_qty': 5.0})
    lot2_productC = LotObj.create({'name': 'C    Lot    2', 'product_id': self.productC.id})
    self.StockPackObj.create({'product_id': self.productC.id, 'product_qty': 2, 'product_uom_id': self.productC.uom_id.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location, 'picking_id': picking_in.id, 'pack_lot_ids': [(0, 0, {'lot_id': lot2_productC.id, 'qty': 2.0})]})
    self.StockPackObj.create({'product_id': self.productD.id, 'product_qty': 2, 'product_uom_id': self.productD.uom_id.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location, 'picking_id': picking_in.id})
    packs = self.StockPackObj.search([('picking_id', '=', picking_in.id)])
    total_qty = [pack.product_qty for pack in packs]
    self.assertEqual(sum(total_qty), 23, ('Wrong    quantity    in    pack    operation    (%s    found    instead    of    23)' % sum(total_qty)))
    picking_in.do_transfer()
    self.assertEqual(len(picking_in.move_lines), 6, 'Wrong    number    of    move    lines.')
    self.assertEqual(picking_in.state, 'done', 'Incoming    shipment    state    should    be    done.')
    for move in picking_in.move_lines:
        self.assertEqual(move.state, 'done', 'Wrong    state    of    move    line.')
    moves = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_in.id)])
    a_done_qty = [move.product_uom_qty for move in moves]
    self.assertEqual(set(a_done_qty), set([1.0, 3.0]), 'Wrong    move    quantity    for    product    A.')
    moves = self.MoveObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_in.id)])
    b_done_qty = [move.product_uom_qty for move in moves]
    self.assertEqual(set(b_done_qty), set([4.0, 1.0]), 'Wrong    move    quantity    for    product    B.')
    c_done_qty = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_in.id)], limit=1).product_uom_qty
    self.assertEqual(c_done_qty, 7.0, ('Wrong    move    quantity    of    product    C    (%s    found    instead    of    7)' % c_done_qty))
    d_done_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_in.id)], limit=1).product_uom_qty
    self.assertEqual(d_done_qty, 7.0, ('Wrong    move    quantity    of    product    D    (%s    found    instead    of    7)' % d_done_qty))
    back_order_in = self.PickingObj.search([('backorder_id', '=', picking_in.id)])
    self.assertEqual(len(back_order_in), 1, 'Back    order    should    be    created.')
    self.assertEqual(len(back_order_in.move_lines), 3, 'Wrong    number    of    move    lines.')
    moves = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', back_order_in.id)])
    product_c_qty = [move.product_uom_qty for move in moves]
    self.assertEqual(sum(product_c_qty), 3.0, ('Wrong    move    quantity    of    product    C    (%s    found    instead    of    3)' % product_c_qty))
    moves = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_in.id)])
    product_d_qty = [move.product_uom_qty for move in moves]
    self.assertEqual(sum(product_d_qty), 8.0, ('Wrong    move    quantity    of    product    D    (%s    found    instead    of    8)' % product_d_qty))
    picking_out = self.PickingObj.create({'partner_id': self.partner_agrolite_id, 'picking_type_id': self.picking_type_out, 'location_id': self.stock_location, 'location_dest_id': self.customer_location})
    self.MoveObj.create({'name': self.productA.name, 'product_id': self.productA.id, 'product_uom_qty': 10, 'product_uom': self.productA.uom_id.id, 'picking_id': picking_out.id, 'location_id': self.stock_location, 'location_dest_id': self.customer_location})
    self.MoveObj.create({'name': self.productB.name, 'product_id': self.productB.id, 'product_uom_qty': 5, 'product_uom': self.productB.uom_id.id, 'picking_id': picking_out.id, 'location_id': self.stock_location, 'location_dest_id': self.customer_location})
    self.MoveObj.create({'name': self.productC.name, 'product_id': self.productC.id, 'product_uom_qty': 3, 'product_uom': self.productC.uom_id.id, 'picking_id': picking_out.id, 'location_id': self.stock_location, 'location_dest_id': self.customer_location})
    self.MoveObj.create({'name': self.productD.name, 'product_id': self.productD.id, 'product_uom_qty': 10, 'product_uom': self.productD.uom_id.id, 'picking_id': picking_out.id, 'location_id': self.stock_location, 'location_dest_id': self.customer_location})
    picking_out.action_confirm()
    for move in picking_out.move_lines:
        self.assertEqual(move.state, 'confirmed', 'Wrong    state    of    move    line.')
    picking_out.action_assign()
    self.assertEqual(picking_out.move_lines[0].state, 'confirmed', 'Wrong    state    of    move    line.')
    self.assertEqual(picking_out.move_lines[1].state, 'assigned', 'Wrong    state    of    move    line.')
    self.assertEqual(picking_out.move_lines[2].state, 'assigned', 'Wrong    state    of    move    line.')
    self.assertEqual(picking_out.move_lines[3].state, 'confirmed', 'Wrong    state    of    move    line.')
    aval_a_qty = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability
    self.assertEqual(aval_a_qty, 4.0, ('Wrong    move    quantity    availability    of    product    A    (%s    found    instead    of    4)' % aval_a_qty))
    aval_b_qty = self.MoveObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability
    self.assertEqual(aval_b_qty, 5.0, ('Wrong    move    quantity    availability    of    product    B    (%s    found    instead    of    5)' % aval_b_qty))
    aval_c_qty = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability
    self.assertEqual(aval_c_qty, 3.0, ('Wrong    move    quantity    availability    of    product    C    (%s    found    instead    of    3)' % aval_c_qty))
    aval_d_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability
    self.assertEqual(aval_d_qty, 7.0, ('Wrong    move    quantity    availability    of    product    D    (%s    found    instead    of    7)' % aval_d_qty))
    picking_out.do_prepare_partial()
    self.StockPackObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_out.id)]).write({'product_qty': 2.0})
    self.StockPackObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_out.id)]).write({'product_qty': 3.0})
    self.StockPackObj.create({'product_id': self.productB.id, 'product_qty': 2, 'product_uom_id': self.productB.uom_id.id, 'location_id': self.stock_location, 'location_dest_id': self.customer_location, 'picking_id': picking_out.id})
    self.StockPackObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_out.id)]).write({'product_qty': 2.0, 'pack_lot_ids': [(0, 0, {'lot_id': lot2_productC.id, 'qty': 2.0})]})
    self.StockPackObj.create({'product_id': self.productC.id, 'product_qty': 3, 'product_uom_id': self.productC.uom_id.id, 'location_id': self.stock_location, 'location_dest_id': self.customer_location, 'picking_id': picking_out.id})
    self.StockPackObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_out.id)]).write({'product_qty': 6.0})
    picking_out.do_transfer()
    self.assertEqual(picking_out.state, 'done', 'Wrong    state    of    outgoing    shipment.')
    self.assertEqual(len(picking_out.move_lines), 5, 'Wrong    number    of    move    lines')
    for move in picking_out.move_lines:
        self.assertEqual(move.state, 'done', 'Wrong    state    of    move    line.')
    back_order_out = self.PickingObj.search([('backorder_id', '=', picking_out.id)])
    self.assertEqual(len(back_order_out), 1, 'Back    order    should    be    created.')
    self.assertEqual(len(back_order_out.move_lines), 2, 'Wrong    number    of    move    lines')
    product_a_qty = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', back_order_out.id)], limit=1).product_uom_qty
    self.assertEqual(product_a_qty, 8.0, ('Wrong    move    quantity    of    product    A    (%s    found    instead    of    8)' % product_a_qty))
    product_d_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_out.id)], limit=1).product_uom_qty
    self.assertEqual(product_d_qty, 4.0, ('Wrong    move    quantity    of    product    D    (%s    found    instead    of    4)' % product_d_qty))
    quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
    total_qty = [quant.qty for quant in quants]
    self.assertEqual(sum(total_qty), 2.0, ('Expecting    2.0    Unit    ,    got    %.4f    Unit    on    location    stock!' % sum(total_qty)))
    self.assertEqual(self.productA.qty_available, 2.0, ('Wrong    quantity    available    (%s    found    instead    of    2.0)' % self.productA.qty_available))
    quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location)])
    self.assertFalse(quants, 'No    quant    should    found    as    outgoing    shipment    took    everything    out    of    stock.')
    self.assertEqual(self.productB.qty_available, 0.0, 'Product    B    should    have    zero    quantity    available.')
    quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location)])
    total_qty = [quant.qty for quant in quants]
    self.assertEqual(sum(total_qty), 2.0, ('Expecting    2.0    Unit,    got    %.4f    Unit    on    location    stock!' % sum(total_qty)))
    self.assertEqual(self.productC.qty_available, 2.0, ('Wrong    quantity    available    (%s    found    instead    of    2.0)' % self.productC.qty_available))
    quant = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location)], limit=1)
    self.assertEqual(quant.qty, 1.0, ('Expecting    1.0    Unit    ,    got    %.4f    Unit    on    location    stock!' % quant.qty))
    self.assertEqual(self.productD.qty_available, 1.0, ('Wrong    quantity    available    (%s    found    instead    of    1.0)' % self.productD.qty_available))
    lot3_productC = LotObj.create({'name': 'Lot    3', 'product_id': self.productC.id})
    lot4_productC = LotObj.create({'name': 'Lot    4', 'product_id': self.productC.id})
    lot5_productC = LotObj.create({'name': 'Lot    5', 'product_id': self.productC.id})
    lot6_productC = LotObj.create({'name': 'Lot    6', 'product_id': self.productC.id})
    lot1_productD = LotObj.create({'name': 'Lot    1', 'product_id': self.productD.id})
    lot2_productD = LotObj.create({'name': 'Lot    2', 'product_id': self.productD.id})
    back_order_in.action_confirm()
    self.assertEqual(back_order_in.state, 'assigned', ('Wrong    state    of    incoming    shipment    back    order:    %s    instead    of    %s' % (back_order_in.state, 'assigned')))
    for move in back_order_in.move_lines:
        self.assertEqual(move.state, 'assigned', 'Wrong    state    of    move    line.')
    back_order_in.do_prepare_partial()
    packD = self.StockPackObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_in.id)])
    self.assertEqual(len(packD), 1, 'Wrong    number    of    pack    operation.')
    packD.write({'product_qty': 4, 'pack_lot_ids': [(0, 0, {'lot_id': lot1_productD.id, 'qty': 4.0})]})
    self.StockPackObj.create({'product_id': self.productD.id, 'product_qty': 4, 'product_uom_id': self.productD.uom_id.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location, 'picking_id': back_order_in.id, 'pack_lot_ids': [(0, 0, {'lot_id': lot1_productD.id, 'qty': 4.0})]})
    packCs = self.StockPackObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', back_order_in.id)], limit=1)
    packCs.write({'product_qty': 1, 'pack_lot_ids': [(0, 0, {'lot_id': lot3_productC.id, 'qty': 1.0})]})
    self.StockPackObj.create({'product_id': self.productC.id, 'product_qty': 1, 'product_uom_id': self.productC.uom_id.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location, 'picking_id': back_order_in.id, 'pack_lot_ids': [(0, 0, {'lot_id': lot4_productC.id, 'qty': 1.0})]})
    self.StockPackObj.create({'product_id': self.productC.id, 'product_qty': 2, 'product_uom_id': self.productC.uom_id.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location, 'picking_id': back_order_in.id, 'pack_lot_ids': [(0, 0, {'lot_id': lot5_productC.id, 'qty': 2.0})]})
    self.StockPackObj.create({'product_id': self.productC.id, 'product_qty': 2, 'product_uom_id': self.productC.uom_id.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location, 'picking_id': back_order_in.id, 'pack_lot_ids': [(0, 0, {'lot_id': lot6_productC.id, 'qty': 2.0})]})
    self.StockPackObj.create({'product_id': self.productA.id, 'product_qty': 10, 'product_uom_id': self.productA.uom_id.id, 'location_id': self.supplier_location, 'location_dest_id': self.stock_location, 'picking_id': back_order_in.id})
    back_order_in.do_transfer()
    self.assertEqual(len(back_order_in.move_lines), 6, 'Wrong    number    of    move    lines')
    self.assertEqual(back_order_in.state, 'done', 'Wrong    state    of    picking.')
    for move in back_order_in.move_lines:
        self.assertEqual(move.state, 'done', 'Wrong    state    of    move    lines.')
    movesA = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', back_order_in.id)])
    self.assertEqual(movesA.product_uom_qty, 10, ('Wrong    move    quantity    of    product    A    (%s    found    instead    of    10)' % movesA.product_uom_qty))
    movesC = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', back_order_in.id)])
    c_done_qty = [move.product_uom_qty for move in movesC]
    self.assertEqual(set(c_done_qty), set([3.0, 1.0, 2.0]), 'Wrong    quantity    of    moves    product    C.')
    movesD = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', back_order_in.id)])
    d_done_qty = [move.product_uom_qty for move in movesD]
    self.assertEqual(set(d_done_qty), set([3.0, 5.0]), 'Wrong    quantity    of    moves    product    D.')
    self.assertFalse(self.PickingObj.search([('backorder_id', '=', back_order_in.id)]), 'Should    not    create    any    back    order.')
    quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
    total_qty = [quant.qty for quant in quants]
    self.assertEqual(sum(total_qty), 12.0, ('Wrong    total    stock    location    quantity    (%s    found    instead    of    12)' % sum(total_qty)))
    self.assertEqual(self.productA.qty_available, 12.0, ('Wrong    quantity    available    (%s    found    instead    of    12)' % self.productA.qty_available))
    quants = self.StockQuantObj.search([('product_id', '=', self.productB.id), ('location_id', '=', self.stock_location)])
    self.assertFalse(quants, 'No    quant    should    found    as    outgoing    shipment    took    everything    out    of    stock')
    self.assertEqual(self.productB.qty_available, 0.0, 'Total    quantity    in    stock    should    be    0    as    the    backorder    took    everything    out    of    stock')
    quants = self.StockQuantObj.search([('product_id', '=', self.productC.id), ('location_id', '=', self.stock_location)])
    total_qty = [quant.qty for quant in quants]
    self.assertEqual(sum(total_qty), 8.0, ('Wrong    total    stock    location    quantity    (%s    found    instead    of    8)' % sum(total_qty)))
    self.assertEqual(self.productC.qty_available, 8.0, ('Wrong    quantity    available    (%s    found    instead    of    8)' % self.productC.qty_available))
    quants = self.StockQuantObj.search([('product_id', '=', self.productD.id), ('location_id', '=', self.stock_location)])
    total_qty = [quant.qty for quant in quants]
    self.assertEqual(sum(total_qty), 9.0, ('Wrong    total    stock    location    quantity    (%s    found    instead    of    9)' % sum(total_qty)))
    self.assertEqual(self.productD.qty_available, 9.0, ('Wrong    quantity    available    (%s    found    instead    of    9)' % self.productD.qty_available))
    back_order_out.do_prepare_partial()
    back_order_out.do_transfer()
    quants = self.StockQuantObj.search([('product_id', '=', self.productA.id), ('location_id', '=', self.stock_location)])
    total_qty = [quant.qty for quant in quants]
    self.assertGreaterEqual(float_round(sum(total_qty), precision_rounding=0.0001), 1, 'Total    stock    location    quantity    for    product    A    should    not    be    nagative.')
