Skip to content

Commit

Permalink
[ADD] tms_sale
Browse files Browse the repository at this point in the history
  • Loading branch information
santiagordz committed Aug 28, 2024
1 parent 9ff4b7d commit 4bc5639
Show file tree
Hide file tree
Showing 17 changed files with 675 additions and 0 deletions.
35 changes: 35 additions & 0 deletions tms_sale/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
**This file is going to be generated by oca-gen-addon-readme.**

*Manual changes will be overwritten.*

Please provide content in the ``readme`` directory:

* **DESCRIPTION.rst** (required)
* INSTALL.rst (optional)
* CONFIGURE.rst (optional)
* **USAGE.rst** (optional, highly recommended)
* DEVELOP.rst (optional)
* ROADMAP.rst (optional)
* HISTORY.rst (optional, recommended)
* **CONTRIBUTORS.rst** (optional, highly recommended)
* CREDITS.rst (optional)

Content of this README will also be drawn from the addon manifest,
from keys such as name, authors, maintainers, development_status,
and license.

A good, one sentence summary in the manifest is also highly recommended.


Automatic changelog generation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`HISTORY.rst` can be auto generated using `towncrier <https://pypi.org/project/towncrier>`_.

Just put towncrier compatible changelog fragments into `readme/newsfragments`
and the changelog file will be automatically generated and updated when a new fragment is added.

Please refer to `towncrier` documentation to know more.

NOTE: the changelog will be automatically generated when using `/ocabot merge $option`.
If you need to run it manually, refer to `OCA/maintainer-tools README <https://github.com/OCA/maintainer-tools>`_.
2 changes: 2 additions & 0 deletions tms_sale/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
21 changes: 21 additions & 0 deletions tms_sale/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright (C) 2018 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "TMS - Sales",
"version": "17.0.1.0.0",
"summary": "Sell transportation management system.",
"category": "TMS",
"author": "Open Source Integrators, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-transport",
"depends": ["tms", "tms_product", "sale_management"],
"data": [
"security/ir.model.access.csv",
"views/sale_order_views.xml",
"views/tms_order_views.xml",
"views/product_template_views.xml",
],
"license": "AGPL-3",
"development_status": "Alpha",
"maintainers": ["max3903", "santiagordz", "EdgarRetes"],
"installable": True,
}
3 changes: 3 additions & 0 deletions tms_sale/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import sale_order_line
from . import sale_order
from . import tms_order
212 changes: 212 additions & 0 deletions tms_sale/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# Copyright (C) 2019 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from markupsafe import Markup

from odoo import _, api, fields, models


class SaleOrder(models.Model):
_inherit = "sale.order"

tms_order_ids = fields.Many2many(
"tms.order",
compute="_compute_tms_order_ids",
string="Transport orders associated to this sale",
copy=False,
)
tms_order_count = fields.Integer(
string="Transport Orders", compute="_compute_tms_order_ids"
)

has_tms_order = fields.Boolean(compute="_compute_has_tms_order")

tms_route_flag = fields.Boolean(string="Use Routes")
tms_route_id = fields.Many2one("tms.route", string="Routes")
tms_origin_id = fields.Many2one(
"res.partner",
string="Origin",
domain="[('tms_type', '=', 'location')]",
context={"default_tms_type": "location"},
)
tms_destination_id = fields.Many2one(
"res.partner",
string="Destination",
domain="[('tms_type', '=', 'location')]",
context={"default_tms_type": "location"},
)

tms_scheduled_date_start = fields.Datetime(string="Scheduled Start")
tms_scheduled_date_end = fields.Datetime(string="Scheduled End")

@api.onchange("order_line")
def _onchange_tms_product_uom(self):
for line in self.order_line:
if line.product_template_id.tms_factor_distance_uom:
line.tms_factor_uom = (
line.product_template_id.tms_factor_distance_uom.name
)
elif line.product_template_id.tms_factor_weight_uom:
line.tms_factor_uom = (
line.product_template_id.tms_factor_weight_uom.name
)
else:
line.tms_factor_uom = False

