From 6abf4f0a8202f002b5b135dbe07be9446eb49461 Mon Sep 17 00:00:00 2001 From: brian10048 Date: Sat, 24 Nov 2018 14:28:34 -0500 Subject: [PATCH 001/138] [ADD] fieldservice_stock 0.0.1 --- fieldservice_stock/__init__.py | 4 + fieldservice_stock/__manifest__.py | 30 +++++ fieldservice_stock/data/fsm_stock_data.xml | 144 +++++++++++++++++++++ fieldservice_stock/models/__init__.py | 7 + fieldservice_stock/models/fsm_location.py | 11 ++ fieldservice_stock/models/fsm_vehicle.py | 11 ++ fieldservice_stock/readme/CONFIGURE.rst | 3 + fieldservice_stock/readme/CONTRIBUTORS.rst | 1 + fieldservice_stock/readme/CREDITS.rst | 3 + fieldservice_stock/readme/DESCRIPTION.rst | 2 + fieldservice_stock/readme/INSTALL.rst | 6 + fieldservice_stock/readme/ROADMAP.rst | 2 + fieldservice_stock/readme/USAGE.rst | 3 + fieldservice_stock/views/fsm_location.xml | 14 ++ fieldservice_stock/views/fsm_vehicle.xml | 14 ++ 15 files changed, 255 insertions(+) create mode 100644 fieldservice_stock/__init__.py create mode 100644 fieldservice_stock/__manifest__.py create mode 100644 fieldservice_stock/data/fsm_stock_data.xml create mode 100644 fieldservice_stock/models/__init__.py create mode 100644 fieldservice_stock/models/fsm_location.py create mode 100644 fieldservice_stock/models/fsm_vehicle.py create mode 100644 fieldservice_stock/readme/CONFIGURE.rst create mode 100644 fieldservice_stock/readme/CONTRIBUTORS.rst create mode 100644 fieldservice_stock/readme/CREDITS.rst create mode 100644 fieldservice_stock/readme/DESCRIPTION.rst create mode 100644 fieldservice_stock/readme/INSTALL.rst create mode 100644 fieldservice_stock/readme/ROADMAP.rst create mode 100644 fieldservice_stock/readme/USAGE.rst create mode 100644 fieldservice_stock/views/fsm_location.xml create mode 100644 fieldservice_stock/views/fsm_vehicle.xml diff --git a/fieldservice_stock/__init__.py b/fieldservice_stock/__init__.py new file mode 100644 index 0000000000..da2feecfe9 --- /dev/null +++ b/fieldservice_stock/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py new file mode 100644 index 0000000000..5740b09ee3 --- /dev/null +++ b/fieldservice_stock/__manifest__.py @@ -0,0 +1,30 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Field Service Stock add-on', + 'summary': 'Inventory and Stock operations for Field Service', + 'version': '11.0.0.0.1', + 'category': 'Field Service', + 'author': 'Open Source Integrators, Brian McMaster, Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/field-service', + 'depends': [ + 'base_geolocalize', + 'mail', + 'fieldservice', + 'stock', + ], + 'data': [ + 'data/fsm_stock_data.xml', + 'views/fsm_location.xml', + 'views/fsm_vehicle.xml', + ], + 'installable': True, + 'license': 'AGPL-3', + 'development_status': 'Beta', + 'maintainers': [ + 'brian10048', + 'wolfhall', + 'max3903', + ], +} diff --git a/fieldservice_stock/data/fsm_stock_data.xml b/fieldservice_stock/data/fsm_stock_data.xml new file mode 100644 index 0000000000..4c3750e2c8 --- /dev/null +++ b/fieldservice_stock/data/fsm_stock_data.xml @@ -0,0 +1,144 @@ + + + + + Field + view + + + + + Vehicle + view + + + + + + + + Vehicle Loading + VL + 5 + + + + + Vehicle Loading + + internal + + + + + + + Output to Vehicle + 3 + + + + + Warehouse → Vehicle + move + + + make_to_stock + + + + + + + Location Delivery + LD + 5 + + + + + Location Delivery + + outgoing + + + + + + Vehicle to Location + 3 + + + + + Vehicle → Location + move + + + make_to_stock + + + + + + + Location Return + 3 + + + + + + Location Pickup + LP + 5 + + + + + Location Pickup + + internal + + + + + + Warehouse → Vehicle + move + + + make_to_stock + + + + + + + + Vehicle Returns + VR + 5 + + + + + Vehicle Returns + + internal + + + + + + + Vehicle → Warehouse + + + manual + + + + + + diff --git a/fieldservice_stock/models/__init__.py b/fieldservice_stock/models/__init__.py new file mode 100644 index 0000000000..4f639d9629 --- /dev/null +++ b/fieldservice_stock/models/__init__.py @@ -0,0 +1,7 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import ( + fsm_location, + fsm_vehicle +) diff --git a/fieldservice_stock/models/fsm_location.py b/fieldservice_stock/models/fsm_location.py new file mode 100644 index 0000000000..8890b3d983 --- /dev/null +++ b/fieldservice_stock/models/fsm_location.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FSMLocation(models.Model): + _inherit = 'fsm.location' + + inventory_location = fields.Many2one('stock.location', + 'Inventory Location') diff --git a/fieldservice_stock/models/fsm_vehicle.py b/fieldservice_stock/models/fsm_vehicle.py new file mode 100644 index 0000000000..eb4f6a21e3 --- /dev/null +++ b/fieldservice_stock/models/fsm_vehicle.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FSMVehicle(models.Model): + _inherit = 'fsm.vehicle' + + inventory_location = fields.Many2one('stock.location', + 'Inventory Location') diff --git a/fieldservice_stock/readme/CONFIGURE.rst b/fieldservice_stock/readme/CONFIGURE.rst new file mode 100644 index 0000000000..eff3513290 --- /dev/null +++ b/fieldservice_stock/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +To configure this module, you need to: + +* Go to Field Service > Configuration > Settings diff --git a/fieldservice_stock/readme/CONTRIBUTORS.rst b/fieldservice_stock/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..39ef3fa3d7 --- /dev/null +++ b/fieldservice_stock/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Brian McMaster diff --git a/fieldservice_stock/readme/CREDITS.rst b/fieldservice_stock/readme/CREDITS.rst new file mode 100644 index 0000000000..0eff0acf4e --- /dev/null +++ b/fieldservice_stock/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Open Source Integrators diff --git a/fieldservice_stock/readme/DESCRIPTION.rst b/fieldservice_stock/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..978a9f3b33 --- /dev/null +++ b/fieldservice_stock/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module is an add-on for the Field Service application in Odoo. +It provides inventory and stock operations. diff --git a/fieldservice_stock/readme/INSTALL.rst b/fieldservice_stock/readme/INSTALL.rst new file mode 100644 index 0000000000..a7c9f727b2 --- /dev/null +++ b/fieldservice_stock/readme/INSTALL.rst @@ -0,0 +1,6 @@ +You will first need to install: + +* fieldservice + +Then go to Field Service > Configuration > Settings +Enable the 'Use Odoo Stock Logistics' option under Integrations diff --git a/fieldservice_stock/readme/ROADMAP.rst b/fieldservice_stock/readme/ROADMAP.rst new file mode 100644 index 0000000000..f607015959 --- /dev/null +++ b/fieldservice_stock/readme/ROADMAP.rst @@ -0,0 +1,2 @@ +The roadmap of the Field Service application is documented on +`Github `_. diff --git a/fieldservice_stock/readme/USAGE.rst b/fieldservice_stock/readme/USAGE.rst new file mode 100644 index 0000000000..fc3d1987aa --- /dev/null +++ b/fieldservice_stock/readme/USAGE.rst @@ -0,0 +1,3 @@ +To use this module, you need to: + +* Set Inventory Locations for FSM Locations and FSM Vehicles diff --git a/fieldservice_stock/views/fsm_location.xml b/fieldservice_stock/views/fsm_location.xml new file mode 100644 index 0000000000..54d7738445 --- /dev/null +++ b/fieldservice_stock/views/fsm_location.xml @@ -0,0 +1,14 @@ + + + + + fsm.location + + + + + + + + + diff --git a/fieldservice_stock/views/fsm_vehicle.xml b/fieldservice_stock/views/fsm_vehicle.xml new file mode 100644 index 0000000000..b1e6aed8e8 --- /dev/null +++ b/fieldservice_stock/views/fsm_vehicle.xml @@ -0,0 +1,14 @@ + + + + + fsm.vehicle + + + + + + + + + \ No newline at end of file From ab604c755196d5d52ef0c8fce0bbab0f97337de1 Mon Sep 17 00:00:00 2001 From: brian10048 Date: Sat, 24 Nov 2018 15:13:33 -0500 Subject: [PATCH 002/138] [FIX] fieldservice_stock Travis errors --- fieldservice_stock/README.rst | 0 fieldservice_stock/__manifest__.py | 6 +++++- fieldservice_stock/models/fsm_location.py | 2 +- fieldservice_stock/models/fsm_vehicle.py | 2 +- fieldservice_stock/views/fsm_vehicle.xml | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 fieldservice_stock/README.rst diff --git a/fieldservice_stock/README.rst b/fieldservice_stock/README.rst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py index 5740b09ee3..a5c61cff67 100644 --- a/fieldservice_stock/__manifest__.py +++ b/fieldservice_stock/__manifest__.py @@ -6,7 +6,11 @@ 'summary': 'Inventory and Stock operations for Field Service', 'version': '11.0.0.0.1', 'category': 'Field Service', - 'author': 'Open Source Integrators, Brian McMaster, Odoo Community Association (OCA)', + 'author': [ + 'Open Source Integrators', + 'Brian McMaster', + 'Odoo Community Association (OCA)', + ], 'website': 'https://github.com/OCA/field-service', 'depends': [ 'base_geolocalize', diff --git a/fieldservice_stock/models/fsm_location.py b/fieldservice_stock/models/fsm_location.py index 8890b3d983..e11989ba5b 100644 --- a/fieldservice_stock/models/fsm_location.py +++ b/fieldservice_stock/models/fsm_location.py @@ -1,7 +1,7 @@ # Copyright (C) 2018 - TODAY, Brian McMaster # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import fields, models class FSMLocation(models.Model): diff --git a/fieldservice_stock/models/fsm_vehicle.py b/fieldservice_stock/models/fsm_vehicle.py index eb4f6a21e3..06d78920d9 100644 --- a/fieldservice_stock/models/fsm_vehicle.py +++ b/fieldservice_stock/models/fsm_vehicle.py @@ -1,7 +1,7 @@ # Copyright (C) 2018 - TODAY, Brian McMaster # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import fields, models class FSMVehicle(models.Model): diff --git a/fieldservice_stock/views/fsm_vehicle.xml b/fieldservice_stock/views/fsm_vehicle.xml index b1e6aed8e8..84b559e8f0 100644 --- a/fieldservice_stock/views/fsm_vehicle.xml +++ b/fieldservice_stock/views/fsm_vehicle.xml @@ -11,4 +11,4 @@ - \ No newline at end of file + From 60c4d14a4cac9fe8058dcfb43f1c077bfb81e4f0 Mon Sep 17 00:00:00 2001 From: brian10048 Date: Sat, 24 Nov 2018 15:34:02 -0500 Subject: [PATCH 003/138] [FIX] fieldservice_stock Travis error [UPD] README.rst --- fieldservice_stock/README.rst | 126 +++++ fieldservice_stock/__manifest__.py | 8 +- .../static/description/index.html | 463 ++++++++++++++++++ 3 files changed, 592 insertions(+), 5 deletions(-) create mode 100644 fieldservice_stock/static/description/index.html diff --git a/fieldservice_stock/README.rst b/fieldservice_stock/README.rst index e69de29bb2..17fa4c5cb7 100644 --- a/fieldservice_stock/README.rst +++ b/fieldservice_stock/README.rst @@ -0,0 +1,126 @@ +========================== +Field Service Stock add-on +========================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/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 + :target: https://github.com/OCA/field-service/tree/11.0/fieldservice_stock + :alt: OCA/field-service +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/field-service-11-0/field-service-11-0-fieldservice_stock + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/264/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is an add-on for the Field Service application in Odoo. +It provides inventory and stock operations. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +You will first need to install: + +* fieldservice + +Then go to Field Service > Configuration > Settings +Enable the 'Use Odoo Stock Logistics' option under Integrations + +Configuration +============= + +To configure this module, you need to: + +* Go to Field Service > Configuration > Settings + +Usage +===== + +To use this module, you need to: + +* Set Inventory Locations for FSM Locations and FSM Vehicles + +Known issues / Roadmap +====================== + +The roadmap of the Field Service application is documented on +`Github `_. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Open Source Integrators +* Brian McMaster + +Contributors +~~~~~~~~~~~~ + +* Brian McMaster + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by: + +* Open Source Integrators + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-brian10048| image:: https://github.com/brian10048.png?size=40px + :target: https://github.com/brian10048 + :alt: brian10048 +.. |maintainer-wolfhall| image:: https://github.com/wolfhall.png?size=40px + :target: https://github.com/wolfhall + :alt: wolfhall +.. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px + :target: https://github.com/max3903 + :alt: max3903 + +Current `maintainers `__: + +|maintainer-brian10048| |maintainer-wolfhall| |maintainer-max3903| + +This module is part of the `OCA/field-service `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py index a5c61cff67..bc6f3d9756 100644 --- a/fieldservice_stock/__manifest__.py +++ b/fieldservice_stock/__manifest__.py @@ -6,11 +6,9 @@ 'summary': 'Inventory and Stock operations for Field Service', 'version': '11.0.0.0.1', 'category': 'Field Service', - 'author': [ - 'Open Source Integrators', - 'Brian McMaster', - 'Odoo Community Association (OCA)', - ], + 'author': "Open Source Integrators, " + "Brian McMaster, " + "Odoo Community Association (OCA)", 'website': 'https://github.com/OCA/field-service', 'depends': [ 'base_geolocalize', diff --git a/fieldservice_stock/static/description/index.html b/fieldservice_stock/static/description/index.html new file mode 100644 index 0000000000..39ee16f80a --- /dev/null +++ b/fieldservice_stock/static/description/index.html @@ -0,0 +1,463 @@ + + + + + + +Field Service Stock add-on + + + +
+

