diff --git a/fleet_vehicle_purchase/tests/test_purchase.py b/fleet_vehicle_purchase/tests/test_purchase.py new file mode 100644 index 00000000..f62af25b --- /dev/null +++ b/fleet_vehicle_purchase/tests/test_purchase.py @@ -0,0 +1,79 @@ +# Copyright 2023 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields +from odoo.tests import Form +from odoo.tests.common import TransactionCase + + +class TestPurchase(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.brand = cls.env["fleet.vehicle.model.brand"].create( + { + "name": "Audi", + } + ) + cls.model = cls.env["fleet.vehicle.model"].create( + { + "brand_id": cls.brand.id, + "name": "A3", + } + ) + cls.car_1 = cls.env["fleet.vehicle"].create( + { + "model_id": cls.model.id, + "driver_id": cls.env.user.partner_id.id, + "plan_to_change_car": False, + } + ) + cls.product = cls.env["product.product"].create( + { + "name": "product", + "type": "service", + } + ) + + def test_purchase(self): + self.assertEqual(0, self.car_1.purchase_order_count) + order_action = self.car_1.action_view_purchase_orders() + self.assertFalse( + self.env[order_action["res_model"]].search(order_action["domain"]) + ) + with Form(self.env["purchase.order"]) as form: + form.partner_id = self.env.user.partner_id + form.fleet_vehicle_id = self.car_1 + with form.order_line.new() as form_line: + form_line.product_id = self.product + form_line.price_unit = 100 + self.assertEqual(1, self.car_1.purchase_order_count) + purchase = form.save() + self.assertEqual( + purchase, self.env[order_action["res_model"]].search(order_action["domain"]) + ) + purchase.button_confirm() + purchase.order_line.qty_received = 1 + invoice_action = purchase.action_create_invoice() + invoice = self.env[invoice_action["res_model"]].browse(invoice_action["res_id"]) + invoice.invoice_date = fields.Date.today() + self.assertTrue(invoice.invoice_line_ids.vehicle_id) + self.assertFalse(self.car_1.log_services) + invoice.action_post() + self.assertTrue(self.car_1.log_services) + + def test_purchase_no_vehicle(self): + """Test purchase order flow without a fleet vehicle.""" + with Form(self.env["purchase.order"]) as form: + form.partner_id = self.env.user.partner_id + with form.order_line.new() as form_line: + form_line.product_id = self.product + form_line.price_unit = 100 + + purchase = form.save() + purchase.button_confirm() + purchase.order_line.qty_received = 1 + invoice_action = purchase.action_create_invoice() + invoice = self.env[invoice_action["res_model"]].browse(invoice_action["res_id"]) + + self.assertFalse(invoice.invoice_line_ids.vehicle_id) diff --git a/fleet_vehicle_stock/README.rst b/fleet_vehicle_stock/README.rst new file mode 100644 index 00000000..0931d29e --- /dev/null +++ b/fleet_vehicle_stock/README.rst @@ -0,0 +1,124 @@ +=================== +Fleet Vehicle Stock +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:52066aeb1aedb8fb23bdebae9ebcd9e48527a0712bae5ba48563be41d5668f2a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Ffleet-lightgray.png?logo=github + :target: https://github.com/OCA/fleet/tree/18.0/fleet_vehicle_stock + :alt: OCA/fleet +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/fleet-18-0/fleet-18-0-fleet_vehicle_stock + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/fleet&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module is an add-on for the Fleet application in Odoo. It allows +you to track your Fleet Vehicles in stock moves. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Products can be automatically converted into Fleet Vehicle. This is only +available only for products tracked by serial number. This needs to be +enabled both on Operation Types and Products. For example, we may want +to create the Fleet Vehicle on Delivery, or on Receipts. + +To enable on Products: + +- Go to Inventory > Master Data > Products +- Open the Product form, Inventory tab +- On the "Traceability" section, make sure "Tracking" is set to "By + Unique Serial Number" +- Enable the "Creates Fleet Vehicle" checkbox +- Open Fleet tab +- Select Fleet Vehicle Model + +To enable on Operation Types: + +- Go to Inventory > Configuration > Operation Types +- Select the intended Operation Type ("Receipts" for example) +- On the "Traceability" section, enable the "Create Fleet Vehicle" + checkbox + +Usage +===== + +A completed stock move for a properly configured Product on a configured +stock Operation Type will automatically create a fleet vehicle. + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Escodoo + +Contributors +------------ + +- Marcel Savegnago +- Kaynnan Lemes + +Other credits +------------- + +The development of this module has been financially supported by: + +- Escodoo - https://www.escodoo.com.br + +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-marcelsavegnago| image:: https://github.com/marcelsavegnago.png?size=40px + :target: https://github.com/marcelsavegnago + :alt: marcelsavegnago + +Current `maintainer `__: + +|maintainer-marcelsavegnago| + +This module is part of the `OCA/fleet `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fleet_vehicle_stock/__init__.py b/fleet_vehicle_stock/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/fleet_vehicle_stock/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/fleet_vehicle_stock/__manifest__.py b/fleet_vehicle_stock/__manifest__.py new file mode 100644 index 00000000..8b95d83b --- /dev/null +++ b/fleet_vehicle_stock/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright 2021 - TODAY, Escodoo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +{ + "name": "Fleet Vehicle Stock", + "summary": """ + This module is an add-on for the Fleet application in Odoo. It allows + you to track your Fleet Vehicles in stock moves.""", + "version": "18.0.1.0.0", + "license": "AGPL-3", + "author": "Escodoo,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/fleet", + "category": "Human Resources/Fleet", + "maintainers": ["marcelsavegnago"], + "depends": ["stock", "fleet"], + "data": [ + "views/stock_production_lot.xml", + "views/stock_picking_type.xml", + "views/product_template.xml", + "views/product_product.xml", + "views/fleet_vehicle.xml", + "views/fleet_vehicle_model.xml", + ], + "demo": [], +} diff --git a/fleet_vehicle_stock/i18n/es.po b/fleet_vehicle_stock/i18n/es.po new file mode 100644 index 00000000..90ff7911 --- /dev/null +++ b/fleet_vehicle_stock/i18n/es.po @@ -0,0 +1,131 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fleet_vehicle_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-08-03 21:10+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "Create Fleet Vehicle" +msgstr "Crear flota de vehículos" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__create_fleet_vehicle +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__create_fleet_vehicle +msgid "Creates a Fleet Vehicle" +msgstr "Crear una flota de vehículos" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__current_stock_location_id +msgid "Current Inventory Location" +msgstr "Ubicación actual del inventario" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle_model__product_id +msgid "Default Product" +msgstr "Producto por defecto" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_template_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_variant_easy_edit_view +msgid "Fleet" +msgstr "Flota" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_lot__fleet_vehicle_id +msgid "Fleet Vehicle" +msgstr "Vehículo de Flota" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_model_form_view +msgid "Inventory" +msgstr "Inventario" + +#. module: fleet_vehicle_stock +#: model:ir.model.constraint,message:fleet_vehicle_stock.constraint_product_template_non_product_tracking_for_vehicle_model +msgid "" +"It is mandatory to configure the traceability by serial number in order to " +"be able to configure the vehicle model of the fleet in this product." +msgstr "" +"Es obligatorio configurar la trazabilidad por número de serie para poder " +"configurar el modelo de vehículo de la flota en este producto." + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_lot +msgid "Lot/Serial" +msgstr "Lote/Serie" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle_model +msgid "Model of a vehicle" +msgstr "Modelo de vehículo" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipo de recogida" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_template +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__product_id +msgid "Product" +msgstr "Producto" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_product +msgid "Product Variant" +msgstr "Variante de Producto" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,help:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "" +"Products with the \"Creates a Fleet Vehicle\" flagwill automatically be " +"converted to an Fleet Vehicle." +msgstr "" +"Los productos con la bandera \"Crea un Vehículo de Flota\" se convertirán " +"automáticamente en un Vehículo de Flota." + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__lot_id +msgid "Serial #" +msgstr "Número de serie #" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_move +msgid "Stock Move" +msgstr "Movimiento de existencias" + +#. module: fleet_vehicle_stock +#. odoo-python +#: code:addons/fleet_vehicle_stock/models/stock_move.py:0 +#, python-format +msgid "" +"The product '%s' is configure to create a fleet vehicle but vehicle model is " +"not configured in the product." +msgstr "" +"El producto '%s' está configurado para crear un vehículo de flota pero el " +"modelo de vehículo no está configurado en el producto." + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle +msgid "Vehicle" +msgstr "Vehículo" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__fleet_vehicle_model_id +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__fleet_vehicle_model_id +msgid "Vehicle Model" +msgstr "Modelo del vehículo" diff --git a/fleet_vehicle_stock/i18n/fleet_vehicle_stock.pot b/fleet_vehicle_stock/i18n/fleet_vehicle_stock.pot new file mode 100644 index 00000000..2e9e4d8a --- /dev/null +++ b/fleet_vehicle_stock/i18n/fleet_vehicle_stock.pot @@ -0,0 +1,122 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fleet_vehicle_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "Create Fleet Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__create_fleet_vehicle +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__create_fleet_vehicle +msgid "Creates a Fleet Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__current_stock_location_id +msgid "Current Inventory Location" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle_model__product_id +msgid "Default Product" +msgstr "" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_template_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_variant_easy_edit_view +msgid "Fleet" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_lot__fleet_vehicle_id +msgid "Fleet Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_model_form_view +msgid "Inventory" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.constraint,message:fleet_vehicle_stock.constraint_product_template_non_product_tracking_for_vehicle_model +msgid "" +"It is mandatory to configure the traceability by serial number in order to " +"be able to configure the vehicle model of the fleet in this product." +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_lot +msgid "Lot/Serial" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle_model +msgid "Model of a vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_template +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__product_id +msgid "Product" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_product +msgid "Product Variant" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,help:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "" +"Products with the \"Creates a Fleet Vehicle\" flagwill automatically be " +"converted to an Fleet Vehicle." +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__lot_id +msgid "Serial #" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: fleet_vehicle_stock +#. odoo-python +#: code:addons/fleet_vehicle_stock/models/stock_move.py:0 +#, python-format +msgid "" +"The product '%s' is configure to create a fleet vehicle but vehicle model is" +" not configured in the product." +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle +msgid "Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__fleet_vehicle_model_id +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__fleet_vehicle_model_id +msgid "Vehicle Model" +msgstr "" diff --git a/fleet_vehicle_stock/i18n/it.po b/fleet_vehicle_stock/i18n/it.po new file mode 100644 index 00000000..3b3e5eee --- /dev/null +++ b/fleet_vehicle_stock/i18n/it.po @@ -0,0 +1,131 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fleet_vehicle_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-08-22 11:07+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "Create Fleet Vehicle" +msgstr "Crea veicolo flotta" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__create_fleet_vehicle +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__create_fleet_vehicle +msgid "Creates a Fleet Vehicle" +msgstr "Crea un veicolo flotta" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__current_stock_location_id +msgid "Current Inventory Location" +msgstr "Ubicazione giacenza attuale" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle_model__product_id +msgid "Default Product" +msgstr "Prodotto predefinito" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_template_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_variant_easy_edit_view +msgid "Fleet" +msgstr "Flotta" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_lot__fleet_vehicle_id +msgid "Fleet Vehicle" +msgstr "Veicolo flotta" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_model_form_view +msgid "Inventory" +msgstr "Inventario" + +#. module: fleet_vehicle_stock +#: model:ir.model.constraint,message:fleet_vehicle_stock.constraint_product_template_non_product_tracking_for_vehicle_model +msgid "" +"It is mandatory to configure the traceability by serial number in order to " +"be able to configure the vehicle model of the fleet in this product." +msgstr "" +"È necessario configurare la tracciabilità per numero seriale per poter " +"configurare in questo prodotto il modello veicolo della flotta." + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_lot +msgid "Lot/Serial" +msgstr "Lotto/seriale" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle_model +msgid "Model of a vehicle" +msgstr "Modello di un veicolo" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipo prelievo" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_template +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__product_id +msgid "Product" +msgstr "Prodotto" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_product +msgid "Product Variant" +msgstr "Variante prodotto" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,help:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "" +"Products with the \"Creates a Fleet Vehicle\" flagwill automatically be " +"converted to an Fleet Vehicle." +msgstr "" +"I prodotti con la spunta \"Crea un veicolo flotta\" verranno automaticamente " +"convertiti in un veicolo flotta." + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__lot_id +msgid "Serial #" +msgstr "Nr. seriale" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_move +msgid "Stock Move" +msgstr "Movimento di magazzino" + +#. module: fleet_vehicle_stock +#. odoo-python +#: code:addons/fleet_vehicle_stock/models/stock_move.py:0 +#, python-format +msgid "" +"The product '%s' is configure to create a fleet vehicle but vehicle model is " +"not configured in the product." +msgstr "" +"Il prodotto '%s' è configurato per creare una veicolo flotta ma il modello " +"veicolo non è configurato nel prodotto." + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle +msgid "Vehicle" +msgstr "Veicolo" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__fleet_vehicle_model_id +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__fleet_vehicle_model_id +msgid "Vehicle Model" +msgstr "Modello veicolo" diff --git a/fleet_vehicle_stock/i18n/nl.po b/fleet_vehicle_stock/i18n/nl.po new file mode 100644 index 00000000..f5bdaca5 --- /dev/null +++ b/fleet_vehicle_stock/i18n/nl.po @@ -0,0 +1,123 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fleet_vehicle_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "Create Fleet Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__create_fleet_vehicle +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__create_fleet_vehicle +msgid "Creates a Fleet Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__current_stock_location_id +msgid "Current Inventory Location" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle_model__product_id +msgid "Default Product" +msgstr "" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_template_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_variant_easy_edit_view +msgid "Fleet" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_lot__fleet_vehicle_id +msgid "Fleet Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_model_form_view +msgid "Inventory" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.constraint,message:fleet_vehicle_stock.constraint_product_template_non_product_tracking_for_vehicle_model +msgid "" +"It is mandatory to configure the traceability by serial number in order to " +"be able to configure the vehicle model of the fleet in this product." +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_lot +msgid "Lot/Serial" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle_model +msgid "Model of a vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_template +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__product_id +msgid "Product" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_product +msgid "Product Variant" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,help:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "" +"Products with the \"Creates a Fleet Vehicle\" flagwill automatically be " +"converted to an Fleet Vehicle." +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__lot_id +msgid "Serial #" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_move +msgid "Stock Move" +msgstr "" + +#. module: fleet_vehicle_stock +#. odoo-python +#: code:addons/fleet_vehicle_stock/models/stock_move.py:0 +#, python-format +msgid "" +"The product '%s' is configure to create a fleet vehicle but vehicle model is" +" not configured in the product." +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle +msgid "Vehicle" +msgstr "" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__fleet_vehicle_model_id +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__fleet_vehicle_model_id +msgid "Vehicle Model" +msgstr "" diff --git a/fleet_vehicle_stock/i18n/pt_BR.po b/fleet_vehicle_stock/i18n/pt_BR.po new file mode 100644 index 00000000..946a4245 --- /dev/null +++ b/fleet_vehicle_stock/i18n/pt_BR.po @@ -0,0 +1,135 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fleet_vehicle_stock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-05-17 21:36+0000\n" +"Last-Translator: Rodrigo Macedo \n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "Create Fleet Vehicle" +msgstr "Criar Veículo de Frota" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__create_fleet_vehicle +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__create_fleet_vehicle +msgid "Creates a Fleet Vehicle" +msgstr "Cria um Veículo da Frota" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__current_stock_location_id +msgid "Current Inventory Location" +msgstr "Localização de Inventário Atual" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle_model__product_id +msgid "Default Product" +msgstr "Produto Padrão" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_template_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.product_variant_easy_edit_view +msgid "Fleet" +msgstr "Frota" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_stock_lot__fleet_vehicle_id +msgid "Fleet Vehicle" +msgstr "Veículo da Frota" + +#. module: fleet_vehicle_stock +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_form_view +#: model_terms:ir.ui.view,arch_db:fleet_vehicle_stock.fleet_vehicle_model_form_view +msgid "Inventory" +msgstr "Inventário" + +#. module: fleet_vehicle_stock +#: model:ir.model.constraint,message:fleet_vehicle_stock.constraint_product_template_non_product_tracking_for_vehicle_model +msgid "" +"It is mandatory to configure the traceability by serial number in order to " +"be able to configure the vehicle model of the fleet in this product." +msgstr "" +"É obrigatório configurar a rastreabilidade por número de série para poder " +"configurar o modelo de veículo da frota neste produto." + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_lot +msgid "Lot/Serial" +msgstr "Lote/Série" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle_model +msgid "Model of a vehicle" +msgstr "Modelo do veículo" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipo de Separação" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_template +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__product_id +msgid "Product" +msgstr "Produto" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_product_product +msgid "Product Variant" +msgstr "Variante do Produto" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,help:fleet_vehicle_stock.field_stock_picking_type__create_fleet_vehicle +msgid "" +"Products with the \"Creates a Fleet Vehicle\" flagwill automatically be " +"converted to an Fleet Vehicle." +msgstr "" +"Os produtos com marcados com \"Cria um Veículo da Frota\" serão " +"automaticamente convertidos em um Veículo da Frota." + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_fleet_vehicle__lot_id +msgid "Serial #" +msgstr "Série #" + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_stock_move +msgid "Stock Move" +msgstr "Movimento de Estoque" + +#. module: fleet_vehicle_stock +#. odoo-python +#: code:addons/fleet_vehicle_stock/models/stock_move.py:0 +#, python-format +msgid "" +"The product '%s' is configure to create a fleet vehicle but vehicle model is " +"not configured in the product." +msgstr "" +"O produto '%s' é configurado para criar um veículo da frota, mas o modelo do " +"veículo não está configurado no produto." + +#. module: fleet_vehicle_stock +#: model:ir.model,name:fleet_vehicle_stock.model_fleet_vehicle +msgid "Vehicle" +msgstr "Veículo" + +#. module: fleet_vehicle_stock +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_product__fleet_vehicle_model_id +#: model:ir.model.fields,field_description:fleet_vehicle_stock.field_product_template__fleet_vehicle_model_id +msgid "Vehicle Model" +msgstr "Modelo" + +#~ msgid "Product Template" +#~ msgstr "Modelo de Produto" diff --git a/fleet_vehicle_stock/models/__init__.py b/fleet_vehicle_stock/models/__init__.py new file mode 100644 index 00000000..061ee23f --- /dev/null +++ b/fleet_vehicle_stock/models/__init__.py @@ -0,0 +1,7 @@ +from . import fleet_vehicle_model +from . import fleet_vehicle +from . import product_template +from . import stock_picking_type +from . import stock_production_lot +from . import stock_move +from . import product_product diff --git a/fleet_vehicle_stock/models/fleet_vehicle.py b/fleet_vehicle_stock/models/fleet_vehicle.py new file mode 100644 index 00000000..c75ab22c --- /dev/null +++ b/fleet_vehicle_stock/models/fleet_vehicle.py @@ -0,0 +1,65 @@ +# Copyright 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FleetVehicle(models.Model): + _inherit = "fleet.vehicle" + + product_id = fields.Many2one( + "product.product", + string="Product", + ) + lot_id = fields.Many2one( + "stock.lot", + string="Serial #", + ) + current_stock_location_id = fields.Many2one( + "stock.location", + string="Current Inventory Location", + compute="_compute_current_stock_loc_id", + ) + + @api.depends("product_id", "lot_id") + def _compute_current_stock_loc_id(self): + for rec in self: + if rec.lot_id: + quants = self.env["stock.quant"].search( + [("lot_id", "=", rec.lot_id.id)], order="id desc", limit=1 + ) + if quants and quants.location_id: + rec.current_stock_location_id = quants.location_id.id + else: + rec.current_stock_location_id = False + else: + rec.current_stock_location_id = False + + @api.onchange("model_id") + def _onchange_model(self): + for rec in self: + rec.product_id = rec.model_id.product_id.id + + @api.onchange("product_id") + def _onchange_product(self): + for rec in self: + rec.lot_id = False + rec.current_stock_location_id = False + + @api.model_create_multi + def create(self, vals_list): + records = super().create(vals_list) + for record, vals in zip(records, vals_list, strict=False): + if record.lot_id and "lot_id" in vals: + record.lot_id.fleet_vehicle_id = record.id + return records + + def write(self, vals): + for rec in self: + prev_lot = rec.lot_id + res = super(FleetVehicle, rec).write(vals) + if prev_lot and (prev_lot != rec.lot_id): + prev_lot.fleet_vehicle_id = False + if rec.lot_id and "lot_id" in vals: + rec.lot_id.fleet_vehicle_id = rec.id + return res diff --git a/fleet_vehicle_stock/models/fleet_vehicle_model.py b/fleet_vehicle_stock/models/fleet_vehicle_model.py new file mode 100644 index 00000000..4b1b4a81 --- /dev/null +++ b/fleet_vehicle_stock/models/fleet_vehicle_model.py @@ -0,0 +1,13 @@ +# Copyright 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class FleetVehicleModel(models.Model): + _inherit = "fleet.vehicle.model" + + product_id = fields.Many2one( + "product.product", + string="Default Product", + ) diff --git a/fleet_vehicle_stock/models/product_product.py b/fleet_vehicle_stock/models/product_product.py new file mode 100644 index 00000000..00df1bd5 --- /dev/null +++ b/fleet_vehicle_stock/models/product_product.py @@ -0,0 +1,13 @@ +# Copyright 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + fleet_vehicle_model_id = fields.Many2one( + "fleet.vehicle.model", + string="Vehicle Model", + ) diff --git a/fleet_vehicle_stock/models/product_template.py b/fleet_vehicle_stock/models/product_template.py new file mode 100644 index 00000000..b2703bb4 --- /dev/null +++ b/fleet_vehicle_stock/models/product_template.py @@ -0,0 +1,56 @@ +# Copyright 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + create_fleet_vehicle = fields.Boolean(string="Creates a Fleet Vehicle") + fleet_vehicle_model_id = fields.Many2one( + "fleet.vehicle.model", + compute="_compute_fleet_vehicle_model_id", + inverse="_inverse_fleet_vehicle_model_id", + store=True, + string="Vehicle Model", + ) + + @api.depends("product_variant_ids", "product_variant_ids.fleet_vehicle_model_id") + def _compute_fleet_vehicle_model_id(self): + unique_variants = self.filtered( + lambda template: len(template.product_variant_ids) == 1 + ) + for template in unique_variants: + template.fleet_vehicle_model_id = ( + template.product_variant_ids.fleet_vehicle_model_id + ) + for template in self - unique_variants: + template.fleet_vehicle_model_id = False + + def _inverse_fleet_vehicle_model_id(self): + if len(self.product_variant_ids) == 1: + self.product_variant_ids.fleet_vehicle_model_id = ( + self.fleet_vehicle_model_id + ) + + @api.model_create_multi + def create(self, vals_list): + templates = super().create(vals_list) + for template, vals in zip(templates, vals_list, strict=False): + related_vals = {} + if vals.get("fleet_vehicle_model_id"): + related_vals["fleet_vehicle_model_id"] = vals["fleet_vehicle_model_id"] + if related_vals: + template.write(related_vals) + return templates + + _sql_constraints = [ + ( + "non_product_tracking_for_vehicle_model", + "CHECK((tracking != 'serial' AND fleet_vehicle_model_id IS NULL) " + "OR (tracking = 'serial'))", + "It is mandatory to configure the traceability by serial number in order " + "to be able to configure the vehicle model of the fleet in this product.", + ), + ] diff --git a/fleet_vehicle_stock/models/stock_move.py b/fleet_vehicle_stock/models/stock_move.py new file mode 100644 index 00000000..b2df76b1 --- /dev/null +++ b/fleet_vehicle_stock/models/stock_move.py @@ -0,0 +1,45 @@ +# Copyright 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, models +from odoo.exceptions import UserError + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _prepare_vehicle_values(self, move_line): + return { + "name": f"{move_line.product_id.name} ({move_line.lot_id.name})", + "model_id": move_line.product_id.fleet_vehicle_model_id.id, + "product_id": move_line.product_id.id, + "lot_id": move_line.lot_id.id, + "current_stock_location_id": move_line.location_dest_id.id, + } + + def _action_done(self, cancel_backorder=False): + res = super()._action_done(cancel_backorder) + for rec in self: + if ( + rec.product_id.product_tmpl_id.create_fleet_vehicle + and not rec.product_id.fleet_vehicle_model_id + ): + raise UserError( + _( + "The product '%s' is configured to create a fleet " + "vehicle but vehicle model is not configured in the " + "product." + ) + % rec.product_id.name + ) + + if ( + rec.state == "done" + and rec.picking_type_id.create_fleet_vehicle + and rec.product_tmpl_id.create_fleet_vehicle + and rec.product_id.fleet_vehicle_model_id + ): + for line in rec.move_line_ids: + vals = self._prepare_vehicle_values(line) + line.lot_id.fleet_vehicle_id = rec.env["fleet.vehicle"].create(vals) + return res diff --git a/fleet_vehicle_stock/models/stock_picking_type.py b/fleet_vehicle_stock/models/stock_picking_type.py new file mode 100644 index 00000000..eb5e56ba --- /dev/null +++ b/fleet_vehicle_stock/models/stock_picking_type.py @@ -0,0 +1,14 @@ +# Copyright 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockPickingType(models.Model): + _inherit = "stock.picking.type" + + create_fleet_vehicle = fields.Boolean( + name="Create Fleet Vehicle", + help='Products with the "Creates a Fleet Vehicle" flag' + "will automatically be converted to an Fleet Vehicle.", + ) diff --git a/fleet_vehicle_stock/models/stock_production_lot.py b/fleet_vehicle_stock/models/stock_production_lot.py new file mode 100644 index 00000000..769c2036 --- /dev/null +++ b/fleet_vehicle_stock/models/stock_production_lot.py @@ -0,0 +1,14 @@ +# Copyright 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockProductionLot(models.Model): + _inherit = "stock.lot" + + fleet_vehicle_id = fields.Many2one( + "fleet.vehicle", + string="Fleet Vehicle", + readonly=True, + ) diff --git a/fleet_vehicle_stock/pyproject.toml b/fleet_vehicle_stock/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/fleet_vehicle_stock/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fleet_vehicle_stock/readme/CONFIGURE.md b/fleet_vehicle_stock/readme/CONFIGURE.md new file mode 100644 index 00000000..86ff0c82 --- /dev/null +++ b/fleet_vehicle_stock/readme/CONFIGURE.md @@ -0,0 +1,21 @@ +Products can be automatically converted into Fleet Vehicle. This is only +available only for products tracked by serial number. This needs to be +enabled both on Operation Types and Products. For example, we may want +to create the Fleet Vehicle on Delivery, or on Receipts. + +To enable on Products: + +- Go to Inventory \> Master Data \> Products +- Open the Product form, Inventory tab +- On the "Traceability" section, make sure "Tracking" is set to "By + Unique Serial Number" +- Enable the "Creates Fleet Vehicle" checkbox +- Open Fleet tab +- Select Fleet Vehicle Model + +To enable on Operation Types: + +- Go to Inventory \> Configuration \> Operation Types +- Select the intended Operation Type ("Receipts" for example) +- On the "Traceability" section, enable the "Create Fleet Vehicle" + checkbox diff --git a/fleet_vehicle_stock/readme/CONTRIBUTORS.md b/fleet_vehicle_stock/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..324ae1f4 --- /dev/null +++ b/fleet_vehicle_stock/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Marcel Savegnago \ +- Kaynnan Lemes \ diff --git a/fleet_vehicle_stock/readme/CREDITS.md b/fleet_vehicle_stock/readme/CREDITS.md new file mode 100644 index 00000000..7bf0340a --- /dev/null +++ b/fleet_vehicle_stock/readme/CREDITS.md @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +- Escodoo - diff --git a/fleet_vehicle_stock/readme/DESCRIPTION.md b/fleet_vehicle_stock/readme/DESCRIPTION.md new file mode 100644 index 00000000..99ad49d7 --- /dev/null +++ b/fleet_vehicle_stock/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module is an add-on for the Fleet application in Odoo. It allows +you to track your Fleet Vehicles in stock moves. diff --git a/fleet_vehicle_stock/readme/USAGE.md b/fleet_vehicle_stock/readme/USAGE.md new file mode 100644 index 00000000..f8310b92 --- /dev/null +++ b/fleet_vehicle_stock/readme/USAGE.md @@ -0,0 +1,2 @@ +A completed stock move for a properly configured Product on a configured +stock Operation Type will automatically create a fleet vehicle. diff --git a/fleet_vehicle_stock/static/description/icon.png b/fleet_vehicle_stock/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/fleet_vehicle_stock/static/description/icon.png differ diff --git a/fleet_vehicle_stock/static/description/index.html b/fleet_vehicle_stock/static/description/index.html new file mode 100644 index 00000000..5b7143b1 --- /dev/null +++ b/fleet_vehicle_stock/static/description/index.html @@ -0,0 +1,466 @@ + + + + + +Fleet Vehicle Stock + + + +
+

Fleet Vehicle Stock

+ + +

Beta License: AGPL-3 OCA/fleet Translate me on Weblate Try me on Runboat

+

This module is an add-on for the Fleet application in Odoo. It allows +you to track your Fleet Vehicles in stock moves.

+

Table of contents

+ +
+

Configuration

+

Products can be automatically converted into Fleet Vehicle. This is only +available only for products tracked by serial number. This needs to be +enabled both on Operation Types and Products. For example, we may want +to create the Fleet Vehicle on Delivery, or on Receipts.

+

To enable on Products:

+
    +
  • Go to Inventory > Master Data > Products
  • +
  • Open the Product form, Inventory tab
  • +
  • On the “Traceability” section, make sure “Tracking” is set to “By +Unique Serial Number”
  • +
  • Enable the “Creates Fleet Vehicle” checkbox
  • +
  • Open Fleet tab
  • +
  • Select Fleet Vehicle Model
  • +
+

To enable on Operation Types:

+
    +
  • Go to Inventory > Configuration > Operation Types
  • +
  • Select the intended Operation Type (“Receipts” for example)
  • +
  • On the “Traceability” section, enable the “Create Fleet Vehicle” +checkbox
  • +
+
+
+

Usage

+

A completed stock move for a properly configured Product on a configured +stock Operation Type will automatically create a fleet vehicle.