@api.depends("order_line")
def _compute_has_tms_order(self):
for sale in self:
has_tms_order = any(
line.product_template_id.tms_trip
and line.product_template_id.trip_product_type == "trip"
for line in sale.order_line
)
sale.has_tms_order = has_tms_order

@api.depends("order_line")
def _compute_tms_order_ids(self):
for sale in self:
tms = self.env["tms.order"].search(
[
"|",
("sale_id", "=", sale.id),
("sale_line_id", "in", sale.order_line.ids),
]
)
sale.tms_order_ids = tms
sale.tms_order_count = len(sale.tms_order_ids)

def _prepare_line_tms_values(self, line):
"""
Prepare the values to create a new TMS Order from a sale order line.
"""
self.ensure_one()
vals = self._prepare_tms_values(so_id=self.id, sol_id=line.id)
return vals

def _prepare_tms_values(self, **kwargs):
"""
Prepare the values to create a new TMS Order from a sale order.
"""
self.ensure_one()
duration = self.tms_scheduled_date_end - self.tms_scheduled_date_start
duration_in_s = duration.total_seconds()
hours = duration_in_s / 3600
return {
"sale_id": kwargs.get("so_id", False),
"sale_line_id": kwargs.get("sol_id", False),
"company_id": self.company_id.id,
"route": self.tms_route_flag,
"route_id": self.tms_route_id.id or None,
"origin_id": self.tms_origin_id.id or None,
"destination_id": self.tms_destination_id.id or None,
"scheduled_date_start": self.tms_scheduled_date_start or None,
"scheduled_date_end": self.tms_scheduled_date_end or None,
"scheduled_duration": hours,
}

def _tms_generate_line_tms_orders(self, new_tms_sol):
"""
Generate TMS Orders for the given sale order lines.
Override this method to filter lines to generate TMS Orders for.
"""
self.ensure_one()
new_tms_orders = self.env["tms.order"]

for line in new_tms_sol:
for qty in range(int(line.product_uom_qty) - len(line.tms_order_ids)):
vals = self._prepare_line_tms_values(line)
tms_by_line = self.env["tms.order"].sudo().create(vals)
line.write({"tms_order_ids": [(4, tms_by_line.id)]})
new_tms_orders |= tms_by_line

return new_tms_orders

def _tms_generate(self):
self.ensure_one()
new_tms_orders = self.env["tms.order"]

new_tms_line_sol = self.order_line.filtered(
lambda L: L.product_id.trip_product_type == "trip"
and len(L.tms_order_ids) != L.product_uom_qty
and len(L.tms_order_ids) < L.product_uom_qty
)

new_tms_orders |= self._tms_generate_line_tms_orders(new_tms_line_sol)

return new_tms_orders

def _tms_generation(self):
"""
Create TMS Orders based on the products' configuration.
:rtype: list(TMS Orders)
:return: list of newly created TMS Orders
"""
created_tms_orders = self.env["tms.order"]

for sale in self:
new_tms_orders = self._tms_generate()

if len(new_tms_orders) > 0:
created_tms_orders |= new_tms_orders
sale._post_tms_message(new_tms_orders)

return created_tms_orders

def _post_tms_message(self, tms_orders):
"""
Post messages to the Sale Order and the newly created TMS Orders
"""
self.ensure_one()
for tms_order in tms_orders:
tms_order.message_mail_with_source(
"mail.message_origin_link",
render_values={"self": tms_order, "origin": self},
subtype_id=self.env.ref("mail.mt_note").id,
author_id=self.env.user.partner_id.id,
)
message = _(
"Transport Order(s) Created: %s",
Markup(
f"""<a href=# data-oe-model=tms.order data-oe-id={tms_order.id}"""
f""">{tms_order.name}</a>"""
),
)
self.message_post(body=message)

def _action_create_new_trips(self):
if any(
sol.product_id.trip_product_type == "trip"
for sol in self.order_line.filtered(
lambda x: x.display_type not in ("line_section", "line_note")
)
):
self._tms_generation()

