Skip to content

Commit

Permalink
[IMP] fieldservice_vehicle_stock: Add fsm_vehicle_out for vehicle-to-…
Browse files Browse the repository at this point in the history
…location transfers

- Introduced `fsm_vehicle_out` field in `stock.picking.type` to support unloading inventory from FSM Vehicles.
- Updated transfer validation to handle both loading (fsm_vehicle_in) and unloading (fsm_vehicle_out) operations.
- Ensured proper location updates and validation when moving stock between FSM vehicles and locations.

This improvement extends the existing vehicle loading logic to also control stock transfers from vehicles to other locations.
  • Loading branch information
ppyczko committed Feb 4, 2025
1 parent dd1cbbd commit 402952e
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 67 deletions.
45 changes: 28 additions & 17 deletions fieldservice_vehicle_stock/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,7 @@ and process stock moves with field service vehicles.
In field service operations, the general flow of inventory is as follows:
Stock Location -> Vehicle Location -> Customer Location

Initially there is a demand for product in the Customer Location, but we
are not sure which field service vehicle needs to load that product until
a field service order and vehicle is planned.

This module will automatically update pickings linked to field service orders
so that inventory is moved to the proper vehicle storage locations.
This module will automatically update pickings linked to field service orders, ensuring that inventory is moved to the correct vehicle storage locations. These pickings have to be of an operation type that is used to load or unload a vehicle.

**Table of contents**

Expand Down Expand Up @@ -82,17 +77,33 @@ rule's Destination Location.
Usage
=====

To use this module you must first generate some stock moves that
that will be transferred using a stock operation type which supports
FSM vehicle loading.

In order to confirm the transfer, a FSM Vehicle must be set. The FSM
Vehicle can be set manually on the transfer in the Additional Info tab
or it will be automatically set if the transfer has a linked FSM Order
and a vehicle is assigned to that order.

When the FSM Vehicle is updated on a transfer, the destination locations
are updated to reflect the vehicle storage location.
1. **Create a Location for the Vehicle**
- Navigate to Inventory > Configuration > Locations and create a new location.
- Set a name.
- Set the `Parent Location` to `Vehicles`.
- Set the location type to `Internal Location`.
- Save the location.

2. **Create an FSM Vehicle**
- Navigate to Field Service > Master Data > Vehicles and create a new vehicle.
- Set a name.
- Assign a driver.
- Assign the location you created in step 1.
- Save the vehicle.
- Enter the driver's record and set the `Default Vehicle` field to the vehicle you just created.

3. **Generate Stock Moves**
- Navigate to Inventory > Operations > Transfers and create a new transfer.
- On the `Operation Type` field, select an operation type that supports FSM vehicle loading or unloading. Examples of this include `Vehicle Loading`, to load a vehicle from stock, and `Location Delivery`, to unload a vehicle to a customer location.
- Add the products you want to transfer and save the transfer.
- By default, the Source Location or Destination Location (depending on the selected operation type) will be set to the default `Vehicles` location.

4. **Validate the Transfer**
- In the `Additional Info` tab, set the FSM Vehicle on the transfer.
- If you link an FSM order to the transfer, and the FSM order has a vehicle assigned with a storage location that is a child of the `Vehicles` location, the vehicle and its corresponding location will be automatically set on the transfer.
- When validating the picking, the destination location of the picking and it's move lines will be updated to the vehicle's storage location. The assigned products will be moved from or to the vehicle location, depending on the selected operation type.
- If you try to confirm a transfer without setting the FSM Vehicle, an error will be raised.
- If you try to set a vehicle or link an FSM order with a vehicle whose storage location is not a child of the Vehicles location, an error will be raised.

Known issues / Roadmap
======================
Expand Down
10 changes: 10 additions & 0 deletions fieldservice_vehicle_stock/data/fsm_stock_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
<field name="sequence_code">OUT</field>
<field name="warehouse_id" eval="False" />
<field name="default_location_src_id" ref="stock_location_vehicle" />
<field
name="default_location_dest_id"
ref="stock.stock_location_customers"
/>
<field name="fsm_vehicle_out" eval="True" />
</record>

<!-- Route: Stock to Vehicle to Location -->
Expand Down Expand Up @@ -99,6 +104,10 @@
<field name="code">incoming</field>
<field name="sequence_code">IN</field>
<!-- <field name="warehouse_id" eval="False" /> -->
<field
name="default_location_src_id"
ref="stock.stock_location_customers"
/>
<field name="default_location_dest_id" ref="stock_location_vehicle" />
<field
name="return_picking_type_id"
Expand Down Expand Up @@ -134,6 +143,7 @@
<field name="default_location_src_id" ref="stock_location_vehicle" />
<field name="default_location_dest_id" ref="stock.stock_location_stock" />
<field name="return_picking_type_id" ref="picking_type_output_to_vehicle" />
<field name="fsm_vehicle_out" eval="True" />
</record>

