diff --git a/fleet_vehicle_category/views/fleet_vehicle_views.xml b/fleet_vehicle_category/views/fleet_vehicle_views.xml index bc7c48da..e8300778 100644 --- a/fleet_vehicle_category/views/fleet_vehicle_views.xml +++ b/fleet_vehicle_category/views/fleet_vehicle_views.xml @@ -29,6 +29,7 @@ + diff --git a/fleet_vehicle_category_analytic/README.rst b/fleet_vehicle_category_analytic/README.rst new file mode 100644 index 00000000..8a735df3 --- /dev/null +++ b/fleet_vehicle_category_analytic/README.rst @@ -0,0 +1,104 @@ +====================== +Fleet Vehicle Analytic +====================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:43e12b9a776f7464fecf6608fc4ee45d591ff74590d37fea45956cbe2b3d34eb + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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_category_analytic + :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_category_analytic + :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 adds analytic account definition per vehicle category. It +define this analytic account on the analytic distribution of account +move line. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Prerequisites: Analytic enabled, Vehicle added to Vehicle Categories. + +1. Browse to "Fleet / Configuration / Categories" and define an Analytic + Account on a Category +2. Create a new invoice from "Accounting / Vendors / Bills / [+ New]" +3. On the invoice line, set a vehicle belonging to the Category modified + at step 1. To view the "Vehicule" column on invoice lines, do not + forget to add the column. +4. Look at the Analytic Distribution: the Analytic Account previously + defined on the Category is added at 100%. + +Warning: this analytic is very simply merged / added to the existing +analytic distribution, which may lead to more than 100% distribution. + +Changelog +========= + +18.0.1.0.0 (2026-02-27) +----------------------- + +- [NEW] Module creation + +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 +------- + +* Arnaud Layec +* Akretion + +Contributors +------------ + +- Arnaud Layec + +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. + +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_category_analytic/__init__.py b/fleet_vehicle_category_analytic/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/fleet_vehicle_category_analytic/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/fleet_vehicle_category_analytic/__manifest__.py b/fleet_vehicle_category_analytic/__manifest__.py new file mode 100644 index 00000000..2aed48e5 --- /dev/null +++ b/fleet_vehicle_category_analytic/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2026 Arnaud Layec () +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Fleet Vehicle Analytic", + "summary": """ + Define Analytic Account per vehicle category + """, + "version": "18.0.1.0.0", + "category": "Human Resources", + "author": "Arnaud Layec, Akretion, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/fleet", + "license": "AGPL-3", + "depends": ["account_fleet", "fleet_vehicle_category"], + "data": ["views/fleet_vehicle_category_views.xml"], + "installable": True, + "auto_install": False, +} diff --git a/fleet_vehicle_category_analytic/models/__init__.py b/fleet_vehicle_category_analytic/models/__init__.py new file mode 100644 index 00000000..03158905 --- /dev/null +++ b/fleet_vehicle_category_analytic/models/__init__.py @@ -0,0 +1 @@ +from . import account_move_line, fleet_vehicle_category diff --git a/fleet_vehicle_category_analytic/models/account_move_line.py b/fleet_vehicle_category_analytic/models/account_move_line.py new file mode 100644 index 00000000..623b4ba5 --- /dev/null +++ b/fleet_vehicle_category_analytic/models/account_move_line.py @@ -0,0 +1,30 @@ +# Copyright 2026 - Arnaud LAYEC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class AccountMoveLine(models.Model): + _inherit = ["account.move.line"] + + @api.depends("vehicle_id") + def _compute_analytic_distribution(self): + """Inject the analytic account of the vehicle's category. + When the account is already present in the distribution its + percentage is kept as-is to avoid overriding manual adjustments made + through other sources; otherwise it is added at 100%. + """ + res = super()._compute_analytic_distribution() + for line in self: + if not line.vehicle_id: + continue + account = line.vehicle_id.vehicle_category_id.analytic_account_id + if not account: + continue + + distribution = line.analytic_distribution or {} + account_key = str(account.id) + if account_key not in distribution: + distribution[account_key] = 100 + line.analytic_distribution = distribution + return res diff --git a/fleet_vehicle_category_analytic/models/fleet_vehicle_category.py b/fleet_vehicle_category_analytic/models/fleet_vehicle_category.py new file mode 100644 index 00000000..460ad0b3 --- /dev/null +++ b/fleet_vehicle_category_analytic/models/fleet_vehicle_category.py @@ -0,0 +1,19 @@ +# Copyright 2026 - Arnaud LAYEC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class FleetVehicleCategory(models.Model): + _inherit = ["fleet.vehicle.category"] + + company_id = fields.Many2one( + comodel_name="res.company", + readonly=True, + default=lambda self: self.env.company.root_id, + ) + analytic_account_id = fields.Many2one( + comodel_name="account.analytic.account", + string="Analytic Account", + check_company=True, + ) diff --git a/fleet_vehicle_category_analytic/pyproject.toml b/fleet_vehicle_category_analytic/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/fleet_vehicle_category_analytic/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fleet_vehicle_category_analytic/readme/CONTRIBUTORS.md b/fleet_vehicle_category_analytic/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..83a4b7a8 --- /dev/null +++ b/fleet_vehicle_category_analytic/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Arnaud Layec \<\> diff --git a/fleet_vehicle_category_analytic/readme/DESCRIPTION.md b/fleet_vehicle_category_analytic/readme/DESCRIPTION.md new file mode 100644 index 00000000..9b7d7120 --- /dev/null +++ b/fleet_vehicle_category_analytic/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module adds analytic account definition per vehicle category. +It define this analytic account on the analytic distribution of account move line. diff --git a/fleet_vehicle_category_analytic/readme/HISTORY.md b/fleet_vehicle_category_analytic/readme/HISTORY.md new file mode 100644 index 00000000..e34e6153 --- /dev/null +++ b/fleet_vehicle_category_analytic/readme/HISTORY.md @@ -0,0 +1,3 @@ +## 18.0.1.0.0 (2026-02-27) + +- \[NEW\] Module creation diff --git a/fleet_vehicle_category_analytic/readme/USAGE.md b/fleet_vehicle_category_analytic/readme/USAGE.md new file mode 100644 index 00000000..59726611 --- /dev/null +++ b/fleet_vehicle_category_analytic/readme/USAGE.md @@ -0,0 +1,12 @@ + +Prerequisites: Analytic enabled, Vehicle added to Vehicle Categories. + +1. Browse to "Fleet / Configuration / Categories" and define an Analytic Account on a Category +2. Create a new invoice from "Accounting / Vendors / Bills / [+ New]" +3. On the invoice line, set a vehicle belonging to the Category modified at step 1. + To view the "Vehicule" column on invoice lines, do not forget to add the column. +4. Look at the Analytic Distribution: the Analytic Account previously defined on the Category + is added at 100%. + +Warning: this analytic is very simply merged / added to the existing analytic distribution, +which may lead to more than 100% distribution. diff --git a/fleet_vehicle_category_analytic/static/description/icon.png b/fleet_vehicle_category_analytic/static/description/icon.png new file mode 100644 index 00000000..1743a4c1 Binary files /dev/null and b/fleet_vehicle_category_analytic/static/description/icon.png differ diff --git a/fleet_vehicle_category_analytic/static/description/index.html b/fleet_vehicle_category_analytic/static/description/index.html new file mode 100644 index 00000000..b023d379 --- /dev/null +++ b/fleet_vehicle_category_analytic/static/description/index.html @@ -0,0 +1,456 @@ + + + + + +Fleet Vehicle Analytic + + + +
+