Field Service Stock add-on

+ + +

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runbot

+

This module is an add-on for the Field Service application in Odoo. +It provides inventory and stock operations.

+

Table of contents

+ +
+

Installation

+

You will first need to install:

+
    +
  • fieldservice
  • +
+

Then go to Field Service > Configuration > Settings +Enable the ‘Use Odoo Stock Logistics’ option under Integrations

+
+
+

Configuration

+

To configure this module, you need to:

+
    +
  • Go to Field Service > Configuration > Settings
  • +
+
+
+

Usage

+

To use this module, you need to:

+
    +
  • Set Inventory Locations for FSM Locations and FSM Vehicles
  • +
+
+
+

Known issues / Roadmap

+

The roadmap of the Field Service application is documented on +Github.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Open Source Integrators
  • +
  • Brian McMaster
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainers:

+

brian10048 wolfhall max3903

+

This module is part of the OCA/field-service project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + From b9f19065b0519c8654c53ab4835d3246459f7761 Mon Sep 17 00:00:00 2001 From: brian10048 Date: Wed, 12 Dec 2018 12:25:54 -0500 Subject: [PATCH 004/138] [ADD] field service stock 0.0.2 (#49) * [ADD] field service stock 0.0.2 [UPD] README.rst --- fieldservice_stock/README.rst | 9 +- fieldservice_stock/__manifest__.py | 6 +- fieldservice_stock/data/fsm_stock_data.xml | 46 ++-- fieldservice_stock/models/__init__.py | 4 +- fieldservice_stock/models/fsm_order.py | 204 ++++++++++++++++++ fieldservice_stock/models/stock.py | 53 +++++ fieldservice_stock/readme/CONFIGURE.rst | 3 +- fieldservice_stock/readme/USAGE.rst | 6 +- .../static/description/index.html | 9 +- fieldservice_stock/views/fsm_order.xml | 42 ++++ fieldservice_stock/views/inventory.xml | 7 + 11 files changed, 358 insertions(+), 31 deletions(-) create mode 100644 fieldservice_stock/models/fsm_order.py create mode 100644 fieldservice_stock/models/stock.py create mode 100644 fieldservice_stock/views/fsm_order.xml create mode 100644 fieldservice_stock/views/inventory.xml diff --git a/fieldservice_stock/README.rst b/fieldservice_stock/README.rst index 17fa4c5cb7..6e47c9a11b 100644 --- a/fieldservice_stock/README.rst +++ b/fieldservice_stock/README.rst @@ -48,14 +48,19 @@ Configuration To configure this module, you need to: -* Go to Field Service > Configuration > Settings +* Set Inventory Locations for FSM Locations and FSM Vehicles +* Verify procurement routes Usage ===== To use this module, you need to: -* Set Inventory Locations for FSM Locations and FSM Vehicles +* Create a new field service order +* Under the Materials tab, add products with quantity +* Confirm an order to create stock moves +* Validate stock moves in the Inventory app +* Quantities Delivered on FSM Order Line will be updated based on move Known issues / Roadmap ====================== diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py index bc6f3d9756..09da289177 100644 --- a/fieldservice_stock/__manifest__.py +++ b/fieldservice_stock/__manifest__.py @@ -4,15 +4,13 @@ { 'name': 'Field Service Stock add-on', 'summary': 'Inventory and Stock operations for Field Service', - 'version': '11.0.0.0.1', + 'version': '11.0.0.0.2', 'category': 'Field Service', 'author': "Open Source Integrators, " "Brian McMaster, " "Odoo Community Association (OCA)", 'website': 'https://github.com/OCA/field-service', 'depends': [ - 'base_geolocalize', - 'mail', 'fieldservice', 'stock', ], @@ -20,6 +18,8 @@ 'data/fsm_stock_data.xml', 'views/fsm_location.xml', 'views/fsm_vehicle.xml', + 'views/fsm_order.xml', + 'views/inventory.xml', ], 'installable': True, 'license': 'AGPL-3', diff --git a/fieldservice_stock/data/fsm_stock_data.xml b/fieldservice_stock/data/fsm_stock_data.xml index 4c3750e2c8..94456fe978 100644 --- a/fieldservice_stock/data/fsm_stock_data.xml +++ b/fieldservice_stock/data/fsm_stock_data.xml @@ -1,6 +1,7 @@ + Field view @@ -12,9 +13,18 @@ view + + + Storage + internal + + + + + Vehicle Loading @@ -32,22 +42,6 @@ - - Output to Vehicle - 3 - - - - - Warehouse → Vehicle - move - - - make_to_stock - - - - Location Delivery @@ -64,8 +58,8 @@ - - Vehicle to Location + + Stock to Vehicle to Location 3 @@ -74,12 +68,22 @@ Vehicle → Location move - - make_to_stock - + + make_to_order + + + Warehouse → Vehicle + move + + + make_to_stock + + + + Location Return diff --git a/fieldservice_stock/models/__init__.py b/fieldservice_stock/models/__init__.py index 4f639d9629..beae0f7109 100644 --- a/fieldservice_stock/models/__init__.py +++ b/fieldservice_stock/models/__init__.py @@ -3,5 +3,7 @@ from . import ( fsm_location, - fsm_vehicle + fsm_vehicle, + fsm_order, + stock, ) diff --git a/fieldservice_stock/models/fsm_order.py b/fieldservice_stock/models/fsm_order.py new file mode 100644 index 0000000000..0bb0849dac --- /dev/null +++ b/fieldservice_stock/models/fsm_order.py @@ -0,0 +1,204 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import datetime, timedelta + +from odoo import api, fields, models + +from odoo.tools import (DEFAULT_SERVER_DATETIME_FORMAT, + float_is_zero, float_compare) +from odoo.addons import decimal_precision as dp +from odoo.exceptions import UserError + +from odoo.addons.base_geoengine import geo_model + + +class FSMOrder(geo_model.GeoModel): + _inherit = 'fsm.order' + + line_ids = fields.One2many( + 'fsm.order.line', 'order_id', string="Order Lines",) + picking_ids = fields.One2many('stock.picking', 'fsm_order_id', + string='Transfers') + procurement_group_id = fields.Many2one( + 'procurement.group', 'Procurement Group', copy=False) + + def action_confirm(self): + self.line_ids._confirm_picking() + return super(FSMOrder, self).action_confirm() + + +class FSMOrderLine(models.Model): + _name = 'fsm.order.line' + _description = "FSM Order Lines" + _order = 'order_id, sequence, id' + + order_id = fields.Many2one( + 'fsm.order', string="FSM Order", required=True, + ondelete='cascade', index=True, copy=False, + readonly=True, states={'draft': [('readonly', False)]}) + name = fields.Char(string='Description', required=True, + readonly=True, states={'draft': [('readonly', False)]}) + sequence = fields.Integer(string='Sequence', default=10, readonly=True, + states={'draft': [('readonly', False)]}) + product_id = fields.Many2one( + 'product.product', string="Product", required=True, + domain=[('type', '=', 'product')], ondelete='restrict', + readonly=True, states={'draft': [('readonly', False)]}) + product_uom_id = fields.Many2one( + 'product.uom', string='Unit of Measure', required=True, + readonly=True, states={'draft': [('readonly', False)]}) + qty_ordered = fields.Float( + string='Quantity Requested', readonly=True, + states={'draft': [('readonly', False)]}, + digits=dp.get_precision('Product Unit of Measure')) + qty_delivered = fields.Float( + string='Quantity Delivered', readonly=True, copy=False, + digits=dp.get_precision('Product Unit of Measure')) + state = fields.Selection([ + ('draft', 'Draft'), + ('confirmed', 'Confirmed'), + ('partial', 'Partially Shipped'), + ('done', 'Done')], + string='State', compute='_compute_state', copy=False, index=True, + readonly=True, store=True) + move_ids = fields.One2many( + 'stock.move', 'fsm_order_line_id', string='Stock Moves', + readonly=True, states={'draft': [('readonly', False)]}) + + @api.depends('move_ids', 'qty_ordered', 'qty_delivered') + def _compute_state(self): + precision = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + for line in self: + if line.move_ids and not float_is_zero(line.qty_delivered, + precision_digits=precision): + if float_compare(line.qty_delivered, line.qty_ordered, + precision_digits=precision) == -1: + line.state = 'partial' + elif float_compare(line.qty_delivered, line.qty_ordered, + precision_digits=precision) >= 0: + line.state = 'done' + elif line.move_ids: + line.state = 'confirmed' + else: + line.state = 'draft' + + @api.multi + @api.onchange('product_id') + def onchange_product_id(self): + if not self.product_id: + return {'domain': {'product_uom': []}} + + vals = {} + domain = {'product_uom': [('category_id', '=', + self.product_id.uom_id.category_id.id)]} + + if (not self.product_uom_id + or (self.product_id.uom_id.id != self.product_uom_id.id)): + vals['product_uom_id'] = self.product_id.uom_id + vals['qty_ordered'] = 1.0 + + product = self.product_id.with_context( + quantity=vals.get('qty_ordered') or self.qty_ordered, + uom=self.product_uom_id.id, + ) + + result = {'domain': domain} + + name = product.name_get()[0][1] + if product.description_sale: + name += '\n' + product.description_sale + vals['name'] = name + + self.update(vals) + return result + + @api.multi + def _prepare_procurement_values(self, group_id=False): + self.ensure_one() + values = {} + date_planned = (self.order_id.scheduled_date_start + or self.order_id.requested_date + or (datetime.now() + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATETIME_FORMAT)) + values.update({ + 'group_id': group_id, + 'fsm_order_line_id': self.id, + 'date_planned': date_planned, + 'route_ids': self.env.ref( + 'fieldservice_stock.route_stock_to_vehicle_to_location'), + 'partner_dest_id': self.order_id.customer_id + }) + return values + + def _get_procurement_qty(self): + self.ensure_one() + qty = 0.0 + for move in self.move_ids.filtered(lambda r: r.state != 'cancel'): + if move.picking_code == 'outgoing': + qty += move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id, + rounding_method='HALF-UP') + elif move.picking_code == 'incoming': + qty -= move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id, + rounding_method='HALF-UP') + return qty + + @api.multi + def _confirm_picking(self): + precision = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + errors = [] + for line in self: + qty_procured = line._get_procurement_qty() + if float_compare(qty_procured, line.qty_ordered, + precision_digits=precision) >= 0: + continue + group_id = line.order_id.procurement_group_id + if not group_id: + group_id = self.env['procurement.group'].create({ + 'name': line.order_id.name, + 'move_type': 'direct', + 'fsm_order_id': line.order_id.id, + 'partner_id': line.order_id.customer_id.id, + }) + line.order_id.procurement_group_id = group_id + values = line._prepare_procurement_values(group_id=group_id) + qty_needed = line.qty_ordered - qty_procured + procurement_uom = line.product_uom_id + quant_uom = line.product_id.uom_id + get_param = self.env['ir.config_parameter'].sudo().get_param + if (procurement_uom.id != quant_uom.id + and get_param('stock.propagate_uom') != '1'): + qty_needed = line.product_uom_id._compute_quantity( + qty_needed, quant_uom, rounding_method='HALF-UP') + procurement_uom = quant_uom + try: + self.env['procurement.group'].run( + line.product_id, qty_needed, procurement_uom, + line.order_id.fsm_location_id.inventory_location, + line.name, line.order_id.name, values) + except UserError as error: + errors.append(error.name) + if errors: + raise UserError('\n'.join(errors)) + return True + + @api.multi + def _get_delivered_qty(self): + self.ensure_one() + qty = 0.0 + for move in self.move_ids.filtered(lambda r: r.state == 'done' + and not r.scrapped): + if move.location_dest_id.usage == "customer": + if (not move.origin_returned_move_id + or (move.origin_returned_move_id and move.to_refund)): + qty += move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id) + elif (move.location_dest_id.usage != "customer" + and move.to_refund): + qty -= move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id) + return qty diff --git a/fieldservice_stock/models/stock.py b/fieldservice_stock/models/stock.py new file mode 100644 index 0000000000..e2ccc4f590 --- /dev/null +++ b/fieldservice_stock/models/stock.py @@ -0,0 +1,53 @@ +# Copyright (C) 2018 - TODAY, Brian McMaster +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + fsm_order_line_id = fields.Many2one('fsm.order.line', 'FSM Order Line') + + def _action_done(self): + result = super(StockMove, self)._action_done() + for line in result.mapped('fsm_order_line_id').sudo(): + line.qty_delivered = line._get_delivered_qty() + 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': + for line in res.mapped('fsm_order_line_id').sudo(): + line.qty_delivered = line._get_delivered_qty() + return res + + +class ProcurementGroup(models.Model): + _inherit = 'procurement.group' + + fsm_order_id = fields.Many2one('fsm.order', 'Field Service Order') + + +class ProcurementRule(models.Model): + _inherit = 'procurement.rule' + + def _get_stock_move_values(self, product_id, product_qty, product_uom, + location_id, name, origin, values, group_id): + result = super(ProcurementRule, self)._get_stock_move_values( + product_id, product_qty, product_uom, + location_id, name, origin, values, group_id) + if values.get('fsm_order_line_id', False): + result['fsm_order_line_id'] = values['fsm_order_line_id'] + return result + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + fsm_order_id = fields.Many2one( + related="group_id.fsm_order_id", string="Field Service Order", + store=True) diff --git a/fieldservice_stock/readme/CONFIGURE.rst b/fieldservice_stock/readme/CONFIGURE.rst index eff3513290..6b50a28919 100644 --- a/fieldservice_stock/readme/CONFIGURE.rst +++ b/fieldservice_stock/readme/CONFIGURE.rst @@ -1,3 +1,4 @@ To configure this module, you need to: -* Go to Field Service > Configuration > Settings +* Set Inventory Locations for FSM Locations and FSM Vehicles +* Verify procurement routes diff --git a/fieldservice_stock/readme/USAGE.rst b/fieldservice_stock/readme/USAGE.rst index fc3d1987aa..2934857b2b 100644 --- a/fieldservice_stock/readme/USAGE.rst +++ b/fieldservice_stock/readme/USAGE.rst @@ -1,3 +1,7 @@ To use this module, you need to: -* Set Inventory Locations for FSM Locations and FSM Vehicles +* Create a new field service order +* Under the Materials tab, add products with quantity +* Confirm an order to create stock moves +* Validate stock moves in the Inventory app +* Quantities Delivered on FSM Order Line will be updated based on move diff --git a/fieldservice_stock/static/description/index.html b/fieldservice_stock/static/description/index.html index 39ee16f80a..0f2fd33746 100644 --- a/fieldservice_stock/static/description/index.html +++ b/fieldservice_stock/static/description/index.html @@ -400,14 +400,19 @@

Installation

Configuration

To configure this module, you need to:

    -
  • Go to Field Service > Configuration > Settings
  • +
  • Set Inventory Locations for FSM Locations and FSM Vehicles
  • +
  • Verify procurement routes

Usage

To use this module, you need to:

    -
  • Set Inventory Locations for FSM Locations and FSM Vehicles
  • +
  • Create a new field service order
  • +
  • Under the Materials tab, add products with quantity
  • +
  • Confirm an order to create stock moves
  • +
  • Validate stock moves in the Inventory app
  • +
  • Quantities Delivered on FSM Order Line will be updated based on move
diff --git a/fieldservice_stock/views/fsm_order.xml b/fieldservice_stock/views/fsm_order.xml new file mode 100644 index 0000000000..a165b4f6e2 --- /dev/null +++ b/fieldservice_stock/views/fsm_order.xml @@ -0,0 +1,42 @@ + + + + + fsm.order.form.stock + fsm.order + + + + + + + + + + + + + + + + + + + + + diff --git a/fieldservice_stock/views/inventory.xml b/fieldservice_stock/views/inventory.xml new file mode 100644 index 0000000000..5061f5c410 --- /dev/null +++ b/fieldservice_stock/views/inventory.xml @@ -0,0 +1,7 @@ + + + + + + From 3b883ce640945c43d995a52c7ccfcb1d780e5a1a Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Thu, 13 Dec 2018 12:37:10 -0600 Subject: [PATCH 005/138] [IMP] Various improvements [UPD] README.rst --- fieldservice_stock/README.rst | 6 +++--- fieldservice_stock/__manifest__.py | 6 +++--- fieldservice_stock/static/description/icon.png | Bin 0 -> 17808 bytes .../static/description/index.html | 6 +++--- fieldservice_stock/views/inventory.xml | 7 ------- fieldservice_stock/views/stock.xml | 10 ++++++++++ 6 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 fieldservice_stock/static/description/icon.png delete mode 100644 fieldservice_stock/views/inventory.xml create mode 100644 fieldservice_stock/views/stock.xml diff --git a/fieldservice_stock/README.rst b/fieldservice_stock/README.rst index 6e47c9a11b..3510cd588b 100644 --- a/fieldservice_stock/README.rst +++ b/fieldservice_stock/README.rst @@ -1,6 +1,6 @@ -========================== -Field Service Stock add-on -========================== +===================== +Field Service - Stock +===================== .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py index 09da289177..b5c819bc34 100644 --- a/fieldservice_stock/__manifest__.py +++ b/fieldservice_stock/__manifest__.py @@ -2,8 +2,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Field Service Stock add-on', - 'summary': 'Inventory and Stock operations for Field Service', + 'name': 'Field Service - Stock', + 'summary': 'Inventory and Stock Operations for Field Service', 'version': '11.0.0.0.2', 'category': 'Field Service', 'author': "Open Source Integrators, " @@ -19,7 +19,7 @@ 'views/fsm_location.xml', 'views/fsm_vehicle.xml', 'views/fsm_order.xml', - 'views/inventory.xml', + 'views/stock.xml', ], 'installable': True, 'license': 'AGPL-3', diff --git a/fieldservice_stock/static/description/icon.png b/fieldservice_stock/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..955674d8f0b8c47de3ffa9db25cb109fbe4a1091 GIT binary patch literal 17808 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 literal 0 HcmV?d00001 diff --git a/fieldservice_stock/static/description/index.html b/fieldservice_stock/static/description/index.html index 0f2fd33746..7cf0406b5e 100644 --- a/fieldservice_stock/static/description/index.html +++ b/fieldservice_stock/static/description/index.html @@ -4,7 +4,7 @@ -Field Service Stock add-on +Field Service - Stock -
-

Field Service Stock add-on

+
+

Field Service - Stock

- - Vehicle Loading - VL - 5 - - - - - Vehicle Loading - - internal - - - - - - - - Location Delivery - LD - 5 - - - - - Location Delivery - - outgoing - - - - - - Stock to Vehicle to Location - 3 - - - - - Vehicle → Location - move - - - make_to_order - - - - - - Warehouse → Vehicle - move - - - make_to_stock - - - - - - - Location Return - 3 - - - - - - Location Pickup - LP - 5 - - - - - Location Pickup - - internal - - - - - - Warehouse → Vehicle - move - - - make_to_stock - - - - - - - - Vehicle Returns - VR - 5 - - - - - Vehicle Returns - - internal - - - - - - - Vehicle → Warehouse - - - manual - - - - - diff --git a/fieldservice_stock/models/__init__.py b/fieldservice_stock/models/__init__.py index beae0f7109..bacddd4307 100644 --- a/fieldservice_stock/models/__init__.py +++ b/fieldservice_stock/models/__init__.py @@ -3,7 +3,6 @@ from . import ( fsm_location, - fsm_vehicle, fsm_order, stock, ) diff --git a/fieldservice_stock/models/fsm_location.py b/fieldservice_stock/models/fsm_location.py index e11989ba5b..bfe38d6d09 100644 --- a/fieldservice_stock/models/fsm_location.py +++ b/fieldservice_stock/models/fsm_location.py @@ -1,10 +1,11 @@ # Copyright (C) 2018 - TODAY, Brian McMaster # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import fields +from odoo.addons.base_geoengine import geo_model -class FSMLocation(models.Model): +class FSMLocation(geo_model.GeoModel): _inherit = 'fsm.location' inventory_location = fields.Many2one('stock.location', diff --git a/fieldservice_stock/models/fsm_order.py b/fieldservice_stock/models/fsm_order.py index 0bb0849dac..efc930d188 100644 --- a/fieldservice_stock/models/fsm_order.py +++ b/fieldservice_stock/models/fsm_order.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta -from odoo import api, fields, models +from odoo import api, fields, models, _ from odoo.tools import (DEFAULT_SERVER_DATETIME_FORMAT, float_is_zero, float_compare) @@ -20,13 +20,42 @@ class FSMOrder(geo_model.GeoModel): 'fsm.order.line', 'order_id', string="Order Lines",) picking_ids = fields.One2many('stock.picking', 'fsm_order_id', string='Transfers') + delivery_count = fields.Integer(string='Delivery Orders', + compute='_compute_picking_ids') procurement_group_id = fields.Many2one( 'procurement.group', 'Procurement Group', copy=False) + warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse', + help="Warehouse used to ship the materials") + + @api.depends('picking_ids') + def _compute_picking_ids(self): + for order in self: + order.delivery_count = len(order.picking_ids) def action_confirm(self): - self.line_ids._confirm_picking() - return super(FSMOrder, self).action_confirm() + if self.location_id and self.line_ids and self.warehouse_id: + self.line_ids._confirm_picking() + return super(FSMOrder, self).action_confirm() + else: + raise UserError(_('Please select the location, a warehouse and a' + ' product.')) + @api.multi + def action_view_delivery(self): + ''' + This function returns an action that display existing delivery orders + of given fsm order ids. It can either be a in a list or in a form + view, if there is only one delivery order to show. + ''' + action = self.env.ref('stock.action_picking_tree_all').read()[0] + + pickings = self.mapped('picking_ids') + if len(pickings) > 1: + action['domain'] = [('id', 'in', pickings.ids)] + elif pickings: + action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')] + action['res_id'] = pickings.id + return action class FSMOrderLine(models.Model): _name = 'fsm.order.line' @@ -42,7 +71,7 @@ class FSMOrderLine(models.Model): sequence = fields.Integer(string='Sequence', default=10, readonly=True, states={'draft': [('readonly', False)]}) product_id = fields.Many2one( - 'product.product', string="Product", required=True, + 'product.product', string="Product", domain=[('type', '=', 'product')], ondelete='restrict', readonly=True, states={'draft': [('readonly', False)]}) product_uom_id = fields.Many2one( @@ -119,15 +148,15 @@ def _prepare_procurement_values(self, group_id=False): self.ensure_one() values = {} date_planned = (self.order_id.scheduled_date_start - or self.order_id.requested_date + or self.order_id.request_early + or self.order_id.request_late or (datetime.now() + timedelta(days=1)).strftime( DEFAULT_SERVER_DATETIME_FORMAT)) values.update({ 'group_id': group_id, 'fsm_order_line_id': self.id, 'date_planned': date_planned, - 'route_ids': self.env.ref( - 'fieldservice_stock.route_stock_to_vehicle_to_location'), + 'route_ids': self.order_id.warehouse_id.delivery_route_id, 'partner_dest_id': self.order_id.customer_id }) return values @@ -170,6 +199,7 @@ def _confirm_picking(self): procurement_uom = line.product_uom_id quant_uom = line.product_id.uom_id get_param = self.env['ir.config_parameter'].sudo().get_param + import pdb;pdb.set_trace() if (procurement_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1'): qty_needed = line.product_uom_id._compute_quantity( @@ -178,7 +208,7 @@ def _confirm_picking(self): try: self.env['procurement.group'].run( line.product_id, qty_needed, procurement_uom, - line.order_id.fsm_location_id.inventory_location, + line.order_id.location_id.inventory_location, line.name, line.order_id.name, values) except UserError as error: errors.append(error.name) diff --git a/fieldservice_stock/models/fsm_vehicle.py b/fieldservice_stock/models/fsm_vehicle.py deleted file mode 100644 index 06d78920d9..0000000000 --- a/fieldservice_stock/models/fsm_vehicle.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (C) 2018 - TODAY, Brian McMaster -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import fields, models - - -class FSMVehicle(models.Model): - _inherit = 'fsm.vehicle' - - inventory_location = fields.Many2one('stock.location', - 'Inventory Location') diff --git a/fieldservice_stock/readme/CONFIGURE.rst b/fieldservice_stock/readme/CONFIGURE.rst index 6b50a28919..59e8366bd3 100644 --- a/fieldservice_stock/readme/CONFIGURE.rst +++ b/fieldservice_stock/readme/CONFIGURE.rst @@ -1,4 +1,4 @@ To configure this module, you need to: -* Set Inventory Locations for FSM Locations and FSM Vehicles -* Verify procurement routes +* Go to Field Service > Master Data > Locations +* Create or select a location and set the inventory location diff --git a/fieldservice_stock/readme/INSTALL.rst b/fieldservice_stock/readme/INSTALL.rst index a7c9f727b2..c010f8f6a2 100644 --- a/fieldservice_stock/readme/INSTALL.rst +++ b/fieldservice_stock/readme/INSTALL.rst @@ -1,6 +1,4 @@ -You will first need to install: +To install Field Service and have the mapping features, you need to install GeoEngine. -* fieldservice - -Then go to Field Service > Configuration > Settings -Enable the 'Use Odoo Stock Logistics' option under Integrations +Please refer to the installation instructions available at: +https://github.com/OCA/geospatial/tree/11.0/base_geoengine diff --git a/fieldservice_stock/readme/USAGE.rst b/fieldservice_stock/readme/USAGE.rst index 2934857b2b..a9332d58c5 100644 --- a/fieldservice_stock/readme/USAGE.rst +++ b/fieldservice_stock/readme/USAGE.rst @@ -1,7 +1,7 @@ To use this module, you need to: -* Create a new field service order -* Under the Materials tab, add products with quantity -* Confirm an order to create stock moves -* Validate stock moves in the Inventory app -* Quantities Delivered on FSM Order Line will be updated based on move +* Create a new service order +* Under the Inventory tab, select the warehouse and add products with quantity +* Confirm the order to create the delivery orders +* Validate the transfers in the Inventory app. Quantities delivered on FSM + Order Line will be updated. diff --git a/fieldservice_stock/views/fsm_location.xml b/fieldservice_stock/views/fsm_location.xml index 54d7738445..a255bd9bcb 100644 --- a/fieldservice_stock/views/fsm_location.xml +++ b/fieldservice_stock/views/fsm_location.xml @@ -6,7 +6,8 @@ - + diff --git a/fieldservice_stock/views/fsm_order.xml b/fieldservice_stock/views/fsm_order.xml index a165b4f6e2..8e422a26c7 100644 --- a/fieldservice_stock/views/fsm_order.xml +++ b/fieldservice_stock/views/fsm_order.xml @@ -6,31 +6,46 @@ fsm.order +
+ + +
- - - + + + + + + + + + - + + attrs="{'readonly': [('state', 'in', ('sale', 'done', 'cancel'))]}" + options='{"no_open": True, "no_create": True}' + groups="product.group_uom"/> diff --git a/fieldservice_stock/views/fsm_vehicle.xml b/fieldservice_stock/views/fsm_vehicle.xml deleted file mode 100644 index 84b559e8f0..0000000000 --- a/fieldservice_stock/views/fsm_vehicle.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - fsm.vehicle - - - - - - - - - diff --git a/fieldservice_stock/views/stock.xml b/fieldservice_stock/views/stock.xml index 07dd7c5408..375d38977b 100644 --- a/fieldservice_stock/views/stock.xml +++ b/fieldservice_stock/views/stock.xml @@ -1,6 +1,12 @@ + + Date: Thu, 13 Dec 2018 19:48:34 -0600 Subject: [PATCH 007/138] [ADD] Security [UPD] README.rst --- fieldservice_stock/README.rst | 22 ++++++++---------- fieldservice_stock/__manifest__.py | 1 + fieldservice_stock/models/fsm_order.py | 5 ++-- .../security/ir.model.access.csv | 3 +++ .../static/description/index.html | 23 ++++++++----------- fieldservice_stock/views/fsm_order.xml | 2 +- 6 files changed, 28 insertions(+), 28 deletions(-) create mode 100644 fieldservice_stock/security/ir.model.access.csv diff --git a/fieldservice_stock/README.rst b/fieldservice_stock/README.rst index 3510cd588b..8ab3047909 100644 --- a/fieldservice_stock/README.rst +++ b/fieldservice_stock/README.rst @@ -36,31 +36,29 @@ It provides inventory and stock operations. Installation ============ -You will first need to install: +To install Field Service and have the mapping features, you need to install GeoEngine. -* fieldservice - -Then go to Field Service > Configuration > Settings -Enable the 'Use Odoo Stock Logistics' option under Integrations +Please refer to the installation instructions available at: +https://github.com/OCA/geospatial/tree/11.0/base_geoengine Configuration ============= To configure this module, you need to: -* Set Inventory Locations for FSM Locations and FSM Vehicles -* Verify procurement routes +* Go to Field Service > Master Data > Locations +* Create or select a location and set the inventory location Usage ===== To use this module, you need to: -* Create a new field service order -* Under the Materials tab, add products with quantity -* Confirm an order to create stock moves -* Validate stock moves in the Inventory app -* Quantities Delivered on FSM Order Line will be updated based on move +* Create a new service order +* Under the Inventory tab, select the warehouse and add products with quantity +* Confirm the order to create the delivery orders +* Validate the transfers in the Inventory app. Quantities delivered on FSM + Order Line will be updated. Known issues / Roadmap ====================== diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py index 266983cc0a..43f953f45c 100644 --- a/fieldservice_stock/__manifest__.py +++ b/fieldservice_stock/__manifest__.py @@ -15,6 +15,7 @@ 'stock', ], 'data': [ + 'security/ir.model.access.csv', 'data/fsm_stock_data.xml', 'views/fsm_location.xml', 'views/fsm_order.xml', diff --git a/fieldservice_stock/models/fsm_order.py b/fieldservice_stock/models/fsm_order.py index efc930d188..94272b5a05 100644 --- a/fieldservice_stock/models/fsm_order.py +++ b/fieldservice_stock/models/fsm_order.py @@ -53,10 +53,12 @@ def action_view_delivery(self): if len(pickings) > 1: action['domain'] = [('id', 'in', pickings.ids)] elif pickings: - action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')] + action['views'] = [(self.env.ref('stock.view_picking_form').id, + 'form')] action['res_id'] = pickings.id return action + class FSMOrderLine(models.Model): _name = 'fsm.order.line' _description = "FSM Order Lines" @@ -199,7 +201,6 @@ def _confirm_picking(self): procurement_uom = line.product_uom_id quant_uom = line.product_id.uom_id get_param = self.env['ir.config_parameter'].sudo().get_param - import pdb;pdb.set_trace() if (procurement_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1'): qty_needed = line.product_uom_id._compute_quantity( diff --git a/fieldservice_stock/security/ir.model.access.csv b/fieldservice_stock/security/ir.model.access.csv new file mode 100644 index 0000000000..0db565d3d6 --- /dev/null +++ b/fieldservice_stock/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_fsm_order_line_fsm_user,fsm.order.line.user,model_fsm_order_line,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_order_line_fsm_dispatcher,fsm.order.line.dispatcher,model_fsm_order_line,fieldservice.group_fsm_dispatcher,1,1,1,1 diff --git a/fieldservice_stock/static/description/index.html b/fieldservice_stock/static/description/index.html index 7cf0406b5e..76dc661c21 100644 --- a/fieldservice_stock/static/description/index.html +++ b/fieldservice_stock/static/description/index.html @@ -389,30 +389,27 @@

Field Service - Stock

Installation

-

You will first need to install:

-
    -
  • fieldservice
  • -
-

Then go to Field Service > Configuration > Settings -Enable the ‘Use Odoo Stock Logistics’ option under Integrations

+

To install Field Service and have the mapping features, you need to install GeoEngine.

+

Please refer to the installation instructions available at: +https://github.com/OCA/geospatial/tree/11.0/base_geoengine

Configuration

To configure this module, you need to:

    -
  • Set Inventory Locations for FSM Locations and FSM Vehicles
  • -
  • Verify procurement routes
  • +
  • Go to Field Service > Master Data > Locations
  • +
  • Create or select a location and set the inventory location

Usage

To use this module, you need to:

    -
  • Create a new field service order
  • -
  • Under the Materials tab, add products with quantity
  • -
  • Confirm an order to create stock moves
  • -
  • Validate stock moves in the Inventory app
  • -
  • Quantities Delivered on FSM Order Line will be updated based on move
  • +
  • Create a new service order
  • +
  • Under the Inventory tab, select the warehouse and add products with quantity
  • +
  • Confirm the order to create the delivery orders
  • +
  • Validate the transfers in the Inventory app. Quantities delivered on FSM +Order Line will be updated.
diff --git a/fieldservice_stock/views/fsm_order.xml b/fieldservice_stock/views/fsm_order.xml index 8e422a26c7..bf7e07f684 100644 --- a/fieldservice_stock/views/fsm_order.xml +++ b/fieldservice_stock/views/fsm_order.xml @@ -20,7 +20,7 @@ - + From 78fc72dd85a75d1006f28f27557600e3db045420 Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Fri, 14 Dec 2018 17:16:23 -0600 Subject: [PATCH 008/138] [IMP] fieldservice_stock --- fieldservice_stock/__manifest__.py | 2 +- fieldservice_stock/models/fsm_order.py | 15 +++- fieldservice_stock/models/stock.py | 6 ++ fieldservice_stock/views/fsm_order.xml | 107 ++++++++++++++++++++++++- fieldservice_stock/views/stock.xml | 18 +++++ 5 files changed, 142 insertions(+), 6 deletions(-) diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py index 43f953f45c..31d548633d 100644 --- a/fieldservice_stock/__manifest__.py +++ b/fieldservice_stock/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Field Service - Stock', 'summary': 'Inventory and Stock Operations for Field Services', - 'version': '11.0.0.0.2', + 'version': '11.0.0.1.0', 'category': 'Field Service', 'author': "Open Source Integrators, " "Brian McMaster, " diff --git a/fieldservice_stock/models/fsm_order.py b/fieldservice_stock/models/fsm_order.py index 94272b5a05..77a8670c2a 100644 --- a/fieldservice_stock/models/fsm_order.py +++ b/fieldservice_stock/models/fsm_order.py @@ -16,6 +16,13 @@ class FSMOrder(geo_model.GeoModel): _inherit = 'fsm.order' + @api.model + def _default_warehouse_id(self): + company = self.env.user.company_id.id + warehouse_ids = self.env['stock.warehouse'].search( + [('company_id', '=', company)], limit=1) + return warehouse_ids + line_ids = fields.One2many( 'fsm.order.line', 'order_id', string="Order Lines",) picking_ids = fields.One2many('stock.picking', 'fsm_order_id', @@ -25,6 +32,8 @@ class FSMOrder(geo_model.GeoModel): procurement_group_id = fields.Many2one( 'procurement.group', 'Procurement Group', copy=False) warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse', + required=True, readonly=True, + default=_default_warehouse_id, help="Warehouse used to ship the materials") @api.depends('picking_ids') @@ -96,6 +105,9 @@ class FSMOrderLine(models.Model): move_ids = fields.One2many( 'stock.move', 'fsm_order_line_id', string='Stock Moves', readonly=True, states={'draft': [('readonly', False)]}) + route_id = fields.Many2one('stock.location.route', string='Route', + domain=[('fsm_selectable', '=', True)], + ondelete='restrict') @api.depends('move_ids', 'qty_ordered', 'qty_delivered') def _compute_state(self): @@ -158,7 +170,8 @@ def _prepare_procurement_values(self, group_id=False): 'group_id': group_id, 'fsm_order_line_id': self.id, 'date_planned': date_planned, - 'route_ids': self.order_id.warehouse_id.delivery_route_id, + 'route_ids': + self.route_id or self.order_id.warehouse_id.delivery_route_id, 'partner_dest_id': self.order_id.customer_id }) return values diff --git a/fieldservice_stock/models/stock.py b/fieldservice_stock/models/stock.py index e2ccc4f590..88995103f6 100644 --- a/fieldservice_stock/models/stock.py +++ b/fieldservice_stock/models/stock.py @@ -51,3 +51,9 @@ class StockPicking(models.Model): fsm_order_id = fields.Many2one( related="group_id.fsm_order_id", string="Field Service Order", store=True) + + +class StockLocationRoute(models.Model): + _inherit = 'stock.location.route' + + fsm_selectable = fields.Boolean(string="Field Service Order Lines") diff --git a/fieldservice_stock/views/fsm_order.xml b/fieldservice_stock/views/fsm_order.xml index bf7e07f684..6ad1bd6372 100644 --- a/fieldservice_stock/views/fsm_order.xml +++ b/fieldservice_stock/views/fsm_order.xml @@ -18,11 +18,12 @@
- - - + + + - + @@ -34,6 +35,7 @@ }" /> + + fsm.order.form + fsm.order + +
+
+
+ +
+

