mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
added more translation-strings
This commit is contained in:
parent
c68220a597
commit
20c455384e
@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
@ -123,6 +123,7 @@ class DeleteForm(forms.Form):
|
|||||||
confirm_delete = forms.BooleanField(
|
confirm_delete = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
initial=False,
|
initial=False,
|
||||||
|
label=_('Confirm delete'),
|
||||||
help_text=_('Confirm item deletion')
|
help_text=_('Confirm item deletion')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -155,6 +156,7 @@ class SetPasswordForm(HelperForm):
|
|||||||
required=True,
|
required=True,
|
||||||
initial='',
|
initial='',
|
||||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||||
|
label=_('Enter password'),
|
||||||
help_text=_('Enter new password'))
|
help_text=_('Enter new password'))
|
||||||
|
|
||||||
confirm_password = forms.CharField(max_length=100,
|
confirm_password = forms.CharField(max_length=100,
|
||||||
@ -162,6 +164,7 @@ class SetPasswordForm(HelperForm):
|
|||||||
required=True,
|
required=True,
|
||||||
initial='',
|
initial='',
|
||||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||||
|
label=_('Confirm password'),
|
||||||
help_text=_('Confirm new password'))
|
help_text=_('Confirm new password'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -382,17 +382,17 @@ def extract_serial_numbers(serials, expected_quantity):
|
|||||||
if a < b:
|
if a < b:
|
||||||
for n in range(a, b + 1):
|
for n in range(a, b + 1):
|
||||||
if n in numbers:
|
if n in numbers:
|
||||||
errors.append(_('Duplicate serial: {n}'.format(n=n)))
|
errors.append(_('Duplicate serial: {n}').format(n=n))
|
||||||
else:
|
else:
|
||||||
numbers.append(n)
|
numbers.append(n)
|
||||||
else:
|
else:
|
||||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
errors.append(_("Invalid group: {g}").format(g=group))
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
errors.append(_("Invalid group: {g}").format(g=group))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
errors.append(_("Invalid group: {g}").format(g=group))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -409,7 +409,7 @@ def extract_serial_numbers(serials, expected_quantity):
|
|||||||
|
|
||||||
# The number of extracted serial numbers must match the expected quantity
|
# The number of extracted serial numbers must match the expected quantity
|
||||||
if not expected_quantity == len(numbers):
|
if not expected_quantity == len(numbers):
|
||||||
raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})".format(s=len(numbers), q=expected_quantity))])
|
raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})").format(s=len(numbers), q=expected_quantity)])
|
||||||
|
|
||||||
return numbers
|
return numbers
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ def validate_part_ipn(value):
|
|||||||
match = re.search(pattern, value)
|
match = re.search(pattern, value)
|
||||||
|
|
||||||
if match is None:
|
if match is None:
|
||||||
raise ValidationError(_('IPN must match regex pattern') + " '{pat}'".format(pat=pattern))
|
raise ValidationError(_('IPN must match regex pattern {pat}').format(pat=pattern))
|
||||||
|
|
||||||
|
|
||||||
def validate_build_order_reference(value):
|
def validate_build_order_reference(value):
|
||||||
|
@ -117,6 +117,7 @@ class BuildOutputDeleteForm(HelperForm):
|
|||||||
|
|
||||||
confirm = forms.BooleanField(
|
confirm = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
|
label=_('Confirm'),
|
||||||
help_text=_('Confirm deletion of build output')
|
help_text=_('Confirm deletion of build output')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ class UnallocateBuildForm(HelperForm):
|
|||||||
Form for auto-de-allocation of stock from a build
|
Form for auto-de-allocation of stock from a build
|
||||||
"""
|
"""
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=False, help_text=_('Confirm unallocation of stock'))
|
confirm = forms.BooleanField(required=False, label=_('Confirm'), help_text=_('Confirm unallocation of stock'))
|
||||||
|
|
||||||
output_id = forms.IntegerField(
|
output_id = forms.IntegerField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -162,7 +163,7 @@ class UnallocateBuildForm(HelperForm):
|
|||||||
class AutoAllocateForm(HelperForm):
|
class AutoAllocateForm(HelperForm):
|
||||||
""" Form for auto-allocation of stock to a build """
|
""" Form for auto-allocation of stock to a build """
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=True, help_text=_('Confirm stock allocation'))
|
confirm = forms.BooleanField(required=True, label=_('Confirm'), help_text=_('Confirm stock allocation'))
|
||||||
|
|
||||||
# Keep track of which build output we are interested in
|
# Keep track of which build output we are interested in
|
||||||
output = forms.ModelChoiceField(
|
output = forms.ModelChoiceField(
|
||||||
@ -209,15 +210,17 @@ class CompleteBuildOutputForm(HelperForm):
|
|||||||
|
|
||||||
location = forms.ModelChoiceField(
|
location = forms.ModelChoiceField(
|
||||||
queryset=StockLocation.objects.all(),
|
queryset=StockLocation.objects.all(),
|
||||||
|
label = _('Location'),
|
||||||
help_text=_('Location of completed parts'),
|
help_text=_('Location of completed parts'),
|
||||||
)
|
)
|
||||||
|
|
||||||
confirm_incomplete = forms.BooleanField(
|
confirm_incomplete = forms.BooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
|
label=_('Confirm incomplete'),
|
||||||
help_text=_("Confirm completion with incomplete stock allocation")
|
help_text=_("Confirm completion with incomplete stock allocation")
|
||||||
)
|
)
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=True, help_text=_('Confirm build completion'))
|
confirm = forms.BooleanField(required=True, label=_('Confirm'), help_text=_('Confirm build completion'))
|
||||||
|
|
||||||
output = forms.ModelChoiceField(
|
output = forms.ModelChoiceField(
|
||||||
queryset=StockItem.objects.all(), # Queryset is narrowed in the view
|
queryset=StockItem.objects.all(), # Queryset is narrowed in the view
|
||||||
@ -237,7 +240,7 @@ class CompleteBuildOutputForm(HelperForm):
|
|||||||
class CancelBuildForm(HelperForm):
|
class CancelBuildForm(HelperForm):
|
||||||
""" Form for cancelling a build """
|
""" Form for cancelling a build """
|
||||||
|
|
||||||
confirm_cancel = forms.BooleanField(required=False, help_text=_('Confirm build cancellation'))
|
confirm_cancel = forms.BooleanField(required=False, label=_('Confirm cancel'), help_text=_('Confirm build cancellation'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Build
|
model = Build
|
||||||
@ -251,7 +254,7 @@ class EditBuildItemForm(HelperForm):
|
|||||||
Form for creating (or editing) a BuildItem object.
|
Form for creating (or editing) a BuildItem object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, help_text=_('Select quantity of stock to allocate'))
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'), help_text=_('Select quantity of stock to allocate'))
|
||||||
|
|
||||||
part_id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
part_id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
# Generated by Django 3.0.7 on 2021-04-03 12:10
|
# Generated by Django 3.0.7 on 2021-04-04 20:16
|
||||||
|
|
||||||
import InvenTree.models
|
import InvenTree.models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
|
||||||
@ -9,8 +10,9 @@ import django.db.models.deletion
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
('stock', '0058_stockitem_packaging'),
|
||||||
('users', '0005_owner_model'),
|
('users', '0005_owner_model'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('build', '0026_auto_20210216_1539'),
|
('build', '0026_auto_20210216_1539'),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -25,6 +27,11 @@ class Migration(migrations.Migration):
|
|||||||
name='completion_date',
|
name='completion_date',
|
||||||
field=models.DateField(blank=True, null=True, verbose_name='Completion Date'),
|
field=models.DateField(blank=True, null=True, verbose_name='Completion Date'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='build',
|
||||||
|
name='creation_date',
|
||||||
|
field=models.DateField(auto_now_add=True, verbose_name='Creation Date'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='build',
|
model_name='build',
|
||||||
name='issued_by',
|
name='issued_by',
|
||||||
@ -35,6 +42,26 @@ class Migration(migrations.Migration):
|
|||||||
name='responsible',
|
name='responsible',
|
||||||
field=models.ForeignKey(blank=True, help_text='User responsible for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_responsible', to='users.Owner', verbose_name='Responsible'),
|
field=models.ForeignKey(blank=True, help_text='User responsible for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_responsible', to='users.Owner', verbose_name='Responsible'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='builditem',
|
||||||
|
name='build',
|
||||||
|
field=models.ForeignKey(help_text='Build to allocate parts', on_delete=django.db.models.deletion.CASCADE, related_name='allocated_stock', to='build.Build', verbose_name='Build'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='builditem',
|
||||||
|
name='install_into',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Destination stock item', limit_choices_to={'is_building': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='items_to_install', to='stock.StockItem', verbose_name='Install into'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='builditem',
|
||||||
|
name='quantity',
|
||||||
|
field=models.DecimalField(decimal_places=5, default=1, help_text='Stock quantity to allocate to build', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='builditem',
|
||||||
|
name='stock_item',
|
||||||
|
field=models.ForeignKey(help_text='Source stock item', limit_choices_to={'belongs_to': None, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.StockItem', verbose_name='Stock Item'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='buildorderattachment',
|
model_name='buildorderattachment',
|
||||||
name='attachment',
|
name='attachment',
|
@ -216,7 +216,7 @@ class Build(MPTTModel):
|
|||||||
help_text=_('Batch code for this build output')
|
help_text=_('Batch code for this build output')
|
||||||
)
|
)
|
||||||
|
|
||||||
creation_date = models.DateField(auto_now_add=True, editable=False)
|
creation_date = models.DateField(auto_now_add=True, editable=False, verbose_name=_('Creation Date'))
|
||||||
|
|
||||||
target_date = models.DateField(
|
target_date = models.DateField(
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
@ -1020,14 +1020,14 @@ class BuildItem(models.Model):
|
|||||||
try:
|
try:
|
||||||
# Allocated part must be in the BOM for the master part
|
# Allocated part must be in the BOM for the master part
|
||||||
if self.stock_item.part not in self.build.part.getRequiredParts(recursive=False):
|
if self.stock_item.part not in self.build.part.getRequiredParts(recursive=False):
|
||||||
errors['stock_item'] = [_("Selected stock item not found in BOM for part '{p}'".format(p=self.build.part.full_name))]
|
errors['stock_item'] = [_("Selected stock item not found in BOM for part '{p}'").format(p=self.build.part.full_name)]
|
||||||
|
|
||||||
# Allocated quantity cannot exceed available stock quantity
|
# Allocated quantity cannot exceed available stock quantity
|
||||||
if self.quantity > self.stock_item.quantity:
|
if self.quantity > self.stock_item.quantity:
|
||||||
errors['quantity'] = [_("Allocated quantity ({n}) must not exceed available quantity ({q})".format(
|
errors['quantity'] = [_("Allocated quantity ({n}) must not exceed available quantity ({q})").format(
|
||||||
n=normalize(self.quantity),
|
n=normalize(self.quantity),
|
||||||
q=normalize(self.stock_item.quantity)
|
q=normalize(self.stock_item.quantity)
|
||||||
))]
|
)]
|
||||||
|
|
||||||
# Allocated quantity cannot cause the stock item to be over-allocated
|
# Allocated quantity cannot cause the stock item to be over-allocated
|
||||||
if self.stock_item.quantity - self.stock_item.allocation_count() + self.quantity < self.quantity:
|
if self.stock_item.quantity - self.stock_item.allocation_count() + self.quantity < self.quantity:
|
||||||
@ -1079,6 +1079,7 @@ class BuildItem(models.Model):
|
|||||||
Build,
|
Build,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='allocated_stock',
|
related_name='allocated_stock',
|
||||||
|
verbose_name=_('Build'),
|
||||||
help_text=_('Build to allocate parts')
|
help_text=_('Build to allocate parts')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1086,6 +1087,7 @@ class BuildItem(models.Model):
|
|||||||
'stock.StockItem',
|
'stock.StockItem',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='allocations',
|
related_name='allocations',
|
||||||
|
verbose_name=_('Stock Item'),
|
||||||
help_text=_('Source stock item'),
|
help_text=_('Source stock item'),
|
||||||
limit_choices_to={
|
limit_choices_to={
|
||||||
'sales_order': None,
|
'sales_order': None,
|
||||||
@ -1098,6 +1100,7 @@ class BuildItem(models.Model):
|
|||||||
max_digits=15,
|
max_digits=15,
|
||||||
default=1,
|
default=1,
|
||||||
validators=[MinValueValidator(0)],
|
validators=[MinValueValidator(0)],
|
||||||
|
verbose_name=_('Quantity'),
|
||||||
help_text=_('Stock quantity to allocate to build')
|
help_text=_('Stock quantity to allocate to build')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1106,6 +1109,7 @@ class BuildItem(models.Model):
|
|||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
blank=True, null=True,
|
blank=True, null=True,
|
||||||
related_name='items_to_install',
|
related_name='items_to_install',
|
||||||
|
verbose_name=_('Install into'),
|
||||||
help_text=_('Destination stock item'),
|
help_text=_('Destination stock item'),
|
||||||
limit_choices_to={
|
limit_choices_to={
|
||||||
'is_building': True,
|
'is_building': True,
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
# Generated by Django 3.0.7 on 2021-04-03 12:10
|
# Generated by Django 3.0.7 on 2021-04-03 18:37
|
||||||
|
|
||||||
|
import InvenTree.fields
|
||||||
|
import company.models
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import markdownx.models
|
||||||
|
import stdimage.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
@ -11,6 +16,11 @@ class Migration(migrations.Migration):
|
|||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='company',
|
||||||
|
name='image',
|
||||||
|
field=stdimage.models.StdImageField(blank=True, null=True, upload_to=company.models.rename_company_image, verbose_name='Image'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='company',
|
model_name='company',
|
||||||
name='is_customer',
|
name='is_customer',
|
||||||
@ -26,6 +36,16 @@ class Migration(migrations.Migration):
|
|||||||
name='is_supplier',
|
name='is_supplier',
|
||||||
field=models.BooleanField(default=True, help_text='Do you purchase items from this company?', verbose_name='is supplier'),
|
field=models.BooleanField(default=True, help_text='Do you purchase items from this company?', verbose_name='is supplier'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='company',
|
||||||
|
name='link',
|
||||||
|
field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external company information', verbose_name='Link'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='company',
|
||||||
|
name='notes',
|
||||||
|
field=markdownx.models.MarkdownxField(blank=True, verbose_name='Notes'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='supplierpart',
|
model_name='supplierpart',
|
||||||
name='base_cost',
|
name='base_cost',
|
||||||
@ -41,4 +61,9 @@ class Migration(migrations.Migration):
|
|||||||
name='packaging',
|
name='packaging',
|
||||||
field=models.CharField(blank=True, help_text='Part packaging', max_length=50, null=True, verbose_name='Packaging'),
|
field=models.CharField(blank=True, help_text='Part packaging', max_length=50, null=True, verbose_name='Packaging'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='supplierpricebreak',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='company.SupplierPart', verbose_name='Part'),
|
||||||
|
),
|
||||||
]
|
]
|
@ -114,7 +114,7 @@ class Company(models.Model):
|
|||||||
verbose_name=_('Contact'),
|
verbose_name=_('Contact'),
|
||||||
blank=True, help_text=_('Point of contact'))
|
blank=True, help_text=_('Point of contact'))
|
||||||
|
|
||||||
link = InvenTreeURLField(blank=True, help_text=_('Link to external company information'))
|
link = InvenTreeURLField(blank=True, verbose_name=_('Link'), help_text=_('Link to external company information'))
|
||||||
|
|
||||||
image = StdImageField(
|
image = StdImageField(
|
||||||
upload_to=rename_company_image,
|
upload_to=rename_company_image,
|
||||||
@ -122,9 +122,10 @@ class Company(models.Model):
|
|||||||
blank=True,
|
blank=True,
|
||||||
variations={'thumbnail': (128, 128)},
|
variations={'thumbnail': (128, 128)},
|
||||||
delete_orphans=True,
|
delete_orphans=True,
|
||||||
|
verbose_name=_('Image'),
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = MarkdownxField(blank=True)
|
notes = MarkdownxField(blank=True, verbose_name=_('Notes'))
|
||||||
|
|
||||||
is_customer = models.BooleanField(default=False, verbose_name=_('is customer'), help_text=_('Do you sell items to this company?'))
|
is_customer = models.BooleanField(default=False, verbose_name=_('is customer'), help_text=_('Do you sell items to this company?'))
|
||||||
|
|
||||||
@ -366,11 +367,11 @@ class SupplierPart(models.Model):
|
|||||||
help_text=_('Notes')
|
help_text=_('Notes')
|
||||||
)
|
)
|
||||||
|
|
||||||
base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], verbose_name=('base cost'), help_text=_('Minimum charge (e.g. stocking fee)'))
|
base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], verbose_name=_('base cost'), help_text=_('Minimum charge (e.g. stocking fee)'))
|
||||||
|
|
||||||
packaging = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Packaging'), help_text=_('Part packaging'))
|
packaging = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Packaging'), help_text=_('Part packaging'))
|
||||||
|
|
||||||
multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], verbose_name=_('multiple'), help_text=('Order multiple'))
|
multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], verbose_name=_('multiple'), help_text=_('Order multiple'))
|
||||||
|
|
||||||
# TODO - Reimplement lead-time as a charfield with special validation (pattern matching).
|
# TODO - Reimplement lead-time as a charfield with special validation (pattern matching).
|
||||||
# lead_time = models.DurationField(blank=True, null=True)
|
# lead_time = models.DurationField(blank=True, null=True)
|
||||||
@ -530,7 +531,7 @@ class SupplierPriceBreak(common.models.PriceBreak):
|
|||||||
currency: Reference to the currency of this pricebreak (leave empty for base currency)
|
currency: Reference to the currency of this pricebreak (leave empty for base currency)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks')
|
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks', verbose_name=_('Part'),)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("part", "quantity")
|
unique_together = ("part", "quantity")
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
{% extends "modal_delete_form.html" %}
|
{% extends "modal_delete_form.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block pre_form_content %}
|
{% block pre_form_content %}
|
||||||
|
|
||||||
Are you sure you want to delete company '{{ company.name }}'?
|
{% blocktrans with company.name as name %}Are you sure you want to delete company '{{ name }}'?{% endblocktrans %}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
{% if company.supplied_part_count > 0 %}
|
{% if company.supplied_part_count > 0 %}
|
||||||
<p>There are {{ company.supplied_part_count }} parts sourced from this company.<br>
|
<p>{% blocktrans with company.supplied_part_count as count %}There are {{ count }} parts sourced from this company.<br>
|
||||||
If this supplier is deleted, these supplier part entries will also be deleted.</p>
|
If this supplier is deleted, these supplier part entries will also be deleted.{% endblocktrans %}</p>
|
||||||
<ul class='list-group'>
|
<ul class='list-group'>
|
||||||
{% for part in company.parts.all %}
|
{% for part in company.parts.all %}
|
||||||
<li class='list-group-item'><b>{{ part.SKU }}</b> - <i>{{ part.part.full_name }}</i></li>
|
<li class='list-group-item'><b>{{ part.SKU }}</b> - <i>{{ part.part.full_name }}</i></li>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
{% if roles.purchase_order.change %}
|
{% if roles.purchase_order.change %}
|
||||||
<div id='button-toolbar'>
|
<div id='button-toolbar'>
|
||||||
<div class='button-toolbar container-fluid'>
|
<div class='button-toolbar container-fluid'>
|
||||||
<div class='btn-group role='group'>
|
<div class='btn-group' role='group'>
|
||||||
{% if roles.purchase_order.add %}
|
{% if roles.purchase_order.add %}
|
||||||
<button class="btn btn-success" id='part-create' title='{% trans "Create new supplier part" %}'>
|
<button class="btn btn-success" id='part-create' title='{% trans "Create new supplier part" %}'>
|
||||||
<span class='fas fa-plus-circle'></span> {% trans "New Supplier Part" %}
|
<span class='fas fa-plus-circle'></span> {% trans "New Supplier Part" %}
|
||||||
|
@ -126,7 +126,7 @@ class LabelTemplate(models.Model):
|
|||||||
|
|
||||||
width = models.FloatField(
|
width = models.FloatField(
|
||||||
default=50,
|
default=50,
|
||||||
verbose_name=('Width [mm]'),
|
verbose_name=_('Width [mm]'),
|
||||||
help_text=_('Label width, specified in mm'),
|
help_text=_('Label width, specified in mm'),
|
||||||
validators=[MinValueValidator(2)]
|
validators=[MinValueValidator(2)]
|
||||||
)
|
)
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,7 @@ from .models import SalesOrderAllocation
|
|||||||
|
|
||||||
class IssuePurchaseOrderForm(HelperForm):
|
class IssuePurchaseOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=True, initial=False, help_text=_('Place order'))
|
confirm = forms.BooleanField(required=True, initial=False, label=_('Confirm'), help_text=_('Place order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -35,7 +35,7 @@ class IssuePurchaseOrderForm(HelperForm):
|
|||||||
|
|
||||||
class CompletePurchaseOrderForm(HelperForm):
|
class CompletePurchaseOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=True, help_text=_("Mark order as complete"))
|
confirm = forms.BooleanField(required=True, label=_('Confirm'), help_text=_("Mark order as complete"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -46,7 +46,7 @@ class CompletePurchaseOrderForm(HelperForm):
|
|||||||
|
|
||||||
class CancelPurchaseOrderForm(HelperForm):
|
class CancelPurchaseOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=True, help_text=_('Cancel order'))
|
confirm = forms.BooleanField(required=True, label=_('Confirm'), help_text=_('Cancel order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -57,7 +57,7 @@ class CancelPurchaseOrderForm(HelperForm):
|
|||||||
|
|
||||||
class CancelSalesOrderForm(HelperForm):
|
class CancelSalesOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=True, help_text=_('Cancel order'))
|
confirm = forms.BooleanField(required=True, label=_('Confirm'), help_text=_('Cancel order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrder
|
model = SalesOrder
|
||||||
@ -68,7 +68,7 @@ class CancelSalesOrderForm(HelperForm):
|
|||||||
|
|
||||||
class ShipSalesOrderForm(HelperForm):
|
class ShipSalesOrderForm(HelperForm):
|
||||||
|
|
||||||
confirm = forms.BooleanField(required=True, help_text=_('Ship order'))
|
confirm = forms.BooleanField(required=True, label=_('Confirm'), help_text=_('Ship order'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrder
|
model = SalesOrder
|
||||||
@ -79,7 +79,7 @@ class ShipSalesOrderForm(HelperForm):
|
|||||||
|
|
||||||
class ReceivePurchaseOrderForm(HelperForm):
|
class ReceivePurchaseOrderForm(HelperForm):
|
||||||
|
|
||||||
location = TreeNodeChoiceField(queryset=StockLocation.objects.all(), required=True, help_text=_('Receive parts to this location'))
|
location = TreeNodeChoiceField(queryset=StockLocation.objects.all(), required=True, label=_('Location'), help_text=_('Receive parts to this location'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -106,6 +106,7 @@ class EditPurchaseOrderForm(HelperForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
target_date = DatePickerFormField(
|
target_date = DatePickerFormField(
|
||||||
|
label=_('Target Date'),
|
||||||
help_text=_('Target date for order delivery. Order will be overdue after this date.'),
|
help_text=_('Target date for order delivery. Order will be overdue after this date.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -140,6 +141,7 @@ class EditSalesOrderForm(HelperForm):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
target_date = DatePickerFormField(
|
target_date = DatePickerFormField(
|
||||||
|
label=_('Target Date'),
|
||||||
help_text=_('Target date for order completion. Order will be overdue after this date.'),
|
help_text=_('Target date for order completion. Order will be overdue after this date.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -183,7 +185,7 @@ class EditSalesOrderAttachmentForm(HelperForm):
|
|||||||
class EditPurchaseOrderLineItemForm(HelperForm):
|
class EditPurchaseOrderLineItemForm(HelperForm):
|
||||||
""" Form for editing a PurchaseOrderLineItem object """
|
""" Form for editing a PurchaseOrderLineItem object """
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PurchaseOrderLineItem
|
model = PurchaseOrderLineItem
|
||||||
@ -200,7 +202,7 @@ class EditPurchaseOrderLineItemForm(HelperForm):
|
|||||||
class EditSalesOrderLineItemForm(HelperForm):
|
class EditSalesOrderLineItemForm(HelperForm):
|
||||||
""" Form for editing a SalesOrderLineItem object """
|
""" Form for editing a SalesOrderLineItem object """
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrderLineItem
|
model = SalesOrderLineItem
|
||||||
@ -256,7 +258,7 @@ class CreateSalesOrderAllocationForm(HelperForm):
|
|||||||
Form for creating a SalesOrderAllocation item.
|
Form for creating a SalesOrderAllocation item.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrderAllocation
|
model = SalesOrderAllocation
|
||||||
@ -273,7 +275,7 @@ class EditSalesOrderAllocationForm(HelperForm):
|
|||||||
Form for editing a SalesOrderAllocation item
|
Form for editing a SalesOrderAllocation item
|
||||||
"""
|
"""
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SalesOrderAllocation
|
model = SalesOrderAllocation
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
# Generated by Django 3.0.7 on 2021-04-03 12:10
|
|
||||||
|
|
||||||
import InvenTree.models
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
('order', '0043_auto_20210330_0013'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='purchaseorderattachment',
|
|
||||||
name='attachment',
|
|
||||||
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='purchaseorderattachment',
|
|
||||||
name='comment',
|
|
||||||
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='purchaseorderattachment',
|
|
||||||
name='upload_date',
|
|
||||||
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='purchaseorderattachment',
|
|
||||||
name='user',
|
|
||||||
field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='salesorderattachment',
|
|
||||||
name='attachment',
|
|
||||||
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='salesorderattachment',
|
|
||||||
name='comment',
|
|
||||||
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='salesorderattachment',
|
|
||||||
name='upload_date',
|
|
||||||
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='salesorderattachment',
|
|
||||||
name='user',
|
|
||||||
field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
|
||||||
),
|
|
||||||
]
|
|
233
InvenTree/order/migrations/0044_auto_20210404_2016.py
Normal file
233
InvenTree/order/migrations/0044_auto_20210404_2016.py
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2021-04-04 20:16
|
||||||
|
|
||||||
|
import InvenTree.fields
|
||||||
|
import InvenTree.models
|
||||||
|
from django.conf import settings
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import markdownx.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('company', '0032_auto_20210403_1837'),
|
||||||
|
('part', '0063_bomitem_inherited'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('stock', '0058_stockitem_packaging'),
|
||||||
|
('order', '0043_auto_20210330_0013'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='created_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='creation_date',
|
||||||
|
field=models.DateField(blank=True, null=True, verbose_name='Creation Date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(help_text='Order description', max_length=250, verbose_name='Description'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='link',
|
||||||
|
field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='notes',
|
||||||
|
field=markdownx.models.MarkdownxField(blank=True, help_text='Order notes', verbose_name='Notes'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='received_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='received by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='reference',
|
||||||
|
field=models.CharField(help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='supplier',
|
||||||
|
field=models.ForeignKey(help_text='Company from which the items are being ordered', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='company.Company', verbose_name='Supplier'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='supplier_reference',
|
||||||
|
field=models.CharField(blank=True, help_text='Supplier order reference code', max_length=64, verbose_name='Supplier Reference'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderattachment',
|
||||||
|
name='attachment',
|
||||||
|
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderattachment',
|
||||||
|
name='comment',
|
||||||
|
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderattachment',
|
||||||
|
name='upload_date',
|
||||||
|
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderattachment',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderlineitem',
|
||||||
|
name='notes',
|
||||||
|
field=models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderlineitem',
|
||||||
|
name='order',
|
||||||
|
field=models.ForeignKey(help_text='Purchase Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.PurchaseOrder', verbose_name='Order'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderlineitem',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_order_line_items', to='company.SupplierPart', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderlineitem',
|
||||||
|
name='quantity',
|
||||||
|
field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderlineitem',
|
||||||
|
name='received',
|
||||||
|
field=models.DecimalField(decimal_places=5, default=0, help_text='Number of items received', max_digits=15, verbose_name='Received'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorderlineitem',
|
||||||
|
name='reference',
|
||||||
|
field=models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='created_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='creation_date',
|
||||||
|
field=models.DateField(blank=True, null=True, verbose_name='Creation Date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='customer',
|
||||||
|
field=models.ForeignKey(help_text='Company to which the items are being sold', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.Company', verbose_name='Customer'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='customer_reference',
|
||||||
|
field=models.CharField(blank=True, help_text='Customer order reference code', max_length=64, verbose_name='Customer Reference '),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(help_text='Order description', max_length=250, verbose_name='Description'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='link',
|
||||||
|
field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='notes',
|
||||||
|
field=markdownx.models.MarkdownxField(blank=True, help_text='Order notes', verbose_name='Notes'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='reference',
|
||||||
|
field=models.CharField(help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='shipment_date',
|
||||||
|
field=models.DateField(blank=True, null=True, verbose_name='Shipment Date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='shipped_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='shipped by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='status',
|
||||||
|
field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Shipped'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Purchase order status', verbose_name='Status'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderallocation',
|
||||||
|
name='item',
|
||||||
|
field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'part__salable': True, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.StockItem', verbose_name='Item'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderallocation',
|
||||||
|
name='line',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.SalesOrderLineItem', verbose_name='Line'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderallocation',
|
||||||
|
name='quantity',
|
||||||
|
field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Enter stock allocation quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderattachment',
|
||||||
|
name='attachment',
|
||||||
|
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderattachment',
|
||||||
|
name='comment',
|
||||||
|
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderattachment',
|
||||||
|
name='upload_date',
|
||||||
|
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderattachment',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderlineitem',
|
||||||
|
name='notes',
|
||||||
|
field=models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderlineitem',
|
||||||
|
name='order',
|
||||||
|
field=models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.SalesOrder', verbose_name='Order'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderlineitem',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.Part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderlineitem',
|
||||||
|
name='quantity',
|
||||||
|
field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorderlineitem',
|
||||||
|
name='reference',
|
||||||
|
field=models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference'),
|
||||||
|
),
|
||||||
|
]
|
@ -96,18 +96,19 @@ class Order(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
reference = models.CharField(unique=True, max_length=64, blank=False, help_text=_('Order reference'))
|
reference = models.CharField(unique=True, max_length=64, blank=False, verbose_name=_('Reference'), help_text=_('Order reference'))
|
||||||
|
|
||||||
description = models.CharField(max_length=250, help_text=_('Order description'))
|
description = models.CharField(max_length=250, verbose_name=_('Description'), help_text=_('Order description'))
|
||||||
|
|
||||||
link = models.URLField(blank=True, help_text=_('Link to external page'))
|
link = models.URLField(blank=True, verbose_name=_('Link'), help_text=_('Link to external page'))
|
||||||
|
|
||||||
creation_date = models.DateField(blank=True, null=True)
|
creation_date = models.DateField(blank=True, null=True, verbose_name=_('Creation Date'))
|
||||||
|
|
||||||
created_by = models.ForeignKey(User,
|
created_by = models.ForeignKey(User,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
blank=True, null=True,
|
blank=True, null=True,
|
||||||
related_name='+'
|
related_name='+',
|
||||||
|
verbose_name=_('Created By')
|
||||||
)
|
)
|
||||||
|
|
||||||
responsible = models.ForeignKey(
|
responsible = models.ForeignKey(
|
||||||
@ -119,7 +120,7 @@ class Order(models.Model):
|
|||||||
related_name='+',
|
related_name='+',
|
||||||
)
|
)
|
||||||
|
|
||||||
notes = MarkdownxField(blank=True, help_text=_('Order notes'))
|
notes = MarkdownxField(blank=True, verbose_name=_('Notes'), help_text=_('Order notes'))
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrder(Order):
|
class PurchaseOrder(Order):
|
||||||
@ -186,16 +187,18 @@ class PurchaseOrder(Order):
|
|||||||
'is_supplier': True,
|
'is_supplier': True,
|
||||||
},
|
},
|
||||||
related_name='purchase_orders',
|
related_name='purchase_orders',
|
||||||
|
verbose_name=_('Supplier'),
|
||||||
help_text=_('Company from which the items are being ordered')
|
help_text=_('Company from which the items are being ordered')
|
||||||
)
|
)
|
||||||
|
|
||||||
supplier_reference = models.CharField(max_length=64, blank=True, help_text=_("Supplier order reference code"))
|
supplier_reference = models.CharField(max_length=64, blank=True, verbose_name=_('Supplier Reference'), help_text=_("Supplier order reference code"))
|
||||||
|
|
||||||
received_by = models.ForeignKey(
|
received_by = models.ForeignKey(
|
||||||
User,
|
User,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
blank=True, null=True,
|
blank=True, null=True,
|
||||||
related_name='+'
|
related_name='+',
|
||||||
|
verbose_name=_('received by')
|
||||||
)
|
)
|
||||||
|
|
||||||
issue_date = models.DateField(
|
issue_date = models.DateField(
|
||||||
@ -434,13 +437,14 @@ class SalesOrder(Order):
|
|||||||
null=True,
|
null=True,
|
||||||
limit_choices_to={'is_customer': True},
|
limit_choices_to={'is_customer': True},
|
||||||
related_name='sales_orders',
|
related_name='sales_orders',
|
||||||
|
verbose_name=_('Customer'),
|
||||||
help_text=_("Company to which the items are being sold"),
|
help_text=_("Company to which the items are being sold"),
|
||||||
)
|
)
|
||||||
|
|
||||||
status = models.PositiveIntegerField(default=SalesOrderStatus.PENDING, choices=SalesOrderStatus.items(),
|
status = models.PositiveIntegerField(default=SalesOrderStatus.PENDING, choices=SalesOrderStatus.items(),
|
||||||
help_text=_('Purchase order status'))
|
verbose_name=_('Status'), help_text=_('Purchase order status'))
|
||||||
|
|
||||||
customer_reference = models.CharField(max_length=64, blank=True, help_text=_("Customer order reference code"))
|
customer_reference = models.CharField(max_length=64, blank=True, verbose_name=_('Customer Reference '), help_text=_("Customer order reference code"))
|
||||||
|
|
||||||
target_date = models.DateField(
|
target_date = models.DateField(
|
||||||
null=True, blank=True,
|
null=True, blank=True,
|
||||||
@ -448,13 +452,14 @@ class SalesOrder(Order):
|
|||||||
help_text=_('Target date for order completion. Order will be overdue after this date.')
|
help_text=_('Target date for order completion. Order will be overdue after this date.')
|
||||||
)
|
)
|
||||||
|
|
||||||
shipment_date = models.DateField(blank=True, null=True)
|
shipment_date = models.DateField(blank=True, null=True, verbose_name=_('Shipment Date'))
|
||||||
|
|
||||||
shipped_by = models.ForeignKey(
|
shipped_by = models.ForeignKey(
|
||||||
User,
|
User,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
blank=True, null=True,
|
blank=True, null=True,
|
||||||
related_name='+'
|
related_name='+',
|
||||||
|
verbose_name=_('shipped by')
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -585,11 +590,11 @@ class OrderLineItem(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
quantity = RoundingDecimalField(max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], default=1, help_text=_('Item quantity'))
|
quantity = RoundingDecimalField(max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], default=1, verbose_name=_('Quantity'), help_text=_('Item quantity'))
|
||||||
|
|
||||||
reference = models.CharField(max_length=100, blank=True, help_text=_('Line item reference'))
|
reference = models.CharField(max_length=100, blank=True, verbose_name=_('Reference'), help_text=_('Line item reference'))
|
||||||
|
|
||||||
notes = models.CharField(max_length=500, blank=True, help_text=_('Line item notes'))
|
notes = models.CharField(max_length=500, blank=True, verbose_name=_('Notes'), help_text=_('Line item notes'))
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItem(OrderLineItem):
|
class PurchaseOrderLineItem(OrderLineItem):
|
||||||
@ -615,6 +620,7 @@ class PurchaseOrderLineItem(OrderLineItem):
|
|||||||
order = models.ForeignKey(
|
order = models.ForeignKey(
|
||||||
PurchaseOrder, on_delete=models.CASCADE,
|
PurchaseOrder, on_delete=models.CASCADE,
|
||||||
related_name='lines',
|
related_name='lines',
|
||||||
|
verbose_name=_('Order'),
|
||||||
help_text=_('Purchase Order')
|
help_text=_('Purchase Order')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -628,10 +634,11 @@ class PurchaseOrderLineItem(OrderLineItem):
|
|||||||
SupplierPart, on_delete=models.SET_NULL,
|
SupplierPart, on_delete=models.SET_NULL,
|
||||||
blank=True, null=True,
|
blank=True, null=True,
|
||||||
related_name='purchase_order_line_items',
|
related_name='purchase_order_line_items',
|
||||||
|
verbose_name=_('Part'),
|
||||||
help_text=_("Supplier part"),
|
help_text=_("Supplier part"),
|
||||||
)
|
)
|
||||||
|
|
||||||
received = models.DecimalField(decimal_places=5, max_digits=15, default=0, help_text=_('Number of items received'))
|
received = models.DecimalField(decimal_places=5, max_digits=15, default=0, verbose_name=_('Received') , help_text=_('Number of items received'))
|
||||||
|
|
||||||
purchase_price = MoneyField(
|
purchase_price = MoneyField(
|
||||||
max_digits=19,
|
max_digits=19,
|
||||||
@ -657,9 +664,9 @@ class SalesOrderLineItem(OrderLineItem):
|
|||||||
part: Link to a Part object (may be null)
|
part: Link to a Part object (may be null)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='lines', help_text=_('Sales Order'))
|
order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='lines', verbose_name=_('Order'), help_text=_('Sales Order'))
|
||||||
|
|
||||||
part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, help_text=_('Part'), limit_choices_to={'salable': True})
|
part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, verbose_name=_('Part'), help_text=_('Part'), limit_choices_to={'salable': True})
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [
|
unique_together = [
|
||||||
@ -759,7 +766,7 @@ class SalesOrderAllocation(models.Model):
|
|||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
|
||||||
line = models.ForeignKey(SalesOrderLineItem, on_delete=models.CASCADE, related_name='allocations')
|
line = models.ForeignKey(SalesOrderLineItem, on_delete=models.CASCADE, verbose_name=_('Line'), related_name='allocations')
|
||||||
|
|
||||||
item = models.ForeignKey(
|
item = models.ForeignKey(
|
||||||
'stock.StockItem',
|
'stock.StockItem',
|
||||||
@ -770,10 +777,11 @@ class SalesOrderAllocation(models.Model):
|
|||||||
'belongs_to': None,
|
'belongs_to': None,
|
||||||
'sales_order': None,
|
'sales_order': None,
|
||||||
},
|
},
|
||||||
|
verbose_name=_('Item'),
|
||||||
help_text=_('Select stock item to allocate')
|
help_text=_('Select stock item to allocate')
|
||||||
)
|
)
|
||||||
|
|
||||||
quantity = RoundingDecimalField(max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], default=1, help_text=_('Enter stock allocation quantity'))
|
quantity = RoundingDecimalField(max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], default=1, verbose_name=_('Quantity'), help_text=_('Enter stock allocation quantity'))
|
||||||
|
|
||||||
def get_serial(self):
|
def get_serial(self):
|
||||||
return self.item.serial
|
return self.item.serial
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
{% extends "modal_form.html" %}
|
{% extends "modal_form.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block pre_form_content %}
|
{% block pre_form_content %}
|
||||||
|
|
||||||
Mark this order as complete?
|
{% trans 'Mark this order as complete?' %}
|
||||||
{% if not order.is_complete %}
|
{% if not order.is_complete %}
|
||||||
<div class='alert alert-warning alert-block'>
|
<div class='alert alert-warning alert-block'>
|
||||||
This order has line items which have not been marked as received.
|
{%trans 'This order has line items which have not been marked as received.
|
||||||
Marking this order as complete will remove these line items.
|
Marking this order as complete will remove these line items.' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
{% extends "modal_form.html" %}
|
{% extends "modal_form.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block pre_form_content %}
|
{% block pre_form_content %}
|
||||||
|
|
||||||
After placing this purchase order, line items will no longer be editable.
|
{% trans 'After placing this purchase order, line items will no longer be editable.' %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -39,7 +39,7 @@
|
|||||||
{{ part.full_name }} <small><i>{{ part.description }}</i></small>
|
{{ part.full_name }} <small><i>{{ part.description }}</i></small>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button class='btn btn-default btn-create' onClick='newSupplierPartFromOrderWizard()' id='new_supplier_part_{{ part.id }}' part='{{ part.pk }}' title='{% trans "Create new supplier part" $}' type='button'>
|
<button class='btn btn-default btn-create' onClick='newSupplierPartFromOrderWizard()' id='new_supplier_part_{{ part.id }}' part='{{ part.pk }}' title='{% trans "Create new supplier part" %}' type='button'>
|
||||||
<span part='{{ part.pk }}' class='fas fa-plus-circle'></span>
|
<span part='{{ part.pk }}' class='fas fa-plus-circle'></span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
|
@ -67,7 +67,7 @@ function showAllocationSubTable(index, row, element) {
|
|||||||
{
|
{
|
||||||
width: '50%',
|
width: '50%',
|
||||||
field: 'allocated',
|
field: 'allocated',
|
||||||
title: 'Quantity',
|
title: '{% trans "Quantity" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
var text = '';
|
var text = '';
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ function showAllocationSubTable(index, row, element) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'buttons',
|
field: 'buttons',
|
||||||
title: 'Actions',
|
title: '{% trans "Actions" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
|
|
||||||
var html = "<div class='btn-group float-right' role='group'>";
|
var html = "<div class='btn-group float-right' role='group'>";
|
||||||
@ -167,7 +167,7 @@ function showFulfilledSubTable(index, row, element) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$("#so-lines-table").inventreeTable({
|
$("#so-lines-table").inventreeTable({
|
||||||
formatNoMatches: function() { return "No matching line items"; },
|
formatNoMatches: function() { return "{% trans 'No matching line items' %}"; },
|
||||||
queryParams: {
|
queryParams: {
|
||||||
order: {{ order.id }},
|
order: {{ order.id }},
|
||||||
part_detail: true,
|
part_detail: true,
|
||||||
@ -196,7 +196,7 @@ $("#so-lines-table").inventreeTable({
|
|||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
field: 'pk',
|
field: 'pk',
|
||||||
title: 'ID',
|
title: '{% trans "ID" %}',
|
||||||
visible: false,
|
visible: false,
|
||||||
switchable: false,
|
switchable: false,
|
||||||
},
|
},
|
||||||
@ -204,7 +204,7 @@ $("#so-lines-table").inventreeTable({
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
sortName: 'part__name',
|
sortName: 'part__name',
|
||||||
field: 'part',
|
field: 'part',
|
||||||
title: 'Part',
|
title: '{% trans "Part" %}',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
if (row.part) {
|
if (row.part) {
|
||||||
return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${value}/`);
|
return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${value}/`);
|
||||||
@ -216,12 +216,12 @@ $("#so-lines-table").inventreeTable({
|
|||||||
{
|
{
|
||||||
sortable: true,
|
sortable: true,
|
||||||
field: 'reference',
|
field: 'reference',
|
||||||
title: 'Reference'
|
title: '{% trans "Reference" %}'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
sortable: true,
|
sortable: true,
|
||||||
field: 'quantity',
|
field: 'quantity',
|
||||||
title: 'Quantity',
|
title: '{% trans "Quantity" %}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'allocated',
|
field: 'allocated',
|
||||||
@ -261,7 +261,7 @@ $("#so-lines-table").inventreeTable({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'notes',
|
field: 'notes',
|
||||||
title: 'Notes',
|
title: '{% trans "Notes" %}',
|
||||||
},
|
},
|
||||||
{% if order.status == SalesOrderStatus.PENDING %}
|
{% if order.status == SalesOrderStatus.PENDING %}
|
||||||
{
|
{
|
||||||
|
@ -275,7 +275,7 @@ class BomUploadManager:
|
|||||||
elif ext in ['.xls', '.xlsx']:
|
elif ext in ['.xls', '.xlsx']:
|
||||||
raw_data = bom_file.read()
|
raw_data = bom_file.read()
|
||||||
else:
|
else:
|
||||||
raise ValidationError({'bom_file': _('Unsupported file format: {f}'.format(f=ext))})
|
raise ValidationError({'bom_file': _('Unsupported file format: {f}').format(f=ext)})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.data = tablib.Dataset().load(raw_data)
|
self.data = tablib.Dataset().load(raw_data)
|
||||||
|
@ -129,6 +129,7 @@ class BomDuplicateForm(HelperForm):
|
|||||||
|
|
||||||
confirm = forms.BooleanField(
|
confirm = forms.BooleanField(
|
||||||
required=False, initial=False,
|
required=False, initial=False,
|
||||||
|
label=_('Confirm'),
|
||||||
help_text=_('Confirm BOM duplication')
|
help_text=_('Confirm BOM duplication')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -147,7 +148,7 @@ class BomValidateForm(HelperForm):
|
|||||||
to confirm that the BOM for this part is valid
|
to confirm that the BOM for this part is valid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
validate = forms.BooleanField(required=False, initial=False, help_text=_('Confirm that the BOM is correct'))
|
validate = forms.BooleanField(required=False, initial=False, label=_('validate'), help_text=_('Confirm that the BOM is correct'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Part
|
model = Part
|
||||||
@ -159,7 +160,7 @@ class BomValidateForm(HelperForm):
|
|||||||
class BomUploadSelectFile(HelperForm):
|
class BomUploadSelectFile(HelperForm):
|
||||||
""" Form for importing a BOM. Provides a file input box for upload """
|
""" Form for importing a BOM. Provides a file input box for upload """
|
||||||
|
|
||||||
bom_file = forms.FileField(label='BOM file', required=True, help_text=_("Select BOM file to upload"))
|
bom_file = forms.FileField(label=_('BOM file'), required=True, help_text=_("Select BOM file to upload"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Part
|
model = Part
|
||||||
@ -336,9 +337,9 @@ class EditCategoryParameterTemplateForm(HelperForm):
|
|||||||
class EditBomItemForm(HelperForm):
|
class EditBomItemForm(HelperForm):
|
||||||
""" Form for editing a BomItem object """
|
""" Form for editing a BomItem object """
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||||
|
|
||||||
sub_part = PartModelChoiceField(queryset=Part.objects.all())
|
sub_part = PartModelChoiceField(queryset=Part.objects.all(), label=_('Sub part'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BomItem
|
model = BomItem
|
||||||
@ -365,6 +366,7 @@ class PartPriceForm(forms.Form):
|
|||||||
quantity = forms.IntegerField(
|
quantity = forms.IntegerField(
|
||||||
required=True,
|
required=True,
|
||||||
initial=1,
|
initial=1,
|
||||||
|
label=_('Quantity'),
|
||||||
help_text=_('Input quantity for price calculation')
|
help_text=_('Input quantity for price calculation')
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -380,7 +382,7 @@ class EditPartSalePriceBreakForm(HelperForm):
|
|||||||
Form for creating / editing a sale price for a part
|
Form for creating / editing a sale price for a part
|
||||||
"""
|
"""
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PartSellPriceBreak
|
model = PartSellPriceBreak
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
# Generated by Django 3.0.7 on 2021-04-03 12:10
|
|
||||||
|
|
||||||
import InvenTree.models
|
|
||||||
import InvenTree.validators
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import mptt.fields
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('stock', '0059_auto_20210403_1210'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
('part', '0063_bomitem_inherited'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='part',
|
|
||||||
name='bom_checked_by',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='boms_checked', to=settings.AUTH_USER_MODEL, verbose_name='BOM checked by'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='part',
|
|
||||||
name='bom_checked_date',
|
|
||||||
field=models.DateField(blank=True, null=True, verbose_name='BOM checked date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='part',
|
|
||||||
name='bom_checksum',
|
|
||||||
field=models.CharField(blank=True, help_text='Stored BOM checksum', max_length=128, verbose_name='BOM checksum'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='part',
|
|
||||||
name='creation_date',
|
|
||||||
field=models.DateField(auto_now_add=True, null=True, verbose_name='Creation Date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='part',
|
|
||||||
name='creation_user',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_created', to=settings.AUTH_USER_MODEL, verbose_name='Creation User'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='part',
|
|
||||||
name='responsible',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL, verbose_name='Responsible'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partattachment',
|
|
||||||
name='attachment',
|
|
||||||
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partattachment',
|
|
||||||
name='comment',
|
|
||||||
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partattachment',
|
|
||||||
name='upload_date',
|
|
||||||
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partattachment',
|
|
||||||
name='user',
|
|
||||||
field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partcategory',
|
|
||||||
name='default_keywords',
|
|
||||||
field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250, null=True, verbose_name='Default keywords'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partcategory',
|
|
||||||
name='default_location',
|
|
||||||
field=mptt.fields.TreeForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.StockLocation', verbose_name='Default Location'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partcategory',
|
|
||||||
name='description',
|
|
||||||
field=models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partcategory',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partcategory',
|
|
||||||
name='parent',
|
|
||||||
field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory', verbose_name='parent'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partparameter',
|
|
||||||
name='data',
|
|
||||||
field=models.CharField(help_text='Parameter Value', max_length=500, verbose_name='Data'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partparameter',
|
|
||||||
name='part',
|
|
||||||
field=models.ForeignKey(help_text='Parent Part', on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='part.Part', verbose_name='Part'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='partparameter',
|
|
||||||
name='template',
|
|
||||||
field=models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='part.PartParameterTemplate', verbose_name='Template'),
|
|
||||||
),
|
|
||||||
]
|
|
218
InvenTree/part/migrations/0064_auto_20210404_2016.py
Normal file
218
InvenTree/part/migrations/0064_auto_20210404_2016.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2021-04-04 20:16
|
||||||
|
|
||||||
|
import InvenTree.models
|
||||||
|
import InvenTree.validators
|
||||||
|
from django.conf import settings
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import mptt.fields
|
||||||
|
import part.models
|
||||||
|
import stdimage.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('stock', '0058_stockitem_packaging'),
|
||||||
|
('part', '0063_bomitem_inherited'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='checksum',
|
||||||
|
field=models.CharField(blank=True, help_text='BOM line checksum', max_length=128, verbose_name='Checksum'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='note',
|
||||||
|
field=models.CharField(blank=True, help_text='BOM item notes', max_length=500, verbose_name='Note'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='optional',
|
||||||
|
field=models.BooleanField(default=False, help_text='This BOM item is optional', verbose_name='Optional'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='overage',
|
||||||
|
field=models.CharField(blank=True, help_text='Estimated build wastage quantity (absolute or percentage)', max_length=24, validators=[InvenTree.validators.validate_overage], verbose_name='Overage'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='quantity',
|
||||||
|
field=models.DecimalField(decimal_places=5, default=1.0, help_text='BOM quantity for this BOM item', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='reference',
|
||||||
|
field=models.CharField(blank=True, help_text='BOM item reference', max_length=500, verbose_name='Reference'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='bomitem',
|
||||||
|
name='sub_part',
|
||||||
|
field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part', verbose_name='Sub part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='bom_checked_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='boms_checked', to=settings.AUTH_USER_MODEL, verbose_name='BOM checked by'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='bom_checked_date',
|
||||||
|
field=models.DateField(blank=True, null=True, verbose_name='BOM checked date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='bom_checksum',
|
||||||
|
field=models.CharField(blank=True, help_text='Stored BOM checksum', max_length=128, verbose_name='BOM checksum'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='creation_date',
|
||||||
|
field=models.DateField(auto_now_add=True, null=True, verbose_name='Creation Date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='creation_user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_created', to=settings.AUTH_USER_MODEL, verbose_name='Creation User'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='image',
|
||||||
|
field=stdimage.models.StdImageField(blank=True, null=True, upload_to=part.models.rename_part_image, verbose_name='Image'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='part',
|
||||||
|
name='responsible',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL, verbose_name='Responsible'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partattachment',
|
||||||
|
name='attachment',
|
||||||
|
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partattachment',
|
||||||
|
name='comment',
|
||||||
|
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partattachment',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='part.Part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partattachment',
|
||||||
|
name='upload_date',
|
||||||
|
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partattachment',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategory',
|
||||||
|
name='default_keywords',
|
||||||
|
field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250, null=True, verbose_name='Default keywords'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategory',
|
||||||
|
name='default_location',
|
||||||
|
field=mptt.fields.TreeForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.StockLocation', verbose_name='Default Location'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategory',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategory',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategory',
|
||||||
|
name='parent',
|
||||||
|
field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory', verbose_name='parent'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategoryparametertemplate',
|
||||||
|
name='category',
|
||||||
|
field=models.ForeignKey(help_text='Part Category', on_delete=django.db.models.deletion.CASCADE, related_name='parameter_templates', to='part.PartCategory', verbose_name='Category'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategoryparametertemplate',
|
||||||
|
name='default_value',
|
||||||
|
field=models.CharField(blank=True, help_text='Default Parameter Value', max_length=500, verbose_name='Default Value'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partcategoryparametertemplate',
|
||||||
|
name='parameter_template',
|
||||||
|
field=models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='part_categories', to='part.PartParameterTemplate', verbose_name='Parameter Template'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partparameter',
|
||||||
|
name='data',
|
||||||
|
field=models.CharField(help_text='Parameter Value', max_length=500, verbose_name='Data'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partparameter',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(help_text='Parent Part', on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='part.Part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partparameter',
|
||||||
|
name='template',
|
||||||
|
field=models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='part.PartParameterTemplate', verbose_name='Template'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partparametertemplate',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(help_text='Parameter Name', max_length=100, unique=True, verbose_name='Name'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partparametertemplate',
|
||||||
|
name='units',
|
||||||
|
field=models.CharField(blank=True, help_text='Parameter Units', max_length=25, verbose_name='Units'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partrelated',
|
||||||
|
name='part_1',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_1', to='part.Part', verbose_name='Part 1'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partrelated',
|
||||||
|
name='part_2',
|
||||||
|
field=models.ForeignKey(help_text='Select Related Part', on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_2', to='part.Part', verbose_name='Part 2'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partsellpricebreak',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(limit_choices_to={'salable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='salepricebreaks', to='part.Part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partstar',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.Part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='partstar',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_parts', to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='parttesttemplate',
|
||||||
|
name='part',
|
||||||
|
field=models.ForeignKey(limit_choices_to={'trackable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.Part', verbose_name='Part'),
|
||||||
|
),
|
||||||
|
]
|
@ -443,10 +443,10 @@ class Part(MPTTModel):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.pk == parent.pk:
|
if self.pk == parent.pk:
|
||||||
raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)".format(
|
raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)").format(
|
||||||
p1=str(self),
|
p1=str(self),
|
||||||
p2=str(parent)
|
p2=str(parent)
|
||||||
))})
|
)})
|
||||||
|
|
||||||
bom_items = self.get_bom_items()
|
bom_items = self.get_bom_items()
|
||||||
|
|
||||||
@ -455,10 +455,10 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
# Check for simple match
|
# Check for simple match
|
||||||
if item.sub_part == parent:
|
if item.sub_part == parent:
|
||||||
raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)".format(
|
raise ValidationError({'sub_part': _("Part '{p1}' is used in BOM for '{p2}' (recursive)").format(
|
||||||
p1=str(parent),
|
p1=str(parent),
|
||||||
p2=str(self)
|
p2=str(self)
|
||||||
))})
|
)})
|
||||||
|
|
||||||
# And recursively check too
|
# And recursively check too
|
||||||
item.sub_part.checkAddToBOM(parent)
|
item.sub_part.checkAddToBOM(parent)
|
||||||
@ -750,6 +750,7 @@ class Part(MPTTModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
variations={'thumbnail': (128, 128)},
|
variations={'thumbnail': (128, 128)},
|
||||||
delete_orphans=False,
|
delete_orphans=False,
|
||||||
|
verbose_name=_('Image'),
|
||||||
)
|
)
|
||||||
|
|
||||||
default_location = TreeForeignKey(
|
default_location = TreeForeignKey(
|
||||||
@ -1852,7 +1853,7 @@ class PartAttachment(InvenTreeAttachment):
|
|||||||
return os.path.join("part_files", str(self.part.id))
|
return os.path.join("part_files", str(self.part.id))
|
||||||
|
|
||||||
part = models.ForeignKey(Part, on_delete=models.CASCADE,
|
part = models.ForeignKey(Part, on_delete=models.CASCADE,
|
||||||
related_name='attachments')
|
verbose_name=_('Part'), related_name='attachments')
|
||||||
|
|
||||||
|
|
||||||
class PartSellPriceBreak(common.models.PriceBreak):
|
class PartSellPriceBreak(common.models.PriceBreak):
|
||||||
@ -1863,7 +1864,8 @@ class PartSellPriceBreak(common.models.PriceBreak):
|
|||||||
part = models.ForeignKey(
|
part = models.ForeignKey(
|
||||||
Part, on_delete=models.CASCADE,
|
Part, on_delete=models.CASCADE,
|
||||||
related_name='salepricebreaks',
|
related_name='salepricebreaks',
|
||||||
limit_choices_to={'salable': True}
|
limit_choices_to={'salable': True},
|
||||||
|
verbose_name=_('Part')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -1881,9 +1883,9 @@ class PartStar(models.Model):
|
|||||||
user: Link to a User object
|
user: Link to a User object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='starred_users')
|
part = models.ForeignKey(Part, on_delete=models.CASCADE, verbose_name=_('Part'), related_name='starred_users')
|
||||||
|
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='starred_parts')
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('User'), related_name='starred_parts')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ['part', 'user']
|
unique_together = ['part', 'user']
|
||||||
@ -1956,6 +1958,7 @@ class PartTestTemplate(models.Model):
|
|||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='test_templates',
|
related_name='test_templates',
|
||||||
limit_choices_to={'trackable': True},
|
limit_choices_to={'trackable': True},
|
||||||
|
verbose_name=_('Part'),
|
||||||
)
|
)
|
||||||
|
|
||||||
test_name = models.CharField(
|
test_name = models.CharField(
|
||||||
@ -2023,9 +2026,9 @@ class PartParameterTemplate(models.Model):
|
|||||||
except PartParameterTemplate.DoesNotExist:
|
except PartParameterTemplate.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
name = models.CharField(max_length=100, help_text=_('Parameter Name'), unique=True)
|
name = models.CharField(max_length=100, verbose_name=_('Name'), help_text=_('Parameter Name'), unique=True)
|
||||||
|
|
||||||
units = models.CharField(max_length=25, help_text=_('Parameter Units'), blank=True)
|
units = models.CharField(max_length=25, verbose_name=_('Units'), help_text=_('Parameter Units'), blank=True)
|
||||||
|
|
||||||
|
|
||||||
class PartParameter(models.Model):
|
class PartParameter(models.Model):
|
||||||
@ -2096,15 +2099,18 @@ class PartCategoryParameterTemplate(models.Model):
|
|||||||
category = models.ForeignKey(PartCategory,
|
category = models.ForeignKey(PartCategory,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='parameter_templates',
|
related_name='parameter_templates',
|
||||||
|
verbose_name=_('Category'),
|
||||||
help_text=_('Part Category'))
|
help_text=_('Part Category'))
|
||||||
|
|
||||||
parameter_template = models.ForeignKey(PartParameterTemplate,
|
parameter_template = models.ForeignKey(PartParameterTemplate,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='part_categories',
|
related_name='part_categories',
|
||||||
|
verbose_name=_('Parameter Template'),
|
||||||
help_text=_('Parameter Template'))
|
help_text=_('Parameter Template'))
|
||||||
|
|
||||||
default_value = models.CharField(max_length=500,
|
default_value = models.CharField(max_length=500,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
verbose_name=_('Default Value'),
|
||||||
help_text=_('Default Parameter Value'))
|
help_text=_('Default Parameter Value'))
|
||||||
|
|
||||||
|
|
||||||
@ -2133,6 +2139,7 @@ class BomItem(models.Model):
|
|||||||
# A link to the parent part
|
# A link to the parent part
|
||||||
# Each part will get a reverse lookup field 'bom_items'
|
# Each part will get a reverse lookup field 'bom_items'
|
||||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='bom_items',
|
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='bom_items',
|
||||||
|
verbose_name=_('Part'),
|
||||||
help_text=_('Select parent part'),
|
help_text=_('Select parent part'),
|
||||||
limit_choices_to={
|
limit_choices_to={
|
||||||
'assembly': True,
|
'assembly': True,
|
||||||
@ -2141,26 +2148,28 @@ class BomItem(models.Model):
|
|||||||
# A link to the child item (sub-part)
|
# A link to the child item (sub-part)
|
||||||
# Each part will get a reverse lookup field 'used_in'
|
# Each part will get a reverse lookup field 'used_in'
|
||||||
sub_part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='used_in',
|
sub_part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='used_in',
|
||||||
|
verbose_name=_('Sub part'),
|
||||||
help_text=_('Select part to be used in BOM'),
|
help_text=_('Select part to be used in BOM'),
|
||||||
limit_choices_to={
|
limit_choices_to={
|
||||||
'component': True,
|
'component': True,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Quantity required
|
# Quantity required
|
||||||
quantity = models.DecimalField(default=1.0, max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], help_text=_('BOM quantity for this BOM item'))
|
quantity = models.DecimalField(default=1.0, max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], verbose_name=_('Quantity'), help_text=_('BOM quantity for this BOM item'))
|
||||||
|
|
||||||
optional = models.BooleanField(default=False, help_text=_("This BOM item is optional"))
|
optional = models.BooleanField(default=False, verbose_name=_('Optional'), help_text=_("This BOM item is optional"))
|
||||||
|
|
||||||
overage = models.CharField(max_length=24, blank=True, validators=[validators.validate_overage],
|
overage = models.CharField(max_length=24, blank=True, validators=[validators.validate_overage],
|
||||||
|
verbose_name=_('Overage'),
|
||||||
help_text=_('Estimated build wastage quantity (absolute or percentage)')
|
help_text=_('Estimated build wastage quantity (absolute or percentage)')
|
||||||
)
|
)
|
||||||
|
|
||||||
reference = models.CharField(max_length=500, blank=True, help_text=_('BOM item reference'))
|
reference = models.CharField(max_length=500, blank=True, verbose_name=_('Reference'), help_text=_('BOM item reference'))
|
||||||
|
|
||||||
# Note attached to this BOM line item
|
# Note attached to this BOM line item
|
||||||
note = models.CharField(max_length=500, blank=True, help_text=_('BOM item notes'))
|
note = models.CharField(max_length=500, blank=True, verbose_name=_('Note'), help_text=_('BOM item notes'))
|
||||||
|
|
||||||
checksum = models.CharField(max_length=128, blank=True, help_text=_('BOM line checksum'))
|
checksum = models.CharField(max_length=128, blank=True, verbose_name=_('Checksum'), help_text=_('BOM line checksum'))
|
||||||
|
|
||||||
inherited = models.BooleanField(
|
inherited = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
@ -2372,11 +2381,11 @@ class PartRelated(models.Model):
|
|||||||
""" Store and handle related parts (eg. mating connector, crimps, etc.) """
|
""" Store and handle related parts (eg. mating connector, crimps, etc.) """
|
||||||
|
|
||||||
part_1 = models.ForeignKey(Part, related_name='related_parts_1',
|
part_1 = models.ForeignKey(Part, related_name='related_parts_1',
|
||||||
on_delete=models.DO_NOTHING)
|
verbose_name=_('Part 1'), on_delete=models.DO_NOTHING)
|
||||||
|
|
||||||
part_2 = models.ForeignKey(Part, related_name='related_parts_2',
|
part_2 = models.ForeignKey(Part, related_name='related_parts_2',
|
||||||
on_delete=models.DO_NOTHING,
|
on_delete=models.DO_NOTHING,
|
||||||
help_text=_('Select Related Part'))
|
verbose_name=_('Part 2'), help_text=_('Select Related Part'))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.part_1} <--> {self.part_2}'
|
return f'{self.part_1} <--> {self.part_2}'
|
||||||
|
@ -16,13 +16,13 @@
|
|||||||
<div class='alert alert-block alert-info'>
|
<div class='alert alert-block alert-info'>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class='alert alert-block alert-danger'>
|
<div class='alert alert-block alert-danger'>
|
||||||
The BOM for <i>{{ part.full_name }}</i> has changed, and must be validated.<br>
|
{% blocktrans with part=part.full_name %}The BOM for <i>{{ part }}</i> has changed, and must be validated.<br>{% endblocktrans %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
The BOM for <i>{{ part.full_name }}</i> was last checked by {{ part.bom_checked_by }} on {{ part.bom_checked_date }}
|
{% blocktrans with part=part.full_name checker=part.bom_checked_by check_date=part.bom_checked_date %}The BOM for <i>{{ part }}</i> was last checked by {{ checker }} on {{ check_date }}{% endblocktrans %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class='alert alert-danger alert-block'>
|
<div class='alert alert-danger alert-block'>
|
||||||
<b>The BOM for <i>{{ part.full_name }}</i> has not been validated.</b>
|
<b>{% blocktrans with part=part.full_name %}The BOM for <i>{{ part }}</i> has not been validated.{% endblocktrans %}</b>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="post" action='' class='js-modal-form' enctype="multipart/form-data">
|
<form method="post" action='' class='js-modal-form' enctype="multipart/form-data">
|
||||||
<button type="submit" class="save btn btn-default">Upload File</button>
|
<button type="submit" class="save btn btn-default">{% trans 'Upload File' %}</button>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
{% extends "modal_form.html" %}
|
{% extends "modal_form.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
{% block pre_form_content %}
|
{% block pre_form_content %}
|
||||||
Confirm that the Bill of Materials (BOM) is valid for:<br><i>{{ part.full_name }}</i>
|
{% blocktrans with part.full_name as part %}Confirm that the Bill of Materials (BOM) is valid for:<br><i>{{ part }}</i>{% endblocktrans %}
|
||||||
|
|
||||||
<div class='alert alert-warning alert-block'>
|
<div class='alert alert-warning alert-block'>
|
||||||
This will validate each line in the BOM.
|
{% trans 'This will validate each line in the BOM.' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -342,7 +342,7 @@ class PartSetCategory(AjaxUpdateView):
|
|||||||
|
|
||||||
data = {
|
data = {
|
||||||
'form_valid': valid,
|
'form_valid': valid,
|
||||||
'success': _('Set category for {n} parts'.format(n=len(self.parts)))
|
'success': _('Set category for {n} parts').format(n=len(self.parts))
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid:
|
if valid:
|
||||||
|
35
InvenTree/report/migrations/0015_auto_20210403_1837.py
Normal file
35
InvenTree/report/migrations/0015_auto_20210403_1837.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Generated by Django 3.0.7 on 2021-04-03 18:37
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
import report.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('report', '0014_purchaseorderreport_salesorderreport'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='reportasset',
|
||||||
|
name='asset',
|
||||||
|
field=models.FileField(help_text='Report asset file', upload_to=report.models.rename_asset, verbose_name='Asset'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='reportasset',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(help_text='Asset file description', max_length=250, verbose_name='Description'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='reportsnippet',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(help_text='Snippet file description', max_length=250, verbose_name='Description'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='reportsnippet',
|
||||||
|
name='snippet',
|
||||||
|
field=models.FileField(help_text='Report snippet file', upload_to=report.models.rename_snippet, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['html', 'htm'])], verbose_name='Snippet'),
|
||||||
|
),
|
||||||
|
]
|
@ -497,11 +497,12 @@ class ReportSnippet(models.Model):
|
|||||||
|
|
||||||
snippet = models.FileField(
|
snippet = models.FileField(
|
||||||
upload_to=rename_snippet,
|
upload_to=rename_snippet,
|
||||||
|
verbose_name=_('Snippet'),
|
||||||
help_text=_('Report snippet file'),
|
help_text=_('Report snippet file'),
|
||||||
validators=[FileExtensionValidator(allowed_extensions=['html', 'htm'])],
|
validators=[FileExtensionValidator(allowed_extensions=['html', 'htm'])],
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.CharField(max_length=250, help_text=_("Snippet file description"))
|
description = models.CharField(max_length=250, verbose_name=_('Description'), help_text=_("Snippet file description"))
|
||||||
|
|
||||||
|
|
||||||
def rename_asset(instance, filename):
|
def rename_asset(instance, filename):
|
||||||
@ -536,7 +537,8 @@ class ReportAsset(models.Model):
|
|||||||
|
|
||||||
asset = models.FileField(
|
asset = models.FileField(
|
||||||
upload_to=rename_asset,
|
upload_to=rename_asset,
|
||||||
|
verbose_name=_('Asset'),
|
||||||
help_text=_("Report asset file"),
|
help_text=_("Report asset file"),
|
||||||
)
|
)
|
||||||
|
|
||||||
description = models.CharField(max_length=250, help_text=_("Asset file description"))
|
description = models.CharField(max_length=250, verbose_name=_('Description'), help_text=_("Asset file description"))
|
||||||
|
@ -195,7 +195,7 @@ class StockCount(StockAdjust):
|
|||||||
if item['item'].stocktake(item['quantity'], request.user, notes=self.notes):
|
if item['item'].stocktake(item['quantity'], request.user, notes=self.notes):
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
return Response({'success': 'Updated stock for {n} items'.format(n=n)})
|
return Response({'success': _('Updated stock for {n} items').format(n=n)})
|
||||||
|
|
||||||
|
|
||||||
class StockAdd(StockAdjust):
|
class StockAdd(StockAdjust):
|
||||||
@ -264,7 +264,7 @@ class StockTransfer(StockAdjust):
|
|||||||
if item['item'].move(location, self.notes, request.user, quantity=item['quantity']):
|
if item['item'].move(location, self.notes, request.user, quantity=item['quantity']):
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
return Response({'success': 'Moved {n} parts to {loc}'.format(
|
return Response({'success': _('Moved {n} parts to {loc}').format(
|
||||||
n=n,
|
n=n,
|
||||||
loc=str(location),
|
loc=str(location),
|
||||||
)})
|
)})
|
||||||
|
@ -111,7 +111,8 @@ class CreateStockItemForm(HelperForm):
|
|||||||
""" Form for creating a new StockItem """
|
""" Form for creating a new StockItem """
|
||||||
|
|
||||||
expiry_date = DatePickerFormField(
|
expiry_date = DatePickerFormField(
|
||||||
help_text=('Expiration date for this stock item'),
|
label=_('Expiry Date'),
|
||||||
|
help_text=_('Expiration date for this stock item'),
|
||||||
)
|
)
|
||||||
|
|
||||||
serial_numbers = forms.CharField(label=_('Serial Numbers'), required=False, help_text=_('Enter unique serial numbers (or leave blank)'))
|
serial_numbers = forms.CharField(label=_('Serial Numbers'), required=False, help_text=_('Enter unique serial numbers (or leave blank)'))
|
||||||
@ -165,13 +166,13 @@ class CreateStockItemForm(HelperForm):
|
|||||||
class SerializeStockForm(HelperForm):
|
class SerializeStockForm(HelperForm):
|
||||||
""" Form for serializing a StockItem. """
|
""" Form for serializing a StockItem. """
|
||||||
|
|
||||||
destination = TreeNodeChoiceField(queryset=StockLocation.objects.all(), label='Destination', required=True, help_text='Destination for serialized stock (by default, will remain in current location)')
|
destination = TreeNodeChoiceField(queryset=StockLocation.objects.all(), label=_('Destination'), required=True, help_text=_('Destination for serialized stock (by default, will remain in current location)'))
|
||||||
|
|
||||||
serial_numbers = forms.CharField(label='Serial numbers', required=True, help_text='Unique serial numbers (must match quantity)')
|
serial_numbers = forms.CharField(label=_('Serial numbers'), required=True, help_text=_('Unique serial numbers (must match quantity)'))
|
||||||
|
|
||||||
note = forms.CharField(label='Notes', required=False, help_text='Add transaction note (optional)')
|
note = forms.CharField(label=_('Notes'), required=False, help_text=_('Add transaction note (optional)'))
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
@ -263,7 +264,7 @@ class ExportOptionsForm(HelperForm):
|
|||||||
|
|
||||||
file_format = forms.ChoiceField(label=_('File Format'), help_text=_('Select output file format'))
|
file_format = forms.ChoiceField(label=_('File Format'), help_text=_('Select output file format'))
|
||||||
|
|
||||||
include_sublocations = forms.BooleanField(required=False, initial=True, help_text=_("Include stock items in sub locations"))
|
include_sublocations = forms.BooleanField(required=False, initial=True, label=_('Include sublocations'), help_text=_("Include stock items in sub locations"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = StockLocation
|
model = StockLocation
|
||||||
@ -402,7 +403,8 @@ class EditStockItemForm(HelperForm):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
expiry_date = DatePickerFormField(
|
expiry_date = DatePickerFormField(
|
||||||
help_text=('Expiration date for this stock item'),
|
label=_('Expiry Date'),
|
||||||
|
help_text=_('Expiration date for this stock item'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
# Generated by Django 3.0.7 on 2021-04-03 12:10
|
# Generated by Django 3.0.7 on 2021-04-04 20:16
|
||||||
|
|
||||||
import InvenTree.fields
|
import InvenTree.fields
|
||||||
import InvenTree.models
|
import InvenTree.models
|
||||||
import InvenTree.validators
|
import InvenTree.validators
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import django.core.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import mptt.fields
|
import mptt.fields
|
||||||
@ -12,11 +13,22 @@ import mptt.fields
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
('users', '0005_owner_model'),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
('stock', '0058_stockitem_packaging'),
|
('stock', '0058_stockitem_packaging'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stockitem',
|
||||||
|
name='delete_on_deplete',
|
||||||
|
field=models.BooleanField(default=True, help_text='Delete this Stock Item when stock is depleted', verbose_name='Delete on deplete'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stockitem',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='users.Owner', verbose_name='Owner'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='stockitemattachment',
|
model_name='stockitemattachment',
|
||||||
name='attachment',
|
name='attachment',
|
||||||
@ -47,6 +59,11 @@ class Migration(migrations.Migration):
|
|||||||
name='notes',
|
name='notes',
|
||||||
field=models.CharField(blank=True, help_text='Entry notes', max_length=512, verbose_name='Notes'),
|
field=models.CharField(blank=True, help_text='Entry notes', max_length=512, verbose_name='Notes'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stockitemtracking',
|
||||||
|
name='quantity',
|
||||||
|
field=models.DecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='stockitemtracking',
|
model_name='stockitemtracking',
|
||||||
name='title',
|
name='title',
|
||||||
@ -62,6 +79,11 @@ class Migration(migrations.Migration):
|
|||||||
name='name',
|
name='name',
|
||||||
field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'),
|
field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='stocklocation',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_locations', to='users.Owner', verbose_name='Owner'),
|
||||||
|
),
|
||||||
migrations.AlterField(
|
migrations.AlterField(
|
||||||
model_name='stocklocation',
|
model_name='stocklocation',
|
||||||
name='parent',
|
name='parent',
|
@ -51,7 +51,8 @@ class StockLocation(InvenTreeTree):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, blank=True, null=True,
|
owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, blank=True, null=True,
|
||||||
help_text='Select Owner',
|
verbose_name=_('Owner'),
|
||||||
|
help_text=_('Select Owner'),
|
||||||
related_name='stock_locations')
|
related_name='stock_locations')
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
@ -483,7 +484,7 @@ class StockItem(MPTTModel):
|
|||||||
|
|
||||||
review_needed = models.BooleanField(default=False)
|
review_needed = models.BooleanField(default=False)
|
||||||
|
|
||||||
delete_on_deplete = models.BooleanField(default=True, help_text=_('Delete this Stock Item when stock is depleted'))
|
delete_on_deplete = models.BooleanField(default=True, verbose_name=_('Delete on deplete'), help_text=_('Delete this Stock Item when stock is depleted'))
|
||||||
|
|
||||||
status = models.PositiveIntegerField(
|
status = models.PositiveIntegerField(
|
||||||
default=StockStatus.OK,
|
default=StockStatus.OK,
|
||||||
@ -507,7 +508,8 @@ class StockItem(MPTTModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, blank=True, null=True,
|
owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, blank=True, null=True,
|
||||||
help_text='Select Owner',
|
verbose_name=_('Owner'),
|
||||||
|
help_text=_('Select Owner'),
|
||||||
related_name='stock_items')
|
related_name='stock_items')
|
||||||
|
|
||||||
def is_stale(self):
|
def is_stale(self):
|
||||||
@ -948,7 +950,7 @@ class StockItem(MPTTModel):
|
|||||||
raise ValidationError({"quantity": _("Quantity must be greater than zero")})
|
raise ValidationError({"quantity": _("Quantity must be greater than zero")})
|
||||||
|
|
||||||
if quantity > self.quantity:
|
if quantity > self.quantity:
|
||||||
raise ValidationError({"quantity": _("Quantity must not exceed available stock quantity ({n})".format(n=self.quantity))})
|
raise ValidationError({"quantity": _("Quantity must not exceed available stock quantity ({n})").format(n=self.quantity)})
|
||||||
|
|
||||||
if not type(serials) in [list, tuple]:
|
if not type(serials) in [list, tuple]:
|
||||||
raise ValidationError({"serial_numbers": _("Serial numbers must be a list of integers")})
|
raise ValidationError({"serial_numbers": _("Serial numbers must be a list of integers")})
|
||||||
@ -989,7 +991,7 @@ class StockItem(MPTTModel):
|
|||||||
new_item.addTransactionNote(_('Add serial number'), user, notes=notes)
|
new_item.addTransactionNote(_('Add serial number'), user, notes=notes)
|
||||||
|
|
||||||
# Remove the equivalent number of items
|
# Remove the equivalent number of items
|
||||||
self.take_stock(quantity, user, notes=_('Serialized {n} items'.format(n=quantity)))
|
self.take_stock(quantity, user, notes=_('Serialized {n} items').format(n=quantity))
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def copyHistoryFrom(self, other):
|
def copyHistoryFrom(self, other):
|
||||||
@ -1558,7 +1560,7 @@ class StockItemTracking(models.Model):
|
|||||||
|
|
||||||
system = models.BooleanField(default=False)
|
system = models.BooleanField(default=False)
|
||||||
|
|
||||||
quantity = models.DecimalField(max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], default=1)
|
quantity = models.DecimalField(max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], default=1, verbose_name=_('Quantity'))
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# image = models.ImageField(upload_to=func, max_length=255, null=True, blank=True)
|
# image = models.ImageField(upload_to=func, max_length=255, null=True, blank=True)
|
||||||
|
@ -965,7 +965,7 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
|
|
||||||
context['stock_action'] = self.stock_action.strip().lower()
|
context['stock_action'] = self.stock_action.strip().lower()
|
||||||
|
|
||||||
context['stock_action_title'] = self.stock_action.capitalize()
|
context['stock_action_title'] = self.stock_action_title
|
||||||
|
|
||||||
# Quantity column will be read-only in some circumstances
|
# Quantity column will be read-only in some circumstances
|
||||||
context['edit_quantity'] = not self.stock_action == 'delete'
|
context['edit_quantity'] = not self.stock_action == 'delete'
|
||||||
@ -993,16 +993,17 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
if self.stock_action not in ['move', 'count', 'take', 'add', 'delete']:
|
if self.stock_action not in ['move', 'count', 'take', 'add', 'delete']:
|
||||||
self.stock_action = 'count'
|
self.stock_action = 'count'
|
||||||
|
|
||||||
# Choose the form title based on the action
|
# Choose form title and action column based on the action
|
||||||
titles = {
|
titles = {
|
||||||
'move': _('Move Stock Items'),
|
'move': [_('Move Stock Items'), _('Move')],
|
||||||
'count': _('Count Stock Items'),
|
'count': [_('Count Stock Items'), _('Count')],
|
||||||
'take': _('Remove From Stock'),
|
'take': [_('Remove From Stock'), _('Take')],
|
||||||
'add': _('Add Stock Items'),
|
'add': [_('Add Stock Items'), _('Add')],
|
||||||
'delete': _('Delete Stock Items')
|
'delete': [_('Delete Stock Items'), _('Delete')],
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ajax_form_title = titles[self.stock_action]
|
self.ajax_form_title = titles[self.stock_action][0]
|
||||||
|
self.stock_action_title = titles[self.stock_action][1]
|
||||||
|
|
||||||
# Save list of items!
|
# Save list of items!
|
||||||
self.stock_items = self.get_GET_items()
|
self.stock_items = self.get_GET_items()
|
||||||
@ -1039,7 +1040,7 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
if self.stock_action in ['move', 'take']:
|
if self.stock_action in ['move', 'take']:
|
||||||
|
|
||||||
if item.new_quantity > item.quantity:
|
if item.new_quantity > item.quantity:
|
||||||
item.error = _('Quantity must not exceed {x}'.format(x=item.quantity))
|
item.error = _('Quantity must not exceed {x}').format(x=item.quantity)
|
||||||
valid = False
|
valid = False
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -1118,7 +1119,7 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
return f"{_('Added stock to ')} {count} {_('items')}"
|
return _('Added stock to {n} items').format(n=count)
|
||||||
|
|
||||||
def do_take(self):
|
def do_take(self):
|
||||||
|
|
||||||
@ -1133,7 +1134,7 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
return f"{_('Removed stock from ')} {count} {_('items')}"
|
return _('Removed stock from {n} items').format(n=count)
|
||||||
|
|
||||||
def do_count(self):
|
def do_count(self):
|
||||||
|
|
||||||
@ -1189,9 +1190,9 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
return _('No items were moved')
|
return _('No items were moved')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return _('Moved {n} items to {dest}'.format(
|
return _('Moved {n} items to {dest}').format(
|
||||||
n=count,
|
n=count,
|
||||||
dest=destination.pathstring))
|
dest=destination.pathstring)
|
||||||
|
|
||||||
def do_delete(self):
|
def do_delete(self):
|
||||||
""" Delete multiple stock items """
|
""" Delete multiple stock items """
|
||||||
@ -1208,7 +1209,7 @@ class StockAdjust(AjaxView, FormMixin):
|
|||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
return _("Deleted {n} stock items".format(n=count))
|
return _("Deleted {n} stock items").format(n=count)
|
||||||
|
|
||||||
|
|
||||||
class StockItemEdit(AjaxUpdateView):
|
class StockItemEdit(AjaxUpdateView):
|
||||||
|
Loading…
Reference in New Issue
Block a user