Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User defined states #7862

Merged
merged 69 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
a66e3d5
Add custom user defined states
matmair Aug 8, 2024
4cee20f
make tests more reliable
matmair Aug 8, 2024
e7ee679
fix list options
matmair Aug 8, 2024
a9239e3
Adapt version
matmair Aug 8, 2024
acf6e11
do not engage if rebuilding
matmair Aug 8, 2024
451362d
remove unneeded attr
matmair Aug 8, 2024
c0ee300
remove unneeded attr
matmair Aug 8, 2024
2ee9235
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Aug 9, 2024
dbf9db5
fix enum imports
matmair Aug 9, 2024
049f9bd
adapt cove target
matmair Aug 11, 2024
c5268f6
Add status_custom_key to all other serializers
matmair Aug 11, 2024
77dc0c7
fix serializer method
matmair Aug 11, 2024
3e803c4
simplify branching
matmair Aug 11, 2024
0e3d2f3
remove unneeded imports
matmair Aug 11, 2024
c81213a
inherit read_only status from leader field
matmair Aug 12, 2024
c9c01be
Add more tests
matmair Aug 12, 2024
5926fc7
fix tests
matmair Aug 12, 2024
04498e9
add test for function
matmair Aug 12, 2024
67dde38
refactor for easier testing
matmair Aug 12, 2024
94858a4
move test to seperate class
matmair Aug 12, 2024
b4e42da
Add options testing
matmair Aug 12, 2024
6a5f9c2
extend serializer
matmair Aug 12, 2024
cf9dcf2
add test for all states and refactor to reuse already build functions
matmair Aug 12, 2024
06886ec
use custom field in PUI too
matmair Aug 12, 2024
f5098d7
reset diff
matmair Aug 12, 2024
4022eac
Merge branch 'master' into custom-user-defined-states
matmair Aug 12, 2024
d62f9b8
style fix
matmair Aug 12, 2024
198bd8f
fix comparison
matmair Aug 12, 2024
d0409ee
Add test for str
matmair Aug 12, 2024
33da48c
test color exceptions too
matmair Aug 12, 2024
1ce1e61
Merge branch 'master' into custom-user-defined-states
matmair Aug 13, 2024
83c91f1
remove user state from tracking
matmair Aug 13, 2024
4d4d3b0
Add intro from model fields too
matmair Aug 13, 2024
ac0c726
update docs
matmair Aug 13, 2024
461f77d
simplify implementation
matmair Aug 13, 2024
30c8979
update tests
matmair Aug 13, 2024
cbf8676
fix name
matmair Aug 13, 2024
93113db
rename test
matmair Aug 13, 2024
7d86ad7
simplify tags and test fully
matmair Aug 14, 2024
fb20790
extend test to machine status
matmair Aug 14, 2024
c6361be
move logic for response formatting over
matmair Aug 14, 2024
fb2d62d
extend api response with machine status
matmair Aug 14, 2024
5affeed
ensure only direct subclasses are discovered
matmair Aug 14, 2024
eb57800
test for length of total respone too
matmair Aug 14, 2024
8782fc7
use new fields on PUI too
matmair Aug 14, 2024
4e94d96
fix test assertion with plugins enabled
matmair Aug 14, 2024
6d3472a
Merge branch 'master' into custom-user-defined-states
matmair Aug 14, 2024
1852e7e
also observe rendering in filters
matmair Aug 15, 2024
d462cad
Add managment endpoints and APIs
matmair Aug 15, 2024
f7a4496
Add contenttypes to PUI renderes
matmair Aug 15, 2024
afad1a7
use filteres instead
matmair Aug 15, 2024
f150840
fix import order
matmair Aug 15, 2024
65c8642
fix api route definition
matmair Aug 15, 2024
3347c37
move status choices to serializer
matmair Aug 15, 2024
80451d4
fix lookup
matmair Aug 15, 2024
486aedb
fix filtering
matmair Aug 15, 2024
5de254f
remove admin integration
matmair Aug 15, 2024
01b15bd
cleanup migration
matmair Aug 15, 2024
448cdb8
fix migration change
matmair Aug 16, 2024
261f8fc
cleanup code location
matmair Aug 16, 2024
8960a33
fix imports
matmair Aug 16, 2024
1a9aa56
Merge branch 'master' into custom-user-defined-states
matmair Aug 16, 2024
0dda98b
Add docs for custom states
matmair Aug 17, 2024
2403508
Merge branch 'master' into custom-user-defined-states
matmair Aug 19, 2024
e3244f2
Merge branch 'master' into custom-user-defined-states
matmair Aug 20, 2024
afa230e
Merge branch 'master' into custom-user-defined-states
matmair Aug 21, 2024
ec9c392
Merge branch 'master' into custom-user-defined-states
matmair Aug 21, 2024
25cf638
add links to custom status
matmair Aug 21, 2024
047fec1
Merge branch 'custom-user-defined-states' of https://github.com/matma…
matmair Aug 21, 2024
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
15 changes: 15 additions & 0 deletions docs/docs/concepts/custom_states.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: Custom States
---

