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
126 changes: 126 additions & 0 deletions project_state_extend/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
====================
Project State Extend
====================

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

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

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

This module extends Odoo's project state selection widget to allow adding extended state options beyond the standard ones (On Track, At Risk, Off Track, On Hold).

Custom states can be configured with:

* Display name (translatable)
* Technical name (unique identifier)
* Color (0-11 for status bubble display)
* Sequence (ordering in selection lists)

Custom states are available for both Projects through the same state selection widget used by native Odoo.

**Table of contents**

.. contents::
:local:

Configuration
=============

To configure custom project states:

#. Go to **Project > Configuration > Extended States**
#. Click **Create**
#. Fill in the form:

* **Name**: Display name for the state (e.g., "Pending Review")
* **Technical Name**: Unique identifier (lowercase, no spaces, e.g., "pending_review")
* **Color**: Select a color index from 0 to 11 for the status bubble
* **Sequence**: Lower numbers appear first in selection lists
* **Active**: Uncheck to archive the state

#. Click **Save**

The extended state will now be available in the state selection widget for Projects.

**Note**: Technical names must be unique across all extended states.

Usage
=====

After configuring extended states, you can use them in Projects and Tasks:

**For Projects:**

#. Open a project
#. Below the standard "Status" field, you'll see the "Custom State" field
#. Click to select from available extended states
#. The selection will save automatically

The extended state field uses the same widget as the native status field, providing a consistent user experience with colored status bubbles.

**Kanban View:**

Custom states are also visible in Kanban views for both Projects, with the configured color displayed as a status bubble.

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/project/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/project/issues/new?body=module:%20project_state_extend%0Aversion:%2016.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
~~~~~~~

* Escodoo

Contributors
~~~~~~~~~~~~

* Escodoo (https://www.escodoo.com.br):

* Kaynnan Lemes <[email protected]>

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

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

from . import models
28 changes: 28 additions & 0 deletions project_state_extend/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2025 - TODAY, Escodoo.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Project State Extend",
"summary": """
Extend project status selection widget with custom states""",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Escodoo, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/project",
"depends": [
"project",
],
"data": [
"security/ir.model.access.csv",
"views/project_state_extend_views.xml",
],
"assets": {
"web.assets_backend": [
"project_state_extend/static/src/**/*.esm.js",
],
},
"demo": [
"demo/project_state_extend_demo.xml",
],
"installable": True,
}
45 changes: 45 additions & 0 deletions project_state_extend/demo/project_state_extend_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 - TODAY, Kaynnan Lemes <[email protected]>
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<!-- Custom State: Pending Review -->
<record id="state_pending_review" model="project.state.extend">
<field name="name">Pending Review</field>
<field name="technical_name">pending_review</field>
<field name="color">5</field>
<!-- Purple -->
<field name="sequence">10</field>
</record>
<!-- Custom State: Blocked -->
<record id="state_blocked" model="project.state.extend">
<field name="name">Blocked</field>
<field name="technical_name">blocked</field>
<field name="color">1</field>
<!-- Red -->
<field name="sequence">20</field>
</record>
<!-- Custom State: Waiting Client -->
<record id="state_waiting_client" model="project.state.extend">
<field name="name">Waiting Client</field>
<field name="technical_name">waiting_client</field>
<field name="color">3</field>
<!-- Yellow/Orange -->
<field name="sequence">30</field>
</record>
<!-- Custom State: Ready to Deploy -->
<record id="state_ready_deploy" model="project.state.extend">
<field name="name">Ready to Deploy</field>
<field name="technical_name">ready_deploy</field>
<field name="color">10</field>
<!-- Green -->
<field name="sequence">40</field>
</record>
<!-- Custom State: In Testing -->
<record id="state_in_testing" model="project.state.extend">
<field name="name">In Testing</field>
<field name="technical_name">in_testing</field>
<field name="color">4</field>
<!-- Light Blue -->
<field name="sequence">50</field>
</record>
</odoo>
5 changes: 5 additions & 0 deletions project_state_extend/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import project_state_extend
from . import project_update
from . import project_project
66 changes: 66 additions & 0 deletions project_state_extend/models/project_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright 2025 - TODAY, Kaynnan Lemes <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models


class ProjectProject(models.Model):
_inherit = "project.project"