<record id="stock_rule_vehicle_to_input" model="stock.rule">
Expand Down
2 changes: 1 addition & 1 deletion fieldservice_vehicle_stock/models/fsm_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class FSMOrder(models.Model):
def assign_vehicle_to_pickings(self):
for rec in self:
for picking in rec.picking_ids:
if picking.state in ("waiting", "confirmed", "assigned"):
if picking.state in ("draft", "waiting", "confirmed", "assigned"):
picking.fsm_vehicle_id = self.vehicle_id.id or False

def write(self, vals):
Expand Down
68 changes: 50 additions & 18 deletions fieldservice_vehicle_stock/models/stock_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,38 @@ class StockPicking(models.Model):

fsm_vehicle_id = fields.Many2one("fsm.vehicle", string="Vehicle")

def _action_done(self):
"""Verify that any pickings with an operation type which requires
loading onto a FSM Vehicle have a vehicle assigned
"""
res = {}
def _check_and_update_vehicle_storage(self):
"""Ensure a vehicle is assigned for the picking type and update its storage location"""
for rec in self:
if rec.picking_type_id.fsm_vehicle_in:
if (
rec.picking_type_id.fsm_vehicle_in
or rec.picking_type_id.fsm_vehicle_out
):
if rec.fsm_vehicle_id:
picking = rec.with_context(vehicle_id=rec.fsm_vehicle_id.id)
res = super(StockPicking, picking)._action_done()
rec.update_vehicle_storage()
rec = rec.with_context(vehicle_id=rec.fsm_vehicle_id.id)
else:
raise UserError(
_("You must provide the vehicle for this picking type.")
)
res = super(StockPicking, rec)._action_done()
return res

def action_assign(self):
"""Verify that any pickings with an operation type which requires
loading onto or unloading from a FSM Vehicle have a vehicle assigned
and ensure the vehicle's storage location is correctly set before reserving
stock to the picking
"""
self._check_and_update_vehicle_storage()
return super().action_assign()

def _action_done(self):
"""Verify that any pickings with an operation type which requires
loading onto or unloading from a FSM Vehicle have a vehicle assigned
and ensure the vehicle's storage location is correctly set before
completing the picking
"""
self._check_and_update_vehicle_storage()
return super()._action_done()

def prepare_fsm_values(self, fsm_order):
res = {}
Expand All @@ -48,23 +64,39 @@ def write(self, vals):
def update_vehicle_storage(self):
"""Update the destination location to the FSM Vehicle's storage location"""
for picking in self.filtered(
lambda x: x.picking_type_id.fsm_vehicle_in
lambda x: (
x.picking_type_id.fsm_vehicle_in or x.picking_type_id.fsm_vehicle_out
)
and x.state not in ["done", "cancel"]
):
# Common validation for both 'vehicle in' and 'vehicle out'
vehicle_location = (
picking.fsm_vehicle_id.inventory_location_id or picking.location_dest_id
)
if vehicle_location == picking.location_dest_id:
continue
allowed_destinations = self.env["stock.location"].search(
[("id", "child_of", picking.location_dest_id.id)]
vehicle_parent_location = self.env.ref(
"fieldservice_vehicle_stock.stock_location_vehicle"
)
if vehicle_location not in allowed_destinations:
if vehicle_location not in self.env["stock.location"].search(
[("id", "child_of", vehicle_parent_location.id)]
):
raise UserError(
_(
"The inventory location of the FSM vehicle must be a "
"descendant of the tranfer's intended destination."
"descendant of the Vehicles location."
)
)
else:

# Handle fsm_vehicle_in: Transferring to vehicle
if picking.picking_type_id.fsm_vehicle_in:
if vehicle_location == picking.location_dest_id:
continue
# Update the destination location
picking.write({"location_dest_id": vehicle_location.id})
picking.move_line_ids.write({"location_dest_id": vehicle_location.id})

# Handle fsm_vehicle_out: Transferring from vehicle
elif picking.picking_type_id.fsm_vehicle_out:
if vehicle_location == picking.location_id:
continue
picking.write({"location_id": vehicle_location.id})
picking.move_line_ids.write({"location_id": vehicle_location.id})

Check warning on line 102 in fieldservice_vehicle_stock/models/stock_picking.py

View check run for this annotation

Codecov / codecov/patch

fieldservice_vehicle_stock/models/stock_picking.py#L100-L102

Added lines #L100 - L102 were not covered by tests
7 changes: 7 additions & 0 deletions fieldservice_vehicle_stock/models/stock_picking_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ class StockPickingType(models.Model):
help="""Check this box for operation types that will be used
to load inventory on FSM Vehicles.""",
)

