forked from Mapan/odoo17e
669 lines
30 KiB
Python
669 lines
30 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from datetime import timedelta
|
|
from odoo import Command
|
|
from odoo.fields import Datetime, Date
|
|
from odoo.tests import Form
|
|
from odoo.addons.sale_stock_renting.tests.test_rental_common import TestRentalCommon
|
|
|
|
|
|
class TestRentalWizard(TestRentalCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
def test_rental_product_flow(self):
|
|
|
|
self.assertEqual(
|
|
self.product_id.qty_available,
|
|
4
|
|
)
|
|
|
|
self.order_line_id1.write({
|
|
'product_uom_qty': 3
|
|
})
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin,
|
|
self.order_line_id1.return_date,
|
|
# self.order_line_id1.id,
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin - timedelta(days=1),
|
|
self.order_line_id1.return_date,
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin,
|
|
self.order_line_id1.return_date - timedelta(days=1),
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin - timedelta(days=1),
|
|
self.order_line_id1.return_date - timedelta(days=1),
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin + timedelta(days=1),
|
|
self.order_line_id1.return_date + timedelta(days=1),
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin,
|
|
self.order_line_id1.return_date + timedelta(days=1),
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin + timedelta(days=1),
|
|
self.order_line_id1.return_date,
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin - timedelta(days=1),
|
|
self.order_line_id1.return_date + timedelta(days=1),
|
|
), 3
|
|
)
|
|
|
|
self.assertEqual(
|
|
self.product_id._get_unavailable_qty(
|
|
self.order_line_id1.reservation_begin + timedelta(days=1),
|
|
self.order_line_id1.return_date - timedelta(days=1),
|
|
), 3
|
|
)
|
|
|
|
"""
|
|
Total Pickup
|
|
"""
|
|
|
|
self.order_line_id1.write({
|
|
'qty_delivered': 3
|
|
})
|
|
|
|
""" In sale order warehouse """
|
|
self.assertEqual(
|
|
self.product_id.with_context(
|
|
warehouse=self.order_line_id1.order_id.warehouse_id.id,
|
|
from_date=self.order_line_id1.reservation_begin,
|
|
to_date=self.order_line_id1.return_date,
|
|
).qty_available,
|
|
1
|
|
)
|
|
|
|
self.env.invalidate_all()
|
|
""" In company internal rental location (in stock valuation but not in available qty) """
|
|
self.assertEqual(
|
|
self.product_id.with_context(
|
|
location=self.env.company.rental_loc_id.id,
|
|
from_date=self.order_line_id1.start_date,
|
|
to_date=self.order_line_id1.return_date,
|
|
).qty_available,
|
|
3
|
|
)
|
|
|
|
""" In company warehouses """
|
|
self.assertEqual(
|
|
self.product_id.qty_available,
|
|
1
|
|
)
|
|
|
|
""" In company stock valuation """
|
|
self.assertEqual(
|
|
self.product_id.quantity_svl,
|
|
4
|
|
)
|
|
|
|
####################################
|
|
# Cancel deliver then re-apply
|
|
####################################
|
|
|
|
self.order_line_id1.write({'qty_delivered': 0})
|
|
self.assertEqual(self.product_id.qty_available, 4)
|
|
self.order_line_id1.write({'qty_delivered': 3})
|
|
|
|
"""
|
|
Partial Return
|
|
"""
|
|
|
|
self.order_line_id1.write({
|
|
'qty_returned': 2
|
|
})
|
|
|
|
""" In sale order warehouse """
|
|
self.assertEqual(
|
|
self.product_id.with_context(
|
|
warehouse=self.order_line_id1.order_id.warehouse_id.id
|
|
).qty_available,
|
|
3
|
|
)
|
|
|
|
""" In company internal rental location (in stock valuation but not in available qty) """
|
|
self.assertEqual(
|
|
self.product_id.with_context(
|
|
location=self.env.company.rental_loc_id.id,
|
|
from_date=self.order_line_id1.start_date,
|
|
to_date=self.order_line_id1.return_date,
|
|
).qty_available,
|
|
1
|
|
)
|
|
|
|
""" In company warehouses """
|
|
self.assertEqual(
|
|
self.product_id.qty_available,
|
|
3
|
|
)
|
|
|
|
""" In company stock valuation """
|
|
self.assertEqual(
|
|
self.product_id.quantity_svl,
|
|
4
|
|
)
|
|
|
|
"""
|
|
Total Return
|
|
"""
|
|
|
|
self.order_line_id1.write({
|
|
'qty_returned': 3
|
|
})
|
|
|
|
self.assertEqual(
|
|
self.product_id.qty_available,
|
|
4.0
|
|
)
|
|
|
|
def test_rental_lot_flow(self):
|
|
self.lots_rental_order.action_confirm()
|
|
|
|
lots = self.env['stock.lot'].search([('product_id', '=', self.tracked_product_id.id)])
|
|
rentable_lots = self.env['stock.lot']._get_available_lots(self.tracked_product_id)
|
|
self.assertEqual(set(lots.ids), set(rentable_lots.ids)) # set is here to ensure that order wont break test
|
|
|
|
self.order_line_id2.reserved_lot_ids += self.lot_id1
|
|
self.order_line_id2.product_uom_qty = 1.0
|
|
|
|
self.order_line_id2.pickedup_lot_ids += self.lot_id2
|
|
|
|
# Ensure lots are unreserved if other lots are picked up in their place
|
|
# and qty pickedup = product_uom_qty (qty reserved)
|
|
self.assertEqual(self.order_line_id2.reserved_lot_ids, self.order_line_id2.pickedup_lot_ids)
|
|
|
|
return
|
|
|
|
def test_rental_lot_concurrent(self):
|
|
"""The purpose of this test is to mimmic a concurrent picking of a rental product.
|
|
As the same lot is applied to the sol twice, its qty_delivered should be 1.
|
|
"""
|
|
so = self.lots_rental_order
|
|
sol = self.order_line_id2
|
|
lot = self.lot_id2
|
|
|
|
sol.product_uom_qty = 1.0
|
|
so.action_confirm()
|
|
|
|
wizard_vals = so.action_open_pickup()
|
|
for _i in range(2):
|
|
wizard = self.env[wizard_vals['res_model']].with_context(wizard_vals['context']).create({
|
|
'rental_wizard_line_ids': [
|
|
(0, 0, {
|
|
'order_line_id': sol.id,
|
|
'product_id': sol.product_id.id,
|
|
'qty_delivered': 1.0,
|
|
'pickedup_lot_ids':[[6, False, [lot.id]]],
|
|
})
|
|
]
|
|
})
|
|
wizard.apply()
|
|
|
|
self.assertEqual(sol.qty_delivered, len(sol.pickedup_lot_ids), "The quantity delivered should not exceed the number of picked up lots")
|
|
|
|
for _i in range(2):
|
|
wizard = self.env[wizard_vals['res_model']].with_context(wizard_vals['context']).create({
|
|
'rental_wizard_line_ids': [
|
|
(0, 0, {
|
|
'order_line_id': sol.id,
|
|
'product_id': sol.product_id.id,
|
|
'qty_returned': 1.0,
|
|
'returned_lot_ids':[[6, False, [lot.id]]],
|
|
})
|
|
]
|
|
})
|
|
wizard.apply()
|
|
|
|
self.assertEqual(sol.qty_returned, len(sol.returned_lot_ids), "The quantity returned should not exceed the number of returned lots")
|
|
|
|
def test_schedule_report(self):
|
|
"""Verify sql scheduling view consistency.
|
|
|
|
One sale.order.line with 3 different lots (reserved/pickedup/returned)
|
|
is represented by 3 sale.rental.schedule to allow grouping reservation information
|
|
by stock.lot .
|
|
|
|
Note that a lot can be pickedup (sol.pickedup_lot_ids) even if not reserved (sol.reserved_lot_ids).
|
|
"""
|
|
self.order_line_id2.reserved_lot_ids = self.lot_id1
|
|
# Avoid magic setting pickedup lots as reserved when full quantity has been pickedup
|
|
self.order_line_id2.product_uom_qty = 2.0
|
|
|
|
# Lot pickedup but not reserved.
|
|
self.order_line_id2.pickedup_lot_ids = self.lot_id2
|
|
|
|
self.assertEqual(
|
|
self.env["sale.rental.schedule"].search_count([('lot_id', '=', self.lot_id2.id)]),
|
|
1,
|
|
)
|
|
scheduling_recs = self.env["sale.rental.schedule"].search([
|
|
('order_line_id', '=', self.order_line_id2.id),
|
|
])
|
|
self.assertEqual(
|
|
len(scheduling_recs),
|
|
2, # 1 reserved, 1 pickedup
|
|
)
|
|
self.assertEqual(
|
|
scheduling_recs.mapped('report_line_status'),
|
|
["reserved", "pickedup"],
|
|
)
|
|
|
|
# More generic behavior:
|
|
# 2 reserved, 2 pickedup, 1 returned
|
|
self.order_line_id2.returned_lot_ids = self.lot_id2
|
|
self.order_line_id2.pickedup_lot_ids += self.lot_id1
|
|
self.env.invalidate_all()
|
|
scheduling_recs = self.env["sale.rental.schedule"].search([
|
|
('order_line_id', '=', self.order_line_id2.id)
|
|
])
|
|
self.assertEqual(
|
|
len(scheduling_recs),
|
|
2,
|
|
)
|
|
self.assertEqual(
|
|
scheduling_recs.lot_id,
|
|
self.lot_id1 + self.lot_id2,
|
|
)
|
|
self.assertEqual(
|
|
scheduling_recs.mapped('report_line_status'),
|
|
["pickedup", "returned"],
|
|
)
|
|
|
|
def test_lot_accuracy_in_schedule(self):
|
|
""" Schedule should only display lots that are associated with
|
|
rental order lines
|
|
"""
|
|
rental_schedule = self.env['sale.rental.schedule']
|
|
rental_transfers_group = self.env.ref('sale_stock_renting.group_rental_stock_picking')
|
|
self.env.user.groups_id = [(4, rental_transfers_group.id)]
|
|
so = self.lots_rental_order
|
|
so.company_id._create_rental_location()
|
|
self.order_line_id2.product_uom_qty = 1.0
|
|
so.order_line = [(6, 0, [self.order_line_id2.id])]
|
|
so.action_confirm()
|
|
|
|
# Rental schedule should have 1 out of the 3 total lots for `self.tracked_product_id`
|
|
self.assertEqual(
|
|
rental_schedule.search_count([('product_id', '=', self.tracked_product_id.id)]),
|
|
1
|
|
)
|
|
|
|
class TestRentalPicking(TestRentalCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
cls.env['res.config.settings'].create({'group_rental_stock_picking': True}).execute()
|
|
|
|
def test_flow_1(self):
|
|
rental_order_1 = self.sale_order_id.copy()
|
|
rental_order_1.order_line.write({'product_uom_qty': 3, 'is_rental': True})
|
|
rental_order_1.rental_start_date = self.rental_start_date
|
|
rental_order_1.rental_return_date = self.rental_return_date
|
|
rental_order_1.action_confirm()
|
|
self.assertEqual(len(rental_order_1.picking_ids), 2)
|
|
self.assertEqual([d.date() for d in rental_order_1.picking_ids.mapped('scheduled_date')],
|
|
[rental_order_1.rental_start_date.date(), rental_order_1.rental_return_date.date()])
|
|
self.assertEqual(rental_order_1.picking_ids.move_ids.mapped('product_uom_qty'), [3.0, 3.0])
|
|
|
|
outgoing_picking = rental_order_1.picking_ids.filtered(lambda p: p.picking_type_code == 'outgoing')
|
|
incoming_picking = rental_order_1.picking_ids.filtered(lambda p: p.picking_type_code == 'incoming')
|
|
|
|
outgoing_picking.move_ids.quantity = 2
|
|
backorder_wizard_dict = outgoing_picking.button_validate()
|
|
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
|
|
backorder_wizard.process()
|
|
self.assertEqual(rental_order_1.order_line.qty_delivered, 2)
|
|
self.assertEqual(rental_order_1.rental_status, 'pickup')
|
|
self.assertEqual(len(rental_order_1.picking_ids), 3)
|
|
self.assertEqual(incoming_picking.move_ids.quantity, 2)
|
|
|
|
incoming_picking.move_ids.quantity = 1
|
|
backorder_wizard_dict = incoming_picking.button_validate()
|
|
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
|
|
backorder_wizard.process()
|
|
self.assertEqual(rental_order_1.order_line.qty_returned, 1)
|
|
self.assertEqual(rental_order_1.rental_status, 'pickup')
|
|
self.assertEqual(len(rental_order_1.picking_ids), 4)
|
|
|
|
outgoing_picking_2 = rental_order_1.picking_ids.filtered(lambda p: p.picking_type_code == 'outgoing' and p.state == 'assigned')
|
|
incoming_picking_2 = rental_order_1.picking_ids.filtered(lambda p: p.picking_type_code == 'incoming' and p.state == 'assigned')
|
|
self.assertEqual(outgoing_picking_2.scheduled_date.date(), rental_order_1.rental_start_date.date())
|
|
self.assertEqual(incoming_picking_2.scheduled_date.date(), rental_order_1.rental_return_date.date())
|
|
self.assertEqual(outgoing_picking_2.move_ids.quantity, 1)
|
|
self.assertEqual(incoming_picking_2.move_ids.quantity, 1)
|
|
|
|
rental_order_1.order_line.write({'product_uom_qty': 5})
|
|
self.assertEqual(outgoing_picking_2.move_ids.product_uom_qty, 3)
|
|
self.assertEqual(incoming_picking_2.move_ids.product_uom_qty, 4)
|
|
|
|
outgoing_picking_2.move_ids.quantity = 1
|
|
backorder_wizard_dict = outgoing_picking_2.button_validate()
|
|
backorder_wizard = Form(self.env[backorder_wizard_dict['res_model']].with_context(backorder_wizard_dict['context'])).save()
|
|
backorder_wizard.process()
|
|
self.assertEqual(rental_order_1.order_line.qty_delivered, 3)
|
|
self.assertEqual(rental_order_1.rental_status, 'pickup')
|
|
self.assertEqual(len(rental_order_1.picking_ids), 5)
|
|
self.assertEqual(incoming_picking_2.move_ids.quantity, 2)
|
|
|
|
rental_order_1.order_line.write({'product_uom_qty': 4})
|
|
outgoing_picking_3 = rental_order_1.picking_ids.filtered(lambda p: p.picking_type_code == 'outgoing' and p.state == 'assigned')
|
|
self.assertEqual(outgoing_picking_3.scheduled_date.date(), rental_order_1.rental_start_date.date())
|
|
self.assertEqual(outgoing_picking_3.move_ids.product_uom_qty, 1)
|
|
self.assertEqual(incoming_picking_2.move_ids.product_uom_qty, 3)
|
|
|
|
outgoing_picking_3.button_validate()
|
|
self.assertEqual(incoming_picking_2.move_ids.quantity, 3)
|
|
self.assertEqual(rental_order_1.order_line.qty_delivered, 4)
|
|
self.assertEqual(rental_order_1.rental_status, 'return')
|
|
|
|
incoming_picking_2.button_validate()
|
|
self.assertEqual(rental_order_1.order_line.qty_returned, 4)
|
|
self.assertEqual(rental_order_1.rental_status, 'returned')
|
|
|
|
def test_flow_multisteps(self):
|
|
self.warehouse_id.delivery_steps = 'pick_pack_ship'
|
|
self.warehouse_id.reception_steps = 'three_steps'
|
|
|
|
rental_order_1 = self.sale_order_id.copy()
|
|
rental_order_1.order_line.write({'product_uom_qty': 3, 'is_rental': True})
|
|
rental_order_1.rental_start_date = self.rental_start_date
|
|
rental_order_1.rental_return_date = self.rental_return_date
|
|
rental_order_1.action_confirm()
|
|
self.assertEqual(len(rental_order_1.picking_ids), 6)
|
|
self.assertEqual([d.date() for d in rental_order_1.picking_ids.mapped('scheduled_date')],
|
|
[rental_order_1.rental_start_date.date(), rental_order_1.rental_start_date.date(), rental_order_1.rental_start_date.date(),
|
|
rental_order_1.rental_return_date.date(), rental_order_1.rental_return_date.date(), rental_order_1.rental_return_date.date()])
|
|
self.assertEqual(rental_order_1.picking_ids.move_ids.mapped('product_uom_qty'), [3.0, 3.0, 3.0, 3.0, 3.0, 3.0])
|
|
|
|
rental_order_1.order_line.write({'product_uom_qty': 4})
|
|
self.assertEqual(len(rental_order_1.picking_ids), 6)
|
|
self.assertEqual(rental_order_1.picking_ids.move_ids.mapped('product_uom_qty'), [4.0, 4.0, 4.0, 4.0, 4.0, 4.0])
|
|
|
|
pick_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(pick_picking.location_dest_id, self.warehouse_id.wh_pack_stock_loc_id)
|
|
pick_picking.button_validate()
|
|
rental_order_1.order_line.write({'product_uom_qty': 1})
|
|
self.assertEqual(len(rental_order_1.picking_ids), 7)
|
|
|
|
return_pick_picking = rental_order_1.picking_ids.filtered(lambda p: p.location_id == self.warehouse_id.wh_pack_stock_loc_id and p.location_dest_id == self.warehouse_id.lot_stock_id)
|
|
all_other_pickings = rental_order_1.picking_ids.filtered(lambda p: p.state != 'done' and p.id != return_pick_picking.id)
|
|
self.assertEqual(return_pick_picking.move_ids.product_uom_qty, 3.0)
|
|
self.assertEqual(return_pick_picking.state, 'waiting')
|
|
self.assertEqual(all_other_pickings.move_ids.mapped('product_uom_qty'), [1.0, 1.0, 1.0, 1.0, 1.0])
|
|
return_pick_picking.action_assign()
|
|
return_pick_picking.button_validate()
|
|
|
|
pack_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(pack_picking.location_dest_id, self.warehouse_id.wh_output_stock_loc_id)
|
|
pack_picking.button_validate()
|
|
|
|
out_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(out_picking.location_dest_id, self.env.company.rental_loc_id)
|
|
out_picking.button_validate()
|
|
self.assertEqual(rental_order_1.order_line.qty_delivered, 1)
|
|
|
|
incoming_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(incoming_picking.location_dest_id, self.warehouse_id.wh_input_stock_loc_id)
|
|
incoming_picking.button_validate()
|
|
self.assertEqual(rental_order_1.order_line.qty_returned, 1)
|
|
|
|
qc_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(qc_picking.location_dest_id, self.warehouse_id.wh_qc_stock_loc_id)
|
|
qc_picking.button_validate()
|
|
|
|
final_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(final_picking.location_dest_id, self.warehouse_id.lot_stock_id)
|
|
final_picking.button_validate()
|
|
|
|
def test_flow_serial(self):
|
|
empty_lot = self.env['stock.lot'].create({
|
|
'product_id': self.tracked_product_id.id,
|
|
'name': "Dofus Ocre",
|
|
'company_id': self.env.company.id,
|
|
})
|
|
available_lot = self.env['stock.lot'].create({
|
|
'product_id': self.tracked_product_id.id,
|
|
'name': "Dofawa",
|
|
'company_id': self.env.company.id,
|
|
})
|
|
available_quant = self.env['stock.quant'].create({
|
|
'product_id': self.tracked_product_id.id,
|
|
'inventory_quantity': 1.0,
|
|
'lot_id': available_lot.id,
|
|
'location_id': self.env.user._get_default_warehouse_id().lot_stock_id.id
|
|
})
|
|
reserved_lot = self.env['stock.lot'].create({
|
|
'product_id': self.tracked_product_id.id,
|
|
'name': "Dolmanax",
|
|
'company_id': self.env.company.id,
|
|
})
|
|
reserved_quant = self.env['stock.quant'].create({
|
|
'product_id': self.tracked_product_id.id,
|
|
'inventory_quantity': 1.0,
|
|
'lot_id': reserved_lot.id,
|
|
'location_id': self.env.user._get_default_warehouse_id().lot_stock_id.id
|
|
})
|
|
(available_quant + reserved_quant).action_apply_inventory()
|
|
|
|
# Reserve 1 serial
|
|
reserved_rental = self.sale_order_id.copy()
|
|
reserved_rental.order_line.write({'product_id': self.tracked_product_id.id, 'reserved_lot_ids': reserved_lot, 'product_uom_qty': 1})
|
|
reserved_rental.order_line.is_rental = True
|
|
reserved_rental.rental_start_date = self.rental_start_date
|
|
reserved_rental.rental_return_date = self.rental_return_date
|
|
reserved_rental.action_confirm()
|
|
|
|
# Test with 3 serials: 1 available, 1 reserved and 1 empty
|
|
rental_order_1 = self.sale_order_id.copy()
|
|
rental_order_1.order_line.write({'product_id': self.tracked_product_id.id, 'reserved_lot_ids': available_lot + reserved_lot + empty_lot, 'product_uom_qty': 3})
|
|
rental_order_1.order_line.is_rental = True
|
|
rental_order_1.rental_start_date = self.rental_start_date
|
|
rental_order_1.rental_return_date = self.rental_return_date
|
|
rental_order_1.action_confirm()
|
|
self.assertEqual(len(rental_order_1.picking_ids), 2)
|
|
|
|
outgoing_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(len(outgoing_picking.move_ids.move_line_ids), 3)
|
|
self.assertEqual(outgoing_picking.move_ids.move_line_ids.lot_id, self.lot_id2 + self.lot_id3 + available_lot)
|
|
|
|
outgoing_picking.button_validate()
|
|
self.assertEqual(rental_order_1.order_line.qty_delivered, 3)
|
|
self.assertEqual(available_lot.quant_ids.filtered(lambda q: q.quantity == 1).location_id, self.env.company.rental_loc_id)
|
|
self.assertEqual(self.lot_id2.quant_ids.filtered(lambda q: q.quantity == 1).location_id, self.env.company.rental_loc_id)
|
|
self.assertEqual(self.lot_id3.quant_ids.filtered(lambda q: q.quantity == 1).location_id, self.env.company.rental_loc_id)
|
|
|
|
incoming_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(len(incoming_picking.move_ids.move_line_ids), 3)
|
|
self.assertEqual(incoming_picking.move_ids.move_line_ids.lot_id, self.lot_id2 + self.lot_id3 + available_lot)
|
|
|
|
incoming_picking.button_validate()
|
|
self.assertEqual(rental_order_1.order_line.qty_returned, 3)
|
|
self.assertEqual(available_lot.quant_ids.filtered(lambda q: q.quantity == 1).location_id, self.warehouse_id.lot_stock_id)
|
|
self.assertEqual(self.lot_id2.quant_ids.filtered(lambda q: q.quantity == 1).location_id, self.warehouse_id.lot_stock_id)
|
|
self.assertEqual(self.lot_id3.quant_ids.filtered(lambda q: q.quantity == 1).location_id, self.warehouse_id.lot_stock_id)
|
|
|
|
def test_late_fee(self):
|
|
rental_order_1 = self.sale_order_id.copy()
|
|
rental_order_1.order_line.write({'product_uom_qty': 1, 'is_rental': True})
|
|
rental_order_1.rental_start_date = Datetime.now() - timedelta(days=7)
|
|
rental_order_1.rental_return_date = Datetime.now() - timedelta(days=3)
|
|
rental_order_1.action_confirm()
|
|
|
|
outgoing_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(outgoing_picking.scheduled_date.date(), rental_order_1.rental_start_date.date())
|
|
outgoing_picking.button_validate()
|
|
|
|
incoming_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
self.assertEqual(incoming_picking.scheduled_date.date(), rental_order_1.rental_return_date.date())
|
|
incoming_picking.button_validate()
|
|
|
|
self.assertEqual(len(rental_order_1.order_line), 2)
|
|
late_fee_order_line = rental_order_1.order_line.filtered(lambda l: l.product_id.type == 'service')
|
|
self.assertEqual(late_fee_order_line.price_unit, 30)
|
|
|
|
def test_buttons(self):
|
|
rental_order_1 = self.sale_order_id.copy()
|
|
rental_order_1.order_line.write({'product_uom_qty': 3, 'is_rental': True})
|
|
rental_order_1.action_confirm()
|
|
|
|
action_open_pickup = rental_order_1.action_open_pickup()
|
|
action_open_return = rental_order_1.action_open_return()
|
|
self.assertEqual(action_open_pickup.get('res_id'), rental_order_1.picking_ids[0].id)
|
|
self.assertEqual(action_open_pickup.get('domain'), '')
|
|
self.assertEqual(action_open_pickup.get('xml_id'), 'stock.action_picking_tree_all')
|
|
self.assertEqual(action_open_return.get('res_id'), 0)
|
|
self.assertEqual(action_open_return.get('domain'), [('id', 'in', rental_order_1.picking_ids.ids)])
|
|
self.assertEqual(action_open_return.get('xml_id'), 'stock.action_picking_tree_all')
|
|
|
|
ready_picking = rental_order_1.picking_ids.filtered(lambda p: p.state == 'assigned')
|
|
ready_picking.button_validate()
|
|
self.assertEqual(rental_order_1.rental_status, 'return')
|
|
|
|
action_open_return_2 = rental_order_1.action_open_return()
|
|
self.assertEqual(action_open_return_2.get('res_id'), rental_order_1.picking_ids[1].id)
|
|
self.assertEqual(action_open_return_2.get('domain'), '')
|
|
self.assertEqual(action_open_return_2.get('xml_id'), 'stock.action_picking_tree_all')
|
|
|
|
def test_create_rental_transfers(self):
|
|
""" E.g., a public/portal user signs & pays for an order via the portal
|
|
"""
|
|
public_user = self.env.ref('base.public_user')
|
|
rental_order_1 = self.sale_order_id.copy()
|
|
rental_order_1.order_line.write({'product_uom_qty': 1, 'is_rental': True})
|
|
rental_order_1.with_user(public_user).sudo().action_confirm()
|
|
self.assertTrue(rental_order_1.picking_ids)
|
|
|
|
def test_reordering_rule_forecast(self):
|
|
""" Test the rental orders will only consider outgoing rental move in the forecast
|
|
computation. """
|
|
# Set a fixed visibility_days
|
|
self.product_id.stock_quant_ids.sudo().unlink()
|
|
self.env['ir.config_parameter'].sudo().set_param('stock.visibility_days', 7)
|
|
date = Date.today() + timedelta(days=7)
|
|
|
|
rental_order_1 = self.sale_order_id.copy()
|
|
rental_order_1.order_line.write({'product_uom_qty': 1, 'is_rental': True})
|
|
rental_order_1.rental_start_date = Datetime.now() + timedelta(days=2)
|
|
rental_order_2 = self.sale_order_id.copy()
|
|
rental_order_2.order_line.write({'product_uom_qty': 2, 'is_rental': True})
|
|
rental_order_2.rental_start_date = Datetime.now() + timedelta(days=4)
|
|
rental_order_2.rental_return_date = Datetime.now() + timedelta(days=5)
|
|
self.assertEqual(self.product_id.with_context(date=date).qty_available, 0)
|
|
(rental_order_1 | rental_order_2).action_confirm()
|
|
self.env['stock.warehouse.orderpoint'].action_open_orderpoints()
|
|
self.assertEqual(self.product_id.orderpoint_ids.lead_days_date, date)
|
|
self.assertEqual(self.product_id.orderpoint_ids.qty_forecast, -2)
|
|
|
|
def test_rental_available_reserved_lots(self):
|
|
"""
|
|
The aim is to check if the `available_reserved_lots` compute
|
|
field correctly determines whether a batch we want to reserve
|
|
will be available or not.
|
|
"""
|
|
# Create a sale order to reserve a lot.
|
|
sale_order_id1 = self.env['sale.order'].create({
|
|
'partner_id': self.cust1.id,
|
|
'rental_start_date': Datetime.today() + timedelta(days=3),
|
|
'rental_return_date': Datetime.today() + timedelta(days=4),
|
|
})
|
|
order_line_id1 = self.env['sale.order.line'].create({
|
|
'order_id': sale_order_id1.id,
|
|
'product_id': self.tracked_product_id.id,
|
|
'reserved_lot_ids': [Command.set(self.lot_id1.ids)],
|
|
'product_uom_qty': 1.0,
|
|
})
|
|
order_line_id1.update({'is_rental': True})
|
|
sale_order_id1.action_confirm()
|
|
|
|
# Create a second sale order and modify reserved lots, start date
|
|
# and return date to check if `available_reserved_lots` is correct.
|
|
sale_order_id = self.env['sale.order'].create({
|
|
'partner_id': self.cust1.id,
|
|
'rental_start_date': Datetime.today() + timedelta(days=3),
|
|
'rental_return_date': Datetime.today() + timedelta(days=4),
|
|
})
|
|
order_line_id = self.env['sale.order.line'].create({
|
|
'order_id': sale_order_id.id,
|
|
'product_id': self.tracked_product_id.id,
|
|
'product_uom_qty': 1.0,
|
|
})
|
|
order_line_id.update({'is_rental': True})
|
|
|
|
self.assertEqual(order_line_id.available_reserved_lots, True)
|
|
order_line_id.reserved_lot_ids = self.lot_id2
|
|
self.assertEqual(order_line_id.available_reserved_lots, True)
|
|
order_line_id.reserved_lot_ids += self.lot_id1
|
|
self.assertEqual(order_line_id.available_reserved_lots, False)
|
|
sale_order_id.write({
|
|
'rental_start_date': Datetime.today() + timedelta(days=1),
|
|
'rental_return_date': Datetime.today() + timedelta(days=2),
|
|
})
|
|
self.assertEqual(order_line_id.available_reserved_lots, True)
|
|
sale_order_id.write({
|
|
'rental_start_date': Datetime.today() + timedelta(days=5),
|
|
'rental_return_date': Datetime.today() + timedelta(days=6),
|
|
})
|
|
# will return in stock in time
|
|
self.assertEqual(order_line_id.available_reserved_lots, True)
|
|
|
|
# Validate the delivery of the first order to test the flow
|
|
# when the product is not in stock
|
|
delivery = sale_order_id1.picking_ids.filtered(lambda p: p.picking_type_id == self.warehouse_id.out_type_id)
|
|
delivery.button_validate()
|
|
self.assertEqual(delivery.state, 'done')
|
|
self.assertEqual(delivery.move_ids.lot_ids, self.lot_id1)
|
|
self.assertEqual(order_line_id1.available_reserved_lots, True)
|
|
sale_order_id.write({
|
|
'rental_start_date': Datetime.today() + timedelta(days=3),
|
|
'rental_return_date': Datetime.today() + timedelta(days=4),
|
|
})
|
|
order_line_id.reserved_lot_ids = self.lot_id2
|
|
self.assertEqual(order_line_id.available_reserved_lots, True)
|
|
order_line_id.reserved_lot_ids += self.lot_id1
|
|
self.assertEqual(order_line_id.available_reserved_lots, False)
|
|
sale_order_id.write({
|
|
'rental_start_date': Datetime.today() + timedelta(days=1),
|
|
'rental_return_date': Datetime.today() + timedelta(days=2),
|
|
})
|
|
self.assertEqual(order_line_id.available_reserved_lots, False)
|
|
sale_order_id.write({
|
|
'rental_start_date': Datetime.today() + timedelta(days=5),
|
|
'rental_return_date': Datetime.today() + timedelta(days=6),
|
|
})
|
|
self.assertEqual(order_line_id.available_reserved_lots, True)
|