Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions fieldservice_stock/README.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

=====================
Field Service - Stock
=====================
Expand All @@ -17,7 +13,7 @@ Field Service - Stock
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github
Expand Down Expand Up @@ -64,6 +60,13 @@ If you are in a multi-warehouse situation:
- Create or select a territory
- Set the warehouse that will serve this territory

If you want to enable autovalidation of related pickings when completing
an FSM order:

- Go to Field Service > Configuration > Settings
- Under the 'Service Orders' tab, check the 'Auto Validate FSM Pickings'
option

Usage
=====

Expand Down Expand Up @@ -103,6 +106,9 @@ Contributors
- Serpent Consulting Services Pvt. Ltd. <support@serpentcs.com>
- Marcel Savegnago <marcel.savegnago@escodoo.com.br>
- Freni Patel <fpatel@opensourceintegrators.com>
- `APSL-Nagarro <https://www.apsl.tech>`__:

- Patryk Pyczko <ppyczko@apsl.net>

Other credits
-------------
Expand Down
1 change: 1 addition & 0 deletions fieldservice_stock/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"views/fsm_order.xml",
"views/stock.xml",
"views/stock_picking.xml",
"views/res_config_settings.xml",
],
"pre_init_hook": "_pre_init_hook",
"license": "AGPL-3",
Expand Down
1 change: 1 addition & 0 deletions fieldservice_stock/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
stock_rule,
stock_picking,
fsm_wizard,
res_config_settings,
)
31 changes: 31 additions & 0 deletions fieldservice_stock/models/fsm_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo.exceptions import ValidationError


class FSMOrder(models.Model):
Expand Down Expand Up @@ -54,6 +55,7 @@ def _compute_picking_ids(self):
lambda p: p.picking_type_id.code == "incoming"
)
order.return_count = len(incoming_pickings.ids)
order.move_ids = order.picking_ids.mapped("move_ids")