fsm_vehicle_out = fields.Boolean(
"Used to unload a Field Service Vehicle",
default=False,
help="""Check this box for operation types that will be used
to unload inventory from FSM Vehicles.""",
)
7 changes: 1 addition & 6 deletions fieldservice_vehicle_stock/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,4 @@ and process stock moves with field service vehicles.
In field service operations, the general flow of inventory is as follows:
Stock Location -> Vehicle Location -> Customer Location

Initially there is a demand for product in the Customer Location, but we
are not sure which field service vehicle needs to load that product until
a field service order and vehicle is planned.

This module will automatically update pickings linked to field service orders
so that inventory is moved to the proper vehicle storage locations.
This module will automatically update pickings linked to field service orders, ensuring that inventory is moved to the correct vehicle storage locations. These pickings have to be of an operation type that is used to load or unload a vehicle.
34 changes: 25 additions & 9 deletions fieldservice_vehicle_stock/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
To use this module you must first generate some stock moves that
that will be transferred using a stock operation type which supports
FSM vehicle loading.
1. **Create a Location for the Vehicle**
- Navigate to Inventory > Configuration > Locations and create a new location.
- Set a name.
- Set the `Parent Location` to `Vehicles`.
- Set the location type to `Internal Location`.
- Save the location.

In order to confirm the transfer, a FSM Vehicle must be set. The FSM
Vehicle can be set manually on the transfer in the Additional Info tab
or it will be automatically set if the transfer has a linked FSM Order
and a vehicle is assigned to that order.
2. **Create an FSM Vehicle**
- Navigate to Field Service > Master Data > Vehicles and create a new vehicle.
- Set a name.
- Assign a driver.
- Assign the location you created in step 1.
- Save the vehicle.
- Enter the driver's record and set the `Default Vehicle` field to the vehicle you just created.

When the FSM Vehicle is updated on a transfer, the destination locations
are updated to reflect the vehicle storage location.
3. **Generate Stock Moves**
- Navigate to Inventory > Operations > Transfers and create a new transfer.
- On the `Operation Type` field, select an operation type that supports FSM vehicle loading or unloading. Examples of this include `Vehicle Loading`, to load a vehicle from stock, and `Location Delivery`, to unload a vehicle to a customer location.
- Add the products you want to transfer and save the transfer.
- By default, the Source Location or Destination Location (depending on the selected operation type) will be set to the default `Vehicles` location.

