Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions helpdesk_mgmt_timesheet_sale/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
============================
Helpdesk Mgmt Timesheet Sale
============================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:8416332eefb96b81e4ba314174f41d33dea4b925f48552cef6a7e0b5a2cfff8e
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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%2Fhelpdesk-lightgray.png?logo=github
:target: https://github.com/OCA/helpdesk/tree/18.0/helpdesk_mgmt_timesheet_sale
:alt: OCA/helpdesk
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/helpdesk-18-0/helpdesk-18-0-helpdesk_mgmt_timesheet_sale
: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/helpdesk&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Allow to set the sale order line directly on helpdesk tickets.

**Table of contents**

.. contents::
:local:

Use Cases / Context
===================

Without this module, the only way to invoice a helpdesk ticket is
throught a project task. It is an option, but it can be confusing if you
are using task tickets and you want to invoice some time to be consumed
on tickets.

This module fills this gap.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/helpdesk/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 <https://github.com/OCA/helpdesk/issues/new?body=module:%20helpdesk_mgmt_timesheet_sale%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
-------

* Dixmit

Contributors
------------

- `Dixmit <http://www.dixmit.com>`__

- Enric Tobella

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/helpdesk <https://github.com/OCA/helpdesk/tree/18.0/helpdesk_mgmt_timesheet_sale>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions helpdesk_mgmt_timesheet_sale/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
19 changes: 19 additions & 0 deletions helpdesk_mgmt_timesheet_sale/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Helpdesk Mgmt Timesheet Sale",
"summary": """Allow to set the sale order line directly on helpdesk tickets""",
"version": "18.0.1.0.0",
"license": "AGPL-3",
"author": "Dixmit,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/helpdesk",
"depends": [
"helpdesk_mgmt_timesheet",
"sale_timesheet",
],
"data": [
"views/helpdesk_ticket.xml",
],
"demo": [],
}
2 changes: 2 additions & 0 deletions helpdesk_mgmt_timesheet_sale/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import helpdesk_ticket
from . import account_analytic_line
13 changes: 13 additions & 0 deletions helpdesk_mgmt_timesheet_sale/models/account_analytic_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models


class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"

def _timesheet_determine_sale_line(self):
if self.ticket_id:
return self.ticket_id.sale_line_id
return super()._timesheet_determine_sale_line()
111 changes: 111 additions & 0 deletions helpdesk_mgmt_timesheet_sale/models/helpdesk_ticket.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo.osv import expression
from odoo.tools.misc import unquote


class HelpdeskTicket(models.Model):
_inherit = "helpdesk.ticket"

def _domain_sale_line_id(self):
domain = expression.AND(
[
self.env["sale.order.line"]._sellable_lines_domain(),
self.env["sale.order.line"]._domain_sale_line_service(),
[
"|",
(
"order_partner_id.commercial_partner_id.id",
"parent_of",
unquote("partner_id if partner_id else []"),
),
("order_partner_id", "=?", unquote("partner_id")),
],
]
)
return domain

sale_order_id = fields.Many2one(
"sale.order",
"Sales Order",
compute="_compute_sale_order_id",
store=True,
help="Sales order to which the task is linked.",
group_expand="_group_expand_sales_order",
)
sale_line_id = fields.Many2one(
"sale.order.line",
"Sales Order Item",
copy=True,
tracking=True,
index="btree_not_null",
recursive=True,
compute="_compute_sale_line",
store=True,
readonly=False,
domain=lambda self: str(self._domain_sale_line_id()),
help="Sales Order Item to which the time spent on this task will "
"be added in order to be invoiced to your customer.\n"
"By default the sales order item set on the project will be selected. "
"In the absence of one, the last prepaid sales order item that has "
"time remaining will be used.\n"
"Remove the sales order item in order to make this task non billable. "
"You can also change or remove the sales order item of each timesheet "
"entry individually.",
)
allow_billable = fields.Boolean(related="project_id.allow_billable")

@api.depends(
"sale_line_id.order_partner_id",
"task_id.sale_line_id",
"project_id.sale_line_id",
"milestone_id.sale_line_id",
"allow_billable",
)
def _compute_sale_line(self):
for ticket in self:
if not (ticket.allow_billable or ticket.task_id.allow_billable):
ticket.sale_line_id = False
continue
if not ticket.sale_line_id:
sale_line = False
if (
ticket.task_id.sale_line_id
and ticket.task_id.partner_id.commercial_partner_id
== ticket.partner_id.commercial_partner_id
):
sale_line = ticket.task_id.sale_line_id
elif ticket.milestone_id.sale_line_id:
sale_line = ticket.milestone_id.sale_line_id
elif (
ticket.project_id.sale_line_id
and ticket.project_id.partner_id.commercial_partner_id
== ticket.partner_id.commercial_partner_id
):
sale_line = ticket.project_id.sale_line_id
ticket.sale_line_id = sale_line

@api.depends("sale_line_id", "project_id", "allow_billable")
def _compute_sale_order_id(self):
for ticket in self:
if not ticket.allow_billable:
ticket.sale_order_id = False
continue
sale_order = (
ticket.sale_line_id.order_id
or ticket.project_id.sale_order_id
or ticket.sale_order_id
)
if sale_order and not ticket.partner_id:
ticket.partner_id = sale_order.partner_id
consistent_partners = (
sale_order.partner_id
| sale_order.partner_invoice_id
| sale_order.partner_shipping_id
).commercial_partner_id
if ticket.partner_id.commercial_partner_id in consistent_partners:
ticket.sale_order_id = sale_order
else:
ticket.sale_order_id = False
3 changes: 3 additions & 0 deletions helpdesk_mgmt_timesheet_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"
4 changes: 4 additions & 0 deletions helpdesk_mgmt_timesheet_sale/readme/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Without this module, the only way to invoice a helpdesk ticket is throught a project task.
It is an option, but it can be confusing if you are using task tickets and you want to invoice some time to be consumed on tickets.

This module fills this gap.
2 changes: 2 additions & 0 deletions helpdesk_mgmt_timesheet_sale/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [Dixmit](http://www.dixmit.com)
- Enric Tobella
1 change: 1 addition & 0 deletions helpdesk_mgmt_timesheet_sale/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow to set the sale order line directly on helpdesk tickets.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading