mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-30 12:45:42 +00:00
[feature] Project code per line (#10657)
* Add project code to line items * Refactor AbstractOrderSerialiazer * Refactor AbstractOrderLineItem serializer * Refactoring for AbstractExtraLineSerializer * UI elements for extra line item project code * UI for ReturnOrderLineItems * UI elements for SalesOrderLineItem * UI elements for PurchaseOrderLineItem * Docs updates * Update API version and CHANGELOG
This commit is contained in:
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Allow stock adjustments for "in production" items in [#10600](https://github.com/inventree/InvenTree/pull/10600)
|
- Allow stock adjustments for "in production" items in [#10600](https://github.com/inventree/InvenTree/pull/10600)
|
||||||
- Adds optional shipping address against individual sales order shipments in [#10650](https://github.com/inventree/InvenTree/pull/10650)
|
- Adds optional shipping address against individual sales order shipments in [#10650](https://github.com/inventree/InvenTree/pull/10650)
|
||||||
- Adds UI elements to "check" and "uncheck" sales order shipments in [#10654](https://github.com/inventree/InvenTree/pull/10654)
|
- Adds UI elements to "check" and "uncheck" sales order shipments in [#10654](https://github.com/inventree/InvenTree/pull/10654)
|
||||||
|
- Allow assigning project codes to order line items in [#10657](https://github.com/inventree/InvenTree/pull/10657)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,18 @@ title: Project Codes
|
|||||||
|
|
||||||
A project code is a unique identifier assigned to a specific project, which helps in tracking and organizing project-related activities and resources. It enables easy retrieval of project-related data and facilitates project management and reporting.
|
A project code is a unique identifier assigned to a specific project, which helps in tracking and organizing project-related activities and resources. It enables easy retrieval of project-related data and facilitates project management and reporting.
|
||||||
|
|
||||||
Individual orders (such as [Purchase Orders](../purchasing/purchase_order.md) or [Sales Orders](../sales/sales_order.md)) can be assigned a *Project Code* to allocate the order against a particular internal project.
|
### Assigning to Orders
|
||||||
|
|
||||||
|
Project codes can be assigned to various orders within the system:
|
||||||
|
|
||||||
|
- [Build Orders](../manufacturing/build.md)
|
||||||
|
- [Purchase Orders](../purchasing/purchase_order.md)
|
||||||
|
- [Sales Orders](../sales/sales_order.md)
|
||||||
|
- [Return Orders](../sales/return_order.md)
|
||||||
|
|
||||||
|
By assigning a project code to an order, users can easily track and manage orders associated with specific projects, enhancing project oversight and resource allocation.
|
||||||
|
|
||||||
|
For orders with external companies, which support individual line items, project codes can be assigned at the line item level, allowing for granular tracking of project-related activities. In such cases, the project code assigned to the order itself serves as a default for all line items, unless explicitly overridden at the line item level.
|
||||||
|
|
||||||
### Managing Project Codes
|
### Managing Project Codes
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 417
|
INVENTREE_API_VERSION = 418
|
||||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
INVENTREE_API_TEXT = """
|
||||||
|
|
||||||
|
v418 -> 2025-10-24 : https://github.com/inventree/InvenTree/pull/10657
|
||||||
|
- Add "project_code" field(s) to OrderLineItem API endpoint(s)
|
||||||
|
- Add "project_code" field(s) to ExtraOrderLineItem API endpoint(s)
|
||||||
|
|
||||||
v417 -> 2025-10-22 : https://github.com/inventree/InvenTree/pull/10654
|
v417 -> 2025-10-22 : https://github.com/inventree/InvenTree/pull/10654
|
||||||
- Adds "checked" filter to SalesOrderShipment API endpoint
|
- Adds "checked" filter to SalesOrderShipment API endpoint
|
||||||
- Adds "order_status" filter to SalesOrdereShipment API endpoint
|
- Adds "order_status" filter to SalesOrdereShipment API endpoint
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# Generated by Django 4.2.25 on 2025-10-24 01:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("common", "0039_emailthread_emailmessage"),
|
||||||
|
("order", "0113_salesordershipment_shipment_address"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="purchaseorderextraline",
|
||||||
|
name="project_code",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Select project code for this order",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="common.projectcode",
|
||||||
|
verbose_name="Project Code",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="purchaseorderlineitem",
|
||||||
|
name="project_code",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Select project code for this order",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="common.projectcode",
|
||||||
|
verbose_name="Project Code",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="returnorderextraline",
|
||||||
|
name="project_code",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Select project code for this order",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="common.projectcode",
|
||||||
|
verbose_name="Project Code",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="returnorderlineitem",
|
||||||
|
name="project_code",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Select project code for this order",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="common.projectcode",
|
||||||
|
verbose_name="Project Code",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="salesorderextraline",
|
||||||
|
name="project_code",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Select project code for this order",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="common.projectcode",
|
||||||
|
verbose_name="Project Code",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="salesorderlineitem",
|
||||||
|
name="project_code",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
help_text="Select project code for this order",
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="common.projectcode",
|
||||||
|
verbose_name="Project Code",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1685,6 +1685,7 @@ class OrderLineItem(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
Attributes:
|
Attributes:
|
||||||
quantity: Number of items
|
quantity: Number of items
|
||||||
reference: Reference text (e.g. customer reference) for this line item
|
reference: Reference text (e.g. customer reference) for this line item
|
||||||
|
project_code: Project code associated with this line item (optional)
|
||||||
note: Annotation for the item
|
note: Annotation for the item
|
||||||
target_date: An (optional) date for expected shipment of this line item.
|
target_date: An (optional) date for expected shipment of this line item.
|
||||||
"""
|
"""
|
||||||
@@ -1768,6 +1769,15 @@ class OrderLineItem(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
project_code = models.ForeignKey(
|
||||||
|
common_models.ProjectCode,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name=_('Project Code'),
|
||||||
|
help_text=_('Select project code for this order'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OrderExtraLine(OrderLineItem):
|
class OrderExtraLine(OrderLineItem):
|
||||||
"""Abstract Model for a single ExtraLine in a Order.
|
"""Abstract Model for a single ExtraLine in a Order.
|
||||||
@@ -1992,8 +2002,6 @@ class PurchaseOrderExtraLine(OrderExtraLine):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
order: Link to the PurchaseOrder that this line belongs to
|
order: Link to the PurchaseOrder that this line belongs to
|
||||||
title: title of line
|
|
||||||
price: The unit price for this OrderLine
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -2422,8 +2430,6 @@ class SalesOrderExtraLine(OrderExtraLine):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
order: Link to the SalesOrder that this line belongs to
|
order: Link to the SalesOrder that this line belongs to
|
||||||
title: title of line
|
|
||||||
price: The unit price for this OrderLine
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ from InvenTree.helpers import (
|
|||||||
)
|
)
|
||||||
from InvenTree.mixins import DataImportExportSerializerMixin
|
from InvenTree.mixins import DataImportExportSerializerMixin
|
||||||
from InvenTree.serializers import (
|
from InvenTree.serializers import (
|
||||||
|
FilterableCharField,
|
||||||
FilterableSerializerMixin,
|
FilterableSerializerMixin,
|
||||||
InvenTreeCurrencySerializer,
|
InvenTreeCurrencySerializer,
|
||||||
InvenTreeDecimalField,
|
InvenTreeDecimalField,
|
||||||
@@ -105,7 +106,9 @@ class DuplicateOrderSerializer(serializers.Serializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Serializer):
|
class AbstractOrderSerializer(
|
||||||
|
DataImportExportSerializerMixin, FilterableSerializerMixin, serializers.Serializer
|
||||||
|
):
|
||||||
"""Abstract serializer class which provides fields common to all order types."""
|
"""Abstract serializer class which provides fields common to all order types."""
|
||||||
|
|
||||||
export_exclude_fields = ['notes', 'duplicate']
|
export_exclude_fields = ['notes', 'duplicate']
|
||||||
@@ -132,30 +135,46 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria
|
|||||||
reference = serializers.CharField(required=True)
|
reference = serializers.CharField(required=True)
|
||||||
|
|
||||||
# Detail for point-of-contact field
|
# Detail for point-of-contact field
|
||||||
contact_detail = ContactSerializer(
|
contact_detail = enable_filter(
|
||||||
source='contact', many=False, read_only=True, allow_null=True
|
ContactSerializer(
|
||||||
|
source='contact', many=False, read_only=True, allow_null=True
|
||||||
|
),
|
||||||
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Detail for responsible field
|
# Detail for responsible field
|
||||||
responsible_detail = OwnerSerializer(
|
responsible_detail = enable_filter(
|
||||||
source='responsible', read_only=True, allow_null=True, many=False
|
OwnerSerializer(
|
||||||
|
source='responsible', read_only=True, allow_null=True, many=False
|
||||||
|
),
|
||||||
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
project_code_label = serializers.CharField(
|
project_code_label = enable_filter(
|
||||||
source='project_code.code',
|
FilterableCharField(
|
||||||
read_only=True,
|
source='project_code.code',
|
||||||
label='Project Code Label',
|
read_only=True,
|
||||||
allow_null=True,
|
label='Project Code Label',
|
||||||
|
allow_null=True,
|
||||||
|
),
|
||||||
|
True,
|
||||||
|
filter_name='project_code_detail',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Detail for project code field
|
# Detail for project code field
|
||||||
project_code_detail = ProjectCodeSerializer(
|
project_code_detail = enable_filter(
|
||||||
source='project_code', read_only=True, many=False, allow_null=True
|
ProjectCodeSerializer(
|
||||||
|
source='project_code', read_only=True, many=False, allow_null=True
|
||||||
|
),
|
||||||
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Detail for address field
|
# Detail for address field
|
||||||
address_detail = AddressBriefSerializer(
|
address_detail = enable_filter(
|
||||||
source='address', many=False, read_only=True, allow_null=True
|
AddressBriefSerializer(
|
||||||
|
source='address', many=False, read_only=True, allow_null=True
|
||||||
|
),
|
||||||
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Boolean field indicating if this order is overdue (Note: must be annotated)
|
# Boolean field indicating if this order is overdue (Note: must be annotated)
|
||||||
@@ -204,15 +223,10 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria
|
|||||||
'completed_lines',
|
'completed_lines',
|
||||||
'link',
|
'link',
|
||||||
'project_code',
|
'project_code',
|
||||||
'project_code_label',
|
|
||||||
'project_code_detail',
|
|
||||||
'reference',
|
'reference',
|
||||||
'responsible',
|
'responsible',
|
||||||
'responsible_detail',
|
|
||||||
'contact',
|
'contact',
|
||||||
'contact_detail',
|
|
||||||
'address',
|
'address',
|
||||||
'address_detail',
|
|
||||||
'status',
|
'status',
|
||||||
'status_text',
|
'status_text',
|
||||||
'status_custom_key',
|
'status_custom_key',
|
||||||
@@ -220,6 +234,12 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria
|
|||||||
'barcode_hash',
|
'barcode_hash',
|
||||||
'overdue',
|
'overdue',
|
||||||
'duplicate',
|
'duplicate',
|
||||||
|
# Extra detail fields
|
||||||
|
'address_detail',
|
||||||
|
'contact_detail',
|
||||||
|
'project_code_detail',
|
||||||
|
'project_code_label',
|
||||||
|
'responsible_detail',
|
||||||
*extra_fields,
|
*extra_fields,
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -263,25 +283,103 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class AbstractLineItemSerializer:
|
class AbstractLineItemSerializer(FilterableSerializerMixin, serializers.Serializer):
|
||||||
"""Abstract serializer for LineItem object."""
|
"""Abstract serializer for LineItem object."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def line_fields(extra_fields):
|
||||||
|
"""Construct a set of fields for this serializer."""
|
||||||
|
return [
|
||||||
|
'pk',
|
||||||
|
'link',
|
||||||
|
'notes',
|
||||||
|
'order',
|
||||||
|
'project_code',
|
||||||
|
'quantity',
|
||||||
|
'reference',
|
||||||
|
'target_date',
|
||||||
|
# Filterable detail fields
|
||||||
|
'order_detail',
|
||||||
|
'project_code_label',
|
||||||
|
'project_code_detail',
|
||||||
|
*extra_fields,
|
||||||
|
]
|
||||||
|
|
||||||
target_date = serializers.DateField(
|
target_date = serializers.DateField(
|
||||||
required=False, allow_null=True, label=_('Target Date')
|
required=False, allow_null=True, label=_('Target Date')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
project_code_label = enable_filter(
|
||||||
|
FilterableCharField(
|
||||||
|
source='project_code.code',
|
||||||
|
read_only=True,
|
||||||
|
label='Project Code Label',
|
||||||
|
allow_null=True,
|
||||||
|
),
|
||||||
|
True,
|
||||||
|
filter_name='project_code_detail',
|
||||||
|
)
|
||||||
|
|
||||||
|
project_code_detail = enable_filter(
|
||||||
|
ProjectCodeSerializer(
|
||||||
|
source='project_code', read_only=True, many=False, allow_null=True
|
||||||
|
),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AbstractExtraLineSerializer(
|
class AbstractExtraLineSerializer(
|
||||||
DataImportExportSerializerMixin, serializers.Serializer
|
DataImportExportSerializerMixin, FilterableSerializerMixin, serializers.Serializer
|
||||||
):
|
):
|
||||||
"""Abstract Serializer for a ExtraLine object."""
|
"""Abstract Serializer for a ExtraLine object."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extra_line_fields(extra_fields):
|
||||||
|
"""Construct a set of fields for this serializer."""
|
||||||
|
return [
|
||||||
|
'pk',
|
||||||
|
'description',
|
||||||
|
'link',
|
||||||
|
'notes',
|
||||||
|
'order',
|
||||||
|
'price',
|
||||||
|
'price_currency',
|
||||||
|
'project_code',
|
||||||
|
'quantity',
|
||||||
|
'reference',
|
||||||
|
'target_date',
|
||||||
|
# Filterable detail fields
|
||||||
|
'order_detail',
|
||||||
|
'project_code_label',
|
||||||
|
'project_code_detail',
|
||||||
|
*extra_fields,
|
||||||
|
]
|
||||||
|
|
||||||
quantity = serializers.FloatField()
|
quantity = serializers.FloatField()
|
||||||
|
|
||||||
price = InvenTreeMoneySerializer(allow_null=True)
|
price = InvenTreeMoneySerializer(allow_null=True)
|
||||||
|
|
||||||
price_currency = InvenTreeCurrencySerializer()
|
price_currency = InvenTreeCurrencySerializer()
|
||||||
|
|
||||||
|
project_code_label = enable_filter(
|
||||||
|
FilterableCharField(
|
||||||
|
source='project_code.code',
|
||||||
|
read_only=True,
|
||||||
|
label='Project Code Label',
|
||||||
|
allow_null=True,
|
||||||
|
),
|
||||||
|
True,
|
||||||
|
filter_name='project_code_detail',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Detail for project code field
|
||||||
|
project_code_detail = enable_filter(
|
||||||
|
ProjectCodeSerializer(
|
||||||
|
source='project_code', read_only=True, many=False, allow_null=True
|
||||||
|
),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AbstractExtraLineMeta:
|
class AbstractExtraLineMeta:
|
||||||
"""Abstract Meta for ExtraLine."""
|
"""Abstract Meta for ExtraLine."""
|
||||||
@@ -303,7 +401,6 @@ class AbstractExtraLineMeta:
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class PurchaseOrderSerializer(
|
class PurchaseOrderSerializer(
|
||||||
FilterableSerializerMixin,
|
|
||||||
NotesFieldMixin,
|
NotesFieldMixin,
|
||||||
TotalPriceMixin,
|
TotalPriceMixin,
|
||||||
InvenTreeCustomStatusSerializerMixin,
|
InvenTreeCustomStatusSerializerMixin,
|
||||||
@@ -460,7 +557,6 @@ class PurchaseOrderIssueSerializer(OrderAdjustSerializer):
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class PurchaseOrderLineItemSerializer(
|
class PurchaseOrderLineItemSerializer(
|
||||||
FilterableSerializerMixin,
|
|
||||||
DataImportExportSerializerMixin,
|
DataImportExportSerializerMixin,
|
||||||
AbstractLineItemSerializer,
|
AbstractLineItemSerializer,
|
||||||
InvenTreeModelSerializer,
|
InvenTreeModelSerializer,
|
||||||
@@ -471,35 +567,28 @@ class PurchaseOrderLineItemSerializer(
|
|||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
model = order.models.PurchaseOrderLineItem
|
model = order.models.PurchaseOrderLineItem
|
||||||
fields = [
|
fields = AbstractLineItemSerializer.line_fields([
|
||||||
'pk',
|
|
||||||
'part',
|
'part',
|
||||||
'quantity',
|
|
||||||
'reference',
|
|
||||||
'notes',
|
|
||||||
'order',
|
|
||||||
'order_detail',
|
|
||||||
'build_order',
|
'build_order',
|
||||||
'build_order_detail',
|
|
||||||
'overdue',
|
'overdue',
|
||||||
'part_detail',
|
|
||||||
'supplier_part_detail',
|
|
||||||
'received',
|
'received',
|
||||||
'purchase_price',
|
'purchase_price',
|
||||||
'purchase_price_currency',
|
'purchase_price_currency',
|
||||||
'auto_pricing',
|
'auto_pricing',
|
||||||
'destination',
|
'destination',
|
||||||
'destination_detail',
|
|
||||||
'target_date',
|
|
||||||
'total_price',
|
'total_price',
|
||||||
'link',
|
|
||||||
'merge_items',
|
'merge_items',
|
||||||
'sku',
|
'sku',
|
||||||
'mpn',
|
'mpn',
|
||||||
'ipn',
|
'ipn',
|
||||||
'internal_part',
|
'internal_part',
|
||||||
'internal_part_name',
|
'internal_part_name',
|
||||||
]
|
# Filterable detail fields
|
||||||
|
'build_order_detail',
|
||||||
|
'destination_detail',
|
||||||
|
'part_detail',
|
||||||
|
'supplier_part_detail',
|
||||||
|
])
|
||||||
|
|
||||||
def skip_create_fields(self):
|
def skip_create_fields(self):
|
||||||
"""Return a list of fields to skip when creating a new object."""
|
"""Return a list of fields to skip when creating a new object."""
|
||||||
@@ -693,21 +782,22 @@ class PurchaseOrderLineItemSerializer(
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class PurchaseOrderExtraLineSerializer(
|
class PurchaseOrderExtraLineSerializer(
|
||||||
FilterableSerializerMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||||
):
|
):
|
||||||
"""Serializer for a PurchaseOrderExtraLine object."""
|
"""Serializer for a PurchaseOrderExtraLine object."""
|
||||||
|
|
||||||
|
class Meta(AbstractExtraLineMeta):
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = order.models.PurchaseOrderExtraLine
|
||||||
|
fields = AbstractExtraLineSerializer.extra_line_fields([])
|
||||||
|
|
||||||
order_detail = enable_filter(
|
order_detail = enable_filter(
|
||||||
PurchaseOrderSerializer(
|
PurchaseOrderSerializer(
|
||||||
source='order', many=False, read_only=True, allow_null=True
|
source='order', many=False, read_only=True, allow_null=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(AbstractExtraLineMeta):
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = order.models.PurchaseOrderExtraLine
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItemReceiveSerializer(serializers.Serializer):
|
class PurchaseOrderLineItemReceiveSerializer(serializers.Serializer):
|
||||||
"""A serializer for receiving a single purchase order line item against a purchase order."""
|
"""A serializer for receiving a single purchase order line item against a purchase order."""
|
||||||
@@ -956,7 +1046,6 @@ class PurchaseOrderReceiveSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class SalesOrderSerializer(
|
class SalesOrderSerializer(
|
||||||
FilterableSerializerMixin,
|
|
||||||
NotesFieldMixin,
|
NotesFieldMixin,
|
||||||
TotalPriceMixin,
|
TotalPriceMixin,
|
||||||
InvenTreeCustomStatusSerializerMixin,
|
InvenTreeCustomStatusSerializerMixin,
|
||||||
@@ -1047,7 +1136,6 @@ class SalesOrderIssueSerializer(OrderAdjustSerializer):
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class SalesOrderLineItemSerializer(
|
class SalesOrderLineItemSerializer(
|
||||||
FilterableSerializerMixin,
|
|
||||||
DataImportExportSerializerMixin,
|
DataImportExportSerializerMixin,
|
||||||
AbstractLineItemSerializer,
|
AbstractLineItemSerializer,
|
||||||
InvenTreeModelSerializer,
|
InvenTreeModelSerializer,
|
||||||
@@ -1058,29 +1146,22 @@ class SalesOrderLineItemSerializer(
|
|||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
model = order.models.SalesOrderLineItem
|
model = order.models.SalesOrderLineItem
|
||||||
fields = [
|
fields = AbstractLineItemSerializer.line_fields([
|
||||||
'pk',
|
|
||||||
'allocated',
|
'allocated',
|
||||||
'customer_detail',
|
'customer_detail',
|
||||||
'quantity',
|
|
||||||
'reference',
|
|
||||||
'notes',
|
|
||||||
'order',
|
|
||||||
'order_detail',
|
|
||||||
'overdue',
|
'overdue',
|
||||||
'part',
|
'part',
|
||||||
'part_detail',
|
'part_detail',
|
||||||
'sale_price',
|
'sale_price',
|
||||||
'sale_price_currency',
|
'sale_price_currency',
|
||||||
'shipped',
|
'shipped',
|
||||||
'target_date',
|
|
||||||
'link',
|
|
||||||
# Annotated fields for part stocking information
|
# Annotated fields for part stocking information
|
||||||
'available_stock',
|
'available_stock',
|
||||||
'available_variant_stock',
|
'available_variant_stock',
|
||||||
'building',
|
'building',
|
||||||
'on_order',
|
'on_order',
|
||||||
]
|
# Filterable detail fields
|
||||||
|
])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
@@ -1795,7 +1876,7 @@ class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class SalesOrderExtraLineSerializer(
|
class SalesOrderExtraLineSerializer(
|
||||||
FilterableSerializerMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||||
):
|
):
|
||||||
"""Serializer for a SalesOrderExtraLine object."""
|
"""Serializer for a SalesOrderExtraLine object."""
|
||||||
|
|
||||||
@@ -1803,6 +1884,7 @@ class SalesOrderExtraLineSerializer(
|
|||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
model = order.models.SalesOrderExtraLine
|
model = order.models.SalesOrderExtraLine
|
||||||
|
fields = AbstractExtraLineSerializer.extra_line_fields([])
|
||||||
|
|
||||||
order_detail = enable_filter(
|
order_detail = enable_filter(
|
||||||
SalesOrderSerializer(
|
SalesOrderSerializer(
|
||||||
@@ -1813,7 +1895,6 @@ class SalesOrderExtraLineSerializer(
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class ReturnOrderSerializer(
|
class ReturnOrderSerializer(
|
||||||
FilterableSerializerMixin,
|
|
||||||
NotesFieldMixin,
|
NotesFieldMixin,
|
||||||
InvenTreeCustomStatusSerializerMixin,
|
InvenTreeCustomStatusSerializerMixin,
|
||||||
AbstractOrderSerializer,
|
AbstractOrderSerializer,
|
||||||
@@ -2004,7 +2085,6 @@ class ReturnOrderReceiveSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class ReturnOrderLineItemSerializer(
|
class ReturnOrderLineItemSerializer(
|
||||||
FilterableSerializerMixin,
|
|
||||||
DataImportExportSerializerMixin,
|
DataImportExportSerializerMixin,
|
||||||
AbstractLineItemSerializer,
|
AbstractLineItemSerializer,
|
||||||
InvenTreeModelSerializer,
|
InvenTreeModelSerializer,
|
||||||
@@ -2015,24 +2095,16 @@ class ReturnOrderLineItemSerializer(
|
|||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
model = order.models.ReturnOrderLineItem
|
model = order.models.ReturnOrderLineItem
|
||||||
fields = [
|
fields = AbstractLineItemSerializer.line_fields([
|
||||||
'pk',
|
|
||||||
'order',
|
|
||||||
'order_detail',
|
|
||||||
'item',
|
'item',
|
||||||
'item_detail',
|
|
||||||
'quantity',
|
|
||||||
'received_date',
|
'received_date',
|
||||||
'outcome',
|
'outcome',
|
||||||
'part_detail',
|
|
||||||
'price',
|
'price',
|
||||||
'price_currency',
|
'price_currency',
|
||||||
'link',
|
# Filterable detail fields
|
||||||
'reference',
|
'item_detail',
|
||||||
'notes',
|
'part_detail',
|
||||||
'target_date',
|
])
|
||||||
'link',
|
|
||||||
]
|
|
||||||
|
|
||||||
order_detail = enable_filter(
|
order_detail = enable_filter(
|
||||||
ReturnOrderSerializer(
|
ReturnOrderSerializer(
|
||||||
@@ -2062,7 +2134,7 @@ class ReturnOrderLineItemSerializer(
|
|||||||
|
|
||||||
@register_importer()
|
@register_importer()
|
||||||
class ReturnOrderExtraLineSerializer(
|
class ReturnOrderExtraLineSerializer(
|
||||||
FilterableSerializerMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||||
):
|
):
|
||||||
"""Serializer for a ReturnOrderExtraLine object."""
|
"""Serializer for a ReturnOrderExtraLine object."""
|
||||||
|
|
||||||
@@ -2070,6 +2142,7 @@ class ReturnOrderExtraLineSerializer(
|
|||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
model = order.models.ReturnOrderExtraLine
|
model = order.models.ReturnOrderExtraLine
|
||||||
|
fields = AbstractExtraLineSerializer.extra_line_fields([])
|
||||||
|
|
||||||
order_detail = enable_filter(
|
order_detail = enable_filter(
|
||||||
ReturnOrderSerializer(
|
ReturnOrderSerializer(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { IconUsers } from '@tabler/icons-react';
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
import type { ApiFormFieldSet } from '@lib/types/Forms';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
import type {
|
import type {
|
||||||
StatusCodeInterface,
|
StatusCodeInterface,
|
||||||
StatusCodeListInterface
|
StatusCodeListInterface
|
||||||
@@ -83,6 +84,9 @@ export function extraLineItemFields(): ApiFormFieldSet {
|
|||||||
quantity: {},
|
quantity: {},
|
||||||
price: {},
|
price: {},
|
||||||
price_currency: {},
|
price_currency: {},
|
||||||
|
project_code: {
|
||||||
|
description: t`Select project code for this line item`
|
||||||
|
},
|
||||||
notes: {},
|
notes: {},
|
||||||
link: {}
|
link: {}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ export function usePurchaseOrderLineItemFields({
|
|||||||
value: autoPricing,
|
value: autoPricing,
|
||||||
onValueChange: setAutoPricing
|
onValueChange: setAutoPricing
|
||||||
},
|
},
|
||||||
|
project_code: {
|
||||||
|
description: t`Select project code for this line item`
|
||||||
|
},
|
||||||
target_date: {
|
target_date: {
|
||||||
icon: <IconCalendar />
|
icon: <IconCalendar />
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ export function useReturnOrderLineItemFields({
|
|||||||
},
|
},
|
||||||
price: {},
|
price: {},
|
||||||
price_currency: {},
|
price_currency: {},
|
||||||
|
project_code: {
|
||||||
|
description: t`Select project code for this line item`
|
||||||
|
},
|
||||||
target_date: {},
|
target_date: {},
|
||||||
notes: {},
|
notes: {},
|
||||||
link: {}
|
link: {}
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ export function useSalesOrderLineItemFields({
|
|||||||
value: partCurrency,
|
value: partCurrency,
|
||||||
onValueChange: setPartCurrency
|
onValueChange: setPartCurrency
|
||||||
},
|
},
|
||||||
|
project_code: {
|
||||||
|
description: t`Select project code for this line item`
|
||||||
|
},
|
||||||
target_date: {},
|
target_date: {},
|
||||||
notes: {},
|
notes: {},
|
||||||
link: {}
|
link: {}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ import {
|
|||||||
DecimalColumn,
|
DecimalColumn,
|
||||||
DescriptionColumn,
|
DescriptionColumn,
|
||||||
LinkColumn,
|
LinkColumn,
|
||||||
NoteColumn
|
NoteColumn,
|
||||||
|
ProjectCodeColumn
|
||||||
} from '../ColumnRenderers';
|
} from '../ColumnRenderers';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
|
|
||||||
@@ -75,6 +76,7 @@ export default function ExtraLineItemTable({
|
|||||||
multiplier: record.quantity
|
multiplier: record.quantity
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
ProjectCodeColumn({}),
|
||||||
NoteColumn({
|
NoteColumn({
|
||||||
accessor: 'notes'
|
accessor: 'notes'
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import {
|
|||||||
LocationColumn,
|
LocationColumn,
|
||||||
NoteColumn,
|
NoteColumn,
|
||||||
PartColumn,
|
PartColumn,
|
||||||
|
ProjectCodeColumn,
|
||||||
ReferenceColumn,
|
ReferenceColumn,
|
||||||
TargetDateColumn
|
TargetDateColumn
|
||||||
} from '../ColumnRenderers';
|
} from '../ColumnRenderers';
|
||||||
@@ -150,6 +151,7 @@ export function PurchaseOrderLineItemTable({
|
|||||||
accessor: 'part_detail.description'
|
accessor: 'part_detail.description'
|
||||||
}),
|
}),
|
||||||
ReferenceColumn({}),
|
ReferenceColumn({}),
|
||||||
|
ProjectCodeColumn({}),
|
||||||
{
|
{
|
||||||
accessor: 'build_order',
|
accessor: 'build_order',
|
||||||
title: t`Build Order`,
|
title: t`Build Order`,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import {
|
|||||||
LinkColumn,
|
LinkColumn,
|
||||||
NoteColumn,
|
NoteColumn,
|
||||||
PartColumn,
|
PartColumn,
|
||||||
|
ProjectCodeColumn,
|
||||||
ReferenceColumn,
|
ReferenceColumn,
|
||||||
StatusColumn
|
StatusColumn
|
||||||
} from '../ColumnRenderers';
|
} from '../ColumnRenderers';
|
||||||
@@ -137,6 +138,7 @@ export default function ReturnOrderLineItemTable({
|
|||||||
title: t`Status`
|
title: t`Status`
|
||||||
}),
|
}),
|
||||||
ReferenceColumn({}),
|
ReferenceColumn({}),
|
||||||
|
ProjectCodeColumn({}),
|
||||||
StatusColumn({
|
StatusColumn({
|
||||||
model: ModelType.returnorderlineitem,
|
model: ModelType.returnorderlineitem,
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import {
|
|||||||
DecimalColumn,
|
DecimalColumn,
|
||||||
DescriptionColumn,
|
DescriptionColumn,
|
||||||
LinkColumn,
|
LinkColumn,
|
||||||
|
ProjectCodeColumn,
|
||||||
RenderPartColumn
|
RenderPartColumn
|
||||||
} from '../ColumnRenderers';
|
} from '../ColumnRenderers';
|
||||||
import { InvenTreeTable } from '../InvenTreeTable';
|
import { InvenTreeTable } from '../InvenTreeTable';
|
||||||
@@ -106,6 +107,7 @@ export default function SalesOrderLineItemTable({
|
|||||||
sortable: false,
|
sortable: false,
|
||||||
switchable: true
|
switchable: true
|
||||||
},
|
},
|
||||||
|
ProjectCodeColumn({}),
|
||||||
DecimalColumn({
|
DecimalColumn({
|
||||||
accessor: 'quantity',
|
accessor: 'quantity',
|
||||||
sortable: true
|
sortable: true
|
||||||
|
|||||||
Reference in New Issue
Block a user