[FR] Add last updated column for supplier parts (#4214)

* Move to updated Meta mixin

* [FR] Add last updated column for supplier parts
Fixes #3327

* add updated to table

* bump API version

* add missing migration

* incremetn api ;-)
This commit is contained in:
Matthias Mair 2023-01-25 02:18:52 +01:00 committed by GitHub
parent af0bc90e48
commit 0e0d961205
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 139 additions and 21 deletions

View File

@ -2,10 +2,13 @@
# InvenTree API version # InvenTree API version
INVENTREE_API_VERSION = 88 INVENTREE_API_VERSION = 89
""" """
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
v89 -> 2023-01-25 : https://github.com/inventree/InvenTree/pull/4214
- Adds updated field to SupplierPart API
- Adds API date orddering for supplier part list
v88 -> 2023-01-17: https://github.com/inventree/InvenTree/pull/4225 v88 -> 2023-01-17: https://github.com/inventree/InvenTree/pull/4225
- Adds 'priority' field to Build model and api endpoints - Adds 'priority' field to Build model and api endpoints
v87 -> 2023-01-04 : https://github.com/inventree/InvenTree/pull/4067 v87 -> 2023-01-04 : https://github.com/inventree/InvenTree/pull/4067

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-01-15 14:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('common', '0015_newsfeedentry'),
]
operations = [
migrations.AlterField(
model_name='notificationentry',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'),
),
]

View File

@ -49,6 +49,25 @@ import order.validators
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
class MetaMixin(models.Model):
"""A base class for InvenTree models to include shared meta fields.
Attributes:
- updated: The last time this object was updated
"""
class Meta:
"""Meta options for MetaMixin."""
abstract = True
updated = models.DateTimeField(
verbose_name=_('Updated'),
help_text=_('Timestamp of last update'),
auto_now=True,
null=True,
)
class EmptyURLValidator(URLValidator): class EmptyURLValidator(URLValidator):
"""Validator for filed with url - that can be empty.""" """Validator for filed with url - that can be empty."""
@ -1875,7 +1894,7 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
} }
class PriceBreak(models.Model): class PriceBreak(MetaMixin):
"""Represents a PriceBreak model.""" """Represents a PriceBreak model."""
class Meta: class Meta:
@ -2246,7 +2265,7 @@ class WebhookMessage(models.Model):
) )
class NotificationEntry(models.Model): class NotificationEntry(MetaMixin):
"""A NotificationEntry records the last time a particular notifaction was sent out. """A NotificationEntry records the last time a particular notifaction was sent out.
It is recorded to ensure that notifications are not sent out "too often" to users. It is recorded to ensure that notifications are not sent out "too often" to users.
@ -2272,11 +2291,6 @@ class NotificationEntry(models.Model):
uid = models.IntegerField( uid = models.IntegerField(
) )
updated = models.DateTimeField(
auto_now=True,
null=False,
)
@classmethod @classmethod
def check_recent(cls, key: str, uid: int, delta: timedelta): def check_recent(cls, key: str, uid: int, delta: timedelta):
"""Test if a particular notification has been sent in the specified time period.""" """Test if a particular notification has been sent in the specified time period."""

View File

