mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'upstream/master' into django-q
# Conflicts: # InvenTree/InvenTree/status.py # InvenTree/templates/about.html
This commit is contained in:
commit
9c38d67b52
@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.http import JsonResponse
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from .validators import allowable_url_schemes
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from django.forms.fields import URLField as FormURLField
|
||||
from django.db import models as models
|
||||
@ -42,6 +42,7 @@ class DatePickerFormField(forms.DateField):
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
help_text = kwargs.get('help_text', _('Enter date'))
|
||||
label = kwargs.get('label', None)
|
||||
required = kwargs.get('required', False)
|
||||
initial = kwargs.get('initial', None)
|
||||
|
||||
@ -56,7 +57,8 @@ class DatePickerFormField(forms.DateField):
|
||||
required=required,
|
||||
initial=initial,
|
||||
help_text=help_text,
|
||||
widget=widget
|
||||
widget=widget,
|
||||
label=label
|
||||
)
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@ Helper forms which subclass Django forms to provide additional functionality
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django import forms
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Field
|
||||
@ -123,6 +123,7 @@ class DeleteForm(forms.Form):
|
||||
confirm_delete = forms.BooleanField(
|
||||
required=False,
|
||||
initial=False,
|
||||
label=_('Confirm delete'),
|
||||
help_text=_('Confirm item deletion')
|
||||
)
|
||||
|
||||
@ -155,6 +156,7 @@ class SetPasswordForm(HelperForm):
|
||||
required=True,
|
||||
initial='',
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||
label=_('Enter password'),
|
||||
help_text=_('Enter new password'))
|
||||
|
||||
confirm_password = forms.CharField(max_length=100,
|
||||
@ -162,6 +164,7 @@ class SetPasswordForm(HelperForm):
|
||||
required=True,
|
||||
initial='',
|
||||
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
|
||||
label=_('Confirm password'),
|
||||
help_text=_('Confirm new password'))
|
||||
|
||||
class Meta:
|
||||
|
@ -13,7 +13,7 @@ from decimal import Decimal
|
||||
from wsgiref.util import FileWrapper
|
||||
from django.http import StreamingHttpResponse
|
||||
from django.core.exceptions import ValidationError, FieldError
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
@ -382,17 +382,17 @@ def extract_serial_numbers(serials, expected_quantity):
|
||||
if a < b:
|
||||
for n in range(a, b + 1):
|
||||
if n in numbers:
|
||||
errors.append(_('Duplicate serial: {n}'.format(n=n)))
|
||||
errors.append(_('Duplicate serial: {n}').format(n=n))
|
||||
else:
|
||||
numbers.append(n)
|
||||
else:
|
||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
||||
errors.append(_("Invalid group: {g}").format(g=group))
|
||||
|
||||
except ValueError:
|
||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
||||
errors.append(_("Invalid group: {g}").format(g=group))
|
||||
continue
|
||||
else:
|
||||
errors.append(_("Invalid group: {g}".format(g=group)))
|
||||
errors.append(_("Invalid group: {g}").format(g=group))
|
||||
continue
|
||||
|
||||
else:
|
||||
@ -409,7 +409,7 @@ def extract_serial_numbers(serials, expected_quantity):
|
||||
|
||||
# The number of extracted serial numbers must match the expected quantity
|
||||
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
|
||||
|
||||
|
@ -56,19 +56,20 @@ class InvenTreeAttachment(models.Model):
|
||||
def __str__(self):
|
||||
return os.path.basename(self.attachment.name)
|
||||
|
||||
attachment = models.FileField(upload_to=rename_attachment,
|
||||
attachment = models.FileField(upload_to=rename_attachment, verbose_name=_('Attachment'),
|
||||
help_text=_('Select file to attach'))
|
||||
|
||||
comment = models.CharField(blank=True, max_length=100, help_text=_('File comment'))
|
||||
comment = models.CharField(blank=True, max_length=100, verbose_name=_('Comment'), help_text=_('File comment'))
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
verbose_name=_('User'),
|
||||
help_text=_('User'),
|
||||
)
|
||||
|
||||
upload_date = models.DateField(auto_now_add=True, null=True, blank=True)
|
||||
upload_date = models.DateField(auto_now_add=True, null=True, blank=True, verbose_name=_('upload date'))
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
@ -103,12 +104,14 @@ class InvenTreeTree(MPTTModel):
|
||||
blank=False,
|
||||
max_length=100,
|
||||
validators=[validate_tree_name],
|
||||
verbose_name=_("Name"),
|
||||
help_text=_("Name"),
|
||||
)
|
||||
|
||||
description = models.CharField(
|
||||
blank=True,
|
||||
max_length=250,
|
||||
verbose_name=_("Description"),
|
||||
help_text=_("Description (optional)")
|
||||
)
|
||||
|
||||
@ -117,6 +120,7 @@ class InvenTreeTree(MPTTModel):
|
||||
on_delete=models.DO_NOTHING,
|
||||
blank=True,
|
||||
null=True,
|
||||
verbose_name=_("parent"),
|
||||
related_name='children')
|
||||
|
||||
@property
|
||||
|
@ -3,13 +3,11 @@ Provides system status functionality checks.
|
||||
"""
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from django_q.models import Success
|
||||
from django_q.monitor import Stat
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class StatusCode:
|
||||
|
@ -110,6 +110,7 @@ dynamic_javascript_urls = [
|
||||
url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.js'), name='stock.js'),
|
||||
url(r'^tables.js', DynamicJsView.as_view(template_name='js/tables.js'), name='tables.js'),
|
||||
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/table_filters.js'), name='table_filters.js'),
|
||||
url(r'^filters.js', DynamicJsView.as_view(template_name='js/filters.js'), name='filters.js'),
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -60,7 +60,7 @@ def validate_part_ipn(value):
|
||||
match = re.search(pattern, value)
|
||||
|
||||
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):
|
||||
|
@ -71,7 +71,7 @@ def inventreeCommitHash():
|
||||
|
||||
try:
|
||||
return str(subprocess.check_output('git rev-parse --short HEAD'.split()), 'utf-8').strip()
|
||||
except FileNotFoundError:
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
@ -81,5 +81,5 @@ def inventreeCommitDate():
|
||||
try:
|
||||
d = str(subprocess.check_output('git show -s --format=%ci'.split()), 'utf-8').strip()
|
||||
return d.split(' ')[0]
|
||||
except FileNotFoundError:
|
||||
except:
|
||||
return None
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from django.urls import reverse
|
||||
from django.conf.urls import url
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework import permissions
|
||||
|
@ -5,7 +5,7 @@ Django Forms for interacting with Build objects
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django import forms
|
||||
|
||||
from InvenTree.forms import HelperForm
|
||||
@ -36,11 +36,13 @@ class EditBuildForm(HelperForm):
|
||||
}
|
||||
|
||||
target_date = DatePickerFormField(
|
||||
label=_('Target Date'),
|
||||
help_text=_('Target date for build completion. Build will be overdue after this date.')
|
||||
)
|
||||
|
||||
quantity = RoundingDecimalFormField(
|
||||
max_digits=10, decimal_places=5,
|
||||
label=_('Quantity'),
|
||||
help_text=_('Number of items to build')
|
||||
)
|
||||
|
||||
@ -87,7 +89,7 @@ class BuildOutputCreateForm(HelperForm):
|
||||
)
|
||||
|
||||
serial_numbers = forms.CharField(
|
||||
label=_('Serial numbers'),
|
||||
label=_('Serial Numbers'),
|
||||
required=False,
|
||||
help_text=_('Enter serial numbers for build outputs'),
|
||||
)
|
||||
@ -115,6 +117,7 @@ class BuildOutputDeleteForm(HelperForm):
|
||||
|
||||
confirm = forms.BooleanField(
|
||||
required=False,
|
||||
label=_('Confirm'),
|
||||
help_text=_('Confirm deletion of build output')
|
||||
)
|
||||
|
||||
@ -136,7 +139,7 @@ class UnallocateBuildForm(HelperForm):
|
||||
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(
|
||||
required=False,
|
||||
@ -160,7 +163,7 @@ class UnallocateBuildForm(HelperForm):
|
||||
class AutoAllocateForm(HelperForm):
|
||||
""" 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
|
||||
output = forms.ModelChoiceField(
|
||||
@ -207,15 +210,17 @@ class CompleteBuildOutputForm(HelperForm):
|
||||
|
||||
location = forms.ModelChoiceField(
|
||||
queryset=StockLocation.objects.all(),
|
||||
label=_('Location'),
|
||||
help_text=_('Location of completed parts'),
|
||||
)
|
||||
|
||||
confirm_incomplete = forms.BooleanField(
|
||||
required=False,
|
||||
label=_('Confirm incomplete'),
|
||||
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(
|
||||
queryset=StockItem.objects.all(), # Queryset is narrowed in the view
|
||||
@ -235,7 +240,7 @@ class CompleteBuildOutputForm(HelperForm):
|
||||
class CancelBuildForm(HelperForm):
|
||||
""" 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:
|
||||
model = Build
|
||||
@ -249,7 +254,7 @@ class EditBuildItemForm(HelperForm):
|
||||
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())
|
||||
|
||||
|
85
InvenTree/build/migrations/0027_auto_20210404_2016.py
Normal file
85
InvenTree/build/migrations/0027_auto_20210404_2016.py
Normal file
@ -0,0 +1,85 @@
|
||||
# Generated by Django 3.0.7 on 2021-04-04 20:16
|
||||
|
||||
import InvenTree.models
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stock', '0058_stockitem_packaging'),
|
||||
('users', '0005_owner_model'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('build', '0026_auto_20210216_1539'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='build',
|
||||
name='completed_by',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_completed', to=settings.AUTH_USER_MODEL, verbose_name='completed by'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='build',
|
||||
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(
|
||||
model_name='build',
|
||||
name='issued_by',
|
||||
field=models.ForeignKey(blank=True, help_text='User who issued this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_issued', to=settings.AUTH_USER_MODEL, verbose_name='Issued by'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='build',
|
||||
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(
|
||||
model_name='buildorderattachment',
|
||||
name='attachment',
|
||||
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='buildorderattachment',
|
||||
name='comment',
|
||||
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='buildorderattachment',
|
||||
name='upload_date',
|
||||
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='buildorderattachment',
|
||||
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'),
|
||||
),
|
||||
]
|
@ -9,7 +9,7 @@ import os
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from django.urls import reverse
|
||||
@ -216,7 +216,7 @@ class Build(MPTTModel):
|
||||
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(
|
||||
null=True, blank=True,
|
||||
@ -224,12 +224,13 @@ class Build(MPTTModel):
|
||||
help_text=_('Target date for build completion. Build will be overdue after this date.')
|
||||
)
|
||||
|
||||
completion_date = models.DateField(null=True, blank=True)
|
||||
completion_date = models.DateField(null=True, blank=True, verbose_name=_('Completion Date'))
|
||||
|
||||
completed_by = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
verbose_name=_('completed by'),
|
||||
related_name='builds_completed'
|
||||
)
|
||||
|
||||
@ -237,6 +238,7 @@ class Build(MPTTModel):
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Issued by'),
|
||||
help_text=_('User who issued this build order'),
|
||||
related_name='builds_issued',
|
||||
)
|
||||
@ -245,6 +247,7 @@ class Build(MPTTModel):
|
||||
UserModels.Owner,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Responsible'),
|
||||
help_text=_('User responsible for this build order'),
|
||||
related_name='builds_responsible',
|
||||
)
|
||||
@ -1017,14 +1020,14 @@ class BuildItem(models.Model):
|
||||
try:
|
||||
# Allocated part must be in the BOM for the master part
|
||||
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
|
||||
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),
|
||||
q=normalize(self.stock_item.quantity)
|
||||
))]
|
||||
)]
|
||||
|
||||
# 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:
|
||||
@ -1076,6 +1079,7 @@ class BuildItem(models.Model):
|
||||
Build,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='allocated_stock',
|
||||
verbose_name=_('Build'),
|
||||
help_text=_('Build to allocate parts')
|
||||
)
|
||||
|
||||
@ -1083,6 +1087,7 @@ class BuildItem(models.Model):
|
||||
'stock.StockItem',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='allocations',
|
||||
verbose_name=_('Stock Item'),
|
||||
help_text=_('Source stock item'),
|
||||
limit_choices_to={
|
||||
'sales_order': None,
|
||||
@ -1095,6 +1100,7 @@ class BuildItem(models.Model):
|
||||
max_digits=15,
|
||||
default=1,
|
||||
validators=[MinValueValidator(0)],
|
||||
verbose_name=_('Quantity'),
|
||||
help_text=_('Stock quantity to allocate to build')
|
||||
)
|
||||
|
||||
@ -1103,6 +1109,7 @@ class BuildItem(models.Model):
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
related_name='items_to_install',
|
||||
verbose_name=_('Install into'),
|
||||
help_text=_('Destination stock item'),
|
||||
limit_choices_to={
|
||||
'is_building': True,
|
||||
|
@ -164,7 +164,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
launchModalForm("{% url 'build-cancel' build.id %}",
|
||||
{
|
||||
reload: true,
|
||||
submit_text: "Cancel Build",
|
||||
submit_text: '{% trans "Cancel Build" %}',
|
||||
});
|
||||
});
|
||||
|
||||
@ -173,7 +173,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
"{% url 'build-complete' build.id %}",
|
||||
{
|
||||
reload: true,
|
||||
submit_text: "Complete Build",
|
||||
submit_text: '{% trans "Complete Build" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -130,6 +130,7 @@ InvenTree | {% trans "Build Orders" %}
|
||||
initialView: 'dayGridMonth',
|
||||
nowIndicator: true,
|
||||
aspectRatio: 2.5,
|
||||
locale: '{{request.LANGUAGE_CODE}}',
|
||||
datesSet: function() {
|
||||
loadOrderEvents(calendar);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ Django views for interacting with Build objects
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.views.generic import DetailView, ListView, UpdateView
|
||||
from django.forms import HiddenInput
|
||||
|
@ -17,7 +17,7 @@ from djmoney.models.fields import MoneyField
|
||||
from djmoney.contrib.exchange.models import convert_money
|
||||
from djmoney.contrib.exchange.exceptions import MissingRate
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.validators import MinValueValidator, URLValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
|
@ -5,7 +5,7 @@ Django views for interacting with common models
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.forms import CheckboxInput, Select
|
||||
|
||||
from InvenTree.views import AjaxUpdateView
|
||||
|
@ -8,7 +8,7 @@ from __future__ import unicode_literals
|
||||
from InvenTree.forms import HelperForm
|
||||
from InvenTree.fields import RoundingDecimalFormField
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import django.forms
|
||||
|
||||
import djmoney.settings
|
||||
@ -34,6 +34,7 @@ class EditCompanyForm(HelperForm):
|
||||
|
||||
currency = django.forms.ChoiceField(
|
||||
required=False,
|
||||
label=_('Currency'),
|
||||
help_text=_('Default currency used for this company'),
|
||||
choices=[('', '----------')] + djmoney.settings.CURRENCY_CHOICES,
|
||||
initial=common.settings.currency_code_default,
|
||||
|
69
InvenTree/company/migrations/0032_auto_20210403_1837.py
Normal file
69
InvenTree/company/migrations/0032_auto_20210403_1837.py
Normal file
@ -0,0 +1,69 @@
|
||||
# Generated by Django 3.0.7 on 2021-04-03 18:37
|
||||
|
||||
import InvenTree.fields
|
||||
import company.models
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import markdownx.models
|
||||
import stdimage.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('company', '0031_auto_20210103_2215'),
|
||||
]
|
||||
|
||||
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(
|
||||
model_name='company',
|
||||
name='is_customer',
|
||||
field=models.BooleanField(default=False, help_text='Do you sell items to this company?', verbose_name='is customer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='company',
|
||||
name='is_manufacturer',
|
||||
field=models.BooleanField(default=False, help_text='Does this company manufacture parts?', verbose_name='is manufacturer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='company',
|
||||
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(
|
||||
model_name='supplierpart',
|
||||
name='base_cost',
|
||||
field=models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)], verbose_name='base cost'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='supplierpart',
|
||||
name='multiple',
|
||||
field=models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(1)], verbose_name='multiple'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='supplierpart',
|
||||
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'),
|
||||
),
|
||||
]
|
@ -9,7 +9,7 @@ import os
|
||||
|
||||
import math
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Sum, Q, UniqueConstraint
|
||||
@ -114,7 +114,7 @@ class Company(models.Model):
|
||||
verbose_name=_('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(
|
||||
upload_to=rename_company_image,
|
||||
@ -122,15 +122,16 @@ class Company(models.Model):
|
||||
blank=True,
|
||||
variations={'thumbnail': (128, 128)},
|
||||
delete_orphans=True,
|
||||
verbose_name=_('Image'),
|
||||
)
|
||||
|
||||
notes = MarkdownxField(blank=True)
|
||||
notes = MarkdownxField(blank=True, verbose_name=_('Notes'))
|
||||
|
||||
is_customer = models.BooleanField(default=False, 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?'))
|
||||
|
||||
is_supplier = models.BooleanField(default=True, help_text=_('Do you purchase items from this company?'))
|
||||
is_supplier = models.BooleanField(default=True, verbose_name=_('is supplier'), help_text=_('Do you purchase items from this company?'))
|
||||
|
||||
is_manufacturer = models.BooleanField(default=False, help_text=_('Does this company manufacture parts?'))
|
||||
is_manufacturer = models.BooleanField(default=False, verbose_name=_('is manufacturer'), help_text=_('Does this company manufacture parts?'))
|
||||
|
||||
currency = models.CharField(
|
||||
max_length=3,
|
||||
@ -366,11 +367,11 @@ class SupplierPart(models.Model):
|
||||
help_text=_('Notes')
|
||||
)
|
||||
|
||||
base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], 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, 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)], 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).
|
||||
# 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)
|
||||
"""
|
||||
|
||||
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:
|
||||
unique_together = ("part", "quantity")
|
||||
|
@ -43,17 +43,17 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
|
||||
<p>{{ company.description }}</p>
|
||||
<div class='btn-group action-buttons'>
|
||||
{% if company.is_supplier and roles.purchase_order.add %}
|
||||
<button type='button' class='btn btn-default' id='company-order-2' title='Create purchase order'>
|
||||
<button type='button' class='btn btn-default' id='company-order-2' title='{% trans "Create Purchase Order" %}'>
|
||||
<span class='fas fa-shopping-cart'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if perms.company.change_company %}
|
||||
<button type='button' class='btn btn-default' id='company-edit' title='Edit company information'>
|
||||
<button type='button' class='btn btn-default' id='company-edit' title='{% trans "Edit company information" %}'>
|
||||
<span class='fas fa-edit icon-green'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if perms.company.delete_company %}
|
||||
<button type='button' class='btn btn-default' id='company-delete' title='Delete company'>
|
||||
<button type='button' class='btn btn-default' id='company-delete' title='{% trans "Delete Company" %}'>
|
||||
<span class='fas fa-trash-alt icon-red'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
@ -1,14 +1,16 @@
|
||||
{% extends "modal_delete_form.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% 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>
|
||||
|
||||
{% if company.supplied_part_count > 0 %}
|
||||
<p>There are {{ company.supplied_part_count }} parts sourced from this company.<br>
|
||||
If this supplier is deleted, these supplier part entries will also be deleted.</p>
|
||||
<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.{% endblocktrans %}</p>
|
||||
<ul class='list-group'>
|
||||
{% for part in company.parts.all %}
|
||||
<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 %}
|
||||
<div id='button-toolbar'>
|
||||
<div class='button-toolbar container-fluid'>
|
||||
<div class='btn-group role='group'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% if roles.purchase_order.add %}
|
||||
<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" %}
|
||||
|
@ -14,7 +14,7 @@
|
||||
{% if roles.purchase_order.add %}
|
||||
<div id='button-bar'>
|
||||
<div class='btn-group'>
|
||||
<button class='btn btn-primary' type='button' id='order-part2' title='Order part'>
|
||||
<button class='btn btn-primary' type='button' id='order-part2' title='{% trans "Order part" %}'>
|
||||
<span class='fas fa-shopping-cart'></span> {% trans "Order Part" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@ Django views for interacting with Company app
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import DetailView, ListView, UpdateView
|
||||
|
||||
from django.urls import reverse
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf.urls import url, include
|
||||
from django.core.exceptions import ValidationError, FieldError
|
||||
from django.http import HttpResponse
|
||||
|
@ -126,7 +126,7 @@ class LabelTemplate(models.Model):
|
||||
|
||||
width = models.FloatField(
|
||||
default=50,
|
||||
verbose_name=('Width [mm]'),
|
||||
verbose_name=_('Width [mm]'),
|
||||
help_text=_('Label width, specified in mm'),
|
||||
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
@ -6,7 +6,7 @@ Django Forms for interacting with Order objects
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from mptt.fields import TreeNodeChoiceField
|
||||
|
||||
@ -24,7 +24,7 @@ from .models import SalesOrderAllocation
|
||||
|
||||
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:
|
||||
model = PurchaseOrder
|
||||
@ -35,7 +35,7 @@ class IssuePurchaseOrderForm(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:
|
||||
model = PurchaseOrder
|
||||
@ -46,7 +46,7 @@ class CompletePurchaseOrderForm(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:
|
||||
model = PurchaseOrder
|
||||
@ -57,7 +57,7 @@ class CancelPurchaseOrderForm(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:
|
||||
model = SalesOrder
|
||||
@ -68,7 +68,7 @@ class CancelSalesOrderForm(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:
|
||||
model = SalesOrder
|
||||
@ -79,7 +79,7 @@ class ShipSalesOrderForm(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:
|
||||
model = PurchaseOrder
|
||||
@ -106,6 +106,7 @@ class EditPurchaseOrderForm(HelperForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
target_date = DatePickerFormField(
|
||||
label=_('Target 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)
|
||||
|
||||
target_date = DatePickerFormField(
|
||||
label=_('Target 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):
|
||||
""" 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:
|
||||
model = PurchaseOrderLineItem
|
||||
@ -200,7 +202,7 @@ class EditPurchaseOrderLineItemForm(HelperForm):
|
||||
class EditSalesOrderLineItemForm(HelperForm):
|
||||
""" 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:
|
||||
model = SalesOrderLineItem
|
||||
@ -256,7 +258,7 @@ class CreateSalesOrderAllocationForm(HelperForm):
|
||||
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:
|
||||
model = SalesOrderAllocation
|
||||
@ -273,7 +275,7 @@ class EditSalesOrderAllocationForm(HelperForm):
|
||||
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:
|
||||
model = SalesOrderAllocation
|
||||
|
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'),
|
||||
),
|
||||
]
|
@ -15,7 +15,7 @@ from django.core.validators import MinValueValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.contrib.auth.models import User
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from markdownx.models import MarkdownxField
|
||||
|
||||
@ -96,18 +96,19 @@ class Order(models.Model):
|
||||
class Meta:
|
||||
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,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
related_name='+'
|
||||
related_name='+',
|
||||
verbose_name=_('Created By')
|
||||
)
|
||||
|
||||
responsible = models.ForeignKey(
|
||||
@ -119,7 +120,7 @@ class Order(models.Model):
|
||||
related_name='+',
|
||||
)
|
||||
|
||||
notes = MarkdownxField(blank=True, help_text=_('Order notes'))
|
||||
notes = MarkdownxField(blank=True, verbose_name=_('Notes'), help_text=_('Order notes'))
|
||||
|
||||
|
||||
class PurchaseOrder(Order):
|
||||
@ -186,16 +187,18 @@ class PurchaseOrder(Order):
|
||||
'is_supplier': True,
|
||||
},
|
||||
related_name='purchase_orders',
|
||||
verbose_name=_('Supplier'),
|
||||
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(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
related_name='+'
|
||||
related_name='+',
|
||||
verbose_name=_('received by')
|
||||
)
|
||||
|
||||
issue_date = models.DateField(
|
||||
@ -434,13 +437,14 @@ class SalesOrder(Order):
|
||||
null=True,
|
||||
limit_choices_to={'is_customer': True},
|
||||
related_name='sales_orders',
|
||||
verbose_name=_('Customer'),
|
||||
help_text=_("Company to which the items are being sold"),
|
||||
)
|
||||
|
||||
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(
|
||||
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.')
|
||||
)
|
||||
|
||||
shipment_date = models.DateField(blank=True, null=True)
|
||||
shipment_date = models.DateField(blank=True, null=True, verbose_name=_('Shipment Date'))
|
||||
|
||||
shipped_by = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
related_name='+'
|
||||
related_name='+',
|
||||
verbose_name=_('shipped by')
|
||||
)
|
||||
|
||||
@property
|
||||
@ -585,11 +590,11 @@ class OrderLineItem(models.Model):
|
||||
class Meta:
|
||||
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):
|
||||
@ -615,6 +620,7 @@ class PurchaseOrderLineItem(OrderLineItem):
|
||||
order = models.ForeignKey(
|
||||
PurchaseOrder, on_delete=models.CASCADE,
|
||||
related_name='lines',
|
||||
verbose_name=_('Order'),
|
||||
help_text=_('Purchase Order')
|
||||
)
|
||||
|
||||
@ -628,10 +634,11 @@ class PurchaseOrderLineItem(OrderLineItem):
|
||||
SupplierPart, on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
related_name='purchase_order_line_items',
|
||||
verbose_name=_('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(
|
||||
max_digits=19,
|
||||
@ -657,9 +664,9 @@ class SalesOrderLineItem(OrderLineItem):
|
||||
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:
|
||||
unique_together = [
|
||||
@ -759,7 +766,7 @@ class SalesOrderAllocation(models.Model):
|
||||
if len(errors) > 0:
|
||||
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(
|
||||
'stock.StockItem',
|
||||
@ -770,10 +777,11 @@ class SalesOrderAllocation(models.Model):
|
||||
'belongs_to': None,
|
||||
'sales_order': None,
|
||||
},
|
||||
verbose_name=_('Item'),
|
||||
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):
|
||||
return self.item.serial
|
||||
|
@ -1,12 +1,14 @@
|
||||
{% extends "modal_form.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block pre_form_content %}
|
||||
|
||||
Mark this order as complete?
|
||||
{% trans 'Mark this order as complete?' %}
|
||||
{% if not order.is_complete %}
|
||||
<div class='alert alert-warning alert-block'>
|
||||
This order has line items which have not been marked as received.
|
||||
Marking this order as complete will remove these line items.
|
||||
{%trans 'This order has line items which have not been marked as received.
|
||||
Marking this order as complete will remove these line items.' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
{% extends "modal_form.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% 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 %}
|
@ -39,7 +39,7 @@
|
||||
{{ part.full_name }} <small><i>{{ part.description }}</i></small>
|
||||
</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>
|
||||
</button>
|
||||
</td>
|
||||
@ -66,7 +66,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class='btn btn-default btn-remove' onclick='removeOrderRowFromOrderWizard()' id='del_item_{{ part.id }}' title='Remove part' type='button'>
|
||||
<button class='btn btn-default btn-remove' onclick='removeOrderRowFromOrderWizard()' id='del_item_{{ part.id }}' title='{% trans "Remove part" %}' type='button'>
|
||||
<span row='part_row_{{ part.id }}' class='fas fa-trash-alt icon-red'></span>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -42,7 +42,7 @@
|
||||
<button
|
||||
class='btn btn-default btn-create'
|
||||
id='new_po_{{ supplier.id }}'
|
||||
title='Create new purchase order for {{ supplier.name }}'
|
||||
title='{% trans "Create new purchase order for {{ supplier.name }}" %}'
|
||||
type='button'
|
||||
supplierid='{{ supplier.id }}'
|
||||
onclick='newPurchaseOrderFromOrderWizard()'>
|
||||
|
@ -116,6 +116,7 @@ InvenTree | {% trans "Purchase Orders" %}
|
||||
initialView: 'dayGridMonth',
|
||||
nowIndicator: true,
|
||||
aspectRatio: 2.5,
|
||||
locale: '{{request.LANGUAGE_CODE}}',
|
||||
datesSet: function() {
|
||||
loadOrderEvents(calendar);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button class='btn btn-default btn-remove' onClick="removeOrderRowFromOrderWizard()" id='del_item_{{ line.id }}' title='Remove line' type='button'>
|
||||
<button class='btn btn-default btn-remove' onClick="removeOrderRowFromOrderWizard()" id='del_item_{{ line.id }}' title='{% trans "Remove line" %}' type='button'>
|
||||
<span row='line_row_{{ line.id }}' class='fas fa-times-circle icon-red'></span>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -49,7 +49,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<span class='fas fa-print'></span>
|
||||
</button>
|
||||
{% if roles.sales_order.change %}
|
||||
<button type='button' class='btn btn-default' id='edit-order' title='Edit order information'>
|
||||
<button type='button' class='btn btn-default' id='edit-order' title='{% trans "Edit order information" %}'>
|
||||
<span class='fas fa-edit icon-green'></span>
|
||||
</button>
|
||||
{% if order.status == SalesOrderStatus.PENDING %}
|
||||
|
@ -67,7 +67,7 @@ function showAllocationSubTable(index, row, element) {
|
||||
{
|
||||
width: '50%',
|
||||
field: 'allocated',
|
||||
title: 'Quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
var text = '';
|
||||
|
||||
@ -89,7 +89,7 @@ function showAllocationSubTable(index, row, element) {
|
||||
},
|
||||
{
|
||||
field: 'buttons',
|
||||
title: 'Actions',
|
||||
title: '{% trans "Actions" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var html = "<div class='btn-group float-right' role='group'>";
|
||||
@ -167,7 +167,7 @@ function showFulfilledSubTable(index, row, element) {
|
||||
}
|
||||
|
||||
$("#so-lines-table").inventreeTable({
|
||||
formatNoMatches: function() { return "No matching line items"; },
|
||||
formatNoMatches: function() { return "{% trans 'No matching line items' %}"; },
|
||||
queryParams: {
|
||||
order: {{ order.id }},
|
||||
part_detail: true,
|
||||
@ -196,7 +196,7 @@ $("#so-lines-table").inventreeTable({
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
title: '{% trans "ID" %}',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
@ -204,7 +204,7 @@ $("#so-lines-table").inventreeTable({
|
||||
sortable: true,
|
||||
sortName: 'part__name',
|
||||
field: 'part',
|
||||
title: 'Part',
|
||||
title: '{% trans "Part" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (row.part) {
|
||||
return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${value}/`);
|
||||
@ -216,12 +216,12 @@ $("#so-lines-table").inventreeTable({
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: 'Reference'
|
||||
title: '{% trans "Reference" %}'
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'quantity',
|
||||
title: 'Quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
},
|
||||
{
|
||||
field: 'allocated',
|
||||
@ -261,7 +261,7 @@ $("#so-lines-table").inventreeTable({
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
title: 'Notes',
|
||||
title: '{% trans "Notes" %}',
|
||||
},
|
||||
{% if order.status == SalesOrderStatus.PENDING %}
|
||||
{
|
||||
|
@ -115,6 +115,7 @@ InvenTree | {% trans "Sales Orders" %}
|
||||
initialView: 'dayGridMonth',
|
||||
nowIndicator: true,
|
||||
aspectRatio: 2.5,
|
||||
locale: '{{request.LANGUAGE_CODE}}',
|
||||
datesSet: function() {
|
||||
loadOrderEvents(calendar);
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import DetailView, ListView, UpdateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
from django.forms import HiddenInput
|
||||
@ -1057,7 +1057,7 @@ class OrderParts(AjaxView):
|
||||
|
||||
data = {
|
||||
'form_valid': valid,
|
||||
'success': 'Ordered {n} parts'.format(n=len(self.parts))
|
||||
'success': _('Ordered {n} parts').format(n=len(self.parts))
|
||||
}
|
||||
|
||||
return self.renderJsonResponse(self.request, data=data)
|
||||
@ -1348,7 +1348,7 @@ class SalesOrderAssignSerials(AjaxView, FormMixin):
|
||||
'form_valid': valid,
|
||||
'form_errors': self.form.errors.as_json(),
|
||||
'non_field_errors': self.form.non_field_errors().as_json(),
|
||||
'success': _("Allocated") + f" {len(self.stock_items)} " + _("items")
|
||||
'success': _("Allocated {n} items").format(n=len(self.stock_items))
|
||||
}
|
||||
|
||||
return self.renderJsonResponse(request, self.form, data)
|
||||
|
@ -275,7 +275,7 @@ class BomUploadManager:
|
||||
elif ext in ['.xls', '.xlsx']:
|
||||
raw_data = bom_file.read()
|
||||
else:
|
||||
raise ValidationError({'bom_file': _('Unsupported file format: {f}'.format(f=ext))})
|
||||
raise ValidationError({'bom_file': _('Unsupported file format: {f}').format(f=ext)})
|
||||
|
||||
try:
|
||||
self.data = tablib.Dataset().load(raw_data)
|
||||
|
@ -11,7 +11,7 @@ from InvenTree.fields import RoundingDecimalFormField
|
||||
|
||||
from mptt.fields import TreeNodeChoiceField
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import common.models
|
||||
|
||||
@ -129,6 +129,7 @@ class BomDuplicateForm(HelperForm):
|
||||
|
||||
confirm = forms.BooleanField(
|
||||
required=False, initial=False,
|
||||
label=_('Confirm'),
|
||||
help_text=_('Confirm BOM duplication')
|
||||
)
|
||||
|
||||
@ -147,7 +148,7 @@ class BomValidateForm(HelperForm):
|
||||
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:
|
||||
model = Part
|
||||
@ -159,7 +160,7 @@ class BomValidateForm(HelperForm):
|
||||
class BomUploadSelectFile(HelperForm):
|
||||
""" 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:
|
||||
model = Part
|
||||
@ -336,9 +337,9 @@ class EditCategoryParameterTemplateForm(HelperForm):
|
||||
class EditBomItemForm(HelperForm):
|
||||
""" 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:
|
||||
model = BomItem
|
||||
@ -365,6 +366,7 @@ class PartPriceForm(forms.Form):
|
||||
quantity = forms.IntegerField(
|
||||
required=True,
|
||||
initial=1,
|
||||
label=_('Quantity'),
|
||||
help_text=_('Input quantity for price calculation')
|
||||
)
|
||||
|
||||
@ -380,7 +382,7 @@ class EditPartSalePriceBreakForm(HelperForm):
|
||||
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:
|
||||
model = PartSellPriceBreak
|
||||
|
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'),
|
||||
),
|
||||
]
|
@ -69,10 +69,11 @@ class PartCategory(InvenTreeTree):
|
||||
'stock.StockLocation', related_name="default_categories",
|
||||
null=True, blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name=_('Default Location'),
|
||||
help_text=_('Default location for parts in this category')
|
||||
)
|
||||
|
||||
default_keywords = models.CharField(null=True, blank=True, max_length=250, help_text=_('Default keywords for parts in this category'))
|
||||
default_keywords = models.CharField(null=True, blank=True, max_length=250, verbose_name=_('Default keywords'), help_text=_('Default keywords for parts in this category'))
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('category-detail', kwargs={'pk': self.id})
|
||||
@ -442,10 +443,10 @@ class Part(MPTTModel):
|
||||
return
|
||||
|
||||
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),
|
||||
p2=str(parent)
|
||||
))})
|
||||
)})
|
||||
|
||||
bom_items = self.get_bom_items()
|
||||
|
||||
@ -454,10 +455,10 @@ class Part(MPTTModel):
|
||||
|
||||
# Check for simple match
|
||||
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),
|
||||
p2=str(self)
|
||||
))})
|
||||
)})
|
||||
|
||||
# And recursively check too
|
||||
item.sub_part.checkAddToBOM(parent)
|
||||
@ -749,6 +750,7 @@ class Part(MPTTModel):
|
||||
blank=True,
|
||||
variations={'thumbnail': (128, 128)},
|
||||
delete_orphans=False,
|
||||
verbose_name=_('Image'),
|
||||
)
|
||||
|
||||
default_location = TreeForeignKey(
|
||||
@ -870,18 +872,18 @@ class Part(MPTTModel):
|
||||
help_text=_('Part notes - supports Markdown formatting')
|
||||
)
|
||||
|
||||
bom_checksum = models.CharField(max_length=128, blank=True, help_text=_('Stored BOM checksum'))
|
||||
bom_checksum = models.CharField(max_length=128, blank=True, verbose_name=_('BOM checksum'), help_text=_('Stored BOM checksum'))
|
||||
|
||||
bom_checked_by = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True,
|
||||
related_name='boms_checked')
|
||||
verbose_name=_('BOM checked by'), related_name='boms_checked')
|
||||
|
||||
bom_checked_date = models.DateField(blank=True, null=True)
|
||||
bom_checked_date = models.DateField(blank=True, null=True, verbose_name=_('BOM checked date'))
|
||||
|
||||
creation_date = models.DateField(auto_now_add=True, editable=False, blank=True, null=True)
|
||||
creation_date = models.DateField(auto_now_add=True, editable=False, blank=True, null=True, verbose_name=_('Creation Date'))
|
||||
|
||||
creation_user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, related_name='parts_created')
|
||||
creation_user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('Creation User'), related_name='parts_created')
|
||||
|
||||
responsible = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, related_name='parts_responible')
|
||||
responsible = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, verbose_name=_('Responsible'), related_name='parts_responible')
|
||||
|
||||
def format_barcode(self, **kwargs):
|
||||
""" Return a JSON string for formatting a barcode for this Part object """
|
||||
@ -1851,7 +1853,7 @@ class PartAttachment(InvenTreeAttachment):
|
||||
return os.path.join("part_files", str(self.part.id))
|
||||
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE,
|
||||
related_name='attachments')
|
||||
verbose_name=_('Part'), related_name='attachments')
|
||||
|
||||
|
||||
class PartSellPriceBreak(common.models.PriceBreak):
|
||||
@ -1862,7 +1864,8 @@ class PartSellPriceBreak(common.models.PriceBreak):
|
||||
part = models.ForeignKey(
|
||||
Part, on_delete=models.CASCADE,
|
||||
related_name='salepricebreaks',
|
||||
limit_choices_to={'salable': True}
|
||||
limit_choices_to={'salable': True},
|
||||
verbose_name=_('Part')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@ -1880,9 +1883,9 @@ class PartStar(models.Model):
|
||||
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:
|
||||
unique_together = ['part', 'user']
|
||||
@ -1955,6 +1958,7 @@ class PartTestTemplate(models.Model):
|
||||
on_delete=models.CASCADE,
|
||||
related_name='test_templates',
|
||||
limit_choices_to={'trackable': True},
|
||||
verbose_name=_('Part'),
|
||||
)
|
||||
|
||||
test_name = models.CharField(
|
||||
@ -2022,9 +2026,9 @@ class PartParameterTemplate(models.Model):
|
||||
except PartParameterTemplate.DoesNotExist:
|
||||
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):
|
||||
@ -2050,11 +2054,11 @@ class PartParameter(models.Model):
|
||||
# Prevent multiple instances of a parameter for a single part
|
||||
unique_together = ('part', 'template')
|
||||
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='parameters', help_text=_('Parent Part'))
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='parameters', verbose_name=_('Part'), help_text=_('Parent Part'))
|
||||
|
||||
template = models.ForeignKey(PartParameterTemplate, on_delete=models.CASCADE, related_name='instances', help_text=_('Parameter Template'))
|
||||
template = models.ForeignKey(PartParameterTemplate, on_delete=models.CASCADE, related_name='instances', verbose_name=_('Template'), help_text=_('Parameter Template'))
|
||||
|
||||
data = models.CharField(max_length=500, help_text=_('Parameter Value'))
|
||||
data = models.CharField(max_length=500, verbose_name=_('Data'), help_text=_('Parameter Value'))
|
||||
|
||||
@classmethod
|
||||
def create(cls, part, template, data, save=False):
|
||||
@ -2095,15 +2099,18 @@ class PartCategoryParameterTemplate(models.Model):
|
||||
category = models.ForeignKey(PartCategory,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='parameter_templates',
|
||||
verbose_name=_('Category'),
|
||||
help_text=_('Part Category'))
|
||||
|
||||
parameter_template = models.ForeignKey(PartParameterTemplate,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='part_categories',
|
||||
verbose_name=_('Parameter Template'),
|
||||
help_text=_('Parameter Template'))
|
||||
|
||||
default_value = models.CharField(max_length=500,
|
||||
blank=True,
|
||||
verbose_name=_('Default Value'),
|
||||
help_text=_('Default Parameter Value'))
|
||||
|
||||
|
||||
@ -2132,6 +2139,7 @@ class BomItem(models.Model):
|
||||
# A link to the parent part
|
||||
# Each part will get a reverse lookup field 'bom_items'
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='bom_items',
|
||||
verbose_name=_('Part'),
|
||||
help_text=_('Select parent part'),
|
||||
limit_choices_to={
|
||||
'assembly': True,
|
||||
@ -2140,26 +2148,28 @@ class BomItem(models.Model):
|
||||
# A link to the child item (sub-part)
|
||||
# Each part will get a reverse lookup field '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'),
|
||||
limit_choices_to={
|
||||
'component': True,
|
||||
})
|
||||
|
||||
# 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],
|
||||
verbose_name=_('Overage'),
|
||||
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 = 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(
|
||||
default=False,
|
||||
@ -2371,11 +2381,11 @@ class PartRelated(models.Model):
|
||||
""" Store and handle related parts (eg. mating connector, crimps, etc.) """
|
||||
|
||||
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',
|
||||
on_delete=models.DO_NOTHING,
|
||||
help_text=_('Select Related Part'))
|
||||
verbose_name=_('Part 2'), help_text=_('Select Related Part'))
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.part_1} <--> {self.part_2}'
|
||||
|
@ -16,13 +16,13 @@
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% else %}
|
||||
<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 %}
|
||||
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>
|
||||
{% else %}
|
||||
<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>
|
||||
{% endif %}
|
||||
|
||||
|
@ -44,7 +44,7 @@
|
||||
<div>
|
||||
<input type='hidden' name='col_name_{{ forloop.counter0 }}' value='{{ col.name }}'/>
|
||||
{{ col.name }}
|
||||
<button class='btn btn-default btn-remove' onClick='removeColFromBomWizard()' id='del_col_{{ forloop.counter0 }}' style='display: inline; float: right;' title='Remove column'>
|
||||
<button class='btn btn-default btn-remove' onClick='removeColFromBomWizard()' id='del_col_{{ forloop.counter0 }}' style='display: inline; float: right;' title='{% trans "Remove column" %}'>
|
||||
<span col_id='{{ forloop.counter0 }}' class='fas fa-trash-alt icon-red'></span>
|
||||
</button>
|
||||
</div>
|
||||
@ -73,7 +73,7 @@
|
||||
{% for row in bom_rows %}
|
||||
<tr>
|
||||
<td>
|
||||
<button class='btn btn-default btn-remove' onClick='removeRowFromBomWizard()' id='del_row_{{ forloop.counter }}' style='display: inline; float: right;' title='Remove row'>
|
||||
<button class='btn btn-default btn-remove' onClick='removeRowFromBomWizard()' id='del_row_{{ forloop.counter }}' style='display: inline; float: right;' title='{% trans "Remove row" %}'>
|
||||
<span row_id='{{ forloop.counter }}' class='fas fa-trash-alt icon-red'></span>
|
||||
</button>
|
||||
</td>
|
||||
|
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<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 %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
{% extends "modal_form.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% 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'>
|
||||
This will validate each line in the BOM.
|
||||
{% trans 'This will validate each line in the BOM.' %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -65,8 +65,8 @@
|
||||
reload: true,
|
||||
secondary: [{
|
||||
field: 'template',
|
||||
label: 'New Template',
|
||||
title: 'Create New Parameter Template',
|
||||
label: '{% trans "New Template" %}',
|
||||
title: '{% trans "Create New Parameter Template" %}',
|
||||
url: "{% url 'part-param-template-create' %}"
|
||||
}],
|
||||
});
|
||||
|
@ -246,7 +246,7 @@
|
||||
launchModalForm(
|
||||
"{% url 'part-pricing' part.id %}",
|
||||
{
|
||||
submit_text: 'Calculate',
|
||||
submit_text: '{% trans "Calculate" %}',
|
||||
hideErrorMessage: true,
|
||||
}
|
||||
);
|
||||
|
@ -1,35 +1,37 @@
|
||||
{% extends "modal_form.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block pre_form_content %}
|
||||
|
||||
<div class='alert alert-info alert-block'>
|
||||
Pricing information for:<br>
|
||||
{% trans 'Pricing information for:' %}<br>
|
||||
{{ part }}.
|
||||
</div>
|
||||
|
||||
<h4>Quantity</h4>
|
||||
<h4>{% trans 'Quantity' %}</h4>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<td><b>Part</b></td>
|
||||
<td><b>{% trans 'Part' %}</b></td>
|
||||
<td colspan='2'>{{ part }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Quantity</b></td>
|
||||
<td><b>{% trans 'Quantity' %}</b></td>
|
||||
<td colspan='2'>{{ quantity }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% if part.supplier_count > 0 %}
|
||||
<h4>Supplier Pricing</h4>
|
||||
<h4>{% trans 'Supplier Pricing' %}</h4>
|
||||
<table class='table table-striped table-condensed'>
|
||||
{% if min_total_buy_price %}
|
||||
<tr>
|
||||
<td><b>Unit Cost</b></td>
|
||||
<td><b>{% trans 'Unit Cost' %}</b></td>
|
||||
<td>Min: {% include "price.html" with price=min_unit_buy_price %}</td>
|
||||
<td>Max: {% include "price.html" with price=max_unit_buy_price %}</td>
|
||||
</tr>
|
||||
{% if quantity > 1 %}
|
||||
<tr>
|
||||
<td><b>Total Cost</b></td>
|
||||
<td><b>{% trans 'Total Cost' %}</b></td>
|
||||
<td>Min: {% include "price.html" with price=min_total_buy_price %}</td>
|
||||
<td>Max: {% include "price.html" with price=max_total_buy_price %}</td>
|
||||
</tr>
|
||||
@ -37,7 +39,7 @@ Pricing information for:<br>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan='3'>
|
||||
<span class='warning-msg'><i>No supplier pricing available</i></span>
|
||||
<span class='warning-msg'><i>{% trans 'No supplier pricing available' %}</i></span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
@ -45,17 +47,17 @@ Pricing information for:<br>
|
||||
{% endif %}
|
||||
|
||||
{% if part.bom_count > 0 %}
|
||||
<h4>BOM Pricing</h4>
|
||||
<h4>{% trans 'BOM Pricing' %}</h4>
|
||||
<table class='table table-striped table-condensed'>
|
||||
{% if min_total_bom_price %}
|
||||
<tr>
|
||||
<td><b>Unit Cost</b></td>
|
||||
<td><b>{% trans 'Unit Cost' %}</b></td>
|
||||
<td>Min: {% include "price.html" with price=min_unit_bom_price %}</td>
|
||||
<td>Max: {% include "price.html" with price=max_unit_bom_price %}</td>
|
||||
</tr>
|
||||
{% if quantity > 1 %}
|
||||
<tr>
|
||||
<td><b>Total Cost</b></td>
|
||||
<td><b>{% trans 'Total Cost' %}</b></td>
|
||||
<td>Min: {% include "price.html" with price=min_total_bom_price %}</td>
|
||||
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td>
|
||||
</tr>
|
||||
@ -63,14 +65,14 @@ Pricing information for:<br>
|
||||
{% if part.has_complete_bom_pricing == False %}
|
||||
<tr>
|
||||
<td colspan='3'>
|
||||
<span class='warning-msg'><i>Note: BOM pricing is incomplete for this part</i></span>
|
||||
<span class='warning-msg'><i>{% trans 'Note: BOM pricing is incomplete for this part' %}</i></span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan='3'>
|
||||
<span class='warning-msg'><i>No BOM pricing available</i></span>
|
||||
<span class='warning-msg'><i>{% trans 'No BOM pricing available' %}</i></span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
@ -80,7 +82,7 @@ Pricing information for:<br>
|
||||
{% if min_unit_buy_price or min_unit_bom_price %}
|
||||
{% else %}
|
||||
<div class='alert alert-danger alert-block'>
|
||||
No pricing information is available for this part.
|
||||
{% trans 'No pricing information is available for this part.' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
@ -342,7 +342,7 @@ class PartSetCategory(AjaxUpdateView):
|
||||
|
||||
data = {
|
||||
'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:
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf.urls import url, include
|
||||
from django.core.exceptions import ValidationError, FieldError
|
||||
from django.http import HttpResponse
|
||||
|
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(
|
||||
upload_to=rename_snippet,
|
||||
verbose_name=_('Snippet'),
|
||||
help_text=_('Report snippet file'),
|
||||
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):
|
||||
@ -536,7 +537,8 @@ class ReportAsset(models.Model):
|
||||
|
||||
asset = models.FileField(
|
||||
upload_to=rename_asset,
|
||||
verbose_name=_('Asset'),
|
||||
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"))
|
||||
|
@ -11,6 +11,7 @@ from django.conf.urls import url, include
|
||||
from django.urls import reverse
|
||||
from django.http import JsonResponse
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .models import StockLocation, StockItem
|
||||
from .models import StockItemTracking
|
||||
@ -195,7 +196,7 @@ class StockCount(StockAdjust):
|
||||
if item['item'].stocktake(item['quantity'], request.user, notes=self.notes):
|
||||
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):
|
||||
@ -264,7 +265,7 @@ class StockTransfer(StockAdjust):
|
||||
if item['item'].move(location, self.notes, request.user, quantity=item['quantity']):
|
||||
n += 1
|
||||
|
||||
return Response({'success': 'Moved {n} parts to {loc}'.format(
|
||||
return Response({'success': _('Moved {n} parts to {loc}').format(
|
||||
n=n,
|
||||
loc=str(location),
|
||||
)})
|
||||
|
@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.forms.utils import ErrorDict
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
@ -111,10 +111,11 @@ class CreateStockItemForm(HelperForm):
|
||||
""" Form for creating a new StockItem """
|
||||
|
||||
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)'))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@ -165,13 +166,13 @@ class CreateStockItemForm(HelperForm):
|
||||
class SerializeStockForm(HelperForm):
|
||||
""" 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):
|
||||
|
||||
@ -263,7 +264,7 @@ class ExportOptionsForm(HelperForm):
|
||||
|
||||
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:
|
||||
model = StockLocation
|
||||
@ -402,7 +403,8 @@ class EditStockItemForm(HelperForm):
|
||||
"""
|
||||
|
||||
expiry_date = DatePickerFormField(
|
||||
help_text=('Expiration date for this stock item'),
|
||||
label=_('Expiry Date'),
|
||||
help_text=_('Expiration date for this stock item'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
92
InvenTree/stock/migrations/0059_auto_20210404_2016.py
Normal file
92
InvenTree/stock/migrations/0059_auto_20210404_2016.py
Normal file
@ -0,0 +1,92 @@
|
||||
# Generated by Django 3.0.7 on 2021-04-04 20:16
|
||||
|
||||
import InvenTree.fields
|
||||
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
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0005_owner_model'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('stock', '0058_stockitem_packaging'),
|
||||
]
|
||||
|
||||
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(
|
||||
model_name='stockitemattachment',
|
||||
name='attachment',
|
||||
field=models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment, verbose_name='Attachment'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitemattachment',
|
||||
name='comment',
|
||||
field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitemattachment',
|
||||
name='upload_date',
|
||||
field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitemattachment',
|
||||
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='stockitemtracking',
|
||||
name='link',
|
||||
field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page for further information', verbose_name='Link'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitemtracking',
|
||||
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(
|
||||
model_name='stockitemtracking',
|
||||
name='title',
|
||||
field=models.CharField(help_text='Tracking entry title', max_length=250, verbose_name='Title'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stocklocation',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stocklocation',
|
||||
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(
|
||||
model_name='stocklocation',
|
||||
name='parent',
|
||||
field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='stock.StockLocation', verbose_name='parent'),
|
||||
),
|
||||
]
|
@ -51,7 +51,8 @@ class StockLocation(InvenTreeTree):
|
||||
"""
|
||||
|
||||
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')
|
||||
|
||||
def get_absolute_url(self):
|
||||
@ -483,7 +484,7 @@ class StockItem(MPTTModel):
|
||||
|
||||
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(
|
||||
default=StockStatus.OK,
|
||||
@ -507,7 +508,8 @@ class StockItem(MPTTModel):
|
||||
)
|
||||
|
||||
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')
|
||||
|
||||
def is_stale(self):
|
||||
@ -948,7 +950,7 @@ class StockItem(MPTTModel):
|
||||
raise ValidationError({"quantity": _("Quantity must be greater than zero")})
|
||||
|
||||
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]:
|
||||
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)
|
||||
|
||||
# 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
|
||||
def copyHistoryFrom(self, other):
|
||||
@ -1548,17 +1550,17 @@ class StockItemTracking(models.Model):
|
||||
|
||||
date = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
|
||||
title = models.CharField(blank=False, max_length=250, help_text=_('Tracking entry title'))
|
||||
title = models.CharField(blank=False, max_length=250, verbose_name=_('Title'), help_text=_('Tracking entry title'))
|
||||
|
||||
notes = models.CharField(blank=True, max_length=512, help_text=_('Entry notes'))
|
||||
notes = models.CharField(blank=True, max_length=512, verbose_name=_('Notes'), help_text=_('Entry notes'))
|
||||
|
||||
link = InvenTreeURLField(blank=True, help_text=_('Link to external page for further information'))
|
||||
link = InvenTreeURLField(blank=True, verbose_name=_('Link'), help_text=_('Link to external page for further information'))
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
|
||||
|
||||
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
|
||||
# image = models.ImageField(upload_to=func, max_length=255, null=True, blank=True)
|
||||
|
@ -451,7 +451,7 @@ $("#stock-edit").click(function () {
|
||||
"{% url 'stock-item-edit' item.id %}",
|
||||
{
|
||||
reload: true,
|
||||
submit_text: "Save",
|
||||
submit_text: '{% trans "Save" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -160,7 +160,7 @@
|
||||
|
||||
$("#stock-export").click(function() {
|
||||
launchModalForm("{% url 'stock-export-options' %}", {
|
||||
submit_text: "Export",
|
||||
submit_text: '{% trans "Export" %}',
|
||||
success: function(response) {
|
||||
var url = "{% url 'stock-export' %}";
|
||||
|
||||
@ -188,8 +188,8 @@
|
||||
secondary: [
|
||||
{
|
||||
field: 'parent',
|
||||
label: 'New Location',
|
||||
title: 'Create new location',
|
||||
label: '{% trans "New Location" %}',
|
||||
title: '{% trans "Create new location" %}',
|
||||
url: "{% url 'stock-location-create' %}",
|
||||
},
|
||||
]
|
||||
|
@ -40,7 +40,7 @@
|
||||
<input type='hidden' name='stock-id-{{ item.id }}' value='{{ item.new_quantity }}'/>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><button class='btn btn-default btn-remove' onclick='removeStockRow()' id='del-{{ item.id }}' title='Remove item' type='button'><span row='stock-row-{{ item.id }}' class='fas fa-trash-alt icon-red'></span></button></td>
|
||||
<td><button class='btn btn-default btn-remove' onclick='removeStockRow()' id='del-{{ item.id }}' title='{% trans "Remove item" %}' type='button'><span row='stock-row-{{ item.id }}' class='fas fa-trash-alt icon-red'></span></button></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -14,7 +14,7 @@ from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from moneyed import CURRENCIES
|
||||
|
||||
@ -965,7 +965,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
|
||||
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
|
||||
context['edit_quantity'] = not self.stock_action == 'delete'
|
||||
@ -993,17 +993,18 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
if self.stock_action not in ['move', 'count', 'take', 'add', 'delete']:
|
||||
self.stock_action = 'count'
|
||||
|
||||
# Choose the form title based on the action
|
||||
# Choose form title and action column based on the action
|
||||
titles = {
|
||||
'move': _('Move Stock Items'),
|
||||
'count': _('Count Stock Items'),
|
||||
'take': _('Remove From Stock'),
|
||||
'add': _('Add Stock Items'),
|
||||
'delete': _('Delete Stock Items')
|
||||
'move': [_('Move Stock Items'), _('Move')],
|
||||
'count': [_('Count Stock Items'), _('Count')],
|
||||
'take': [_('Remove From Stock'), _('Take')],
|
||||
'add': [_('Add Stock Items'), _('Add')],
|
||||
'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!
|
||||
self.stock_items = self.get_GET_items()
|
||||
|
||||
@ -1039,7 +1040,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
if self.stock_action in ['move', 'take']:
|
||||
|
||||
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
|
||||
continue
|
||||
|
||||
@ -1118,7 +1119,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
|
||||
count += 1
|
||||
|
||||
return f"{_('Added stock to ')} {count} {_('items')}"
|
||||
return _('Added stock to {n} items').format(n=count)
|
||||
|
||||
def do_take(self):
|
||||
|
||||
@ -1133,7 +1134,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
|
||||
count += 1
|
||||
|
||||
return f"{_('Removed stock from ')} {count} {_('items')}"
|
||||
return _('Removed stock from {n} items').format(n=count)
|
||||
|
||||
def do_count(self):
|
||||
|
||||
@ -1189,9 +1190,9 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
return _('No items were moved')
|
||||
|
||||
else:
|
||||
return _('Moved {n} items to {dest}'.format(
|
||||
return _('Moved {n} items to {dest}').format(
|
||||
n=count,
|
||||
dest=destination.pathstring))
|
||||
dest=destination.pathstring)
|
||||
|
||||
def do_delete(self):
|
||||
""" Delete multiple stock items """
|
||||
@ -1208,7 +1209,7 @@ class StockAdjust(AjaxView, FormMixin):
|
||||
|
||||
count += 1
|
||||
|
||||
return _("Deleted {n} stock items".format(n=count))
|
||||
return _("Deleted {n} stock items").format(n=count)
|
||||
|
||||
|
||||
class StockItemEdit(AjaxUpdateView):
|
||||
|
@ -133,14 +133,14 @@ InvenTree | {% trans "Search Results" %}
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: 'Name',
|
||||
title: '{% trans "Name" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return renderLink(value, '/part/category/' + row.pk + '/');
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: 'Description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
],
|
||||
});
|
||||
@ -270,14 +270,14 @@ InvenTree | {% trans "Search Results" %}
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: 'Name',
|
||||
title: '{% trans "Name" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return renderLink(row.pathstring, '/stock/location/' + row.pk + '/');
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: 'Description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -8,7 +8,7 @@
|
||||
<span class='fas {{ icon }}'></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><b>{{ setting.name }}</b></td>
|
||||
<td><b>{% trans setting.name %}</b></td>
|
||||
<td>
|
||||
{% if setting.is_bool %}
|
||||
<div>
|
||||
@ -24,7 +24,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<td>
|
||||
{{ setting.description }}
|
||||
{% trans setting.description %}
|
||||
</td>
|
||||
<td>
|
||||
<div class='btn-group float-right'>
|
||||
|
@ -34,16 +34,20 @@
|
||||
<td>{% trans "Django Version" %}</td>
|
||||
<td><a href="https://www.djangoproject.com/">{% django_version %}</a></td>
|
||||
</tr>
|
||||
{% inventree_commit_hash as hash %}
|
||||
{% if hash %}
|
||||
<tr>
|
||||
<td><span class='fas fa-code-branch'></span></td>
|
||||
<td>{% trans "Commit Hash" %}</td>
|
||||
<td><a href="https://github.com/inventree/InvenTree/commit/{% inventree_commit_hash %}">{% inventree_commit_hash %}</a></td>
|
||||
<td>{% trans "Commit Hash" %}</td><td>{{ hash }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% inventree_commit_date as commit_date %}
|
||||
{% if commit_date %}
|
||||
<tr>
|
||||
<td><span class='fas fa-calendar-alt'></span></td>
|
||||
<td>{% trans "Commit Date" %}</td>
|
||||
<td>{% inventree_commit_date %}</td>
|
||||
<td>{% trans "Commit Date" %}</td><td>{{ commit_date }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td><span class='fas fa-book'></span></td>
|
||||
<td>{% trans "InvenTree Documentation" %}</td>
|
||||
@ -74,4 +78,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -134,12 +134,12 @@ InvenTree
|
||||
<script type='text/javascript' src='{% static "bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js" %}'></script>
|
||||
|
||||
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'fullcalendar/locales-all.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
||||
|
||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/filters.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/sidenav.js' %}"></script>
|
||||
|
||||
@ -156,6 +156,7 @@ InvenTree
|
||||
<script type='text/javascript' src="{% url 'calendar.js' %}"></script>
|
||||
<script type='text/javascript' src="{% url 'tables.js' %}"></script>
|
||||
<script type='text/javascript' src="{% url 'table_filters.js' %}"></script>
|
||||
<script type='text/javascript' src="{% url 'filters.js' %}"></script>
|
||||
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/solid.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/brands.js' %}"></script>
|
||||
@ -180,6 +181,7 @@ $(document).ready(function () {
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
moment.locale('{{request.LANGUAGE_CODE}}');
|
||||
});
|
||||
|
||||
</script>
|
||||
|
@ -1,3 +1,5 @@
|
||||
{% load i18n %}
|
||||
|
||||
/**
|
||||
* Code for managing query filters / table options.
|
||||
*
|
||||
@ -188,7 +190,7 @@ function generateAvailableFilterList(tableKey) {
|
||||
|
||||
var html = `<select class='form-control filter-input' id='${id}' name='tag'>`;
|
||||
|
||||
html += `<option value=''>Select filter</option>`;
|
||||
html += "<option value=''>{% trans 'Select filter' %}</option>";
|
||||
|
||||
for (var opt in remaining) {
|
||||
var title = getFilterTitle(tableKey, opt);
|
||||
@ -263,10 +265,10 @@ function setupFilterList(tableKey, table, target) {
|
||||
// One blank slate, please
|
||||
element.empty();
|
||||
|
||||
element.append(`<button id='${add}' title='Add new filter' class='btn btn-default filter-tag'><span class='fas fa-filter'></span></button>`);
|
||||
element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-default filter-tag'><span class='fas fa-filter'></span></button>`);
|
||||
|
||||
if (Object.keys(filters).length > 0) {
|
||||
element.append(`<button id='${clear}' title='Clear all filters' class='btn btn-default filter-tag'><span class='fas fa-trash-alt'></span></button>`);
|
||||
element.append(`<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-default filter-tag'><span class='fas fa-trash-alt'></span></button>`);
|
||||
}
|
||||
|
||||
for (var key in filters) {
|
||||
@ -291,7 +293,7 @@ function setupFilterList(tableKey, table, target) {
|
||||
html += generateAvailableFilterList(tableKey);
|
||||
html += generateFilterInput(tableKey);
|
||||
|
||||
html += `<button title='Create filter' class='btn btn-default filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
html += `<button title='{% trans "Create filter" %}' class='btn btn-default filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
|
||||
//html += '</div>';
|
||||
|
@ -253,7 +253,7 @@ function loadingMessageContent() {
|
||||
*/
|
||||
|
||||
// TODO - This can be made a lot better
|
||||
return "<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> Waiting for server...";
|
||||
return "<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> {% trans 'Waiting for server...' %}";
|
||||
}
|
||||
|
||||
|
||||
|
@ -976,8 +976,8 @@ function loadStockTrackingTable(table, options) {
|
||||
formatter: function(value, row, index, field) {
|
||||
// Manually created entries can be edited or deleted
|
||||
if (!row.system) {
|
||||
var bEdit = "<button title='Edit tracking entry' class='btn btn-entry-edit btn-default btn-glyph' type='button' url='/stock/track/" + row.pk + "/edit/'><span class='fas fa-edit'/></button>";
|
||||
var bDel = "<button title='Delete tracking entry' class='btn btn-entry-delete btn-default btn-glyph' type='button' url='/stock/track/" + row.pk + "/delete/'><span class='fas fa-trash-alt icon-red'/></button>";
|
||||
var bEdit = "<button title='{% trans 'Edit tracking entry' %}' class='btn btn-entry-edit btn-default btn-glyph' type='button' url='/stock/track/" + row.pk + "/edit/'><span class='fas fa-edit'/></button>";
|
||||
var bDel = "<button title='{% trans 'Delete tracking entry' %}' class='btn btn-entry-delete btn-default btn-glyph' type='button' url='/stock/track/" + row.pk + "/delete/'><span class='fas fa-trash-alt icon-red'/></button>";
|
||||
|
||||
return "<div class='btn-group' role='group'>" + bEdit + bDel + "</div>";
|
||||
} else {
|
||||
|
@ -1 +1 @@
|
||||
<button type='button' class='btn btn-default' id='show-qr-code' title='Show QR code'><span class='fas fa-qrcode'></span></button>
|
||||
<button type='button' class='btn btn-default' id='show-qr-code' title='{% trans "Show QR Code" %}'><span class='fas fa-qrcode'></span></button>
|
Loading…
Reference in New Issue
Block a user