+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+
+ + + + fsm.order.tree + fsm.order + + + + + + + + + + + FSM Orders + fsm.order + form + tree,form + + + +

+ No Field Service Orders to confirm. +

+
+
+ diff --git a/fieldservice_stock/views/stock.xml b/fieldservice_stock/views/stock.xml index 375d38977b..9f03d5a4dd 100644 --- a/fieldservice_stock/views/stock.xml +++ b/fieldservice_stock/views/stock.xml @@ -1,6 +1,17 @@ + + stock.location.route.form + stock.location.route + + + + + + + + + + + From 71369334fd03e9166a58a03d6197e945bf83cce0 Mon Sep 17 00:00:00 2001 From: Sandip Mangukiya Date: Mon, 31 Dec 2018 02:54:31 -0800 Subject: [PATCH 009/138] [IMP] fieldservice_stock [UPD] README.rst --- fieldservice_stock/README.rst | 6 +- fieldservice_stock/__manifest__.py | 2 + fieldservice_stock/models/__init__.py | 1 + fieldservice_stock/models/fsm_equipment.py | 28 +++ fieldservice_stock/models/fsm_order.py | 234 +++++++++++++++++- fieldservice_stock/models/stock.py | 9 + fieldservice_stock/readme/CONTRIBUTORS.rst | 1 + .../security/ir.model.access.csv | 2 + .../static/description/index.html | 3 +- fieldservice_stock/views/fsm_equipment.xml | 24 ++ fieldservice_stock/views/fsm_order.xml | 61 +++++ fieldservice_stock/views/stock.xml | 2 + 12 files changed, 363 insertions(+), 10 deletions(-) create mode 100644 fieldservice_stock/models/fsm_equipment.py create mode 100644 fieldservice_stock/views/fsm_equipment.xml diff --git a/fieldservice_stock/README.rst b/fieldservice_stock/README.rst index 8ab3047909..152727d3ac 100644 --- a/fieldservice_stock/README.rst +++ b/fieldservice_stock/README.rst @@ -89,6 +89,7 @@ Contributors ~~~~~~~~~~~~ * Brian McMaster +* Sandip Mangukiya Other credits ~~~~~~~~~~~~~ @@ -119,10 +120,13 @@ promote its widespread use. .. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px :target: https://github.com/max3903 :alt: max3903 +.. |maintainer-smangukiya| image:: https://github.com/smangukiya.png?size=40px + :target: https://github.com/smangukiya + :alt: smangukiya Current `maintainers `__: -|maintainer-brian10048| |maintainer-wolfhall| |maintainer-max3903| +|maintainer-brian10048| |maintainer-wolfhall| |maintainer-max3903| |maintainer-smangukiya| This module is part of the `OCA/field-service `_ project on GitHub. diff --git a/fieldservice_stock/__manifest__.py b/fieldservice_stock/__manifest__.py index 31d548633d..642b44845b 100644 --- a/fieldservice_stock/__manifest__.py +++ b/fieldservice_stock/__manifest__.py @@ -20,6 +20,7 @@ 'views/fsm_location.xml', 'views/fsm_order.xml', 'views/stock.xml', + 'views/fsm_equipment.xml', ], 'installable': True, 'license': 'AGPL-3', @@ -28,5 +29,6 @@ 'brian10048', 'wolfhall', 'max3903', + 'smangukiya', ], } diff --git a/fieldservice_stock/models/__init__.py b/fieldservice_stock/models/__init__.py index bacddd4307..0a2022e17b 100644 --- a/fieldservice_stock/models/__init__.py +++ b/fieldservice_stock/models/__init__.py @@ -5,4 +5,5 @@ fsm_location, fsm_order, stock, + fsm_equipment, ) diff --git a/fieldservice_stock/models/fsm_equipment.py b/fieldservice_stock/models/fsm_equipment.py new file mode 100644 index 0000000000..103790dee2 --- /dev/null +++ b/fieldservice_stock/models/fsm_equipment.py @@ -0,0 +1,28 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FSMEquipment(models.Model): + _inherit = 'fsm.equipment' + _description = "FSM Equipment Inherits" + + product_id = fields.Many2one( + 'product.product', string='Product', required=True) + lot_id = fields.Many2one( + 'stock.production.lot', string='Serial #', required=True) + current_stock_location_id = fields.Many2one( + 'stock.location', string='Current Inventory Location', + compute='_compute_current_stock_loc_id') + + @api.multi + def _compute_current_stock_loc_id(self): + for equipment in self: + quants = self.env['stock.quant'].search( + [('lot_id', '=', self.lot_id.id)], order="id desc") + if quants: + equipment.current_stock_location_id = \ + quants[0].location_id.id or False + else: + equipment.current_stock_location_id = False diff --git a/fieldservice_stock/models/fsm_order.py b/fieldservice_stock/models/fsm_order.py index 77a8670c2a..75dc76b912 100644 --- a/fieldservice_stock/models/fsm_order.py +++ b/fieldservice_stock/models/fsm_order.py @@ -24,7 +24,7 @@ def _default_warehouse_id(self): return warehouse_ids line_ids = fields.One2many( - 'fsm.order.line', 'order_id', string="Order Lines",) + 'fsm.order.line', 'order_id', string="Order Lines", ) picking_ids = fields.One2many('stock.picking', 'fsm_order_id', string='Transfers') delivery_count = fields.Integer(string='Delivery Orders', @@ -35,15 +35,33 @@ def _default_warehouse_id(self): required=True, readonly=True, default=_default_warehouse_id, help="Warehouse used to ship the materials") + # FSM Order return + return_ids = fields.One2many('fsm.order.return', 'order_id', + string="Return Lines") + return_count = fields.Integer(string='Return Orders', + compute='_compute_picking_ids') @api.depends('picking_ids') def _compute_picking_ids(self): for order in self: - order.delivery_count = len(order.picking_ids) + order.delivery_count = len( + [picking for picking in order.picking_ids if + picking.picking_type_id.code == 'outgoing']) + order.return_count = len( + [picking for picking in order.picking_ids if + picking.picking_type_id.code == 'incoming']) def action_confirm(self): - if self.location_id and self.line_ids and self.warehouse_id: - self.line_ids._confirm_picking() + if self.location_id and (self.line_ids or self.return_ids) and\ + self.warehouse_id: + if self.line_ids: + line_ids = self.mapped('line_ids').filtered( + lambda l: l.state == 'draft') + line_ids._confirm_picking() + if self.return_ids: + return_ids = self.mapped('return_ids').filtered( + lambda l: l.state == 'draft') + return_ids._confirm_picking() return super(FSMOrder, self).action_confirm() else: raise UserError(_('Please select the location, a warehouse and a' @@ -57,10 +75,30 @@ def action_view_delivery(self): view, if there is only one delivery order to show. ''' action = self.env.ref('stock.action_picking_tree_all').read()[0] + pickings = self.mapped('picking_ids') + delivery_ids = [picking.id for picking in pickings if + picking.picking_type_id.code == 'outgoing'] + if len(pickings) > 1: + action['domain'] = [('id', 'in', delivery_ids)] + elif pickings: + action['views'] = [(self.env.ref('stock.view_picking_form').id, + 'form')] + action['res_id'] = pickings.id + return action + @api.multi + def action_view_returns(self): + ''' + This function returns an action that display existing return orders + of given fsm order ids. It can either be a in a list or in a form + view, if there is only one return order to show. + ''' + action = self.env.ref('stock.action_picking_tree_all').read()[0] pickings = self.mapped('picking_ids') + receipt_ids = [picking.id for picking in pickings if + picking.picking_type_id.code == 'incoming'] if len(pickings) > 1: - action['domain'] = [('id', 'in', pickings.ids)] + action['domain'] = [('id', 'in', receipt_ids)] elif pickings: action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')] @@ -234,15 +272,195 @@ def _confirm_picking(self): def _get_delivered_qty(self): self.ensure_one() qty = 0.0 - for move in self.move_ids.filtered(lambda r: r.state == 'done' - and not r.scrapped): + for move in self.move_ids.filtered( + lambda r: r.state == 'done' and not r.scrapped): if move.location_dest_id.usage == "customer": if (not move.origin_returned_move_id or (move.origin_returned_move_id and move.to_refund)): qty += move.product_uom._compute_quantity( move.product_uom_qty, self.product_uom_id) elif (move.location_dest_id.usage != "customer" - and move.to_refund): + and move.to_refund): + qty -= move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id) + return qty + + +class FSMOrderReturn(models.Model): + _name = 'fsm.order.return' + _description = "FSM Order Return" + _order = 'order_id, sequence, id' + + order_id = fields.Many2one( + 'fsm.order', string="FSM Order", required=True, + ondelete='cascade', index=True, copy=False, + readonly=True, states={'draft': [('readonly', False)]}) + name = fields.Char(string='Description', required=True, + readonly=True, states={'draft': [('readonly', False)]}) + sequence = fields.Integer(string='Sequence', default=10, readonly=True, + states={'draft': [('readonly', False)]}) + product_id = fields.Many2one( + 'product.product', string="Product", + domain=[('type', '=', 'product')], ondelete='restrict', + readonly=True, states={'draft': [('readonly', False)]}) + product_uom_id = fields.Many2one( + 'product.uom', string='Unit of Measure', required=True, + readonly=True, states={'draft': [('readonly', False)]}) + qty_returned = fields.Float( + string='Quantity Returned', readonly=True, + states={'draft': [('readonly', False)]}, + digits=dp.get_precision('Product Unit of Measure')) + qty_received = fields.Float( + string='Quantity Received', readonly=True, copy=False, + digits=dp.get_precision('Product Unit of Measure')) + state = fields.Selection([ + ('draft', 'Draft'), + ('confirmed', 'Confirmed'), + ('partial', 'Partially Shipped'), + ('done', 'Done')], + string='State', compute='_compute_state', copy=False, index=True, + readonly=True, store=True) + move_ids = fields.One2many( + 'stock.move', 'fsm_order_return_line_id', string='Stock Moves', + readonly=True, states={'draft': [('readonly', False)]}) + route_id = fields.Many2one('stock.location.route', string='Route', + domain=[('fsm_return_selectable', '=', True)], + ondelete='restrict') + + @api.depends('move_ids', 'qty_returned', 'qty_received') + def _compute_state(self): + precision = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + for line in self: + if line.move_ids and not float_is_zero(line.qty_received, + precision_digits=precision): + if float_compare(line.qty_received, line.qty_returned, + precision_digits=precision) == -1: + line.state = 'partial' + elif float_compare(line.qty_received, line.qty_returned, + precision_digits=precision) >= 0: + line.state = 'done' + elif line.move_ids: + line.state = 'confirmed' + else: + line.state = 'draft' + + @api.multi + @api.onchange('product_id') + def onchange_product_id(self): + if not self.product_id: + return {'domain': {'product_uom': []}} + + vals = {} + domain = {'product_uom': [('category_id', '=', + self.product_id.uom_id.category_id.id)]} + + if (not self.product_uom_id + or (self.product_id.uom_id.id != self.product_uom_id.id)): + vals['product_uom_id'] = self.product_id.uom_id + vals['qty_returned'] = 1.0 + + product = self.product_id.with_context( + quantity=vals.get('qty_returned') or self.qty_returned, + uom=self.product_uom_id.id, + ) + + result = {'domain': domain} + + name = product.name_get()[0][1] + if product.description_sale: + name += '\n' + product.description_sale + vals['name'] = name + + self.update(vals) + return result + + @api.multi + def _prepare_procurement_values(self, group_id=False): + self.ensure_one() + values = {} + date_planned = (self.order_id.scheduled_date_start + or self.order_id.request_early + or self.order_id.request_late + or (datetime.now() + timedelta(days=1)).strftime( + DEFAULT_SERVER_DATETIME_FORMAT)) + values.update({ + 'group_id': group_id, + 'fsm_order_return_line_id': self.id, + 'date_planned': date_planned, + 'route_ids': + self.route_id or self.order_id.warehouse_id.reception_route_id, + 'partner_dest_id': self.order_id.warehouse_id + }) + return values + + def _get_procurement_qty(self): + self.ensure_one() + qty = 0.0 + for move in self.move_ids.filtered(lambda r: r.state != 'cancel'): + if move.picking_code == 'incoming': + qty += move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id, + rounding_method='HALF-UP') + elif move.picking_code == 'outoging': + qty -= move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id, + rounding_method='HALF-UP') + return qty + + @api.multi + def _confirm_picking(self): + precision = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + errors = [] + for line in self: + qty_procured = line._get_procurement_qty() + if float_compare(qty_procured, line.qty_returned, + precision_digits=precision) >= 0: + continue + group_id = line.order_id.procurement_group_id + if not group_id: + group_id = self.env['procurement.group'].create({ + 'name': line.order_id.name, + 'move_type': 'direct', + 'fsm_order_id': line.order_id.id, + 'partner_id': line.order_id.customer_id.id, + }) + line.order_id.procurement_group_id = group_id + values = line._prepare_procurement_values(group_id=group_id) + qty_needed = line.qty_returned - qty_procured + procurement_uom = line.product_uom_id + quant_uom = line.product_id.uom_id + get_param = self.env['ir.config_parameter'].sudo().get_param + if (procurement_uom.id != quant_uom.id + and get_param('stock.propagate_uom') != '1'): + qty_needed = line.product_uom_id._compute_quantity( + qty_needed, quant_uom, rounding_method='HALF-UP') + procurement_uom = quant_uom + try: + self.env['procurement.group'].run( + line.product_id, qty_needed, procurement_uom, + line.order_id.location_id.inventory_location, + line.name, line.order_id.name, values) + except UserError as error: + errors.append(error.name) + if errors: + raise UserError('\n'.join(errors)) + return True + + @api.multi + def _get_received_qty(self): + self.ensure_one() + qty = 0.0 + for move in self.move_ids.filtered( + lambda r: r.state == 'done' and not r.scrapped): + if move.location_dest_id.usage != "customer": + if (not move.origin_returned_move_id + or (move.origin_returned_move_id and move.to_refund)): + qty += move.product_uom._compute_quantity( + move.product_uom_qty, self.product_uom_id) + elif (move.location_dest_id.usage == "customer" + and move.to_refund): qty -= move.product_uom._compute_quantity( move.product_uom_qty, self.product_uom_id) return qty diff --git a/fieldservice_stock/models/stock.py b/fieldservice_stock/models/stock.py index 88995103f6..cec0467504 100644 --- a/fieldservice_stock/models/stock.py +++ b/fieldservice_stock/models/stock.py @@ -8,11 +8,15 @@ class StockMove(models.Model): _inherit = "stock.move" fsm_order_line_id = fields.Many2one('fsm.order.line', 'FSM Order Line') + fsm_order_return_line_id = fields.Many2one( + 'fsm.order.return', 'FSM Order Return Line') def _action_done(self): result = super(StockMove, self)._action_done() for line in result.mapped('fsm_order_line_id').sudo(): line.qty_delivered = line._get_delivered_qty() + for line in result.mapped('fsm_order_return_line_id').sudo(): + line.qty_received = line._get_received_qty() return result @api.multi @@ -42,6 +46,9 @@ def _get_stock_move_values(self, product_id, product_qty, product_uom, location_id, name, origin, values, group_id) if values.get('fsm_order_line_id', False): result['fsm_order_line_id'] = values['fsm_order_line_id'] + if values.get('fsm_order_return_line_id', False): + result['fsm_order_return_line_id'] = \ + values['fsm_order_return_line_id'] return result @@ -57,3 +64,5 @@ class StockLocationRoute(models.Model): _inherit = 'stock.location.route' fsm_selectable = fields.Boolean(string="Field Service Order Lines") + fsm_return_selectable = fields.Boolean( + string="Field Service Return Order Lines") diff --git a/fieldservice_stock/readme/CONTRIBUTORS.rst b/fieldservice_stock/readme/CONTRIBUTORS.rst index 39ef3fa3d7..a3606b15df 100644 --- a/fieldservice_stock/readme/CONTRIBUTORS.rst +++ b/fieldservice_stock/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Brian McMaster +* Sandip Mangukiya diff --git a/fieldservice_stock/security/ir.model.access.csv b/fieldservice_stock/security/ir.model.access.csv index 0db565d3d6..f7bbd49d5d 100644 --- a/fieldservice_stock/security/ir.model.access.csv +++ b/fieldservice_stock/security/ir.model.access.csv @@ -1,3 +1,5 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_fsm_order_line_fsm_user,fsm.order.line.user,model_fsm_order_line,fieldservice.group_fsm_user,1,0,0,0 access_fsm_order_line_fsm_dispatcher,fsm.order.line.dispatcher,model_fsm_order_line,fieldservice.group_fsm_dispatcher,1,1,1,1 +access_fsm_order_return_fsm_user,fsm.order.return.user,model_fsm_order_return,fieldservice.group_fsm_user,1,0,0,0 +access_fsm_order_return_fsm_dispatcher,fsm.order.return.dispatcher,model_fsm_order_return,fieldservice.group_fsm_dispatcher,1,1,1,1 diff --git a/fieldservice_stock/static/description/index.html b/fieldservice_stock/static/description/index.html index 76dc661c21..7b4547acab 100644 --- a/fieldservice_stock/static/description/index.html +++ b/fieldservice_stock/static/description/index.html @@ -438,6 +438,7 @@