def action_view_delivery(self):
"""
Expand Down Expand Up @@ -94,3 +96,32 @@ def action_view_returns(self):
action["views"] = [(self.env.ref("stock.view_picking_form").id, "form")]
action["res_id"] = return_ids[0]
return action

def action_complete(self):
"""Validate related pickings before marking FSM Order as complete."""
auto_validate = (
self.env["ir.config_parameter"]
.sudo()
.get_param("fieldservice_stock.auto_validate_pickings", default=False)
)

if auto_validate:
for order in self:
for picking in order.picking_ids.filtered(
lambda p: p.state in ["confirmed", "assigned"]
):
picking.action_assign()

if any(
move.quantity < move.product_uom_qty
for move in picking.move_ids
):
raise ValidationError(
f"Not enough stock to complete transfer for FSM Order "
f"{order.name} - {picking.name}. "
f"Please check product quantities."
)

picking.button_validate()

return super().action_complete()
15 changes: 15 additions & 0 deletions fieldservice_stock/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2025 Patryk Pyczko (APSL-Nagarro)<ppyczko@apsl.net>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

auto_validate_pickings = fields.Boolean(
string="Auto Validate FSM Pickings",
config_parameter="fieldservice_stock.auto_validate_pickings",
help="If enabled, related stock pickings will be automatically "
"validated when an FSM order is completed.",
)
5 changes: 5 additions & 0 deletions fieldservice_stock/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ If you are in a multi-warehouse situation:
- Go to Field Service \> Configuration \> Territories
- Create or select a territory
- Set the warehouse that will serve this territory

If you want to enable autovalidation of related pickings when completing an FSM order:

- Go to Field Service > Configuration > Settings
- Under the 'Service Orders' tab, check the 'Auto Validate FSM Pickings' option
2 changes: 2 additions & 0 deletions fieldservice_stock/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
- Serpent Consulting Services Pvt. Ltd. \<<support@serpentcs.com>\>
- Marcel Savegnago \<<marcel.savegnago@escodoo.com.br>\>
- Freni Patel \<<fpatel@opensourceintegrators.com>\>
- [APSL-Nagarro](https://www.apsl.tech):
- Patryk Pyczko \<<ppyczko@apsl.net>\>
45 changes: 25 additions & 20 deletions fieldservice_stock/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<title>Field Service - Stock</title>
<style type="text/css">

/*
Expand Down Expand Up @@ -360,21 +360,16 @@
</style>
</head>
<body>
<div class="document">
<div class="document" id="field-service-stock">
<h1 class="title">Field Service - Stock</h1>


<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="field-service-stock">
<h1>Field Service - Stock</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:2e7f72477b0a198268b842a4ef03036a7afa7281c5e5e118a52a2b368368b017
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/field-service/tree/18.0/fieldservice_stock"><img alt="OCA/field-service" src="https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/field-service-18-0/field-service-18-0-fieldservice_stock"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/field-service&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/field-service/tree/18.0/fieldservice_stock"><img alt="OCA/field-service" src="https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/field-service-18-0/field-service-18-0-fieldservice_stock"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/field-service&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module is an add-on for the Field Service application in Odoo. It
provides a generic framework to allow you to link inventory and stock
operations with your field service operations.</p>
Expand All @@ -396,14 +391,14 @@ <h1>Field Service - Stock</h1>
</ul>
</div>
<div class="section" id="installation">
<h2><a class="toc-backref" href="#toc-entry-1">Installation</a></h2>
<h1><a class="toc-backref" href="#toc-entry-1">Installation</a></h1>
<p>To install Field Service and have the mapping features, you need to
install GeoEngine.</p>
<p>Please refer to the installation instructions available at:
<a class="reference external" href="https://github.com/OCA/geospatial/tree/13.0/base_geoengine">https://github.com/OCA/geospatial/tree/13.0/base_geoengine</a></p>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-2">Configuration</a></h2>
<h1><a class="toc-backref" href="#toc-entry-2">Configuration</a></h1>
<p>To configure this module, you need to:</p>
<ul class="simple">
<li>Go to Field Service &gt; Master Data &gt; Locations</li>
Expand All @@ -415,53 +410,64 @@ <h2><a class="toc-backref" href="#toc-entry-2">Configuration</a></h2>
<li>Create or select a territory</li>
<li>Set the warehouse that will serve this territory</li>
</ul>
<p>If you want to enable autovalidation of related pickings when completing
an FSM order:</p>
<ul class="simple">
<li>Go to Field Service &gt; Configuration &gt; Settings</li>
<li>Under the ‘Service Orders’ tab, check the ‘Auto Validate FSM Pickings’
option</li>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-3">Usage</a></h2>
<h1><a class="toc-backref" href="#toc-entry-3">Usage</a></h1>
<p>This module primarily establishes a connection between stock and field
service operations so therefore has no specific usage instructions.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-4">Known issues / Roadmap</a></h2>
<h1><a class="toc-backref" href="#toc-entry-4">Known issues / Roadmap</a></h1>
<p>The roadmap of the Field Service application is documented on
<a class="reference external" href="https://github.com/OCA/field-service/issues/1">Github</a>.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-5">Bug Tracker</a></h2>
<h1><a class="toc-backref" href="#toc-entry-5">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/field-service/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/field-service/issues/new?body=module:%20fieldservice_stock%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-6">Credits</a></h2>
<h1><a class="toc-backref" href="#toc-entry-6">Credits</a></h1>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-7">Authors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-7">Authors</a></h2>
<ul class="simple">
<li>Open Source Integrators</li>
<li>Brian McMaster</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-8">Contributors</a></h3>
<h2><a class="toc-backref" href="#toc-entry-8">Contributors</a></h2>
<ul class="simple">
<li>Brian McMaster &lt;<a class="reference external" href="mailto:brian&#64;mcmpest.com">brian&#64;mcmpest.com</a>&gt;</li>
<li>Sandip Mangukiya &lt;<a class="reference external" href="mailto:smangukiya&#64;opensourceintegrators.com">smangukiya&#64;opensourceintegrators.com</a>&gt;</li>
<li>Serpent Consulting Services Pvt. Ltd. &lt;<a class="reference external" href="mailto:support&#64;serpentcs.com">support&#64;serpentcs.com</a>&gt;</li>
<li>Marcel Savegnago &lt;<a class="reference external" href="mailto:marcel.savegnago&#64;escodoo.com.br">marcel.savegnago&#64;escodoo.com.br</a>&gt;</li>
<li>Freni Patel &lt;<a class="reference external" href="mailto:fpatel&#64;opensourceintegrators.com">fpatel&#64;opensourceintegrators.com</a>&gt;</li>
<li><a class="reference external" href="https://www.apsl.tech">APSL-Nagarro</a>:<ul>
<li>Patryk Pyczko &lt;<a class="reference external" href="mailto:ppyczko&#64;apsl.net">ppyczko&#64;apsl.net</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="other-credits">
<h3><a class="toc-backref" href="#toc-entry-9">Other credits</a></h3>
<h2><a class="toc-backref" href="#toc-entry-9">Other credits</a></h2>
<p>The development of this module has been financially supported by:</p>
<ul class="simple">
<li>Open Source Integrators &lt;<a class="reference external" href="https://opensourceintegrators.com">https://opensourceintegrators.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-10">Maintainers</a></h3>
<h2><a class="toc-backref" href="#toc-entry-10">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
Expand All @@ -476,6 +482,5 @@ <h3><a class="toc-backref" href="#toc-entry-10">Maintainers</a></h3>
</div>
</div>
</div>
</div>
</body>
</html>
1 change: 1 addition & 0 deletions fieldservice_stock/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

from . import test_fsm_stock
from . import test_fsm_wizard
from . import test_fsm_order_autovalidate
84 changes: 84 additions & 0 deletions fieldservice_stock/tests/test_fsm_order_autovalidate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from odoo import fields
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase


class TestFSMStockActionComplete(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.FSMOrder = cls.env["fsm.order"]
cls.StockPicking = cls.env["stock.picking"]
cls.StockMove = cls.env["stock.move"]
cls.Product = cls.env.ref("stock.product_cable_management_box")
cls.customer_location = cls.env.ref("stock.stock_location_customers")
cls.stock_location = cls.env.ref("stock.stock_location_stock")
cls.picking_type_out = cls.env.ref("stock.picking_type_out")
cls.partner = cls.env["res.partner"].create({"name": "Test Partner"})

cls.fsm_order = cls.FSMOrder.create(
{
"location_id": cls.env.ref("fieldservice.test_location").id,
"date_start": fields.Datetime.now(),
"date_end": fields.Datetime.now(),
"request_early": fields.Datetime.now(),
"resolution": "Test resolution",
}
)

cls.picking = cls.StockPicking.create(
{
"location_id": cls.stock_location.id,
"location_dest_id": cls.customer_location.id,
"partner_id": cls.partner.id,
"picking_type_id": cls.picking_type_out.id,
}
)

cls.stock_move = cls.StockMove.create(
{
"name": "Move Test Product",
"product_id": cls.Product.id,
"product_uom_qty": 5.0,
"product_uom": cls.Product.uom_id.id,
"location_id": cls.stock_location.id,
"location_dest_id": cls.customer_location.id,
"picking_id": cls.picking.id,
}
)

cls.picking.action_confirm()
cls.picking.fsm_order_id = cls.fsm_order.id

def test_action_complete_auto_validate_disabled(self):
"""Ensure pickings are not validated when auto_validate_pickings is False."""
self.env["ir.config_parameter"].sudo().set_param(
"fieldservice_stock.auto_validate_pickings", False
)
self.stock_move.quantity = self.stock_move.product_uom_qty
self.fsm_order.action_complete()
self.assertNotEqual(
self.picking.state,
"done",
"Picking should not validate when auto-validate is False.",
)

def test_action_complete_validates_pickings(self):
"""Ensure pickings are validated when auto_validate_pickings is True."""
self.env["ir.config_parameter"].sudo().set_param(
"fieldservice_stock.auto_validate_pickings", True
)
self.stock_move.quantity = self.stock_move.product_uom_qty
self.fsm_order.action_complete()
self.assertEqual(
self.picking.state, "done", "Picking should be validated and set to 'done'."
)

def test_action_complete_raises_error_on_insufficient_stock(self):
"""Ensure ValidationError is raised when stock is insufficient."""
self.env["ir.config_parameter"].sudo().set_param(
"fieldservice_stock.auto_validate_pickings", True
)
self.stock_move.product_uom_qty = 9999
with self.assertRaises(ValidationError):
self.fsm_order.action_complete()
22 changes: 22 additions & 0 deletions fieldservice_stock/views/res_config_settings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Copyright 2025 Patryk Pyczko (APSL-Nagarro)<ppyczko@apsl.net>
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->

<record id="res_config_settings_view_form_fsm_stock" model="ir.ui.view">
<field name="name">res.config.settings.view.form.fsm.stock</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="40" />
<field name="inherit_id" ref="fieldservice.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//block[@name='order_setting_container']" position="inside">
<setting
string="Auto Validate FSM Pickings"
help="Automatically validates related stock pickings when an FSM Order is completed."
>
<field name="auto_validate_pickings" />
</setting>
</xpath>
</field>
</record>
</odoo>
Loading