## Custom States
matmair marked this conversation as resolved.
Show resolved Hide resolved

Several models within InvenTree support the use of custom states. The custom states are display only - the business logic is not affected by the state.

States can be added in the Admin Center under the "Custom States" section. Each state has a name, label and a color that are used to display the state in the user interface. Changes to these settings will only be reflected in the user interface after a full reload of the interface.

States need to be assigned to a model, state (for example status on a StockItem) and a logical key - that will be used for business logic. These 3 values combined need to be unique throughout the system.

Custom states can be used in the following models:
- StockItem
- Orders (PurchaseOrder, SalesOrder, ReturnOrder, ReturnOrderLine)
8 changes: 5 additions & 3 deletions docs/docs/extend/machines/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ If you want to create your own machine type, please also take a look at the alre

```py
from django.utils.translation import gettext_lazy as _

from generic.states import ColorEnum
from plugin.machine import BaseDriver, BaseMachineType, MachineStatus

class ABCBaseDriver(BaseDriver):
Expand All @@ -72,9 +74,9 @@ class ABCMachine(BaseMachineType):
base_driver = ABCBaseDriver

class ABCStatus(MachineStatus):
CONNECTED = 100, _('Connected'), 'success'
STANDBY = 101, _('Standby'), 'success'
PRINTING = 110, _('Printing'), 'primary'
CONNECTED = 100, _('Connected'), ColorEnum.success
STANDBY = 101, _('Standby'), ColorEnum.success
PRINTING = 110, _('Printing'), ColorEnum.primary

MACHINE_STATUS = ABCStatus
default_machine_status = ABCStatus.DISCONNECTED
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/order/purchase_order.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Refer to the source code for the Purchase Order status codes:
show_source: True
members: []

Purchase Order Status supports [custom states](../concepts/custom_states.md).

### Purchase Order Currency

The currency code can be specified for an individual purchase order. If not specified, the default currency specified against the [supplier](./company.md#suppliers) will be used.
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/order/return_order.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ Refer to the source code for the Return Order status codes:
show_source: True
members: []

Return Order Status supports [custom states](../concepts/custom_states.md).

## Create a Return Order

From the Return Order index, click on <span class='badge inventree add'><span class='fas fa-plus-circle'></span> New Return Order</span> which opens the "Create Return Order" form.
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/order/sales_order.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Refer to the source code for the Sales Order status codes:
show_source: True
members: []

Sales Order Status supports [custom states](../concepts/custom_states.md).

### Sales Order Currency

The currency code can be specified for an individual sales order. If not specified, the default currency specified against the [customer](./company.md#customers) will be used.
Expand Down
4 changes: 3 additions & 1 deletion docs/docs/stock/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Certain stock item status codes will restrict the availability of the stock item

Below is the list of available stock status codes and their meaning:

| Status | Description | Available |
| Status | Description | Available |
| ----------- | ----------- | --- |
| <span class='badge inventree success'>OK</span> | Stock item is healthy, nothing wrong to report | <span class='badge inventree success'>Yes</span> |
| <span class='badge inventree warning'>Attention needed</span> | Stock item hasn't been checked or tested yet | <span class='badge inventree success'>Yes</span> |
Expand Down Expand Up @@ -38,6 +38,8 @@ Refer to the source code for the Stock status codes:
show_source: True
members: []

Stock Status supports [custom states](../concepts/custom_states.md).

### Default Status Code

The default status code for any newly created Stock Item is <span class='badge inventree success'>OK</span>
Expand Down
1 change: 1 addition & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ nav:
- Core Concepts:
- Terminology: concepts/terminology.md
- Physical Units: concepts/units.md
- Custom States: concepts/custom_states.md
- Development:
- Contributing: develop/contributing.md
- Devcontainer: develop/devcontainer.md
Expand Down
6 changes: 5 additions & 1 deletion src/backend/InvenTree/InvenTree/api_version.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
"""InvenTree API version information."""

# InvenTree API version
INVENTREE_API_VERSION = 245
INVENTREE_API_VERSION = 246

"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""


INVENTREE_API_TEXT = """

v246 - 2024-08-21 : https://github.com/inventree/InvenTree/pull/7862
- Adds custom status fields to various serializers
- Adds endpoints to admin custom status fields

v245 - 2024-08-21 : https://github.com/inventree/InvenTree/pull/7520
- Documented pagination fields (no functional changes)

Expand Down
9 changes: 6 additions & 3 deletions src/backend/InvenTree/InvenTree/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"""Provides extra global data to all templates."""

import InvenTree.email
import InvenTree.ready
import InvenTree.status
from generic.states import StatusCode
from InvenTree.helpers import inheritors
from generic.states.custom import get_custom_classes
from users.models import RuleSet, check_user_role


Expand Down Expand Up @@ -53,7 +53,10 @@ def status_codes(request):
return {}

request._inventree_status_codes = True
return {cls.__name__: cls.template_context() for cls in inheritors(StatusCode)}
get_custom = InvenTree.ready.isRebuildingData() is False
return {
cls.__name__: cls.template_context() for cls in get_custom_classes(get_custom)
}


def user_roles(request):
Expand Down
14 changes: 11 additions & 3 deletions src/backend/InvenTree/InvenTree/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,8 +953,15 @@ def get_target(self, obj):
Inheritors_T = TypeVar('Inheritors_T')


def inheritors(cls: type[Inheritors_T]) -> set[type[Inheritors_T]]:
"""Return all classes that are subclasses from the supplied cls."""
def inheritors(
cls: type[Inheritors_T], subclasses: bool = True
) -> set[type[Inheritors_T]]:
"""Return all classes that are subclasses from the supplied cls.

Args:
cls: The class to search for subclasses
subclasses: Include subclasses of subclasses (default = True)
"""
subcls = set()
work = [cls]

Expand All @@ -963,7 +970,8 @@ def inheritors(cls: type[Inheritors_T]) -> set[type[Inheritors_T]]:
for child in parent.__subclasses__():
if child not in subcls:
subcls.add(child)
work.append(child)
if subclasses:
work.append(child)
return subcls


Expand Down
10 changes: 9 additions & 1 deletion src/backend/InvenTree/InvenTree/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ def get_field_info(self, field):
field_info['type'] = 'related field'
field_info['model'] = model._meta.model_name

# Special case for 'user' model
# Special case for special models
if field_info['model'] == 'user':
field_info['api_url'] = '/api/user/'
elif field_info['model'] == 'contenttype':
Expand All @@ -381,6 +381,14 @@ def get_field_info(self, field):
if field_info['type'] == 'dependent field':
field_info['depends_on'] = field.depends_on

# Extend field info if the field has a get_field_info method
if (
not field_info.get('read_only')
and hasattr(field, 'get_field_info')
and callable(field.get_field_info)
):
field_info = field.get_field_info(field, field_info)

return field_info


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 4.2.14 on 2024-08-07 22:40

import django.core.validators
from django.db import migrations

import generic.states.fields
import InvenTree.status_codes


class Migration(migrations.Migration):

dependencies = [
("build", "0051_delete_buildorderattachment"),
]

operations = [
migrations.AddField(
model_name="build",
name="status_custom_key",
field=generic.states.fields.ExtraInvenTreeCustomStatusModelField(
blank=True,
default=None,
help_text="Additional status information for this item",
null=True,
verbose_name="Custom status key",
),
),
migrations.AlterField(
model_name="build",
name="status",
field=generic.states.fields.InvenTreeCustomStatusModelField(
choices=InvenTree.status_codes.BuildStatus.items(),
default=10,
help_text="Build status code",
validators=[django.core.validators.MinValueValidator(0)],
verbose_name="Build Status",
),
),
]
3 changes: 2 additions & 1 deletion src/backend/InvenTree/build/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import report.mixins
import stock.models
import users.models
import generic.states


logger = logging.getLogger('inventree')
Expand Down Expand Up @@ -315,7 +316,7 @@ def get_absolute_url(self):
help_text=_('Number of stock items which have been completed')
)

status = models.PositiveIntegerField(
status = generic.states.fields.InvenTreeCustomStatusModelField(
verbose_name=_('Build Status'),
default=BuildStatus.PENDING.value,
choices=BuildStatus.items(),
Expand Down
55 changes: 32 additions & 23 deletions src/backend/InvenTree/build/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,53 @@

from decimal import Decimal

from django.db import transaction
from django.core.exceptions import ValidationError as DjangoValidationError
from django.utils.translation import gettext_lazy as _

from django.db import models
from django.db.models import ExpressionWrapper, F, FloatField
from django.db.models import Case, Sum, When, Value
from django.db.models import BooleanField, Q
from django.db import models, transaction
from django.db.models import (
BooleanField,
Case,
ExpressionWrapper,
F,
FloatField,
Q,
Sum,
Value,
When,
)
from django.db.models.functions import Coalesce
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers
from rest_framework.serializers import ValidationError

from InvenTree.serializers import InvenTreeModelSerializer, UserSerializer

import InvenTree.helpers
import InvenTree.tasks
from InvenTree.serializers import InvenTreeDecimalField, NotesFieldMixin
from stock.status_codes import StockStatus

from stock.generators import generate_batch_code
from stock.models import StockItem, StockLocation
from stock.serializers import StockItemSerializerBrief, LocationBriefSerializer

import build.tasks
import common.models
from common.serializers import ProjectCodeSerializer
from common.settings import get_global_setting
from importer.mixins import DataImportExportSerializerMixin
import company.serializers
import InvenTree.helpers
import InvenTree.tasks
import part.filters
import part.serializers as part_serializers
from common.serializers import ProjectCodeSerializer
from common.settings import get_global_setting
from generic.states.fields import InvenTreeCustomStatusSerializerMixin
from importer.mixins import DataImportExportSerializerMixin
from InvenTree.serializers import (
InvenTreeDecimalField,
InvenTreeModelSerializer,
NotesFieldMixin,
UserSerializer,
)
from stock.generators import generate_batch_code
from stock.models import StockItem, StockLocation
from stock.serializers import LocationBriefSerializer, StockItemSerializerBrief
from stock.status_codes import StockStatus
from users.serializers import OwnerSerializer

from .models import Build, BuildLine, BuildItem
from .models import Build, BuildItem, BuildLine
from .status_codes import BuildStatus


class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer):
class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTreeCustomStatusSerializerMixin, InvenTreeModelSerializer):
"""Serializes a Build object."""

class Meta:
Expand Down Expand Up @@ -69,6 +77,7 @@ class Meta:
'quantity',
'status',
'status_text',
'status_custom_key',
'target_date',
'take_from',
'notes',
Expand Down
12 changes: 6 additions & 6 deletions src/backend/InvenTree/build/status_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

from django.utils.translation import gettext_lazy as _

from generic.states import StatusCode
from generic.states import ColorEnum, StatusCode


class BuildStatus(StatusCode):
"""Build status codes."""

PENDING = 10, _('Pending'), 'secondary' # Build is pending / active
PRODUCTION = 20, _('Production'), 'primary' # Build is in production
ON_HOLD = 25, _('On Hold'), 'warning' # Build is on hold
CANCELLED = 30, _('Cancelled'), 'danger' # Build was cancelled
COMPLETE = 40, _('Complete'), 'success' # Build is complete
PENDING = 10, _('Pending'), ColorEnum.secondary # Build is pending / active
PRODUCTION = 20, _('Production'), ColorEnum.primary # Build is in production
ON_HOLD = 25, _('On Hold'), ColorEnum.warning # Build is on hold
CANCELLED = 30, _('Cancelled'), ColorEnum.danger # Build was cancelled
COMPLETE = 40, _('Complete'), ColorEnum.success # Build is complete


class BuildStatusGroups:
Expand Down
4 changes: 2 additions & 2 deletions src/backend/InvenTree/build/templates/build/build_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>
{% status_label 'build' build.status %}
{% display_status_label 'build' build.status_custom_key build.status %}
</td>
</tr>
{% if build.target_date %}
Expand Down Expand Up @@ -225,7 +225,7 @@

{% block page_data %}
<h3>
{% status_label 'build' build.status large=True %}
{% display_status_label 'build' build.status_custom_key build.status large=True %}
{% if build.is_overdue %}
<span class='badge rounded-pill bg-danger'>{% trans "Overdue" %}</span>
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion src/backend/InvenTree/build/templates/build/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ <h4>{% trans "Build Details" %}</h4>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>{% status_label 'build' build.status %}</td>
<td>{% display_status_label 'build' build.status_custom_key build.status %}</td>
</tr>
<tr>
<td><span class='fas fa-check-circle'></span></td>
Expand Down
Loading
Loading