4. **Validate the Transfer**
- In the `Additional Info` tab, set the FSM Vehicle on the transfer.
- If you link an FSM order to the transfer, and the FSM order has a vehicle assigned with a storage location that is a child of the `Vehicles` location, the vehicle and its corresponding location will be automatically set on the transfer.
- When validating the picking, the destination location of the picking and it's move lines will be updated to the vehicle's storage location. The assigned products will be moved from or to the vehicle location, depending on the selected operation type.
- If you try to confirm a transfer without setting the FSM Vehicle, an error will be raised.
- If you try to set a vehicle or link an FSM order with a vehicle whose storage location is not a child of the Vehicles location, an error will be raised.
39 changes: 25 additions & 14 deletions fieldservice_vehicle_stock/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,7 @@ <h1 class="title">Field Service Vehicles - Stock</h1>
and process stock moves with field service vehicles.</p>
<p>In field service operations, the general flow of inventory is as follows:
Stock Location -&gt; Vehicle Location -&gt; Customer Location</p>
<p>Initially there is a demand for product in the Customer Location, but we
are not sure which field service vehicle needs to load that product until
a field service order and vehicle is planned.</p>
<p>This module will automatically update pickings linked to field service orders
so that inventory is moved to the proper vehicle storage locations.</p>
<p>This module will automatically update pickings linked to field service orders, ensuring that inventory is moved to the correct vehicle storage locations. These pickings have to be of an operation type that is used to load or unload a vehicle.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
Expand Down Expand Up @@ -425,15 +421,30 @@ <h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>To use this module you must first generate some stock moves that
that will be transferred using a stock operation type which supports
FSM vehicle loading.</p>
<p>In order to confirm the transfer, a FSM Vehicle must be set. The FSM
Vehicle can be set manually on the transfer in the Additional Info tab
or it will be automatically set if the transfer has a linked FSM Order
and a vehicle is assigned to that order.</p>
<p>When the FSM Vehicle is updated on a transfer, the destination locations
are updated to reflect the vehicle storage location.</p>
<p>1. <strong>Create a Location for the Vehicle</strong>
- Navigate to Inventory &gt; Configuration &gt; Locations and create a new location.
- Set a name.
- Set the <cite>Parent Location</cite> to <cite>Vehicles</cite>.
- Set the location type to <cite>Internal Location</cite>.
- Save the location.</p>
<p>2. <strong>Create an FSM Vehicle</strong>
- Navigate to Field Service &gt; Master Data &gt; Vehicles and create a new vehicle.
- Set a name.
- Assign a driver.
- Assign the location you created in step 1.
- Save the vehicle.
- Enter the driver’s record and set the <cite>Default Vehicle</cite> field to the vehicle you just created.</p>
<p>3. <strong>Generate Stock Moves</strong>
- Navigate to Inventory &gt; Operations &gt; Transfers and create a new transfer.
- On the <cite>Operation Type</cite> field, select an operation type that supports FSM vehicle loading or unloading. Examples of this include <cite>Vehicle Loading</cite>, to load a vehicle from stock, and <cite>Location Delivery</cite>, to unload a vehicle to a customer location.
- Add the products you want to transfer and save the transfer.
- By default, the Source Location or Destination Location (depending on the selected operation type) will be set to the default <cite>Vehicles</cite> location.</p>
<p>4. <strong>Validate the Transfer</strong>
- In the <cite>Additional Info</cite> tab, set the FSM Vehicle on the transfer.
- If you link an FSM order to the transfer, and the FSM order has a vehicle assigned with a storage location that is a child of the <cite>Vehicles</cite> location, the vehicle and its corresponding location will be automatically set on the transfer.
- When validating the picking, the destination location of the picking and it’s move lines will be updated to the vehicle’s storage location. The assigned products will be moved from or to the vehicle location, depending on the selected operation type.
- If you try to confirm a transfer without setting the FSM Vehicle, an error will be raised.
- If you try to set a vehicle or link an FSM order with a vehicle whose storage location is not a child of the Vehicles location, an error will be raised.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#toc-entry-3">Known issues / Roadmap</a></h1>
Expand Down
14 changes: 12 additions & 2 deletions fieldservice_vehicle_stock/tests/test_fsm_vehicle_stock.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Copyright (C) 2022, Brian McMaster
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase
from odoo.tests.common import TransactionCase, tagged


@tagged("post_install", "-at_install")
class TestFSMVehicleStock(TransactionCase):
@classmethod
def setUpClass(cls):
Expand Down Expand Up @@ -36,23 +37,29 @@ def setUpClass(cls):
}
)

# Fleet model (required when fieldservice_fleet is installed)
cls.fleet_model = cls.env.ref("fleet.vehicle_1")

# Set up FSM Vehicles with inventory locations
cls.fsm_veh_1 = cls.env["fsm.vehicle"].create(
{
"name": "Vehicle 1",
"inventory_location_id": cls.veh_1_loc.id,
"fleet_vehicle_id": cls.fleet_model.id,
}
)
cls.fsm_veh_2 = cls.env["fsm.vehicle"].create(
{
"name": "Vehicle 2",
"inventory_location_id": cls.veh_2_loc.id,
"fleet_vehicle_id": cls.fleet_model.id,
}
)
cls.fsm_veh_bad_loc = cls.env["fsm.vehicle"].create(
{
"name": "Vehicle with Incorrect Location",
"inventory_location_id": cls.non_vehicle_stock_loc.id,
"fleet_vehicle_id": cls.fleet_model.id,
}
)

Expand Down Expand Up @@ -101,7 +108,9 @@ def setUpClass(cls):
)

def test_fsm_vehicle_stock(self):
self.picking_out.action_assign()
# Test assing quantities to transfer w/out a vehicle
with self.assertRaises(UserError):
self.picking_out.action_assign()
# Test confirm transfer w/out a vehicle
with self.assertRaises(UserError):
self.picking_out._action_done()
Expand All @@ -117,6 +126,7 @@ def test_fsm_vehicle_stock(self):
# Test same vehicle is on the transfer
self.assertEqual(self.picking_out.fsm_vehicle_id, self.fsm_veh_1)
# Test correct vehicle storage location is on the transfer
self.picking_out.action_assign()
move_line = self.move.move_line_ids
self.assertEqual(move_line.location_dest_id, self.veh_1_loc)
# confirm the transfer
Expand Down
1 change: 1 addition & 0 deletions fieldservice_vehicle_stock/views/stock_picking_type.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<field name="arch" type="xml">
<field name="show_reserved" position="after">
<field name="fsm_vehicle_in" />
<field name="fsm_vehicle_out" />
</field>
</field>
</record>
Expand Down

0 comments on commit 402952e

Please sign in to comment.