Initial commit: Odoo 18.0-20251222 extra-addons
This commit is contained in:
3
sale_order_invoicing_finished_task/models/__init__.py
Executable file
3
sale_order_invoicing_finished_task/models/__init__.py
Executable file
@@ -0,0 +1,3 @@
|
||||
from . import product
|
||||
from . import project
|
||||
from . import sale_order
|
||||
14
sale_order_invoicing_finished_task/models/product.py
Executable file
14
sale_order_invoicing_finished_task/models/product.py
Executable file
@@ -0,0 +1,14 @@
|
||||
# Copyright 2017 Tecnativa - Sergio Teruel
|
||||
# Copyright 2017 Tecnativa - Carlos Dauden
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = "product.template"
|
||||
|
||||
invoicing_finished_task = fields.Boolean(
|
||||
string="Invoicing control by task",
|
||||
help="Invoice the order lines only when the task is set to invoiceable",
|
||||
)
|
||||
67
sale_order_invoicing_finished_task/models/project.py
Executable file
67
sale_order_invoicing_finished_task/models/project.py
Executable file
@@ -0,0 +1,67 @@
|
||||
# Copyright 2017 Camptocamp SA
|
||||
# Copyright 2017 Tecnativa - Sergio Teruel
|
||||
# Copyright 2017 Tecnativa - Carlos Dauden
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ProjectTaskType(models.Model):
|
||||
_inherit = "project.task.type"
|
||||
|
||||
invoiceable = fields.Boolean()
|
||||
|
||||
|
||||
class ProjectTask(models.Model):
|
||||
_inherit = "project.task"
|
||||
|
||||
invoiceable = fields.Boolean()
|
||||
invoicing_finished_task = fields.Boolean(
|
||||
related="sale_line_id.product_id.invoicing_finished_task",
|
||||
)
|
||||
|
||||
@api.onchange("stage_id")
|
||||
def _onchange_stage_id(self):
|
||||
tasks = self.filtered(
|
||||
lambda t: (
|
||||
t.invoicing_finished_task
|
||||
and t.stage_id.invoiceable
|
||||
and not t.invoiceable
|
||||
)
|
||||
)
|
||||
tasks.toggle_invoiceable()
|
||||
|
||||
def toggle_invoiceable(self):
|
||||
self._check_sale_line_state()
|
||||
for task in self:
|
||||
task.invoiceable = not task.invoiceable
|
||||
|
||||
def write(self, vals):
|
||||
if "sale_line_id" in vals:
|
||||
self._check_sale_line_state(vals["sale_line_id"])
|
||||
return super().write(vals)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get("sale_line_id"):
|
||||
self._check_sale_line_state(vals["sale_line_id"])
|
||||
return super().create(vals_list)
|
||||
|
||||
def _check_sale_line_state(self, sale_line_id=False):
|
||||
sale_lines = self.mapped("sale_line_id")
|
||||
if sale_line_id:
|
||||
sale_lines |= self.env["sale.order.line"].browse(sale_line_id)
|
||||
for sale_line in sale_lines:
|
||||
if (
|
||||
sale_line.state == "cancel"
|
||||
or sale_line.invoice_status == "invoiced"
|
||||
or sale_line.order_id.locked
|
||||
):
|
||||
raise ValidationError(
|
||||
self.env._(
|
||||
"You cannot create/modify a task related with a "
|
||||
"invoiced, locked or cancel sale order line "
|
||||
)
|
||||
)
|
||||
67
sale_order_invoicing_finished_task/models/sale_order.py
Executable file
67
sale_order_invoicing_finished_task/models/sale_order.py
Executable file
@@ -0,0 +1,67 @@
|
||||
# Copyright 2017 Tecnativa - Sergio Teruel
|
||||
# Copyright 2017 Tecnativa - Carlos Dauden
|
||||
# Copyright 2025 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
|
||||
task_ids = fields.One2many(
|
||||
comodel_name="project.task",
|
||||
inverse_name="sale_line_id",
|
||||
string="Tasks",
|
||||
)
|
||||
|
||||
@api.depends("task_ids.invoiceable")
|
||||
def _compute_invoice_status(self):
|
||||
res = super()._compute_invoice_status()
|
||||
for item in self.filtered(lambda x: x.task_ids and x.invoice_status != "no"):
|
||||
if not all(
|
||||
t.invoiceable for t in item.task_ids if t.invoicing_finished_task
|
||||
):
|
||||
item.invoice_status = "no"
|
||||
return res
|
||||
|
||||
@api.depends("task_ids.invoiceable")
|
||||
def _compute_qty_to_invoice(self):
|
||||
lines = self.filtered(
|
||||
lambda x: (
|
||||
x.product_id.type == "service"
|
||||
and x.product_id.invoicing_finished_task
|
||||
and x.product_id.service_tracking
|
||||
in ["task_global_project", "task_in_project"]
|
||||
and not all(x.task_ids.mapped("invoiceable"))
|
||||
)
|
||||
)
|
||||
if lines:
|
||||
lines.update({"qty_to_invoice": 0.0})
|
||||
return super(SaleOrderLine, self - lines)._compute_qty_to_invoice()
|
||||
|
||||
def _timesheet_compute_delivered_quantity_domain(self):
|
||||
vals = super()._timesheet_compute_delivered_quantity_domain()
|
||||
vals = (
|
||||
["|", ("amount", "<=", 0.0)]
|
||||
+ vals
|
||||
+ [
|
||||
# don't update the qty on sale order lines which are not
|
||||
# with a product invoiced on ordered qty +
|
||||
# invoice_finished task = True
|
||||
"|",
|
||||
("so_line.product_id.invoice_policy", "=", "delivery"),
|
||||
("so_line.product_id.invoicing_finished_task", "=", False),
|
||||
]
|
||||
)
|
||||
return vals
|
||||
|
||||
@api.depends(
|
||||
"qty_delivered_method",
|
||||
"analytic_line_ids.so_line",
|
||||
"analytic_line_ids.unit_amount",
|
||||
"analytic_line_ids.product_uom_id",
|
||||
"task_ids.invoiceable",
|
||||
)
|
||||
def _compute_qty_delivered(self):
|
||||
return super()._compute_qty_delivered()
|
||||
Reference in New Issue
Block a user