Merge pull request #1131 from SchrodingersGat/purchase-price-fix

Fix for StockItem creation form
This commit is contained in:
Oliver 2020-11-17 16:07:31 +11:00 committed by GitHub
commit 4daf291619
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 115 deletions

View File

@ -388,6 +388,7 @@ class AjaxCreateView(AjaxMixin, CreateView):
# Save the object to the database # Save the object to the database
self.object = self.save(self.form) self.object = self.save(self.form)
if self.object:
# Return the PK of the newly-created object # Return the PK of the newly-created object
data['pk'] = self.object.pk data['pk'] = self.object.pk
data['text'] = str(self.object) data['text'] = str(self.object)

View File

@ -124,11 +124,11 @@ class CreateStockItemForm(HelperForm):
fields = [ fields = [
'part', 'part',
'supplier_part', 'supplier_part',
'purchase_price',
'location', 'location',
'quantity', 'quantity',
'batch', 'batch',
'serial_numbers', 'serial_numbers',
'purchase_price',
'link', 'link',
'delete_on_deplete', 'delete_on_deplete',
'status', 'status',

View File

@ -0,0 +1,19 @@
# Generated by Django 3.0.7 on 2020-11-17 03:53
from django.db import migrations
import djmoney.models.fields
class Migration(migrations.Migration):
dependencies = [
('stock', '0054_remove_stockitem_build_order'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='purchase_price',
field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency='USD', help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, verbose_name='Purchase Price'),
),
]

View File

@ -453,6 +453,7 @@ class StockItem(MPTTModel):
max_digits=19, max_digits=19,
decimal_places=4, decimal_places=4,
default_currency='USD', default_currency='USD',
blank=True,
null=True, null=True,
verbose_name=_('Purchase Price'), verbose_name=_('Purchase Price'),
help_text=_('Single unit purchase price at time of purchase'), help_text=_('Single unit purchase price at time of purchase'),

View File

@ -1607,127 +1607,101 @@ class StockItemCreate(AjaxCreateView):
return initials return initials
def post(self, request, *args, **kwargs): def validate(self, item, form):
""" Handle POST of StockItemCreate form. """
Extra form validation steps
- Manage serial-number valdiation for tracked parts
""" """
part = None data = form.cleaned_data
form = self.get_form() part = data['part']
data = {} quantity = data.get('quantity', None)
valid = form.is_valid() if not quantity:
return
if valid:
part_id = form['part'].value()
try: try:
part = Part.objects.get(id=part_id) quantity = Decimal(quantity)
quantity = Decimal(form['quantity'].value()) except (ValueError, InvalidOperation):
form.add_error('quantity', _('Invalid quantity provided'))
except (Part.DoesNotExist, ValueError, InvalidOperation): return
part = None
quantity = 1
valid = False
form.add_error('quantity', _('Invalid quantity'))
if quantity < 0: if quantity < 0:
form.add_error('quantity', _('Quantity cannot be less than zero')) form.add_error('quantity', _('Quantity cannot be negative'))
valid = False
if part is None: # Trackable parts are treated differently
form.add_error('part', _('Invalid part selection'))
else:
# A trackable part must provide serial numbesr
if part.trackable: if part.trackable:
sn = request.POST.get('serial_numbers', '') sn = data.get('serial_numbers', '')
sn = str(sn).strip() sn = str(sn).strip()
# If user has specified a range of serial numbers
if len(sn) > 0: if len(sn) > 0:
try:
serials = extract_serial_numbers(sn, quantity) serials = extract_serial_numbers(sn, quantity)
existing = part.find_conflicting_serial_numbers(serials) existing = part.find_conflicting_serial_numbers(serials)
if len(existing) > 0: if len(existing) > 0:
exists = ",".join([str(x) for x in existing]) exists = ','.join([str(x) for x in existing])
form.add_error( form.add_error(
'serial_numbers', 'serial_numbers',
_('Serial numbers already exist') + ': ' + exists _('Serial numbers already exist') + ': ' + exists
) )
valid = False
else: def save(self, form, **kwargs):
# At this point we have a list of serial numbers which we know are valid, """
# and do not currently exist Create a new StockItem based on the provided form data.
form.clean() """
form_data = form.cleaned_data data = form.cleaned_data
if form.is_valid(): part = data['part']
quantity = data['quantity']
if part.trackable:
sn = data.get('serial_numbers', '')
sn = str(sn).strip()
# Create a single stock item for each provided serial number
if len(sn) > 0:
serials = extract_serial_numbers(sn, quantity)
for serial in serials: for serial in serials:
# Create a new stock item for each serial number
item = StockItem( item = StockItem(
part=part, part=part,
quantity=1, quantity=1,
serial=serial, serial=serial,
supplier_part=form_data.get('supplier_part'), supplier_part=data.get('supplier_part', None),
location=form_data.get('location'), location=data.get('location', None),
batch=form_data.get('batch'), batch=data.get('batch', None),
delete_on_deplete=False, delete_on_deplete=False,
status=form_data.get('status'), status=data.get('status'),
link=form_data.get('link'), link=data.get('link', ''),
) )
item.save(user=request.user) item.save(user=self.request.user)
data['success'] = _('Created {n} new stock items'.format(n=len(serials)))
valid = True
except ValidationError as e:
form.add_error('serial_numbers', e.messages)
valid = False
# Create a single StockItem of the specified quantity
else: else:
# We have a serialized part, but no serial numbers specified...
form.clean()
form._post_clean() form._post_clean()
if form.is_valid():
item = form.save(commit=False) item = form.save(commit=False)
item.save(user=request.user) item.user = self.request.user
item.save()
data['pk'] = item.pk return item
data['url'] = item.get_absolute_url()
data['success'] = _("Created new stock item")
valid = True # Non-trackable part
else:
else: # Referenced Part object is not marked as "trackable"
# For non-serialized items, simply save the form.
# We need to call _post_clean() here because it is prevented in the form implementation
form.clean()
form._post_clean() form._post_clean()
if form.is_valid:
item = form.save(commit=False) item = form.save(commit=False)
item.save(user=request.user) item.user = self.request.user
item.save()
data['pk'] = item.pk return item
data['url'] = item.get_absolute_url()
data['success'] = _("Created new stock item")
valid = True
data['form_valid'] = valid and form.is_valid()
return self.renderJsonResponse(request, form, data=data)
class StockLocationDelete(AjaxDeleteView): class StockLocationDelete(AjaxDeleteView):