Fleet Vehicle Analytic

+ + +

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

+

This module adds analytic account definition per vehicle category. It +define this analytic account on the analytic distribution of account +move line.

+

Table of contents

+ +
+

Usage

+

Prerequisites: Analytic enabled, Vehicle added to Vehicle Categories.

+
    +
  1. Browse to “Fleet / Configuration / Categories” and define an Analytic +Account on a Category
  2. +
  3. Create a new invoice from “Accounting / Vendors / Bills / [+ New]”
  4. +
  5. On the invoice line, set a vehicle belonging to the Category modified +at step 1. To view the “Vehicule” column on invoice lines, do not +forget to add the column.
  6. +
  7. Look at the Analytic Distribution: the Analytic Account previously +defined on the Category is added at 100%.
  8. +
+

Warning: this analytic is very simply merged / added to the existing +analytic distribution, which may lead to more than 100% distribution.

+
+
+

Changelog

+
+

18.0.1.0.0 (2026-02-27)

+
    +
  • [NEW] Module creation
  • +
+
+
+
+

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

+
    +
  • Arnaud Layec
  • +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

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.

+

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_category_analytic/tests/__init__.py b/fleet_vehicle_category_analytic/tests/__init__.py new file mode 100644 index 00000000..6e1d2d7e --- /dev/null +++ b/fleet_vehicle_category_analytic/tests/__init__.py @@ -0,0 +1 @@ +from . import test_fleet_vehicle_category_analytic diff --git a/fleet_vehicle_category_analytic/tests/test_fleet_vehicle_category_analytic.py b/fleet_vehicle_category_analytic/tests/test_fleet_vehicle_category_analytic.py new file mode 100644 index 00000000..1657014f --- /dev/null +++ b/fleet_vehicle_category_analytic/tests/test_fleet_vehicle_category_analytic.py @@ -0,0 +1,113 @@ +# Copyright 2026 - Arnaud LAYEC +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import Command +from odoo.tests import tagged + +from odoo.addons.account.tests.common import AccountTestInvoicingCommon + + +@tagged("post_install", "-at_install") +class TestFleetVehicleCategoryAnalytic(AccountTestInvoicingCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Analytic + analytic_plan = cls.env["account.analytic.plan"].create({"name": "Default"}) + analytic_account_vehicle = cls.env["account.analytic.account"].create( + { + "name": "Analytic Vehicle", + "plan_id": analytic_plan.id, + } + ) + analytic_account_other = analytic_account_vehicle.copy( + { + "name": "Analytic Other", + } + ) + cls.vehicle_key = str(analytic_account_vehicle.id) + cls.other_key = str(analytic_account_other.id) + + # Fleet + vehicle_brand = cls.env["fleet.vehicle.model.brand"].create( + {"name": "TestBrand"} + ) + vehicle_model = cls.env["fleet.vehicle.model"].create( + { + "name": "TestModel", + "brand_id": vehicle_brand.id, + } + ) + cls.vehicle_category = cls.env["fleet.vehicle.category"].create( + { + "name": "Test Category", + "analytic_account_id": analytic_account_vehicle.id, + } + ) + cls.vehicle = cls.env["fleet.vehicle"].create( + { + "name": "Test Vehicle", + "model_id": vehicle_model.id, + "vehicle_category_id": cls.vehicle_category.id, + "license_plate": "TEST-001", + } + ) + + # Helpers + def _create_vendor_bill(self, vehicle=None, analytic_distribution=None): + """Return a draft vendor bill with one product line.""" + line_vals = { + "name": "Test vehicle", + "account_id": self.company_data["default_account_expense"].id, + "quantity": 1, + "price_unit": 100.0, + "analytic_distribution": {self.other_key: 100}, + } + if vehicle: + line_vals["vehicle_id"] = vehicle.id + if analytic_distribution: + line_vals["analytic_distribution"] |= analytic_distribution + move = self.env["account.move"].create( + { + "move_type": "in_invoice", + "partner_id": self.partner_a.id, + "invoice_line_ids": [Command.create(line_vals)], + } + ) + line = move.invoice_line_ids[0] + if vehicle: + # we need to force-compute: since we give 'analytic_distribution' at + # line creation, _compute method is not called + line._compute_analytic_distribution() + return line + + # ====== Test cases =====# + def test_analytic_cascading_from_vehicle_category(self): + """Analytic should cascade from vehicle category to account move line + without overwritten existing analytic distribution + """ + line = self._create_vendor_bill(vehicle=self.vehicle) + self.assertIn(self.vehicle_key, line.analytic_distribution) + self.assertIn(self.other_key, line.analytic_distribution) + + def test_not_overwritten(self): + """If the category account is already in the distribution (e.g. set + manually), its percentage should not be overwritten""" + line = self._create_vendor_bill( + vehicle=self.vehicle, + analytic_distribution={self.vehicle_key: 50}, + ) + self.assertEqual(line.analytic_distribution.get(self.vehicle_key), 50) + + def test_without_vehicle(self): + """An account move line without a vehicle is not impacted""" + line = self._create_vendor_bill() + self.assertNotIn(self.vehicle_key, line.analytic_distribution) + self.assertIn(self.other_key, line.analytic_distribution) + + def test_without_category_analytic(self): + """A vehicle category without analytic should not modify anything""" + self.vehicle_category.analytic_account_id = False + line = self._create_vendor_bill(vehicle=self.vehicle) + self.assertIn(self.other_key, line.analytic_distribution) diff --git a/fleet_vehicle_category_analytic/views/fleet_vehicle_category_views.xml b/fleet_vehicle_category_analytic/views/fleet_vehicle_category_views.xml new file mode 100644 index 00000000..307fa5f7 --- /dev/null +++ b/fleet_vehicle_category_analytic/views/fleet_vehicle_category_views.xml @@ -0,0 +1,18 @@ + + + + + fleet.vehicle.category.form.analytic + fleet.vehicle.category + + + + + + + +