diff --git a/joint_buying_base/__manifest__.py b/joint_buying_base/__manifest__.py
index 04b3b6a1..4e80430f 100644
--- a/joint_buying_base/__manifest__.py
+++ b/joint_buying_base/__manifest__.py
@@ -11,6 +11,8 @@
"depends": [
"base",
"mail",
+ "web",
+ "decimal_precision",
# OCA
"base_geolocalize_company",
"res_company_code",
@@ -21,7 +23,9 @@
# GRAP
"name_search_reset_res_partner",
],
- "external_dependencies": {"python": ["openupgradelib", "geopy", "bokeh", "pandas"]},
+ "external_dependencies": {
+ "python": ["openupgradelib", "geopy", "bokeh", "pandas", "treelib"]
+ },
"data": [
"security/ir_module_category.xml",
"security/res_groups.xml",
@@ -38,7 +42,12 @@
"views/view_joint_buying_tour_type.xml",
"views/view_joint_buying_tour.xml",
"views/view_joint_buying_tour_line.xml",
+ "views/view_joint_buying_transport_request.xml",
+ "views/view_joint_buying_transport_request_line.xml",
"wizards/view_joint_buying_wizard_set_tour.xml",
+ "wizards/joint_buying_wizard_find_route.xml",
+ "reports/report_joint_buying_tour.xml",
+ "reports/report.xml",
"views/templates.xml",
],
"demo": [
@@ -49,6 +58,7 @@
"demo/joint_buying_carrier.xml",
"demo/joint_buying_tour_type.xml",
"demo/joint_buying_tour.xml",
+ "demo/joint_buying_transport_request.xml",
],
"post_init_hook": "_create_joint_buying_partner_for_companies",
"installable": True,
diff --git a/joint_buying_product/demo/joint_buying_transport_request.xml b/joint_buying_base/demo/joint_buying_transport_request.xml
similarity index 87%
rename from joint_buying_product/demo/joint_buying_transport_request.xml
rename to joint_buying_base/demo/joint_buying_transport_request.xml
index f7940a0e..ac741e88 100644
--- a/joint_buying_product/demo/joint_buying_transport_request.xml
+++ b/joint_buying_base/demo/joint_buying_transport_request.xml
@@ -42,4 +42,14 @@
33
+
+
+
+
diff --git a/joint_buying_base/demo/res_partner.xml b/joint_buying_base/demo/res_partner.xml
index 0fc7b424..2efb0752 100644
--- a/joint_buying_base/demo/res_partner.xml
+++ b/joint_buying_base/demo/res_partner.xml
@@ -7,7 +7,7 @@
- Joint Buying Supplier for Your Company / Demo User
+ Joint Buying Supplier for Your Company
street
street 2
City
diff --git a/joint_buying_base/models/__init__.py b/joint_buying_base/models/__init__.py
index d5c93963..7f073775 100644
--- a/joint_buying_base/models/__init__.py
+++ b/joint_buying_base/models/__init__.py
@@ -10,3 +10,5 @@
from . import joint_buying_tour_type
from . import joint_buying_tour
from . import joint_buying_tour_line
+from . import joint_buying_transport_request
+from . import joint_buying_transport_request_line
diff --git a/joint_buying_base/models/joint_buying_tour_line.py b/joint_buying_base/models/joint_buying_tour_line.py
index 5905457f..0445fe31 100644
--- a/joint_buying_base/models/joint_buying_tour_line.py
+++ b/joint_buying_base/models/joint_buying_tour_line.py
@@ -7,6 +7,8 @@
from odoo import _, api, fields, models
from odoo.exceptions import UserError
+from odoo.addons import decimal_precision as dp
+
from .res_partner import _JOINT_BUYING_PARTNER_CONTEXT
_TOUR_LINE_SEQUENCE_TYPES = [
@@ -64,6 +66,17 @@ class JointBuyingTourLine(models.Model):
compute="_compute_costs", store=True, currency_field="currency_id"
)
+ transport_request_line_ids = fields.One2many(
+ comodel_name="joint.buying.transport.request.line",
+ string="Transport Lines",
+ inverse_name="tour_line_id",
+ )
+
+ load = fields.Float(
+ compute="_compute_load",
+ digits=dp.get_precision("Stock Weight"),
+ )
+
@api.depends("start_date", "arrival_date")
def _compute_hours(self):
for line in self:
@@ -86,6 +99,12 @@ def _compute_costs(self):
line.salary_cost = line.duration * line.tour_id.hourly_cost
line.vehicle_cost = +line.distance * line.tour_id.kilometer_cost
+ def _compute_load(self):
+ for tour_line in self:
+ tour_line.load = sum(
+ tour_line.mapped("transport_request_line_ids.request_id.total_weight")
+ )
+
def _estimate_route_project_osrm(self):
self.ensure_one()
url = (
diff --git a/joint_buying_base/models/joint_buying_transport_request.py b/joint_buying_base/models/joint_buying_transport_request.py
new file mode 100644
index 00000000..b4603606
--- /dev/null
+++ b/joint_buying_base/models/joint_buying_transport_request.py
@@ -0,0 +1,202 @@
+# Copyright (C) 2023-Today: GRAP (http://www.grap.coop)
+# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from odoo import api, fields, models
+
+from odoo.addons import decimal_precision as dp
+
+from .res_partner import _JOINT_BUYING_PARTNER_CONTEXT
+
+
+class JointBuyingTransportRequest(models.Model):
+ _name = "joint.buying.transport.request"
+ _description = "Joint Buying Transport Request"
+
+ name = fields.Char(readonly=True, compute="_compute_name", store=True)
+
+ state = fields.Selection(
+ selection=[
+ ("to_compute", "To Compute"),
+ ("computed", "Computed"),
+ ("not_computable", "Not Computable"),
+ ],
+ required=True,
+ readonly=True,
+ default="to_compute",
+ )
+
+ manual_start_date = fields.Datetime(
+ string="Start Date (Manual)",
+ )
+
+ start_date = fields.Datetime(
+ string="Start Date",
+ compute="_compute_start_date",
+ store=True,
+ )
+
+ manual_origin_partner_id = fields.Many2one(
+ comodel_name="res.partner",
+ string="Origin (Manual)",
+ context=_JOINT_BUYING_PARTNER_CONTEXT,
+ domain="[('is_joint_buying_stage', '=', True)]",
+ )
+
+ origin_partner_id = fields.Many2one(
+ comodel_name="res.partner",
+ compute="_compute_origin_partner_id",
+ string="Origin",
+ store=True,
+ context=_JOINT_BUYING_PARTNER_CONTEXT,
+ )
+
+ manual_destination_partner_id = fields.Many2one(
+ comodel_name="res.partner",
+ string="Destination (Manual)",
+ context=_JOINT_BUYING_PARTNER_CONTEXT,
+ domain="[('is_joint_buying_stage', '=', True)]",
+ )
+
+ destination_partner_id = fields.Many2one(
+ comodel_name="res.partner",
+ compute="_compute_destination_partner_id",
+ string="Destination",
+ store=True,
+ context=_JOINT_BUYING_PARTNER_CONTEXT,
+ )
+
+ manual_amount_untaxed = fields.Float(
+ string="Untaxed Amount (Manual)",
+ digits=dp.get_precision("Product Price"),
+ )
+
+ amount_untaxed = fields.Float(
+ string="Untaxed Amount",
+ compute="_compute_amount_untaxed",
+ store=True,
+ digits=dp.get_precision("Product Price"),
+ )
+
+ manual_total_weight = fields.Float(
+ string="Weight (Manual)",
+ digits=dp.get_precision("Stock Weight"),
+ )
+
+ total_weight = fields.Float(
+ string="Weight",
+ compute="_compute_weight",
+ store=True,
+ digits=dp.get_precision("Stock Weight"),
+ )
+
+ line_ids = fields.One2many(
+ comodel_name="joint.buying.transport.request.line",
+ string="Transport Lines",
+ inverse_name="request_id",
+ )
+
+ arrival_date = fields.Datetime(string="Arrival Date", readonly=True)
+
+ manual_description = fields.Html()
+
+ def _get_depends_start_date(self):
+ return ["manual_start_date"]
+
+ def _get_depends_origin_partner_id(self):
+ return ["manual_origin_partner_id"]
+
+ def _get_depends_destination_partner_id(self):
+ return ["manual_destination_partner_id"]
+
+ def _get_depends_amount_untaxed(self):
+ return ["manual_amount_untaxed"]
+
+ def _get_depends_total_weight(self):
+ return ["manual_total_weight"]
+
+ @api.depends("origin_partner_id", "destination_partner_id", "start_date")
+ def _compute_name(self):
+ for request in self:
+ request.name = (
+ f"{request.origin_partner_id.joint_buying_code}"
+ f" -> {request.destination_partner_id.joint_buying_code}"
+ f" ({request.start_date})"
+ )
+
+ @api.depends(lambda x: x._get_depends_start_date())
+ def _compute_start_date(self):
+ for request in self:
+ request.start_date = request.manual_start_date
+
+ @api.depends(lambda x: x._get_depends_origin_partner_id())
+ def _compute_origin_partner_id(self):
+ for request in self:
+ request.origin_partner_id = request.manual_origin_partner_id
+
+ @api.depends(lambda x: x._get_depends_destination_partner_id())
+ def _compute_destination_partner_id(self):
+ for request in self:
+ request.destination_partner_id = request.manual_destination_partner_id
+
+ @api.depends(lambda x: x._get_depends_amount_untaxed())
+ def _compute_amount_untaxed(self):
+ for request in self:
+ request.amount_untaxed = request.manual_amount_untaxed
+
+ @api.depends(lambda x: x._get_depends_total_weight())
+ def _compute_weight(self):
+ for request in self:
+ request.total_weight = request.manual_total_weight
+
+ def _set_tour_lines(self, tour_lines):
+ self.ensure_one()
+ # If lines are valid
+ if (
+ tour_lines
+ and tour_lines[-1].arrival_point_id == self.destination_partner_id
+ ):
+ line_vals = []
+ previous_tour_id = False
+ for i, tour_line in enumerate(tour_lines):
+ # Compute if it is a loading at the beginning of the tour line
+ if previous_tour_id != tour_line.tour_id.id:
+ start_action_type = "loading"
+ else:
+ start_action_type = "no"
+ previous_tour_id = tour_line.tour_id.id
+
+ # Compute if it is an unloading at the end of the tour line
+ if i < len(tour_lines) - 1:
+ if tour_line.tour_id != tour_lines[i + 1].tour_id:
+ arrival_action_type = "unloading"
+ else:
+ arrival_action_type = "no"
+ else:
+ arrival_action_type = "unloading"
+ line_vals.append(
+ {
+ "start_action_type": start_action_type,
+ "arrival_action_type": arrival_action_type,
+ "tour_line_id": tour_line.id,
+ }
+ )
+
+ vals = {
+ "arrival_date": max(tour_lines.mapped("arrival_date")),
+ "state": "computed",
+ "line_ids": [(5,)] + [(0, 0, x) for x in line_vals],
+ }
+ else:
+ vals = {
+ "arrival_date": False,
+ "state": "not_computable",
+ "line_ids": [(5,)],
+ }
+ self.write(vals)
+
+ def button_compute_tour(self):
+ Wizard = self.env["joint.buying.wizard.find.route"]
+ results = Wizard.compute_tours(self)
+ for request in self:
+ request._set_tour_lines(results[request][1])
diff --git a/joint_buying_product/models/joint_buying_transport_request_line.py b/joint_buying_base/models/joint_buying_transport_request_line.py
similarity index 95%
rename from joint_buying_product/models/joint_buying_transport_request_line.py
rename to joint_buying_base/models/joint_buying_transport_request_line.py
index 257a9288..021c788c 100644
--- a/joint_buying_product/models/joint_buying_transport_request_line.py
+++ b/joint_buying_base/models/joint_buying_transport_request_line.py
@@ -4,9 +4,7 @@
from odoo import fields, models
-from odoo.addons.joint_buying_base.models.res_partner import (
- _JOINT_BUYING_PARTNER_CONTEXT,
-)
+from .res_partner import _JOINT_BUYING_PARTNER_CONTEXT
class JointBuyingTransportRequestLine(models.Model):
diff --git a/joint_buying_base/reports/report.xml b/joint_buying_base/reports/report.xml
new file mode 100644
index 00000000..13c8e173
--- /dev/null
+++ b/joint_buying_base/reports/report.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/joint_buying_product/reports/report_joint_buying_tour.xml b/joint_buying_base/reports/report_joint_buying_tour.xml
similarity index 95%
rename from joint_buying_product/reports/report_joint_buying_tour.xml
rename to joint_buying_base/reports/report_joint_buying_tour.xml
index 9bdcc403..d7915e2f 100644
--- a/joint_buying_product/reports/report_joint_buying_tour.xml
+++ b/joint_buying_base/reports/report_joint_buying_tour.xml
@@ -154,8 +154,8 @@
Loading |
-
-
+
@@ -176,8 +176,8 @@
Unloading |
-
-
+
@@ -189,7 +189,7 @@
-
+
diff --git a/joint_buying_base/security/ir.model.access.csv b/joint_buying_base/security/ir.model.access.csv
index a532d77e..700dcf90 100644
--- a/joint_buying_base/security/ir.model.access.csv
+++ b/joint_buying_base/security/ir.model.access.csv
@@ -6,6 +6,8 @@ access_joint_buying_tour_user,access_joint_buying_tour_user,model_joint_buying_t
access_joint_buying_tour_manager,access_joint_buying_tour_manager,model_joint_buying_tour,joint_buying_base.group_joint_buying_manager,1,1,1,1
access_joint_buying_tour_line_user,access_joint_buying_tour_line_user,model_joint_buying_tour_line,joint_buying_base.group_joint_buying_user,1,,,
access_joint_buying_tour_line_manager,access_joint_buying_tour_line_manager,model_joint_buying_tour_line,joint_buying_base.group_joint_buying_manager,1,1,1,1
+access_joint_buying_transport_request_user,access_joint_buying_transport_request_user,model_joint_buying_transport_request,joint_buying_base.group_joint_buying_user,1,1,1,1
+access_joint_buying_transport_request_line_user,access_joint_buying_transport_request_line_user,model_joint_buying_transport_request_line,joint_buying_base.group_joint_buying_user,1,1,1,1
access_joint_buying_carrier_user,access_joint_buying_carrier_user,model_joint_buying_carrier,joint_buying_base.group_joint_buying_user,1,,,
access_joint_buying_carrier_manager,access_joint_buying_carrier_manager,model_joint_buying_carrier,joint_buying_base.group_joint_buying_manager,1,1,1,1
access_joint_buying_tour_type_user,access_joint_buying_tour_type_user,model_joint_buying_tour_type,joint_buying_base.group_joint_buying_user,1,,,
diff --git a/joint_buying_base/tests/__init__.py b/joint_buying_base/tests/__init__.py
index 8407548c..bb2b96f6 100644
--- a/joint_buying_base/tests/__init__.py
+++ b/joint_buying_base/tests/__init__.py
@@ -4,4 +4,5 @@
from . import test_joint_buying_mixin
from . import test_partner_global_local
from . import test_partner_subscription
+from . import test_joint_buying_wizard_find_route
from . import test_tour
diff --git a/joint_buying_product/tests/test_joint_buying_wizard_find_route.py b/joint_buying_base/tests/test_joint_buying_wizard_find_route.py
similarity index 89%
rename from joint_buying_product/tests/test_joint_buying_wizard_find_route.py
rename to joint_buying_base/tests/test_joint_buying_wizard_find_route.py
index 01ceca4d..5f4be7e2 100644
--- a/joint_buying_product/tests/test_joint_buying_wizard_find_route.py
+++ b/joint_buying_base/tests/test_joint_buying_wizard_find_route.py
@@ -16,7 +16,7 @@ def setUp(self):
def test_20_transport_request_vev_cda_week_1(self):
"""simplest case: direct route"""
self._verify_tour_lines_computation(
- "joint_buying_product.request_vev_cda_week_1",
+ "joint_buying_base.request_vev_cda_week_1",
["joint_buying_base.tour_lyon_loire_1_line_4"],
"computed",
)
@@ -24,7 +24,7 @@ def test_20_transport_request_vev_cda_week_1(self):
def test_21_transport_request_vev_che_week_1(self):
"""Classic case: 1 change"""
self._verify_tour_lines_computation(
- "joint_buying_product.request_vev_che_week_1",
+ "joint_buying_base.request_vev_che_week_1",
[
"joint_buying_base.tour_lyon_loire_1_line_4",
"joint_buying_base.tour_lyon_loire_1_line_6",
@@ -37,7 +37,7 @@ def test_21_transport_request_vev_che_week_1(self):
def test_22_transport_request_vev_edc_1(self):
"""Classic case: 2 change"""
self._verify_tour_lines_computation(
- "joint_buying_product.request_vev_edc_week_1",
+ "joint_buying_base.request_vev_edc_week_1",
[
"joint_buying_base.tour_lyon_loire_1_line_4",
"joint_buying_base.tour_lyon_loire_1_line_6",
@@ -50,13 +50,13 @@ def test_22_transport_request_vev_edc_1(self):
def test_23_transport_request_vev_fumet_week_1(self):
"""Use case: No route available"""
self._verify_tour_lines_computation(
- "joint_buying_product.request_vev_fumet_dombes_week_1", [], "not_computable"
+ "joint_buying_base.request_vev_fumet_dombes_week_1", [], "not_computable"
)
def test_24_transport_request_vev_che_week_2(self):
"""Complex case: a later start arrives earlier"""
self._verify_tour_lines_computation(
- "joint_buying_product.request_vev_che_week_2",
+ "joint_buying_base.request_vev_che_week_2",
[
"joint_buying_base.tour_lyon_loire_3_line_2",
"joint_buying_base.tour_lyon_drome_2_line_2",
diff --git a/joint_buying_base/tests/test_tour.py b/joint_buying_base/tests/test_tour.py
index 11f499fd..e1e4da62 100644
--- a/joint_buying_base/tests/test_tour.py
+++ b/joint_buying_base/tests/test_tour.py
@@ -20,6 +20,9 @@ def setUp(self):
"joint_buying_base.company_3PP"
).joint_buying_partner_id
self.tour_lyon_savoie = self.env.ref("joint_buying_base.tour_lyon_savoie_1")
+ self.tour_report = self.env.ref(
+ "joint_buying_base.action_report_joint_buying_tour"
+ )
# Test Section
def _assert_not_very_differrent(self, value_1, value_2):
@@ -99,3 +102,8 @@ def test_104_check_cost_chart(self):
self.assertIn("Salary", self.tour_lyon_savoie.cost_chart)
self.assertIn("Vehicle", self.tour_lyon_savoie.cost_chart)
self.assertIn("Toll", self.tour_lyon_savoie.cost_chart)
+
+ def test_110_report(self):
+ # Generate report to make sure the syntax is correct
+ tours = self.JointBuyingTour.search([])
+ self.tour_report.render_qweb_html(tours.ids)
diff --git a/joint_buying_base/views/menu.xml b/joint_buying_base/views/menu.xml
index afea641d..39d200b2 100644
--- a/joint_buying_base/views/menu.xml
+++ b/joint_buying_base/views/menu.xml
@@ -12,6 +12,12 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
groups="group_joint_buying_user"
/>
+
+
diff --git a/joint_buying_product/security/ir.model.access.csv b/joint_buying_product/security/ir.model.access.csv
index 71cafb88..1d8d247c 100644
--- a/joint_buying_product/security/ir.model.access.csv
+++ b/joint_buying_product/security/ir.model.access.csv
@@ -4,8 +4,6 @@ access_joint_buying_purchase_order,access_joint_buying_purchase_order,model_join
access_joint_buying_purchase_order_grouped,access_joint_buying_purchase_order_grouped,model_joint_buying_purchase_order_grouped,joint_buying_base.group_joint_buying_user,1,1,1,
access_joint_buying_purchase_order_grouped_manager,access_joint_buying_purchase_order_grouped_manager,model_joint_buying_purchase_order_grouped,joint_buying_base.group_joint_buying_manager,1,1,1,1
access_joint_buying_frequency_user,access_joint_buying_frequency_user,model_joint_buying_frequency,joint_buying_base.group_joint_buying_user,1,1,1,1
-access_joint_buying_transport_request_user,access_joint_buying_transport_request_user,model_joint_buying_transport_request,joint_buying_base.group_joint_buying_user,1,1,1,1
-access_joint_buying_transport_request_line_user,access_joint_buying_transport_request_line_user,model_joint_buying_transport_request_line,joint_buying_base.group_joint_buying_user,1,1,1,1
access_joint_buying_category_user,access_joint_buying_category_user,model_joint_buying_category,joint_buying_base.group_joint_buying_user,1,1,1,1
access_product_product,access_product_product,product.model_product_product,joint_buying_base.group_joint_buying_user,1,1,1,1
access_product_template,access_product_template,product.model_product_template,joint_buying_base.group_joint_buying_user,1,1,1,1
diff --git a/joint_buying_product/tests/__init__.py b/joint_buying_product/tests/__init__.py
index b754c1df..9ee38a16 100644
--- a/joint_buying_product/tests/__init__.py
+++ b/joint_buying_product/tests/__init__.py
@@ -1,7 +1,6 @@
from . import test_abstract
from . import test_joint_buying_purchase_order
from . import test_joint_buying_transport_request
-from . import test_joint_buying_wizard_find_route
from . import test_product
from . import test_check_access_product
from . import test_check_access_joint_buying_purchase
diff --git a/joint_buying_product/uninstall_hook.py b/joint_buying_product/uninstall_hook.py
new file mode 100644
index 00000000..e4299999
--- /dev/null
+++ b/joint_buying_product/uninstall_hook.py
@@ -0,0 +1,13 @@
+# Copyright (C) 2023-Today: GRAP (http://www.grap.coop)
+# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+
+def uninstall_product_db(cr):
+
+ cr.execute(
+ """
+ ALTER TABLE product_template
+ DROP COLUMN is_joint_buying;
+ """
+ )
diff --git a/joint_buying_product/views/menu.xml b/joint_buying_product/views/menu.xml
index 4d2f7af4..134653c0 100644
--- a/joint_buying_product/views/menu.xml
+++ b/joint_buying_product/views/menu.xml
@@ -12,11 +12,4 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
sequence="1"
/>
-
-
-
diff --git a/joint_buying_product/wizards/__init__.py b/joint_buying_product/wizards/__init__.py
index d8d007c0..76df2dde 100644
--- a/joint_buying_product/wizards/__init__.py
+++ b/joint_buying_product/wizards/__init__.py
@@ -1,6 +1,5 @@
from . import joint_buying_wizard_create_order
from . import joint_buying_wizard_create_order_line
-from . import joint_buying_wizard_find_route
from . import joint_buying_wizard_update_order_grouped
from . import mail_compose_message_purchase_order_grouped