@ -364,6 +364,7 @@ class SupplierPartList(ListCreateDestroyAPIView):
'packaging', 'packaging',
'pack_size', 'pack_size',
'in_stock', 'in_stock',
'updated',
] ]
ordering_field_aliases = { ordering_field_aliases = {

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-01-15 14:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('company', '0051_alter_supplierpricebreak_price'),
]
operations = [
migrations.AlterField(
model_name='supplierpricebreak',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-01-17 20:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('company', '0052_alter_supplierpricebreak_updated'),
]
operations = [
migrations.AddField(
model_name='supplierpart',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'),
),
]

View File

@ -396,7 +396,7 @@ class SupplierPartManager(models.Manager):
) )
class SupplierPart(InvenTreeBarcodeMixin, models.Model): class SupplierPart(InvenTreeBarcodeMixin, common.models.MetaMixin):
"""Represents a unique part as provided by a Supplier Each SupplierPart is identified by a SKU (Supplier Part Number) Each SupplierPart is also linked to a Part or ManufacturerPart object. A Part may be available from multiple suppliers. """Represents a unique part as provided by a Supplier Each SupplierPart is identified by a SKU (Supplier Part Number) Each SupplierPart is also linked to a Part or ManufacturerPart object. A Part may be available from multiple suppliers.
Attributes: Attributes:
@ -412,6 +412,7 @@ class SupplierPart(InvenTreeBarcodeMixin, models.Model):
lead_time: Supplier lead time lead_time: Supplier lead time
packaging: packaging that the part is supplied in, e.g. "Reel" packaging: packaging that the part is supplied in, e.g. "Reel"
pack_size: Quantity of item supplied in a single pack (e.g. 30ml in a single tube) pack_size: Quantity of item supplied in a single pack (e.g. 30ml in a single tube)
updated: Date that the SupplierPart was last updated
""" """
objects = SupplierPartManager() objects = SupplierPartManager()
@ -683,8 +684,6 @@ class SupplierPriceBreak(common.models.PriceBreak):
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks', verbose_name=_('Part'),) part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks', verbose_name=_('Part'),)
updated = models.DateTimeField(auto_now=True, null=True, verbose_name=_('last updated'))
class Meta: class Meta:
"""Metaclass defines extra model options""" """Metaclass defines extra model options"""
unique_together = ("part", "quantity") unique_together = ("part", "quantity")

View File

@ -282,6 +282,9 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True) url = serializers.CharField(source='get_absolute_url', read_only=True)
# Date fields
updated = serializers.DateTimeField(allow_null=True, read_only=True)
class Meta: class Meta:
"""Metaclass options.""" """Metaclass options."""
@ -309,6 +312,7 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
'supplier', 'supplier',
'supplier_detail', 'supplier_detail',
'url', 'url',
'updated',
] ]
read_only_fields = [ read_only_fields = [

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.16 on 2023-01-15 14:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0092_part_last_stocktake'),
]
operations = [
migrations.AddField(
model_name='partinternalpricebreak',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'),
),
migrations.AddField(
model_name='partsellpricebreak',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'),
),
migrations.AlterField(
model_name='partpricing',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'),
),
]

View File

@ -2275,7 +2275,7 @@ def after_save_part(sender, instance: Part, created, **kwargs):
pass pass
class PartPricing(models.Model): class PartPricing(common.models.MetaMixin):
"""Model for caching min/max pricing information for a particular Part """Model for caching min/max pricing information for a particular Part
It is prohibitively expensive to calculate min/max pricing for a part "on the fly". It is prohibitively expensive to calculate min/max pricing for a part "on the fly".
@ -2785,12 +2785,6 @@ class PartPricing(models.Model):
choices=common.settings.currency_code_mappings(), choices=common.settings.currency_code_mappings(),
) )
updated = models.DateTimeField(
verbose_name=_('Updated'),
help_text=_('Timestamp of last pricing update'),
auto_now=True
)
scheduled_for_update = models.BooleanField( scheduled_for_update = models.BooleanField(
default=False, default=False,
) )

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.16 on 2023-01-15 14:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0091_alter_stockitem_delete_on_deplete'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='updated',
field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'),
),
]

View File

@ -266,7 +266,7 @@ def default_delete_on_deplete():
return True return True
class StockItem(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel): class StockItem(InvenTreeBarcodeMixin, MetadataMixin, common.models.MetaMixin, MPTTModel):
"""A StockItem object represents a quantity of physical instances of a part. """A StockItem object represents a quantity of physical instances of a part.
Attributes: Attributes:
@ -729,8 +729,6 @@ class StockItem(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
default=1 default=1
) )
updated = models.DateField(auto_now=True, null=True)
build = models.ForeignKey( build = models.ForeignKey(
'build.Build', on_delete=models.SET_NULL, 'build.Build', on_delete=models.SET_NULL,
verbose_name=_('Source Build'), verbose_name=_('Source Build'),

View File

@ -1043,6 +1043,11 @@ function loadSupplierPartTable(table, url, options) {
} }
} }
}, },
{
field: 'updated',
title: '{% trans "Last Updated" %}',
sortable: true,
},
{ {
field: 'actions', field: 'actions',
title: '', title: '',