Add "target_date" for Build model

- Add "overdue" status to Build serializer
This commit is contained in:
Oliver Walters 2020-12-15 23:24:37 +11:00
parent a7d825158c
commit 802dd5174c
6 changed files with 108 additions and 28 deletions

View File

@ -46,6 +46,8 @@ class BuildList(generics.ListCreateAPIView):
queryset = super().get_queryset().prefetch_related('part') queryset = super().get_queryset().prefetch_related('part')
queryset = BuildSerializer.annotate_queryset(queryset)
return queryset return queryset
def filter_queryset(self, queryset): def filter_queryset(self, queryset):

View File

@ -40,6 +40,7 @@ class EditBuildForm(HelperForm):
'part', 'part',
'quantity', 'quantity',
'batch', 'batch',
'target_date',
'take_from', 'take_from',
'destination', 'destination',
'parent', 'parent',

View File

@ -0,0 +1,18 @@
# Generated by Django 3.0.7 on 2020-12-15 12:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0024_auto_20201201_1023'),
]
operations = [
migrations.AddField(
model_name='build',
name='target_date',
field=models.DateField(blank=True, help_text='Target date for build completion. Build will be overdue after this date.', null=True, verbose_name='Target completion date'),
),
]

View File

@ -47,7 +47,8 @@ class Build(MPTTModel):
status: Build status code status: Build status code
batch: Batch code transferred to build parts (optional) batch: Batch code transferred to build parts (optional)
creation_date: Date the build was created (auto) creation_date: Date the build was created (auto)
completion_date: Date the build was completed target_date: Date the build will be overdue
completion_date: Date the build was completed (or, if incomplete, the expected date of completion)
link: External URL for extra information link: External URL for extra information
notes: Text notes notes: Text notes
""" """
@ -164,6 +165,12 @@ class Build(MPTTModel):
creation_date = models.DateField(auto_now_add=True, editable=False) creation_date = models.DateField(auto_now_add=True, editable=False)
target_date = models.DateField(
null=True, blank=True,
verbose_name=_('Target completion date'),
help_text=_('Target date for build completion. Build will be overdue after this date.')
)
completion_date = models.DateField(null=True, blank=True) completion_date = models.DateField(null=True, blank=True)
completed_by = models.ForeignKey( completed_by = models.ForeignKey(
@ -183,6 +190,22 @@ class Build(MPTTModel):
blank=True, help_text=_('Extra build notes') blank=True, help_text=_('Extra build notes')
) )
def is_overdue(self):
"""
Returns true if this build is "overdue":
- Not completed
- Target date is "in the past"
"""
# Cannot be deemed overdue if target_date is not set
if self.target_date is None:
return False
today = datetime.now().date()
return self.active and self.target_date < today
@property @property
def active(self): def active(self):
""" """

View File

@ -5,12 +5,21 @@ JSON serializers for Build API
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime
from django.db.models import Q
from django.db.models import Case, When, Value
from django.db.models import BooleanField
from rest_framework import serializers from rest_framework import serializers
from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeModelSerializer
from InvenTree.status_codes import BuildStatus
from stock.serializers import StockItemSerializerBrief from stock.serializers import StockItemSerializerBrief
from part.serializers import PartBriefSerializer
from .models import Build, BuildItem from .models import Build, BuildItem
from part.serializers import PartBriefSerializer
class BuildSerializer(InvenTreeModelSerializer): class BuildSerializer(InvenTreeModelSerializer):
@ -23,6 +32,38 @@ class BuildSerializer(InvenTreeModelSerializer):
quantity = serializers.FloatField() quantity = serializers.FloatField()
overdue = serializers.BooleanField()
@staticmethod
def annotate_queryset(queryset):
"""
Add custom annotations to the BuildSerializer queryset,
performing database queries as efficiently as possible.
The following annoted fields are added:
- overdue: True if the build is outstanding *and* the completion date has past
"""
# Annotate a boolean 'overdue' flag
# Construct a filter for finding overdue builds
today = datetime.datetime.now().date()
overdue = Q(status__in=BuildStatus.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=today)
queryset = queryset.annotate(
overdue=Case(
When(
overdue, then=Value(True, output_field=BooleanField()),
),
default=Value(False, output_field=BooleanField())
)
)
return queryset
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
part_detail = kwargs.pop('part_detail', False) part_detail = kwargs.pop('part_detail', False)
@ -42,11 +83,13 @@ class BuildSerializer(InvenTreeModelSerializer):
'completion_date', 'completion_date',
'part', 'part',
'part_detail', 'part_detail',
'overdue',
'reference', 'reference',
'sales_order', 'sales_order',
'quantity', 'quantity',
'status', 'status',
'status_text', 'status_text',
'target_date',
'notes', 'notes',
'link', 'link',
] ]

View File

@ -95,33 +95,26 @@
<td>{% trans "Created" %}</td> <td>{% trans "Created" %}</td>
<td>{{ build.creation_date }}</td> <td>{{ build.creation_date }}</td>
</tr> </tr>
</table>
</div>
<div class='col-sm-6'>
<table class='table table-striped'>
<col width='25'>
<tr> <tr>
<td><span class='fas fa-dollar-sign'></span></td> <td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "BOM Price" %}</td> <td>{% trans "Target Date" %}</td>
{% if build.target_date %}
<td> <td>
{% if bom_price %} {{ build.target_date }}{% if build.is_overdue %} <span class='fas fa-calendar-times icon-red'></span>{% endif %}
{{ bom_price }}
{% if build.part.has_complete_bom_pricing == False %}
<br><span class='warning-msg'><i>{% trans "BOM pricing is incomplete" %}</i></span>
{% endif %}
{% else %}
<span class='warning-msg'><i>{% trans "No pricing information" %}</i></span>
{% endif %}
</td> </td>
{% else %}
<td><i>{% trans "No target date set" %}</i></td>
{% endif %}
</tr> </tr>
{% if build.completion_date %}
<tr> <tr>
<td><span class='fas fa-calendar-alt'></span></td> <td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Completed" %}</td> <td>{% trans "Completed" %}</td>
{% if build.completion_date %}
<td>{{ build.completion_date }}{% if build.completed_by %}<span class='badge'>{{ build.completed_by }}</span>{% endif %}</td> <td>{{ build.completion_date }}{% if build.completed_by %}<span class='badge'>{{ build.completed_by }}</span>{% endif %}</td>
</tr> {% else %}
<td><i>{% trans "Build not complete" %}</i></td>
{% endif %} {% endif %}
</tr>
</table> </table>
</div> </div>
</div> </div>