@api.model
def _get_extended_status_selection(self):
"""Get status selection with custom states added"""
# Native states - hardcoded to avoid issues with field inheritance
# These values come from the original Odoo project module
native_states = [
("on_track", "On Track"),
("at_risk", "At Risk"),
("off_track", "Off Track"),
("on_hold", "On Hold"),
("to_define", "Set Status"),
]

# Get custom states
custom_states = self.env["project.state.extend"].search(
[], order="sequence, id"
)
extended_states = [
(state.technical_name, state.name) for state in custom_states
]

return native_states + extended_states

# Override the last_update_status field to use dynamic selection
last_update_status = fields.Selection(
selection="_get_extended_status_selection",
default="to_define",
compute="_compute_last_update_status",
store=True,
readonly=False,
required=True,
)

@api.depends("last_update_status")
def _compute_last_update_color(self):
"""Override to handle custom state colors"""
# Native status colors from Odoo core
# Must be defined here because super() would fail on custom states
native_status_color = {
"on_track": 10, # Green
"at_risk": 2, # Orange
"off_track": 1, # Red
"on_hold": 4, # Blue
"to_define": 0, # Gray
}

# Build extended color map with custom states
custom_states = self.env["project.state.extend"].search([])
extended_status_color = native_status_color.copy()
for state in custom_states:
extended_status_color[state.technical_name] = state.color

# Apply colors to all projects
for project in self:
project.last_update_color = extended_status_color.get(
project.last_update_status, 0
)
40 changes: 40 additions & 0 deletions project_state_extend/models/project_state_extend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2025 - TODAY, Kaynnan Lemes <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ProjectStateExtend(models.Model):
_name = "project.state.extend"
_description = "Extended Project State"
_order = "sequence, name"

name = fields.Char(
required=True,
translate=True,
)
technical_name = fields.Char(
required=True,
help="Technical name used internally. Must be unique and lowercase.",
)
color = fields.Integer(
string="Color Index",
required=True,
default=0,
help="Color index for the status bubble (0-11)",
)
sequence = fields.Integer(
default=100,
help="Sequence for ordering. Lower values appear first.",
)
active = fields.Boolean(
default=True,
)

_sql_constraints = [
(
"technical_name_uniq",
"unique(technical_name)",
"Technical name must be unique!",
)
]
59 changes: 59 additions & 0 deletions project_state_extend/models/project_update.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2025 - TODAY, Kaynnan Lemes <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models


class ProjectUpdate(models.Model):
_inherit = "project.update"

@api.model
def _get_extended_status_selection(self):
"""Get status selection with custom states added"""
# Native states - hardcoded to avoid issues with field inheritance
# These values come from the original Odoo project module
native_states = [
("on_track", "On Track"),
("at_risk", "At Risk"),
("off_track", "Off Track"),
("on_hold", "On Hold"),
]

# Get custom states
custom_states = self.env["project.state.extend"].search(
[], order="sequence, id"
)
extended_states = [
(state.technical_name, state.name) for state in custom_states
]

return native_states + extended_states

# Override the status field to use dynamic selection
status = fields.Selection(
selection="_get_extended_status_selection",
required=True,
tracking=True,
)

@api.depends("status")
def _compute_color(self):
"""Override to handle custom state colors"""
# Native status colors from Odoo core
# Must be defined here because super() would fail on custom states
native_status_color = {
"on_track": 10, # Green
"at_risk": 2, # Orange
"off_track": 1, # Red
"on_hold": 4, # Blue
}

# Build extended color map with custom states
custom_states = self.env["project.state.extend"].search([])
extended_status_color = native_status_color.copy()
for state in custom_states:
extended_status_color[state.technical_name] = state.color

# Apply colors to all updates
for update in self:
update.color = extended_status_color.get(update.status, 0)
17 changes: 17 additions & 0 deletions project_state_extend/readme/CONFIGURE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
To configure custom project states:

#. Go to **Project > Configuration > Extended States**
#. Click **Create**
#. Fill in the form:

* **Name**: Display name for the state (e.g., "Pending Review")
* **Technical Name**: Unique identifier (lowercase, no spaces, e.g., "pending_review")
* **Color**: Select a color index from 0 to 11 for the status bubble
* **Sequence**: Lower numbers appear first in selection lists
* **Active**: Uncheck to archive the state

#. Click **Save**

The extended state will now be available in the state selection widget for Projects.

**Note**: Technical names must be unique across all extended states.
Loading