Authors

Contributors

@@ -455,7 +456,7 @@

Maintainers

mission is to support the collaborative development of Odoo features and promote its widespread use.

Current maintainers:

-

brian10048 wolfhall max3903

+

brian10048 wolfhall max3903 smangukiya

This module is part of the OCA/field-service project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/fieldservice_stock/views/fsm_equipment.xml b/fieldservice_stock/views/fsm_equipment.xml new file mode 100644 index 0000000000..64e485b287 --- /dev/null +++ b/fieldservice_stock/views/fsm_equipment.xml @@ -0,0 +1,24 @@ + + + + + fsm.equipment.form.stock + fsm.equipment + + + + + + + + + + + + + + + + + + diff --git a/fieldservice_stock/views/fsm_order.xml b/fieldservice_stock/views/fsm_order.xml index 6ad1bd6372..00fed00649 100644 --- a/fieldservice_stock/views/fsm_order.xml +++ b/fieldservice_stock/views/fsm_order.xml @@ -15,6 +15,13 @@ attrs="{'invisible': [('delivery_count', '=', 0)]}" groups="base.group_user"> +
@@ -25,6 +32,7 @@ + @@ -51,6 +59,33 @@ + + + + + + + + + + + + + @@ -111,6 +146,32 @@ + + + + + + + + + + + +
diff --git a/fieldservice_stock/views/stock.xml b/fieldservice_stock/views/stock.xml index 9f03d5a4dd..9b6636bc1a 100644 --- a/fieldservice_stock/views/stock.xml +++ b/fieldservice_stock/views/stock.xml @@ -8,6 +8,8 @@ + From cf276481cc0f470a443355e661c26cac68b275d5 Mon Sep 17 00:00:00 2001 From: Michael Allen Date: Wed, 2 Jan 2019 10:52:21 -0700 Subject: [PATCH 010/138] [IMP] fieldservice_stock [UPD] README.rst --- fieldservice_stock/README.rst | 16 +++ fieldservice_stock/data/fsm_stock_data.xml | 1 + fieldservice_stock/models/fsm_location.py | 5 +- fieldservice_stock/models/fsm_order.py | 116 +++++++++++++----- fieldservice_stock/models/stock.py | 37 +++++- fieldservice_stock/readme/CONFIGURE.rst | 16 +++ .../static/description/index.html | 16 +++ fieldservice_stock/views/fsm_location.xml | 2 +- fieldservice_stock/views/fsm_order.xml | 98 ++++++++++----- fieldservice_stock/views/stock.xml | 23 ++++ 10 files changed, 260 insertions(+), 70 deletions(-) diff --git a/fieldservice_stock/README.rst b/fieldservice_stock/README.rst index 152727d3ac..7205cf40a3 100644 --- a/fieldservice_stock/README.rst +++ b/fieldservice_stock/README.rst @@ -48,6 +48,22 @@ To configure this module, you need to: * Go to Field Service > Master Data > Locations * Create or select a location and set the inventory location +* Go to Inventory > Configuration > Routes +* Select the routes that you want to use from a FSM order +* Check the box 'FSM Order Line' for outbound transfer +* Check the box 'FSM Return Line' for inbound transfer + +The route 'Receipt in 1 step' has no procurement rule so if you want items to be +returned from the service location to your warehouse, you need to create a new +procurement rule for that route: + +* Name: YourCompany: Return +* Action: Move From Another Location +* Procurement Location: WH/Stock +* Served Warehouse: YourCompany +* Source Location: Partner Locations/Customers +* Move Supply Method: Take From Stock +* Operation Type: YourCompany: Receipts Usage ===== diff --git a/fieldservice_stock/data/fsm_stock_data.xml b/fieldservice_stock/data/fsm_stock_data.xml index c74036dae3..290cd237e7 100644 --- a/fieldservice_stock/data/fsm_stock_data.xml +++ b/fieldservice_stock/data/fsm_stock_data.xml @@ -1,6 +1,7 @@ + Field view diff --git a/fieldservice_stock/models/fsm_location.py b/fieldservice_stock/models/fsm_location.py index bfe38d6d09..2ffc4aa075 100644 --- a/fieldservice_stock/models/fsm_location.py +++ b/fieldservice_stock/models/fsm_location.py @@ -8,5 +8,6 @@ class FSMLocation(geo_model.GeoModel): _inherit = 'fsm.location' - inventory_location = fields.Many2one('stock.location', - 'Inventory Location') + inventory_location_id = fields.Many2one('stock.location', + string='Inventory Location', + required=True) diff --git a/fieldservice_stock/models/fsm_order.py b/fieldservice_stock/models/fsm_order.py index 75dc76b912..34cc5f28c5 100644 --- a/fieldservice_stock/models/fsm_order.py +++ b/fieldservice_stock/models/fsm_order.py @@ -12,6 +12,13 @@ from odoo.addons.base_geoengine import geo_model +STOCK_STAGES = [('draft', 'Draft'), + ('requested', 'Requested'), + ('confirmed', 'Confirmed'), + ('partial', 'Partially Shipped'), + ('done', 'Done'), + ('cancelled', 'Cancelled')] + class FSMOrder(geo_model.GeoModel): _inherit = 'fsm.order' @@ -23,14 +30,16 @@ def _default_warehouse_id(self): [('company_id', '=', company)], limit=1) return warehouse_ids - line_ids = fields.One2many( - 'fsm.order.line', 'order_id', string="Order Lines", ) + line_ids = fields.One2many('fsm.order.line', 'order_id', + string="Order Lines") picking_ids = fields.One2many('stock.picking', 'fsm_order_id', string='Transfers') delivery_count = fields.Integer(string='Delivery Orders', compute='_compute_picking_ids') procurement_group_id = fields.Many2one( 'procurement.group', 'Procurement Group', copy=False) + inventory_location_id = fields.Many2one( + related='location_id.inventory_location_id', readonly=True) warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse', required=True, readonly=True, default=_default_warehouse_id, @@ -40,6 +49,9 @@ def _default_warehouse_id(self): string="Return Lines") return_count = fields.Integer(string='Return Orders', compute='_compute_picking_ids') + inventory_stage = fields.Selection(STOCK_STAGES, string='State', + default='draft', required=True, + readonly=True, store=True) @api.depends('picking_ids') def _compute_picking_ids(self): @@ -51,21 +63,63 @@ def _compute_picking_ids(self): [picking for picking in order.picking_ids if picking.picking_type_id.code == 'incoming']) - def action_confirm(self): + def action_request_inventory(self): + if self.location_id and (self.line_ids or self.return_ids) and\ + self.warehouse_id: + for line in self.line_ids: + if line.state == 'draft': + line.state = 'requested' + line.qty_ordered = line.qty_requested + for line in self.return_ids: + if line.state == 'draft': + line.state = 'requested' + line.qty_ordered = line.qty_requested + self.inventory_stage = 'requested' + else: + raise UserError( + _('Please select the location, a warehouse and a product.')) + + def action_confirm_inventory(self): if self.location_id and (self.line_ids or self.return_ids) and\ self.warehouse_id: if self.line_ids: line_ids = self.mapped('line_ids').filtered( - lambda l: l.state == 'draft') + lambda l: l.state == 'requested') line_ids._confirm_picking() if self.return_ids: return_ids = self.mapped('return_ids').filtered( - lambda l: l.state == 'draft') + lambda l: l.state == 'requested') return_ids._confirm_picking() - return super(FSMOrder, self).action_confirm() + self.inventory_stage = 'confirmed' else: - raise UserError(_('Please select the location, a warehouse and a' - ' product.')) + raise UserError( + _('Please select the location, a warehouse and a product.')) + + def action_cancel_inventory(self): + if self.line_ids: + line_ids = self.mapped('line_ids').filtered( + lambda l: l.state == 'requested') + for line in line_ids: + line.state = 'cancelled' + if self.return_ids: + return_ids = self.mapped('return_ids').filtered( + lambda l: l.state == 'requested') + for line in return_ids: + line.state = 'cancelled' + self.inventory_stage = 'cancelled' + + def action_reset_inventory(self): + if self.line_ids: + line_ids = self.mapped('line_ids').filtered( + lambda l: l.state == 'cancelled') + for line in line_ids: + line.state = 'draft' + if self.return_ids: + return_ids = self.mapped('return_ids').filtered( + lambda l: l.state == 'cancelled') + for line in return_ids: + line.state = 'draft' + self.inventory_stage = 'draft' @api.multi def action_view_delivery(self): @@ -126,20 +180,20 @@ class FSMOrderLine(models.Model): product_uom_id = fields.Many2one( 'product.uom', string='Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}) - qty_ordered = fields.Float( + qty_requested = fields.Float( string='Quantity Requested', readonly=True, states={'draft': [('readonly', False)]}, digits=dp.get_precision('Product Unit of Measure')) + qty_ordered = fields.Float( + string='Quantity Ordered', readonly=True, + states={'requested': [('readonly', False)]}, + digits=dp.get_precision('Product Unit of Measure')) qty_delivered = fields.Float( string='Quantity Delivered', readonly=True, copy=False, digits=dp.get_precision('Product Unit of Measure')) - state = fields.Selection([ - ('draft', 'Draft'), - ('confirmed', 'Confirmed'), - ('partial', 'Partially Shipped'), - ('done', 'Done')], - string='State', compute='_compute_state', copy=False, index=True, - readonly=True, store=True) + state = fields.Selection(STOCK_STAGES, string='State', required=True, + compute='_compute_state', default='draft', + copy=False, index=True, readonly=True, store=True) move_ids = fields.One2many( 'stock.move', 'fsm_order_line_id', string='Stock Moves', readonly=True, states={'draft': [('readonly', False)]}) @@ -163,7 +217,10 @@ def _compute_state(self): elif line.move_ids: line.state = 'confirmed' else: - line.state = 'draft' + if line.state == 'requested': + break + else: + line.state = 'draft' @api.multi @api.onchange('product_id') @@ -260,7 +317,7 @@ def _confirm_picking(self): try: self.env['procurement.group'].run( line.product_id, qty_needed, procurement_uom, - line.order_id.location_id.inventory_location, + line.order_id.inventory_location_id, line.name, line.order_id.name, values) except UserError as error: errors.append(error.name) @@ -306,20 +363,20 @@ class FSMOrderReturn(models.Model): product_uom_id = fields.Many2one( 'product.uom', string='Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}) + qty_requested = fields.Float( + string='Quantity Requested', readonly=True, + states={'draft': [('readonly', False)]}, + digits=dp.get_precision('Product Unit of Measure')) qty_returned = fields.Float( string='Quantity Returned', readonly=True, - states={'draft': [('readonly', False)]}, + states={'requested': [('readonly', False)]}, digits=dp.get_precision('Product Unit of Measure')) qty_received = fields.Float( string='Quantity Received', readonly=True, copy=False, digits=dp.get_precision('Product Unit of Measure')) - state = fields.Selection([ - ('draft', 'Draft'), - ('confirmed', 'Confirmed'), - ('partial', 'Partially Shipped'), - ('done', 'Done')], - string='State', compute='_compute_state', copy=False, index=True, - readonly=True, store=True) + state = fields.Selection(STOCK_STAGES, string='State', required=True, + compute='_compute_state', default='draft', + copy=False, index=True, readonly=True, store=True) move_ids = fields.One2many( 'stock.move', 'fsm_order_return_line_id', string='Stock Moves', readonly=True, states={'draft': [('readonly', False)]}) @@ -364,14 +421,11 @@ def onchange_product_id(self): quantity=vals.get('qty_returned') or self.qty_returned, uom=self.product_uom_id.id, ) - result = {'domain': domain} - name = product.name_get()[0][1] if product.description_sale: name += '\n' + product.description_sale vals['name'] = name - self.update(vals) return result @@ -402,7 +456,7 @@ def _get_procurement_qty(self): qty += move.product_uom._compute_quantity( move.product_uom_qty, self.product_uom_id, rounding_method='HALF-UP') - elif move.picking_code == 'outoging': + elif move.picking_code == 'outgoing': qty -= move.product_uom._compute_quantity( move.product_uom_qty, self.product_uom_id, rounding_method='HALF-UP') @@ -440,7 +494,7 @@ def _confirm_picking(self): try: self.env['procurement.group'].run( line.product_id, qty_needed, procurement_uom, - line.order_id.location_id.inventory_location, + line.order_id.warehouse_id.lot_stock_id, line.name, line.order_id.name, values) except UserError as error: errors.append(error.name) diff --git a/fieldservice_stock/models/stock.py b/fieldservice_stock/models/stock.py index cec0467504..0707722424 100644 --- a/fieldservice_stock/models/stock.py +++ b/fieldservice_stock/models/stock.py @@ -64,5 +64,38 @@ class StockLocationRoute(models.Model): _inherit = 'stock.location.route' fsm_selectable = fields.Boolean(string="Field Service Order Lines") - fsm_return_selectable = fields.Boolean( - string="Field Service Return Order Lines") + fsm_return_selectable = fields.Boolean(string="Field Service Return Lines") + + +class StockPickingType(models.Model): + _inherit = 'stock.picking.type' + + count_fsm_requests = fields.Integer(compute='_compute_fsm_request') + + def _compute_fsm_request(self): + for ptype in self: + if ptype.code == 'outgoing': + res = self.env['fsm.order'].search([('warehouse_id', + '=', + ptype.warehouse_id.id), + ('inventory_stage', '=', + 'requested')]) + for order in res: + for line in order.line_ids: + if line.state == 'requested': + ptype.count_fsm_requests += 1 + break + if ptype.code == 'incoming': + res = self.env['fsm.order'].search([('warehouse_id', + '=', + ptype.warehouse_id.id), + ('inventory_stage', '=', + 'requested')]) + for order in res: + for line in order.return_ids: + if line.state == 'requested': + ptype.count_fsm_requests += 1 + break + + def get_action_fsm_requests(self): + return self._get_action('fieldservice_stock.action_stock_fsm_order') diff --git a/fieldservice_stock/readme/CONFIGURE.rst b/fieldservice_stock/readme/CONFIGURE.rst index 59e8366bd3..60a1a510d9 100644 --- a/fieldservice_stock/readme/CONFIGURE.rst +++ b/fieldservice_stock/readme/CONFIGURE.rst @@ -2,3 +2,19 @@ To configure this module, you need to: * Go to Field Service > Master Data > Locations * Create or select a location and set the inventory location +* Go to Inventory > Configuration > Routes +* Select the routes that you want to use from a FSM order +* Check the box 'FSM Order Line' for outbound transfer +* Check the box 'FSM Return Line' for inbound transfer + +The route 'Receipt in 1 step' has no procurement rule so if you want items to be +returned from the service location to your warehouse, you need to create a new +procurement rule for that route: + +* Name: YourCompany: Return +* Action: Move From Another Location +* Procurement Location: WH/Stock +* Served Warehouse: YourCompany +* Source Location: Partner Locations/Customers +* Move Supply Method: Take From Stock +* Operation Type: YourCompany: Receipts diff --git a/fieldservice_stock/static/description/index.html b/fieldservice_stock/static/description/index.html index 7b4547acab..c8e6fcf34b 100644 --- a/fieldservice_stock/static/description/index.html +++ b/fieldservice_stock/static/description/index.html @@ -399,6 +399,22 @@

Configuration

  • Go to Field Service > Master Data > Locations
  • Create or select a location and set the inventory location
  • +
  • Go to Inventory > Configuration > Routes
  • +
  • Select the routes that you want to use from a FSM order
  • +
  • Check the box ‘FSM Order Line’ for outbound transfer
  • +
  • Check the box ‘FSM Return Line’ for inbound transfer
  • +
+

The route ‘Receipt in 1 step’ has no procurement rule so if you want items to be +returned from the service location to your warehouse, you need to create a new +procurement rule for that route:

+
    +
  • Name: YourCompany: Return
  • +
  • Action: Move From Another Location
  • +
  • Procurement Location: WH/Stock
  • +
  • Served Warehouse: YourCompany
  • +
  • Source Location: Partner Locations/Customers
  • +
  • Move Supply Method: Take From Stock
  • +
  • Operation Type: YourCompany: Receipts
diff --git a/fieldservice_stock/views/fsm_location.xml b/fieldservice_stock/views/fsm_location.xml index a255bd9bcb..658e2758bf 100644 --- a/fieldservice_stock/views/fsm_location.xml +++ b/fieldservice_stock/views/fsm_location.xml @@ -6,7 +6,7 @@ - diff --git a/fieldservice_stock/views/fsm_order.xml b/fieldservice_stock/views/fsm_order.xml index 00fed00649..750ef3e3e4 100644 --- a/fieldservice_stock/views/fsm_order.xml +++ b/fieldservice_stock/views/fsm_order.xml @@ -13,7 +13,7 @@ class="oe_stat_button" icon="fa-truck" attrs="{'invisible': [('delivery_count', '=', 0)]}" groups="base.group_user"> - +
+
+
- - - -