def action_view_tms_order(self):
tms_orders = self.mapped("tms_order_ids")
action = self.env["ir.actions.act_window"]._for_xml_id(
"tms.action_tms_dash_order"
)
if len(tms_orders) > 1:
action["domain"] = [("id", "in", tms_orders.ids)]
elif len(tms_orders) == 1:
action["views"] = [(self.env.ref("tms.tms_order_view_form").id, "form")]
action["res_id"] = tms_orders.id
else:
action = {"type": "ir.actions.act_window_close"}
return action

@api.model
def create(self, vals):
order = super().create(vals)
if "order_line" in vals and order.has_tms_order:
order._action_create_new_trips()
return order

@api.model
def write(self, vals):
order = super().write(vals)
if "order_line" in vals and self.has_tms_order:
self._action_create_new_trips()
return order
70 changes: 70 additions & 0 deletions tms_sale/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (C) 2019 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

tms_order_ids = fields.One2many(
"tms.order",
"sale_line_id",
index=True,
copy=False,
help="Transport Order generated by the sales order item",
)

tms_factor_uom = fields.Char()
tms_factor = fields.Float(default=1)

tms_route_id = fields.Many2one("tms.route", string="Routes")
tms_origin_id = fields.Many2one(
"res.partner",
string="Origin",
domain="[('tms_type', '=', 'location')]",
context={"default_tms_type": "location"},
)
tms_destination_id = fields.Many2one(
"res.partner",
string="Destination",
domain="[('tms_type', '=', 'location')]",
context={"default_tms_type": "location"},
)

tms_scheduled_date_start = fields.Datetime(string="Scheduled Start")
tms_scheduled_date_end = fields.Datetime(string="Scheduled End")

def action_view_trip_sale_order_line(self):
return {
"type": "ir.actions.act_window",
"res_model": "sale.order.line.trip",
"target": "new",
"view_mode": "form",
"view_type": "form",
"context": {"default_user_id": self.id},
}

def _convert_to_tax_base_line_dict(self, **kwargs):
"""Convert the current record to a dictionary in
order to use the generic taxes computation method
defined on account.tax.
:return: A python dictionary.
"""
self.ensure_one()
return self.env["account.tax"]._convert_to_tax_base_line_dict(
self,
partner=self.order_id.partner_id,
currency=self.order_id.currency_id,
product=self.product_id,
taxes=self.tax_id,
price_unit=self.price_unit,
quantity=self.product_uom_qty * self.tms_factor,
discount=self.discount,
price_subtotal=self.price_subtotal,
**kwargs,
)

@api.depends("product_uom_qty", "discount", "price_unit", "tax_id", "tms_factor")
def _compute_amount(self):
return super()._compute_amount()
32 changes: 32 additions & 0 deletions tms_sale/models/tms_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (C) 2019 Brian McMaster
# Copyright (C) 2019 Open Source Integrators
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models


class TMSOrder(models.Model):
_inherit = "tms.order"

sale_id = fields.Many2one("sale.order", copy=False)
sale_line_id = fields.Many2one("sale.order.line", copy=False)

def action_view_sales(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"res_model": "sale.order",
"views": [[False, "form"]],
"res_id": self.sale_line_id.order_id.id or self.sale_id.id,
"context": {"create": False},
"name": _("Sales Orders"),
}

@api.model
def write(self, vals):
for order in self:
if "stage_id" in vals:
stage = self.env.ref("tms.tms_stage_order_completed")
if vals["stage_id"] == stage.id:
for line in order.sale_id.order_line:
line.qty_delivered = line.product_uom_qty
return super().write(vals)
3 changes: 3 additions & 0 deletions tms_sale/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
2 changes: 2 additions & 0 deletions tms_sale/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_order_tms_user,sale.order.tms.user,model_sale_order,tms.group_tms_user,1,0,0,0
Binary file added tms_sale/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4bc5639

Please sign in to comment.