Build completion now handles unique serial numbers

Provide a method to test if a serial number matches for a given part
This commit is contained in:
Oliver Walters 2019-07-22 15:55:36 +10:00
parent 9a8e4d25d2
commit 23d03d6b9b
4 changed files with 93 additions and 27 deletions

View File

@ -199,7 +199,7 @@ class Build(models.Model):
build_item.save()
@transaction.atomic
def completeBuild(self, location, user):
def completeBuild(self, location, serial_numbers, user):
""" Mark the Build as COMPLETE
- Takes allocated items from stock
@ -227,19 +227,36 @@ class Build(models.Model):
self.completed_by = user
# Add stock of the newly created item
item = StockItem.objects.create(
part=self.part,
location=location,
quantity=self.quantity,
batch=str(self.batch) if self.batch else '',
notes='Built {q} on {now}'.format(
q=self.quantity,
now=str(datetime.now().date())
)
notes = 'Built {q} on {now}'.format(
q=self.quantity,
now=str(datetime.now().date())
)
item.save()
if self.part.trackable:
# Add new serial numbers
for serial in serial_numbers:
item = StockItem.objects.create(
part=self.part,
location=location,
quantity=1,
serial=serial,
batch=str(self.batch) if self.batch else '',
notes=notes
)
item.save()
else:
# Add stock of the newly created item
item = StockItem.objects.create(
part=self.part,
location=location,
quantity=self.quantity,
batch=str(self.batch) if self.batch else '',
notes=notes
)
item.save()
# Finally, mark the build as complete
self.status = BuildStatus.COMPLETE

View File

@ -225,7 +225,7 @@ class BuildComplete(AjaxUpdateView):
build = Build.objects.get(id=self.kwargs['pk'])
context = {}
# Build object
context['build'] = build
@ -263,21 +263,35 @@ class BuildComplete(AjaxUpdateView):
except StockLocation.DoesNotExist:
form.errors['location'] = ['Invalid location selected']
valid = False
serials = []
serials = request.POST.get('serial_numbers', '')
if build.part.trackable:
# A build for a trackable part must specify serial numbers
try:
serials = ExtractSerialNumbers(serials, build.quantity)
sn = request.POST.get('serial_numbers', '')
print(serials)
try:
# Exctract a list of provided serial numbers
serials = ExtractSerialNumbers(sn, build.quantity)
except ValidationError as e:
form.errors['serial_numbers'] = e.messages
valid = False
existing = []
for serial in serials:
if not StockItem.check_serial_number(build.part, serial):
existing.append(serial)
if len(existing) > 0:
exists = ",".join([str(x) for x in existing])
form.errors['serial_numbers'] = [_('The following serial numbers already exist: {sn}'.format(sn=exists))]
valid = False
except ValidationError as e:
form.errors['serial_numbers'] = e.messages
valid = False
if valid:
build.completeBuild(location, request.user)
build.completeBuild(location, serials, request.user)
data = {
'form_valid': valid,

View File

@ -280,6 +280,7 @@ class Part(models.Model):
else:
return static('/img/blank_image.png')
def validate_unique(self, exclude=None):
""" Validate that a part is 'unique'.
Uniqueness is checked across the following (case insensitive) fields:

View File

@ -92,6 +92,7 @@ class StockItem(models.Model):
location: Where this StockItem is located
quantity: Number of stocked units
batch: Batch number for this StockItem
serial: Unique serial number for this StockItem
URL: Optional URL to link to external resource
updated: Date that this stock item was last updated (auto)
stocktake_date: Date of last stocktake for this item
@ -121,6 +122,32 @@ class StockItem(models.Model):
system=True
)
@classmethod
def check_serial_number(cls, part, serial_number):
""" Check if a new stock item can be created with the provided part_id
Args:
part: The part to be checked
"""
if not part.trackable:
return False
items = StockItem.objects.filter(serial=serial_number)
# Is this part a variant? If so, check S/N across all sibling variants
if part.variant_of is not None:
items = items.filter(part__variant_of=part.variant_of)
else:
items = items.filter(part=part)
# An existing serial number exists
if items.exists():
return False
return True
def validate_unique(self, exclude=None):
super(StockItem, self).validate_unique(exclude)
@ -129,11 +156,18 @@ class StockItem(models.Model):
# across all variants of the same template part
try:
if self.serial is not None and self.part.variant_of is not None:
if StockItem.objects.filter(part__variant_of=self.part.variant_of, serial=self.serial).exclude(id=self.id).exists():
raise ValidationError({
'serial': _('A part with this serial number already exists for template part {part}'.format(part=self.part.variant_of))
})
if self.serial is not None:
# This is a variant part (check S/N across all sibling variants)
if self.part.variant_of is not None:
if StockItem.objects.filter(part__variant_of=self.part.variant_of, serial=self.serial).exclude(id=self.id).exists():
raise ValidationError({
'serial': _('A part with this serial number already exists for template part {part}'.format(part=self.part.variant_of))
})
else:
if StockItem.objects.filter(serial=self.serial).exclude(id=self.id).exists():
raise ValidationError({
'serial': _('A part with this serial number already exists')
})
except Part.DoesNotExist:
pass