+
+
+

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 to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Escodoo
  • +
+
+ +
+

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 maintainer:

+

marcelsavegnago

+

This module is part of the OCA/fleet project on GitHub.

+

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

+
+
+
+ + diff --git a/fleet_vehicle_stock/tests/__init__.py b/fleet_vehicle_stock/tests/__init__.py new file mode 100644 index 00000000..5a56fa0a --- /dev/null +++ b/fleet_vehicle_stock/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import test_fleet_vehicle diff --git a/fleet_vehicle_stock/tests/test_fleet_vehicle.py b/fleet_vehicle_stock/tests/test_fleet_vehicle.py new file mode 100644 index 00000000..4e33bb4e --- /dev/null +++ b/fleet_vehicle_stock/tests/test_fleet_vehicle.py @@ -0,0 +1,294 @@ +# Copyright (C) 2021 - TODAY, Marcel Savegnago +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.exceptions import UserError +from odoo.tests.common import TransactionCase + + +class TestFleetVehicle(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.brand = cls.env.ref("fleet.brand_opel") + cls.stock_location = cls.env.ref("stock.stock_location_customers") + + cls.vehicle_model1 = cls.env["fleet.vehicle.model"].create( + {"name": "Test Vehicle Model 1", "brand_id": cls.brand.id} + ) + cls.vehicle_model2 = cls.env["fleet.vehicle.model"].create( + {"name": "Test Vehicle Model 2", "brand_id": cls.brand.id} + ) + + cls.product1 = cls.env["product.product"].create( + { + "name": "Product 1", + "is_storable": True, + "type": "consu", + "fleet_vehicle_model_id": cls.vehicle_model1.id, + "tracking": "serial", + } + ) + cls.product2 = cls.env["product.product"].create( + { + "name": "Product 2", + "is_storable": True, + "type": "consu", + "fleet_vehicle_model_id": cls.vehicle_model1.id, + "tracking": "serial", + } + ) + + cls.lot1 = cls.env["stock.lot"].create( + { + "name": "serial1", + "product_id": cls.product1.id, + "company_id": cls.env.user.company_id.id, + } + ) + cls.lot2 = cls.env["stock.lot"].create( + { + "name": "serial2", + "product_id": cls.product1.id, + "company_id": cls.env.user.company_id.id, + } + ) + + cls.quant = cls.env["stock.quant"].create( + { + "product_id": cls.product1.id, + "location_id": cls.stock_location.id, + "quantity": 1.0, + "lot_id": cls.lot1.id, + } + ) + + cls.vehicle = cls.env["fleet.vehicle"].create( + { + "model_id": cls.vehicle_model1.id, + "product_id": cls.product1.id, + "lot_id": cls.lot1.id, + } + ) + + def test_onchange_product(self): + vehicle = self.vehicle + vehicle._onchange_product() + self.assertFalse(vehicle.current_stock_location_id) + + def test_onchange_model(self): + vehicle = self.vehicle + vehicle._onchange_model() + self.assertEqual(vehicle.product_id, vehicle.model_id.product_id) + + def test_compute_current_stock_loc_id(self): + vehicle = self.vehicle + self.assertEqual(vehicle.current_stock_location_id, self.stock_location) + + vehicle.lot_id = self.lot2 + self.assertTrue(vehicle.current_stock_location_id.id is False) + + vehicle.lot_id = False + self.assertTrue(vehicle.current_stock_location_id.id is False) + + def test_inverse_fleet_vehicle_model_id(self): + product2 = self.product2 + self.assertEqual(len(product2.product_variant_ids), 1) + + product2.fleet_vehicle_model_id = self.vehicle_model2 + self.assertEqual( + product2.product_variant_ids.fleet_vehicle_model_id, + product2.fleet_vehicle_model_id, + ) + + def test_create_product_template_with_model(self): + product_tmpl = self.env["product.template"].create( + { + "name": "Product Template With Model", + "fleet_vehicle_model_id": self.vehicle_model1.id, + "tracking": "serial", + } + ) + self.assertEqual(product_tmpl.fleet_vehicle_model_id, self.vehicle_model1) + self.assertEqual(len(product_tmpl.product_variant_ids), 1) + self.assertEqual( + product_tmpl.product_variant_ids.fleet_vehicle_model_id, self.vehicle_model1 + ) + + def test_stock_move_create_vehicle(self): + picking_type = self.env["stock.picking.type"].create( + { + "name": "Test Picking Type", + "code": "incoming", + "sequence_code": "TPT", + "create_fleet_vehicle": True, + } + ) + self.product1.product_tmpl_id.create_fleet_vehicle = True + + move = self.env["stock.move"].create( + { + "name": "Test Move", + "product_id": self.product1.id, + "product_uom_qty": 1.0, + "product_uom": self.product1.uom_id.id, + "location_id": self.env.ref("stock.stock_location_suppliers").id, + "location_dest_id": self.stock_location.id, + "picking_type_id": picking_type.id, + } + ) + move._action_confirm() + + lot = self.env["stock.lot"].create( + { + "name": "new_serial", + "product_id": self.product1.id, + "company_id": self.env.user.company_id.id, + } + ) + + move.move_line_ids.write({"quantity": 1.0, "lot_id": lot.id}) + + move.picked = True + move._action_done() + + self.assertTrue(lot.fleet_vehicle_id) + self.assertEqual(lot.fleet_vehicle_id.model_id, self.vehicle_model1) + self.assertEqual(lot.fleet_vehicle_id.product_id, self.product1) + self.assertEqual( + lot.fleet_vehicle_id.current_stock_location_id, self.stock_location + ) + + def test_stock_move_skip_vehicle_creation(self): + picking_type_no_create = self.env["stock.picking.type"].create( + { + "name": "Test Picking Type No Create", + "code": "incoming", + "sequence_code": "TPTNC", + "create_fleet_vehicle": False, + } + ) + self.product1.product_tmpl_id.create_fleet_vehicle = True + + move1 = self.env["stock.move"].create( + { + "name": "Test Move Skip 1", + "product_id": self.product1.id, + "product_uom_qty": 1.0, + "product_uom": self.product1.uom_id.id, + "location_id": self.env.ref("stock.stock_location_suppliers").id, + "location_dest_id": self.stock_location.id, + "picking_type_id": picking_type_no_create.id, + } + ) + move1._action_confirm() + lot1 = self.env["stock.lot"].create( + { + "name": "serial_skip_1", + "product_id": self.product1.id, + "company_id": self.env.user.company_id.id, + } + ) + move1.move_line_ids.write({"quantity": 1.0, "lot_id": lot1.id}) + move1.picked = True + move1._action_done() + self.assertFalse( + lot1.fleet_vehicle_id, + "Vehicle should not be created if picking type disabled", + ) + + picking_type_create = self.env["stock.picking.type"].create( + { + "name": "Test Picking Type Create", + "code": "incoming", + "sequence_code": "TPTC", + "create_fleet_vehicle": True, + } + ) + self.product1.product_tmpl_id.create_fleet_vehicle = False + + move2 = self.env["stock.move"].create( + { + "name": "Test Move Skip 2", + "product_id": self.product1.id, + "product_uom_qty": 1.0, + "product_uom": self.product1.uom_id.id, + "location_id": self.env.ref("stock.stock_location_suppliers").id, + "location_dest_id": self.stock_location.id, + "picking_type_id": picking_type_create.id, + } + ) + move2._action_confirm() + lot2 = self.env["stock.lot"].create( + { + "name": "serial_skip_2", + "product_id": self.product1.id, + "company_id": self.env.user.company_id.id, + } + ) + move2.move_line_ids.write({"quantity": 1.0, "lot_id": lot2.id}) + move2.picked = True + move2._action_done() + self.assertFalse( + lot2.fleet_vehicle_id, + "Vehicle should not be created if product template disabled", + ) + + def test_create_fleet_vehicle_updates_lot(self): + lot = self.env["stock.lot"].create( + { + "name": "serial_vehicle_create", + "product_id": self.product1.id, + "company_id": self.env.user.company_id.id, + } + ) + + vehicle = self.env["fleet.vehicle"].create( + { + "model_id": self.vehicle_model1.id, + "product_id": self.product1.id, + "lot_id": lot.id, + } + ) + + self.assertEqual( + lot.fleet_vehicle_id, vehicle, "Lot should be linked to the created vehicle" + ) + + def test_create_fleet_vehicle_no_lot(self): + vehicle1 = self.env["fleet.vehicle"].create( + { + "model_id": self.vehicle_model1.id, + "product_id": self.product1.id, + } + ) + self.assertFalse(vehicle1.lot_id, "Vehicle should have no lot") + vehicle2 = self.env["fleet.vehicle"].create( + { + "model_id": self.vehicle_model1.id, + "product_id": self.product1.id, + "lot_id": False, + } + ) + self.assertFalse(vehicle2.lot_id, "Vehicle should have no lot") + + def test_stock_move_error(self): + product_no_model = self.env["product.product"].create( + { + "name": "Product No Model", + "is_storable": True, + "create_fleet_vehicle": True, + } + ) + + move = self.env["stock.move"].create( + { + "name": "Test Move Error", + "product_id": product_no_model.id, + "product_uom_qty": 1.0, + "location_id": self.env.ref("stock.stock_location_suppliers").id, + "location_dest_id": self.stock_location.id, + } + ) + + with self.assertRaises(UserError): + move._action_done() diff --git a/fleet_vehicle_stock/views/fleet_vehicle.xml b/fleet_vehicle_stock/views/fleet_vehicle.xml new file mode 100644 index 00000000..1267779e --- /dev/null +++ b/fleet_vehicle_stock/views/fleet_vehicle.xml @@ -0,0 +1,40 @@ + + + + + fleet.vehicle.form (in fleet_vehicle_stock) + fleet.vehicle + + + + + + + + + + + + + + + fleet.vehicle.search (in fleet_vehicle_stock) + fleet.vehicle + + + + + + + + + diff --git a/fleet_vehicle_stock/views/fleet_vehicle_model.xml b/fleet_vehicle_stock/views/fleet_vehicle_model.xml new file mode 100644 index 00000000..d743ccad --- /dev/null +++ b/fleet_vehicle_stock/views/fleet_vehicle_model.xml @@ -0,0 +1,19 @@ + + + + + fleet.vehicle.model.form (in fleet_vehicle_stock) + fleet.vehicle.model + + + + + + + + + + + + diff --git a/fleet_vehicle_stock/views/product_product.xml b/fleet_vehicle_stock/views/product_product.xml new file mode 100644 index 00000000..3bdb2f70 --- /dev/null +++ b/fleet_vehicle_stock/views/product_product.xml @@ -0,0 +1,48 @@ + + + + + product.product.form (in fleet_vehicle_stock) + product.product + + + + + + + + + + + + + product.product.view.form.easy (in fleet_vehicle_stock) + product.product + + + + + + + + + + + + diff --git a/fleet_vehicle_stock/views/product_template.xml b/fleet_vehicle_stock/views/product_template.xml new file mode 100644 index 00000000..fbe8aea9 --- /dev/null +++ b/fleet_vehicle_stock/views/product_template.xml @@ -0,0 +1,41 @@ + + + + + product.template.form (in fleet_vehicle_stock) + product.template + + + + + + + + + + + + product.template.product.form (in fleet_vehicle_stock) + product.template + + + + + + + + + + + diff --git a/fleet_vehicle_stock/views/stock_picking_type.xml b/fleet_vehicle_stock/views/stock_picking_type.xml new file mode 100644 index 00000000..7015e78e --- /dev/null +++ b/fleet_vehicle_stock/views/stock_picking_type.xml @@ -0,0 +1,15 @@ + + + + + stock.picking.type.form (in fleet_vehicle_stock) + stock.picking.type + + + + + + + + diff --git a/fleet_vehicle_stock/views/stock_production_lot.xml b/fleet_vehicle_stock/views/stock_production_lot.xml new file mode 100644 index 00000000..2f9f6c81 --- /dev/null +++ b/fleet_vehicle_stock/views/stock_production_lot.xml @@ -0,0 +1,26 @@ + + + + + stock.production.lot.form (in fleet_vehicle_stock) + stock.lot + + + + + + + + + + + {'search_default_group_by_product': 1, + 'default_product_id': context.get('product_id', False)} + + +