mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-18 12:56:31 -06:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0804ecf538 | ||
|
|
39176d9a19 | ||
|
|
75f507d4c7 | ||
|
|
c826b8231f | ||
|
|
a05e07b2dd | ||
|
|
fc4e20f1b7 | ||
|
|
9494a8ad43 | ||
|
|
c12e6dbf42 | ||
|
|
3daf85c3d0 | ||
|
|
8fe5dcfafb | ||
|
|
6a66be36f2 | ||
|
|
12cd6a915b | ||
|
|
e81349e70e | ||
|
|
2c6bc8852b | ||
|
|
774d0f50fd | ||
|
|
3a37947dbb | ||
|
|
e0b2895ef5 | ||
|
|
9aa859a428 | ||
|
|
ddd65cff5e | ||
|
|
1abdb1fd46 | ||
|
|
2b0ef2bc61 | ||
|
|
f259fa6792 | ||
|
|
55f09d8723 | ||
|
|
4973d9c726 | ||
|
|
e22779872e |
@@ -1,6 +1,7 @@
|
||||
"""Admin classes"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.db.models.fields import CharField
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
from djmoney.contrib.exchange.admin import RateAdmin
|
||||
@@ -83,7 +84,16 @@ class InvenTreeResource(ModelResource):
|
||||
return [f for f in fields if f.column_name not in fields_to_exclude]
|
||||
|
||||
def before_import_row(self, row, row_number=None, **kwargs):
|
||||
"""Run custom code before importing each row"""
|
||||
"""Run custom code before importing each row.
|
||||
|
||||
- Convert any null fields to empty strings, for fields which do not support null values
|
||||
"""
|
||||
# We can automatically determine which fields might need such a conversion
|
||||
for field in self.Meta.model._meta.fields:
|
||||
if isinstance(field, CharField):
|
||||
if field.blank and not field.null:
|
||||
if field.name not in self.CONVERT_NULL_FIELDS:
|
||||
self.CONVERT_NULL_FIELDS.append(field.name)
|
||||
|
||||
for field in self.CONVERT_NULL_FIELDS:
|
||||
if field in row and row[field] is None:
|
||||
|
||||
@@ -9,7 +9,6 @@ import traceback
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.db.utils import IntegrityError, OperationalError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import rest_framework.views as drfviews
|
||||
@@ -23,13 +22,18 @@ import InvenTree.sentry
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
def log_error(path):
|
||||
def log_error(path, error_name=None, error_info=None, error_data=None):
|
||||
"""Log an error to the database.
|
||||
|
||||
- Uses python exception handling to extract error details
|
||||
|
||||
Arguments:
|
||||
path: The 'path' (most likely a URL) associated with this error (optional)
|
||||
|
||||
kwargs:
|
||||
error_name: The name of the error (optional, overrides 'kind')
|
||||
error_info: The error information (optional, overrides 'info')
|
||||
error_data: The error data (optional, overrides 'data')
|
||||
"""
|
||||
kind, info, data = sys.exc_info()
|
||||
|
||||
@@ -37,19 +41,31 @@ def log_error(path):
|
||||
if kind in settings.IGNORED_ERRORS:
|
||||
return
|
||||
|
||||
if error_name:
|
||||
kind = error_name
|
||||
else:
|
||||
kind = getattr(kind, '__name__', 'Unknown Error')
|
||||
|
||||
if error_info:
|
||||
info = error_info
|
||||
|
||||
if error_data:
|
||||
data = error_data
|
||||
else:
|
||||
data = '\n'.join(traceback.format_exception(kind, info, data))
|
||||
|
||||
# Log error to stderr
|
||||
logger.error(info)
|
||||
|
||||
# Ensure the error information does not exceed field size limits
|
||||
path = path[:200]
|
||||
kind = kind[:128]
|
||||
|
||||
try:
|
||||
Error.objects.create(
|
||||
kind=kind.__name__,
|
||||
info=info,
|
||||
data='\n'.join(traceback.format_exception(kind, info, data)),
|
||||
path=path,
|
||||
)
|
||||
except (OperationalError, IntegrityError):
|
||||
Error.objects.create(kind=kind, info=info or '', data=data or '', path=path)
|
||||
except Exception:
|
||||
# Not much we can do if logging the error throws a db exception
|
||||
pass
|
||||
logger.exception('Failed to log exception to database')
|
||||
|
||||
|
||||
def exception_handler(exc, context):
|
||||
|
||||
@@ -361,9 +361,16 @@ class CustomSocialAccountAdapter(CustomUrlMixin, RegistratonMixin, DefaultSocial
|
||||
|
||||
def authentication_error(self, request, provider_id, error=None, exception=None, extra_context=None):
|
||||
"""Callback method for authentication errors."""
|
||||
if not error:
|
||||
error = request.GET.get('error', None)
|
||||
|
||||
if not exception:
|
||||
exception = request.GET.get('error_description', None)
|
||||
|
||||
path = request.path or 'sso'
|
||||
|
||||
# Log the error to the database
|
||||
log_error(request.path if request else 'sso')
|
||||
log_error(path, error_name=error, error_data=exception)
|
||||
logger.error("SSO error for provider '%s' - check admin error log", provider_id)
|
||||
|
||||
|
||||
|
||||
@@ -970,6 +970,11 @@ SOCIALACCOUNT_PROVIDERS = get_setting('INVENTREE_SOCIAL_PROVIDERS', 'social_prov
|
||||
|
||||
SOCIALACCOUNT_STORE_TOKENS = True
|
||||
|
||||
# Explicitly set empty URL prefix for OIDC
|
||||
# The SOCIALACCOUNT_OPENID_CONNECT_URL_PREFIX setting was introduced in v0.60.0
|
||||
# Ref: https://github.com/pennersr/django-allauth/blob/0.60.0/ChangeLog.rst#backwards-incompatible-changes
|
||||
SOCIALACCOUNT_OPENID_CONNECT_URL_PREFIX = ''
|
||||
|
||||
# settings for allauth
|
||||
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = get_setting('INVENTREE_LOGIN_CONFIRM_DAYS', 'login_confirm_days', 3, typecast=int)
|
||||
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = get_setting('INVENTREE_LOGIN_ATTEMPTS', 'login_attempts', 5, typecast=int)
|
||||
|
||||
@@ -19,7 +19,7 @@ from dulwich.repo import NotGitRepository, Repo
|
||||
from .api_version import INVENTREE_API_TEXT, INVENTREE_API_VERSION
|
||||
|
||||
# InvenTree software version
|
||||
INVENTREE_SW_VERSION = "0.13.1"
|
||||
INVENTREE_SW_VERSION = "0.13.5"
|
||||
|
||||
# Discover git
|
||||
try:
|
||||
|
||||
@@ -270,7 +270,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
'{% url "api-build-detail" build.pk %}',
|
||||
{
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Build Order" %}',
|
||||
title: '{% jstrans "Delete Build Order" %}',
|
||||
redirect: "{% url 'build-index' %}",
|
||||
}
|
||||
);
|
||||
@@ -280,7 +280,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<!-- Barcode functionality callbacks -->
|
||||
$('#show-qr-code').click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Build Order QR Code" %}',
|
||||
'{% jstrans "Build Order QR Code" %}',
|
||||
'{"build": {{ build.pk }} }'
|
||||
);
|
||||
});
|
||||
@@ -292,7 +292,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
build: {{ build.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Build Order" %}',
|
||||
title: '{% jstrans "Link Barcode to Build Order" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -419,8 +419,8 @@ function allocateSelectedLines() {
|
||||
|
||||
if (unallocated_lines.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Allocation Complete" %}',
|
||||
'{% trans "All lines have been fully allocated" %}',
|
||||
'{% jstrans "Allocation Complete" %}',
|
||||
'{% jstrans "All lines have been fully allocated" %}',
|
||||
);
|
||||
} else {
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ class NotificationMethod:
|
||||
return False
|
||||
|
||||
# Check if method globally enabled
|
||||
plg_instance = registry.plugins.get(plg_cls.NAME.lower())
|
||||
plg_instance = registry.get_plugin(plg_cls.NAME.lower())
|
||||
if plg_instance and not plg_instance.get_setting(self.GLOBAL_SETTING):
|
||||
return True
|
||||
|
||||
@@ -390,7 +390,7 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
|
||||
|
||||
# Collect possible methods
|
||||
if delivery_methods is None:
|
||||
delivery_methods = storage.liste
|
||||
delivery_methods = storage.liste or []
|
||||
else:
|
||||
delivery_methods = (delivery_methods - IGNORED_NOTIFICATION_CLS)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ from django.db.utils import IntegrityError, OperationalError
|
||||
from django.utils import timezone
|
||||
|
||||
import feedparser
|
||||
import requests
|
||||
|
||||
from InvenTree.helpers_model import getModelsWithMixin
|
||||
from InvenTree.models import InvenTreeNotesMixin
|
||||
@@ -45,11 +46,16 @@ def update_news_feed():
|
||||
logger.info("Could not perform 'update_news_feed' - App registry not ready")
|
||||
return
|
||||
|
||||
# News feed isn't defined, no need to continue
|
||||
if not settings.INVENTREE_NEWS_URL or type(settings.INVENTREE_NEWS_URL) is not str:
|
||||
return
|
||||
|
||||
# Fetch and parse feed
|
||||
try:
|
||||
d = feedparser.parse(settings.INVENTREE_NEWS_URL)
|
||||
except Exception as entry: # pragma: no cover
|
||||
logger.warning("update_news_feed: Error parsing the newsfeed", entry)
|
||||
feed = requests.get(settings.INVENTREE_NEWS_URL)
|
||||
d = feedparser.parse(feed.content)
|
||||
except Exception: # pragma: no cover
|
||||
logger.warning('update_news_feed: Error parsing the newsfeed')
|
||||
return
|
||||
|
||||
# Get a reference list
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"""Tests for tasks in app common."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
|
||||
from common.models import NotificationEntry
|
||||
from common.models import NewsFeedEntry, NotificationEntry
|
||||
from InvenTree.tasks import offload_task
|
||||
|
||||
from . import tasks as common_tasks
|
||||
@@ -15,4 +16,78 @@ class TaskTest(TestCase):
|
||||
"""Test that the task `delete_old_notifications` runs through without errors."""
|
||||
# check empty run
|
||||
self.assertEqual(NotificationEntry.objects.all().count(), 0)
|
||||
offload_task(common_tasks.delete_old_notifications,)
|
||||
offload_task(common_tasks.delete_old_notifications)
|
||||
|
||||
|
||||
class NewsFeedTests(TestCase):
|
||||
"""Tests for update_news_feed task.
|
||||
|
||||
Tests cover different networking and addressing possibilities.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""Setup for tests."""
|
||||
# Needs to be set to allow SQLite to store entries
|
||||
settings.USE_TZ = True
|
||||
|
||||
# Store setting to restore on teardown
|
||||
self.news_url = settings.INVENTREE_NEWS_URL
|
||||
|
||||
NewsFeedEntry.objects.all().delete()
|
||||
|
||||
def tearDown(self):
|
||||
"""Teardown for tests."""
|
||||
# Restore proper setting
|
||||
settings.INVENTREE_NEWS_URL = self.news_url
|
||||
|
||||
NewsFeedEntry.objects.all().delete()
|
||||
|
||||
def test_valid_url(self):
|
||||
"""Tests that news feed is updated when accessing a valid URL."""
|
||||
try:
|
||||
common_tasks.update_news_feed()
|
||||
except Exception as ex:
|
||||
self.fail(f'News feed raised exceptions: {ex}')
|
||||
|
||||
self.assertNotEqual(NewsFeedEntry.objects.all().count(), 0)
|
||||
|
||||
def test_connection_error(self):
|
||||
"""Test connecting to an unavailable endpoint.
|
||||
|
||||
This also emulates calling the endpoint behind a blocking proxy.
|
||||
"""
|
||||
settings.INVENTREE_NEWS_URL = 'http://10.255.255.1:81'
|
||||
|
||||
with self.assertLogs('inventree', level='WARNING'):
|
||||
common_tasks.update_news_feed()
|
||||
|
||||
self.assertEqual(NewsFeedEntry.objects.all().count(), 0)
|
||||
|
||||
def test_unset_url(self):
|
||||
"""Test that no call is made to news feed if URL setting is invalid."""
|
||||
settings.INVENTREE_NEWS_URL = ''
|
||||
|
||||
self.assertTrue(
|
||||
offload_task(common_tasks.update_news_feed)
|
||||
) # Task considered complete
|
||||
self.assertEqual(
|
||||
NewsFeedEntry.objects.all().count(), 0
|
||||
) # No Feed entries created
|
||||
|
||||
settings.INVENTREE_NEWS_URL = 0
|
||||
|
||||
self.assertTrue(
|
||||
offload_task(common_tasks.update_news_feed)
|
||||
) # Task considered complete
|
||||
self.assertEqual(
|
||||
NewsFeedEntry.objects.all().count(), 0
|
||||
) # No Feed entries created
|
||||
|
||||
settings.INVENTREE_NEWS_URL = None
|
||||
|
||||
self.assertTrue(
|
||||
offload_task(common_tasks.update_news_feed)
|
||||
) # Task considered complete
|
||||
self.assertEqual(
|
||||
NewsFeedEntry.objects.all().count(), 0
|
||||
) # No Feed entries created
|
||||
|
||||
@@ -337,6 +337,7 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer):
|
||||
read_only_fields = [
|
||||
'availability_updated',
|
||||
'barcode_hash',
|
||||
'pack_quantity_native',
|
||||
]
|
||||
|
||||
tags = TagListSerializerField(required=False)
|
||||
@@ -375,6 +376,8 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer):
|
||||
in_stock = serializers.FloatField(read_only=True)
|
||||
available = serializers.FloatField(required=False)
|
||||
|
||||
pack_quantity_native = serializers.FloatField(read_only=True)
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||
|
||||
supplier_detail = CompanyBriefSerializer(source='supplier', many=False, read_only=True)
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
$('#company-delete').click(function() {
|
||||
constructForm('{% url "api-company-detail" company.pk %}', {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Company" %}',
|
||||
title: '{% jstrans "Delete Company" %}',
|
||||
redirect: '{% url "company-index" %}',
|
||||
});
|
||||
});
|
||||
@@ -202,10 +202,10 @@
|
||||
$('#company-image-delete').click(function(event) {
|
||||
event.stopPropagation();
|
||||
showQuestionDialog(
|
||||
'{% trans "Remove Image" %}',
|
||||
'{% trans "Remove associated image from this company" %}',
|
||||
'{% jstrans "Remove Image" %}',
|
||||
'{% jstrans "Remove associated image from this company" %}',
|
||||
{
|
||||
accept_text: '{% trans "Remove" %}',
|
||||
accept_text: '{% jstrans "Remove" %}',
|
||||
submitClass: 'danger',
|
||||
accept: function() {
|
||||
inventreePut(
|
||||
@@ -234,7 +234,7 @@
|
||||
fields: {
|
||||
image: {},
|
||||
},
|
||||
title: '{% trans "Upload Image" %}',
|
||||
title: '{% jstrans "Upload Image" %}',
|
||||
onSuccess: function(data) {
|
||||
reloadImage(data);
|
||||
}
|
||||
@@ -249,7 +249,7 @@
|
||||
'{% url "api-company-detail" company.pk %}',
|
||||
{
|
||||
method: 'PATCH',
|
||||
title: '{% trans "Download Image" %}',
|
||||
title: '{% jstrans "Download Image" %}',
|
||||
fields: {
|
||||
remote_image: {},
|
||||
},
|
||||
|
||||
@@ -203,7 +203,7 @@ $('#parameter-create').click(function() {
|
||||
hidden: true,
|
||||
}
|
||||
},
|
||||
title: '{% trans "Add Parameter" %}',
|
||||
title: '{% jstrans "Add Parameter" %}',
|
||||
refreshTable: '#parameter-table',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -273,7 +273,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
|
||||
$("#show-qr-code").click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Supplier Part QR Code" %}',
|
||||
'{% jstrans "Supplier Part QR Code" %}',
|
||||
'{"supplierpart": {{ part.pk }} }'
|
||||
);
|
||||
});
|
||||
@@ -284,7 +284,7 @@ $("#barcode-link").click(function() {
|
||||
supplierpart: {{ part.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Supplier Part" %}',
|
||||
title: '{% jstrans "Link Barcode to Supplier Part" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -356,7 +356,7 @@ $('#update-part-availability').click(function() {
|
||||
fields: {
|
||||
available: {},
|
||||
},
|
||||
title: '{% trans "Update Part Availability" %}',
|
||||
title: '{% jstrans "Update Part Availability" %}',
|
||||
onSuccess: function() {
|
||||
location.reload();
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ class SalesOrderLineItemResource(PriceResourceMixin, InvenTreeResource):
|
||||
Ref: https://github.com/inventree/InvenTree/issues/2207
|
||||
"""
|
||||
if item.sale_price:
|
||||
return str(item.sale_price)
|
||||
return item.sale_price.amount
|
||||
return ''
|
||||
|
||||
|
||||
|
||||
@@ -1385,7 +1385,12 @@ class SalesOrderLineItem(OrderLineItem):
|
||||
|
||||
def fulfilled_quantity(self):
|
||||
"""Return the total stock quantity fulfilled against this line item."""
|
||||
query = self.order.stock_items.filter(part=self.part).aggregate(fulfilled=Coalesce(Sum('quantity'), Decimal(0)))
|
||||
if not self.pk:
|
||||
return 0
|
||||
|
||||
query = self.order.stock_items.filter(part=self.part).aggregate(
|
||||
fulfilled=Coalesce(Sum('quantity'), Decimal(0))
|
||||
)
|
||||
|
||||
return query['fulfilled']
|
||||
|
||||
@@ -1394,7 +1399,12 @@ class SalesOrderLineItem(OrderLineItem):
|
||||
|
||||
This is a summation of the quantity of each attached StockItem
|
||||
"""
|
||||
query = self.allocations.aggregate(allocated=Coalesce(Sum('quantity'), Decimal(0)))
|
||||
if not self.pk:
|
||||
return 0
|
||||
|
||||
query = self.allocations.aggregate(
|
||||
allocated=Coalesce(Sum('quantity'), Decimal(0))
|
||||
)
|
||||
|
||||
return query['allocated']
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ $("#export-order").click(function() {
|
||||
<!-- Barcode functionality callbacks -->
|
||||
$('#show-qr-code').click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Purchase Order QR Code" %}',
|
||||
'{% jstrans "Purchase Order QR Code" %}',
|
||||
'{"purchaseorder": {{ order.pk }} }'
|
||||
);
|
||||
});
|
||||
@@ -327,7 +327,7 @@ $("#barcode-link").click(function() {
|
||||
purchaseorder: {{ order.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Purchase Order" %}',
|
||||
title: '{% jstrans "Link Barcode to Purchase Order" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -260,7 +260,7 @@ $('#print-order-report').click(function() {
|
||||
<!-- Barcode functionality callbacks -->
|
||||
$('#show-qr-code').click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Return Order QR Code" %}',
|
||||
'{% jstrans "Return Order QR Code" %}',
|
||||
'{"returnorder": {{ order.pk }} }'
|
||||
);
|
||||
});
|
||||
@@ -272,7 +272,7 @@ $("#barcode-link").click(function() {
|
||||
returnorder: {{ order.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Return Order" %}',
|
||||
title: '{% jstrans "Link Barcode to Return Order" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -309,7 +309,7 @@ $('#print-order-report').click(function() {
|
||||
<!-- Barcode functionality callbacks -->
|
||||
$('#show-qr-code').click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Sales Order QR Code" %}',
|
||||
'{% jstrans "Sales Order QR Code" %}',
|
||||
'{"salesorder": {{ order.pk }} }'
|
||||
);
|
||||
});
|
||||
@@ -321,7 +321,7 @@ $("#barcode-link").click(function() {
|
||||
salesorder: {{ order.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Sales Order" %}',
|
||||
title: '{% jstrans "Link Barcode to Sales Order" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -57,7 +57,6 @@ class AddFieldOrSkip(migrations.AddField):
|
||||
|
||||
try:
|
||||
super().database_forwards(app_label, schema_editor, from_state, to_state)
|
||||
print(f'Added field {self.name} to model {self.model_name}')
|
||||
except Exception as exc:
|
||||
pass
|
||||
|
||||
|
||||
@@ -592,7 +592,10 @@ class PartSerializer(InvenTree.serializers.RemoteImageMixin, InvenTree.serialize
|
||||
|
||||
if not create:
|
||||
# These fields are only used for the LIST API endpoint
|
||||
for f in self.skip_create_fields()[1:]:
|
||||
for f in self.skip_create_fields():
|
||||
# Fields required for certain operations, but are not part of the model
|
||||
if f in ["remote_image", "existing_image"]:
|
||||
continue
|
||||
self.fields.pop(f)
|
||||
|
||||
if not pricing:
|
||||
|
||||
@@ -656,7 +656,7 @@
|
||||
value: {{ part.pk }},
|
||||
},
|
||||
part_2: {
|
||||
label: '{% trans "Related Part" %}',
|
||||
label: '{% jstrans "Related Part" %}',
|
||||
filters: {
|
||||
exclude_id: {{ part.pk }},
|
||||
exclude_related: {{ part.pk }},
|
||||
@@ -664,7 +664,7 @@
|
||||
}
|
||||
},
|
||||
focus: 'part_2',
|
||||
title: '{% trans "Add Related Part" %}',
|
||||
title: '{% jstrans "Add Related Part" %}',
|
||||
refreshTable: '#related-parts-table',
|
||||
});
|
||||
});
|
||||
@@ -749,7 +749,7 @@
|
||||
fields: partTestTemplateFields({
|
||||
part: {{ part.pk }}
|
||||
}),
|
||||
title: '{% trans "Add Test Result Template" %}',
|
||||
title: '{% jstrans "Add Test Result Template" %}',
|
||||
refreshTable: '#test-template-table',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -441,7 +441,7 @@
|
||||
{% if barcodes %}
|
||||
$("#show-qr-code").click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Part QR Code" %}',
|
||||
'{% jstrans "Part QR Code" %}',
|
||||
'{"part": {{ part.pk }} }',
|
||||
);
|
||||
});
|
||||
@@ -458,7 +458,7 @@
|
||||
part: {{ part.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Part" %}',
|
||||
title: '{% jstrans "Link Barcode to Part" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -469,7 +469,7 @@
|
||||
printLabels({
|
||||
items: [{{ part.pk }}],
|
||||
key: 'part',
|
||||
singular_name: '{% trans "part" %}',
|
||||
singular_name: 'part',
|
||||
url: '{% url "api-part-label-list" %}',
|
||||
});
|
||||
});
|
||||
@@ -509,7 +509,7 @@
|
||||
launchModalForm(
|
||||
"{% url 'part-pricing' part.id %}",
|
||||
{
|
||||
submit_text: '{% trans "Calculate" %}',
|
||||
submit_text: '{% jstrans "Calculate" %}',
|
||||
hideErrorMessage: true,
|
||||
}
|
||||
);
|
||||
@@ -525,10 +525,10 @@
|
||||
$('#part-image-delete').click(function(event) {
|
||||
event.stopPropagation();
|
||||
showQuestionDialog(
|
||||
'{% trans "Remove Image" %}',
|
||||
'{% trans "Remove associated image from this part" %}',
|
||||
'{% jstrans "Remove Image" %}',
|
||||
'{% jstrans "Remove associated image from this part" %}',
|
||||
{
|
||||
accept_text: '{% trans "Remove" %}',
|
||||
accept_text: '{% jstrans "Remove" %}',
|
||||
submitClass: 'danger',
|
||||
accept: function() {
|
||||
inventreePut(
|
||||
@@ -557,7 +557,7 @@
|
||||
fields: {
|
||||
image: {},
|
||||
},
|
||||
title: '{% trans "Upload Image" %}',
|
||||
title: '{% jstrans "Upload Image" %}',
|
||||
onSuccess: function(data) {
|
||||
reloadImage(data);
|
||||
}
|
||||
@@ -577,7 +577,7 @@
|
||||
sidePagination: 'server',
|
||||
singleSelect: true,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No matching images found" %}';
|
||||
return '{% jstrans "No matching images found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
@@ -611,7 +611,7 @@
|
||||
'{% url "api-part-detail" part.pk %}',
|
||||
{
|
||||
method: 'PATCH',
|
||||
title: '{% trans "Download Image" %}',
|
||||
title: '{% jstrans "Download Image" %}',
|
||||
fields: {
|
||||
remote_image: {},
|
||||
},
|
||||
@@ -673,13 +673,13 @@
|
||||
|
||||
// Callback function when the "part details" panel is shown
|
||||
$('#collapse-part-details').on('show.bs.collapse', function() {
|
||||
$('#toggle-details-button').html('{% trans "Hide Part Details" %}');
|
||||
$('#toggle-details-button').html('{% jstrans "Hide Part Details" %}');
|
||||
inventreeSave('show-part-details', true);
|
||||
});
|
||||
|
||||
// Callback function when the "part details" panel is hidden
|
||||
$('#collapse-part-details').on('hide.bs.collapse', function() {
|
||||
$('#toggle-details-button').html('{% trans "Show Part Details" %}');
|
||||
$('#toggle-details-button').html('{% jstrans "Show Part Details" %}');
|
||||
inventreeSave('show-part-details', false);
|
||||
});
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ $('#part-pricing-refresh').click(function() {
|
||||
|
||||
$('#part-pricing-edit').click(function() {
|
||||
constructForm('{% url "api-part-pricing" part.pk %}', {
|
||||
title: '{% trans "Update Pricing" %}',
|
||||
title: '{% jstrans "Update Pricing" %}',
|
||||
fields: {
|
||||
override_min: {},
|
||||
override_min_currency: {},
|
||||
|
||||
@@ -27,6 +27,14 @@ def translation_stats(lang_code):
|
||||
class CustomTranslateNode(TranslateNode):
|
||||
"""Custom translation node class, which sanitizes the translated strings for javascript use"""
|
||||
|
||||
def __init__(self, filter_expression, noop, asvar, message_context, escape=False):
|
||||
"""Custom constructor for TranslateNode class.
|
||||
|
||||
- Adds an 'escape' argument, which is passed to the render function
|
||||
"""
|
||||
super().__init__(filter_expression, noop, asvar, message_context)
|
||||
self.escape = escape
|
||||
|
||||
def render(self, context):
|
||||
"""Custom render function overrides / extends default behaviour"""
|
||||
result = super().render(context)
|
||||
@@ -41,18 +49,32 @@ class CustomTranslateNode(TranslateNode):
|
||||
for c in ['\\', '`', ';', '|', '&']:
|
||||
result = result.replace(c, '')
|
||||
|
||||
# Escape any quotes contained in the string
|
||||
result = result.replace("'", r"\'")
|
||||
result = result.replace('"', r'\"')
|
||||
# Escape any quotes contained in the string, if the request is for a javascript file
|
||||
request = context.get('request', None)
|
||||
|
||||
template = getattr(context, 'template_name', None)
|
||||
request = context.get('request', None)
|
||||
|
||||
escape = self.escape
|
||||
|
||||
if template and str(template).endswith('.js'):
|
||||
escape = True
|
||||
|
||||
if request and str(request.path).endswith('.js'):
|
||||
escape = True
|
||||
|
||||
if escape:
|
||||
result = result.replace("'", r'\'')
|
||||
result = result.replace('"', r'\"')
|
||||
|
||||
# Return the 'clean' resulting string
|
||||
return result
|
||||
|
||||
|
||||
@register.tag("translate")
|
||||
@register.tag("trans")
|
||||
def do_translate(parser, token):
|
||||
"""Custom translation function, lifted from https://github.com/django/django/blob/main/django/templatetags/i18n.py
|
||||
@register.tag('translate')
|
||||
@register.tag('trans')
|
||||
def do_translate(parser, token, escape=False):
|
||||
"""Custom translation function.
|
||||
|
||||
The only difference is that we pass this to our custom rendering node class
|
||||
"""
|
||||
@@ -109,7 +131,21 @@ def do_translate(parser, token):
|
||||
)
|
||||
seen.add(option)
|
||||
|
||||
return CustomTranslateNode(message_string, noop, asvar, message_context)
|
||||
return CustomTranslateNode(
|
||||
message_string, noop, asvar, message_context, escape=escape
|
||||
)
|
||||
|
||||
|
||||
@register.tag('jstrans')
|
||||
def do_jstrans(parser, token):
|
||||
"""Custom translation function for javascript strings.
|
||||
|
||||
- Usage: {% jstrans "String to translate" %}
|
||||
- Performs the same function as the 'trans' tag, but also escapes the translated string.
|
||||
- Explicitly required for javascript code within a .html template
|
||||
- Note: Any {% trans %} tag is automatically escaped in a .js file
|
||||
"""
|
||||
return do_translate(parser, token, escape=True)
|
||||
|
||||
|
||||
# Re-register tags which we have not explicitly overridden
|
||||
|
||||
@@ -1741,6 +1741,64 @@ class PartDetailTests(PartAPITestBase):
|
||||
|
||||
self.assertEqual(response.data['image'], image_name)
|
||||
|
||||
def test_update_existing_image(self):
|
||||
"""Test that we can update the image of an existing part with an already existing image"""
|
||||
# First, upload an image for an existing part
|
||||
p = Part.objects.first()
|
||||
|
||||
fn = 'part_image_123abc.png'
|
||||
|
||||
img = PIL.Image.new('RGB', (128, 128), color='blue')
|
||||
img.save(fn)
|
||||
|
||||
# Upload the image to a part
|
||||
with open(fn, 'rb') as img_file:
|
||||
response = self.upload_client.patch(
|
||||
reverse('api-part-detail', kwargs={'pk': p.pk}),
|
||||
{
|
||||
'image': img_file,
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
image_name = response.data['image']
|
||||
self.assertTrue(image_name.startswith('/media/part_images/part_image'))
|
||||
|
||||
# Create a new part without an image
|
||||
response = self.post(
|
||||
reverse('api-part-list'),
|
||||
{
|
||||
'name': 'Some New Part',
|
||||
'description': 'Description of the part',
|
||||
'category': 1
|
||||
},
|
||||
expected_code=201
|
||||
)
|
||||
|
||||
self.assertEqual(response.data['image'], None)
|
||||
part_pk = response.data['pk']
|
||||
|
||||
# Add image from the first part to the new part
|
||||
response = self.patch(
|
||||
reverse('api-part-detail', kwargs={'pk': part_pk}),
|
||||
{
|
||||
'existing_image': image_name
|
||||
},
|
||||
expected_code=200
|
||||
)
|
||||
|
||||
self.assertEqual(response.data['image'], image_name)
|
||||
|
||||
# Attempt to add a non-existent image to an existing part
|
||||
last_p = Part.objects.last()
|
||||
response = self.patch(
|
||||
reverse('api-part-detail', kwargs={'pk': last_p.pk}),
|
||||
{
|
||||
'existing_image': 'bogus_image.jpg'
|
||||
},
|
||||
expected_code=400
|
||||
)
|
||||
|
||||
def test_details(self):
|
||||
"""Test that the required details are available."""
|
||||
p = Part.objects.get(pk=1)
|
||||
|
||||
@@ -147,16 +147,16 @@ class BarcodeAssign(BarcodeView):
|
||||
"""
|
||||
|
||||
# Here we only check against 'InvenTree' plugins
|
||||
plugins = registry.with_mixin('barcode', builtin=True)
|
||||
inventree_barcode_plugin = registry.get_plugin('inventreebarcode')
|
||||
|
||||
# First check if the provided barcode matches an existing database entry
|
||||
for plugin in plugins:
|
||||
result = plugin.scan(barcode)
|
||||
if inventree_barcode_plugin:
|
||||
result = inventree_barcode_plugin.scan(barcode)
|
||||
|
||||
if result is not None:
|
||||
result["error"] = _("Barcode matches existing item")
|
||||
result["plugin"] = plugin.name
|
||||
result["barcode_data"] = barcode
|
||||
result['error'] = _('Barcode matches existing item')
|
||||
result['plugin'] = inventree_barcode_plugin.name
|
||||
result['barcode_data'] = barcode
|
||||
|
||||
raise ValidationError(result)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import logging
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import F
|
||||
from django.db.models import F, Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from company.models import Company, SupplierPart
|
||||
@@ -347,12 +347,17 @@ class SupplierBarcodeMixin(BarcodeMixin):
|
||||
if supplier:
|
||||
orders = orders.filter(supplier=supplier)
|
||||
|
||||
if customer_order_number:
|
||||
orders = orders.filter(reference__iexact=customer_order_number)
|
||||
elif supplier_order_number:
|
||||
orders = orders.filter(supplier_reference__iexact=supplier_order_number)
|
||||
# this works because reference and supplier_reference are not nullable, so if
|
||||
# customer_order_number or supplier_order_number is None, the query won't return anything
|
||||
reference_filter = Q(reference__iexact=customer_order_number)
|
||||
supplier_reference_filter = Q(supplier_reference__iexact=supplier_order_number)
|
||||
|
||||
return orders
|
||||
orders_union = orders.filter(reference_filter | supplier_reference_filter)
|
||||
if orders_union.count() == 1:
|
||||
return orders_union
|
||||
else:
|
||||
orders_intersection = orders.filter(reference_filter & supplier_reference_filter)
|
||||
return orders_intersection if orders_intersection else orders_union
|
||||
|
||||
@staticmethod
|
||||
def get_supplier_parts(sku: str = None, supplier: Company = None, mpn: str = None):
|
||||
|
||||
@@ -8,6 +8,8 @@ from InvenTree.unit_test import InvenTreeAPITestCase
|
||||
from part.models import Part
|
||||
from stock.models import StockItem
|
||||
|
||||
from .mixins import SupplierBarcodeMixin
|
||||
|
||||
|
||||
class BarcodeAPITest(InvenTreeAPITestCase):
|
||||
"""Tests for barcode api."""
|
||||
@@ -431,3 +433,142 @@ class SOAllocateTest(InvenTreeAPITestCase):
|
||||
self.line_item.refresh_from_db()
|
||||
self.assertEqual(self.line_item.allocated_quantity(), 10)
|
||||
self.assertTrue(self.line_item.is_fully_allocated())
|
||||
|
||||
|
||||
class SupplierBarcodeMixinTest(InvenTreeAPITestCase):
|
||||
"""Unit tests for the SupplierBarcodeMixin class."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Setup for all tests."""
|
||||
super().setUpTestData()
|
||||
|
||||
cls.supplier = company.models.Company.objects.create(
|
||||
name='Supplier Barcode Mixin Test Company', is_supplier=True
|
||||
)
|
||||
|
||||
cls.supplier_other = company.models.Company.objects.create(
|
||||
name='Other Supplier Barcode Mixin Test Company', is_supplier=True
|
||||
)
|
||||
|
||||
cls.supplier_no_orders = company.models.Company.objects.create(
|
||||
name='Supplier Barcode Mixin Test Company with no Orders', is_supplier=True
|
||||
)
|
||||
|
||||
cls.purchase_order_pending = order.models.PurchaseOrder.objects.create(
|
||||
status=order.models.PurchaseOrderStatus.PENDING.value,
|
||||
supplier=cls.supplier,
|
||||
supplier_reference='ORDER#1337',
|
||||
)
|
||||
|
||||
cls.purchase_order_1 = order.models.PurchaseOrder.objects.create(
|
||||
status=order.models.PurchaseOrderStatus.PLACED.value,
|
||||
supplier=cls.supplier,
|
||||
supplier_reference='ORDER#1338',
|
||||
)
|
||||
|
||||
cls.purchase_order_duplicate_1 = order.models.PurchaseOrder.objects.create(
|
||||
status=order.models.PurchaseOrderStatus.PLACED.value,
|
||||
supplier=cls.supplier,
|
||||
supplier_reference='ORDER#1339',
|
||||
)
|
||||
|
||||
cls.purchase_order_duplicate_2 = order.models.PurchaseOrder.objects.create(
|
||||
status=order.models.PurchaseOrderStatus.PLACED.value,
|
||||
supplier=cls.supplier_other,
|
||||
supplier_reference='ORDER#1339',
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
"""Setup method for each test."""
|
||||
super().setUp()
|
||||
|
||||
def test_order_not_placed(self):
|
||||
"""Check that purchase order which has not been placed doesn't get returned."""
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
self.purchase_order_pending.reference, None
|
||||
)
|
||||
self.assertIsNone(purchase_orders.first())
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
None, self.purchase_order_pending.supplier_reference
|
||||
)
|
||||
self.assertIsNone(purchase_orders.first())
|
||||
|
||||
def test_order_simple(self):
|
||||
"""Check that we can get a purchase order by either reference, supplier_reference or both."""
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
self.purchase_order_1.reference, None
|
||||
)
|
||||
self.assertEqual(purchase_orders.count(), 1)
|
||||
self.assertEqual(purchase_orders.first(), self.purchase_order_1)
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
None, self.purchase_order_1.supplier_reference
|
||||
)
|
||||
self.assertEqual(purchase_orders.count(), 1)
|
||||
self.assertEqual(purchase_orders.first(), self.purchase_order_1)
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
self.purchase_order_1.reference, self.purchase_order_1.supplier_reference
|
||||
)
|
||||
self.assertEqual(purchase_orders.count(), 1)
|
||||
self.assertEqual(purchase_orders.first(), self.purchase_order_1)
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
self.purchase_order_1.reference,
|
||||
self.purchase_order_1.supplier_reference,
|
||||
supplier=self.supplier,
|
||||
)
|
||||
self.assertEqual(purchase_orders.count(), 1)
|
||||
self.assertEqual(purchase_orders.first(), self.purchase_order_1)
|
||||
|
||||
def test_wrong_supplier_order(self):
|
||||
"""Check that no orders get returned if the wrong supplier is specified."""
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
self.purchase_order_1.reference, None, supplier=self.supplier_no_orders
|
||||
)
|
||||
self.assertIsNone(purchase_orders.first())
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
None,
|
||||
self.purchase_order_1.supplier_reference,
|
||||
supplier=self.supplier_no_orders,
|
||||
)
|
||||
self.assertIsNone(purchase_orders.first())
|
||||
|
||||
def test_supplier_order_duplicate(self):
|
||||
"""Test getting purchase_orders with the same supplier_reference works correctly."""
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
None, self.purchase_order_duplicate_1.supplier_reference
|
||||
)
|
||||
self.assertEqual(purchase_orders.count(), 2)
|
||||
self.assertEqual(
|
||||
set(purchase_orders),
|
||||
{self.purchase_order_duplicate_1, self.purchase_order_duplicate_2},
|
||||
)
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
self.purchase_order_duplicate_1.reference,
|
||||
self.purchase_order_duplicate_1.supplier_reference,
|
||||
)
|
||||
self.assertEqual(purchase_orders.count(), 1)
|
||||
self.assertEqual(purchase_orders.first(), self.purchase_order_duplicate_1)
|
||||
|
||||
# check that mixing the reference and supplier_reference doesn't work
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
self.purchase_order_duplicate_1.supplier_reference,
|
||||
self.purchase_order_duplicate_1.reference,
|
||||
)
|
||||
self.assertIsNone(purchase_orders.first())
|
||||
|
||||
# check that specifying the right supplier works
|
||||
|
||||
purchase_orders = SupplierBarcodeMixin.get_purchase_orders(
|
||||
None,
|
||||
self.purchase_order_duplicate_1.supplier_reference,
|
||||
supplier=self.supplier_other,
|
||||
)
|
||||
self.assertEqual(purchase_orders.count(), 1)
|
||||
self.assertEqual(purchase_orders.first(), self.purchase_order_duplicate_2)
|
||||
|
||||
@@ -99,7 +99,7 @@ def process_event(plugin_slug, event, *args, **kwargs):
|
||||
This function is run by the background worker process.
|
||||
This function may queue multiple functions to be handled by the background worker.
|
||||
"""
|
||||
plugin = registry.plugins.get(plugin_slug, None)
|
||||
plugin = registry.get_plugin(plugin_slug)
|
||||
|
||||
if plugin is None: # pragma: no cover
|
||||
logger.error("Could not find matching plugin for '%s'", plugin_slug)
|
||||
|
||||
@@ -9,7 +9,7 @@ class EventMixin:
|
||||
Implementing classes must provide a "process_event" function:
|
||||
"""
|
||||
|
||||
def wants_process_event(self, event):
|
||||
def wants_process_event(self, event: str) -> bool:
|
||||
"""Function to subscribe to events.
|
||||
|
||||
Return true if you're interested in the given event, false if not.
|
||||
@@ -17,7 +17,7 @@ class EventMixin:
|
||||
# Default implementation always returns true (backwards compatibility)
|
||||
return True
|
||||
|
||||
def process_event(self, event, *args, **kwargs):
|
||||
def process_event(self, event: str, *args, **kwargs) -> None:
|
||||
"""Function to handle events.
|
||||
|
||||
Must be overridden by plugin
|
||||
|
||||
@@ -133,7 +133,7 @@ class InvenTreeCoreNotificationsPlugin(SettingsContentMixin, SettingsMixin, Inve
|
||||
|
||||
def send_bulk(self):
|
||||
"""Send the notifications out via slack."""
|
||||
instance = registry.plugins.get(self.get_plugin().NAME.lower())
|
||||
instance = registry.get_plugin(self.get_plugin().NAME.lower())
|
||||
url = instance.get_setting('NOTIFICATION_SLACK_URL')
|
||||
|
||||
if not url:
|
||||
|
||||
@@ -18,7 +18,7 @@ class CoreNotificationTestTests(BaseNotificationIntegrationTest):
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
|
||||
# enable plugin and set mail setting to true
|
||||
plugin = registry.plugins.get('inventreecorenotificationsplugin')
|
||||
plugin = registry.get_plugin('inventreecorenotificationsplugin')
|
||||
plugin.set_setting('ENABLE_NOTIFICATION_EMAILS', True)
|
||||
NotificationUserSetting.set_setting(
|
||||
key='NOTIFICATION_METHOD_MAIL',
|
||||
|
||||
@@ -36,7 +36,7 @@ class LCSCPlugin(SupplierBarcodeMixin, SettingsMixin, InvenTreePlugin):
|
||||
"pm": SupplierBarcodeMixin.MANUFACTURER_PART_NUMBER,
|
||||
"pc": SupplierBarcodeMixin.SUPPLIER_PART_NUMBER,
|
||||
"qty": SupplierBarcodeMixin.QUANTITY,
|
||||
"on": SupplierBarcodeMixin.CUSTOMER_ORDER_NUMBER,
|
||||
"on": SupplierBarcodeMixin.SUPPLIER_ORDER_NUMBER,
|
||||
}
|
||||
|
||||
def extract_barcode_fields(self, barcode_data: str) -> dict[str, str]:
|
||||
|
||||
@@ -30,4 +30,11 @@ class MouserPlugin(SupplierBarcodeMixin, SettingsMixin, InvenTreePlugin):
|
||||
def extract_barcode_fields(self, barcode_data: str) -> dict[str, str]:
|
||||
"""Get supplier_part and barcode_fields from Mouser DataMatrix-Code."""
|
||||
|
||||
return self.parse_ecia_barcode2d(barcode_data)
|
||||
barcode_fields = self.parse_ecia_barcode2d(barcode_data)
|
||||
|
||||
# Mouser uses the custom order number ('K') field of the 2D barcode for both,
|
||||
# the order number and the customer order number
|
||||
if order_number := barcode_fields.get(self.CUSTOMER_ORDER_NUMBER):
|
||||
barcode_fields.setdefault(self.SUPPLIER_ORDER_NUMBER, order_number)
|
||||
|
||||
return barcode_fields
|
||||
|
||||
@@ -35,7 +35,8 @@ class TMEPlugin(SupplierBarcodeMixin, SettingsMixin, InvenTreePlugin):
|
||||
# Custom field mapping
|
||||
TME_QRCODE_FIELDS = {
|
||||
"PN": SupplierBarcodeMixin.SUPPLIER_PART_NUMBER,
|
||||
"PO": SupplierBarcodeMixin.CUSTOMER_ORDER_NUMBER,
|
||||
"CPO": SupplierBarcodeMixin.CUSTOMER_ORDER_NUMBER,
|
||||
"PO": SupplierBarcodeMixin.SUPPLIER_ORDER_NUMBER,
|
||||
"MPN": SupplierBarcodeMixin.MANUFACTURER_PART_NUMBER,
|
||||
"QTY": SupplierBarcodeMixin.QUANTITY,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""URL lookup for plugin app."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import include, re_path
|
||||
|
||||
PLUGIN_BASE = 'plugin' # Constant for links
|
||||
@@ -13,7 +14,7 @@ def get_plugin_urls():
|
||||
urls = []
|
||||
|
||||
# Only allow custom routing if the setting is enabled
|
||||
if InvenTreeSetting.get_setting('ENABLE_PLUGINS_URL', False, create=False, cache=False):
|
||||
if InvenTreeSetting.get_setting('ENABLE_PLUGINS_URL', False, create=False, cache=False) or settings.PLUGIN_TESTING_SETUP:
|
||||
for plugin in registry.plugins.values():
|
||||
if plugin.mixin_enabled('urls'):
|
||||
urls.append(plugin.urlpatterns)
|
||||
|
||||
@@ -86,7 +86,7 @@ def asset(filename):
|
||||
filename = '' + filename
|
||||
|
||||
# If in debug mode, return URL to the image, not a local file
|
||||
debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE')
|
||||
debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE', cache=False)
|
||||
|
||||
# Test if the file actually exists
|
||||
full_path = settings.MEDIA_ROOT.joinpath('report', 'assets', filename).resolve()
|
||||
@@ -124,7 +124,7 @@ def uploaded_image(filename, replace_missing=True, replacement_file='blank_image
|
||||
filename = '' + filename
|
||||
|
||||
# If in debug mode, return URL to the image, not a local file
|
||||
debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE')
|
||||
debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE', cache=False)
|
||||
|
||||
# Check if the file exists
|
||||
if not filename:
|
||||
@@ -286,7 +286,7 @@ def logo_image(**kwargs):
|
||||
- Otherwise, return a path to the default InvenTree logo
|
||||
"""
|
||||
# If in debug mode, return URL to the image, not a local file
|
||||
debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE')
|
||||
debug_mode = InvenTreeSetting.get_setting('REPORT_DEBUG_MODE', cache=False)
|
||||
|
||||
return InvenTree.helpers.getLogoImage(as_file=not debug_mode, **kwargs)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal, InvalidOperation
|
||||
@@ -40,6 +41,8 @@ from part import models as PartModels
|
||||
from plugin.events import trigger_event
|
||||
from users.models import Owner
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
class StockLocationType(MetadataMixin, models.Model):
|
||||
"""A type of stock location like Warehouse, room, shelf, drawer.
|
||||
@@ -1660,9 +1663,12 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
# Nullify the PK so a new record is created
|
||||
new_stock = StockItem.objects.get(pk=self.pk)
|
||||
new_stock.pk = None
|
||||
new_stock.parent = self
|
||||
new_stock.quantity = quantity
|
||||
|
||||
# Update the new stock item to ensure the tree structure is observed
|
||||
new_stock.parent = self
|
||||
new_stock.level = self.level + 1
|
||||
|
||||
# Move to the new location if specified, otherwise use current location
|
||||
if location:
|
||||
new_stock.location = location
|
||||
@@ -1704,6 +1710,19 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
stockitem=new_stock,
|
||||
)
|
||||
|
||||
# Rebuild the tree for this parent item
|
||||
try:
|
||||
StockItem.objects.partial_rebuild(tree_id=self.tree_id)
|
||||
except Exception:
|
||||
logger.warning('Rebuilding entire StockItem tree')
|
||||
StockItem.objects.rebuild()
|
||||
|
||||
# Attempt to reload the new item from the database
|
||||
try:
|
||||
new_stock.refresh_from_db()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Return a copy of the "new" stock item
|
||||
return new_stock
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
},
|
||||
multi_delete: true,
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Test Data" %}',
|
||||
title: '{% jstrans "Delete Test Data" %}',
|
||||
preFormContent: html,
|
||||
refreshTable: '#test-result-table',
|
||||
});
|
||||
@@ -293,7 +293,7 @@
|
||||
fields: stockItemTestResultFields({
|
||||
stock_item: {{ item.pk }},
|
||||
}),
|
||||
title: '{% trans "Add Test Result" %}',
|
||||
title: '{% jstrans "Add Test Result" %}',
|
||||
refreshTable: '#test-result-table',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -504,7 +504,7 @@ $("#stock-test-report").click(function() {
|
||||
$("#print-label").click(function() {
|
||||
printLabels({
|
||||
items: [{{ item.pk }}],
|
||||
singular_name: '{% trans "stock item" %}',
|
||||
singular_name: '{% jstrans "stock item" %}',
|
||||
url: '{% url "api-stockitem-label-list" %}',
|
||||
key: 'item',
|
||||
});
|
||||
@@ -529,7 +529,7 @@ $('#stock-edit-status').click(function () {
|
||||
status: {},
|
||||
},
|
||||
reload: true,
|
||||
title: '{% trans "Edit Stock Status" %}',
|
||||
title: '{% jstrans "Edit Stock Status" %}',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -538,7 +538,7 @@ $('#stock-edit-status').click(function () {
|
||||
{% if barcodes %}
|
||||
$("#show-qr-code").click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Stock Item QR Code" %}',
|
||||
'{% jstrans "Stock Item QR Code" %}',
|
||||
'{"stockitem": {{ item.pk }} }',
|
||||
);
|
||||
});
|
||||
@@ -549,7 +549,7 @@ $("#barcode-link").click(function() {
|
||||
stockitem: {{ item.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Stock Item" %}',
|
||||
title: '{% jstrans "Link Barcode to Stock Item" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -625,7 +625,7 @@ $("#stock-convert").click(function() {
|
||||
'{% url "api-stock-item-convert" item.pk %}',
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Convert Stock Item" %}',
|
||||
title: '{% jstrans "Convert Stock Item" %}',
|
||||
preFormContent: html,
|
||||
reload: true,
|
||||
fields: {
|
||||
@@ -659,7 +659,7 @@ $("#stock-return-from-customer").click(function() {
|
||||
},
|
||||
},
|
||||
method: 'POST',
|
||||
title: '{% trans "Return to Stock" %}',
|
||||
title: '{% jstrans "Return to Stock" %}',
|
||||
reload: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@
|
||||
|
||||
printLabels({
|
||||
items: locs,
|
||||
singular_name: '{% trans "stock location" %}',
|
||||
singular_name: '{% jstrans "stock location" %}',
|
||||
key: 'location',
|
||||
url: '{% url "api-stocklocation-label-list" %}',
|
||||
});
|
||||
@@ -314,7 +314,7 @@
|
||||
{
|
||||
onSuccess: function() {
|
||||
showMessage(
|
||||
'{% trans "Scanned stock container into this location" %}',
|
||||
'{% jstrans "Scanned stock container into this location" %}',
|
||||
{
|
||||
style: 'success',
|
||||
}
|
||||
@@ -387,7 +387,7 @@
|
||||
{% if barcodes %}
|
||||
$('#show-qr-code').click(function() {
|
||||
showQRDialog(
|
||||
'{% trans "Stock Location QR Code" %}',
|
||||
'{% jstrans "Stock Location QR Code" %}',
|
||||
'{"stocklocation": {{ location.pk }} }'
|
||||
);
|
||||
});
|
||||
@@ -398,7 +398,7 @@
|
||||
stocklocation: {{ location.pk }},
|
||||
},
|
||||
{
|
||||
title: '{% trans "Link Barcode to Stock Location" %}',
|
||||
title: '{% jstrans "Link Barcode to Stock Location" %}',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ from .models import (StockItem, StockItemTestResult, StockItemTracking,
|
||||
|
||||
|
||||
class StockTestBase(InvenTreeTestCase):
|
||||
"""Base class for running Stock tests"""
|
||||
"""Base class for running Stock tests."""
|
||||
|
||||
fixtures = [
|
||||
'category',
|
||||
@@ -54,11 +54,11 @@ class StockTest(StockTestBase):
|
||||
"""Tests to ensure that the stock location tree functions correctly."""
|
||||
|
||||
def test_pathstring(self):
|
||||
"""Check that pathstring updates occur as expected"""
|
||||
a = StockLocation.objects.create(name="A")
|
||||
b = StockLocation.objects.create(name="B", parent=a)
|
||||
c = StockLocation.objects.create(name="C", parent=b)
|
||||
d = StockLocation.objects.create(name="D", parent=c)
|
||||
"""Check that pathstring updates occur as expected."""
|
||||
a = StockLocation.objects.create(name='A')
|
||||
b = StockLocation.objects.create(name='B', parent=a)
|
||||
c = StockLocation.objects.create(name='C', parent=b)
|
||||
d = StockLocation.objects.create(name='D', parent=c)
|
||||
|
||||
def refresh():
|
||||
a.refresh_from_db()
|
||||
@@ -67,56 +67,56 @@ class StockTest(StockTestBase):
|
||||
d.refresh_from_db()
|
||||
|
||||
# Initial checks
|
||||
self.assertEqual(a.pathstring, "A")
|
||||
self.assertEqual(b.pathstring, "A/B")
|
||||
self.assertEqual(c.pathstring, "A/B/C")
|
||||
self.assertEqual(d.pathstring, "A/B/C/D")
|
||||
self.assertEqual(a.pathstring, 'A')
|
||||
self.assertEqual(b.pathstring, 'A/B')
|
||||
self.assertEqual(c.pathstring, 'A/B/C')
|
||||
self.assertEqual(d.pathstring, 'A/B/C/D')
|
||||
|
||||
c.name = "Cc"
|
||||
c.name = 'Cc'
|
||||
c.save()
|
||||
|
||||
refresh()
|
||||
self.assertEqual(a.pathstring, "A")
|
||||
self.assertEqual(b.pathstring, "A/B")
|
||||
self.assertEqual(c.pathstring, "A/B/Cc")
|
||||
self.assertEqual(d.pathstring, "A/B/Cc/D")
|
||||
self.assertEqual(a.pathstring, 'A')
|
||||
self.assertEqual(b.pathstring, 'A/B')
|
||||
self.assertEqual(c.pathstring, 'A/B/Cc')
|
||||
self.assertEqual(d.pathstring, 'A/B/Cc/D')
|
||||
|
||||
b.name = "Bb"
|
||||
b.name = 'Bb'
|
||||
b.save()
|
||||
|
||||
refresh()
|
||||
self.assertEqual(a.pathstring, "A")
|
||||
self.assertEqual(b.pathstring, "A/Bb")
|
||||
self.assertEqual(c.pathstring, "A/Bb/Cc")
|
||||
self.assertEqual(d.pathstring, "A/Bb/Cc/D")
|
||||
self.assertEqual(a.pathstring, 'A')
|
||||
self.assertEqual(b.pathstring, 'A/Bb')
|
||||
self.assertEqual(c.pathstring, 'A/Bb/Cc')
|
||||
self.assertEqual(d.pathstring, 'A/Bb/Cc/D')
|
||||
|
||||
a.name = "Aa"
|
||||
a.name = 'Aa'
|
||||
a.save()
|
||||
|
||||
refresh()
|
||||
self.assertEqual(a.pathstring, "Aa")
|
||||
self.assertEqual(b.pathstring, "Aa/Bb")
|
||||
self.assertEqual(c.pathstring, "Aa/Bb/Cc")
|
||||
self.assertEqual(d.pathstring, "Aa/Bb/Cc/D")
|
||||
self.assertEqual(a.pathstring, 'Aa')
|
||||
self.assertEqual(b.pathstring, 'Aa/Bb')
|
||||
self.assertEqual(c.pathstring, 'Aa/Bb/Cc')
|
||||
self.assertEqual(d.pathstring, 'Aa/Bb/Cc/D')
|
||||
|
||||
d.name = "Dd"
|
||||
d.name = 'Dd'
|
||||
d.save()
|
||||
|
||||
refresh()
|
||||
self.assertEqual(a.pathstring, "Aa")
|
||||
self.assertEqual(b.pathstring, "Aa/Bb")
|
||||
self.assertEqual(c.pathstring, "Aa/Bb/Cc")
|
||||
self.assertEqual(d.pathstring, "Aa/Bb/Cc/Dd")
|
||||
self.assertEqual(a.pathstring, 'Aa')
|
||||
self.assertEqual(b.pathstring, 'Aa/Bb')
|
||||
self.assertEqual(c.pathstring, 'Aa/Bb/Cc')
|
||||
self.assertEqual(d.pathstring, 'Aa/Bb/Cc/Dd')
|
||||
|
||||
# Test a really long name
|
||||
# (it will be clipped to < 250 characters)
|
||||
a.name = "A" * 100
|
||||
a.name = 'A' * 100
|
||||
a.save()
|
||||
b.name = "B" * 100
|
||||
b.name = 'B' * 100
|
||||
b.save()
|
||||
c.name = "C" * 100
|
||||
c.name = 'C' * 100
|
||||
c.save()
|
||||
d.name = "D" * 100
|
||||
d.name = 'D' * 100
|
||||
d.save()
|
||||
|
||||
refresh()
|
||||
@@ -125,19 +125,15 @@ class StockTest(StockTestBase):
|
||||
self.assertEqual(len(c.pathstring), 249)
|
||||
self.assertEqual(len(d.pathstring), 249)
|
||||
|
||||
self.assertTrue(d.pathstring.startswith("AAAAAAAA"))
|
||||
self.assertTrue(d.pathstring.endswith("DDDDDDDD"))
|
||||
self.assertTrue(d.pathstring.startswith('AAAAAAAA'))
|
||||
self.assertTrue(d.pathstring.endswith('DDDDDDDD'))
|
||||
|
||||
def test_link(self):
|
||||
"""Test the link URL field validation"""
|
||||
"""Test the link URL field validation."""
|
||||
item = StockItem.objects.get(pk=1)
|
||||
|
||||
# Check that invalid URLs fail
|
||||
for bad_url in [
|
||||
'test.com',
|
||||
'httpx://abc.xyz',
|
||||
'https:google.com',
|
||||
]:
|
||||
for bad_url in ['test.com', 'httpx://abc.xyz', 'https:google.com']:
|
||||
with self.assertRaises(ValidationError):
|
||||
item.link = bad_url
|
||||
item.save()
|
||||
@@ -168,52 +164,42 @@ class StockTest(StockTestBase):
|
||||
|
||||
@override_settings(EXTRA_URL_SCHEMES=['ssh'])
|
||||
def test_exteneded_schema(self):
|
||||
"""Test that extended URL schemes are allowed"""
|
||||
"""Test that extended URL schemes are allowed."""
|
||||
item = StockItem.objects.get(pk=1)
|
||||
item.link = 'ssh://user:pwd@deb.org:223'
|
||||
item.save()
|
||||
item.full_clean()
|
||||
|
||||
def test_serial_numbers(self):
|
||||
"""Test serial number uniqueness"""
|
||||
"""Test serial number uniqueness."""
|
||||
# Ensure that 'global uniqueness' setting is enabled
|
||||
InvenTreeSetting.set_setting('SERIAL_NUMBER_GLOBALLY_UNIQUE', True, self.user)
|
||||
|
||||
part_a = Part.objects.create(name='A', description='A part with a description', trackable=True)
|
||||
part_b = Part.objects.create(name='B', description='B part with a description', trackable=True)
|
||||
part_a = Part.objects.create(
|
||||
name='A', description='A part with a description', trackable=True
|
||||
)
|
||||
part_b = Part.objects.create(
|
||||
name='B', description='B part with a description', trackable=True
|
||||
)
|
||||
|
||||
# Create a StockItem for part_a
|
||||
StockItem.objects.create(
|
||||
part=part_a,
|
||||
quantity=1,
|
||||
serial='ABCDE',
|
||||
)
|
||||
StockItem.objects.create(part=part_a, quantity=1, serial='ABCDE')
|
||||
|
||||
# Create a StockItem for part_a (but, will error due to identical serial)
|
||||
with self.assertRaises(ValidationError):
|
||||
StockItem.objects.create(
|
||||
part=part_b,
|
||||
quantity=1,
|
||||
serial='ABCDE',
|
||||
)
|
||||
StockItem.objects.create(part=part_b, quantity=1, serial='ABCDE')
|
||||
|
||||
# Now, allow serial numbers to be duplicated between different parts
|
||||
InvenTreeSetting.set_setting('SERIAL_NUMBER_GLOBALLY_UNIQUE', False, self.user)
|
||||
|
||||
StockItem.objects.create(
|
||||
part=part_b,
|
||||
quantity=1,
|
||||
serial='ABCDE',
|
||||
)
|
||||
StockItem.objects.create(part=part_b, quantity=1, serial='ABCDE')
|
||||
|
||||
def test_expiry(self):
|
||||
"""Test expiry date functionality for StockItem model."""
|
||||
today = datetime.datetime.now().date()
|
||||
|
||||
item = StockItem.objects.create(
|
||||
location=self.office,
|
||||
part=Part.objects.get(pk=1),
|
||||
quantity=10,
|
||||
location=self.office, part=Part.objects.get(pk=1), quantity=10
|
||||
)
|
||||
|
||||
# Without an expiry_date set, item should not be "expired"
|
||||
@@ -249,13 +235,14 @@ class StockTest(StockTestBase):
|
||||
# And there should be *no* items being build
|
||||
self.assertEqual(part.quantity_being_built, 0)
|
||||
|
||||
build = Build.objects.create(reference='BO-4444', part=part, title='A test build', quantity=1)
|
||||
build = Build.objects.create(
|
||||
reference='BO-4444', part=part, title='A test build', quantity=1
|
||||
)
|
||||
|
||||
# Add some stock items which are "building"
|
||||
for _ in range(10):
|
||||
StockItem.objects.create(
|
||||
part=part, build=build,
|
||||
quantity=10, is_building=True
|
||||
part=part, build=build, quantity=10, is_building=True
|
||||
)
|
||||
|
||||
# The "is_building" quantity should not be counted here
|
||||
@@ -330,7 +317,10 @@ class StockTest(StockTestBase):
|
||||
|
||||
# There should be 16 widgets "in stock"
|
||||
self.assertEqual(
|
||||
StockItem.objects.filter(part=25).aggregate(Sum('quantity'))['quantity__sum'], 16
|
||||
StockItem.objects.filter(part=25).aggregate(Sum('quantity'))[
|
||||
'quantity__sum'
|
||||
],
|
||||
16,
|
||||
)
|
||||
|
||||
def test_delete_location(self):
|
||||
@@ -339,7 +329,9 @@ class StockTest(StockTestBase):
|
||||
n_stock = StockItem.objects.count()
|
||||
|
||||
# What parts are in drawer 3?
|
||||
stock_ids = [part.id for part in StockItem.objects.filter(location=self.drawer3.id)]
|
||||
stock_ids = [
|
||||
part.id for part in StockItem.objects.filter(location=self.drawer3.id)
|
||||
]
|
||||
|
||||
# Delete location - parts should move to parent location
|
||||
self.drawer3.delete()
|
||||
@@ -361,7 +353,9 @@ class StockTest(StockTestBase):
|
||||
self.assertEqual(it.location, self.bathroom)
|
||||
|
||||
# There now should be 2 lots of screws in the bathroom
|
||||
self.assertEqual(StockItem.objects.filter(part=1, location=self.bathroom).count(), 2)
|
||||
self.assertEqual(
|
||||
StockItem.objects.filter(part=1, location=self.bathroom).count(), 2
|
||||
)
|
||||
|
||||
# Check that a tracking item was added
|
||||
track = StockItemTracking.objects.filter(item=it).latest('id')
|
||||
@@ -463,13 +457,15 @@ class StockTest(StockTestBase):
|
||||
self.assertFalse(it.add_stock(-10, None))
|
||||
|
||||
def test_allocate_to_customer(self):
|
||||
"""Test allocating stock to a customer"""
|
||||
"""Test allocating stock to a customer."""
|
||||
it = StockItem.objects.get(pk=2)
|
||||
n = it.quantity
|
||||
an = n - 10
|
||||
customer = Company.objects.create(name="MyTestCompany")
|
||||
order = SalesOrder.objects.create(description="Test order")
|
||||
ait = it.allocateToCustomer(customer, quantity=an, order=order, user=None, notes='Allocated some stock')
|
||||
customer = Company.objects.create(name='MyTestCompany')
|
||||
order = SalesOrder.objects.create(description='Test order')
|
||||
ait = it.allocateToCustomer(
|
||||
customer, quantity=an, order=order, user=None, notes='Allocated some stock'
|
||||
)
|
||||
|
||||
# Check if new stockitem is created
|
||||
self.assertTrue(ait)
|
||||
@@ -485,29 +481,48 @@ class StockTest(StockTestBase):
|
||||
# Check that a tracking item was added
|
||||
track = StockItemTracking.objects.filter(item=ait).latest('id')
|
||||
|
||||
self.assertEqual(track.tracking_type, StockHistoryCode.SHIPPED_AGAINST_SALES_ORDER)
|
||||
self.assertEqual(
|
||||
track.tracking_type, StockHistoryCode.SHIPPED_AGAINST_SALES_ORDER
|
||||
)
|
||||
self.assertIn('Allocated some stock', track.notes)
|
||||
|
||||
def test_return_from_customer(self):
|
||||
"""Test removing previous allocated stock from customer"""
|
||||
"""Test removing previous allocated stock from customer."""
|
||||
it = StockItem.objects.get(pk=2)
|
||||
|
||||
# First establish total stock for this part
|
||||
allstock_before = StockItem.objects.filter(part=it.part).aggregate(Sum("quantity"))["quantity__sum"]
|
||||
allstock_before = StockItem.objects.filter(part=it.part).aggregate(
|
||||
Sum('quantity')
|
||||
)['quantity__sum']
|
||||
|
||||
n = it.quantity
|
||||
an = n - 10
|
||||
customer = Company.objects.create(name="MyTestCompany")
|
||||
order = SalesOrder.objects.create(description="Test order")
|
||||
customer = Company.objects.create(name='MyTestCompany')
|
||||
order = SalesOrder.objects.create(description='Test order')
|
||||
|
||||
ait = it.allocateToCustomer(customer, quantity=an, order=order, user=None, notes='Allocated some stock')
|
||||
ait.return_from_customer(it.location, None, notes="Stock removed from customer")
|
||||
ait = it.allocateToCustomer(
|
||||
customer, quantity=an, order=order, user=None, notes='Allocated some stock'
|
||||
)
|
||||
|
||||
self.assertEqual(ait.quantity, an)
|
||||
self.assertTrue(ait.parent, it)
|
||||
|
||||
# There should be only quantity 10x remaining
|
||||
it.refresh_from_db()
|
||||
self.assertEqual(it.quantity, 10)
|
||||
|
||||
ait.return_from_customer(it.location, None, notes='Stock removed from customer')
|
||||
|
||||
# When returned stock is returned to its original (parent) location, check that the parent has correct quantity
|
||||
it.refresh_from_db()
|
||||
self.assertEqual(it.quantity, n)
|
||||
|
||||
ait = it.allocateToCustomer(customer, quantity=an, order=order, user=None, notes='Allocated some stock')
|
||||
ait.return_from_customer(self.drawer3, None, notes="Stock removed from customer")
|
||||
ait = it.allocateToCustomer(
|
||||
customer, quantity=an, order=order, user=None, notes='Allocated some stock'
|
||||
)
|
||||
ait.return_from_customer(
|
||||
self.drawer3, None, notes='Stock removed from customer'
|
||||
)
|
||||
|
||||
# Check correct assignment of the new location
|
||||
self.assertEqual(ait.location, self.drawer3)
|
||||
@@ -527,7 +542,9 @@ class StockTest(StockTestBase):
|
||||
self.assertIn('Stock removed from customer', track.notes)
|
||||
|
||||
# Establish total stock for the part after remove from customer to check that we still have the correct quantity in stock
|
||||
allstock_after = StockItem.objects.filter(part=it.part).aggregate(Sum("quantity"))["quantity__sum"]
|
||||
allstock_after = StockItem.objects.filter(part=it.part).aggregate(
|
||||
Sum('quantity')
|
||||
)['quantity__sum']
|
||||
self.assertEqual(allstock_before, allstock_after)
|
||||
|
||||
def test_take_stock(self):
|
||||
@@ -578,10 +595,7 @@ class StockTest(StockTestBase):
|
||||
# Ensure we do not have unique serials enabled
|
||||
InvenTreeSetting.set_setting('SERIAL_NUMBER_GLOBALLY_UNIQUE', False, None)
|
||||
|
||||
item = StockItem.objects.create(
|
||||
part=p,
|
||||
quantity=1,
|
||||
)
|
||||
item = StockItem.objects.create(part=p, quantity=1)
|
||||
|
||||
self.assertFalse(item.serialized)
|
||||
|
||||
@@ -609,10 +623,7 @@ class StockTest(StockTestBase):
|
||||
trackable=True,
|
||||
)
|
||||
|
||||
item = StockItem.objects.create(
|
||||
part=p,
|
||||
quantity=1,
|
||||
)
|
||||
item = StockItem.objects.create(part=p, quantity=1)
|
||||
|
||||
for sn in [12345, '12345', ' 12345 ']:
|
||||
item.serial = sn
|
||||
@@ -620,7 +631,7 @@ class StockTest(StockTestBase):
|
||||
|
||||
self.assertEqual(item.serial_int, 12345)
|
||||
|
||||
item.serial = "-123"
|
||||
item.serial = '-123'
|
||||
item.save()
|
||||
|
||||
# Negative number should map to positive value
|
||||
@@ -631,7 +642,7 @@ class StockTest(StockTestBase):
|
||||
item.save()
|
||||
|
||||
# The 'integer' portion has been clipped to a maximum value
|
||||
self.assertEqual(item.serial_int, 0x7fffffff)
|
||||
self.assertEqual(item.serial_int, 0x7FFFFFFF)
|
||||
|
||||
# Non-numeric values should encode to zero
|
||||
for sn in ['apple', 'banana', 'carrot']:
|
||||
@@ -644,30 +655,18 @@ class StockTest(StockTestBase):
|
||||
item.serial = 100
|
||||
item.save()
|
||||
|
||||
item_next = StockItem.objects.create(
|
||||
part=p,
|
||||
serial=150,
|
||||
quantity=1
|
||||
)
|
||||
item_next = StockItem.objects.create(part=p, serial=150, quantity=1)
|
||||
|
||||
self.assertEqual(item.get_next_serialized_item(), item_next)
|
||||
|
||||
item_prev = StockItem.objects.create(
|
||||
part=p,
|
||||
serial=' 57',
|
||||
quantity=1,
|
||||
)
|
||||
item_prev = StockItem.objects.create(part=p, serial=' 57', quantity=1)
|
||||
|
||||
self.assertEqual(item.get_next_serialized_item(reverse=True), item_prev)
|
||||
|
||||
# Create a number of serialized stock items around the current item
|
||||
for i in range(75, 125):
|
||||
try:
|
||||
StockItem.objects.create(
|
||||
part=p,
|
||||
serial=i,
|
||||
quantity=1,
|
||||
)
|
||||
StockItem.objects.create(part=p, serial=i, quantity=1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -696,14 +695,14 @@ class StockTest(StockTestBase):
|
||||
|
||||
# Try an invalid quantity
|
||||
with self.assertRaises(ValidationError):
|
||||
item.serializeStock("k", [], self.user)
|
||||
item.serializeStock('k', [], self.user)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
item.serializeStock(-1, [], self.user)
|
||||
|
||||
# Not enough serial numbers for all stock items.
|
||||
with self.assertRaises(ValidationError):
|
||||
item.serializeStock(3, "hello", self.user)
|
||||
item.serializeStock(3, 'hello', self.user)
|
||||
|
||||
def test_serialize_stock_valid(self):
|
||||
"""Perform valid stock serializations."""
|
||||
@@ -755,55 +754,25 @@ class StockTest(StockTestBase):
|
||||
"""
|
||||
# First, we will create a stock location structure
|
||||
|
||||
A = StockLocation.objects.create(
|
||||
name='A',
|
||||
description='Top level location'
|
||||
)
|
||||
A = StockLocation.objects.create(name='A', description='Top level location')
|
||||
|
||||
B1 = StockLocation.objects.create(
|
||||
name='B1',
|
||||
parent=A
|
||||
)
|
||||
B1 = StockLocation.objects.create(name='B1', parent=A)
|
||||
|
||||
B2 = StockLocation.objects.create(
|
||||
name='B2',
|
||||
parent=A
|
||||
)
|
||||
B2 = StockLocation.objects.create(name='B2', parent=A)
|
||||
|
||||
B3 = StockLocation.objects.create(
|
||||
name='B3',
|
||||
parent=A
|
||||
)
|
||||
B3 = StockLocation.objects.create(name='B3', parent=A)
|
||||
|
||||
C11 = StockLocation.objects.create(
|
||||
name='C11',
|
||||
parent=B1,
|
||||
)
|
||||
C11 = StockLocation.objects.create(name='C11', parent=B1)
|
||||
|
||||
C12 = StockLocation.objects.create(
|
||||
name='C12',
|
||||
parent=B1,
|
||||
)
|
||||
C12 = StockLocation.objects.create(name='C12', parent=B1)
|
||||
|
||||
C21 = StockLocation.objects.create(
|
||||
name='C21',
|
||||
parent=B2,
|
||||
)
|
||||
C21 = StockLocation.objects.create(name='C21', parent=B2)
|
||||
|
||||
C22 = StockLocation.objects.create(
|
||||
name='C22',
|
||||
parent=B2,
|
||||
)
|
||||
C22 = StockLocation.objects.create(name='C22', parent=B2)
|
||||
|
||||
C31 = StockLocation.objects.create(
|
||||
name='C31',
|
||||
parent=B3,
|
||||
)
|
||||
C31 = StockLocation.objects.create(name='C31', parent=B3)
|
||||
|
||||
C32 = StockLocation.objects.create(
|
||||
name='C32',
|
||||
parent=B3
|
||||
)
|
||||
C32 = StockLocation.objects.create(name='C32', parent=B3)
|
||||
|
||||
# Check that the tree_id is correct for each sublocation
|
||||
for loc in [B1, B2, B3, C11, C12, C21, C22, C31, C32]:
|
||||
@@ -850,9 +819,7 @@ class StockTest(StockTestBase):
|
||||
# Add some stock items to B3
|
||||
for _ in range(10):
|
||||
StockItem.objects.create(
|
||||
part=Part.objects.get(pk=1),
|
||||
quantity=10,
|
||||
location=B3
|
||||
part=Part.objects.get(pk=1), quantity=10, location=B3
|
||||
)
|
||||
|
||||
self.assertEqual(StockItem.objects.filter(location=B3).count(), 10)
|
||||
@@ -930,10 +897,10 @@ class StockTest(StockTestBase):
|
||||
|
||||
|
||||
class StockBarcodeTest(StockTestBase):
|
||||
"""Run barcode tests for the stock app"""
|
||||
"""Run barcode tests for the stock app."""
|
||||
|
||||
def test_stock_item_barcode_basics(self):
|
||||
"""Simple tests for the StockItem barcode integration"""
|
||||
"""Simple tests for the StockItem barcode integration."""
|
||||
item = StockItem.objects.get(pk=1)
|
||||
|
||||
self.assertEqual(StockItem.barcode_model_type(), 'stockitem')
|
||||
@@ -949,7 +916,7 @@ class StockBarcodeTest(StockTestBase):
|
||||
self.assertEqual(barcode, '{"stockitem": 1}')
|
||||
|
||||
def test_location_barcode_basics(self):
|
||||
"""Simple tests for the StockLocation barcode integration"""
|
||||
"""Simple tests for the StockLocation barcode integration."""
|
||||
self.assertEqual(StockLocation.barcode_model_type(), 'stocklocation')
|
||||
|
||||
loc = StockLocation.objects.get(pk=1)
|
||||
@@ -982,7 +949,10 @@ class VariantTest(StockTestBase):
|
||||
chair = Part.objects.get(pk=10000)
|
||||
|
||||
# Operations on the top-level object
|
||||
[self.assertFalse(chair.validate_serial_number(i)) for i in [1, 2, 3, 4, 5, 20, 21, 22]]
|
||||
[
|
||||
self.assertFalse(chair.validate_serial_number(i))
|
||||
for i in [1, 2, 3, 4, 5, 20, 21, 22]
|
||||
]
|
||||
|
||||
self.assertFalse(chair.validate_serial_number(20))
|
||||
self.assertFalse(chair.validate_serial_number(21))
|
||||
@@ -1006,11 +976,7 @@ class VariantTest(StockTestBase):
|
||||
# Create a new serial number
|
||||
n = variant.get_latest_serial_number()
|
||||
|
||||
item = StockItem(
|
||||
part=variant,
|
||||
quantity=1,
|
||||
serial=n
|
||||
)
|
||||
item = StockItem(part=variant, quantity=1, serial=n)
|
||||
|
||||
# This should fail
|
||||
with self.assertRaises(ValidationError):
|
||||
@@ -1031,6 +997,63 @@ class VariantTest(StockTestBase):
|
||||
item.save()
|
||||
|
||||
|
||||
class StockTreeTest(StockTestBase):
|
||||
"""Unit test for StockItem tree structure."""
|
||||
|
||||
def test_stock_split(self):
|
||||
"""Test that stock splitting works correctly."""
|
||||
StockItem.objects.rebuild()
|
||||
|
||||
part = Part.objects.create(name='My part', description='My part description')
|
||||
location = StockLocation.objects.create(name='Test Location')
|
||||
|
||||
# Create an initial stock item
|
||||
item = StockItem.objects.create(part=part, quantity=1000, location=location)
|
||||
|
||||
# Test that the initial MPTT values are correct
|
||||
self.assertEqual(item.level, 0)
|
||||
self.assertEqual(item.lft, 1)
|
||||
self.assertEqual(item.rght, 2)
|
||||
|
||||
children = []
|
||||
|
||||
self.assertEqual(item.get_descendants(include_self=False).count(), 0)
|
||||
self.assertEqual(item.get_descendants(include_self=True).count(), 1)
|
||||
|
||||
# Create child items by splitting stock
|
||||
for idx in range(10):
|
||||
child = item.splitStock(50, None, None)
|
||||
children.append(child)
|
||||
|
||||
# Check that the child item has been correctly created
|
||||
self.assertEqual(child.parent.pk, item.pk)
|
||||
self.assertEqual(child.tree_id, item.tree_id)
|
||||
self.assertEqual(child.level, 1)
|
||||
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.get_children().count(), idx + 1)
|
||||
self.assertEqual(item.get_descendants(include_self=True).count(), idx + 2)
|
||||
|
||||
item.refresh_from_db()
|
||||
n = item.get_descendants(include_self=True).count()
|
||||
|
||||
for child in children:
|
||||
# Create multiple sub-childs
|
||||
for _idx in range(3):
|
||||
sub_child = child.splitStock(10, None, None)
|
||||
self.assertEqual(sub_child.parent.pk, child.pk)
|
||||
self.assertEqual(sub_child.tree_id, child.tree_id)
|
||||
self.assertEqual(sub_child.level, 2)
|
||||
|
||||
self.assertEqual(sub_child.get_ancestors(include_self=True).count(), 3)
|
||||
|
||||
child.refresh_from_db()
|
||||
self.assertEqual(child.get_descendants(include_self=True).count(), 4)
|
||||
|
||||
item.refresh_from_db()
|
||||
self.assertEqual(item.get_descendants(include_self=True).count(), n + 30)
|
||||
|
||||
|
||||
class TestResultTest(StockTestBase):
|
||||
"""Tests for the StockItemTestResult model."""
|
||||
|
||||
@@ -1040,7 +1063,7 @@ class TestResultTest(StockTestBase):
|
||||
tests = item.test_results
|
||||
self.assertEqual(tests.count(), 4)
|
||||
|
||||
results = item.getTestResults(test="Temperature Test")
|
||||
results = item.getTestResults(test='Temperature Test')
|
||||
self.assertEqual(results.count(), 2)
|
||||
|
||||
# Passing tests
|
||||
@@ -1074,9 +1097,7 @@ class TestResultTest(StockTestBase):
|
||||
test.save()
|
||||
|
||||
StockItemTestResult.objects.create(
|
||||
stock_item=item,
|
||||
test='sew cushion',
|
||||
result=True
|
||||
stock_item=item, test='sew cushion', result=True
|
||||
)
|
||||
|
||||
# Still should be failing at this point,
|
||||
@@ -1088,7 +1109,7 @@ class TestResultTest(StockTestBase):
|
||||
stock_item=item,
|
||||
test='apply paint',
|
||||
date=datetime.datetime(2022, 12, 12),
|
||||
result=True
|
||||
result=True,
|
||||
)
|
||||
|
||||
self.assertTrue(item.passedAllRequiredTests())
|
||||
@@ -1096,6 +1117,9 @@ class TestResultTest(StockTestBase):
|
||||
def test_duplicate_item_tests(self):
|
||||
"""Test duplicate item behaviour."""
|
||||
# Create an example stock item by copying one from the database (because we are lazy)
|
||||
|
||||
StockItem.objects.rebuild()
|
||||
|
||||
item = StockItem.objects.get(pk=522)
|
||||
|
||||
item.pk = None
|
||||
@@ -1103,32 +1127,25 @@ class TestResultTest(StockTestBase):
|
||||
item.quantity = 50
|
||||
|
||||
# Try with an invalid batch code (according to sample validatoin plugin)
|
||||
item.batch = "X234"
|
||||
item.batch = 'X234'
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
item.save()
|
||||
|
||||
item.batch = "B123"
|
||||
item.batch = 'B123'
|
||||
item.save()
|
||||
|
||||
# Do some tests!
|
||||
StockItemTestResult.objects.create(
|
||||
stock_item=item,
|
||||
test="Firmware",
|
||||
result=True
|
||||
stock_item=item, test='Firmware', result=True
|
||||
)
|
||||
|
||||
StockItemTestResult.objects.create(
|
||||
stock_item=item,
|
||||
test="Paint Color",
|
||||
result=True,
|
||||
value="Red"
|
||||
stock_item=item, test='Paint Color', result=True, value='Red'
|
||||
)
|
||||
|
||||
StockItemTestResult.objects.create(
|
||||
stock_item=item,
|
||||
test="Applied Sticker",
|
||||
result=False
|
||||
stock_item=item, test='Applied Sticker', result=False
|
||||
)
|
||||
|
||||
self.assertEqual(item.test_results.count(), 3)
|
||||
@@ -1142,10 +1159,7 @@ class TestResultTest(StockTestBase):
|
||||
self.assertEqual(item.test_results.count(), 3)
|
||||
self.assertEqual(item2.test_results.count(), 3)
|
||||
|
||||
StockItemTestResult.objects.create(
|
||||
stock_item=item2,
|
||||
test='A new test'
|
||||
)
|
||||
StockItemTestResult.objects.create(stock_item=item2, test='A new test')
|
||||
|
||||
self.assertEqual(item.test_results.count(), 3)
|
||||
self.assertEqual(item2.test_results.count(), 4)
|
||||
@@ -1154,10 +1168,7 @@ class TestResultTest(StockTestBase):
|
||||
item2.serializeStock(1, [100], self.user)
|
||||
|
||||
# Add a test result to the parent *after* serialization
|
||||
StockItemTestResult.objects.create(
|
||||
stock_item=item2,
|
||||
test='abcde'
|
||||
)
|
||||
StockItemTestResult.objects.create(stock_item=item2, test='abcde')
|
||||
|
||||
self.assertEqual(item2.test_results.count(), 5)
|
||||
|
||||
@@ -1182,10 +1193,7 @@ class TestResultTest(StockTestBase):
|
||||
|
||||
# Create a stock item which is installed *inside* the master item
|
||||
sub_item = StockItem.objects.create(
|
||||
part=item.part,
|
||||
quantity=1,
|
||||
belongs_to=item,
|
||||
location=None
|
||||
part=item.part, quantity=1, belongs_to=item, location=None
|
||||
)
|
||||
|
||||
# Now, create some test results against the sub item
|
||||
@@ -1195,7 +1203,7 @@ class TestResultTest(StockTestBase):
|
||||
stock_item=sub_item,
|
||||
test='firmware version',
|
||||
date=datetime.datetime.now().date(),
|
||||
result=True
|
||||
result=True,
|
||||
)
|
||||
|
||||
# Should return the same number of tests as before
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
{% to_list setting_part_starred setting_part_latest setting_bom_validation as settings_list_part %}
|
||||
|
||||
{% if roles.part.view and True in settings_list_part %}
|
||||
addHeaderTitle('{% trans "Parts" %}');
|
||||
addHeaderTitle('{% jstrans "Parts" %}');
|
||||
|
||||
{% if setting_part_starred %}
|
||||
addHeaderAction('starred-parts', '{% trans "Subscribed Parts" %}', 'fa-bell');
|
||||
addHeaderAction('starred-parts', '{% jstrans "Subscribed Parts" %}', 'fa-bell');
|
||||
loadSimplePartTable("#table-starred-parts", "{% url 'api-part-list' %}", {
|
||||
name: 'starred-parts',
|
||||
params: {
|
||||
@@ -49,7 +49,7 @@ loadSimplePartTable("#table-starred-parts", "{% url 'api-part-list' %}", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_category_starred %}
|
||||
addHeaderAction('starred-categories', '{% trans "Subscribed Categories" %}', 'fa-bell');
|
||||
addHeaderAction('starred-categories', '{% jstrans "Subscribed Categories" %}', 'fa-bell');
|
||||
loadPartCategoryTable($('#table-starred-categories'), {
|
||||
params: {
|
||||
starred: true,
|
||||
@@ -59,7 +59,7 @@ loadPartCategoryTable($('#table-starred-categories'), {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_part_latest %}
|
||||
addHeaderAction('latest-parts', '{% trans "Latest Parts" %}', 'fa-newspaper');
|
||||
addHeaderAction('latest-parts', '{% jstrans "Latest Parts" %}', 'fa-newspaper');
|
||||
loadSimplePartTable("#table-latest-parts", "{% url 'api-part-list' %}", {
|
||||
name: 'latest-parts',
|
||||
params: {
|
||||
@@ -74,7 +74,7 @@ loadSimplePartTable("#table-latest-parts", "{% url 'api-part-list' %}", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_bom_validation %}
|
||||
addHeaderAction('bom-validation', '{% trans "BOM Waiting Validation" %}', 'fa-times-circle');
|
||||
addHeaderAction('bom-validation', '{% jstrans "BOM Waiting Validation" %}', 'fa-times-circle');
|
||||
loadSimplePartTable("#table-bom-validation", "{% url 'api-part-list' %}", {
|
||||
name: 'parts-invalid-bom',
|
||||
params: {
|
||||
@@ -103,7 +103,7 @@ loadSimplePartTable("#table-bom-validation", "{% url 'api-part-list' %}", {
|
||||
|
||||
{% if roles.stock.view %}
|
||||
{% if setting_stock_recent %}
|
||||
addHeaderAction('recently-updated-stock', '{% trans "Recently Updated" %}', 'fa-clock');
|
||||
addHeaderAction('recently-updated-stock', '{% jstrans "Recently Updated" %}', 'fa-clock');
|
||||
loadStockTable($('#table-recently-updated-stock'), {
|
||||
disableFilters: true,
|
||||
params: {
|
||||
@@ -117,7 +117,7 @@ loadStockTable($('#table-recently-updated-stock'), {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_stock_low %}
|
||||
addHeaderAction('low-stock', '{% trans "Low Stock" %}', 'fa-flag');
|
||||
addHeaderAction('low-stock', '{% jstrans "Low Stock" %}', 'fa-flag');
|
||||
loadSimplePartTable("#table-low-stock", "{% url 'api-part-list' %}", {
|
||||
name: 'parts-low-stock',
|
||||
params: {
|
||||
@@ -131,7 +131,7 @@ loadSimplePartTable("#table-low-stock", "{% url 'api-part-list' %}", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_stock_depleted %}
|
||||
addHeaderAction('depleted-stock', '{% trans "Depleted Stock" %}', 'fa-times');
|
||||
addHeaderAction('depleted-stock', '{% jstrans "Depleted Stock" %}', 'fa-times');
|
||||
loadSimplePartTable("#table-depleted-stock", "{% url 'api-part-list' %}", {
|
||||
name: 'parts-depleted-stock',
|
||||
params: {
|
||||
@@ -145,7 +145,7 @@ loadSimplePartTable("#table-depleted-stock", "{% url 'api-part-list' %}", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_stock_needed %}
|
||||
addHeaderAction('stock-to-build', '{% trans "Required for Build Orders" %}', 'fa-bullhorn');
|
||||
addHeaderAction('stock-to-build', '{% jstrans "Required for Build Orders" %}', 'fa-bullhorn');
|
||||
|
||||
loadRequiredForBuildsPartsTable("#table-stock-to-build", {});
|
||||
{% endif %}
|
||||
@@ -153,7 +153,7 @@ loadRequiredForBuildsPartsTable("#table-stock-to-build", {});
|
||||
{% if expiry %}
|
||||
|
||||
{% if setting_stock_expired %}
|
||||
addHeaderAction('expired-stock', '{% trans "Expired Stock" %}', 'fa-calendar-times');
|
||||
addHeaderAction('expired-stock', '{% jstrans "Expired Stock" %}', 'fa-calendar-times');
|
||||
loadStockTable($("#table-expired-stock"), {
|
||||
disableFilters: true,
|
||||
params: {
|
||||
@@ -169,7 +169,7 @@ loadStockTable($("#table-expired-stock"), {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_stock_stale %}
|
||||
addHeaderAction('stale-stock', '{% trans "Stale Stock" %}', 'fa-stopwatch');
|
||||
addHeaderAction('stale-stock', '{% jstrans "Stale Stock" %}', 'fa-stopwatch');
|
||||
loadStockTable($("#table-stale-stock"), {
|
||||
disableFilters: true,
|
||||
params: {
|
||||
@@ -193,10 +193,10 @@ loadStockTable($("#table-stale-stock"), {
|
||||
{% to_list setting_build_pending setting_build_overdue as settings_list_build %}
|
||||
|
||||
{% if roles.build.view and True in settings_list_build %}
|
||||
addHeaderTitle('{% trans "Build Orders" %}');
|
||||
addHeaderTitle('{% jstrans "Build Orders" %}');
|
||||
|
||||
{% if setting_build_pending %}
|
||||
addHeaderAction('build-pending', '{% trans "Build Orders In Progress" %}', 'fa-cogs');
|
||||
addHeaderAction('build-pending', '{% jstrans "Build Orders In Progress" %}', 'fa-cogs');
|
||||
loadBuildTable("#table-build-pending", {
|
||||
locale: '{{ request.LANGUAGE_CODE }}',
|
||||
params: {
|
||||
@@ -207,7 +207,7 @@ loadBuildTable("#table-build-pending", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_build_overdue %}
|
||||
addHeaderAction('build-overdue', '{% trans "Overdue Build Orders" %}', 'fa-calendar-times');
|
||||
addHeaderAction('build-overdue', '{% jstrans "Overdue Build Orders" %}', 'fa-calendar-times');
|
||||
loadBuildTable("#table-build-overdue", {
|
||||
locale: '{{ request.LANGUAGE_CODE }}',
|
||||
params: {
|
||||
@@ -224,10 +224,10 @@ loadBuildTable("#table-build-overdue", {
|
||||
{% to_list setting_po_outstanding setting_po_overdue as settings_list_po %}
|
||||
|
||||
{% if roles.purchase_order.view and True in settings_list_po %}
|
||||
addHeaderTitle('{% trans "Purchase Orders" %}');
|
||||
addHeaderTitle('{% jstrans "Purchase Orders" %}');
|
||||
|
||||
{% if setting_po_outstanding %}
|
||||
addHeaderAction('po-outstanding', '{% trans "Outstanding Purchase Orders" %}', 'fa-sign-in-alt');
|
||||
addHeaderAction('po-outstanding', '{% jstrans "Outstanding Purchase Orders" %}', 'fa-sign-in-alt');
|
||||
loadPurchaseOrderTable("#table-po-outstanding", {
|
||||
url: "{% url 'api-po-list' %}",
|
||||
params: {
|
||||
@@ -238,7 +238,7 @@ loadPurchaseOrderTable("#table-po-outstanding", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_po_overdue %}
|
||||
addHeaderAction('po-overdue', '{% trans "Overdue Purchase Orders" %}', 'fa-calendar-times');
|
||||
addHeaderAction('po-overdue', '{% jstrans "Overdue Purchase Orders" %}', 'fa-calendar-times');
|
||||
loadPurchaseOrderTable("#table-po-overdue", {
|
||||
url: "{% url 'api-po-list' %}",
|
||||
params: {
|
||||
@@ -256,10 +256,10 @@ loadPurchaseOrderTable("#table-po-overdue", {
|
||||
{% to_list setting_so_outstanding setting_so_overdue setting_so_shipments_pending as settings_list_so %}
|
||||
|
||||
{% if roles.sales_order.view and True in settings_list_so %}
|
||||
addHeaderTitle('{% trans "Sales Orders" %}');
|
||||
addHeaderTitle('{% jstrans "Sales Orders" %}');
|
||||
|
||||
{% if setting_so_outstanding %}
|
||||
addHeaderAction('so-outstanding', '{% trans "Outstanding Sales Orders" %}', 'fa-sign-out-alt');
|
||||
addHeaderAction('so-outstanding', '{% jstrans "Outstanding Sales Orders" %}', 'fa-sign-out-alt');
|
||||
loadSalesOrderTable("#table-so-outstanding", {
|
||||
url: "{% url 'api-so-list' %}",
|
||||
params: {
|
||||
@@ -270,7 +270,7 @@ loadSalesOrderTable("#table-so-outstanding", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_so_overdue %}
|
||||
addHeaderAction('so-overdue', '{% trans "Overdue Sales Orders" %}', 'fa-calendar-times');
|
||||
addHeaderAction('so-overdue', '{% jstrans "Overdue Sales Orders" %}', 'fa-calendar-times');
|
||||
loadSalesOrderTable("#table-so-overdue", {
|
||||
url: "{% url 'api-so-list' %}",
|
||||
params: {
|
||||
@@ -281,7 +281,7 @@ loadSalesOrderTable("#table-so-overdue", {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_so_shipments_pending %}
|
||||
addHeaderAction('so-shipments', '{% trans "Pending Shipments" %}', 'fa-truck-loading');
|
||||
addHeaderAction('so-shipments', '{% jstrans "Pending Shipments" %}', 'fa-truck-loading');
|
||||
loadSalesOrderShipmentTable("#table-so-shipments", {
|
||||
url: "{% url 'api-so-shipment-list' %}",
|
||||
params: {
|
||||
@@ -296,9 +296,9 @@ loadSalesOrderShipmentTable("#table-so-shipments", {
|
||||
{% settings_value 'HOMEPAGE_NEWS' user=request.user as setting_news %}
|
||||
{% if setting_news and user.is_staff %}
|
||||
|
||||
addHeaderTitle('{% trans "InvenTree News" %}');
|
||||
addHeaderTitle('{% jstrans "InvenTree News" %}');
|
||||
|
||||
addHeaderAction('news', '{% trans "Current News" %}', 'fa-newspaper');
|
||||
addHeaderAction('news', '{% jstrans "Current News" %}', 'fa-newspaper');
|
||||
loadNewsFeedTable("#table-news", {
|
||||
url: "{% url 'api-news-list' %}",
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ loadNotificationTable("#inbox-table", {
|
||||
params: {
|
||||
read: false,
|
||||
},
|
||||
no_matches: function() { return '{% trans "No unread notifications found" %}'; },
|
||||
no_matches: function() { return '{% jstrans "No unread notifications found" %}'; },
|
||||
});
|
||||
|
||||
$("#mark-all").on('click', function() {
|
||||
@@ -55,7 +55,7 @@ $("#mark-all").on('click', function() {
|
||||
loadNotificationTable("#history-table", {
|
||||
name: 'history',
|
||||
url: '{% url 'api-notifications-list' %}',
|
||||
no_matches: function() { return '{% trans "No notification history found" %}'; },
|
||||
no_matches: function() { return '{% jstrans "No notification history found" %}'; },
|
||||
}, true);
|
||||
|
||||
$('#history-delete').click(function() {
|
||||
@@ -72,7 +72,7 @@ $('#history-delete').click(function() {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
preFormContent: html,
|
||||
title: '{% trans "Delete Notifications" %}',
|
||||
title: '{% jstrans "Delete Notifications" %}',
|
||||
refreshTable: '#history-table',
|
||||
form_data: {
|
||||
filters: {
|
||||
@@ -86,7 +86,7 @@ $('#history-delete').click(function() {
|
||||
$("#history-table").on('click', '.notification-delete', function() {
|
||||
constructForm(`{% url "api-notifications-list" %}${$(this).attr('pk')}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Notification" %}',
|
||||
title: '{% jstrans "Delete Notification" %}',
|
||||
onSuccess: function(data) {
|
||||
updateNotificationTables();
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@
|
||||
}
|
||||
|
||||
{% if roles.part.view %}
|
||||
addItemTitle('{% trans "Part" %}');
|
||||
addItemTitle('{% jstrans "Part" %}');
|
||||
|
||||
addItem('part', '{% trans "Parts" %}', 'fa-shapes');
|
||||
addItem('part', '{% jstrans "Parts" %}', 'fa-shapes');
|
||||
|
||||
loadPartTable("#table-part",
|
||||
"{% url 'api-part-list' %}",
|
||||
@@ -94,7 +94,7 @@
|
||||
}
|
||||
);
|
||||
|
||||
addItem('category', '{% trans "Part Categories" %}', 'fa-sitemap');
|
||||
addItem('category', '{% jstrans "Part Categories" %}', 'fa-sitemap');
|
||||
|
||||
loadPartCategoryTable($("#table-category"), {
|
||||
params: {
|
||||
@@ -102,7 +102,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
addItem('manufacturer-part', '{% trans "Manufacturer Parts" %}', 'fa-toolbox');
|
||||
addItem('manufacturer-part', '{% jstrans "Manufacturer Parts" %}', 'fa-toolbox');
|
||||
|
||||
loadManufacturerPartTable(
|
||||
"#table-manufacturer-part",
|
||||
@@ -117,7 +117,7 @@
|
||||
}
|
||||
);
|
||||
|
||||
addItem('supplier-part', '{% trans "Supplier Parts" %}', 'fa-pallet');
|
||||
addItem('supplier-part', '{% jstrans "Supplier Parts" %}', 'fa-pallet');
|
||||
|
||||
loadSupplierPartTable(
|
||||
"#table-supplier-part",
|
||||
@@ -136,9 +136,9 @@
|
||||
|
||||
{% if roles.build.view %}
|
||||
|
||||
addItemTitle('{% trans "Build" %}');
|
||||
addItemTitle('{% jstrans "Build" %}');
|
||||
|
||||
addItem('build-order', '{% trans "Build Orders" %}', 'fa-tools');
|
||||
addItem('build-order', '{% jstrans "Build Orders" %}', 'fa-tools');
|
||||
|
||||
loadBuildTable('#table-build-order', {
|
||||
locale: '{{ request.LANGUAGE_CODE }}',
|
||||
@@ -150,9 +150,9 @@
|
||||
{% endif %}
|
||||
|
||||
{% if roles.stock.view %}
|
||||
addItemTitle('{% trans "Stock" %}');
|
||||
addItemTitle('{% jstrans "Stock" %}');
|
||||
|
||||
addItem('stock', '{% trans "Stock Items" %}', 'fa-boxes');
|
||||
addItem('stock', '{% jstrans "Stock Items" %}', 'fa-boxes');
|
||||
|
||||
loadStockTable($('#table-stock'), {
|
||||
filterKey: 'stocksearch',
|
||||
@@ -163,7 +163,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
addItem('location', '{% trans "Stock Locations" %}', 'fa-map-marker-alt');
|
||||
addItem('location', '{% jstrans "Stock Locations" %}', 'fa-map-marker-alt');
|
||||
|
||||
loadStockLocationTable($("#table-location"), {
|
||||
filterKey: 'locationsearch',
|
||||
@@ -175,9 +175,9 @@
|
||||
{% endif %}
|
||||
|
||||
{% if roles.purchase_order.view or roles.sales_order.view %}
|
||||
addItemTitle('{% trans "Company" %}');
|
||||
addItemTitle('{% jstrans "Company" %}');
|
||||
|
||||
addItem('manufacturer', '{% trans "Manufacturers" %}', 'fa-industry');
|
||||
addItem('manufacturer', '{% jstrans "Manufacturers" %}', 'fa-industry');
|
||||
|
||||
loadCompanyTable('#table-manufacturer', "{% url 'api-company-list' %}", {
|
||||
params: {
|
||||
@@ -187,7 +187,7 @@
|
||||
});
|
||||
|
||||
{% if roles.purchase_order.view %}
|
||||
addItem('supplier', '{% trans "Suppliers" %}', 'fa-building');
|
||||
addItem('supplier', '{% jstrans "Suppliers" %}', 'fa-building');
|
||||
|
||||
loadCompanyTable('#table-supplier', "{% url 'api-company-list' %}", {
|
||||
params: {
|
||||
@@ -196,7 +196,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
addItem('purchase-order', '{% trans "Purchase Orders" %}', 'fa-shopping-cart');
|
||||
addItem('purchase-order', '{% jstrans "Purchase Orders" %}', 'fa-shopping-cart');
|
||||
|
||||
loadPurchaseOrderTable('#table-purchase-order', {
|
||||
params: {
|
||||
@@ -207,7 +207,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if roles.sales_order.view %}
|
||||
addItem('customer', '{% trans "Customers" %}', 'fa-user-tie');
|
||||
addItem('customer', '{% jstrans "Customers" %}', 'fa-user-tie');
|
||||
|
||||
loadCompanyTable('#table-customer', "{% url 'api-company-list' %}", {
|
||||
params: {
|
||||
@@ -216,7 +216,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
addItem('sales-orders', '{% trans "Sales Orders" %}', 'fa-truck');
|
||||
addItem('sales-orders', '{% jstrans "Sales Orders" %}', 'fa-truck');
|
||||
|
||||
loadSalesOrderTable('#table-sales-orders', {
|
||||
params: {
|
||||
|
||||
@@ -55,14 +55,14 @@ $('table').find('.btn-edit-setting').click(function() {
|
||||
var title = '';
|
||||
|
||||
if (plugin != null) {
|
||||
title = '{% trans "Edit Plugin Setting" %}';
|
||||
title = '{% jstrans "Edit Plugin Setting" %}';
|
||||
} else if (notification) {
|
||||
title = '{% trans "Edit Notification Setting" %}';
|
||||
title = '{% jstrans "Edit Notification Setting" %}';
|
||||
setting = $(this).attr('pk');
|
||||
} else if (is_global) {
|
||||
title = '{% trans "Edit Global Setting" %}';
|
||||
title = '{% jstrans "Edit Global Setting" %}';
|
||||
} else {
|
||||
title = '{% trans "Edit User Setting" %}';
|
||||
title = '{% jstrans "Edit User Setting" %}';
|
||||
}
|
||||
|
||||
editSetting(setting, {
|
||||
|
||||
@@ -41,12 +41,12 @@ onPanelLoad('pricing', function() {
|
||||
{
|
||||
field: 'currency',
|
||||
sortable: true,
|
||||
title: '{% trans "Currency" %}',
|
||||
title: '{% jstrans "Currency" %}',
|
||||
},
|
||||
{
|
||||
field: 'rate',
|
||||
sortable: true,
|
||||
title: '{% trans "Rate" %}',
|
||||
title: '{% jstrans "Rate" %}',
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -64,21 +64,21 @@ onPanelLoad('units', function() {
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
title: '{% jstrans "Name" %}',
|
||||
},
|
||||
{
|
||||
field: 'definition',
|
||||
title: '{% trans "Definition" %}',
|
||||
title: '{% jstrans "Definition" %}',
|
||||
},
|
||||
{
|
||||
field: 'symbol',
|
||||
title: '{% trans "Symbol" %}',
|
||||
title: '{% jstrans "Symbol" %}',
|
||||
formatter: function(value, row) {
|
||||
let html = value;
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeEditButton('button-units-edit', row.pk, '{% trans "Edit" %}');
|
||||
buttons += makeDeleteButton('button-units-delete', row.pk, '{% trans "Delete" %}');
|
||||
buttons += makeEditButton('button-units-edit', row.pk, '{% jstrans "Edit" %}');
|
||||
buttons += makeDeleteButton('button-units-delete', row.pk, '{% jstrans "Delete" %}');
|
||||
|
||||
html += wrapButtons(buttons);
|
||||
return html;
|
||||
@@ -92,7 +92,7 @@ onPanelLoad('units', function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-custom-unit-list" %}${pk}/`, {
|
||||
title: '{% trans "Edit Custom Unit" %}',
|
||||
title: '{% jstrans "Edit Custom Unit" %}',
|
||||
fields: {
|
||||
name: {},
|
||||
definition: {},
|
||||
@@ -107,7 +107,7 @@ onPanelLoad('units', function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-custom-unit-list" %}${pk}/`, {
|
||||
title: '{% trans "Delete Custom Unit" %}',
|
||||
title: '{% jstrans "Delete Custom Unit" %}',
|
||||
method: 'DELETE',
|
||||
refreshTable: '#physical-units-table',
|
||||
});
|
||||
@@ -121,7 +121,7 @@ onPanelLoad('units', function() {
|
||||
definition: {},
|
||||
symbol: {},
|
||||
},
|
||||
title: '{% trans "New Custom Unit" %}',
|
||||
title: '{% jstrans "New Custom Unit" %}',
|
||||
method: 'POST',
|
||||
refreshTable: '#physical-units-table',
|
||||
});
|
||||
@@ -137,17 +137,17 @@ onPanelLoad('project-codes', function() {
|
||||
search: true,
|
||||
sortable: true,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No project codes found" %}';
|
||||
return '{% jstrans "No project codes found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'code',
|
||||
sortable: true,
|
||||
title: '{% trans "Project Code" %}',
|
||||
title: '{% jstrans "Project Code" %}',
|
||||
},
|
||||
{
|
||||
field: 'responsible',
|
||||
title: '{% trans "Responsible" %}',
|
||||
title: '{% jstrans "Responsible" %}',
|
||||
formatter: function(value, row) {
|
||||
if (!row.responsible_detail) {
|
||||
return '-';
|
||||
@@ -155,7 +155,7 @@ onPanelLoad('project-codes', function() {
|
||||
|
||||
var html = row.responsible_detail.name;
|
||||
|
||||
if (row.responsible_detail.label == '{% trans "group" %}') {
|
||||
if (row.responsible_detail.label == '{% jstrans "group" %}') {
|
||||
html += `<span class='float-right fas fa-users'></span>`;
|
||||
} else {
|
||||
html += `<span class='float-right fas fa-user'></span>`;
|
||||
@@ -167,13 +167,13 @@ onPanelLoad('project-codes', function() {
|
||||
{
|
||||
field: 'description',
|
||||
sortable: false,
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
formatter: function(value, row) {
|
||||
let html = value;
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeEditButton('button-project-code-edit', row.pk, '{% trans "Edit Project Code" %}');
|
||||
buttons += makeDeleteButton('button-project-code-delete', row.pk, '{% trans "Delete Project Code" %}');
|
||||
buttons += makeEditButton('button-project-code-edit', row.pk, '{% jstrans "Edit Project Code" %}');
|
||||
buttons += makeDeleteButton('button-project-code-delete', row.pk, '{% jstrans "Delete Project Code" %}');
|
||||
|
||||
html += wrapButtons(buttons);
|
||||
return html;
|
||||
@@ -186,7 +186,7 @@ onPanelLoad('project-codes', function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-project-code-list" %}${pk}/`, {
|
||||
title: '{% trans "Edit Project Code" %}',
|
||||
title: '{% jstrans "Edit Project Code" %}',
|
||||
fields: {
|
||||
code: {},
|
||||
description: {},
|
||||
@@ -200,7 +200,7 @@ onPanelLoad('project-codes', function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-project-code-list" %}${pk}/`, {
|
||||
title: '{% trans "Delete Project Code" %}',
|
||||
title: '{% jstrans "Delete Project Code" %}',
|
||||
method: 'DELETE',
|
||||
refreshTable: '#project-code-table',
|
||||
});
|
||||
@@ -213,7 +213,7 @@ onPanelLoad('project-codes', function() {
|
||||
code: {},
|
||||
description: {},
|
||||
},
|
||||
title: '{% trans "New Project Code" %}',
|
||||
title: '{% jstrans "New Project Code" %}',
|
||||
method: 'POST',
|
||||
refreshTable: '#project-code-table',
|
||||
});
|
||||
@@ -282,7 +282,7 @@ onPanelLoad('category', function() {
|
||||
});
|
||||
|
||||
$('#cat-param-table').inventreeTable({
|
||||
formatNoMatches: function() { return '{% trans "No category parameter templates found" %}'; },
|
||||
formatNoMatches: function() { return '{% jstrans "No category parameter templates found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
@@ -292,21 +292,21 @@ onPanelLoad('category', function() {
|
||||
},
|
||||
{
|
||||
field: 'parameter_template_detail.name',
|
||||
title: '{% trans "Parameter Template" %}',
|
||||
title: '{% jstrans "Parameter Template" %}',
|
||||
sortable: 'true',
|
||||
},
|
||||
{
|
||||
field: 'category_detail.pathstring',
|
||||
title: '{% trans "Category" %}',
|
||||
title: '{% jstrans "Category" %}',
|
||||
},
|
||||
{
|
||||
field: 'default_value',
|
||||
title: '{% trans "Default Value" %}',
|
||||
title: '{% jstrans "Default Value" %}',
|
||||
sortable: 'true',
|
||||
formatter: function(value, row, index, field) {
|
||||
let buttons = '';
|
||||
buttons += makeEditButton('template-edit', row.pk, '{% trans "Edit Template" %}');
|
||||
buttons += makeDeleteButton('template-delete', row.pk, '{% trans "Delete Template" %}');
|
||||
buttons += makeEditButton('template-edit', row.pk, '{% jstrans "Edit Template" %}');
|
||||
buttons += makeDeleteButton('template-delete', row.pk, '{% jstrans "Delete Template" %}');
|
||||
|
||||
let html = value
|
||||
html += wrapButtons(buttons);
|
||||
@@ -323,7 +323,7 @@ onPanelLoad('category', function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/part/category/parameters/${pk}/`, {
|
||||
title: '{% trans "Edit Category Parameter Template" %}',
|
||||
title: '{% jstrans "Edit Category Parameter Template" %}',
|
||||
fields: {
|
||||
parameter_template: {},
|
||||
category: {
|
||||
@@ -350,7 +350,7 @@ onPanelLoad('category', function() {
|
||||
|
||||
constructForm(`/api/part/category/parameters/${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Category Parameter Template" %}',
|
||||
title: '{% jstrans "Delete Category Parameter Template" %}',
|
||||
onSuccess: function() {
|
||||
loadTemplateTable(pk);
|
||||
}
|
||||
@@ -385,7 +385,7 @@ onPanelLoad('category', function() {
|
||||
var pk = $('#category-select').val();
|
||||
|
||||
constructForm('{% url "api-part-category-parameter-list" %}', {
|
||||
title: '{% trans "Create Category Parameter Template" %}',
|
||||
title: '{% jstrans "Create Category Parameter Template" %}',
|
||||
method: 'POST',
|
||||
fields: {
|
||||
parameter_template: {},
|
||||
@@ -415,7 +415,7 @@ onPanelLoad('part-parameters', function() {
|
||||
constructForm('{% url "api-part-parameter-template-list" %}', {
|
||||
fields: partParameterTemplateFields(),
|
||||
method: 'POST',
|
||||
title: '{% trans "Create Part Parameter Template" %}',
|
||||
title: '{% jstrans "Create Part Parameter Template" %}',
|
||||
refreshTable: '#param-table',
|
||||
});
|
||||
});
|
||||
@@ -437,34 +437,34 @@ onPanelLoad("stock", function() {
|
||||
search: true,
|
||||
sortable: true,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No stock location types found" %}';
|
||||
return '{% jstrans "No stock location types found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
sortable: true,
|
||||
title: '{% trans "Name" %}',
|
||||
title: '{% jstrans "Name" %}',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
sortable: false,
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'icon',
|
||||
sortable: true,
|
||||
title: '{% trans "Icon" %}',
|
||||
title: '{% jstrans "Icon" %}',
|
||||
},
|
||||
{
|
||||
field: 'location_count',
|
||||
sortable: true,
|
||||
title: '{% trans "Location count" %}',
|
||||
title: '{% jstrans "Location count" %}',
|
||||
formatter: function(value, row) {
|
||||
let html = value;
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeEditButton('button-location-type-edit', row.pk, '{% trans "Edit Location Type" %}');
|
||||
buttons += makeDeleteButton('button-location-type-delete', row.pk, '{% trans "Delete Location type" %}');
|
||||
buttons += makeEditButton('button-location-type-edit', row.pk, '{% jstrans "Edit Location Type" %}');
|
||||
buttons += makeDeleteButton('button-location-type-delete', row.pk, '{% jstrans "Delete Location type" %}');
|
||||
|
||||
html += wrapButtons(buttons);
|
||||
return html;
|
||||
@@ -477,7 +477,7 @@ onPanelLoad("stock", function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-location-type-list" %}${pk}/`, {
|
||||
title: '{% trans "Edit Location Type" %}',
|
||||
title: '{% jstrans "Edit Location Type" %}',
|
||||
fields: stockLocationTypeFields(),
|
||||
refreshTable: '#location-type-table',
|
||||
});
|
||||
@@ -487,7 +487,7 @@ onPanelLoad("stock", function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-location-type-list" %}${pk}/`, {
|
||||
title: '{% trans "Delete Location Type" %}',
|
||||
title: '{% jstrans "Delete Location Type" %}',
|
||||
method: 'DELETE',
|
||||
refreshTable: '#location-type-table',
|
||||
});
|
||||
@@ -497,7 +497,7 @@ onPanelLoad("stock", function() {
|
||||
// Construct a new location type
|
||||
constructForm('{% url "api-location-type-list" %}', {
|
||||
fields: stockLocationTypeFields(),
|
||||
title: '{% trans "New Location Type" %}',
|
||||
title: '{% jstrans "New Location Type" %}',
|
||||
method: 'POST',
|
||||
refreshTable: '#location-type-table',
|
||||
});
|
||||
@@ -526,18 +526,18 @@ onPanelLoad('stocktake', function() {
|
||||
columns: [
|
||||
{
|
||||
field: 'report',
|
||||
title: '{% trans "Report" %}',
|
||||
title: '{% jstrans "Report" %}',
|
||||
formatter: function(value, row) {
|
||||
return attachmentLink(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'part_count',
|
||||
title: '{% trans "Part Count" %}',
|
||||
title: '{% jstrans "Part Count" %}',
|
||||
},
|
||||
{
|
||||
field: 'date',
|
||||
title: '{% trans "Date" %}',
|
||||
title: '{% jstrans "Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
let html = renderDate(value);
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
|
||||
{% block js_ready %}
|
||||
(function() {
|
||||
var message = "{% trans 'Do you really want to remove the selected email address?' %}";
|
||||
var message = "{% jstrans 'Do you really want to remove the selected email address?' %}";
|
||||
var actions = document.getElementsByName('action_remove');
|
||||
if (actions.length) {
|
||||
actions[0].addEventListener("click", function(e) {
|
||||
|
||||
@@ -222,48 +222,48 @@ function showApiError(xhr, url) {
|
||||
switch (xhr.status || 0) {
|
||||
// No response
|
||||
case 0:
|
||||
title = '{% trans "No Response" %}';
|
||||
message = '{% trans "No response from the InvenTree server" %}';
|
||||
title = '{% jstrans "No Response" %}';
|
||||
message = '{% jstrans "No response from the InvenTree server" %}';
|
||||
break;
|
||||
// Bad request
|
||||
case 400:
|
||||
// Note: Normally error code 400 is handled separately,
|
||||
// and should now be shown here!
|
||||
title = '{% trans "Error 400: Bad request" %}';
|
||||
message = '{% trans "API request returned error code 400" %}';
|
||||
title = '{% jstrans "Error 400: Bad request" %}';
|
||||
message = '{% jstrans "API request returned error code 400" %}';
|
||||
break;
|
||||
// Not authenticated
|
||||
case 401:
|
||||
title = '{% trans "Error 401: Not Authenticated" %}';
|
||||
message = '{% trans "Authentication credentials not supplied" %}';
|
||||
title = '{% jstrans "Error 401: Not Authenticated" %}';
|
||||
message = '{% jstrans "Authentication credentials not supplied" %}';
|
||||
break;
|
||||
// Permission denied
|
||||
case 403:
|
||||
title = '{% trans "Error 403: Permission Denied" %}';
|
||||
message = '{% trans "You do not have the required permissions to access this function" %}';
|
||||
title = '{% jstrans "Error 403: Permission Denied" %}';
|
||||
message = '{% jstrans "You do not have the required permissions to access this function" %}';
|
||||
break;
|
||||
// Resource not found
|
||||
case 404:
|
||||
title = '{% trans "Error 404: Resource Not Found" %}';
|
||||
message = '{% trans "The requested resource could not be located on the server" %}';
|
||||
title = '{% jstrans "Error 404: Resource Not Found" %}';
|
||||
message = '{% jstrans "The requested resource could not be located on the server" %}';
|
||||
break;
|
||||
// Method not allowed
|
||||
case 405:
|
||||
title = '{% trans "Error 405: Method Not Allowed" %}';
|
||||
message = '{% trans "HTTP method not allowed at URL" %}';
|
||||
title = '{% jstrans "Error 405: Method Not Allowed" %}';
|
||||
message = '{% jstrans "HTTP method not allowed at URL" %}';
|
||||
break;
|
||||
// Timeout
|
||||
case 408:
|
||||
title = '{% trans "Error 408: Timeout" %}';
|
||||
message = '{% trans "Connection timeout while requesting data from server" %}';
|
||||
title = '{% jstrans "Error 408: Timeout" %}';
|
||||
message = '{% jstrans "Connection timeout while requesting data from server" %}';
|
||||
break;
|
||||
case 503:
|
||||
title = '{% trans "Error 503: Service Unavailable" %}';
|
||||
message = '{% trans "The server is currently unavailable" %}';
|
||||
title = '{% jstrans "Error 503: Service Unavailable" %}';
|
||||
message = '{% jstrans "The server is currently unavailable" %}';
|
||||
break;
|
||||
default:
|
||||
title = '{% trans "Unhandled Error Code" %}';
|
||||
message = `{% trans "Error code" %}: ${xhr.status}`;
|
||||
title = '{% jstrans "Unhandled Error Code" %}';
|
||||
message = `{% jstrans "Error code" %}: ${xhr.status}`;
|
||||
|
||||
var response = xhr.responseJSON;
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ function addAttachmentButtonCallbacks(url, fields={}) {
|
||||
fields: file_fields,
|
||||
method: 'POST',
|
||||
refreshTable: '#attachment-table',
|
||||
title: '{% trans "Add Attachment" %}',
|
||||
title: '{% jstrans "Add Attachment" %}',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,7 +67,7 @@ function addAttachmentButtonCallbacks(url, fields={}) {
|
||||
fields: link_fields,
|
||||
method: 'POST',
|
||||
refreshTable: '#attachment-table',
|
||||
title: '{% trans "Add Link" %}',
|
||||
title: '{% jstrans "Add Link" %}',
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -111,13 +111,13 @@ function deleteAttachments(attachments, url, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected attachments will be deleted" %}
|
||||
{% jstrans "All selected attachments will be deleted" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% trans "Attachment" %}</th>
|
||||
<th>{% trans "Comment" %}</th>
|
||||
<th>{% jstrans "Attachment" %}</th>
|
||||
<th>{% jstrans "Comment" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>
|
||||
@@ -126,7 +126,7 @@ function deleteAttachments(attachments, url, options={}) {
|
||||
constructForm(url, {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete Attachments" %}',
|
||||
title: '{% jstrans "Delete Attachments" %}',
|
||||
preFormContent: html,
|
||||
form_data: {
|
||||
items: ids,
|
||||
@@ -202,7 +202,7 @@ function makeAttachmentActions(permissions, options) {
|
||||
actions.push({
|
||||
label: 'delete',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
title: '{% trans "Delete attachments" %}',
|
||||
title: '{% jstrans "Delete attachments" %}',
|
||||
callback: options.callback,
|
||||
});
|
||||
}
|
||||
@@ -250,7 +250,7 @@ function loadAttachmentTable(url, options) {
|
||||
{
|
||||
label: 'attachments',
|
||||
icon: 'fa-tools',
|
||||
title: '{% trans "Attachment actions" %}',
|
||||
title: '{% jstrans "Attachment actions" %}',
|
||||
actions: makeAttachmentActions(permissions, {
|
||||
callback: function(attachments) {
|
||||
deleteAttachments(attachments, url, options);
|
||||
@@ -272,7 +272,7 @@ function loadAttachmentTable(url, options) {
|
||||
url: url,
|
||||
name: options.name || 'attachments',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No attachments found" %}';
|
||||
return '{% jstrans "No attachments found" %}';
|
||||
},
|
||||
sortable: true,
|
||||
search: true,
|
||||
@@ -312,7 +312,7 @@ function loadAttachmentTable(url, options) {
|
||||
}
|
||||
},
|
||||
refreshTable: '#attachment-table',
|
||||
title: '{% trans "Edit Attachment" %}',
|
||||
title: '{% jstrans "Edit Attachment" %}',
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -323,7 +323,7 @@ function loadAttachmentTable(url, options) {
|
||||
},
|
||||
{
|
||||
field: 'attachment',
|
||||
title: '{% trans "Attachment" %}',
|
||||
title: '{% jstrans "Attachment" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (row.attachment) {
|
||||
@@ -338,12 +338,12 @@ function loadAttachmentTable(url, options) {
|
||||
},
|
||||
{
|
||||
field: 'comment',
|
||||
title: '{% trans "Comment" %}',
|
||||
title: '{% jstrans "Comment" %}',
|
||||
},
|
||||
{
|
||||
field: 'upload_date',
|
||||
sortable: true,
|
||||
title: '{% trans "Upload Date" %}',
|
||||
title: '{% jstrans "Upload Date" %}',
|
||||
formatter: function(value, row) {
|
||||
var html = renderDate(value);
|
||||
|
||||
@@ -363,7 +363,7 @@ function loadAttachmentTable(url, options) {
|
||||
buttons += makeEditButton(
|
||||
'button-attachment-edit',
|
||||
row.pk,
|
||||
'{% trans "Edit attachment" %}',
|
||||
'{% jstrans "Edit attachment" %}',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ function loadAttachmentTable(url, options) {
|
||||
buttons += makeDeleteButton(
|
||||
'button-attachment-delete',
|
||||
row.pk,
|
||||
'{% trans "Delete attachment" %}',
|
||||
'{% jstrans "Delete attachment" %}',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,23 +40,23 @@ var barcodeInputTimer = null;
|
||||
*/
|
||||
function makeBarcodeInput(placeholderText='', hintText='') {
|
||||
|
||||
placeholderText = placeholderText || '{% trans "Scan barcode data here using barcode scanner" %}';
|
||||
placeholderText = placeholderText || '{% jstrans "Scan barcode data here using barcode scanner" %}';
|
||||
|
||||
hintText = hintText || '{% trans "Enter barcode data" %}';
|
||||
hintText = hintText || '{% jstrans "Enter barcode data" %}';
|
||||
|
||||
var html = `
|
||||
<div id='barcode_scan_video_container' class="mx-auto" style='width: 100%; max-width: 240px; display: none;'>
|
||||
<div id="barcode_scan_video"></div>
|
||||
</div>
|
||||
<div class='form-group'>
|
||||
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
||||
<label class='control-label' for='barcode'>{% jstrans "Barcode" %}</label>
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-text'>
|
||||
${makeIcon('fa-qrcode')}
|
||||
</span>
|
||||
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
||||
<button title='{% trans "Scan barcode using connected webcam" %}' id='barcode_scan_btn' type='button' class='btn btn-secondary' onclick='onBarcodeScanClicked()' style='display: none;'>
|
||||
<button title='{% jstrans "Scan barcode using connected webcam" %}' id='barcode_scan_btn' type='button' class='btn btn-secondary' onclick='onBarcodeScanClicked()' style='display: none;'>
|
||||
${makeIcon('fa-camera')}
|
||||
</button>
|
||||
</div>
|
||||
@@ -135,12 +135,12 @@ function onBarcodeScanCompleted(result, options) {
|
||||
*/
|
||||
function makeNotesField(options={}) {
|
||||
|
||||
var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}';
|
||||
var placeholder = options.placeholder || '{% trans "Enter notes" %}';
|
||||
var tooltip = options.tooltip || '{% jstrans "Enter optional notes for stock transfer" %}';
|
||||
var placeholder = options.placeholder || '{% jstrans "Enter notes" %}';
|
||||
|
||||
return `
|
||||
<div class='form-group'>
|
||||
<label class='control-label' for='notes'>{% trans "Notes" %}</label>
|
||||
<label class='control-label' for='notes'>{% jstrans "Notes" %}</label>
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-text'>
|
||||
@@ -185,7 +185,7 @@ function postBarcodeData(barcode_data, options={}) {
|
||||
} else {
|
||||
console.error(xhr);
|
||||
data = xhr.responseJSON || {};
|
||||
showBarcodeMessage(modal, data.error || '{% trans "Server error" %}');
|
||||
showBarcodeMessage(modal, data.error || '{% jstrans "Server error" %}');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -214,7 +214,7 @@ function postBarcodeData(barcode_data, options={}) {
|
||||
} else {
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
'{% trans "Unknown response from server" %}',
|
||||
'{% jstrans "Unknown response from server" %}',
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
@@ -249,7 +249,7 @@ function showBarcodeMessage(modal, message, style='danger') {
|
||||
function showInvalidResponseError(modal, response, status) {
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
`{% trans "Invalid server response" %}<br>{% trans "Status" %}: '${status}'`
|
||||
`{% jstrans "Invalid server response" %}<br>{% jstrans "Status" %}: '${status}'`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ function barcodeDialog(title, options={}) {
|
||||
modalShowSubmitButton(modal, false);
|
||||
}
|
||||
|
||||
var details = options.details || '{% trans "Scan barcode data" %}';
|
||||
var details = options.details || '{% jstrans "Scan barcode data" %}';
|
||||
|
||||
var content = '';
|
||||
|
||||
@@ -417,7 +417,7 @@ function barcodeDialog(title, options={}) {
|
||||
function barcodeScanDialog(options={}) {
|
||||
|
||||
let modal = options.modal || createNewModal();
|
||||
let title = options.title || '{% trans "Scan Barcode" %}';
|
||||
let title = options.title || '{% jstrans "Scan Barcode" %}';
|
||||
|
||||
const matching_models = [
|
||||
'build',
|
||||
@@ -455,7 +455,7 @@ function barcodeScanDialog(options={}) {
|
||||
// No match
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
'{% trans "No URL in response" %}',
|
||||
'{% jstrans "No URL in response" %}',
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
@@ -493,15 +493,15 @@ function linkBarcodeDialog(data, options={}) {
|
||||
*/
|
||||
function unlinkBarcode(data, options={}) {
|
||||
|
||||
var html = `<b>{% trans "Unlink Barcode" %}</b><br>`;
|
||||
var html = `<b>{% jstrans "Unlink Barcode" %}</b><br>`;
|
||||
|
||||
html += '{% trans "This will remove the link to the associated barcode" %}';
|
||||
html += '{% jstrans "This will remove the link to the associated barcode" %}';
|
||||
|
||||
showQuestionDialog(
|
||||
'{% trans "Unlink Barcode" %}',
|
||||
'{% jstrans "Unlink Barcode" %}',
|
||||
html,
|
||||
{
|
||||
accept_text: '{% trans "Unlink" %}',
|
||||
accept_text: '{% jstrans "Unlink" %}',
|
||||
accept: function() {
|
||||
inventreePut(
|
||||
'{% url "api-barcode-unlink" %}',
|
||||
@@ -543,9 +543,9 @@ function barcodeCheckInStockItems(location_id, options={}) {
|
||||
<table class='table table-condensed table-striped' id='items-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Location" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Location" %}</th>
|
||||
<th>{% jstrans "Quantity" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -564,7 +564,7 @@ function barcodeCheckInStockItems(location_id, options={}) {
|
||||
<td>${imageHoverIcon(item.part_detail.thumbnail)} ${item.part_detail.name}</td>
|
||||
<td>${location_info}</td>
|
||||
<td>${item.quantity}</td>
|
||||
<td>${makeRemoveButton('button-item-remove', item.pk, '{% trans "Remove stock item" %}')}</td>
|
||||
<td>${makeRemoveButton('button-item-remove', item.pk, '{% jstrans "Remove stock item" %}')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
@@ -607,12 +607,12 @@ function barcodeCheckInStockItems(location_id, options={}) {
|
||||
var extra = makeNotesField();
|
||||
|
||||
barcodeDialog(
|
||||
'{% trans "Scan Stock Items Into Location" %}',
|
||||
'{% jstrans "Scan Stock Items Into Location" %}',
|
||||
{
|
||||
details: '{% trans "Scan stock item barcode to check in to this location" %}',
|
||||
details: '{% jstrans "Scan stock item barcode to check in to this location" %}',
|
||||
headerContent: table,
|
||||
preShow: function() {
|
||||
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||
modalSetSubmitText(modal, '{% jstrans "Check In" %}');
|
||||
modalEnable(modal, false);
|
||||
reloadTable();
|
||||
},
|
||||
@@ -644,7 +644,7 @@ function barcodeCheckInStockItems(location_id, options={}) {
|
||||
|
||||
// Prevent submission without any entries
|
||||
if (entries.length == 0) {
|
||||
showBarcodeMessage(modal, '{% trans "No barcode provided" %}', 'warning');
|
||||
showBarcodeMessage(modal, '{% jstrans "No barcode provided" %}', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -684,18 +684,18 @@ function barcodeCheckInStockItems(location_id, options={}) {
|
||||
});
|
||||
|
||||
if (duplicate) {
|
||||
showBarcodeMessage(modal, '{% trans "Stock Item already scanned" %}', 'warning');
|
||||
showBarcodeMessage(modal, '{% jstrans "Stock Item already scanned" %}', 'warning');
|
||||
} else {
|
||||
|
||||
if (stockitem.location == location_id) {
|
||||
showBarcodeMessage(modal, '{% trans "Stock Item already in this location" %}');
|
||||
showBarcodeMessage(modal, '{% jstrans "Stock Item already in this location" %}');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add this stock item to the list
|
||||
items.push(stockitem);
|
||||
|
||||
showBarcodeMessage(modal, '{% trans "Added stock item" %}', 'success');
|
||||
showBarcodeMessage(modal, '{% jstrans "Added stock item" %}', 'success');
|
||||
|
||||
reloadTable();
|
||||
}
|
||||
@@ -704,7 +704,7 @@ function barcodeCheckInStockItems(location_id, options={}) {
|
||||
);
|
||||
} else {
|
||||
// Barcode does not match a stock item
|
||||
showBarcodeMessage(modal, '{% trans "Barcode does not match valid stock item" %}', 'warning');
|
||||
showBarcodeMessage(modal, '{% jstrans "Barcode does not match valid stock item" %}', 'warning');
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -723,9 +723,9 @@ function barcodeCheckInStockLocations(location_id, options={}) {
|
||||
var header = '';
|
||||
|
||||
barcodeDialog(
|
||||
'{% trans "Scan Stock Container Into Location" %}',
|
||||
'{% jstrans "Scan Stock Container Into Location" %}',
|
||||
{
|
||||
details: '{% trans "Scan stock container barcode to check in to this location" %}',
|
||||
details: '{% jstrans "Scan stock container barcode to check in to this location" %}',
|
||||
headerContent: header,
|
||||
preShow: function() {
|
||||
modalEnable(modal, false);
|
||||
@@ -759,7 +759,7 @@ function barcodeCheckInStockLocations(location_id, options={}) {
|
||||
);
|
||||
} else {
|
||||
// Barcode does not match a valid stock location
|
||||
showBarcodeMessage(modal, '{% trans "Barcode does not match valid stock location" %}', 'warning');
|
||||
showBarcodeMessage(modal, '{% jstrans "Barcode does not match valid stock location" %}', 'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -792,7 +792,7 @@ function scanItemsIntoLocation(item_list, options={}) {
|
||||
if (location && location.pk) {
|
||||
div.html(`
|
||||
<div class='alert alert-block alert-info'>
|
||||
<b>{% trans "Location" %}</b></br>
|
||||
<b>{% jstrans "Location" %}</b></br>
|
||||
${location.name}<br>
|
||||
<i>${location.description}</i>
|
||||
</div>
|
||||
@@ -803,13 +803,13 @@ function scanItemsIntoLocation(item_list, options={}) {
|
||||
}
|
||||
|
||||
barcodeDialog(
|
||||
'{% trans "Check Into Location" %}',
|
||||
'{% jstrans "Check Into Location" %}',
|
||||
{
|
||||
headerContent: header,
|
||||
extraFields: extra,
|
||||
modal: modal,
|
||||
preShow: function() {
|
||||
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||
modalSetSubmitText(modal, '{% jstrans "Check In" %}');
|
||||
modalEnable(modal, false);
|
||||
},
|
||||
onShow: function() {
|
||||
@@ -872,7 +872,7 @@ function scanItemsIntoLocation(item_list, options={}) {
|
||||
// Barcode does *NOT* correspond to a StockLocation
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
'{% trans "Barcode does not match a valid location" %}',
|
||||
'{% jstrans "Barcode does not match a valid location" %}',
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
@@ -881,7 +881,7 @@ function scanItemsIntoLocation(item_list, options={}) {
|
||||
// Barcode does *NOT* correspond to a StockLocation
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
'{% trans "Barcode does not match a valid location" %}',
|
||||
'{% jstrans "Barcode does not match a valid location" %}',
|
||||
'warning',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ function addBomItem(part_id, options={}) {
|
||||
constructForm('{% url "api-bom-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Create BOM Item" %}',
|
||||
title: '{% jstrans "Create BOM Item" %}',
|
||||
focus: 'sub_part',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
@@ -129,8 +129,8 @@ function constructBomUploadTable(data, options={}) {
|
||||
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeInfoButton('button-row-data', idx, '{% trans "Display row data" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', idx, '{% trans "Remove row" %}');
|
||||
buttons += makeInfoButton('button-row-data', idx, '{% jstrans "Display row data" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', idx, '{% jstrans "Remove row" %}');
|
||||
|
||||
buttons = wrapButtons(buttons);
|
||||
|
||||
@@ -185,8 +185,8 @@ function constructBomUploadTable(data, options={}) {
|
||||
$(`#button-row-data-${idx}`).click(function() {
|
||||
|
||||
var modal = createNewModal({
|
||||
title: '{% trans "Row Data" %}',
|
||||
closeText: '{% trans "Close" %}',
|
||||
title: '{% jstrans "Row Data" %}',
|
||||
closeText: '{% jstrans "Close" %}',
|
||||
hideSubmitButton: true
|
||||
});
|
||||
|
||||
@@ -303,11 +303,11 @@ function downloadBomTemplate(options={}) {
|
||||
}
|
||||
|
||||
constructFormBody({}, {
|
||||
title: '{% trans "Download BOM Template" %}',
|
||||
title: '{% jstrans "Download BOM Template" %}',
|
||||
fields: {
|
||||
format: {
|
||||
label: '{% trans "Format" %}',
|
||||
help_text: '{% trans "Select file format" %}',
|
||||
label: '{% jstrans "Format" %}',
|
||||
help_text: '{% jstrans "Select file format" %}',
|
||||
required: true,
|
||||
type: 'choice',
|
||||
value: format,
|
||||
@@ -337,63 +337,63 @@ function downloadBomTemplate(options={}) {
|
||||
function exportBom(part_id, options={}) {
|
||||
|
||||
constructFormBody({}, {
|
||||
title: '{% trans "Export BOM" %}',
|
||||
title: '{% jstrans "Export BOM" %}',
|
||||
fields: {
|
||||
format: {
|
||||
label: '{% trans "Format" %}',
|
||||
help_text: '{% trans "Select file format" %}',
|
||||
label: '{% jstrans "Format" %}',
|
||||
help_text: '{% jstrans "Select file format" %}',
|
||||
required: true,
|
||||
type: 'choice',
|
||||
value: inventreeLoad('bom-export-format', 'csv'),
|
||||
choices: exportFormatOptions(),
|
||||
},
|
||||
cascade: {
|
||||
label: '{% trans "Multi Level BOM" %}',
|
||||
help_text: '{% trans "Include BOM data for subassemblies" %}',
|
||||
label: '{% jstrans "Multi Level BOM" %}',
|
||||
help_text: '{% jstrans "Include BOM data for subassemblies" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-cascading', true),
|
||||
},
|
||||
levels: {
|
||||
label: '{% trans "Levels" %}',
|
||||
help_text: '{% trans "Select maximum number of BOM levels to export (0 = all levels)" %}',
|
||||
label: '{% jstrans "Levels" %}',
|
||||
help_text: '{% jstrans "Select maximum number of BOM levels to export (0 = all levels)" %}',
|
||||
type: 'integer',
|
||||
value: 0,
|
||||
required: true,
|
||||
min_value: 0,
|
||||
},
|
||||
substitute_part_data: {
|
||||
label: '{% trans "Include Alternative Parts" %}',
|
||||
help_text: '{% trans "Include alternative parts in exported BOM" %}',
|
||||
label: '{% jstrans "Include Alternative Parts" %}',
|
||||
help_text: '{% jstrans "Include alternative parts in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-substitute_part_data', false),
|
||||
},
|
||||
parameter_data: {
|
||||
label: '{% trans "Include Parameter Data" %}',
|
||||
help_text: '{% trans "Include part parameter data in exported BOM" %}',
|
||||
label: '{% jstrans "Include Parameter Data" %}',
|
||||
help_text: '{% jstrans "Include part parameter data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-parameter_data', false),
|
||||
},
|
||||
stock_data: {
|
||||
label: '{% trans "Include Stock Data" %}',
|
||||
help_text: '{% trans "Include part stock data in exported BOM" %}',
|
||||
label: '{% jstrans "Include Stock Data" %}',
|
||||
help_text: '{% jstrans "Include part stock data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-stock_data', false),
|
||||
},
|
||||
manufacturer_data: {
|
||||
label: '{% trans "Include Manufacturer Data" %}',
|
||||
help_text: '{% trans "Include part manufacturer data in exported BOM" %}',
|
||||
label: '{% jstrans "Include Manufacturer Data" %}',
|
||||
help_text: '{% jstrans "Include part manufacturer data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-manufacturer_data', false),
|
||||
},
|
||||
supplier_data: {
|
||||
label: '{% trans "Include Supplier Data" %}',
|
||||
help_text: '{% trans "Include part supplier data in exported BOM" %}',
|
||||
label: '{% jstrans "Include Supplier Data" %}',
|
||||
help_text: '{% jstrans "Include part supplier data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-supplier_data', false),
|
||||
},
|
||||
pricing_data: {
|
||||
label: '{% trans "Include Pricing Data" %}',
|
||||
help_text: '{% trans "Include part pricing data in exported BOM" %}',
|
||||
label: '{% jstrans "Include Pricing Data" %}',
|
||||
help_text: '{% jstrans "Include part pricing data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-pricing_data', false),
|
||||
}
|
||||
@@ -441,7 +441,7 @@ function bomItemFields() {
|
||||
sub_part: {
|
||||
icon: 'fa-shapes',
|
||||
secondary: {
|
||||
title: '{% trans "New Part" %}',
|
||||
title: '{% jstrans "New Part" %}',
|
||||
fields: function() {
|
||||
var fields = partFields();
|
||||
|
||||
@@ -588,7 +588,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
|
||||
var buttons = '';
|
||||
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove substitute part" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% jstrans "Remove substitute part" %}');
|
||||
|
||||
// Render a single row
|
||||
var html = `
|
||||
@@ -619,7 +619,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block'>
|
||||
<strong>{% trans "Base Part" %}</strong><hr>
|
||||
<strong>{% jstrans "Base Part" %}</strong><hr>
|
||||
${part_thumb} ${part_name} - <em>${part_desc}</em>
|
||||
</div>
|
||||
`;
|
||||
@@ -629,8 +629,8 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
<table class='table table-striped table-condensed' id='substitute-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Description" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Description" %}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -642,7 +642,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
|
||||
html += `
|
||||
<div class='alert alert-success alert-block'>
|
||||
{% trans "Select and add a new substitute part using the input below" %}
|
||||
{% jstrans "Select and add a new substitute part using the input below" %}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -653,13 +653,13 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
|
||||
var pre = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "Are you sure you wish to remove this substitute part link?" %}
|
||||
{% jstrans "Are you sure you wish to remove this substitute part link?" %}
|
||||
</div>
|
||||
`;
|
||||
|
||||
constructForm(`{% url "api-bom-substitute-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Remove Substitute Part" %}',
|
||||
title: '{% jstrans "Remove Substitute Part" %}',
|
||||
preFormContent: pre,
|
||||
confirm: true,
|
||||
onSuccess: function() {
|
||||
@@ -697,9 +697,9 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
},
|
||||
},
|
||||
preFormContent: html,
|
||||
closeText: '{% trans "Close" %}',
|
||||
submitText: '{% trans "Add Substitute" %}',
|
||||
title: '{% trans "Edit BOM Item Substitutes" %}',
|
||||
closeText: '{% jstrans "Close" %}',
|
||||
submitText: '{% jstrans "Add Substitute" %}',
|
||||
title: '{% jstrans "Edit BOM Item Substitutes" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
addRemoveCallback(opts.modal, '.button-row-remove');
|
||||
},
|
||||
@@ -761,14 +761,14 @@ function deleteBomItems(items, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected BOM items will be deleted" %}
|
||||
{% jstrans "All selected BOM items will be deleted" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Reference" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Reference" %}</th>
|
||||
<th>{% jstrans "Quantity" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>
|
||||
@@ -777,7 +777,7 @@ function deleteBomItems(items, options={}) {
|
||||
constructForm('{% url "api-bom-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete selected BOM items?" %}',
|
||||
title: '{% jstrans "Delete selected BOM items?" %}',
|
||||
form_data: {
|
||||
items: ids,
|
||||
},
|
||||
@@ -823,7 +823,7 @@ function loadBomTable(table, options={}) {
|
||||
label: 'actions',
|
||||
actions: [{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete items" %}',
|
||||
title: '{% jstrans "Delete items" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'part.change',
|
||||
callback: function(data) {
|
||||
@@ -902,7 +902,7 @@ function loadBomTable(table, options={}) {
|
||||
cols.push(
|
||||
{
|
||||
field: 'sub_part',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
sorter: function(_valA, _valB, rowA, rowB) {
|
||||
@@ -933,7 +933,7 @@ function loadBomTable(table, options={}) {
|
||||
} else {
|
||||
html += `
|
||||
<a href='#' pk='${row.pk}' class='load-sub-assembly' id='load-sub-assembly-${row.pk}'>
|
||||
<span class='fas fa-sync-alt' title='{% trans "Load BOM for subassembly" %}'></span>
|
||||
<span class='fas fa-sync-alt' title='{% jstrans "Load BOM for subassembly" %}'></span>
|
||||
</a> `;
|
||||
}
|
||||
}
|
||||
@@ -943,11 +943,11 @@ function loadBomTable(table, options={}) {
|
||||
html += makePartIcons(sub_part);
|
||||
|
||||
if (row.substitutes && row.substitutes.length > 0) {
|
||||
html += makeIconBadge('fa-exchange-alt', '{% trans "Substitutes Available" %}');
|
||||
html += makeIconBadge('fa-exchange-alt', '{% jstrans "Substitutes Available" %}');
|
||||
}
|
||||
|
||||
if (row.allow_variants) {
|
||||
html += makeIconBadge('fa-sitemap', '{% trans "Variant stock allowed" %}');
|
||||
html += makeIconBadge('fa-sitemap', '{% jstrans "Variant stock allowed" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -960,7 +960,7 @@ function loadBomTable(table, options={}) {
|
||||
cols.push(
|
||||
{
|
||||
field: 'sub_part_detail.description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
formatter: function(value) {
|
||||
return withTitle(shortenString(value), value);
|
||||
}
|
||||
@@ -970,7 +970,7 @@ function loadBomTable(table, options={}) {
|
||||
// Part reference
|
||||
cols.push({
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
title: '{% jstrans "Reference" %}',
|
||||
searchable: true,
|
||||
sortable: true,
|
||||
});
|
||||
@@ -978,7 +978,7 @@ function loadBomTable(table, options={}) {
|
||||
// Part quantity
|
||||
cols.push({
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
searchable: false,
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
@@ -994,11 +994,11 @@ function loadBomTable(table, options={}) {
|
||||
}
|
||||
|
||||
if (row.consumable) {
|
||||
text += ` <small>({% trans "Consumable" %})</small>`;
|
||||
text += ` <small>({% jstrans "Consumable" %})</small>`;
|
||||
}
|
||||
|
||||
if (row.optional) {
|
||||
text += ' <small>({% trans "Optional" %})</small>';
|
||||
text += ' <small>({% jstrans "Optional" %})</small>';
|
||||
}
|
||||
|
||||
if (row.overage) {
|
||||
@@ -1011,7 +1011,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
cols.push({
|
||||
field: 'substitutes',
|
||||
title: '{% trans "Substitutes" %}',
|
||||
title: '{% jstrans "Substitutes" %}',
|
||||
searchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1025,7 +1025,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
cols.push({
|
||||
field: 'optional',
|
||||
title: '{% trans "Optional" %}',
|
||||
title: '{% jstrans "Optional" %}',
|
||||
searchable: false,
|
||||
formatter: function(value) {
|
||||
return yesNoLabel(value);
|
||||
@@ -1034,7 +1034,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
cols.push({
|
||||
field: 'consumable',
|
||||
title: '{% trans "Consumable" %}',
|
||||
title: '{% jstrans "Consumable" %}',
|
||||
searchable: false,
|
||||
formatter: function(value) {
|
||||
return yesNoLabel(value);
|
||||
@@ -1043,7 +1043,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
cols.push({
|
||||
field: 'allow_variants',
|
||||
title: '{% trans "Allow Variants" %}',
|
||||
title: '{% jstrans "Allow Variants" %}',
|
||||
formatter: function(value) {
|
||||
return yesNoLabel(value);
|
||||
}
|
||||
@@ -1051,7 +1051,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
cols.push({
|
||||
field: 'inherited',
|
||||
title: '{% trans "Gets inherited" %}',
|
||||
title: '{% jstrans "Gets inherited" %}',
|
||||
searchable: false,
|
||||
formatter: function(value, row) {
|
||||
// This BOM item *is* inheritable, but is defined for this BOM
|
||||
@@ -1068,7 +1068,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
cols.push({
|
||||
field: 'pricing',
|
||||
title: '{% trans "Price Range" %}',
|
||||
title: '{% jstrans "Price Range" %}',
|
||||
sortable: true,
|
||||
sorter: function(valA, valB, rowA, rowB) {
|
||||
var a = rowA.pricing_min || rowA.pricing_max;
|
||||
@@ -1136,19 +1136,19 @@ function loadBomTable(table, options={}) {
|
||||
if (complete_pricing) {
|
||||
html += makeIconBadge(
|
||||
'fa-check-circle icon-green',
|
||||
'{% trans "BOM pricing is complete" %}',
|
||||
'{% jstrans "BOM pricing is complete" %}',
|
||||
);
|
||||
} else {
|
||||
html += makeIconBadge(
|
||||
'fa-exclamation-circle icon-yellow',
|
||||
'{% trans "BOM pricing is incomplete" %}',
|
||||
'{% jstrans "BOM pricing is incomplete" %}',
|
||||
);
|
||||
}
|
||||
|
||||
return html;
|
||||
|
||||
} else {
|
||||
let html = '<em>{% trans "No pricing available" %}</em>';
|
||||
let html = '<em>{% jstrans "No pricing available" %}</em>';
|
||||
html += makeIconBadge('fa-times-circle icon-red');
|
||||
|
||||
return html;
|
||||
@@ -1159,7 +1159,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
cols.push({
|
||||
field: 'available_stock',
|
||||
title: '{% trans "Available" %}',
|
||||
title: '{% jstrans "Available" %}',
|
||||
searchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1179,16 +1179,16 @@ function loadBomTable(table, options={}) {
|
||||
}
|
||||
|
||||
if (available_stock <= 0) {
|
||||
text += makeIconBadge('fa-times-circle icon-red', '{% trans "No Stock Available" %}');
|
||||
text += makeIconBadge('fa-times-circle icon-red', '{% jstrans "No Stock Available" %}');
|
||||
} else {
|
||||
var extra = '';
|
||||
|
||||
if ((substitute_stock > 0) && (variant_stock > 0)) {
|
||||
extra = '{% trans "Includes variant and substitute stock" %}';
|
||||
extra = '{% jstrans "Includes variant and substitute stock" %}';
|
||||
} else if (variant_stock > 0) {
|
||||
extra = '{% trans "Includes variant stock" %}';
|
||||
extra = '{% jstrans "Includes variant stock" %}';
|
||||
} else if (substitute_stock > 0) {
|
||||
extra = '{% trans "Includes substitute stock" %}';
|
||||
extra = '{% jstrans "Includes substitute stock" %}';
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
@@ -1199,7 +1199,7 @@ function loadBomTable(table, options={}) {
|
||||
if (row.on_order && row.on_order > 0) {
|
||||
text += makeIconBadge(
|
||||
'fa-shopping-cart',
|
||||
`{% trans "On Order" %}: ${row.on_order}`,
|
||||
`{% jstrans "On Order" %}: ${row.on_order}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1210,13 +1210,13 @@ function loadBomTable(table, options={}) {
|
||||
cols.push(
|
||||
{
|
||||
field: 'can_build',
|
||||
title: '{% trans "Can Build" %}',
|
||||
title: '{% jstrans "Can Build" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
|
||||
// "Consumable" parts are not tracked in the build
|
||||
if (row.consumable) {
|
||||
return `<em>{% trans "Consumable item" %}</em>`;
|
||||
return `<em>{% jstrans "Consumable item" %}</em>`;
|
||||
}
|
||||
|
||||
var can_build = canBuildQuantity(row);
|
||||
@@ -1256,7 +1256,7 @@ function loadBomTable(table, options={}) {
|
||||
cols.push(
|
||||
{
|
||||
field: 'note',
|
||||
title: '{% trans "Notes" %}',
|
||||
title: '{% jstrans "Notes" %}',
|
||||
searchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
@@ -1268,7 +1268,7 @@ function loadBomTable(table, options={}) {
|
||||
if (options.editable) {
|
||||
|
||||
cols.push({
|
||||
title: '{% trans "Actions" %}',
|
||||
title: '{% jstrans "Actions" %}',
|
||||
switchable: false,
|
||||
field: 'pk',
|
||||
visible: true,
|
||||
@@ -1276,15 +1276,15 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
if (row.part == options.parent_id) {
|
||||
|
||||
var bValidate = makeIconButton('fa-check-circle icon-green', 'bom-validate-button', row.pk, '{% trans "Validate BOM Item" %}');
|
||||
var bValidate = makeIconButton('fa-check-circle icon-green', 'bom-validate-button', row.pk, '{% jstrans "Validate BOM Item" %}');
|
||||
|
||||
var bValid = makeIconButton('fa-check-double icon-green', 'bom-valid-button', row.pk, '{% trans "This line has been validated" %}', {disabled: true});
|
||||
var bValid = makeIconButton('fa-check-double icon-green', 'bom-valid-button', row.pk, '{% jstrans "This line has been validated" %}', {disabled: true});
|
||||
|
||||
var bSubs = makeIconButton('fa-exchange-alt icon-blue', 'bom-substitutes-button', row.pk, '{% trans "Edit substitute parts" %}');
|
||||
var bSubs = makeIconButton('fa-exchange-alt icon-blue', 'bom-substitutes-button', row.pk, '{% jstrans "Edit substitute parts" %}');
|
||||
|
||||
var bEdit = makeEditButton('bom-edit-button', row.pk, '{% trans "Edit BOM Item" %}');
|
||||
var bEdit = makeEditButton('bom-edit-button', row.pk, '{% jstrans "Edit BOM Item" %}');
|
||||
|
||||
var bDelt = makeDeleteButton('bom-delete-button', row.pk, '{% trans "Delete BOM Item" %}');
|
||||
var bDelt = makeDeleteButton('bom-delete-button', row.pk, '{% jstrans "Delete BOM Item" %}');
|
||||
|
||||
let buttons = '';
|
||||
|
||||
@@ -1304,15 +1304,15 @@ function loadBomTable(table, options={}) {
|
||||
// Return a link to the external BOM
|
||||
|
||||
return renderLink(
|
||||
'{% trans "View BOM" %}',
|
||||
'{% jstrans "View BOM" %}',
|
||||
`/part/${row.part}/bom/`
|
||||
);
|
||||
}
|
||||
},
|
||||
footerFormatter: function(data) {
|
||||
return `
|
||||
<button class='btn btn-success float-right' type='button' title='{% trans "Add BOM Item" %}' id='bom-item-new-footer'>
|
||||
${makeIcon('fa-plus-circle')} {% trans "Add BOM Item" %}
|
||||
<button class='btn btn-success float-right' type='button' title='{% jstrans "Add BOM Item" %}' id='bom-item-new-footer'>
|
||||
${makeIcon('fa-plus-circle')} {% jstrans "Add BOM Item" %}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
@@ -1388,7 +1388,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
},
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No BOM items found" %}';
|
||||
return '{% jstrans "No BOM items found" %}';
|
||||
},
|
||||
queryParams: filters,
|
||||
original: params,
|
||||
@@ -1477,7 +1477,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
constructForm(`{% url "api-bom-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit BOM Item" %}',
|
||||
title: '{% jstrans "Edit BOM Item" %}',
|
||||
focus: 'sub_part',
|
||||
onSuccess: function() {
|
||||
reloadBomTable(table);
|
||||
@@ -1630,7 +1630,7 @@ function loadUsedInTable(table, part_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'part',
|
||||
title: '{% trans "Assembly" %}',
|
||||
title: '{% jstrans "Assembly" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1648,7 +1648,7 @@ function loadUsedInTable(table, part_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'sub_part',
|
||||
title: '{% trans "Required Part" %}',
|
||||
title: '{% jstrans "Required Part" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
var url = `/part/${value}/`;
|
||||
@@ -1665,7 +1665,7 @@ function loadUsedInTable(table, part_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Required Quantity" %}',
|
||||
title: '{% jstrans "Required Quantity" %}',
|
||||
formatter: function(value, row) {
|
||||
var html = value;
|
||||
|
||||
@@ -1674,7 +1674,7 @@ function loadUsedInTable(table, part_id, options={}) {
|
||||
}
|
||||
|
||||
if (row.parent && row.parent != 'top-level-item') {
|
||||
html += ` <em>({% trans "Inherited from parent BOM" %})</em>`;
|
||||
html += ` <em>({% jstrans "Inherited from parent BOM" %})</em>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
|
||||
@@ -139,7 +139,7 @@ function editBuildOrder(pk) {
|
||||
constructForm(`{% url "api-build-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
reload: true,
|
||||
title: '{% trans "Edit Build Order" %}',
|
||||
title: '{% jstrans "Edit Build Order" %}',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ function newBuildOrder(options={}) {
|
||||
data: options.data,
|
||||
follow: true,
|
||||
method: 'POST',
|
||||
title: '{% trans "Create Build Order" %}',
|
||||
title: '{% jstrans "Create Build Order" %}',
|
||||
onSuccess: options.onSuccess,
|
||||
});
|
||||
}
|
||||
@@ -214,7 +214,7 @@ function cancelBuildOrder(build_id, options={}) {
|
||||
`{% url "api-build-list" %}${build_id}/cancel/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Cancel Build Order" %}',
|
||||
title: '{% jstrans "Cancel Build Order" %}',
|
||||
confirm: true,
|
||||
fields: {
|
||||
remove_allocated_stock: {},
|
||||
@@ -223,20 +223,20 @@ function cancelBuildOrder(build_id, options={}) {
|
||||
preFormContent: function(opts) {
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Are you sure you wish to cancel this build?" %}
|
||||
{% jstrans "Are you sure you wish to cancel this build?" %}
|
||||
</div>`;
|
||||
|
||||
if (opts.context.has_allocated_stock) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "Stock items have been allocated to this build order" %}
|
||||
{% jstrans "Stock items have been allocated to this build order" %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (opts.context.incomplete_outputs) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "There are incomplete outputs remaining for this build order" %}
|
||||
{% jstrans "There are incomplete outputs remaining for this build order" %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -288,30 +288,30 @@ function completeBuildOrder(build_id, options={}) {
|
||||
if (ctx.allocated && ctx.remaining == 0 && ctx.incomplete == 0) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-success'>
|
||||
{% trans "Build order is ready to be completed" %}'
|
||||
{% jstrans "Build order is ready to be completed" %}'
|
||||
</div>`;
|
||||
} else {
|
||||
|
||||
if (ctx.incomplete > 0) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
<strong>{% trans "Build order has incomplete outputs" %}</strong><br>
|
||||
{% trans "This build order cannot be completed as there are incomplete outputs" %}
|
||||
<strong>{% jstrans "Build order has incomplete outputs" %}</strong><br>
|
||||
{% jstrans "This build order cannot be completed as there are incomplete outputs" %}
|
||||
</div>`;
|
||||
} else {
|
||||
html += `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
<strong>{% trans "Build Order is incomplete" %}</strong>
|
||||
<strong>{% jstrans "Build Order is incomplete" %}</strong>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (!ctx.allocated) {
|
||||
html += `<div class='alert alert-block alert-warning'>{% trans "Required stock has not been fully allocated" %}</div>`;
|
||||
html += `<div class='alert alert-block alert-warning'>{% jstrans "Required stock has not been fully allocated" %}</div>`;
|
||||
}
|
||||
|
||||
if (ctx.remaining > 0) {
|
||||
html += `<div class='alert alert-block alert-warning'>{% trans "Required build quantity has not been completed" %}</div>`;
|
||||
html += `<div class='alert alert-block alert-warning'>{% jstrans "Required build quantity has not been completed" %}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ function completeBuildOrder(build_id, options={}) {
|
||||
},
|
||||
reload: true,
|
||||
confirm: true,
|
||||
title: '{% trans "Complete Build Order" %}',
|
||||
title: '{% jstrans "Complete Build Order" %}',
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
@@ -360,9 +360,9 @@ function createBuildOutput(build_id, options) {
|
||||
inventreeGet(`{% url "api-part-list" %}${build.part}/serial-numbers/`, {}, {
|
||||
success: function(data) {
|
||||
if (data.next) {
|
||||
fields.serial_numbers.placeholder = `{% trans "Next available serial number" %}: ${data.next}`;
|
||||
fields.serial_numbers.placeholder = `{% jstrans "Next available serial number" %}: ${data.next}`;
|
||||
} else if (data.latest) {
|
||||
fields.serial_numbers.placeholder = `{% trans "Latest serial number" %}: ${data.latest}`;
|
||||
fields.serial_numbers.placeholder = `{% jstrans "Latest serial number" %}: ${data.latest}`;
|
||||
}
|
||||
},
|
||||
async: false,
|
||||
@@ -371,8 +371,8 @@ function createBuildOutput(build_id, options) {
|
||||
if (options.trackable_parts) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "The Bill of Materials contains trackable parts" %}.<br>
|
||||
{% trans "Build outputs must be generated individually" %}.
|
||||
{% jstrans "The Bill of Materials contains trackable parts" %}.<br>
|
||||
{% jstrans "Build outputs must be generated individually" %}.
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -380,15 +380,15 @@ function createBuildOutput(build_id, options) {
|
||||
if (trackable) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Trackable parts can have serial numbers specified" %}<br>
|
||||
{% trans "Enter serial numbers to generate multiple single build outputs" %}
|
||||
{% jstrans "Trackable parts can have serial numbers specified" %}<br>
|
||||
{% jstrans "Enter serial numbers to generate multiple single build outputs" %}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/create-output/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Create Build Output" %}',
|
||||
title: '{% jstrans "Create Build Output" %}',
|
||||
confirm: true,
|
||||
fields: fields,
|
||||
preFormContent: html,
|
||||
@@ -419,7 +419,7 @@ function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
'fa-sign-in-alt icon-blue',
|
||||
'button-output-allocate',
|
||||
output_id,
|
||||
'{% trans "Allocate stock items to this build output" %}',
|
||||
'{% jstrans "Allocate stock items to this build output" %}',
|
||||
);
|
||||
|
||||
// Add a button to deallocate stock from this build output
|
||||
@@ -427,7 +427,7 @@ function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
'fa-minus-circle icon-red',
|
||||
'button-output-deallocate',
|
||||
output_id,
|
||||
'{% trans "Deallocate stock from build output" %}',
|
||||
'{% jstrans "Deallocate stock from build output" %}',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
'fa-check-circle icon-green',
|
||||
'button-output-complete',
|
||||
output_id,
|
||||
'{% trans "Complete build output" %}',
|
||||
'{% jstrans "Complete build output" %}',
|
||||
);
|
||||
|
||||
// Add a button to "scrap" the build output
|
||||
@@ -444,14 +444,14 @@ function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
'fa-times-circle icon-red',
|
||||
'button-output-scrap',
|
||||
output_id,
|
||||
'{% trans "Scrap build output" %}',
|
||||
'{% jstrans "Scrap build output" %}',
|
||||
);
|
||||
|
||||
// Add a button to "remove" this build output
|
||||
html += makeDeleteButton(
|
||||
'button-output-remove',
|
||||
output_id,
|
||||
'{% trans "Delete build output" %}',
|
||||
'{% jstrans "Delete build output" %}',
|
||||
);
|
||||
|
||||
return wrapButtons(html);
|
||||
@@ -471,7 +471,7 @@ function deallocateStock(build_id, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "Are you sure you wish to deallocate the selected stock items from this build?" %}
|
||||
{% jstrans "Are you sure you wish to deallocate the selected stock items from this build?" %}
|
||||
</dvi>
|
||||
`;
|
||||
|
||||
@@ -489,7 +489,7 @@ function deallocateStock(build_id, options={}) {
|
||||
value: options.build_line,
|
||||
},
|
||||
},
|
||||
title: '{% trans "Deallocate Stock Items" %}',
|
||||
title: '{% jstrans "Deallocate Stock Items" %}',
|
||||
onSuccess: function(response, opts) {
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess(response, opts);
|
||||
@@ -511,9 +511,9 @@ function renderBuildOutput(output, options={}) {
|
||||
let output_html = imageHoverIcon(output.part_detail.thumbnail);
|
||||
|
||||
if (output.quantity == 1 && output.serial) {
|
||||
output_html += `{% trans "Serial Number" %}: ${output.serial}`;
|
||||
output_html += `{% jstrans "Serial Number" %}: ${output.serial}`;
|
||||
} else {
|
||||
output_html += `{% trans "Quantity" %}: ${output.quantity}`;
|
||||
output_html += `{% jstrans "Quantity" %}: ${output.quantity}`;
|
||||
if (output.part_detail && output.part_detail.units) {
|
||||
output_html += ` ${output.part_detail.units} `;
|
||||
}
|
||||
@@ -521,7 +521,7 @@ function renderBuildOutput(output, options={}) {
|
||||
|
||||
let buttons = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% jstrans "Remove row" %}');
|
||||
|
||||
buttons += '</div>';
|
||||
|
||||
@@ -575,8 +575,8 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
if (outputs.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Build Outputs" %}',
|
||||
'{% trans "At least one build output must be selected" %}',
|
||||
'{% jstrans "Select Build Outputs" %}',
|
||||
'{% jstrans "At least one build output must be selected" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -590,11 +590,11 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-success'>
|
||||
{% trans "Selected build outputs will be marked as complete" %}
|
||||
{% jstrans "Selected build outputs will be marked as complete" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed' id='build-complete-table'>
|
||||
<thead>
|
||||
<th colspan='2'>{% trans "Output" %}</th>
|
||||
<th colspan='2'>{% jstrans "Output" %}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -622,7 +622,7 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
accept_incomplete_allocation: {},
|
||||
},
|
||||
confirm: true,
|
||||
title: '{% trans "Complete Build Outputs" %}',
|
||||
title: '{% jstrans "Complete Build Outputs" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
// Setup callbacks to remove outputs
|
||||
$(opts.modal).find('.button-row-remove').click(function() {
|
||||
@@ -703,8 +703,8 @@ function scrapBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
if (outputs.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Build Outputs" %}',
|
||||
'{% trans "At least one build output must be selected" %}',
|
||||
'{% jstrans "Select Build Outputs" %}',
|
||||
'{% jstrans "At least one build output must be selected" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -719,17 +719,17 @@ function scrapBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "Selected build outputs will be marked as scrapped" %}
|
||||
{% jstrans "Selected build outputs will be marked as scrapped" %}
|
||||
<ul>
|
||||
<li>{% trans "Scrapped output are marked as rejected" %}</li>
|
||||
<li>{% trans "Allocated stock items will no longer be available" %}</li>
|
||||
<li>{% trans "The completion status of the build order will not be adjusted" %}</li>
|
||||
<li>{% jstrans "Scrapped output are marked as rejected" %}</li>
|
||||
<li>{% jstrans "Allocated stock items will no longer be available" %}</li>
|
||||
<li>{% jstrans "The completion status of the build order will not be adjusted" %}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<table class='table table-striped table-condensed' id='build-scrap-table'>
|
||||
<thead>
|
||||
<th colspan='2'>{% trans "Output" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th colspan='2'>{% jstrans "Output" %}</th>
|
||||
<th>{% jstrans "Quantity" %}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -754,7 +754,7 @@ function scrapBuildOutputs(build_id, outputs, options={}) {
|
||||
discard_allocations: {},
|
||||
},
|
||||
confirm: true,
|
||||
title: '{% trans "Scrap Build Outputs" %}',
|
||||
title: '{% jstrans "Scrap Build Outputs" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
// Setup callbacks to remove outputs
|
||||
$(opts.modal).find('.button-row-remove').click(function() {
|
||||
@@ -829,8 +829,8 @@ function deleteBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
if (outputs.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Build Outputs" %}',
|
||||
'{% trans "At least one build output must be selected" %}',
|
||||
'{% jstrans "Select Build Outputs" %}',
|
||||
'{% jstrans "At least one build output must be selected" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -844,15 +844,15 @@ function deleteBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "Selected build outputs will be deleted" %}
|
||||
{% jstrans "Selected build outputs will be deleted" %}
|
||||
<ul>
|
||||
<li>{% trans "Build output data will be permanently deleted" %}</li>
|
||||
<li>{% trans "Allocated stock items will be returned to stock" %}</li>
|
||||
<li>{% jstrans "Build output data will be permanently deleted" %}</li>
|
||||
<li>{% jstrans "Allocated stock items will be returned to stock" %}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<table class='table table-striped table-condensed' id='build-complete-table'>
|
||||
<thead>
|
||||
<th colspan='2'>{% trans "Output" %}</th>
|
||||
<th colspan='2'>{% jstrans "Output" %}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -865,7 +865,7 @@ function deleteBuildOutputs(build_id, outputs, options={}) {
|
||||
preFormContent: html,
|
||||
fields: {},
|
||||
confirm: true,
|
||||
title: '{% trans "Delete Build Outputs" %}',
|
||||
title: '{% jstrans "Delete Build Outputs" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
// Setup callbacks to remove outputs
|
||||
$(opts.modal).find('.button-row-remove').click(function() {
|
||||
@@ -952,7 +952,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
paginationVAlign: 'bottom',
|
||||
original: options.params,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No build order allocations found" %}';
|
||||
return '{% jstrans "No build order allocations found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
@@ -964,7 +964,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
field: 'build',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
title: '{% trans "Build Order" %}',
|
||||
title: '{% jstrans "Build Order" %}',
|
||||
formatter: function(value, row) {
|
||||
let ref = `${row.build_detail.reference}`;
|
||||
let html = renderLink(ref, `/build/${row.build}/`);
|
||||
@@ -981,7 +981,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
{
|
||||
field: 'quantity',
|
||||
sortable: true,
|
||||
title: '{% trans "Allocated Quantity" %}',
|
||||
title: '{% jstrans "Allocated Quantity" %}',
|
||||
formatter: function(value, row) {
|
||||
let link = `/stock/item/${row.stock_item}/`;
|
||||
let text = formatDecimal(value);
|
||||
@@ -991,11 +991,11 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'location_detail',
|
||||
title: '{% trans "Location" %}',
|
||||
title: '{% jstrans "Location" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (!value) {
|
||||
return '{% trans "Location not specified" %}';
|
||||
return '{% jstrans "Location not specified" %}';
|
||||
}
|
||||
|
||||
let item = row.stock_item_detail;
|
||||
@@ -1017,7 +1017,7 @@ function makeBuildOutputActions(build_info) {
|
||||
return [
|
||||
{
|
||||
label: 'complete',
|
||||
title: '{% trans "Complete outputs" %}',
|
||||
title: '{% jstrans "Complete outputs" %}',
|
||||
icon: 'fa-check-circle icon-green',
|
||||
permission: 'build.add',
|
||||
callback: function(data) {
|
||||
@@ -1035,7 +1035,7 @@ function makeBuildOutputActions(build_info) {
|
||||
},
|
||||
{
|
||||
label: 'scrap',
|
||||
title: '{% trans "Scrap outputs" %}',
|
||||
title: '{% jstrans "Scrap outputs" %}',
|
||||
icon: 'fa-times-circle icon-red',
|
||||
permission: 'build.change',
|
||||
callback: function(data) {
|
||||
@@ -1053,7 +1053,7 @@ function makeBuildOutputActions(build_info) {
|
||||
},
|
||||
{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete outputs" %}',
|
||||
title: '{% jstrans "Delete outputs" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'build.delete',
|
||||
callback: function(data) {
|
||||
@@ -1089,7 +1089,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
var params = options.params || {};
|
||||
|
||||
// test templates for the part being assembled
|
||||
let test_templates = null;
|
||||
let test_templates = [];
|
||||
|
||||
// tracked line items for this build
|
||||
let has_tracked_lines = false;
|
||||
@@ -1107,12 +1107,12 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
url: '{% url "api-stockitem-label-list" %}',
|
||||
key: 'item',
|
||||
},
|
||||
singular_name: '{% trans "build output" %}',
|
||||
plural_name: '{% trans "build outputs" %}',
|
||||
singular_name: '{% jstrans "build output" %}',
|
||||
plural_name: '{% jstrans "build outputs" %}',
|
||||
custom_actions: [{
|
||||
label: 'buildoutput',
|
||||
icon: 'fa-tools',
|
||||
title: '{% trans "Build output actions" %}',
|
||||
title: '{% jstrans "Build output actions" %}',
|
||||
actions: makeBuildOutputActions(build_info),
|
||||
}]
|
||||
});
|
||||
@@ -1133,6 +1133,9 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
test_templates.push(item);
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function() {
|
||||
test_templates = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -1281,7 +1284,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
return constructOutputSubTable(index, row, element);
|
||||
},
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No active build outputs found" %}';
|
||||
return '{% jstrans "No active build outputs found" %}';
|
||||
},
|
||||
onLoadSuccess: function() {
|
||||
reloadOutputAllocations();
|
||||
@@ -1296,7 +1299,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'part',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
return imageHoverIcon(row.part_detail.thumbnail) +
|
||||
@@ -1306,7 +1309,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Build Output" %}',
|
||||
title: '{% jstrans "Build Output" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
sorter: function(fieldA, fieldB, rowA, rowB) {
|
||||
@@ -1351,9 +1354,9 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
let text = '';
|
||||
|
||||
if (row.serial && row.quantity == 1) {
|
||||
text = `{% trans "Serial Number" %}: ${row.serial}`;
|
||||
text = `{% jstrans "Serial Number" %}: ${row.serial}`;
|
||||
} else {
|
||||
text = `{% trans "Quantity" %}: ${row.quantity}`;
|
||||
text = `{% jstrans "Quantity" %}: ${row.quantity}`;
|
||||
|
||||
}
|
||||
|
||||
@@ -1364,7 +1367,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
}
|
||||
|
||||
if (row.batch) {
|
||||
text += ` <small>({% trans "Batch" %}: ${row.batch})</small>`;
|
||||
text += ` <small>({% jstrans "Batch" %}: ${row.batch})</small>`;
|
||||
}
|
||||
|
||||
text += stockStatusDisplay(row.status, {classes: 'float-right'});
|
||||
@@ -1374,7 +1377,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'fully_allocated',
|
||||
title: '{% trans "Allocated Lines" %}',
|
||||
title: '{% jstrans "Allocated Lines" %}',
|
||||
visible: false,
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
@@ -1388,7 +1391,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'tests',
|
||||
title: '{% trans "Required Tests" %}',
|
||||
title: '{% jstrans "Required Tests" %}',
|
||||
visible: test_templates.length > 0,
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
@@ -1560,8 +1563,8 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
if (line_items.length == 0) {
|
||||
|
||||
showAlertDialog(
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "You must select at least one part to allocate" %}',
|
||||
'{% jstrans "Select Parts" %}',
|
||||
'{% jstrans "You must select at least one part to allocate" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
@@ -1613,7 +1616,7 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
delete_button += makeRemoveButton(
|
||||
'button-row-remove',
|
||||
pk,
|
||||
'{% trans "Remove row" %}',
|
||||
'{% jstrans "Remove row" %}',
|
||||
);
|
||||
|
||||
delete_button += `</div>`;
|
||||
@@ -1624,7 +1627,7 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
type: 'decimal',
|
||||
min_value: 0,
|
||||
value: quantity || 0,
|
||||
title: '{% trans "Specify stock allocation quantity" %}',
|
||||
title: '{% jstrans "Specify stock allocation quantity" %}',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@@ -1701,8 +1704,8 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
if (table_entries.length == 0) {
|
||||
|
||||
showAlertDialog(
|
||||
'{% trans "All Parts Allocated" %}',
|
||||
'{% trans "All selected parts have been fully allocated" %}',
|
||||
'{% jstrans "All Parts Allocated" %}',
|
||||
'{% jstrans "All selected parts have been fully allocated" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
@@ -1715,8 +1718,8 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
'take_from',
|
||||
{
|
||||
type: 'related field',
|
||||
label: '{% trans "Source Location" %}',
|
||||
help_text: '{% trans "Select source location (leave blank to take from all locations)" %}',
|
||||
label: '{% jstrans "Source Location" %}',
|
||||
help_text: '{% jstrans "Select source location (leave blank to take from all locations)" %}',
|
||||
required: false,
|
||||
},
|
||||
{},
|
||||
@@ -1727,10 +1730,10 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
<table class='table table-striped table-condensed' id='stock-allocation-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Allocated" %}</th>
|
||||
<th style='min-width: 250px;'>{% trans "Stock Item" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Allocated" %}</th>
|
||||
<th style='min-width: 250px;'>{% jstrans "Stock Item" %}</th>
|
||||
<th>{% jstrans "Quantity" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1744,7 +1747,7 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
method: 'POST',
|
||||
fields: {},
|
||||
preFormContent: html,
|
||||
title: '{% trans "Allocate Stock Items to Build Order" %}',
|
||||
title: '{% jstrans "Allocate Stock Items to Build Order" %}',
|
||||
afterRender: function(fields, options) {
|
||||
|
||||
var take_from_field = {
|
||||
@@ -1755,7 +1758,7 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
type: 'related field',
|
||||
value: source_location,
|
||||
noResults: function(query) {
|
||||
return '{% trans "No matching stock locations" %}';
|
||||
return '{% jstrans "No matching stock locations" %}';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1828,7 +1831,7 @@ function allocateStockToBuild(build_id, line_items, options={}) {
|
||||
return filters;
|
||||
},
|
||||
noResults: function(query) {
|
||||
return '{% trans "No matching stock items" %}';
|
||||
return '{% jstrans "No matching stock items" %}';
|
||||
}
|
||||
},
|
||||
null,
|
||||
@@ -1925,12 +1928,12 @@ function autoAllocateStockToBuild(build_id, bom_items=[], options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
<strong>{% trans "Automatic Stock Allocation" %}</strong><br>
|
||||
{% trans "Stock items will be automatically allocated to this build order, according to the provided guidelines" %}:
|
||||
<strong>{% jstrans "Automatic Stock Allocation" %}</strong><br>
|
||||
{% jstrans "Stock items will be automatically allocated to this build order, according to the provided guidelines" %}:
|
||||
<ul>
|
||||
<li>{% trans "If a location is specified, stock will only be allocated from that location" %}</li>
|
||||
<li>{% trans "If stock is considered interchangeable, it will be allocated from the first location it is found" %}</li>
|
||||
<li>{% trans "If substitute stock is allowed, it will be used where stock of the primary part cannot be found" %}</li>
|
||||
<li>{% jstrans "If a location is specified, stock will only be allocated from that location" %}</li>
|
||||
<li>{% jstrans "If stock is considered interchangeable, it will be allocated from the first location it is found" %}</li>
|
||||
<li>{% jstrans "If substitute stock is allowed, it will be used where stock of the primary part cannot be found" %}</li>
|
||||
</ul>
|
||||
</div>
|
||||
`;
|
||||
@@ -1961,7 +1964,7 @@ function autoAllocateStockToBuild(build_id, bom_items=[], options={}) {
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/auto-allocate/`, {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Allocate Stock Items" %}',
|
||||
title: '{% jstrans "Allocate Stock Items" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
@@ -2067,7 +2070,7 @@ function loadBuildTable(table, options) {
|
||||
$(table).inventreeTable({
|
||||
method: 'get',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No builds matching query" %}';
|
||||
return '{% jstrans "No builds matching query" %}';
|
||||
},
|
||||
url: '{% url "api-build-list" %}',
|
||||
queryParams: filters,
|
||||
@@ -2102,13 +2105,13 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
checkbox: true,
|
||||
title: '{% trans "Select" %}',
|
||||
title: '{% jstrans "Select" %}',
|
||||
searchable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Build" %}',
|
||||
title: '{% jstrans "Build" %}',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -2116,7 +2119,7 @@ function loadBuildTable(table, options) {
|
||||
var html = renderLink(value, '/build/' + row.pk + '/');
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Build order is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% jstrans "Build order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -2124,12 +2127,12 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'project_code',
|
||||
title: '{% trans "Project Code" %}',
|
||||
title: '{% jstrans "Project Code" %}',
|
||||
sortable: true,
|
||||
switchable: global_settings.PROJECT_CODES_ENABLED,
|
||||
visible: global_settings.PROJECT_CODES_ENABLED,
|
||||
@@ -2141,13 +2144,13 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'priority',
|
||||
title: '{% trans "Priority" %}',
|
||||
title: '{% jstrans "Priority" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'part',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
sortable: true,
|
||||
sortName: 'part__name',
|
||||
formatter: function(value, row) {
|
||||
@@ -2162,7 +2165,7 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'completed',
|
||||
title: '{% trans "Progress" %}',
|
||||
title: '{% jstrans "Progress" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
return makeProgressBar(
|
||||
@@ -2176,7 +2179,7 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
title: '{% jstrans "Status" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return buildStatusDisplay(value);
|
||||
@@ -2184,7 +2187,7 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Created" %}',
|
||||
title: '{% jstrans "Created" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
@@ -2192,19 +2195,19 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'issued_by',
|
||||
title: '{% trans "Issued by" %}',
|
||||
title: '{% jstrans "Issued by" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
return row.issued_by_detail.username;
|
||||
} else {
|
||||
return `<i>{% trans "No user information" %}</i>`;
|
||||
return `<i>{% jstrans "No user information" %}</i>`;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'responsible',
|
||||
title: '{% trans "Responsible" %}',
|
||||
title: '{% jstrans "Responsible" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (!row.responsible_detail) {
|
||||
@@ -2213,7 +2216,7 @@ function loadBuildTable(table, options) {
|
||||
|
||||
var html = row.responsible_detail.name;
|
||||
|
||||
if (row.responsible_detail.label == '{% trans "group" %}') {
|
||||
if (row.responsible_detail.label == '{% jstrans "group" %}') {
|
||||
html += `<span class='float-right fas fa-users'></span>`;
|
||||
} else {
|
||||
html += `<span class='float-right fas fa-user'></span>`;
|
||||
@@ -2224,7 +2227,7 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
title: '{% jstrans "Target Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
@@ -2232,7 +2235,7 @@ function loadBuildTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'completion_date',
|
||||
title: '{% trans "Completion Date" %}',
|
||||
title: '{% jstrans "Completion Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
@@ -2320,7 +2323,7 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
columns: [
|
||||
{
|
||||
field: 'part',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
formatter: function(_value, row) {
|
||||
let html = imageHoverIcon(row.part_detail.thumbnail);
|
||||
html += renderLink(row.part_detail.full_name, `/part/${row.part_detail.pk}/`);
|
||||
@@ -2329,7 +2332,7 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Allocated Quantity" %}',
|
||||
title: '{% jstrans "Allocated Quantity" %}',
|
||||
formatter: function(_value, row) {
|
||||
let text = '';
|
||||
let url = '';
|
||||
@@ -2340,9 +2343,9 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
}
|
||||
|
||||
if (serial && row.quantity == 1) {
|
||||
text = `{% trans "Serial Number" %}: ${serial}`;
|
||||
text = `{% jstrans "Serial Number" %}: ${serial}`;
|
||||
} else {
|
||||
text = `{% trans "Quantity" %}: ${row.quantity}`;
|
||||
text = `{% jstrans "Quantity" %}: ${row.quantity}`;
|
||||
if (row.part_detail && row.part_detail.units) {
|
||||
text += ` <small>${row.part_detail.units}</small>`;
|
||||
}
|
||||
@@ -2357,7 +2360,7 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
title: '{% trans "Location" %}',
|
||||
title: '{% jstrans "Location" %}',
|
||||
formatter: function(value, row) {
|
||||
if (row.location_detail) {
|
||||
let text = shortenString(row.location_detail.pathstring);
|
||||
@@ -2365,7 +2368,7 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
|
||||
return renderLink(text, url);
|
||||
} else {
|
||||
return '<i>{% trans "No location set" %}</i>';
|
||||
return '<i>{% jstrans "No location set" %}</i>';
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2374,8 +2377,8 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
title: '',
|
||||
formatter: function(value, row) {
|
||||
let buttons = '';
|
||||
buttons += makeEditButton('button-allocation-edit', row.pk, '{% trans "Edit stock allocation" %}');
|
||||
buttons += makeDeleteButton('button-allocation-delete', row.pk, '{% trans "Delete stock allocation" %}');
|
||||
buttons += makeEditButton('button-allocation-edit', row.pk, '{% jstrans "Edit stock allocation" %}');
|
||||
buttons += makeDeleteButton('button-allocation-delete', row.pk, '{% jstrans "Delete stock allocation" %}');
|
||||
return wrapButtons(buttons);
|
||||
}
|
||||
}
|
||||
@@ -2390,7 +2393,7 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
fields: {
|
||||
quantity: {},
|
||||
},
|
||||
title: '{% trans "Edit Allocation" %}',
|
||||
title: '{% jstrans "Edit Allocation" %}',
|
||||
onSuccess: function() {
|
||||
$(options.parent_table).bootstrapTable('refresh');
|
||||
},
|
||||
@@ -2402,7 +2405,7 @@ function renderBuildLineAllocationTable(element, build_line, options={}) {
|
||||
|
||||
constructForm(`{% url "api-build-item-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Remove Allocation" %}',
|
||||
title: '{% jstrans "Remove Allocation" %}',
|
||||
onSuccess: function() {
|
||||
$(options.parent_table).bootstrapTable('refresh');
|
||||
},
|
||||
@@ -2443,8 +2446,8 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
url: '{% url "api-buildline-label-list" %}',
|
||||
key: 'line',
|
||||
},
|
||||
singular_name: '{% trans "build line" %}',
|
||||
plural_name: '{% trans "build lines" %}',
|
||||
singular_name: '{% jstrans "build line" %}',
|
||||
plural_name: '{% jstrans "build lines" %}',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2462,18 +2465,18 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
});
|
||||
},
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No build lines found" %}';
|
||||
return '{% jstrans "No build lines found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
checkbox: true,
|
||||
title: '{% trans "Select" %}',
|
||||
title: '{% jstrans "Select" %}',
|
||||
searchable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'bom_item',
|
||||
title: '{% trans "Required Part" %}',
|
||||
title: '{% jstrans "Required Part" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
sortName: 'part',
|
||||
@@ -2488,11 +2491,11 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
html += imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${row.part_detail.pk}/`);
|
||||
|
||||
if (row.bom_item_detail.allow_variants) {
|
||||
html += makeIconBadge('fa-sitemap', '{% trans "Variant stock allowed" %}');
|
||||
html += makeIconBadge('fa-sitemap', '{% jstrans "Variant stock allowed" %}');
|
||||
}
|
||||
|
||||
if (row.part_detail.trackable) {
|
||||
html += makeIconBadge('fa-directions', '{% trans "Trackable part" %}');
|
||||
html += makeIconBadge('fa-directions', '{% jstrans "Trackable part" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -2500,7 +2503,7 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
title: '{% jstrans "Reference" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
return row.bom_item_detail.reference;
|
||||
@@ -2508,7 +2511,7 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'consumable',
|
||||
title: '{% trans "Consumable" %}',
|
||||
title: '{% jstrans "Consumable" %}',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -2517,7 +2520,7 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'optional',
|
||||
title: '{% trans "Optional" %}',
|
||||
title: '{% jstrans "Optional" %}',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -2527,7 +2530,7 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
{
|
||||
field: 'unit_quantity',
|
||||
sortable: true,
|
||||
title: '{% trans "Unit Quantity" %}',
|
||||
title: '{% jstrans "Unit Quantity" %}',
|
||||
formatter: function(value, row) {
|
||||
let text = row.bom_item_detail.quantity;
|
||||
|
||||
@@ -2544,12 +2547,12 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Required Quantity" %}',
|
||||
title: '{% jstrans "Required Quantity" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'available_stock',
|
||||
title: '{% trans "Available" %}',
|
||||
title: '{% jstrans "Available" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
var url = `/part/${row.part_detail.pk}/?display=part-stock`;
|
||||
@@ -2573,24 +2576,24 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
let icons = '';
|
||||
|
||||
if (row.bom_item_detail.consumable) {
|
||||
icons += `<span class='fas fa-info-circle icon-blue float-right' title='{% trans "Consumable item" %}'></span>`;
|
||||
icons += `<span class='fas fa-info-circle icon-blue float-right' title='{% jstrans "Consumable item" %}'></span>`;
|
||||
} else {
|
||||
if (available < (row.quantity - row.allocated)) {
|
||||
icons += makeIconBadge('fa-times-circle icon-red', '{% trans "Insufficient stock available" %}');
|
||||
icons += makeIconBadge('fa-times-circle icon-red', '{% jstrans "Insufficient stock available" %}');
|
||||
} else {
|
||||
icons += makeIconBadge('fa-check-circle icon-green', '{% trans "Sufficient stock available" %}');
|
||||
icons += makeIconBadge('fa-check-circle icon-green', '{% jstrans "Sufficient stock available" %}');
|
||||
}
|
||||
|
||||
if (available <= 0) {
|
||||
icons += `<span class='badge rounded-pill bg-danger'>{% trans "No Stock Available" %}</span>`;
|
||||
icons += `<span class='badge rounded-pill bg-danger'>{% jstrans "No Stock Available" %}</span>`;
|
||||
} else {
|
||||
let extra = '';
|
||||
if ((row.available_substitute_stock > 0) && (row.available_variant_stock > 0)) {
|
||||
extra = '{% trans "Includes variant and substitute stock" %}';
|
||||
extra = '{% jstrans "Includes variant and substitute stock" %}';
|
||||
} else if (row.available_variant_stock > 0) {
|
||||
extra = '{% trans "Includes variant stock" %}';
|
||||
extra = '{% jstrans "Includes variant stock" %}';
|
||||
} else if (row.available_substitute_stock > 0) {
|
||||
extra = '{% trans "Includes substitute stock" %}';
|
||||
extra = '{% jstrans "Includes substitute stock" %}';
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
@@ -2600,7 +2603,7 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
}
|
||||
|
||||
if (row.on_order && row.on_order > 0) {
|
||||
icons += makeIconBadge('fa-shopping-cart', `{% trans "On Order" %}: ${formatDecimal(row.on_order)}`);
|
||||
icons += makeIconBadge('fa-shopping-cart', `{% jstrans "On Order" %}: ${formatDecimal(row.on_order)}`);
|
||||
}
|
||||
|
||||
return renderLink(text, url) + icons;
|
||||
@@ -2608,7 +2611,7 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'allocated',
|
||||
title: '{% trans "Allocated" %}',
|
||||
title: '{% jstrans "Allocated" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
return makeProgressBar(row.allocated, row.quantity);
|
||||
@@ -2625,32 +2628,32 @@ function loadBuildLineTable(table, build_id, options={}) {
|
||||
|
||||
// Consumable items do not need to be allocated
|
||||
if (row.bom_item_detail.consumable) {
|
||||
return `<em>{% trans "Consumable Item" %}</em>`;
|
||||
return `<em>{% jstrans "Consumable Item" %}</em>`;
|
||||
}
|
||||
|
||||
if (row.part_detail.trackable && !options.output) {
|
||||
// Tracked parts must be allocated to a specific build output
|
||||
return `<em>{% trans "Tracked item" %}</em>`;
|
||||
return `<em>{% jstrans "Tracked item" %}</em>`;
|
||||
}
|
||||
|
||||
if (row.allocated < row.quantity) {
|
||||
|
||||
// Add a button to "build" stock for this line
|
||||
if (row.part_detail.assembly) {
|
||||
buttons += makeIconButton('fa-tools icon-blue', 'button-build', pk, '{% trans "Build stock" %}');
|
||||
buttons += makeIconButton('fa-tools icon-blue', 'button-build', pk, '{% jstrans "Build stock" %}');
|
||||
}
|
||||
|
||||
// Add a button to "purchase" stock for this line
|
||||
if (row.part_detail.purchaseable) {
|
||||
buttons += makeIconButton('fa-shopping-cart icon-blue', 'button-buy', pk, '{% trans "Order stock" %}');
|
||||
buttons += makeIconButton('fa-shopping-cart icon-blue', 'button-buy', pk, '{% jstrans "Order stock" %}');
|
||||
}
|
||||
|
||||
// Add a button to "allocate" stock for this line
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-allocate', pk, '{% trans "Allocate stock" %}');
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-allocate', pk, '{% jstrans "Allocate stock" %}');
|
||||
}
|
||||
|
||||
if (row.allocated > 0) {
|
||||
buttons += makeRemoveButton('button-unallocate', pk, '{% trans "Remove stock allocation" %}');
|
||||
buttons += makeRemoveButton('button-unallocate', pk, '{% jstrans "Remove stock allocation" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(buttons);
|
||||
|
||||
@@ -95,7 +95,7 @@ function createManufacturerPart(options={}) {
|
||||
}
|
||||
|
||||
fields.manufacturer.secondary = {
|
||||
title: '{% trans "Add Manufacturer" %}',
|
||||
title: '{% jstrans "Add Manufacturer" %}',
|
||||
fields: function() {
|
||||
var company_fields = companyFormFields();
|
||||
|
||||
@@ -108,7 +108,7 @@ function createManufacturerPart(options={}) {
|
||||
constructForm('{% url "api-manufacturer-part-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Manufacturer Part" %}',
|
||||
title: '{% jstrans "Add Manufacturer Part" %}',
|
||||
onSuccess: options.onSuccess
|
||||
});
|
||||
}
|
||||
@@ -129,7 +129,7 @@ function editManufacturerPart(part, options={}) {
|
||||
|
||||
constructForm(url, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Manufacturer Part" %}',
|
||||
title: '{% jstrans "Edit Manufacturer Part" %}',
|
||||
onSuccess: options.onSuccess
|
||||
});
|
||||
}
|
||||
@@ -198,7 +198,7 @@ function createSupplierPart(options={}) {
|
||||
|
||||
// Add a secondary modal for the supplier
|
||||
fields.supplier.secondary = {
|
||||
title: '{% trans "Add Supplier" %}',
|
||||
title: '{% jstrans "Add Supplier" %}',
|
||||
fields: function() {
|
||||
var company_fields = companyFormFields();
|
||||
|
||||
@@ -210,7 +210,7 @@ function createSupplierPart(options={}) {
|
||||
|
||||
// Add a secondary modal for the manufacturer part
|
||||
fields.manufacturer_part.secondary = {
|
||||
title: '{% trans "Add Manufacturer Part" %}',
|
||||
title: '{% jstrans "Add Manufacturer Part" %}',
|
||||
fields: function(data) {
|
||||
var mp_fields = manufacturerPartFields();
|
||||
|
||||
@@ -240,7 +240,7 @@ function createSupplierPart(options={}) {
|
||||
constructForm('{% url "api-supplier-part-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Supplier Part" %}',
|
||||
title: '{% jstrans "Add Supplier Part" %}',
|
||||
onSuccess: options.onSuccess,
|
||||
header_html: header,
|
||||
});
|
||||
@@ -266,7 +266,7 @@ function duplicateSupplierPart(part, options={}) {
|
||||
constructForm('{% url "api-supplier-part-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Duplicate Supplier Part" %}',
|
||||
title: '{% jstrans "Duplicate Supplier Part" %}',
|
||||
data: data,
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
@@ -291,7 +291,7 @@ function editSupplierPart(part, options={}) {
|
||||
|
||||
constructForm(`{% url "api-supplier-part-list" %}${part}/`, {
|
||||
fields: fields,
|
||||
title: options.title || '{% trans "Edit Supplier Part" %}',
|
||||
title: options.title || '{% jstrans "Edit Supplier Part" %}',
|
||||
onSuccess: options.onSuccess
|
||||
});
|
||||
}
|
||||
@@ -341,14 +341,14 @@ function deleteSupplierParts(parts, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected supplier parts will be deleted" %}
|
||||
{% jstrans "All selected supplier parts will be deleted" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "SKU" %}</th>
|
||||
<th>{% trans "Supplier" %}</th>
|
||||
<th>{% trans "MPN" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "SKU" %}</th>
|
||||
<th>{% jstrans "Supplier" %}</th>
|
||||
<th>{% jstrans "MPN" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>
|
||||
@@ -357,7 +357,7 @@ function deleteSupplierParts(parts, options={}) {
|
||||
constructForm('{% url "api-supplier-part-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete Supplier Parts" %}',
|
||||
title: '{% jstrans "Delete Supplier Parts" %}',
|
||||
preFormContent: html,
|
||||
form_data: {
|
||||
items: ids,
|
||||
@@ -395,7 +395,7 @@ function createSupplierPartPriceBreak(part_id, options={}) {
|
||||
constructForm('{% url "api-part-supplier-price-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Price Break" %}',
|
||||
title: '{% jstrans "Add Price Break" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -441,7 +441,7 @@ function editCompany(pk, options={}) {
|
||||
method: 'PATCH',
|
||||
fields: fields,
|
||||
reload: true,
|
||||
title: '{% trans "Edit Company" %}',
|
||||
title: '{% jstrans "Edit Company" %}',
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -462,7 +462,7 @@ function createCompany(options={}) {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
follow: true,
|
||||
title: '{% trans "Add new Company" %}',
|
||||
title: '{% jstrans "Add new Company" %}',
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -492,22 +492,22 @@ function loadCompanyTable(table, url, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Company" %}',
|
||||
title: '{% jstrans "Company" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var html = imageHoverIcon(row.image) + renderLink(value, row.url);
|
||||
|
||||
if (row.is_customer) {
|
||||
html += `<span title='{% trans "Customer" %}' class='fas fa-user-tie float-right'></span>`;
|
||||
html += `<span title='{% jstrans "Customer" %}' class='fas fa-user-tie float-right'></span>`;
|
||||
}
|
||||
|
||||
if (row.is_manufacturer) {
|
||||
html += `<span title='{% trans "Manufacturer" %}' class='fas fa-industry float-right'></span>`;
|
||||
html += `<span title='{% jstrans "Manufacturer" %}' class='fas fa-industry float-right'></span>`;
|
||||
}
|
||||
|
||||
if (row.is_supplier) {
|
||||
html += `<span title='{% trans "Supplier" %}' class='fas fa-building float-right'></span>`;
|
||||
html += `<span title='{% jstrans "Supplier" %}' class='fas fa-building float-right'></span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -515,11 +515,11 @@ function loadCompanyTable(table, url, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'website',
|
||||
title: '{% trans "Website" %}',
|
||||
title: '{% jstrans "Website" %}',
|
||||
formatter: function(value) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
@@ -533,7 +533,7 @@ function loadCompanyTable(table, url, options={}) {
|
||||
columns.push({
|
||||
sortable: true,
|
||||
field: 'parts_supplied',
|
||||
title: '{% trans "Parts Supplied" %}',
|
||||
title: '{% jstrans "Parts Supplied" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderLink(value, `/company/${row.pk}/?display=supplier-parts`);
|
||||
}
|
||||
@@ -542,7 +542,7 @@ function loadCompanyTable(table, url, options={}) {
|
||||
columns.push({
|
||||
sortable: true,
|
||||
field: 'parts_manufactured',
|
||||
title: '{% trans "Parts Manufactured" %}',
|
||||
title: '{% jstrans "Parts Manufactured" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderLink(value, `/company/${row.pk}/?display=manufacturer-parts`);
|
||||
}
|
||||
@@ -557,7 +557,7 @@ function loadCompanyTable(table, url, options={}) {
|
||||
groupBy: false,
|
||||
sidePagination: 'server',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No company information found" %}';
|
||||
return '{% jstrans "No company information found" %}';
|
||||
},
|
||||
showColumns: true,
|
||||
name: options.pagetype || 'company',
|
||||
@@ -606,7 +606,7 @@ function createContact(options={}) {
|
||||
constructForm('{% url "api-contact-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create New Contact" %}',
|
||||
title: '{% jstrans "Create New Contact" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -622,7 +622,7 @@ function editContact(pk, options={}) {
|
||||
|
||||
constructForm(`{% url "api-contact-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Contact" %}',
|
||||
title: '{% jstrans "Edit Contact" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -659,13 +659,13 @@ function deleteContacts(contacts, options={}) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
let html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected contacts will be deleted" %}
|
||||
{% jstrans "All selected contacts will be deleted" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Email" %}</th>
|
||||
<th>{% trans "Role" %}</th>
|
||||
<th>{% jstrans "Name" %}</th>
|
||||
<th>{% jstrans "Email" %}</th>
|
||||
<th>{% jstrans "Role" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>`;
|
||||
@@ -673,7 +673,7 @@ function deleteContacts(contacts, options={}) {
|
||||
constructForm('{% url "api-contact-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete Contacts" %}',
|
||||
title: '{% jstrans "Delete Contacts" %}',
|
||||
preFormContent: html,
|
||||
form_data: {
|
||||
items: ids,
|
||||
@@ -704,32 +704,32 @@ function loadContactTable(table, options={}) {
|
||||
uniqueId: 'pk',
|
||||
sidePagination: 'server',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No contacts found" %}';
|
||||
return '{% jstrans "No contacts found" %}';
|
||||
},
|
||||
showColumns: true,
|
||||
name: 'contacts',
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
title: '{% jstrans "Name" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
title: '{% trans "Phone Number" %}',
|
||||
title: '{% jstrans "Phone Number" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
title: '{% trans "Email Address" %}',
|
||||
title: '{% jstrans "Email Address" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'role',
|
||||
title: '{% trans "Role" %}',
|
||||
title: '{% jstrans "Role" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
@@ -745,11 +745,11 @@ function loadContactTable(table, options={}) {
|
||||
let html = '';
|
||||
|
||||
if (options.allow_edit) {
|
||||
html += makeEditButton('btn-contact-edit', pk, '{% trans "Edit Contact" %}');
|
||||
html += makeEditButton('btn-contact-edit', pk, '{% jstrans "Edit Contact" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
html += makeDeleteButton('btn-contact-delete', pk, '{% trans "Delete Contact" %}');
|
||||
html += makeDeleteButton('btn-contact-delete', pk, '{% jstrans "Delete Contact" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(html);
|
||||
@@ -846,7 +846,7 @@ function createAddress(options={}) {
|
||||
constructForm('{% url "api-address-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create New Address" %}',
|
||||
title: '{% jstrans "Create New Address" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -861,7 +861,7 @@ function editAddress(pk, options={}) {
|
||||
|
||||
constructForm(`{% url "api-address-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Address" %}',
|
||||
title: '{% jstrans "Edit Address" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -896,13 +896,13 @@ function deleteAddress(addresses, options={}) {
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected addresses will be deleted" %}
|
||||
{% jstrans "All selected addresses will be deleted" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Line 1" %}</th>
|
||||
<th>{% trans "Line 2" %}</th>
|
||||
<th>{% jstrans "Name" %}</th>
|
||||
<th>{% jstrans "Line 1" %}</th>
|
||||
<th>{% jstrans "Line 2" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>`;
|
||||
@@ -910,7 +910,7 @@ function deleteAddress(addresses, options={}) {
|
||||
constructForm('{% url "api-address-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete Addresses" %}',
|
||||
title: '{% jstrans "Delete Addresses" %}',
|
||||
preFormContent: html,
|
||||
form_data: {
|
||||
items: ids,
|
||||
@@ -937,14 +937,14 @@ function loadAddressTable(table, options={}) {
|
||||
sidePagination: 'server',
|
||||
sortable: true,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No addresses found" %}';
|
||||
return '{% jstrans "No addresses found" %}';
|
||||
},
|
||||
showColumns: true,
|
||||
name: 'addresses',
|
||||
columns: [
|
||||
{
|
||||
field: 'primary',
|
||||
title: '{% trans "Primary" %}',
|
||||
title: '{% jstrans "Primary" %}',
|
||||
switchable: false,
|
||||
formatter: function(value) {
|
||||
return yesNoLabel(value);
|
||||
@@ -952,61 +952,61 @@ function loadAddressTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: '{% trans "Title" %}',
|
||||
title: '{% jstrans "Title" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'line1',
|
||||
title: '{% trans "Line 1" %}',
|
||||
title: '{% jstrans "Line 1" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'line2',
|
||||
title: '{% trans "Line 2" %}',
|
||||
title: '{% jstrans "Line 2" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'postal_code',
|
||||
title: '{% trans "Postal code" %}',
|
||||
title: '{% jstrans "Postal code" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'postal_city',
|
||||
title: '{% trans "Postal city" %}',
|
||||
title: '{% jstrans "Postal city" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'province',
|
||||
title: '{% trans "State/province" %}',
|
||||
title: '{% jstrans "State/province" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'country',
|
||||
title: '{% trans "Country" %}',
|
||||
title: '{% jstrans "Country" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'shipping_notes',
|
||||
title: '{% trans "Courier notes" %}',
|
||||
title: '{% jstrans "Courier notes" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'internal_shipping_notes',
|
||||
title: '{% trans "Internal notes" %}',
|
||||
title: '{% jstrans "Internal notes" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "External Link" %}',
|
||||
title: '{% jstrans "External Link" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
@@ -1022,11 +1022,11 @@ function loadAddressTable(table, options={}) {
|
||||
let html = '';
|
||||
|
||||
if (options.allow_edit) {
|
||||
html += makeEditButton('btn-address-edit', pk, '{% trans "Edit Address" %}');
|
||||
html += makeEditButton('btn-address-edit', pk, '{% jstrans "Edit Address" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
html += makeDeleteButton('btn-address-delete', pk, '{% trans "Delete Address" %}');
|
||||
html += makeDeleteButton('btn-address-delete', pk, '{% jstrans "Delete Address" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(html);
|
||||
@@ -1099,13 +1099,13 @@ function deleteManufacturerParts(selections, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected manufacturer parts will be deleted" %}
|
||||
{% jstrans "All selected manufacturer parts will be deleted" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "MPN" %}</th>
|
||||
<th>{% trans "Manufacturer" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "MPN" %}</th>
|
||||
<th>{% jstrans "Manufacturer" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>
|
||||
@@ -1114,7 +1114,7 @@ function deleteManufacturerParts(selections, options={}) {
|
||||
constructForm('{% url "api-manufacturer-part-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete Manufacturer Parts" %}',
|
||||
title: '{% jstrans "Delete Manufacturer Parts" %}',
|
||||
preFormContent: html,
|
||||
form_data: {
|
||||
items: ids,
|
||||
@@ -1148,12 +1148,12 @@ function deleteManufacturerPartParameters(selections, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected parameters will be deleted" %}
|
||||
{% jstrans "All selected parameters will be deleted" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Value" %}</th>
|
||||
<th>{% jstrans "Name" %}</th>
|
||||
<th>{% jstrans "Value" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>
|
||||
@@ -1162,7 +1162,7 @@ function deleteManufacturerPartParameters(selections, options={}) {
|
||||
constructForm('{% url "api-manufacturer-part-parameter-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete Parameters" %}',
|
||||
title: '{% jstrans "Delete Parameters" %}',
|
||||
preFormContent: html,
|
||||
form_data: {
|
||||
items: ids,
|
||||
@@ -1178,7 +1178,7 @@ function makeManufacturerPartActions(options={}) {
|
||||
return [
|
||||
{
|
||||
label: 'order',
|
||||
title: '{% trans "Order parts" %}',
|
||||
title: '{% jstrans "Order parts" %}',
|
||||
icon: 'fa-shopping-cart',
|
||||
permission: 'purchase_order.add',
|
||||
callback: function(data) {
|
||||
@@ -1195,7 +1195,7 @@ function makeManufacturerPartActions(options={}) {
|
||||
},
|
||||
{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete manufacturer parts" %}',
|
||||
title: '{% jstrans "Delete manufacturer parts" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'purchase_order.delete',
|
||||
callback: function(data) {
|
||||
@@ -1227,7 +1227,7 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
custom_actions: [
|
||||
{
|
||||
label: 'manufacturer-part',
|
||||
title: '{% trans "Manufacturer part actions" %}',
|
||||
title: '{% jstrans "Manufacturer part actions" %}',
|
||||
icon: 'fa-tools',
|
||||
actions: makeManufacturerPartActions({
|
||||
manufacturer_id: options.params.manufacturer,
|
||||
@@ -1246,7 +1246,7 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
name: 'manufacturerparts',
|
||||
groupBy: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No manufacturer parts found" %}';
|
||||
return '{% jstrans "No manufacturer parts found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
@@ -1258,7 +1258,7 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
switchable: params['part_detail'],
|
||||
sortable: true,
|
||||
field: 'part_detail.full_name',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
var url = `/part/${row.part}/`;
|
||||
@@ -1266,15 +1266,15 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
|
||||
|
||||
if (row.part_detail.is_template) {
|
||||
html += makeIconBadge('fa-clone', '{% trans "Template part" %}');
|
||||
html += makeIconBadge('fa-clone', '{% jstrans "Template part" %}');
|
||||
}
|
||||
|
||||
if (row.part_detail.assembly) {
|
||||
html += makeIconBadge('fa-tools', '{% trans "Assembled part" %}');
|
||||
html += makeIconBadge('fa-tools', '{% jstrans "Assembled part" %}');
|
||||
}
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`;
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% jstrans "Inactive" %}</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -1283,7 +1283,7 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'manufacturer',
|
||||
title: '{% trans "Manufacturer" %}',
|
||||
title: '{% jstrans "Manufacturer" %}',
|
||||
formatter: function(value, row) {
|
||||
if (value && row.manufacturer_detail) {
|
||||
var name = row.manufacturer_detail.name;
|
||||
@@ -1299,14 +1299,14 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'MPN',
|
||||
title: '{% trans "MPN" %}',
|
||||
title: '{% jstrans "MPN" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderClipboard(renderLink(value, `/manufacturer-part/${row.pk}/`));
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value) {
|
||||
if (value) {
|
||||
return renderLink(value, value, {external: true});
|
||||
@@ -1317,7 +1317,7 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
@@ -1330,8 +1330,8 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
html += makeEditButton('button-manufacturer-part-edit', pk, '{% trans "Edit manufacturer part" %}');
|
||||
html += makeDeleteButton('button-manufacturer-part-delete', pk, '{% trans "Delete manufacturer part" %}');
|
||||
html += makeEditButton('button-manufacturer-part-edit', pk, '{% jstrans "Edit manufacturer part" %}');
|
||||
html += makeDeleteButton('button-manufacturer-part-delete', pk, '{% jstrans "Delete manufacturer part" %}');
|
||||
|
||||
return wrapButtons(html);
|
||||
}
|
||||
@@ -1390,7 +1390,7 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
name: 'manufacturerpartparameters',
|
||||
groupBy: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No parameters found" %}';
|
||||
return '{% jstrans "No parameters found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
@@ -1400,19 +1400,19 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
title: '{% jstrans "Name" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
title: '{% trans "Value" %}',
|
||||
title: '{% jstrans "Value" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'units',
|
||||
title: '{% trans "Units" %}',
|
||||
title: '{% jstrans "Units" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
},
|
||||
@@ -1425,8 +1425,8 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
html += makeEditButton('button-parameter-edit', pk, '{% trans "Edit parameter" %}');
|
||||
html += makeDeleteButton('button-parameter-delete', pk, '{% trans "Delete parameter" %}');
|
||||
html += makeEditButton('button-parameter-edit', pk, '{% jstrans "Edit parameter" %}');
|
||||
html += makeDeleteButton('button-parameter-delete', pk, '{% jstrans "Delete parameter" %}');
|
||||
|
||||
return wrapButtons(html);
|
||||
}
|
||||
@@ -1443,7 +1443,7 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
value: {},
|
||||
units: {},
|
||||
},
|
||||
title: '{% trans "Edit Parameter" %}',
|
||||
title: '{% jstrans "Edit Parameter" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -1452,7 +1452,7 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
|
||||
constructForm(`{% url "api-manufacturer-part-parameter-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Parameter" %}',
|
||||
title: '{% jstrans "Delete Parameter" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -1466,7 +1466,7 @@ function makeSupplierPartActions(options={}) {
|
||||
return [
|
||||
{
|
||||
label: 'order',
|
||||
title: '{% trans "Order parts" %}',
|
||||
title: '{% jstrans "Order parts" %}',
|
||||
icon: 'fa-shopping-cart',
|
||||
permission: 'purchase_order.add',
|
||||
callback: function(data) {
|
||||
@@ -1483,7 +1483,7 @@ function makeSupplierPartActions(options={}) {
|
||||
},
|
||||
{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete supplier parts" %}',
|
||||
title: '{% jstrans "Delete supplier parts" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'purchase_order.delete',
|
||||
callback: function(data) {
|
||||
@@ -1513,7 +1513,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
custom_actions: [
|
||||
{
|
||||
label: 'supplier-part',
|
||||
title: '{% trans "Supplier part actions" %}',
|
||||
title: '{% jstrans "Supplier part actions" %}',
|
||||
icon: 'fa-tools',
|
||||
actions: makeSupplierPartActions({
|
||||
supplier_id: options.params.supplier,
|
||||
@@ -1533,7 +1533,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
groupBy: false,
|
||||
sortable: true,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No supplier parts found" %}';
|
||||
return '{% jstrans "No supplier parts found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
@@ -1546,7 +1546,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
sortable: true,
|
||||
field: 'part_detail.full_name',
|
||||
sortName: 'part',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
var url = `/part/${row.part}/`;
|
||||
@@ -1554,15 +1554,15 @@ function loadSupplierPartTable(table, url, options) {
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
|
||||
|
||||
if (row.part_detail.is_template) {
|
||||
html += makeIconBadge('fa-clone', '{% trans "Template part" %}');
|
||||
html += makeIconBadge('fa-clone', '{% jstrans "Template part" %}');
|
||||
}
|
||||
|
||||
if (row.part_detail.assembly) {
|
||||
html += makeIconBadge('fa-tools', '{% trans "Assembled part" %}');
|
||||
html += makeIconBadge('fa-tools', '{% jstrans "Assembled part" %}');
|
||||
}
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`;
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% jstrans "Inactive" %}</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -1571,7 +1571,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'supplier',
|
||||
title: '{% trans "Supplier" %}',
|
||||
title: '{% jstrans "Supplier" %}',
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
var name = row.supplier_detail.name;
|
||||
@@ -1587,7 +1587,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'SKU',
|
||||
title: '{% trans "Supplier Part" %}',
|
||||
title: '{% jstrans "Supplier Part" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderClipboard(renderLink(value, `/supplier-part/${row.pk}/`));
|
||||
}
|
||||
@@ -1598,7 +1598,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
sortable: true,
|
||||
sortName: 'manufacturer',
|
||||
field: 'manufacturer_detail.name',
|
||||
title: '{% trans "Manufacturer" %}',
|
||||
title: '{% jstrans "Manufacturer" %}',
|
||||
formatter: function(value, row) {
|
||||
if (value && row.manufacturer_detail) {
|
||||
var name = value;
|
||||
@@ -1617,7 +1617,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
sortable: true,
|
||||
sortName: 'MPN',
|
||||
field: 'manufacturer_part_detail.MPN',
|
||||
title: '{% trans "MPN" %}',
|
||||
title: '{% jstrans "MPN" %}',
|
||||
formatter: function(value, row) {
|
||||
if (value && row.manufacturer_part) {
|
||||
return renderClipboard(renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`));
|
||||
@@ -1628,17 +1628,17 @@ function loadSupplierPartTable(table, url, options) {
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: 'packaging',
|
||||
title: '{% trans "Packaging" %}',
|
||||
title: '{% jstrans "Packaging" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'pack_quantity',
|
||||
title: '{% trans "Pack Quantity" %}',
|
||||
title: '{% jstrans "Pack Quantity" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
|
||||
@@ -1651,7 +1651,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
}
|
||||
|
||||
if (row.part_detail && row.part_detail.units) {
|
||||
html += `<span class='fas fa-info-circle float-right' title='{% trans "Base Units" %}: ${row.part_detail.units}'></span>`;
|
||||
html += `<span class='fas fa-info-circle float-right' title='{% jstrans "Base Units" %}: ${row.part_detail.units}'></span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -1660,7 +1660,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
{
|
||||
field: 'link',
|
||||
sortable: false,
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value) {
|
||||
if (value) {
|
||||
return renderLink(value, value, {external: true});
|
||||
@@ -1671,17 +1671,17 @@ function loadSupplierPartTable(table, url, options) {
|
||||
},
|
||||
{
|
||||
field: 'note',
|
||||
title: '{% trans "Notes" %}',
|
||||
title: '{% jstrans "Notes" %}',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: 'in_stock',
|
||||
title: '{% trans "In Stock" %}',
|
||||
title: '{% jstrans "In Stock" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'available',
|
||||
title: '{% trans "Availability" %}',
|
||||
title: '{% jstrans "Availability" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (row.availability_updated) {
|
||||
@@ -1690,7 +1690,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
|
||||
html += makeIconBadge(
|
||||
'fa-info-circle',
|
||||
`{% trans "Last Updated" %}: ${date}`
|
||||
`{% jstrans "Last Updated" %}: ${date}`
|
||||
);
|
||||
return html;
|
||||
} else {
|
||||
@@ -1700,7 +1700,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
},
|
||||
{
|
||||
field: 'updated',
|
||||
title: '{% trans "Last Updated" %}',
|
||||
title: '{% jstrans "Last Updated" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
@@ -1712,8 +1712,8 @@ function loadSupplierPartTable(table, url, options) {
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
html += makeEditButton('button-supplier-part-edit', pk, '{% trans "Edit supplier part" %}');
|
||||
html += makeDeleteButton('button-supplier-part-delete', pk, '{% trans "Delete supplier part" %}');
|
||||
html += makeEditButton('button-supplier-part-edit', pk, '{% jstrans "Edit supplier part" %}');
|
||||
html += makeDeleteButton('button-supplier-part-delete', pk, '{% jstrans "Delete supplier part" %}');
|
||||
|
||||
return wrapButtons(html);
|
||||
}
|
||||
@@ -1766,7 +1766,7 @@ function loadSupplierPriceBreakTable(options={}) {
|
||||
|
||||
constructForm(`{% url "api-part-supplier-price-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Price Break" %}',
|
||||
title: '{% jstrans "Delete Price Break" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -1776,7 +1776,7 @@ function loadSupplierPriceBreakTable(options={}) {
|
||||
|
||||
constructForm(`{% url "api-part-supplier-price-list" %}${pk}/`, {
|
||||
fields: supplierPartPriceBreakFields(),
|
||||
title: '{% trans "Edit Price Break" %}',
|
||||
title: '{% jstrans "Edit Price Break" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -1791,7 +1791,7 @@ function loadSupplierPriceBreakTable(options={}) {
|
||||
part: options.part,
|
||||
},
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No price break information found" %}';
|
||||
return '{% jstrans "No price break information found" %}';
|
||||
},
|
||||
onPostBody: function() {
|
||||
setupCallbacks();
|
||||
@@ -1805,12 +1805,12 @@ function loadSupplierPriceBreakTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '{% trans "Price" %}',
|
||||
title: '{% jstrans "Price" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index) {
|
||||
return formatCurrency(value, {
|
||||
@@ -1820,15 +1820,15 @@ function loadSupplierPriceBreakTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'updated',
|
||||
title: '{% trans "Last updated" %}',
|
||||
title: '{% jstrans "Last updated" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
var html = renderDate(value);
|
||||
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeEditButton('button-price-break-edit', row.pk, '{% trans "Edit price break" %}');
|
||||
buttons += makeDeleteButton('button-price-break-delete', row.pk, '{% trans "Delete price break" %}');
|
||||
buttons += makeEditButton('button-price-break-edit', row.pk, '{% jstrans "Edit price break" %}');
|
||||
buttons += makeDeleteButton('button-price-break-delete', row.pk, '{% jstrans "Delete price break" %}');
|
||||
|
||||
html += wrapButtons(buttons);
|
||||
|
||||
|
||||
@@ -183,11 +183,11 @@ function getFilterOptionList(tableKey, filterKey) {
|
||||
return {
|
||||
'1': {
|
||||
key: '1',
|
||||
value: '{% trans "true" %}',
|
||||
value: '{% jstrans "true" %}',
|
||||
},
|
||||
'0': {
|
||||
key: '0',
|
||||
value: '{% trans "false" %}',
|
||||
value: '{% jstrans "false" %}',
|
||||
},
|
||||
};
|
||||
} else if (settings.type == 'date') {
|
||||
@@ -211,7 +211,7 @@ function generateAvailableFilterList(tableKey) {
|
||||
|
||||
var html = `<select class='form-control filter-input' id='${id}' name='tag'>`;
|
||||
|
||||
html += `<option value=''>{% trans 'Select filter' %}</option>`;
|
||||
html += `<option value=''>{% jstrans 'Select filter' %}</option>`;
|
||||
|
||||
for (var opt in remaining) {
|
||||
var title = getFilterTitle(tableKey, opt);
|
||||
@@ -293,7 +293,7 @@ function makeCustomActionGroup(action_group, table) {
|
||||
|
||||
let buttons = [];
|
||||
let label = action_group.label || 'actions';
|
||||
let title = action_group.title || '{% trans "Actions" %}';
|
||||
let title = action_group.title || '{% jstrans "Actions" %}';
|
||||
let icon = action_group.icon || 'fa-tools';
|
||||
|
||||
// Construct the HTML for each button
|
||||
@@ -332,7 +332,7 @@ function makeBarcodeActions(barcode_actions, table) {
|
||||
|
||||
let html = `
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='barcode-actions' title='{% trans "Barcode actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<button id='barcode-actions' title='{% jstrans "Barcode actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-qrcode'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
@@ -428,17 +428,17 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
if (report_button || labels_button) {
|
||||
let print_buttons = `
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='printing-options' title='{% trans "Printing actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<button id='printing-options' title='{% jstrans "Printing actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-print'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu' role='menu'>`;
|
||||
|
||||
if (labels_button) {
|
||||
print_buttons += `<li><a class='dropdown-item' href='#' id='print-labels-${tableKey}'><span class='fas fa-tag'></span> {% trans "Print Labels" %}</a></li>`;
|
||||
print_buttons += `<li><a class='dropdown-item' href='#' id='print-labels-${tableKey}'><span class='fas fa-tag'></span> {% jstrans "Print Labels" %}</a></li>`;
|
||||
}
|
||||
|
||||
if (report_button) {
|
||||
print_buttons += `<li><a class='dropdown-item' href='#' id='print-report-${tableKey}'><span class='fas fa-file-pdf'></span> {% trans "Print Reports" %}</a></li>`;
|
||||
print_buttons += `<li><a class='dropdown-item' href='#' id='print-report-${tableKey}'><span class='fas fa-file-pdf'></span> {% jstrans "Print Reports" %}</a></li>`;
|
||||
}
|
||||
|
||||
print_buttons += `</ul></div>`;
|
||||
@@ -450,14 +450,14 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
if (options.download) {
|
||||
buttons += makeFilterButton({
|
||||
id: `download-${tableKey}`,
|
||||
title: '{% trans "Download table data" %}',
|
||||
title: '{% jstrans "Download table data" %}',
|
||||
icon: 'fa-download',
|
||||
});
|
||||
}
|
||||
|
||||
buttons += makeFilterButton({
|
||||
id: `reload-${tableKey}`,
|
||||
title: '{% trans "Reload table data" %}',
|
||||
title: '{% jstrans "Reload table data" %}',
|
||||
icon: 'fa-redo-alt',
|
||||
});
|
||||
|
||||
@@ -466,7 +466,7 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
|
||||
buttons += makeFilterButton({
|
||||
id: add,
|
||||
title: '{% trans "Add new filter" %}',
|
||||
title: '{% jstrans "Add new filter" %}',
|
||||
icon: 'fa-filter',
|
||||
});
|
||||
|
||||
@@ -474,7 +474,7 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
if (Object.keys(filters).length > 0) {
|
||||
buttons += makeFilterButton({
|
||||
id: clear,
|
||||
title: '{% trans "Clear all filters" %}',
|
||||
title: '{% jstrans "Clear all filters" %}',
|
||||
icon: 'fa-backspace icon-red',
|
||||
});
|
||||
}
|
||||
@@ -579,7 +579,7 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
html += generateAvailableFilterList(tableKey);
|
||||
html += generateFilterInput(tableKey);
|
||||
|
||||
html += `<button title='{% trans "Create filter" %}' class='btn btn-outline-secondary filter-button' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
html += `<button title='{% jstrans "Create filter" %}' class='btn btn-outline-secondary filter-button' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
html += `</div>`;
|
||||
|
||||
element.append(html);
|
||||
@@ -669,8 +669,8 @@ function getFilterOptionValue(tableKey, filterKey, valueKey) {
|
||||
|
||||
// Lookup for boolean options
|
||||
if (filter.type == 'bool') {
|
||||
if (value == '1') return '{% trans "true" %}';
|
||||
if (value == '0') return '{% trans "false" %}';
|
||||
if (value == '1') return '{% jstrans "true" %}';
|
||||
if (value == '0') return '{% jstrans "false" %}';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -371,9 +371,9 @@ function constructForm(url, options={}) {
|
||||
constructCreateForm(OPTIONS.actions.POST, options);
|
||||
} else {
|
||||
// User does not have permission to POST to the endpoint
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
showMessage('{% jstrans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "Create operation not allowed" %}',
|
||||
details: '{% jstrans "Create operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
@@ -386,9 +386,9 @@ function constructForm(url, options={}) {
|
||||
constructChangeForm(OPTIONS.actions.PUT, options);
|
||||
} else {
|
||||
// User does not have permission to PUT/PATCH to the endpoint
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
showMessage('{% jstrans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "Update operation not allowed" %}',
|
||||
details: '{% jstrans "Update operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
@@ -400,9 +400,9 @@ function constructForm(url, options={}) {
|
||||
constructDeleteForm(OPTIONS.actions.DELETE, options);
|
||||
} else {
|
||||
// User does not have permission to DELETE to the endpoint
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
showMessage('{% jstrans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "Delete operation not allowed" %}',
|
||||
details: '{% jstrans "Delete operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
@@ -414,9 +414,9 @@ function constructForm(url, options={}) {
|
||||
// TODO?
|
||||
} else {
|
||||
// User does not have permission to GET to the endpoint
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
showMessage('{% jstrans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "View operation not allowed" %}',
|
||||
details: '{% jstrans "View operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
@@ -540,7 +540,7 @@ function constructFormBody(fields, options) {
|
||||
}
|
||||
|
||||
if (!('submitText' in options)) {
|
||||
options.submitText = '{% trans "Delete" %}';
|
||||
options.submitText = '{% jstrans "Delete" %}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,7 +767,7 @@ function updateForm(options) {
|
||||
// The "submit" button will be disabled unless "confirm" is checked
|
||||
function insertConfirmButton(options) {
|
||||
|
||||
var message = options.confirmMessage || '{% trans "Confirm" %}';
|
||||
var message = options.confirmMessage || '{% jstrans "Confirm" %}';
|
||||
|
||||
var html = `
|
||||
<div class="form-check form-switch">
|
||||
@@ -793,7 +793,7 @@ function insertConfirmButton(options) {
|
||||
/* Add a checkbox to select if the modal will stay open after success */
|
||||
function insertPersistButton(options) {
|
||||
|
||||
var message = options.persistMessage || '{% trans "Keep this form open" %}';
|
||||
var message = options.persistMessage || '{% jstrans "Keep this form open" %}';
|
||||
|
||||
var html = `
|
||||
<div class="form-check form-switch">
|
||||
@@ -896,7 +896,7 @@ function submitFormData(fields, options) {
|
||||
if (!validateFormField(name, options)) {
|
||||
data_valid = false;
|
||||
|
||||
data_errors[name] = ['{% trans "Enter a valid number" %}'];
|
||||
data_errors[name] = ['{% jstrans "Enter a valid number" %}'];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -1466,7 +1466,7 @@ function handleFormErrors(errors, fields={}, options={}) {
|
||||
// TODO: Display the JSON error text when hovering over the "info" icon
|
||||
non_field_errors.append(
|
||||
`<div class='alert alert-block alert-danger'>
|
||||
<b>{% trans "Form errors exist" %}</b>
|
||||
<b>{% jstrans "Form errors exist" %}</b>
|
||||
<span id='form-errors-info' class='float-right fas fa-info-circle icon-red'>
|
||||
</span>
|
||||
</div>`
|
||||
@@ -1964,7 +1964,7 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
if (field.noResults) {
|
||||
return field.noResults(query);
|
||||
} else {
|
||||
return '{% trans "No results found" %}';
|
||||
return '{% jstrans "No results found" %}';
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2148,11 +2148,11 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
button.on("click", () => {
|
||||
const tree_id = `${name}_tree`;
|
||||
|
||||
const title = '{% trans "Select" %}' + " " + options.actions[name].label;
|
||||
const title = '{% jstrans "Select" %}' + " " + options.actions[name].label;
|
||||
const content = `
|
||||
<div class="mb-1">
|
||||
<div class="input-group mb-2">
|
||||
<input class="form-control" type="text" id="${name}_tree_search" placeholder="{% trans "Search" %} ${options.actions[name].label}..." />
|
||||
<input class="form-control" type="text" id="${name}_tree_search" placeholder="{% jstrans "Search" %} ${options.actions[name].label}..." />
|
||||
<button class="input-group-text" id="${name}_tree_search_btn"><i class="fas fa-search"></i></button>
|
||||
</div>
|
||||
|
||||
@@ -2164,7 +2164,7 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
</div>
|
||||
`;
|
||||
showQuestionDialog(title, content, {
|
||||
accept_text: '{% trans "Select" %}',
|
||||
accept_text: '{% jstrans "Select" %}',
|
||||
accept: () => {
|
||||
const selectedNode = $(`#${tree_id}`).treeview('getSelected');
|
||||
if(selectedNode.length > 0) {
|
||||
@@ -2268,7 +2268,7 @@ function initializeChoiceField(field, fields, options) {
|
||||
|
||||
// Render a 'no results' element
|
||||
function searching() {
|
||||
return `<span>{% trans "Searching" %}...</span>`;
|
||||
return `<span>{% jstrans "Searching" %}...</span>`;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2482,7 +2482,7 @@ function constructField(name, parameters, options={}) {
|
||||
|
||||
if (!parameters.required && !options.hideClearButton) {
|
||||
html += `
|
||||
<button class='input-group-text form-clear' id='clear_${field_name}' title='{% trans "Clear input" %}'>
|
||||
<button class='input-group-text form-clear' id='clear_${field_name}' title='{% jstrans "Clear input" %}'>
|
||||
<span class='icon-red fas fa-backspace'></span>
|
||||
</button>`;
|
||||
}
|
||||
@@ -3068,7 +3068,7 @@ function selectImportFields(url, data={}, options={}) {
|
||||
rows += `<tr><td><em>${field_name}</em></td><td>${choice_input}</td></tr>`;
|
||||
}
|
||||
|
||||
var headers = `<tr><th>{% trans "File Column" %}</th><th>{% trans "Field Name" %}</th></tr>`;
|
||||
var headers = `<tr><th>{% jstrans "File Column" %}</th><th>{% jstrans "Field Name" %}</th></tr>`;
|
||||
|
||||
var html = '';
|
||||
|
||||
@@ -3080,7 +3080,7 @@ function selectImportFields(url, data={}, options={}) {
|
||||
|
||||
constructForm(url, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Select Columns" %}',
|
||||
title: '{% jstrans "Select Columns" %}',
|
||||
fields: {},
|
||||
preFormContent: html,
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
@@ -74,10 +74,10 @@ function yesNoLabel(value, options={}) {
|
||||
let color = '';
|
||||
|
||||
if (toBool(value)) {
|
||||
text = options.pass || '{% trans "YES" %}';
|
||||
text = options.pass || '{% jstrans "YES" %}';
|
||||
color = 'bg-success';
|
||||
} else {
|
||||
text = options.fail || '{% trans "NO" %}';
|
||||
text = options.fail || '{% jstrans "NO" %}';
|
||||
color = 'bg-warning';
|
||||
}
|
||||
|
||||
@@ -90,19 +90,19 @@ function yesNoLabel(value, options={}) {
|
||||
|
||||
|
||||
function trueFalseLabel(value, options={}) {
|
||||
options.pass = '{% trans "True" %}';
|
||||
options.fail = '{% trans "False" %}';
|
||||
options.pass = '{% jstrans "True" %}';
|
||||
options.fail = '{% jstrans "False" %}';
|
||||
|
||||
return yesNoLabel(value, options);
|
||||
}
|
||||
|
||||
|
||||
function editButton(url, text='{% trans "Edit" %}') {
|
||||
function editButton(url, text='{% jstrans "Edit" %}') {
|
||||
return `<button class='btn btn-success edit-button btn-sm' type='button' url='${url}'>${text}</button>`;
|
||||
}
|
||||
|
||||
|
||||
function deleteButton(url, text='{% trans "Delete" %}') {
|
||||
function deleteButton(url, text='{% jstrans "Delete" %}') {
|
||||
return `<button class='btn btn-danger delete-button btn-sm' type='button' url='${url}'>${text}</button>`;
|
||||
}
|
||||
|
||||
@@ -582,7 +582,7 @@ function renderClipboard(s, prepend=false) {
|
||||
return s;
|
||||
}
|
||||
|
||||
let clipString = `<span class="d-none d-xl-inline"><button class="btn clip-btn" type="button" data-bs-toggle='tooltip' title='{% trans "copy to clipboard" %}'><em class="fas fa-copy"></em></button></span>`;
|
||||
let clipString = `<span class="d-none d-xl-inline"><button class="btn clip-btn" type="button" data-bs-toggle='tooltip' title='{% jstrans "copy to clipboard" %}'><em class="fas fa-copy"></em></button></span>`;
|
||||
|
||||
if (prepend === true) {
|
||||
return `<div class="flex-cell">${clipString+s}</div>`;
|
||||
|
||||
@@ -101,12 +101,12 @@ function loadRequiredForBuildsPartsTable(table, options={}) {
|
||||
search: false,
|
||||
sortable: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No parts required for builds" %}';
|
||||
return '{% jstrans "No parts required for builds" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
formatter: function(value, row) {
|
||||
let name = shortenString(row.full_name);
|
||||
let display= imageHoverIcon(row.thumbnail) + renderLink(name, `/part/${row.pk}/`);
|
||||
@@ -116,18 +116,18 @@ function loadRequiredForBuildsPartsTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'total_in_stock',
|
||||
title: '{% trans "Available" %}',
|
||||
title: '{% jstrans "Available" %}',
|
||||
formatter: function(value, row) {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'allocated_to_build_orders',
|
||||
title: '{% trans "Allocated Stock" %}',
|
||||
title: '{% jstrans "Allocated Stock" %}',
|
||||
formatter: function(_value, row) {
|
||||
return makeProgressBar(
|
||||
row.allocated_to_build_orders,
|
||||
|
||||
@@ -50,8 +50,8 @@ function printLabels(options) {
|
||||
|
||||
if (!options.items || options.items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Items" %}',
|
||||
'{% trans "No items selected for printing" %}',
|
||||
'{% jstrans "Select Items" %}',
|
||||
'{% jstrans "No items selected for printing" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -69,8 +69,8 @@ function printLabels(options) {
|
||||
success: function (response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No label templates found which match the selected items" %}',
|
||||
'{% jstrans "No Labels Found" %}',
|
||||
'{% jstrans "No label templates found which match the selected items" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ function printLabels(options) {
|
||||
if (options.items.length > 1) {
|
||||
header_html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
${options.items.length} ${options.plural_name} {% trans "selected" %}
|
||||
${options.items.length} ${options.plural_name} {% jstrans "selected" %}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ function printLabels(options) {
|
||||
if (Object.keys(printingOptions).length > 0) {
|
||||
formOptions.fields = {
|
||||
...formOptions.fields,
|
||||
divider: { type: "candy", html: `<hr/><h5>{% trans "Printing Options" %}</h5>` },
|
||||
divider: { type: "candy", html: `<hr/><h5>{% jstrans "Printing Options" %}</h5>` },
|
||||
...printingOptions,
|
||||
};
|
||||
}
|
||||
@@ -145,14 +145,14 @@ function printLabels(options) {
|
||||
}
|
||||
|
||||
const printingFormOptions = {
|
||||
title: options.items.length === 1 ? `{% trans "Print label" %}` : `{% trans "Print labels" %}`,
|
||||
submitText: `{% trans "Print" %}`,
|
||||
title: options.items.length === 1 ? `{% jstrans "Print label" %}` : `{% jstrans "Print labels" %}`,
|
||||
submitText: `{% jstrans "Print" %}`,
|
||||
method: "POST",
|
||||
disableSuccessMessage: true,
|
||||
header_html,
|
||||
fields: {
|
||||
_label_template: {
|
||||
label: `{% trans "Select label template" %}`,
|
||||
label: `{% jstrans "Select label template" %}`,
|
||||
type: "choice",
|
||||
localOnly: true,
|
||||
value: defaultLabelTemplates[options.key],
|
||||
@@ -165,7 +165,7 @@ function printLabels(options) {
|
||||
}
|
||||
},
|
||||
_plugin: {
|
||||
label: `{% trans "Select plugin" %}`,
|
||||
label: `{% jstrans "Select plugin" %}`,
|
||||
type: "choice",
|
||||
localOnly: true,
|
||||
value: user_settings.LABEL_DEFAULT_PRINTER || plugins[0].key,
|
||||
@@ -184,7 +184,7 @@ function printLabels(options) {
|
||||
// Download the generated file
|
||||
window.open(response.file);
|
||||
} else {
|
||||
showMessage('{% trans "Labels sent to printer" %}', {
|
||||
showMessage('{% jstrans "Labels sent to printer" %}', {
|
||||
style: 'success',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,12 +55,12 @@ function createNewModal(options={}) {
|
||||
|
||||
// Add in a "close" button
|
||||
if (!options.hideCloseButton) {
|
||||
buttons += `<button type='button' class='btn btn-secondary' id='modal-form-close' data-bs-dismiss='modal'>{% trans "Cancel" %}</button>`;
|
||||
buttons += `<button type='button' class='btn btn-secondary' id='modal-form-close' data-bs-dismiss='modal'>{% jstrans "Cancel" %}</button>`;
|
||||
}
|
||||
|
||||
// Add in a "submit" button
|
||||
if (!options.hideSubmitButton) {
|
||||
buttons += `<button type='button' class='btn btn-${submitClass}' id='modal-form-submit'>{% trans "Submit" %}</button>`;
|
||||
buttons += `<button type='button' class='btn btn-${submitClass}' id='modal-form-submit'>{% jstrans "Submit" %}</button>`;
|
||||
}
|
||||
|
||||
var html = `
|
||||
@@ -71,7 +71,7 @@ function createNewModal(options={}) {
|
||||
<h4 id='modal-title' class='modal-title'>
|
||||
<!-- Form title to be injected here -->
|
||||
</h4>
|
||||
<button type='button' class='btn-close' data-bs-dismiss='modal' aria-label='{% trans "Close" %}'></button>
|
||||
<button type='button' class='btn-close' data-bs-dismiss='modal' aria-label='{% jstrans "Close" %}'></button>
|
||||
</div>
|
||||
<div class='modal-body modal-form-content-wrapper'>
|
||||
<div id='non-field-errors'>
|
||||
@@ -153,9 +153,9 @@ function createNewModal(options={}) {
|
||||
});
|
||||
|
||||
// Set labels based on supplied options
|
||||
modalSetTitle(modal_name, options.title || '{% trans "Form Title" %}');
|
||||
modalSetSubmitText(modal_name, options.submitText || '{% trans "Submit" %}');
|
||||
modalSetCloseText(modal_name, options.closeText || '{% trans "Cancel" %}');
|
||||
modalSetTitle(modal_name, options.title || '{% jstrans "Form Title" %}');
|
||||
modalSetSubmitText(modal_name, options.submitText || '{% jstrans "Submit" %}');
|
||||
modalSetCloseText(modal_name, options.closeText || '{% jstrans "Cancel" %}');
|
||||
|
||||
// Return the "name" of the modal
|
||||
return modal_name;
|
||||
@@ -442,7 +442,7 @@ function attachBootstrapCheckbox(modal) {
|
||||
function loadingMessageContent() {
|
||||
|
||||
// TODO - This can be made a lot better
|
||||
return `<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> {% trans 'Waiting for server...' %}`;
|
||||
return `<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> {% jstrans 'Waiting for server...' %}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -593,7 +593,7 @@ function renderErrorMessage(xhr) {
|
||||
<div class='panel'>
|
||||
<div class='panel panel-heading'>
|
||||
<div class='panel-title'>
|
||||
<a data-bs-toggle='collapse' href="#collapse-error-info">{% trans "Show Error Information" %}</a>
|
||||
<a data-bs-toggle='collapse' href="#collapse-error-info">{% jstrans "Show Error Information" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='panel-collapse collapse' id='collapse-error-info'>
|
||||
@@ -625,7 +625,7 @@ function showAlertDialog(title, content, options={}) {
|
||||
|
||||
var modal = createNewModal({
|
||||
title: title,
|
||||
closeText: '{% trans "Close" %}',
|
||||
closeText: '{% jstrans "Close" %}',
|
||||
hideSubmitButton: true,
|
||||
});
|
||||
|
||||
@@ -679,8 +679,8 @@ function showQuestionDialog(title, content, options={}) {
|
||||
*/
|
||||
|
||||
options.title = title;
|
||||
options.submitText = options.accept_text || '{% trans "Accept" %}';
|
||||
options.closeText = options.cancel_text || '{% trans "Cancel" %}';
|
||||
options.submitText = options.accept_text || '{% jstrans "Accept" %}';
|
||||
options.closeText = options.cancel_text || '{% jstrans "Cancel" %}';
|
||||
|
||||
var modal = createNewModal(options);
|
||||
|
||||
@@ -737,7 +737,7 @@ function openModal(options) {
|
||||
if (options.title) {
|
||||
modalSetTitle(modal, options.title);
|
||||
} else {
|
||||
modalSetTitle(modal, '{% trans "Loading Data" %}...');
|
||||
modalSetTitle(modal, '{% jstrans "Loading Data" %}...');
|
||||
}
|
||||
|
||||
// Unless the content is explicitly set, display loading message
|
||||
@@ -748,8 +748,8 @@ function openModal(options) {
|
||||
}
|
||||
|
||||
// Default labels for 'Submit' and 'Close' buttons in the form
|
||||
var submit_text = options.submit_text || '{% trans "Submit" %}';
|
||||
var close_text = options.close_text || '{% trans "Close" %}';
|
||||
var submit_text = options.submit_text || '{% jstrans "Submit" %}';
|
||||
var close_text = options.close_text || '{% jstrans "Close" %}';
|
||||
|
||||
modalSetButtonText(modal, submit_text, close_text);
|
||||
|
||||
@@ -1008,7 +1008,7 @@ function handleModalForm(url, options) {
|
||||
}
|
||||
} else {
|
||||
$(modal).modal('hide');
|
||||
showAlertDialog('{% trans "Invalid response from server" %}', '{% trans "Form data missing from server response" %}');
|
||||
showAlertDialog('{% jstrans "Invalid response from server" %}', '{% jstrans "Form data missing from server response" %}');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1020,7 +1020,7 @@ function handleModalForm(url, options) {
|
||||
// There was an error submitting form data via POST
|
||||
|
||||
$(modal).modal('hide');
|
||||
showAlertDialog('{% trans "Error posting form data" %}', renderErrorMessage(xhr));
|
||||
showAlertDialog('{% jstrans "Error posting form data" %}', renderErrorMessage(xhr));
|
||||
},
|
||||
complete: function() {
|
||||
// TODO
|
||||
@@ -1056,8 +1056,8 @@ function launchModalForm(url, options = {}) {
|
||||
var modal = options.modal || '#modal-form';
|
||||
|
||||
// Default labels for 'Submit' and 'Close' buttons in the form
|
||||
var submit_text = options.submit_text || '{% trans "Submit" %}';
|
||||
var close_text = options.close_text || '{% trans "Close" %}';
|
||||
var submit_text = options.submit_text || '{% jstrans "Submit" %}';
|
||||
var close_text = options.close_text || '{% jstrans "Close" %}';
|
||||
|
||||
// Clean custom action buttons
|
||||
$(modal).find('#modal-footer-buttons').html('');
|
||||
@@ -1117,7 +1117,7 @@ function launchModalForm(url, options = {}) {
|
||||
|
||||
} else {
|
||||
$(modal).modal('hide');
|
||||
showAlertDialog('{% trans "Invalid server response" %}', '{% trans "JSON response missing form data" %}');
|
||||
showAlertDialog('{% jstrans "Invalid server response" %}', '{% jstrans "JSON response missing form data" %}');
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
@@ -1127,36 +1127,36 @@ function launchModalForm(url, options = {}) {
|
||||
if (xhr.status == 0) {
|
||||
// No response from the server
|
||||
showAlertDialog(
|
||||
'{% trans "No Response" %}',
|
||||
'{% trans "No response from the InvenTree server" %}',
|
||||
'{% jstrans "No Response" %}',
|
||||
'{% jstrans "No response from the InvenTree server" %}',
|
||||
);
|
||||
} else if (xhr.status == 400) {
|
||||
showAlertDialog(
|
||||
'{% trans "Error 400: Bad Request" %}',
|
||||
'{% trans "Server returned error code 400" %}',
|
||||
'{% jstrans "Error 400: Bad Request" %}',
|
||||
'{% jstrans "Server returned error code 400" %}',
|
||||
);
|
||||
} else if (xhr.status == 401) {
|
||||
showAlertDialog(
|
||||
'{% trans "Error 401: Not Authenticated" %}',
|
||||
'{% trans "Authentication credentials not supplied" %}',
|
||||
'{% jstrans "Error 401: Not Authenticated" %}',
|
||||
'{% jstrans "Authentication credentials not supplied" %}',
|
||||
);
|
||||
} else if (xhr.status == 403) {
|
||||
showAlertDialog(
|
||||
'{% trans "Error 403: Permission Denied" %}',
|
||||
'{% trans "You do not have the required permissions to access this function" %}',
|
||||
'{% jstrans "Error 403: Permission Denied" %}',
|
||||
'{% jstrans "You do not have the required permissions to access this function" %}',
|
||||
);
|
||||
} else if (xhr.status == 404) {
|
||||
showAlertDialog(
|
||||
'{% trans "Error 404: Resource Not Found" %}',
|
||||
'{% trans "The requested resource could not be located on the server" %}',
|
||||
'{% jstrans "Error 404: Resource Not Found" %}',
|
||||
'{% jstrans "The requested resource could not be located on the server" %}',
|
||||
);
|
||||
} else if (xhr.status == 408) {
|
||||
showAlertDialog(
|
||||
'{% trans "Error 408: Timeout" %}',
|
||||
'{% trans "Connection timeout while requesting data from server" %}',
|
||||
'{% jstrans "Error 408: Timeout" %}',
|
||||
'{% jstrans "Connection timeout while requesting data from server" %}',
|
||||
);
|
||||
} else {
|
||||
showAlertDialog('{% trans "Error requesting form data" %}', renderErrorMessage(xhr));
|
||||
showAlertDialog('{% jstrans "Error requesting form data" %}', renderErrorMessage(xhr));
|
||||
}
|
||||
|
||||
console.error('Modal form error: ' + xhr.status);
|
||||
|
||||
@@ -216,21 +216,21 @@ function renderStockItem(data, parameters={}) {
|
||||
}
|
||||
|
||||
if (data.quantity == 0) {
|
||||
stock_detail = `<span class='badge rounded-pill bg-danger'>{% trans "No Stock"% }</span>`;
|
||||
stock_detail = `<span class='badge rounded-pill bg-danger'>{% jstrans "No Stock"% }</span>`;
|
||||
} else {
|
||||
if (data.serial && data.quantity == 1) {
|
||||
stock_detail = `{% trans "Serial Number" %}: ${data.serial}`;
|
||||
stock_detail = `{% jstrans "Serial Number" %}: ${data.serial}`;
|
||||
} else {
|
||||
if (render_available_quantity) {
|
||||
var available = data.quantity - data.allocated;
|
||||
stock_detail = `{% trans "Available" %}: ${available}`;
|
||||
stock_detail = `{% jstrans "Available" %}: ${available}`;
|
||||
} else {
|
||||
stock_detail = `{% trans "Quantity" %}: ${data.quantity}`;
|
||||
stock_detail = `{% jstrans "Quantity" %}: ${data.quantity}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.batch) {
|
||||
stock_detail += ` - <small>{% trans "Batch" %}: ${data.batch}</small>`;
|
||||
stock_detail += ` - <small>{% jstrans "Batch" %}: ${data.batch}</small>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ function renderPart(data, parameters={}) {
|
||||
labels = partStockLabel(data);
|
||||
|
||||
if (!data.active) {
|
||||
labels += `<span class='badge badge-right rounded-pill bg-danger'>{% trans "Inactive" %}</span>`;
|
||||
labels += `<span class='badge badge-right rounded-pill bg-danger'>{% jstrans "Inactive" %}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ function renderSalesOrderShipment(data, parameters={}) {
|
||||
return renderModel(
|
||||
{
|
||||
text: data.order_detail.reference,
|
||||
textSecondary: `{% trans "Shipment" %} ${data.reference}`,
|
||||
textSecondary: `{% jstrans "Shipment" %} ${data.reference}`,
|
||||
},
|
||||
parameters
|
||||
);
|
||||
|
||||
@@ -30,18 +30,18 @@ function loadNewsFeedTable(table, options={}, enableDelete=false) {
|
||||
},
|
||||
paginationVAlign: 'bottom',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No news found" %}';
|
||||
return '{% jstrans "No news found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: '{% trans "ID" %}',
|
||||
title: '{% jstrans "ID" %}',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'title',
|
||||
title: '{% trans "Title" %}',
|
||||
title: '{% jstrans "Title" %}',
|
||||
sortable: 'true',
|
||||
formatter: function(value, row) {
|
||||
return `<a href="` + row.link + `">` + value + `</a>`;
|
||||
@@ -49,15 +49,15 @@ function loadNewsFeedTable(table, options={}, enableDelete=false) {
|
||||
},
|
||||
{
|
||||
field: 'summary',
|
||||
title: '{% trans "Summary" %}',
|
||||
title: '{% jstrans "Summary" %}',
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
title: '{% trans "Author" %}',
|
||||
title: '{% jstrans "Author" %}',
|
||||
},
|
||||
{
|
||||
field: 'published',
|
||||
title: '{% trans "Published" %}',
|
||||
title: '{% jstrans "Published" %}',
|
||||
sortable: 'true',
|
||||
formatter: function(value, row) {
|
||||
var html = renderDate(value);
|
||||
|
||||
@@ -43,13 +43,13 @@ function loadNotificationTable(table, options={}, enableDelete=false) {
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: '{% trans "ID" %}',
|
||||
title: '{% jstrans "ID" %}',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'age',
|
||||
title: '{% trans "Age" %}',
|
||||
title: '{% jstrans "Age" %}',
|
||||
sortable: 'true',
|
||||
formatter: function(value, row) {
|
||||
return row.age_human;
|
||||
@@ -57,12 +57,12 @@ function loadNotificationTable(table, options={}, enableDelete=false) {
|
||||
},
|
||||
{
|
||||
field: 'category',
|
||||
title: '{% trans "Category" %}',
|
||||
title: '{% jstrans "Category" %}',
|
||||
sortable: 'true',
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Notification" %}',
|
||||
title: '{% jstrans "Notification" %}',
|
||||
formatter: function(value, row) {
|
||||
if (row.target && row.target.link) {
|
||||
return renderLink(value, row.target.link);
|
||||
@@ -73,7 +73,7 @@ function loadNotificationTable(table, options={}, enableDelete=false) {
|
||||
},
|
||||
{
|
||||
field: 'message',
|
||||
title: '{% trans "Message" %}',
|
||||
title: '{% jstrans "Message" %}',
|
||||
},
|
||||
{
|
||||
formatter: function(value, row, index, field) {
|
||||
@@ -82,7 +82,7 @@ function loadNotificationTable(table, options={}, enableDelete=false) {
|
||||
let bDel = '';
|
||||
|
||||
if (enableDelete) {
|
||||
bDel = `<button title='{% trans "Delete Notification" %}' class='notification-delete btn btn-outline-secondary' type='button' pk='${row.pk}'><span class='fas fa-trash-alt icon-red'></span></button>`;
|
||||
bDel = `<button title='{% jstrans "Delete Notification" %}' class='notification-delete btn btn-outline-secondary' type='button' pk='${row.pk}'><span class='fas fa-trash-alt icon-red'></span></button>`;
|
||||
}
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>${bRead}${bDel}</div>`;
|
||||
@@ -221,11 +221,11 @@ function getReadEditButton(pk, state, small=false) {
|
||||
let bReadTarget = '';
|
||||
|
||||
if (state) {
|
||||
bReadText = '{% trans "Mark as unread" %}';
|
||||
bReadText = '{% jstrans "Mark as unread" %}';
|
||||
bReadIcon = 'fas fa-bookmark icon-red';
|
||||
bReadTarget = 'unread';
|
||||
} else {
|
||||
bReadText = '{% trans "Mark as read" %}';
|
||||
bReadText = '{% jstrans "Mark as read" %}';
|
||||
bReadIcon = 'far fa-bookmark icon-green';
|
||||
bReadTarget = 'read';
|
||||
}
|
||||
@@ -251,7 +251,7 @@ function openNotificationPanel() {
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
html = `<p class='text-muted'><em>{% trans "No unread notifications" %}</em><span class='fas fa-check-circle icon-green float-right'></span></p>`;
|
||||
html = `<p class='text-muted'><em>{% jstrans "No unread notifications" %}</em><span class='fas fa-check-circle icon-green float-right'></span></p>`;
|
||||
} else {
|
||||
// build up items
|
||||
response.forEach(function(item, index) {
|
||||
@@ -293,7 +293,7 @@ function openNotificationPanel() {
|
||||
* clears the notification panel when closed
|
||||
**/
|
||||
function closeNotificationPanel() {
|
||||
$('#notification-center').html(`<p class='text-muted'>{% trans "Notifications will load here" %}</p>`);
|
||||
$('#notification-center').html(`<p class='text-muted'>{% jstrans "Notifications will load here" %}</p>`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,7 +86,7 @@ function createExtraLineItem(options={}) {
|
||||
constructForm(options.url, {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Extra Line Item" %}',
|
||||
title: '{% jstrans "Add Extra Line Item" %}',
|
||||
onSuccess: function(response) {
|
||||
if (options.table) {
|
||||
reloadBootstrapTable(options.table);
|
||||
@@ -123,11 +123,11 @@ function exportOrder(redirect_url, options={}) {
|
||||
}
|
||||
|
||||
constructFormBody({}, {
|
||||
title: '{% trans "Export Order" %}',
|
||||
title: '{% jstrans "Export Order" %}',
|
||||
fields: {
|
||||
format: {
|
||||
label: '{% trans "Format" %}',
|
||||
help_text: '{% trans "Select file format" %}',
|
||||
label: '{% jstrans "Format" %}',
|
||||
help_text: '{% jstrans "Select file format" %}',
|
||||
required: true,
|
||||
type: 'choice',
|
||||
value: format,
|
||||
@@ -238,7 +238,7 @@ function loadExtraLineTable(options={}) {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
data: data,
|
||||
title: '{% trans "Duplicate Line" %}',
|
||||
title: '{% jstrans "Duplicate Line" %}',
|
||||
onSuccess: reloadExtraLineTable,
|
||||
});
|
||||
}
|
||||
@@ -252,7 +252,7 @@ function loadExtraLineTable(options={}) {
|
||||
|
||||
constructForm(`${options.url}${pk}/`, {
|
||||
fields: extraLineFields(),
|
||||
title: '{% trans "Edit Line" %}',
|
||||
title: '{% jstrans "Edit Line" %}',
|
||||
onSuccess: reloadExtraLineTable,
|
||||
});
|
||||
});
|
||||
@@ -265,7 +265,7 @@ function loadExtraLineTable(options={}) {
|
||||
|
||||
constructForm(`${options.url}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Line" %}',
|
||||
title: '{% jstrans "Delete Line" %}',
|
||||
onSuccess: reloadExtraLineTable,
|
||||
});
|
||||
});
|
||||
@@ -278,7 +278,7 @@ function loadExtraLineTable(options={}) {
|
||||
sidePagination: 'server',
|
||||
onPostBody: setupCallbacks,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No line items found" %}';
|
||||
return '{% jstrans "No line items found" %}';
|
||||
},
|
||||
queryParams: filters,
|
||||
original: options.params,
|
||||
@@ -288,20 +288,20 @@ function loadExtraLineTable(options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
title: '{% jstrans "Reference" %}',
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
footerFormatter: function(data) {
|
||||
return data.map(function(row) {
|
||||
return +row['quantity'];
|
||||
@@ -313,7 +313,7 @@ function loadExtraLineTable(options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'price',
|
||||
title: '{% trans "Unit Price" %}',
|
||||
title: '{% jstrans "Unit Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.price, {
|
||||
currency: row.price_currency,
|
||||
@@ -324,7 +324,7 @@ function loadExtraLineTable(options={}) {
|
||||
field: 'total_price',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
title: '{% trans "Total Price" %}',
|
||||
title: '{% jstrans "Total Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.price * row.quantity, {
|
||||
currency: row.price_currency,
|
||||
@@ -344,11 +344,11 @@ function loadExtraLineTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
title: '{% trans "Notes" %}',
|
||||
title: '{% jstrans "Notes" %}',
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
@@ -366,12 +366,12 @@ function loadExtraLineTable(options={}) {
|
||||
var pk = row.pk;
|
||||
|
||||
if (options.allow_edit) {
|
||||
html += makeCopyButton('button-duplicate', pk, '{% trans "Duplicate line" %}');
|
||||
html += makeEditButton('button-edit', pk, '{% trans "Edit line" %}');
|
||||
html += makeCopyButton('button-duplicate', pk, '{% jstrans "Duplicate line" %}');
|
||||
html += makeEditButton('button-edit', pk, '{% jstrans "Edit line" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
html += makeDeleteButton('button-delete', pk, '{% trans "Delete line" %}', );
|
||||
html += makeDeleteButton('button-delete', pk, '{% jstrans "Delete line" %}', );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,33 +43,33 @@ function loadPluginTable(table, options={}) {
|
||||
queryParams: filters,
|
||||
sortable: true,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No plugins found" %}';
|
||||
return '{% jstrans "No plugins found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Plugin" %}',
|
||||
title: '{% jstrans "Plugin" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
let html = '';
|
||||
|
||||
if (!row.is_installed) {
|
||||
html += `<span class='fa fa-question-circle' title='{% trans "This plugin is no longer installed" %}'></span>`;
|
||||
html += `<span class='fa fa-question-circle' title='{% jstrans "This plugin is no longer installed" %}'></span>`;
|
||||
} else if (row.active) {
|
||||
html += `<span class='fa fa-check-circle icon-green' title='{% trans "This plugin is active" %}'></span>`;
|
||||
html += `<span class='fa fa-check-circle icon-green' title='{% jstrans "This plugin is active" %}'></span>`;
|
||||
} else {
|
||||
html += `<span class='fa fa-times-circle icon-red' title ='{% trans "This plugin is installed but not active" %}'></span>`;
|
||||
html += `<span class='fa fa-times-circle icon-red' title ='{% jstrans "This plugin is installed but not active" %}'></span>`;
|
||||
}
|
||||
|
||||
html += ` <span>${value}</span>`;
|
||||
|
||||
if (row.is_builtin) {
|
||||
html += `<span class='badge bg-success rounded-pill badge-right'>{% trans "Builtin" %}</span>`;
|
||||
html += `<span class='badge bg-success rounded-pill badge-right'>{% jstrans "Builtin" %}</span>`;
|
||||
}
|
||||
|
||||
if (row.is_sample) {
|
||||
html += `<span class='badge bg-info rounded-pill badge-right'>{% trans "Sample" %}</span>`;
|
||||
html += `<span class='badge bg-info rounded-pill badge-right'>{% jstrans "Sample" %}</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -77,13 +77,13 @@ function loadPluginTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'meta.description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'meta.version',
|
||||
title: '{% trans "Version" %}',
|
||||
title: '{% jstrans "Version" %}',
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
let html = value;
|
||||
@@ -100,7 +100,7 @@ function loadPluginTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'meta.author',
|
||||
title: '{% trans "Author" %}',
|
||||
title: '{% jstrans "Author" %}',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
@@ -114,9 +114,9 @@ function loadPluginTable(table, options={}) {
|
||||
// Check if custom plugins are enabled for this instance
|
||||
if (options.custom && !row.is_builtin && row.is_installed) {
|
||||
if (row.active) {
|
||||
buttons += makeIconButton('fa-stop-circle icon-red', 'btn-plugin-disable', row.pk, '{% trans "Disable Plugin" %}');
|
||||
buttons += makeIconButton('fa-stop-circle icon-red', 'btn-plugin-disable', row.pk, '{% jstrans "Disable Plugin" %}');
|
||||
} else {
|
||||
buttons += makeIconButton('fa-play-circle icon-green', 'btn-plugin-enable', row.pk, '{% trans "Enable Plugin" %}');
|
||||
buttons += makeIconButton('fa-play-circle icon-green', 'btn-plugin-enable', row.pk, '{% jstrans "Enable Plugin" %}');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,14 +148,14 @@ function loadPluginTable(table, options={}) {
|
||||
function installPlugin() {
|
||||
constructForm(`/api/plugins/install/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Install Plugin" %}',
|
||||
title: '{% jstrans "Install Plugin" %}',
|
||||
fields: {
|
||||
packagename: {},
|
||||
url: {},
|
||||
confirm: {},
|
||||
},
|
||||
onSuccess: function(data) {
|
||||
let msg = '{% trans "The Plugin was installed" %}';
|
||||
let msg = '{% jstrans "The Plugin was installed" %}';
|
||||
showMessage(msg, {style: 'success', details: data.result, timeout: 30000});
|
||||
|
||||
// Reload the plugin table
|
||||
@@ -174,19 +174,19 @@ function activatePlugin(plugin_id, active=true) {
|
||||
|
||||
let html = active ? `
|
||||
<span class='alert alert-block alert-info'>
|
||||
{% trans "Are you sure you want to enable this plugin?" %}
|
||||
{% jstrans "Are you sure you want to enable this plugin?" %}
|
||||
</span>
|
||||
` : `
|
||||
<span class='alert alert-block alert-danger'>
|
||||
{% trans "Are you sure you want to disable this plugin?" %}
|
||||
{% jstrans "Are you sure you want to disable this plugin?" %}
|
||||
</span>
|
||||
`;
|
||||
|
||||
constructForm(null, {
|
||||
title: active ? '{% trans "Enable Plugin" %}' : '{% trans "Disable Plugin" %}',
|
||||
title: active ? '{% jstrans "Enable Plugin" %}' : '{% jstrans "Disable Plugin" %}',
|
||||
preFormContent: html,
|
||||
confirm: true,
|
||||
submitText: active ? '{% trans "Enable" %}' : '{% trans "Disable" %}',
|
||||
submitText: active ? '{% jstrans "Enable" %}' : '{% jstrans "Disable" %}',
|
||||
submitClass: active ? 'success' : 'danger',
|
||||
onSubmit: function(_fields, opts) {
|
||||
showModalSpinner(opts.modal);
|
||||
@@ -200,7 +200,7 @@ function activatePlugin(plugin_id, active=true) {
|
||||
method: 'PATCH',
|
||||
success: function() {
|
||||
$(opts.modal).modal('hide');
|
||||
addCachedAlert('{% trans "Plugin updated" %}', {style: 'success'});
|
||||
addCachedAlert('{% jstrans "Plugin updated" %}', {style: 'success'});
|
||||
location.reload();
|
||||
},
|
||||
error: function(xhr) {
|
||||
@@ -221,7 +221,7 @@ function reloadPlugins() {
|
||||
let url = '{% url "api-plugin-reload" %}';
|
||||
|
||||
constructForm(url, {
|
||||
title: '{% trans "Reload Plugins" %}',
|
||||
title: '{% jstrans "Reload Plugins" %}',
|
||||
method: 'POST',
|
||||
confirm: true,
|
||||
fields: {
|
||||
|
||||
@@ -156,7 +156,7 @@ function calculateTotalPrice(dataset, value_func, currency_func, options={}) {
|
||||
|
||||
if (!rates) {
|
||||
console.error('Could not retrieve currency conversion information from the server');
|
||||
return `<span class='icon-red fas fa-exclamation-circle' title='{% trans "Error fetching currency data" %}'></span>`;
|
||||
return `<span class='icon-red fas fa-exclamation-circle' title='{% jstrans "Error fetching currency data" %}'></span>`;
|
||||
}
|
||||
|
||||
if (!currency) {
|
||||
@@ -318,7 +318,7 @@ function loadBomPricingChart(options={}) {
|
||||
search: false,
|
||||
showColumns: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No BOM data available" %}';
|
||||
return '{% jstrans "No BOM data available" %}';
|
||||
},
|
||||
onLoadSuccess: function(data) {
|
||||
// Construct BOM pricing chart
|
||||
@@ -350,12 +350,12 @@ function loadBomPricingChart(options={}) {
|
||||
labels: graphLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: '{% trans "Maximum Price" %}',
|
||||
label: '{% jstrans "Maximum Price" %}',
|
||||
data: maxValues,
|
||||
backgroundColor: colors,
|
||||
},
|
||||
{
|
||||
label: '{% trans "Minimum Price" %}',
|
||||
label: '{% jstrans "Minimum Price" %}',
|
||||
data: minValues,
|
||||
backgroundColor: colors,
|
||||
},
|
||||
@@ -366,7 +366,7 @@ function loadBomPricingChart(options={}) {
|
||||
columns: [
|
||||
{
|
||||
field: 'sub_part',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
var url = `/part/${row.sub_part}/`;
|
||||
@@ -378,17 +378,17 @@ function loadBomPricingChart(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
title: '{% jstrans "Reference" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'pricing',
|
||||
title: '{% trans "Price Range" %}',
|
||||
title: '{% jstrans "Price Range" %}',
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
var min_price = row.pricing_min;
|
||||
@@ -460,7 +460,7 @@ function loadPartSupplierPricingTable(options={}) {
|
||||
search: false,
|
||||
showColumns: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No supplier pricing data available" %}';
|
||||
return '{% jstrans "No supplier pricing data available" %}';
|
||||
},
|
||||
onLoadSuccess: function(data) {
|
||||
// Update supplier pricing chart
|
||||
@@ -471,7 +471,7 @@ function loadPartSupplierPricingTable(options={}) {
|
||||
// Sort in increasing order of quantity
|
||||
data = data.sort((a, b) => (a.quantity - b.quantity));
|
||||
|
||||
var graphLabels = Array.from(data, (x) => (`${x.part_detail.SKU} - {% trans "Quantity" %} ${x.quantity}`));
|
||||
var graphLabels = Array.from(data, (x) => (`${x.part_detail.SKU} - {% jstrans "Quantity" %} ${x.quantity}`));
|
||||
var graphValues = Array.from(data, (x) => (x.price / x.part_detail.pack_quantity_native));
|
||||
|
||||
if (chart) {
|
||||
@@ -482,7 +482,7 @@ function loadPartSupplierPricingTable(options={}) {
|
||||
labels: graphLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: '{% trans "Supplier Pricing" %}',
|
||||
label: '{% jstrans "Supplier Pricing" %}',
|
||||
data: graphValues,
|
||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||
borderColor: 'rgb(255, 206, 86)',
|
||||
@@ -495,7 +495,7 @@ function loadPartSupplierPricingTable(options={}) {
|
||||
columns: [
|
||||
{
|
||||
field: 'supplier',
|
||||
title: '{% trans "Supplier" %}',
|
||||
title: '{% jstrans "Supplier" %}',
|
||||
formatter: function(value, row) {
|
||||
var html = '';
|
||||
|
||||
@@ -507,7 +507,7 @@ function loadPartSupplierPricingTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'sku',
|
||||
title: '{% trans "SKU" %}',
|
||||
title: '{% jstrans "SKU" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
return renderLink(
|
||||
@@ -519,12 +519,12 @@ function loadPartSupplierPricingTable(options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'price',
|
||||
title: '{% trans "Unit Price" %}',
|
||||
title: '{% jstrans "Unit Price" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (row.price == null) {
|
||||
@@ -569,7 +569,7 @@ function loadPriceBreakTable(table, options={}) {
|
||||
pageSize: 10,
|
||||
method: 'get',
|
||||
formatNoMatches: function() {
|
||||
return `{% trans "No price break data available" %}`;
|
||||
return `{% jstrans "No price break data available" %}`;
|
||||
},
|
||||
queryParams: {
|
||||
part: options.part
|
||||
@@ -593,7 +593,7 @@ function loadPriceBreakTable(table, options={}) {
|
||||
labels: graphLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: '{% trans "Unit Price" %}',
|
||||
label: '{% jstrans "Unit Price" %}',
|
||||
data: graphData,
|
||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||
borderColor: 'rgb(255, 206, 86)',
|
||||
@@ -613,20 +613,20 @@ function loadPriceBreakTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '{% trans "Price" %}',
|
||||
title: '{% jstrans "Price" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
let html = formatCurrency(value, {currency: row.price_currency});
|
||||
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeEditButton(`button-${name}-edit`, row.pk, `{% trans "Edit" %} ${human_name}`);
|
||||
buttons += makeDeleteButton(`button-${name}-delete`, row.pk, `{% trans "Delete" %} ${human_name}"`);
|
||||
buttons += makeEditButton(`button-${name}-edit`, row.pk, `{% jstrans "Edit" %} ${human_name}`);
|
||||
buttons += makeDeleteButton(`button-${name}-delete`, row.pk, `{% jstrans "Delete" %} ${human_name}"`);
|
||||
|
||||
html += wrapButtons(buttons);
|
||||
|
||||
@@ -681,7 +681,7 @@ function initPriceBreakSet(table, options) {
|
||||
},
|
||||
},
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Price Break" %}',
|
||||
title: '{% jstrans "Add Price Break" %}',
|
||||
onSuccess: reloadPriceBreakTable,
|
||||
});
|
||||
});
|
||||
@@ -691,7 +691,7 @@ function initPriceBreakSet(table, options) {
|
||||
|
||||
constructForm(`${pb_url}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Price Break" %}',
|
||||
title: '{% jstrans "Delete Price Break" %}',
|
||||
onSuccess: reloadPriceBreakTable,
|
||||
});
|
||||
});
|
||||
@@ -709,7 +709,7 @@ function initPriceBreakSet(table, options) {
|
||||
icon: 'fa-coins',
|
||||
},
|
||||
},
|
||||
title: '{% trans "Edit Price Break" %}',
|
||||
title: '{% jstrans "Edit Price Break" %}',
|
||||
onSuccess: reloadPriceBreakTable,
|
||||
});
|
||||
});
|
||||
@@ -752,7 +752,7 @@ function loadPurchasePriceHistoryTable(options={}) {
|
||||
search: false,
|
||||
showColumns: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No purchase history data available" %}';
|
||||
return '{% jstrans "No purchase history data available" %}';
|
||||
},
|
||||
onLoadSuccess: function(data) {
|
||||
// Update purchase price history chart
|
||||
@@ -788,7 +788,7 @@ function loadPurchasePriceHistoryTable(options={}) {
|
||||
labels: graphLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: '{% trans "Purchase Price History" %}',
|
||||
label: '{% jstrans "Purchase Price History" %}',
|
||||
data: graphValues,
|
||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||
borderColor: 'rgb(255, 206, 86)',
|
||||
@@ -801,7 +801,7 @@ function loadPurchasePriceHistoryTable(options={}) {
|
||||
columns: [
|
||||
{
|
||||
field: 'order',
|
||||
title: '{% trans "Purchase Order" %}',
|
||||
title: '{% jstrans "Purchase Order" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
var order = row.order_detail;
|
||||
@@ -823,7 +823,7 @@ function loadPurchasePriceHistoryTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'order_detail.complete_date',
|
||||
title: '{% trans "Date" %}',
|
||||
title: '{% jstrans "Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
@@ -831,7 +831,7 @@ function loadPurchasePriceHistoryTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'purchase_price',
|
||||
title: '{% trans "Unit Price" %}',
|
||||
title: '{% jstrans "Unit Price" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
|
||||
@@ -891,7 +891,7 @@ function loadSalesPriceHistoryTable(options={}) {
|
||||
search: false,
|
||||
showColumns: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No sales history data available" %}';
|
||||
return '{% jstrans "No sales history data available" %}';
|
||||
},
|
||||
onLoadSuccess: function(data) {
|
||||
// Update sales price history chart
|
||||
@@ -913,7 +913,7 @@ function loadSalesPriceHistoryTable(options={}) {
|
||||
labels: graphLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: '{% trans "Sale Price History" %}',
|
||||
label: '{% jstrans "Sale Price History" %}',
|
||||
data: graphValues,
|
||||
backgroundColor: 'rgba(255, 206, 86, 0.2)',
|
||||
borderColor: 'rgb(255, 206, 86)',
|
||||
@@ -926,7 +926,7 @@ function loadSalesPriceHistoryTable(options={}) {
|
||||
columns: [
|
||||
{
|
||||
field: 'order',
|
||||
title: '{% trans "Sales Order" %}',
|
||||
title: '{% jstrans "Sales Order" %}',
|
||||
formatter: function(value, row) {
|
||||
var order = row.order_detail;
|
||||
var customer = row.customer_detail;
|
||||
@@ -947,14 +947,14 @@ function loadSalesPriceHistoryTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'shipment_date',
|
||||
title: '{% trans "Date" %}',
|
||||
title: '{% jstrans "Date" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderDate(row.order_detail.shipment_date);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'sale_price',
|
||||
title: '{% trans "Sale Price" %}',
|
||||
title: '{% jstrans "Sale Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(value, {
|
||||
currency: row.sale_price_currency
|
||||
@@ -1002,7 +1002,7 @@ function loadVariantPricingChart(options={}) {
|
||||
search: false,
|
||||
showColumns: false,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No variant data available" %}';
|
||||
return '{% jstrans "No variant data available" %}';
|
||||
},
|
||||
onLoadSuccess: function(data) {
|
||||
// Construct variant pricing chart
|
||||
@@ -1021,7 +1021,7 @@ function loadVariantPricingChart(options={}) {
|
||||
labels: graphLabels,
|
||||
datasets: [
|
||||
{
|
||||
label: '{% trans "Minimum Price" %}',
|
||||
label: '{% jstrans "Minimum Price" %}',
|
||||
data: minValues,
|
||||
backgroundColor: 'rgba(200, 250, 200, 0.75)',
|
||||
borderColor: 'rgba(200, 250, 200)',
|
||||
@@ -1029,7 +1029,7 @@ function loadVariantPricingChart(options={}) {
|
||||
fill: true,
|
||||
},
|
||||
{
|
||||
label: '{% trans "Maximum Price" %}',
|
||||
label: '{% jstrans "Maximum Price" %}',
|
||||
data: maxValues,
|
||||
backgroundColor: 'rgba(250, 220, 220, 0.75)',
|
||||
borderColor: 'rgba(250, 220, 220)',
|
||||
@@ -1042,7 +1042,7 @@ function loadVariantPricingChart(options={}) {
|
||||
columns: [
|
||||
{
|
||||
field: 'part',
|
||||
title: '{% trans "Variant Part" %}',
|
||||
title: '{% jstrans "Variant Part" %}',
|
||||
formatter: function(value, row) {
|
||||
var name = shortenString(row.full_name);
|
||||
var display = imageHoverIcon(row.thumbnail) + renderLink(name, `/part/${row.pk}/`);
|
||||
@@ -1051,7 +1051,7 @@ function loadVariantPricingChart(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'pricing',
|
||||
title: '{% trans "Price Range" %}',
|
||||
title: '{% jstrans "Price Range" %}',
|
||||
formatter: function(value, row) {
|
||||
var min_price = row.pricing_min;
|
||||
var max_price = row.pricing_max;
|
||||
|
||||
@@ -90,7 +90,7 @@ function purchaseOrderFields(options={}) {
|
||||
supplier: {
|
||||
icon: 'fa-building',
|
||||
secondary: {
|
||||
title: '{% trans "Add Supplier" %}',
|
||||
title: '{% jstrans "Add Supplier" %}',
|
||||
fields: function() {
|
||||
var fields = companyFormFields();
|
||||
|
||||
@@ -165,24 +165,24 @@ function purchaseOrderFields(options={}) {
|
||||
supplier_detail: true,
|
||||
},
|
||||
api_url: '{% url "api-po-list" %}',
|
||||
label: '{% trans "Purchase Order" %}',
|
||||
help_text: '{% trans "Select purchase order to duplicate" %}',
|
||||
label: '{% jstrans "Purchase Order" %}',
|
||||
help_text: '{% jstrans "Select purchase order to duplicate" %}',
|
||||
};
|
||||
|
||||
fields.duplicate_line_items = {
|
||||
value: true,
|
||||
group: 'duplicate',
|
||||
type: 'boolean',
|
||||
label: '{% trans "Duplicate Line Items" %}',
|
||||
help_text: '{% trans "Duplicate all line items from the selected order" %}',
|
||||
label: '{% jstrans "Duplicate Line Items" %}',
|
||||
help_text: '{% jstrans "Duplicate all line items from the selected order" %}',
|
||||
};
|
||||
|
||||
fields.duplicate_extra_lines = {
|
||||
value: true,
|
||||
group: 'duplicate',
|
||||
type: 'boolean',
|
||||
label: '{% trans "Duplicate Extra Lines" %}',
|
||||
help_text: '{% trans "Duplicate extra line items from the selected order" %}',
|
||||
label: '{% jstrans "Duplicate Extra Lines" %}',
|
||||
help_text: '{% jstrans "Duplicate extra line items from the selected order" %}',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ function editPurchaseOrder(pk, options={}) {
|
||||
|
||||
constructForm(`{% url "api-po-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Purchase Order" %}',
|
||||
title: '{% jstrans "Edit Purchase Order" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -220,7 +220,7 @@ function createPurchaseOrder(options={}) {
|
||||
|
||||
if (options.duplicate_order) {
|
||||
groups.duplicate = {
|
||||
title: '{% trans "Duplication Options" %}',
|
||||
title: '{% jstrans "Duplication Options" %}',
|
||||
collapsible: false,
|
||||
};
|
||||
}
|
||||
@@ -239,7 +239,7 @@ function createPurchaseOrder(options={}) {
|
||||
location.href = `/order/purchase-order/${data.pk}/`;
|
||||
}
|
||||
},
|
||||
title: options.title || '{% trans "Create Purchase Order" %}',
|
||||
title: options.title || '{% jstrans "Create Purchase Order" %}',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ function poLineItemFields(options={}) {
|
||||
).then(function() {
|
||||
// Update pack size information
|
||||
if (pack_quantity != 1) {
|
||||
var txt = `<span class='fas fa-info-circle icon-blue'></span> {% trans "Pack Quantity" %}: ${formatDecimal(pack_quantity)} ${units}`;
|
||||
var txt = `<span class='fas fa-info-circle icon-blue'></span> {% jstrans "Pack Quantity" %}: ${formatDecimal(pack_quantity)} ${units}`;
|
||||
$(opts.modal).find('#hint_id_quantity').after(`<div class='form-info-message' id='info-pack-size'>${txt}</div>`);
|
||||
}
|
||||
}).then(function() {
|
||||
@@ -349,7 +349,7 @@ function poLineItemFields(options={}) {
|
||||
},
|
||||
secondary: {
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Supplier Part" %}',
|
||||
title: '{% jstrans "Add Supplier Part" %}',
|
||||
fields: function(data) {
|
||||
var fields = supplierPartFields({
|
||||
part: data.part,
|
||||
@@ -430,7 +430,7 @@ function createPurchaseOrderLineItem(order, options={}) {
|
||||
constructForm('{% url "api-po-line-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Line Item" %}',
|
||||
title: '{% jstrans "Add Line Item" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -447,7 +447,7 @@ function completePurchaseOrder(order_id, options={}) {
|
||||
`/api/order/po/${order_id}/complete/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Complete Purchase Order" %}',
|
||||
title: '{% jstrans "Complete Purchase Order" %}',
|
||||
confirm: true,
|
||||
fieldsFunction: function(opts) {
|
||||
var fields = {
|
||||
@@ -464,19 +464,19 @@ function completePurchaseOrder(order_id, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Mark this order as complete?" %}
|
||||
{% jstrans "Mark this order as complete?" %}
|
||||
</div>`;
|
||||
|
||||
if (opts.context.is_complete) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-success'>
|
||||
{% trans "All line items have been received" %}
|
||||
{% jstrans "All line items have been received" %}
|
||||
</div>`;
|
||||
} else {
|
||||
html += `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans 'This order has line items which have not been marked as received.' %}</br>
|
||||
{% trans 'Completing this order means that the order and line items will no longer be editable.' %}
|
||||
{% jstrans 'This order has line items which have not been marked as received.' %}</br>
|
||||
{% jstrans 'Completing this order means that the order and line items will no longer be editable.' %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -499,18 +499,18 @@ function cancelPurchaseOrder(order_id, options={}) {
|
||||
`/api/order/po/${order_id}/cancel/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Cancel Purchase Order" %}',
|
||||
title: '{% jstrans "Cancel Purchase Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: function(opts) {
|
||||
var html = `
|
||||
<div class='alert alert-info alert-block'>
|
||||
{% trans "Are you sure you wish to cancel this purchase order?" %}
|
||||
{% jstrans "Are you sure you wish to cancel this purchase order?" %}
|
||||
</div>`;
|
||||
|
||||
if (!opts.context.can_cancel) {
|
||||
html += `
|
||||
<div class='alert alert-danger alert-block'>
|
||||
{% trans "This purchase order can not be cancelled" %}
|
||||
{% jstrans "This purchase order can not be cancelled" %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -531,12 +531,12 @@ function issuePurchaseOrder(order_id, options={}) {
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans 'After placing this order, line items will no longer be editable.' %}
|
||||
{% jstrans 'After placing this order, line items will no longer be editable.' %}
|
||||
</div>`;
|
||||
|
||||
constructForm(`{% url "api-po-list" %}${order_id}/issue/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Issue Purchase Order" %}',
|
||||
title: '{% jstrans "Issue Purchase Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
@@ -627,8 +627,8 @@ function orderParts(parts_list, options={}) {
|
||||
|
||||
if (parts.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "At least one purchaseable part must be selected" %}',
|
||||
'{% jstrans "Select Parts" %}',
|
||||
'{% jstrans "At least one purchaseable part must be selected" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -653,7 +653,7 @@ function orderParts(parts_list, options={}) {
|
||||
type: 'decimal',
|
||||
min_value: 0,
|
||||
value: quantity,
|
||||
title: '{% trans "Quantity to order" %}',
|
||||
title: '{% jstrans "Quantity to order" %}',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@@ -662,7 +662,7 @@ function orderParts(parts_list, options={}) {
|
||||
);
|
||||
|
||||
var supplier_part_prefix = `
|
||||
<button type='button' class='input-group-text button-row-new-sp' pk='${pk}' title='{% trans "New supplier part" %}'>
|
||||
<button type='button' class='input-group-text button-row-new-sp' pk='${pk}' title='{% jstrans "New supplier part" %}'>
|
||||
<span class='fas fa-plus-circle icon-green'></span>
|
||||
</button>
|
||||
`;
|
||||
@@ -680,7 +680,7 @@ function orderParts(parts_list, options={}) {
|
||||
);
|
||||
|
||||
var purchase_order_prefix = `
|
||||
<button type='button' class='input-group-text button-row-new-po' pk='${pk}' title='{% trans "New purchase order" %}'>
|
||||
<button type='button' class='input-group-text button-row-new-po' pk='${pk}' title='{% jstrans "New purchase order" %}'>
|
||||
<span class='fas fa-plus-circle icon-green'></span>
|
||||
</button>
|
||||
`;
|
||||
@@ -703,7 +703,7 @@ function orderParts(parts_list, options={}) {
|
||||
buttons += makeRemoveButton(
|
||||
'button-row-remove',
|
||||
pk,
|
||||
'{% trans "Remove row" %}',
|
||||
'{% jstrans "Remove row" %}',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -712,7 +712,7 @@ function orderParts(parts_list, options={}) {
|
||||
'fa-shopping-cart icon-blue',
|
||||
'button-row-add',
|
||||
pk,
|
||||
'{% trans "Add to purchase order" %}',
|
||||
'{% jstrans "Add to purchase order" %}',
|
||||
);
|
||||
|
||||
buttons = wrapButtons(buttons);
|
||||
@@ -757,10 +757,10 @@ function orderParts(parts_list, options={}) {
|
||||
<table class='table table-striped table-condensed' id='order-parts-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th style='min-width: 300px;'>{% trans "Supplier Part" %}</th>
|
||||
<th style='min-width: 300px;'>{% trans "Purchase Order" %}</th>
|
||||
<th style='min-width: 50px;'>{% trans "Quantity" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th style='min-width: 300px;'>{% jstrans "Supplier Part" %}</th>
|
||||
<th style='min-width: 300px;'>{% jstrans "Purchase Order" %}</th>
|
||||
<th style='min-width: 50px;'>{% jstrans "Quantity" %}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -800,9 +800,9 @@ function orderParts(parts_list, options={}) {
|
||||
|
||||
constructFormBody({}, {
|
||||
preFormContent: html,
|
||||
title: '{% trans "Order Parts" %}',
|
||||
title: '{% jstrans "Order Parts" %}',
|
||||
hideSubmitButton: true,
|
||||
closeText: '{% trans "Close" %}',
|
||||
closeText: '{% jstrans "Close" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
parts.forEach(function(part) {
|
||||
|
||||
@@ -842,7 +842,7 @@ function orderParts(parts_list, options={}) {
|
||||
}
|
||||
).then(function() {
|
||||
if (pack_quantity != 1) {
|
||||
var txt = `<span class='fas fa-info-circle icon-blue'></span> {% trans "Pack Quantity" %}: ${pack_quantity} ${units}`;
|
||||
var txt = `<span class='fas fa-info-circle icon-blue'></span> {% jstrans "Pack Quantity" %}: ${pack_quantity} ${units}`;
|
||||
$(opts.modal).find(`#id_quantity_${pk}`).after(`<div class='form-info-message' id='info-pack-size-${pk}'>${txt}</div>`);
|
||||
}
|
||||
});
|
||||
@@ -860,7 +860,7 @@ function orderParts(parts_list, options={}) {
|
||||
filters: supplier_part_filters,
|
||||
onEdit: onSupplierPartChanged,
|
||||
noResults: function(query) {
|
||||
return '{% trans "No matching supplier parts" %}';
|
||||
return '{% jstrans "No matching supplier parts" %}';
|
||||
}
|
||||
};
|
||||
|
||||
@@ -879,7 +879,7 @@ function orderParts(parts_list, options={}) {
|
||||
value: options.order,
|
||||
filters: order_filters,
|
||||
noResults: function(query) {
|
||||
return '{% trans "No matching purchase orders" %}';
|
||||
return '{% jstrans "No matching purchase orders" %}';
|
||||
}
|
||||
}, null, opts);
|
||||
|
||||
@@ -1066,8 +1066,8 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
if (line_items.length == 0) {
|
||||
|
||||
showAlertDialog(
|
||||
'{% trans "Select Line Items" %}',
|
||||
'{% trans "At least one line item must be selected" %}',
|
||||
'{% jstrans "Select Line Items" %}',
|
||||
'{% jstrans "At least one line item must be selected" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1096,8 +1096,8 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
if (native_pack_quantity != 1) {
|
||||
pack_size_div = `
|
||||
<div class='alert alert-small alert-block alert-info'>
|
||||
{% trans "Pack Quantity" %}: ${pack_quantity}<br>
|
||||
{% trans "Received Quantity" %}: <span class='pack_received_quantity' id='items_received_quantity_${pk}'>${received}</span> ${units}
|
||||
{% jstrans "Pack Quantity" %}: ${pack_quantity}<br>
|
||||
{% jstrans "Received Quantity" %}: <span class='pack_received_quantity' id='items_received_quantity_${pk}'>${received}</span> ${units}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -1108,7 +1108,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
type: 'decimal',
|
||||
min_value: 0,
|
||||
value: quantity,
|
||||
title: '{% trans "Quantity to receive" %}',
|
||||
title: '{% jstrans "Quantity to receive" %}',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@@ -1122,8 +1122,8 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
{
|
||||
type: 'string',
|
||||
required: false,
|
||||
label: '{% trans "Batch Code" %}',
|
||||
help_text: '{% trans "Enter batch code for incoming stock items" %}',
|
||||
label: '{% jstrans "Batch Code" %}',
|
||||
help_text: '{% jstrans "Enter batch code for incoming stock items" %}',
|
||||
icon: 'fa-layer-group',
|
||||
},
|
||||
{
|
||||
@@ -1146,8 +1146,8 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
{
|
||||
type: 'string',
|
||||
required: false,
|
||||
label: '{% trans "Serial Numbers" %}',
|
||||
help_text: '{% trans "Enter serial numbers for incoming stock items" %}',
|
||||
label: '{% jstrans "Serial Numbers" %}',
|
||||
help_text: '{% jstrans "Enter serial numbers for incoming stock items" %}',
|
||||
icon: 'fa-hashtag',
|
||||
},
|
||||
{
|
||||
@@ -1171,7 +1171,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
`items_location_${pk}`,
|
||||
{
|
||||
type: 'related field',
|
||||
label: '{% trans "Location" %}',
|
||||
label: '{% jstrans "Location" %}',
|
||||
required: false,
|
||||
icon: 'fa-sitemap',
|
||||
},
|
||||
@@ -1184,7 +1184,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
`items_status_${pk}`,
|
||||
{
|
||||
type: 'choice',
|
||||
label: '{% trans "Stock Status" %}',
|
||||
label: '{% jstrans "Stock Status" %}',
|
||||
required: true,
|
||||
choices: choices,
|
||||
value: 10, // OK
|
||||
@@ -1198,11 +1198,11 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
let buttons = '';
|
||||
|
||||
if (global_settings.BARCODE_ENABLE) {
|
||||
buttons += makeIconButton('fa-qrcode', 'button-row-add-barcode', pk, '{% trans "Add barcode" %}');
|
||||
buttons += makeIconButton('fa-unlink icon-red', 'button-row-remove-barcode', pk, '{% trans "Remove barcode" %}', {hidden: true});
|
||||
buttons += makeIconButton('fa-qrcode', 'button-row-add-barcode', pk, '{% jstrans "Add barcode" %}');
|
||||
buttons += makeIconButton('fa-unlink icon-red', 'button-row-remove-barcode', pk, '{% jstrans "Remove barcode" %}', {hidden: true});
|
||||
}
|
||||
|
||||
buttons += makeIconButton('fa-sitemap', 'button-row-add-location', pk, '{% trans "Specify location" %}', {
|
||||
buttons += makeIconButton('fa-sitemap', 'button-row-add-location', pk, '{% jstrans "Specify location" %}', {
|
||||
collapseTarget: `row-destination-${pk}`
|
||||
});
|
||||
|
||||
@@ -1210,7 +1210,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
'fa-layer-group',
|
||||
'button-row-add-batch',
|
||||
pk,
|
||||
'{% trans "Add batch code" %}',
|
||||
'{% jstrans "Add batch code" %}',
|
||||
{
|
||||
collapseTarget: `row-batch-${pk}`
|
||||
}
|
||||
@@ -1221,7 +1221,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
'fa-hashtag',
|
||||
'button-row-add-serials',
|
||||
pk,
|
||||
'{% trans "Add serial numbers" %}',
|
||||
'{% jstrans "Add serial numbers" %}',
|
||||
{
|
||||
collapseTarget: `row-serials-${pk}`,
|
||||
}
|
||||
@@ -1229,7 +1229,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
}
|
||||
|
||||
if (line_items.length > 1) {
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% jstrans "Remove row" %}');
|
||||
}
|
||||
|
||||
buttons = wrapButtons(buttons);
|
||||
@@ -1261,19 +1261,19 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
<!-- Hidden rows for extra data entry -->
|
||||
<tr id='row-destination-${pk}' class='collapse'>
|
||||
<td colspan='2'></td>
|
||||
<th>{% trans "Location" %}</th>
|
||||
<th>{% jstrans "Location" %}</th>
|
||||
<td colspan='2'>${destination_input}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr id='row-batch-${pk}' class='collapse'>
|
||||
<td colspan='2'></td>
|
||||
<th>{% trans "Batch" %}</th>
|
||||
<th>{% jstrans "Batch" %}</th>
|
||||
<td colspan='2'>${batch_input}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr id='row-serials-${pk}' class='collapse'>
|
||||
<td colspan='2'></td>
|
||||
<th>{% trans "Serials" %}</th>
|
||||
<th>{% jstrans "Serials" %}</th>
|
||||
<td colspan=2'>${sn_input}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
@@ -1297,11 +1297,11 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
<table class='table table-condensed' id='order-receive-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Order Code" %}</th>
|
||||
<th>{% trans "Received" %}</th>
|
||||
<th style='min-width: 50px;'>{% trans "Quantity to Receive" %}</th>
|
||||
<th style='min-width: 150px;'>{% trans "Status" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Order Code" %}</th>
|
||||
<th>{% jstrans "Received" %}</th>
|
||||
<th style='min-width: 50px;'>{% jstrans "Quantity to Receive" %}</th>
|
||||
<th style='min-width: 150px;'>{% jstrans "Status" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1326,8 +1326,8 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
},
|
||||
preFormContent: html,
|
||||
confirm: true,
|
||||
confirmMessage: '{% trans "Confirm receipt of items" %}',
|
||||
title: '{% trans "Receive Purchase Order Items" %}',
|
||||
confirmMessage: '{% jstrans "Confirm receipt of items" %}',
|
||||
title: '{% jstrans "Receive Purchase Order Items" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
|
||||
// Run initialization routines for each line in the form
|
||||
@@ -1395,11 +1395,11 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
let pk = btn.attr('pk');
|
||||
|
||||
// Scan to see if the barcode matches an existing StockItem
|
||||
barcodeDialog('{% trans "Scan Item Barcode" %}', {
|
||||
details: '{% trans "Scan barcode on incoming item (must not match any existing stock items)" %}',
|
||||
barcodeDialog('{% jstrans "Scan Item Barcode" %}', {
|
||||
details: '{% jstrans "Scan barcode on incoming item (must not match any existing stock items)" %}',
|
||||
onScan: function(response, barcode_options) {
|
||||
// A 'success' result means that the barcode matches something existing in the database
|
||||
showBarcodeMessage(barcode_options.modal, '{% trans "Barcode matches existing item" %}');
|
||||
showBarcodeMessage(barcode_options.modal, '{% jstrans "Barcode matches existing item" %}');
|
||||
},
|
||||
onError400: function(response, barcode_options) {
|
||||
if (response.barcode_data && response.barcode_hash) {
|
||||
@@ -1410,7 +1410,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
$(opts.modal).find(`#button-row-remove-barcode-${pk}`).show();
|
||||
updateFieldValue(`items_barcode_${pk}`, response.barcode_data, {}, opts);
|
||||
} else {
|
||||
showBarcodeMessage(barcode_options.modal, '{% trans "Invalid barcode data" %}');
|
||||
showBarcodeMessage(barcode_options.modal, '{% jstrans "Invalid barcode data" %}');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1645,7 +1645,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
showCustomView: display_mode == 'calendar',
|
||||
search: display_mode != 'calendar',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No purchase orders found" %}';
|
||||
return '{% jstrans "No purchase orders found" %}';
|
||||
},
|
||||
buttons: constructOrderTableButtons({
|
||||
prefix: 'purchaseorder',
|
||||
@@ -1664,7 +1664,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Purchase Order" %}',
|
||||
title: '{% jstrans "Purchase Order" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
@@ -1672,7 +1672,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
var html = renderLink(value, `/order/purchase-order/${row.pk}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% jstrans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -1680,7 +1680,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'supplier_detail',
|
||||
title: '{% trans "Supplier" %}',
|
||||
title: '{% jstrans "Supplier" %}',
|
||||
sortable: true,
|
||||
sortName: 'supplier__name',
|
||||
formatter: function(value, row) {
|
||||
@@ -1693,15 +1693,15 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'supplier_reference',
|
||||
title: '{% trans "Supplier Reference" %}',
|
||||
title: '{% jstrans "Supplier Reference" %}',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'project_code',
|
||||
title: '{% trans "Project Code" %}',
|
||||
title: '{% jstrans "Project Code" %}',
|
||||
switchable: global_settings.PROJECT_CODES_ENABLED,
|
||||
visible: global_settings.PROJECT_CODES_ENABLED,
|
||||
sortable: true,
|
||||
@@ -1713,7 +1713,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
title: '{% jstrans "Status" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1722,7 +1722,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Date" %}',
|
||||
title: '{% jstrans "Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
@@ -1730,7 +1730,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
title: '{% jstrans "Target Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
@@ -1738,12 +1738,12 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'line_items',
|
||||
title: '{% trans "Items" %}',
|
||||
title: '{% jstrans "Items" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'total_price',
|
||||
title: '{% trans "Total Cost" %}',
|
||||
title: '{% jstrans "Total Cost" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1754,7 +1754,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'responsible',
|
||||
title: '{% trans "Responsible" %}',
|
||||
title: '{% jstrans "Responsible" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1834,16 +1834,16 @@ function deletePurchaseOrderLineItems(items, options={}) {
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected Line items will be deleted" %}
|
||||
{% jstrans "All selected Line items will be deleted" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Description" %}</th>
|
||||
<th>{% trans "SKU" %}</th>
|
||||
<th>{% trans "MPN" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Description" %}</th>
|
||||
<th>{% jstrans "SKU" %}</th>
|
||||
<th>{% jstrans "MPN" %}</th>
|
||||
<th>{% jstrans "Quantity" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>
|
||||
@@ -1852,7 +1852,7 @@ function deletePurchaseOrderLineItems(items, options={}) {
|
||||
constructForm('{% url "api-po-line-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete selected Line items?" %}',
|
||||
title: '{% jstrans "Delete selected Line items?" %}',
|
||||
form_data: {
|
||||
items: ids,
|
||||
},
|
||||
@@ -1907,7 +1907,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
data: data,
|
||||
title: '{% trans "Duplicate Line Item" %}',
|
||||
title: '{% jstrans "Duplicate Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
}
|
||||
@@ -1922,7 +1922,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
|
||||
constructForm(`{% url "api-po-line-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Line Item" %}',
|
||||
title: '{% jstrans "Edit Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -1933,7 +1933,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
|
||||
constructForm(`{% url "api-po-line-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Line Item" %}',
|
||||
title: '{% jstrans "Delete Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -1981,7 +1981,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
name: 'purchaseorderlines',
|
||||
sidePagination: 'server',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No line items found" %}';
|
||||
return '{% jstrans "No line items found" %}';
|
||||
},
|
||||
queryParams: filters,
|
||||
original: options.params,
|
||||
@@ -1998,7 +1998,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
field: 'part',
|
||||
sortable: true,
|
||||
sortName: 'part_name',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
switchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
if (row.part_detail) {
|
||||
@@ -2008,18 +2008,18 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
}
|
||||
},
|
||||
footerFormatter: function() {
|
||||
return '{% trans "Total" %}';
|
||||
return '{% jstrans "Total" %}';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'part_detail.description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
sortName: 'SKU',
|
||||
field: 'supplier_part_detail.SKU',
|
||||
title: '{% trans "SKU" %}',
|
||||
title: '{% jstrans "SKU" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value) {
|
||||
return renderClipboard(renderLink(value, `/supplier-part/${row.part}/`));
|
||||
@@ -2031,7 +2031,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
{
|
||||
sortable: false,
|
||||
field: 'supplier_part_detail.link',
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value) {
|
||||
return renderLink(value, value, {external: true});
|
||||
@@ -2044,7 +2044,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
sortable: true,
|
||||
sortName: 'MPN',
|
||||
field: 'supplier_part_detail.manufacturer_part_detail.MPN',
|
||||
title: '{% trans "MPN" %}',
|
||||
title: '{% jstrans "MPN" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (row.supplier_part_detail && row.supplier_part_detail.manufacturer_part) {
|
||||
return renderClipboard(renderLink(value, `/manufacturer-part/${row.supplier_part_detail.manufacturer_part}/`));
|
||||
@@ -2056,13 +2056,13 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
title: '{% jstrans "Reference" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
formatter: function(value, row) {
|
||||
let units = '';
|
||||
|
||||
@@ -2075,7 +2075,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
if (row.supplier_part_detail && row.supplier_part_detail.pack_quantity_native != 1.0) {
|
||||
let pack_quantity = row.supplier_part_detail.pack_quantity;
|
||||
let total = value * row.supplier_part_detail.pack_quantity_native;
|
||||
data += `<span class='fas fa-info-circle icon-blue float-right' title='{% trans "Pack Quantity" %}: ${pack_quantity} - {% trans "Total Quantity" %}: ${total}${units}'></span>`;
|
||||
data += `<span class='fas fa-info-circle icon-blue float-right' title='{% jstrans "Pack Quantity" %}: ${pack_quantity} - {% jstrans "Total Quantity" %}: ${total}${units}'></span>`;
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -2092,7 +2092,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
field: 'supplier_part_detail.pack_quantity',
|
||||
title: '{% trans "Pack Quantity" %}',
|
||||
title: '{% jstrans "Pack Quantity" %}',
|
||||
formatter: function(value, row) {
|
||||
var units = row.part_detail.units;
|
||||
|
||||
@@ -2106,7 +2106,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'purchase_price',
|
||||
title: '{% trans "Unit Price" %}',
|
||||
title: '{% jstrans "Unit Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.purchase_price, {
|
||||
currency: row.purchase_price_currency,
|
||||
@@ -2116,7 +2116,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
{
|
||||
field: 'total_price',
|
||||
sortable: true,
|
||||
title: '{% trans "Total Price" %}',
|
||||
title: '{% jstrans "Total Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.purchase_price * row.quantity, {
|
||||
currency: row.purchase_price_currency
|
||||
@@ -2138,13 +2138,13 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
sortable: true,
|
||||
field: 'target_date',
|
||||
switchable: true,
|
||||
title: '{% trans "Target Date" %}',
|
||||
title: '{% jstrans "Target Date" %}',
|
||||
formatter: function(value, row) {
|
||||
if (row.target_date) {
|
||||
var html = renderDate(row.target_date);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "This line item is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% jstrans "This line item is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -2160,7 +2160,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
sortable: false,
|
||||
field: 'received',
|
||||
switchable: false,
|
||||
title: '{% trans "Received" %}',
|
||||
title: '{% jstrans "Received" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return makeProgressBar(row.received, row.quantity, {
|
||||
id: `order-line-progress-${row.pk}`,
|
||||
@@ -2180,7 +2180,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'destination',
|
||||
title: '{% trans "Destination" %}',
|
||||
title: '{% jstrans "Destination" %}',
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
return renderLink(row.destination_detail.pathstring, `/stock/location/${value}/`);
|
||||
@@ -2191,11 +2191,11 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
title: '{% trans "Notes" %}',
|
||||
title: '{% jstrans "Notes" %}',
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
@@ -2211,13 +2211,13 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
let pk = row.pk;
|
||||
|
||||
if (options.allow_receive && row.received < row.quantity) {
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-line-receive', pk, '{% trans "Receive line item" %}');
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-line-receive', pk, '{% jstrans "Receive line item" %}');
|
||||
}
|
||||
|
||||
if (options.allow_edit) {
|
||||
buttons += makeCopyButton('button-line-duplicate', pk, '{% trans "Duplicate line item" %}');
|
||||
buttons += makeEditButton('button-line-edit', pk, '{% trans "Edit line item" %}');
|
||||
buttons += makeDeleteButton('button-line-delete', pk, '{% trans "Delete line item" %}');
|
||||
buttons += makeCopyButton('button-line-duplicate', pk, '{% jstrans "Duplicate line item" %}');
|
||||
buttons += makeEditButton('button-line-edit', pk, '{% jstrans "Edit line item" %}');
|
||||
buttons += makeDeleteButton('button-line-delete', pk, '{% jstrans "Delete line item" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(buttons);
|
||||
|
||||
@@ -60,7 +60,7 @@ function selectReport(reports, items, options={}) {
|
||||
|
||||
html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
${items.length} {% trans "items selected" %}
|
||||
${items.length} {% jstrans "items selected" %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ function selectReport(reports, items, options={}) {
|
||||
<form method='post' action='' class='js-modal-form' enctype='multipart/form-data'>
|
||||
<div class='form-group'>
|
||||
<label class='control-label requiredField' for='id_report'>
|
||||
{% trans "Select Report Template" %}
|
||||
{% jstrans "Select Report Template" %}
|
||||
</label>
|
||||
<div class='controls'>
|
||||
<select id='id_report' class='select form-control name='report'>
|
||||
@@ -83,7 +83,7 @@ function selectReport(reports, items, options={}) {
|
||||
});
|
||||
|
||||
modalEnable(modal, true);
|
||||
modalSetTitle(modal, '{% trans "Select Test Report Template" %}');
|
||||
modalSetTitle(modal, '{% jstrans "Select Test Report Template" %}');
|
||||
modalSetContent(modal, html);
|
||||
|
||||
attachSelect(modal);
|
||||
@@ -120,8 +120,8 @@ function printReports(options) {
|
||||
|
||||
if (!options.items || options.items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Items" %}',
|
||||
'{% trans "No items selected for printing" }',
|
||||
'{% jstrans "Select Items" %}',
|
||||
'{% jstrans "No items selected for printing" }',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -137,8 +137,8 @@ function printReports(options) {
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match the selected items" %}',
|
||||
'{% jstrans "No Reports Found" %}',
|
||||
'{% jstrans "No report templates found which match the selected items" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ function returnOrderFields(options={}) {
|
||||
customer: {
|
||||
icon: 'fa-user-tie',
|
||||
secondary: {
|
||||
title: '{% trans "Add Customer" %}',
|
||||
title: '{% jstrans "Add Customer" %}',
|
||||
fields: function() {
|
||||
var fields = companyFormFields();
|
||||
fields.is_customer.value = true;
|
||||
@@ -131,7 +131,7 @@ function createReturnOrder(options={}) {
|
||||
constructForm('{% url "api-return-order-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create Return Order" %}',
|
||||
title: '{% jstrans "Create Return Order" %}',
|
||||
onSuccess: function(data) {
|
||||
location.href = `/order/return-order/${data.pk}/`;
|
||||
},
|
||||
@@ -146,7 +146,7 @@ function editReturnOrder(order_id, options={}) {
|
||||
|
||||
constructForm(`{% url "api-return-order-list" %}${order_id}/`, {
|
||||
fields: returnOrderFields(options),
|
||||
title: '{% trans "Edit Return Order" %}',
|
||||
title: '{% jstrans "Edit Return Order" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -161,12 +161,12 @@ function issueReturnOrder(order_id, options={}) {
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans 'After placing this order, line items will no longer be editable.' %}
|
||||
{% jstrans 'After placing this order, line items will no longer be editable.' %}
|
||||
</div>`;
|
||||
|
||||
constructForm(`{% url "api-return-order-list" %}${order_id}/issue/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Issue Return Order" %}',
|
||||
title: '{% jstrans "Issue Return Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
@@ -183,14 +183,14 @@ function cancelReturnOrder(order_id, options={}) {
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-danger alert-block'>
|
||||
{% trans "Are you sure you wish to cancel this Return Order?" %}
|
||||
{% jstrans "Are you sure you wish to cancel this Return Order?" %}
|
||||
</div>`;
|
||||
|
||||
constructForm(
|
||||
`{% url "api-return-order-list" %}${order_id}/cancel/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Cancel Return Order" %}',
|
||||
title: '{% jstrans "Cancel Return Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
@@ -207,7 +207,7 @@ function cancelReturnOrder(order_id, options={}) {
|
||||
function completeReturnOrder(order_id, options={}) {
|
||||
let html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "Mark this order as complete?" %}
|
||||
{% jstrans "Mark this order as complete?" %}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -215,7 +215,7 @@ function completeReturnOrder(order_id, options={}) {
|
||||
`{% url "api-return-order-list" %}${order_id}/complete/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Complete Return Order" %}',
|
||||
title: '{% jstrans "Complete Return Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
@@ -263,7 +263,7 @@ function loadReturnOrderTable(table, options={}) {
|
||||
showCustomView: is_calendar,
|
||||
disablePagination: is_calendar,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No return orders found" %}';
|
||||
return '{% jstrans "No return orders found" %}';
|
||||
},
|
||||
onLoadSuccess: function() {
|
||||
// TODO
|
||||
@@ -278,12 +278,12 @@ function loadReturnOrderTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Return Order" %}',
|
||||
title: '{% jstrans "Return Order" %}',
|
||||
formatter: function(value, row) {
|
||||
let html = renderLink(value, `/order/return-order/${row.pk}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% jstrans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -293,11 +293,11 @@ function loadReturnOrderTable(table, options={}) {
|
||||
sortable: true,
|
||||
sortName: 'customer__name',
|
||||
field: 'customer_detail',
|
||||
title: '{% trans "Customer" %}',
|
||||
title: '{% jstrans "Customer" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (!row.customer_detail) {
|
||||
return '{% trans "Invalid Customer" %}';
|
||||
return '{% jstrans "Invalid Customer" %}';
|
||||
}
|
||||
|
||||
return imageHoverIcon(row.customer_detail.image) + renderLink(row.customer_detail.name, `/company/${row.customer}/sales-orders/`);
|
||||
@@ -306,16 +306,16 @@ function loadReturnOrderTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'customer_reference',
|
||||
title: '{% trans "Customer Reference" %}',
|
||||
title: '{% jstrans "Customer Reference" %}',
|
||||
},
|
||||
{
|
||||
sortable: false,
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'project_code',
|
||||
title: '{% trans "Project Code" %}',
|
||||
title: '{% jstrans "Project Code" %}',
|
||||
switchable: global_settings.PROJECT_CODES_ENABLED,
|
||||
visible: global_settings.PROJECT_CODES_ENABLED,
|
||||
sortable: true,
|
||||
@@ -328,7 +328,7 @@ function loadReturnOrderTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
title: '{% jstrans "Status" %}',
|
||||
formatter: function(value, row) {
|
||||
return returnOrderStatusDisplay(row.status);
|
||||
}
|
||||
@@ -336,7 +336,7 @@ function loadReturnOrderTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Creation Date" %}',
|
||||
title: '{% jstrans "Creation Date" %}',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
}
|
||||
@@ -344,19 +344,19 @@ function loadReturnOrderTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
title: '{% jstrans "Target Date" %}',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'line_items',
|
||||
title: '{% trans "Items" %}',
|
||||
title: '{% jstrans "Items" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'responsible',
|
||||
title: '{% trans "Responsible" %}',
|
||||
title: '{% jstrans "Responsible" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -378,7 +378,7 @@ function loadReturnOrderTable(table, options={}) {
|
||||
{
|
||||
// TODO: Add in the 'total cost' field
|
||||
field: 'total_price',
|
||||
title: '{% trans "Total Cost" %}',
|
||||
title: '{% jstrans "Total Cost" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
visible: false,
|
||||
@@ -456,7 +456,7 @@ function createReturnOrderLineItem(options={}) {
|
||||
constructForm('{% url "api-return-order-line-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Line Item" %}',
|
||||
title: '{% jstrans "Add Line Item" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -473,7 +473,7 @@ function editReturnOrderLineItem(pk, options={}) {
|
||||
|
||||
constructForm(`{% url "api-return-order-line-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Line Item" %}',
|
||||
title: '{% jstrans "Edit Line Item" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -488,8 +488,8 @@ function receiveReturnOrderItems(order_id, line_items, options={}) {
|
||||
|
||||
if (line_items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Line Items"% }',
|
||||
'{% trans "At least one line item must be selected" %}'
|
||||
'{% jstrans "Select Line Items"% }',
|
||||
'{% jstrans "At least one line item must be selected" %}'
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -503,7 +503,7 @@ function receiveReturnOrderItems(order_id, line_items, options={}) {
|
||||
let buttons = '';
|
||||
|
||||
if (line_items.length > 1) {
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% jstrans "Remove row" %}');
|
||||
}
|
||||
|
||||
buttons = wrapButtons(buttons);
|
||||
@@ -536,8 +536,8 @@ function receiveReturnOrderItems(order_id, line_items, options={}) {
|
||||
<table class='table table-striped table-condensed' id='order-receive-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Serial Number" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Serial Number" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>${table_entries}</tbody>
|
||||
@@ -558,8 +558,8 @@ function receiveReturnOrderItems(order_id, line_items, options={}) {
|
||||
}
|
||||
},
|
||||
confirm: true,
|
||||
confirmMessage: '{% trans "Confirm receipt of items" %}',
|
||||
title: '{% trans "Receive Return Order Items" %}',
|
||||
confirmMessage: '{% jstrans "Confirm receipt of items" %}',
|
||||
title: '{% jstrans "Receive Return Order Items" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
// Add callback to remove rows
|
||||
$(opts.modal).find('.button-row-remove').click(function() {
|
||||
@@ -666,7 +666,7 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
|
||||
constructForm(`{% url "api-return-order-line-list" %}${pk}/`, {
|
||||
fields: returnOrderLineItemFields(),
|
||||
title: '{% trans "Edit Line Item" %}',
|
||||
title: '{% jstrans "Edit Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -679,7 +679,7 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
|
||||
constructForm(`{% url "api-return-order-line-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Line Item" %}',
|
||||
title: '{% jstrans "Delete Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -690,7 +690,7 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
url: '{% url "api-return-order-line-list" %}',
|
||||
name: 'returnorderlineitems',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No matching line items" %}';
|
||||
return '{% jstrans "No matching line items" %}';
|
||||
},
|
||||
onPostBody: setupCallbacks,
|
||||
queryParams: filters,
|
||||
@@ -707,7 +707,7 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
field: 'part',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
formatter: function(value, row) {
|
||||
let part = row.part_detail;
|
||||
let html = thumbnailImage(part.thumbnail) + ' ';
|
||||
@@ -719,18 +719,18 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
field: 'item',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
title: '{% trans "Item" %}',
|
||||
title: '{% jstrans "Item" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderLink(`{% trans "Serial Number" %}: ${row.item_detail.serial}`, `/stock/item/${row.item}/`);
|
||||
return renderLink(`{% jstrans "Serial Number" %}: ${row.item_detail.serial}`, `/stock/item/${row.item}/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
title: '{% jstrans "Reference" %}',
|
||||
},
|
||||
{
|
||||
field: 'outcome',
|
||||
title: '{% trans "Outcome" %}',
|
||||
title: '{% jstrans "Outcome" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
return returnOrderLineItemStatusDisplay(value);
|
||||
@@ -738,7 +738,7 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '{% trans "Price" %}',
|
||||
title: '{% jstrans "Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.price, {
|
||||
currency: row.price_currency,
|
||||
@@ -748,12 +748,12 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
title: '{% jstrans "Target Date" %}',
|
||||
formatter: function(value, row) {
|
||||
let html = renderDate(value);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "This line item is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% jstrans "This line item is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -761,7 +761,7 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'received_date',
|
||||
title: '{% trans "Received" %}',
|
||||
title: '{% jstrans "Received" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
if (!value) {
|
||||
@@ -773,11 +773,11 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
title: '{% trans "Notes" %}',
|
||||
title: '{% jstrans "Notes" %}',
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
@@ -795,14 +795,14 @@ function loadReturnOrderLineItemTable(options={}) {
|
||||
if (options.allow_edit) {
|
||||
|
||||
if (options.allow_receive && !row.received_date) {
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-line-receive', pk, '{% trans "Mark item as received" %}');
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-line-receive', pk, '{% jstrans "Mark item as received" %}');
|
||||
}
|
||||
|
||||
buttons += makeEditButton('button-line-edit', pk, '{% trans "Edit line item" %}');
|
||||
buttons += makeEditButton('button-line-edit', pk, '{% jstrans "Edit line item" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
buttons += makeDeleteButton('button-line-delete', pk, '{% trans "Delete line item" %}');
|
||||
buttons += makeDeleteButton('button-line-delete', pk, '{% jstrans "Delete line item" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(buttons);
|
||||
|
||||
@@ -83,7 +83,7 @@ function salesOrderFields(options={}) {
|
||||
customer: {
|
||||
icon: 'fa-user-tie',
|
||||
secondary: {
|
||||
title: '{% trans "Add Customer" %}',
|
||||
title: '{% jstrans "Add Customer" %}',
|
||||
fields: function() {
|
||||
var fields = companyFormFields();
|
||||
fields.is_customer.value = true;
|
||||
@@ -158,7 +158,7 @@ function createSalesOrder(options={}) {
|
||||
constructForm('{% url "api-so-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create Sales Order" %}',
|
||||
title: '{% jstrans "Create Sales Order" %}',
|
||||
onSuccess: function(data) {
|
||||
location.href = `/order/sales-order/${data.pk}/`;
|
||||
},
|
||||
@@ -173,7 +173,7 @@ function editSalesOrder(order_id, options={}) {
|
||||
|
||||
constructForm(`{% url "api-so-list" %}${order_id}/`, {
|
||||
fields: salesOrderFields(options),
|
||||
title: '{% trans "Edit Sales Order" %}',
|
||||
title: '{% jstrans "Edit Sales Order" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
@@ -234,7 +234,7 @@ function createSalesOrderLineItem(options={}) {
|
||||
constructForm('{% url "api-so-line-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Line Item" %}',
|
||||
title: '{% jstrans "Add Line Item" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
},
|
||||
@@ -288,17 +288,17 @@ function completeSalesOrderShipment(shipment_id, options={}) {
|
||||
if (!allocations || allocations.length == 0) {
|
||||
html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "No stock items have been allocated to this shipment" %}
|
||||
{% jstrans "No stock items have been allocated to this shipment" %}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
html = `
|
||||
{% trans "The following stock items will be shipped" %}
|
||||
{% jstrans "The following stock items will be shipped" %}
|
||||
<table class='table table-striped table-condensed'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Stock Item" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th>{% jstrans "Stock Item" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -312,9 +312,9 @@ function completeSalesOrderShipment(shipment_id, options={}) {
|
||||
var stock = '';
|
||||
|
||||
if (allocation.serial) {
|
||||
stock = `{% trans "Serial Number" %}: ${allocation.serial}`;
|
||||
stock = `{% jstrans "Serial Number" %}: ${allocation.serial}`;
|
||||
} else {
|
||||
stock = `{% trans "Quantity" %}: ${allocation.quantity}`;
|
||||
stock = `{% jstrans "Quantity" %}: ${allocation.quantity}`;
|
||||
}
|
||||
|
||||
html += `
|
||||
@@ -333,7 +333,7 @@ function completeSalesOrderShipment(shipment_id, options={}) {
|
||||
|
||||
constructForm(`{% url "api-so-shipment-list" %}${shipment_id}/ship/`, {
|
||||
method: 'POST',
|
||||
title: `{% trans "Complete Shipment" %} ${shipment.reference}`,
|
||||
title: `{% jstrans "Complete Shipment" %} ${shipment.reference}`,
|
||||
fields: {
|
||||
shipment_date: {
|
||||
value: moment().format('YYYY-MM-DD'),
|
||||
@@ -357,7 +357,7 @@ function completeSalesOrderShipment(shipment_id, options={}) {
|
||||
},
|
||||
preFormContent: html,
|
||||
confirm: true,
|
||||
confirmMessage: '{% trans "Confirm Shipment" %}',
|
||||
confirmMessage: '{% jstrans "Confirm Shipment" %}',
|
||||
buttons: options.buttons,
|
||||
onSuccess: function(data) {
|
||||
// Reload tables
|
||||
@@ -413,11 +413,11 @@ function completePendingShipments(order_id, options={}) {
|
||||
|
||||
if (!pending_shipments.length) {
|
||||
html += `
|
||||
{% trans "No pending shipments found" %}
|
||||
{% jstrans "No pending shipments found" %}
|
||||
`;
|
||||
} else {
|
||||
html += `
|
||||
{% trans "No stock items have been allocated to pending shipments" %}
|
||||
{% jstrans "No stock items have been allocated to pending shipments" %}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ function completePendingShipments(order_id, options={}) {
|
||||
|
||||
constructForm(`{% url "api-so-shipment-list" %}0/ship/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Complete Shipments" %}',
|
||||
title: '{% jstrans "Complete Shipments" %}',
|
||||
preFormContent: html,
|
||||
onSubmit: function(fields, options) {
|
||||
handleFormSuccess(fields, options);
|
||||
@@ -449,7 +449,7 @@ function completePendingShipmentsHelper(shipments, shipment_idx, options={}) {
|
||||
buttons: [
|
||||
{
|
||||
name: 'skip',
|
||||
title: `{% trans "Skip" %}`,
|
||||
title: `{% jstrans "Skip" %}`,
|
||||
onClick: function(form_options) {
|
||||
if (form_options.modal) {
|
||||
$(form_options.modal).modal('hide');
|
||||
@@ -481,7 +481,7 @@ function completeSalesOrder(order_id, options={}) {
|
||||
`/api/order/so/${order_id}/complete/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Complete Sales Order" %}',
|
||||
title: '{% jstrans "Complete Sales Order" %}',
|
||||
confirm: true,
|
||||
fieldsFunction: function(opts) {
|
||||
var fields = {
|
||||
@@ -497,21 +497,21 @@ function completeSalesOrder(order_id, options={}) {
|
||||
preFormContent: function(opts) {
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Mark this order as complete?" %}
|
||||
{% jstrans "Mark this order as complete?" %}
|
||||
</div>`;
|
||||
|
||||
if (opts.context.pending_shipments) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "Order cannot be completed as there are incomplete shipments" %}<br>
|
||||
{% jstrans "Order cannot be completed as there are incomplete shipments" %}<br>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (!opts.context.is_complete) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "This order has line items which have not been completed." %}<br>
|
||||
{% trans "Completing this order means that the order and line items will no longer be editable." %}
|
||||
{% jstrans "This order has line items which have not been completed." %}<br>
|
||||
{% jstrans "Completing this order means that the order and line items will no longer be editable." %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -532,12 +532,12 @@ function issueSalesOrder(order_id, options={}) {
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Issue this Sales Order?" %}
|
||||
{% jstrans "Issue this Sales Order?" %}
|
||||
</div>`;
|
||||
|
||||
constructForm(`{% url "api-so-list" %}${order_id}/issue/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Issue Sales Order" %}',
|
||||
title: '{% jstrans "Issue Sales Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
@@ -556,12 +556,12 @@ function cancelSalesOrder(order_id, options={}) {
|
||||
`/api/order/so/${order_id}/cancel/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Cancel Sales Order" %}',
|
||||
title: '{% jstrans "Cancel Sales Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: function(opts) {
|
||||
var html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "Cancelling this order means that the order will no longer be editable." %}
|
||||
{% jstrans "Cancelling this order means that the order will no longer be editable." %}
|
||||
</div>`;
|
||||
|
||||
return html;
|
||||
@@ -615,7 +615,7 @@ function createSalesOrderShipment(options={}) {
|
||||
constructForm('{% url "api-so-shipment-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create New Shipment" %}',
|
||||
title: '{% jstrans "Create New Shipment" %}',
|
||||
onSuccess: function(data) {
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess(data);
|
||||
@@ -725,7 +725,7 @@ function loadSalesOrderTable(table, options) {
|
||||
showCustomView: display_mode == 'calendar',
|
||||
disablePagination: display_mode == 'calendar',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No sales orders found" %}';
|
||||
return '{% jstrans "No sales orders found" %}';
|
||||
},
|
||||
buttons: constructOrderTableButtons({
|
||||
prefix: 'salesorder',
|
||||
@@ -766,12 +766,12 @@ function loadSalesOrderTable(table, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Sales Order" %}',
|
||||
title: '{% jstrans "Sales Order" %}',
|
||||
formatter: function(value, row) {
|
||||
var html = renderLink(value, `/order/sales-order/${row.pk}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% jstrans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -781,11 +781,11 @@ function loadSalesOrderTable(table, options) {
|
||||
sortable: true,
|
||||
sortName: 'customer__name',
|
||||
field: 'customer_detail',
|
||||
title: '{% trans "Customer" %}',
|
||||
title: '{% jstrans "Customer" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (!row.customer_detail) {
|
||||
return '{% trans "Invalid Customer" %}';
|
||||
return '{% jstrans "Invalid Customer" %}';
|
||||
}
|
||||
|
||||
return imageHoverIcon(row.customer_detail.image) + renderLink(row.customer_detail.name, `/company/${row.customer}/sales-orders/`);
|
||||
@@ -794,16 +794,16 @@ function loadSalesOrderTable(table, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'customer_reference',
|
||||
title: '{% trans "Customer Reference" %}',
|
||||
title: '{% jstrans "Customer Reference" %}',
|
||||
},
|
||||
{
|
||||
sortable: false,
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'project_code',
|
||||
title: '{% trans "Project Code" %}',
|
||||
title: '{% jstrans "Project Code" %}',
|
||||
switchable: global_settings.PROJECT_CODES_ENABLED,
|
||||
visible: global_settings.PROJECT_CODES_ENABLED,
|
||||
sortable: true,
|
||||
@@ -816,7 +816,7 @@ function loadSalesOrderTable(table, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
title: '{% jstrans "Status" %}',
|
||||
formatter: function(value, row) {
|
||||
return salesOrderStatusDisplay(row.status);
|
||||
}
|
||||
@@ -824,7 +824,7 @@ function loadSalesOrderTable(table, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Creation Date" %}',
|
||||
title: '{% jstrans "Creation Date" %}',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
}
|
||||
@@ -832,7 +832,7 @@ function loadSalesOrderTable(table, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
title: '{% jstrans "Target Date" %}',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
}
|
||||
@@ -840,7 +840,7 @@ function loadSalesOrderTable(table, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'shipment_date',
|
||||
title: '{% trans "Shipment Date" %}',
|
||||
title: '{% jstrans "Shipment Date" %}',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
}
|
||||
@@ -848,11 +848,11 @@ function loadSalesOrderTable(table, options) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'line_items',
|
||||
title: '{% trans "Items" %}'
|
||||
title: '{% jstrans "Items" %}'
|
||||
},
|
||||
{
|
||||
field: 'total_price',
|
||||
title: '{% trans "Total Cost" %}',
|
||||
title: '{% jstrans "Total Cost" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -905,15 +905,15 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
|
||||
let html = '';
|
||||
|
||||
html += makeEditButton('button-shipment-edit', pk, '{% trans "Edit shipment" %}');
|
||||
html += makeEditButton('button-shipment-edit', pk, '{% jstrans "Edit shipment" %}');
|
||||
|
||||
if (!options.shipped) {
|
||||
html += makeIconButton('fa-truck icon-green', 'button-shipment-ship', pk, '{% trans "Complete shipment" %}');
|
||||
html += makeIconButton('fa-truck icon-green', 'button-shipment-ship', pk, '{% jstrans "Complete shipment" %}');
|
||||
}
|
||||
|
||||
var enable_delete = row.allocations && row.allocations.length == 0;
|
||||
|
||||
html += makeDeleteButton('button-shipment-delete', pk, '{% trans "Delete shipment" %}', {disabled: !enable_delete});
|
||||
html += makeDeleteButton('button-shipment-delete', pk, '{% jstrans "Delete shipment" %}', {disabled: !enable_delete});
|
||||
|
||||
return wrapButtons(html);
|
||||
}
|
||||
@@ -930,7 +930,7 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
|
||||
constructForm(`{% url "api-so-shipment-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Shipment" %}',
|
||||
title: '{% jstrans "Edit Shipment" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
@@ -945,7 +945,7 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-so-shipment-list" %}${pk}/`, {
|
||||
title: '{% trans "Delete Shipment" %}',
|
||||
title: '{% jstrans "Delete Shipment" %}',
|
||||
method: 'DELETE',
|
||||
refreshTable: table,
|
||||
});
|
||||
@@ -978,7 +978,7 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
}
|
||||
},
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No matching shipments found" %}';
|
||||
return '{% jstrans "No matching shipments found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
@@ -989,13 +989,13 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
{
|
||||
visible: show_so_reference,
|
||||
field: 'order_detail',
|
||||
title: '{% trans "Sales Order" %}',
|
||||
title: '{% jstrans "Sales Order" %}',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var html = renderLink(row.order_detail.reference, `/order/sales-order/${row.order}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% jstrans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -1003,12 +1003,12 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Shipment Reference" %}',
|
||||
title: '{% jstrans "Shipment Reference" %}',
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'allocations',
|
||||
title: '{% trans "Items" %}',
|
||||
title: '{% jstrans "Items" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1021,39 +1021,39 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'shipment_date',
|
||||
title: '{% trans "Shipment Date" %}',
|
||||
title: '{% jstrans "Shipment Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
return renderDate(value);
|
||||
} else {
|
||||
return '<em>{% trans "Not shipped" %}</em>';
|
||||
return '<em>{% jstrans "Not shipped" %}</em>';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'delivery_date',
|
||||
title: '{% trans "Delivery Date" %}',
|
||||
title: '{% jstrans "Delivery Date" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
return renderDate(value);
|
||||
} else {
|
||||
return '<em>{% trans "Unknown" %}</em>';
|
||||
return '<em>{% jstrans "Unknown" %}</em>';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'tracking_number',
|
||||
title: '{% trans "Tracking" %}',
|
||||
title: '{% jstrans "Tracking" %}',
|
||||
},
|
||||
{
|
||||
field: 'invoice_number',
|
||||
title: '{% trans "Invoice" %}',
|
||||
title: '{% jstrans "Invoice" %}',
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
@@ -1064,7 +1064,7 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
title: '{% trans "Notes" %}',
|
||||
title: '{% jstrans "Notes" %}',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
// TODO: Implement 'notes' field
|
||||
@@ -1106,7 +1106,7 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
makeRemoveButton(
|
||||
'button-row-remove',
|
||||
pk,
|
||||
'{% trans "Remove row" %}',
|
||||
'{% jstrans "Remove row" %}',
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1118,7 +1118,7 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
type: 'decimal',
|
||||
min_value: 0,
|
||||
value: quantity || 0,
|
||||
title: '{% trans "Specify stock allocation quantity" %}',
|
||||
title: '{% jstrans "Specify stock allocation quantity" %}',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
@@ -1168,8 +1168,8 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
|
||||
if (table_entries.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "You must select at least one part to allocate" %}',
|
||||
'{% jstrans "Select Parts" %}',
|
||||
'{% jstrans "You must select at least one part to allocate" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
@@ -1182,8 +1182,8 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
'take_from',
|
||||
{
|
||||
type: 'related field',
|
||||
label: '{% trans "Source Location" %}',
|
||||
help_text: '{% trans "Select source location (leave blank to take from all locations)" %}',
|
||||
label: '{% jstrans "Source Location" %}',
|
||||
help_text: '{% jstrans "Select source location (leave blank to take from all locations)" %}',
|
||||
required: false,
|
||||
},
|
||||
{},
|
||||
@@ -1194,9 +1194,9 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
<table class='table table-striped table-condensed' id='stock-allocation-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th style='min-width: 250px;'>{% trans "Stock Item" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th>{% jstrans "Part" %}</th>
|
||||
<th style='min-width: 250px;'>{% jstrans "Stock Item" %}</th>
|
||||
<th>{% jstrans "Quantity" %}</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -1216,7 +1216,7 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
auto_fill: true,
|
||||
secondary: {
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Shipment" %}',
|
||||
title: '{% jstrans "Add Shipment" %}',
|
||||
fields: function() {
|
||||
var ref = null;
|
||||
|
||||
@@ -1267,8 +1267,8 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
},
|
||||
preFormContent: html,
|
||||
confirm: true,
|
||||
confirmMessage: '{% trans "Confirm stock allocation" %}',
|
||||
title: '{% trans "Allocate Stock Items to Sales Order" %}',
|
||||
confirmMessage: '{% jstrans "Confirm stock allocation" %}',
|
||||
title: '{% jstrans "Allocate Stock Items to Sales Order" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
|
||||
// Initialize source location field
|
||||
@@ -1280,7 +1280,7 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
type: 'related field',
|
||||
value: options.source_location || null,
|
||||
noResults: function(query) {
|
||||
return '{% trans "No matching stock locations" %}';
|
||||
return '{% jstrans "No matching stock locations" %}';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1359,7 +1359,7 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
return filters;
|
||||
},
|
||||
noResults: function(query) {
|
||||
return '{% trans "No matching stock items" %}';
|
||||
return '{% jstrans "No matching stock items" %}';
|
||||
}
|
||||
},
|
||||
null,
|
||||
@@ -1474,7 +1474,7 @@ function loadSalesOrderAllocationTable(table, options={}) {
|
||||
paginationVAlign: 'bottom',
|
||||
original: options.params,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No sales order allocations found" %}';
|
||||
return '{% jstrans "No sales order allocations found" %}';
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
@@ -1485,7 +1485,7 @@ function loadSalesOrderAllocationTable(table, options={}) {
|
||||
{
|
||||
field: 'order',
|
||||
switchable: false,
|
||||
title: '{% trans "Order" %}',
|
||||
title: '{% jstrans "Order" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
var ref = `${row.order_detail.reference}`;
|
||||
@@ -1496,37 +1496,37 @@ function loadSalesOrderAllocationTable(table, options={}) {
|
||||
{
|
||||
field: 'item',
|
||||
switchable: false,
|
||||
title: '{% trans "Stock Item" %}',
|
||||
title: '{% jstrans "Stock Item" %}',
|
||||
formatter: function(value, row) {
|
||||
// Render a link to the particular stock item
|
||||
|
||||
var link = `/stock/item/${row.item}/`;
|
||||
var text = `{% trans "Stock Item" %} ${row.item}`;
|
||||
var text = `{% jstrans "Stock Item" %} ${row.item}`;
|
||||
|
||||
return renderLink(text, link);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
title: '{% trans "Location" %}',
|
||||
title: '{% jstrans "Location" %}',
|
||||
formatter: function(value, row) {
|
||||
return locationDetail(row.item_detail, true);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'shipment_date',
|
||||
title: '{% trans "Shipped" %}',
|
||||
title: '{% jstrans "Shipped" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
return renderDate(value);
|
||||
} else {
|
||||
return `<em>{% trans "Not shipped" %}</em>`;
|
||||
return `<em>{% jstrans "Not shipped" %}</em>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1566,7 +1566,7 @@ function showAllocationSubTable(index, row, element, options) {
|
||||
fields: {
|
||||
quantity: {},
|
||||
},
|
||||
title: '{% trans "Edit Stock Allocation" %}',
|
||||
title: '{% jstrans "Edit Stock Allocation" %}',
|
||||
refreshTable: options.table,
|
||||
},
|
||||
);
|
||||
@@ -1580,8 +1580,8 @@ function showAllocationSubTable(index, row, element, options) {
|
||||
`/api/order/so-allocation/${pk}/`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
confirmMessage: '{% trans "Confirm Delete Operation" %}',
|
||||
title: '{% trans "Delete Stock Allocation" %}',
|
||||
confirmMessage: '{% jstrans "Confirm Delete Operation" %}',
|
||||
title: '{% jstrans "Delete Stock Allocation" %}',
|
||||
refreshTable: options.table,
|
||||
}
|
||||
);
|
||||
@@ -1595,20 +1595,20 @@ function showAllocationSubTable(index, row, element, options) {
|
||||
columns: [
|
||||
{
|
||||
field: 'part_detail',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
formatter: function(part, row) {
|
||||
return imageHoverIcon(part.thumbnail) + renderLink(part.full_name, `/part/${part.pk}/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'allocated',
|
||||
title: '{% trans "Stock Item" %}',
|
||||
title: '{% jstrans "Stock Item" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
let item = row.item_detail;
|
||||
let text = `{% trans "Quantity" %}: ${row.quantity}`;
|
||||
let text = `{% jstrans "Quantity" %}: ${row.quantity}`;
|
||||
|
||||
if (item && item.serial != null && row.quantity == 1) {
|
||||
text = `{% trans "Serial Number" %}: ${item.serial}`;
|
||||
text = `{% jstrans "Serial Number" %}: ${item.serial}`;
|
||||
}
|
||||
|
||||
return renderLink(text, `/stock/item/${row.item}/`);
|
||||
@@ -1616,19 +1616,19 @@ function showAllocationSubTable(index, row, element, options) {
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
title: '{% trans "Location" %}',
|
||||
title: '{% jstrans "Location" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
if (row.shipment_date) {
|
||||
return `<em>{% trans "Shipped to customer" %} - ${row.shipment_date}</em>`;
|
||||
return `<em>{% jstrans "Shipped to customer" %} - ${row.shipment_date}</em>`;
|
||||
} else if (row.location) {
|
||||
// Location specified
|
||||
return renderLink(
|
||||
row.location_detail.pathstring || '{% trans "Location" %}',
|
||||
row.location_detail.pathstring || '{% jstrans "Location" %}',
|
||||
`/stock/location/${row.location}/`
|
||||
);
|
||||
} else {
|
||||
return `<em>{% trans "Stock location not specified" %}</em>`;
|
||||
return `<em>{% jstrans "Stock location not specified" %}</em>`;
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -1641,10 +1641,10 @@ function showAllocationSubTable(index, row, element, options) {
|
||||
let pk = row.pk;
|
||||
|
||||
if (row.shipment_date) {
|
||||
html += `<span class='badge bg-success badge-right'>{% trans "Shipped" %}</span>`;
|
||||
html += `<span class='badge bg-success badge-right'>{% jstrans "Shipped" %}</span>`;
|
||||
} else {
|
||||
html += makeEditButton('button-allocation-edit', pk, '{% trans "Edit stock allocation" %}');
|
||||
html += makeDeleteButton('button-allocation-delete', pk, '{% trans "Delete stock allocation" %}');
|
||||
html += makeEditButton('button-allocation-edit', pk, '{% jstrans "Edit stock allocation" %}');
|
||||
html += makeDeleteButton('button-allocation-delete', pk, '{% jstrans "Delete stock allocation" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(html);
|
||||
@@ -1689,13 +1689,13 @@ function showFulfilledSubTable(index, row, element, options) {
|
||||
},
|
||||
{
|
||||
field: 'stock',
|
||||
title: '{% trans "Stock Item" %}',
|
||||
title: '{% jstrans "Stock Item" %}',
|
||||
formatter: function(value, row) {
|
||||
var text = '';
|
||||
if (row.serial && row.quantity == 1) {
|
||||
text = `{% trans "Serial Number" %}: ${row.serial}`;
|
||||
text = `{% jstrans "Serial Number" %}: ${row.serial}`;
|
||||
} else {
|
||||
text = `{% trans "Quantity" %}: ${row.quantity}`;
|
||||
text = `{% jstrans "Quantity" %}: ${row.quantity}`;
|
||||
}
|
||||
|
||||
return renderLink(text, `/stock/item/${row.pk}/`);
|
||||
@@ -1703,11 +1703,11 @@ function showFulfilledSubTable(index, row, element, options) {
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
title: '{% trans "Location" %}',
|
||||
title: '{% jstrans "Location" %}',
|
||||
formatter: function(value, row) {
|
||||
if (row.customer) {
|
||||
return renderLink(
|
||||
'{% trans "Shipped to customer" %}',
|
||||
'{% jstrans "Shipped to customer" %}',
|
||||
`/company/${row.customer}/`
|
||||
);
|
||||
} else if (row.location && row.location_detail) {
|
||||
@@ -1716,7 +1716,7 @@ function showFulfilledSubTable(index, row, element, options) {
|
||||
`/stock/location/${row.location}`,
|
||||
);
|
||||
} else {
|
||||
return `<em>{% trans "Stock location not specified" %}</em>`;
|
||||
return `<em>{% jstrans "Stock location not specified" %}</em>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1793,7 +1793,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
sortable: true,
|
||||
sortName: 'part_detail.name',
|
||||
field: 'part',
|
||||
title: '{% trans "Part" %}',
|
||||
title: '{% jstrans "Part" %}',
|
||||
switchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
if (row.part_detail) {
|
||||
@@ -1803,25 +1803,25 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
}
|
||||
},
|
||||
footerFormatter: function() {
|
||||
return '{% trans "Total" %}';
|
||||
return '{% jstrans "Total" %}';
|
||||
},
|
||||
},
|
||||
{
|
||||
sortable: false,
|
||||
field: 'part_detail.description',
|
||||
title: '{% trans "Description" %}',
|
||||
title: '{% jstrans "Description" %}',
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
title: '{% jstrans "Reference" %}',
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
title: '{% jstrans "Quantity" %}',
|
||||
footerFormatter: function(data) {
|
||||
return data.map(function(row) {
|
||||
return +row['quantity'];
|
||||
@@ -1834,7 +1834,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
{
|
||||
sortable: true,
|
||||
field: 'sale_price',
|
||||
title: '{% trans "Unit Price" %}',
|
||||
title: '{% jstrans "Unit Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.sale_price, {
|
||||
currency: row.sale_price_currency
|
||||
@@ -1844,7 +1844,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
{
|
||||
field: 'total_price',
|
||||
sortable: true,
|
||||
title: '{% trans "Total Price" %}',
|
||||
title: '{% jstrans "Total Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.sale_price * row.quantity, {
|
||||
currency: row.sale_price_currency,
|
||||
@@ -1864,7 +1864,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
},
|
||||
{
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
title: '{% jstrans "Target Date" %}',
|
||||
sortable: true,
|
||||
switchable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1872,7 +1872,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
var html = renderDate(row.target_date);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times', '{% trans "This line item is overdue" %}');
|
||||
html += makeIconBadge('fa-calendar-times', '{% jstrans "This line item is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
@@ -1890,7 +1890,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
columns.push(
|
||||
{
|
||||
field: 'stock',
|
||||
title: '{% trans "Available Stock" %}',
|
||||
title: '{% jstrans "Available Stock" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
let available = row.available_stock + row.available_variant_stock;
|
||||
@@ -1904,17 +1904,17 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
html = renderLink(available, url);
|
||||
|
||||
if (row.available_variant_stock && row.available_variant_stock > 0) {
|
||||
html += makeIconBadge('fa-info-circle icon-blue', '{% trans "Includes variant stock" %}');
|
||||
html += makeIconBadge('fa-info-circle icon-blue', '{% jstrans "Includes variant stock" %}');
|
||||
}
|
||||
} else {
|
||||
html += `<span class='badge rounded-pill bg-danger'>{% trans "No Stock Available" %}</span>`;
|
||||
html += `<span class='badge rounded-pill bg-danger'>{% jstrans "No Stock Available" %}</span>`;
|
||||
}
|
||||
|
||||
if (required > 0) {
|
||||
if (available >= required) {
|
||||
html += makeIconBadge('fa-check-circle icon-green', '{% trans "Sufficient stock available" %}');
|
||||
html += makeIconBadge('fa-check-circle icon-green', '{% jstrans "Sufficient stock available" %}');
|
||||
} else {
|
||||
html += makeIconBadge('fa-times-circle icon-red', '{% trans "Insufficient stock available" %}');
|
||||
html += makeIconBadge('fa-times-circle icon-red', '{% jstrans "Insufficient stock available" %}');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1926,7 +1926,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
columns.push(
|
||||
{
|
||||
field: 'allocated',
|
||||
title: '{% trans "Allocated" %}',
|
||||
title: '{% jstrans "Allocated" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
@@ -1954,7 +1954,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
columns.push({
|
||||
field: 'shipped',
|
||||
title: '{% trans "Shipped" %}',
|
||||
title: '{% jstrans "Shipped" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
@@ -1979,12 +1979,12 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
columns.push({
|
||||
field: 'notes',
|
||||
title: '{% trans "Notes" %}',
|
||||
title: '{% jstrans "Notes" %}',
|
||||
});
|
||||
|
||||
columns.push({
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
title: '{% jstrans "Link" %}',
|
||||
formatter: function(value) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
@@ -2005,37 +2005,37 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
if (options.allow_edit && (row.shipped < row.quantity)) {
|
||||
if (part.trackable) {
|
||||
buttons += makeIconButton('fa-hashtag icon-green', 'button-add-by-sn', pk, '{% trans "Allocate serial numbers" %}');
|
||||
buttons += makeIconButton('fa-hashtag icon-green', 'button-add-by-sn', pk, '{% jstrans "Allocate serial numbers" %}');
|
||||
}
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-add', pk, '{% trans "Allocate stock" %}');
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-add', pk, '{% jstrans "Allocate stock" %}');
|
||||
if (part.purchaseable) {
|
||||
buttons += makeIconButton('fa-shopping-cart', 'button-buy', row.part, '{% trans "Purchase stock" %}');
|
||||
buttons += makeIconButton('fa-shopping-cart', 'button-buy', row.part, '{% jstrans "Purchase stock" %}');
|
||||
}
|
||||
|
||||
if (part.assembly) {
|
||||
buttons += makeIconButton('fa-tools', 'button-build', row.part, '{% trans "Build stock" %}');
|
||||
buttons += makeIconButton('fa-tools', 'button-build', row.part, '{% jstrans "Build stock" %}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buttons += makeIconButton('fa-dollar-sign icon-green', 'button-price', pk, '{% trans "Calculate price" %}');
|
||||
buttons += makeIconButton('fa-dollar-sign icon-green', 'button-price', pk, '{% jstrans "Calculate price" %}');
|
||||
|
||||
if (options.allow_edit) {
|
||||
buttons += makeCopyButton('button-duplicate', pk, '{% trans "Duplicate line item" %}');
|
||||
buttons += makeEditButton('button-edit', pk, '{% trans "Edit line item" %}');
|
||||
buttons += makeCopyButton('button-duplicate', pk, '{% jstrans "Duplicate line item" %}');
|
||||
buttons += makeEditButton('button-edit', pk, '{% jstrans "Edit line item" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
var delete_disabled = false;
|
||||
|
||||
var title = '{% trans "Delete line item" %}';
|
||||
var title = '{% jstrans "Delete line item" %}';
|
||||
|
||||
if (row.shipped) {
|
||||
delete_disabled = true;
|
||||
title = '{% trans "Cannot be deleted as items have been shipped" %}';
|
||||
title = '{% jstrans "Cannot be deleted as items have been shipped" %}';
|
||||
} else if (row.allocated) {
|
||||
delete_disabled = true;
|
||||
title = '{% trans "Cannot be deleted as items have been allocated" %}';
|
||||
title = '{% jstrans "Cannot be deleted as items have been allocated" %}';
|
||||
}
|
||||
|
||||
// Prevent deletion of the line item if items have been allocated or shipped!
|
||||
@@ -2067,7 +2067,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
data: data,
|
||||
title: '{% trans "Duplicate Line Item" %}',
|
||||
title: '{% jstrans "Duplicate Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
}
|
||||
@@ -2080,7 +2080,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
constructForm(`{% url "api-so-line-list" %}${pk}/`, {
|
||||
fields: soLineItemFields(),
|
||||
title: '{% trans "Edit Line Item" %}',
|
||||
title: '{% jstrans "Edit Line Item" %}',
|
||||
onSuccess: reloadTable,
|
||||
});
|
||||
});
|
||||
@@ -2091,7 +2091,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
constructForm(`{% url "api-so-line-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Line Item" %}',
|
||||
title: '{% jstrans "Delete Line Item" %}',
|
||||
onSuccess: reloadTable,
|
||||
});
|
||||
});
|
||||
@@ -2106,7 +2106,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
constructForm(`{% url "api-so-list" %}${options.order}/allocate-serials/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Allocate Serial Numbers" %}',
|
||||
title: '{% jstrans "Allocate Serial Numbers" %}',
|
||||
fields: {
|
||||
line_item: {
|
||||
value: pk,
|
||||
@@ -2205,7 +2205,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
launchModalForm(
|
||||
'{% url "line-pricing" %}',
|
||||
{
|
||||
submit_text: '{% trans "Calculate price" %}',
|
||||
submit_text: '{% jstrans "Calculate price" %}',
|
||||
data: {
|
||||
line_item: pk,
|
||||
quantity: row.quantity,
|
||||
@@ -2213,7 +2213,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
buttons: [
|
||||
{
|
||||
name: 'update_price',
|
||||
title: '{% trans "Update Unit Price" %}'
|
||||
title: '{% jstrans "Update Unit Price" %}'
|
||||
},
|
||||
],
|
||||
success: reloadTable,
|
||||
@@ -2227,7 +2227,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
name: 'salesorderlineitems',
|
||||
sidePagination: 'client',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No matching line items" %}';
|
||||
return '{% jstrans "No matching line items" %}';
|
||||
},
|
||||
queryParams: filters,
|
||||
original: options.params,
|
||||
|
||||
@@ -127,7 +127,7 @@ function updateSearch() {
|
||||
filters.active = true;
|
||||
}
|
||||
|
||||
addSearchQuery('part', '{% trans "Parts" %}', filters);
|
||||
addSearchQuery('part', '{% jstrans "Parts" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('part') && checkPermission('purchase_order')) {
|
||||
@@ -144,18 +144,18 @@ function updateSearch() {
|
||||
}
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_SHOW_SUPPLIER_PARTS) {
|
||||
addSearchQuery('supplierpart', '{% trans "Supplier Parts" %}', filters);
|
||||
addSearchQuery('supplierpart', '{% jstrans "Supplier Parts" %}', filters);
|
||||
}
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_SHOW_MANUFACTURER_PARTS) {
|
||||
addSearchQuery('manufacturerpart', '{% trans "Manufacturer Parts" %}', filters);
|
||||
addSearchQuery('manufacturerpart', '{% jstrans "Manufacturer Parts" %}', filters);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkPermission('part_category') && user_settings.SEARCH_PREVIEW_SHOW_CATEGORIES) {
|
||||
let filters = {};
|
||||
|
||||
addSearchQuery('partcategory', '{% trans "Part Categories" %}', filters);
|
||||
addSearchQuery('partcategory', '{% jstrans "Part Categories" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('stock') && user_settings.SEARCH_PREVIEW_SHOW_STOCK) {
|
||||
@@ -169,13 +169,13 @@ function updateSearch() {
|
||||
filters.in_stock = true;
|
||||
}
|
||||
|
||||
addSearchQuery('stockitem', '{% trans "Stock Items" %}', filters);
|
||||
addSearchQuery('stockitem', '{% jstrans "Stock Items" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('stock_location') && user_settings.SEARCH_PREVIEW_SHOW_LOCATIONS) {
|
||||
let filters = {};
|
||||
|
||||
addSearchQuery('stocklocation', '{% trans "Stock Locations" %}', filters);
|
||||
addSearchQuery('stocklocation', '{% jstrans "Stock Locations" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('build') && user_settings.SEARCH_PREVIEW_SHOW_BUILD_ORDERS) {
|
||||
@@ -183,13 +183,13 @@ function updateSearch() {
|
||||
part_detail: true
|
||||
};
|
||||
|
||||
addSearchQuery('build', '{% trans "Build Orders" %}', filters);
|
||||
addSearchQuery('build', '{% jstrans "Build Orders" %}', filters);
|
||||
}
|
||||
|
||||
if ((checkPermission('sales_order') || checkPermission('purchase_order')) && user_settings.SEARCH_PREVIEW_SHOW_COMPANIES) {
|
||||
let filters = {};
|
||||
|
||||
addSearchQuery('company', '{% trans "Companies" %}', filters);
|
||||
addSearchQuery('company', '{% jstrans "Companies" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('purchase_order') && user_settings.SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS) {
|
||||
@@ -202,7 +202,7 @@ function updateSearch() {
|
||||
filters.outstanding = true;
|
||||
}
|
||||
|
||||
addSearchQuery('purchaseorder', '{% trans "Purchase Orders" %}', filters);
|
||||
addSearchQuery('purchaseorder', '{% jstrans "Purchase Orders" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('sales_order') && user_settings.SEARCH_PREVIEW_SHOW_SALES_ORDERS) {
|
||||
@@ -216,7 +216,7 @@ function updateSearch() {
|
||||
filters.outstanding = true;
|
||||
}
|
||||
|
||||
addSearchQuery('salesorder', '{% trans "Sales Orders" %}', filters);
|
||||
addSearchQuery('salesorder', '{% jstrans "Sales Orders" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('return_order') && user_settings.SEARCH_PREVIEW_SHOW_RETURN_ORDERS) {
|
||||
@@ -229,14 +229,14 @@ function updateSearch() {
|
||||
filters.outstanding = true;
|
||||
}
|
||||
|
||||
addSearchQuery('returnorder', '{% trans "Return Orders" %}', filters);
|
||||
addSearchQuery('returnorder', '{% jstrans "Return Orders" %}', filters);
|
||||
}
|
||||
|
||||
let ctx = $('#offcanvas-search').find('#search-context');
|
||||
|
||||
ctx.html(`
|
||||
<div class='alert alert-block alert-secondary'>
|
||||
<span class='fas fa-spinner fa-spin'></span> <em>{% trans "Searching" %}</em>
|
||||
<span class='fas fa-spinner fa-spin'></span> <em>{% jstrans "Searching" %}</em>
|
||||
</div>
|
||||
`);
|
||||
|
||||
@@ -267,7 +267,7 @@ function updateSearch() {
|
||||
} else {
|
||||
ctx.html(`
|
||||
<div class='alert alert-block alert-warning'>
|
||||
<span class='fas fa-exclamation-circle'></span> <em>{% trans "No results" %}</em>
|
||||
<span class='fas fa-exclamation-circle'></span> <em>{% jstrans "No results" %}</em>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
@@ -289,7 +289,7 @@ function clearSearchResults() {
|
||||
|
||||
panel.find('#search-context').html(`
|
||||
<div class='alert alert-block alert-info'>
|
||||
<span class='fas fa-search'></span> <em>{% trans "Enter search query" %}</em>
|
||||
<span class='fas fa-search'></span> <em>{% jstrans "Enter search query" %}</em>
|
||||
</div>
|
||||
`);
|
||||
|
||||
@@ -339,7 +339,7 @@ function addSearchResults(results, resultType, resultCount) {
|
||||
let renderer = resultType.renderer;
|
||||
let renderParams = resultType.renderParams;
|
||||
|
||||
let resultText = resultCount == 1 ? '{% trans "result" %}' : '{% trans "results" %}';
|
||||
let resultText = resultCount == 1 ? '{% jstrans "result" %}' : '{% jstrans "results" %}';
|
||||
|
||||
// Add the result group to the panel
|
||||
panel.find('#search-results').append(`
|
||||
@@ -349,10 +349,10 @@ function addSearchResults(results, resultType, resultCount) {
|
||||
<h5>${title}</h5><span class='float-right'><em><small> - ${resultCount} ${resultText}</small></em></span>
|
||||
<span class='flex' style='flex-grow: 1;'></span>
|
||||
<div class='search-result-group-buttons btn-group float-right' role='group'>
|
||||
<button class='btn btn-outline-secondary' id='hide-results-${key}' title='{% trans "Minimize results" %}'>
|
||||
<button class='btn btn-outline-secondary' id='hide-results-${key}' title='{% jstrans "Minimize results" %}'>
|
||||
<span class='fas fa-chevron-up'></span>
|
||||
</button>
|
||||
<button class='btn btn-outline-secondary' id='remove-results-${key}' title='{% trans "Remove results" %}'>
|
||||
<button class='btn btn-outline-secondary' id='remove-results-${key}' title='{% jstrans "Remove results" %}'>
|
||||
<span class='fas fa-times icon-red'></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@
|
||||
// Construct a dynamic API filter for the "issued by" field
|
||||
function constructIssuedByFilter() {
|
||||
return {
|
||||
title: '{% trans "Issued By" %}',
|
||||
title: '{% jstrans "Issued By" %}',
|
||||
options: function() {
|
||||
let users = {};
|
||||
|
||||
@@ -45,7 +45,7 @@ function constructIssuedByFilter() {
|
||||
// Construct a dynamic API filter for the "project" field
|
||||
function constructProjectCodeFilter() {
|
||||
return {
|
||||
title: '{% trans "Project Code" %}',
|
||||
title: '{% jstrans "Project Code" %}',
|
||||
options: function() {
|
||||
let project_codes = {};
|
||||
|
||||
@@ -71,7 +71,7 @@ function constructProjectCodeFilter() {
|
||||
function constructHasProjectCodeFilter() {
|
||||
return {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has project code" %}',
|
||||
title: '{% jstrans "Has project code" %}',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,20 +86,20 @@ function getAttachmentFilters() {
|
||||
function getReturnOrderFilters() {
|
||||
var filters = {
|
||||
status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
title: '{% jstrans "Order status" %}',
|
||||
options: returnOrderCodes
|
||||
},
|
||||
outstanding: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
title: '{% jstrans "Outstanding" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
title: '{% jstrans "Overdue" %}',
|
||||
},
|
||||
assigned_to_me: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assigned to me" %}',
|
||||
title: '{% jstrans "Assigned to me" %}',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -117,10 +117,10 @@ function getReturnOrderLineItemFilters() {
|
||||
return {
|
||||
received: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Received" %}',
|
||||
title: '{% jstrans "Received" %}',
|
||||
},
|
||||
outcome: {
|
||||
title: '{% trans "Outcome" %}',
|
||||
title: '{% jstrans "Outcome" %}',
|
||||
options: returnOrderLineItemCodes,
|
||||
}
|
||||
};
|
||||
@@ -132,19 +132,19 @@ function getVariantsTableFilters() {
|
||||
return {
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
title: '{% jstrans "Active" %}',
|
||||
},
|
||||
template: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
title: '{% jstrans "Template" %}',
|
||||
},
|
||||
virtual: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Virtual" %}',
|
||||
title: '{% jstrans "Virtual" %}',
|
||||
},
|
||||
trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
title: '{% jstrans "Trackable" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -155,43 +155,43 @@ function getBOMTableFilters() {
|
||||
return {
|
||||
sub_part_trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable Part" %}',
|
||||
title: '{% jstrans "Trackable Part" %}',
|
||||
},
|
||||
sub_part_assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembled Part" %}',
|
||||
title: '{% jstrans "Assembled Part" %}',
|
||||
},
|
||||
available_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has Available Stock" %}',
|
||||
title: '{% jstrans "Has Available Stock" %}',
|
||||
},
|
||||
on_order: {
|
||||
type: 'bool',
|
||||
title: '{% trans "On Order" %}',
|
||||
title: '{% jstrans "On Order" %}',
|
||||
},
|
||||
validated: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Validated" %}',
|
||||
title: '{% jstrans "Validated" %}',
|
||||
},
|
||||
inherited: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Gets inherited" %}',
|
||||
title: '{% jstrans "Gets inherited" %}',
|
||||
},
|
||||
allow_variants: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Allow Variant Stock" %}',
|
||||
title: '{% jstrans "Allow Variant Stock" %}',
|
||||
},
|
||||
optional: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Optional" %}',
|
||||
title: '{% jstrans "Optional" %}',
|
||||
},
|
||||
consumable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Consumable" %}',
|
||||
title: '{% jstrans "Consumable" %}',
|
||||
},
|
||||
has_pricing: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has Pricing" %}',
|
||||
title: '{% jstrans "Has Pricing" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -208,19 +208,19 @@ function getUsedInTableFilters() {
|
||||
return {
|
||||
'inherited': {
|
||||
type: 'bool',
|
||||
title: '{% trans "Gets inherited" %}',
|
||||
title: '{% jstrans "Gets inherited" %}',
|
||||
},
|
||||
'optional': {
|
||||
type: 'bool',
|
||||
title: '{% trans "Optional" %}',
|
||||
title: '{% jstrans "Optional" %}',
|
||||
},
|
||||
'part_active': {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
title: '{% jstrans "Active" %}',
|
||||
},
|
||||
'part_trackable': {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
title: '{% jstrans "Trackable" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -231,19 +231,19 @@ function getStockLocationFilters() {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include sublocations" %}',
|
||||
description: '{% trans "Include locations" %}',
|
||||
title: '{% jstrans "Include sublocations" %}',
|
||||
description: '{% jstrans "Include locations" %}',
|
||||
},
|
||||
structural: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Structural" %}',
|
||||
title: '{% jstrans "Structural" %}',
|
||||
},
|
||||
external: {
|
||||
type: 'bool',
|
||||
title: '{% trans "External" %}',
|
||||
title: '{% jstrans "External" %}',
|
||||
},
|
||||
location_type: {
|
||||
title: '{% trans "Location type" %}',
|
||||
title: '{% jstrans "Location type" %}',
|
||||
options: function() {
|
||||
const locationTypes = {};
|
||||
|
||||
@@ -264,7 +264,7 @@ function getStockLocationFilters() {
|
||||
},
|
||||
has_location_type: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has location type" %}'
|
||||
title: '{% jstrans "Has location type" %}'
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -275,16 +275,16 @@ function getPartCategoryFilters() {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include subcategories" %}',
|
||||
description: '{% trans "Include subcategories" %}',
|
||||
title: '{% jstrans "Include subcategories" %}',
|
||||
description: '{% jstrans "Include subcategories" %}',
|
||||
},
|
||||
structural: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Structural" %}',
|
||||
title: '{% jstrans "Structural" %}',
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Subscribed" %}',
|
||||
title: '{% jstrans "Subscribed" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -295,23 +295,23 @@ function getCustomerStockFilters() {
|
||||
return {
|
||||
serialized: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Is Serialized" %}',
|
||||
title: '{% jstrans "Is Serialized" %}',
|
||||
},
|
||||
serial_gte: {
|
||||
title: '{% trans "Serial number GTE" %}',
|
||||
description: '{% trans "Serial number greater than or equal to" %}',
|
||||
title: '{% jstrans "Serial number GTE" %}',
|
||||
description: '{% jstrans "Serial number greater than or equal to" %}',
|
||||
},
|
||||
serial_lte: {
|
||||
title: '{% trans "Serial number LTE" %}',
|
||||
description: '{% trans "Serial number less than or equal to" %}',
|
||||
title: '{% jstrans "Serial number LTE" %}',
|
||||
description: '{% jstrans "Serial number less than or equal to" %}',
|
||||
},
|
||||
serial: {
|
||||
title: '{% trans "Serial number" %}',
|
||||
description: '{% trans "Serial number" %}',
|
||||
title: '{% jstrans "Serial number" %}',
|
||||
description: '{% jstrans "Serial number" %}',
|
||||
},
|
||||
batch: {
|
||||
title: '{% trans "Batch" %}',
|
||||
description: '{% trans "Batch code" %}',
|
||||
title: '{% jstrans "Batch" %}',
|
||||
description: '{% jstrans "Batch code" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -322,109 +322,109 @@ function getStockTableFilters() {
|
||||
var filters = {
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active parts" %}',
|
||||
description: '{% trans "Show stock for active parts" %}',
|
||||
title: '{% jstrans "Active parts" %}',
|
||||
description: '{% jstrans "Show stock for active parts" %}',
|
||||
},
|
||||
assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembly" %}',
|
||||
description: '{% trans "Part is an assembly" %}',
|
||||
title: '{% jstrans "Assembly" %}',
|
||||
description: '{% jstrans "Part is an assembly" %}',
|
||||
},
|
||||
allocated: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Is allocated" %}',
|
||||
description: '{% trans "Item has been allocated" %}',
|
||||
title: '{% jstrans "Is allocated" %}',
|
||||
description: '{% jstrans "Item has been allocated" %}',
|
||||
},
|
||||
available: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Available" %}',
|
||||
description: '{% trans "Stock is available for use" %}',
|
||||
title: '{% jstrans "Available" %}',
|
||||
description: '{% jstrans "Stock is available for use" %}',
|
||||
},
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include sublocations" %}',
|
||||
description: '{% trans "Include stock in sublocations" %}',
|
||||
title: '{% jstrans "Include sublocations" %}',
|
||||
description: '{% jstrans "Include stock in sublocations" %}',
|
||||
},
|
||||
depleted: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Depleted" %}',
|
||||
description: '{% trans "Show stock items which are depleted" %}',
|
||||
title: '{% jstrans "Depleted" %}',
|
||||
description: '{% jstrans "Show stock items which are depleted" %}',
|
||||
},
|
||||
in_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "In Stock" %}',
|
||||
description: '{% trans "Show items which are in stock" %}',
|
||||
title: '{% jstrans "In Stock" %}',
|
||||
description: '{% jstrans "Show items which are in stock" %}',
|
||||
},
|
||||
is_building: {
|
||||
type: 'bool',
|
||||
title: '{% trans "In Production" %}',
|
||||
description: '{% trans "Show items which are in production" %}',
|
||||
title: '{% jstrans "In Production" %}',
|
||||
description: '{% jstrans "Show items which are in production" %}',
|
||||
},
|
||||
include_variants: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include Variants" %}',
|
||||
description: '{% trans "Include stock items for variant parts" %}',
|
||||
title: '{% jstrans "Include Variants" %}',
|
||||
description: '{% jstrans "Include stock items for variant parts" %}',
|
||||
},
|
||||
installed: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Installed" %}',
|
||||
description: '{% trans "Show stock items which are installed in another item" %}',
|
||||
title: '{% jstrans "Installed" %}',
|
||||
description: '{% jstrans "Show stock items which are installed in another item" %}',
|
||||
},
|
||||
sent_to_customer: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Sent to customer" %}',
|
||||
description: '{% trans "Show items which have been assigned to a customer" %}',
|
||||
title: '{% jstrans "Sent to customer" %}',
|
||||
description: '{% jstrans "Show items which have been assigned to a customer" %}',
|
||||
},
|
||||
serialized: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Is Serialized" %}',
|
||||
title: '{% jstrans "Is Serialized" %}',
|
||||
},
|
||||
serial: {
|
||||
title: '{% trans "Serial number" %}',
|
||||
description: '{% trans "Serial number" %}',
|
||||
title: '{% jstrans "Serial number" %}',
|
||||
description: '{% jstrans "Serial number" %}',
|
||||
},
|
||||
serial_gte: {
|
||||
title: '{% trans "Serial number GTE" %}',
|
||||
description: '{% trans "Serial number greater than or equal to" %}',
|
||||
title: '{% jstrans "Serial number GTE" %}',
|
||||
description: '{% jstrans "Serial number greater than or equal to" %}',
|
||||
},
|
||||
serial_lte: {
|
||||
title: '{% trans "Serial number LTE" %}',
|
||||
description: '{% trans "Serial number less than or equal to" %}',
|
||||
title: '{% jstrans "Serial number LTE" %}',
|
||||
description: '{% jstrans "Serial number less than or equal to" %}',
|
||||
},
|
||||
status: {
|
||||
options: stockCodes,
|
||||
title: '{% trans "Stock status" %}',
|
||||
description: '{% trans "Stock status" %}',
|
||||
title: '{% jstrans "Stock status" %}',
|
||||
description: '{% jstrans "Stock status" %}',
|
||||
},
|
||||
has_batch: {
|
||||
title: '{% trans "Has batch code" %}',
|
||||
title: '{% jstrans "Has batch code" %}',
|
||||
type: 'bool',
|
||||
},
|
||||
batch: {
|
||||
title: '{% trans "Batch" %}',
|
||||
description: '{% trans "Batch code" %}',
|
||||
title: '{% jstrans "Batch" %}',
|
||||
description: '{% jstrans "Batch code" %}',
|
||||
},
|
||||
tracked: {
|
||||
title: '{% trans "Tracked" %}',
|
||||
description: '{% trans "Stock item is tracked by either batch code or serial number" %}',
|
||||
title: '{% jstrans "Tracked" %}',
|
||||
description: '{% jstrans "Stock item is tracked by either batch code or serial number" %}',
|
||||
type: 'bool',
|
||||
},
|
||||
has_purchase_price: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has purchase price" %}',
|
||||
description: '{% trans "Show stock items which have a purchase price set" %}',
|
||||
title: '{% jstrans "Has purchase price" %}',
|
||||
description: '{% jstrans "Show stock items which have a purchase price set" %}',
|
||||
},
|
||||
expiry_date_lte: {
|
||||
type: 'date',
|
||||
title: '{% trans "Expiry Date before" %}',
|
||||
title: '{% jstrans "Expiry Date before" %}',
|
||||
},
|
||||
expiry_date_gte: {
|
||||
type: 'date',
|
||||
title: '{% trans "Expiry Date after" %}',
|
||||
title: '{% jstrans "Expiry Date after" %}',
|
||||
},
|
||||
external: {
|
||||
type: 'bool',
|
||||
title: '{% trans "External Location" %}',
|
||||
title: '{% jstrans "External Location" %}',
|
||||
}
|
||||
};
|
||||
|
||||
@@ -432,14 +432,14 @@ function getStockTableFilters() {
|
||||
if (global_settings.STOCK_ENABLE_EXPIRY) {
|
||||
filters.expired = {
|
||||
type: 'bool',
|
||||
title: '{% trans "Expired" %}',
|
||||
description: '{% trans "Show stock items which have expired" %}',
|
||||
title: '{% jstrans "Expired" %}',
|
||||
description: '{% jstrans "Show stock items which have expired" %}',
|
||||
};
|
||||
|
||||
filters.stale = {
|
||||
type: 'bool',
|
||||
title: '{% trans "Stale" %}',
|
||||
description: '{% trans "Show stock which is close to expiring" %}',
|
||||
title: '{% jstrans "Stale" %}',
|
||||
description: '{% jstrans "Show stock which is close to expiring" %}',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -453,11 +453,11 @@ function getStockTestTableFilters() {
|
||||
return {
|
||||
result: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Test Passed" %}',
|
||||
title: '{% jstrans "Test Passed" %}',
|
||||
},
|
||||
include_installed: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include Installed Items" %}',
|
||||
title: '{% jstrans "Include Installed Items" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -474,7 +474,7 @@ function getPartTestTemplateFilters() {
|
||||
return {
|
||||
required: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Required" %}',
|
||||
title: '{% jstrans "Required" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -485,19 +485,19 @@ function getPluginTableFilters() {
|
||||
return {
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
title: '{% jstrans "Active" %}',
|
||||
},
|
||||
builtin: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Builtin" %}',
|
||||
title: '{% jstrans "Builtin" %}',
|
||||
},
|
||||
sample: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Sample" %}',
|
||||
title: '{% jstrans "Sample" %}',
|
||||
},
|
||||
installed: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Installed" %}'
|
||||
title: '{% jstrans "Installed" %}'
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -508,23 +508,23 @@ function getBuildTableFilters() {
|
||||
|
||||
let filters = {
|
||||
status: {
|
||||
title: '{% trans "Build status" %}',
|
||||
title: '{% jstrans "Build status" %}',
|
||||
options: buildCodes,
|
||||
},
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
title: '{% jstrans "Active" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
title: '{% jstrans "Overdue" %}',
|
||||
},
|
||||
assigned_to_me: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assigned to me" %}',
|
||||
title: '{% jstrans "Assigned to me" %}',
|
||||
},
|
||||
assigned_to: {
|
||||
title: '{% trans "Responsible" %}',
|
||||
title: '{% jstrans "Responsible" %}',
|
||||
options: function() {
|
||||
var ownersList = {};
|
||||
inventreeGet('{% url "api-owner-list" %}', {}, {
|
||||
@@ -564,23 +564,23 @@ function getBuildLineTableFilters() {
|
||||
return {
|
||||
allocated: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Allocated" %}',
|
||||
title: '{% jstrans "Allocated" %}',
|
||||
},
|
||||
available: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Available" %}',
|
||||
title: '{% jstrans "Available" %}',
|
||||
},
|
||||
tracked: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Tracked" %}',
|
||||
title: '{% jstrans "Tracked" %}',
|
||||
},
|
||||
consumable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Consumable" %}',
|
||||
title: '{% jstrans "Consumable" %}',
|
||||
},
|
||||
optional: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Optional" %}',
|
||||
title: '{% jstrans "Optional" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -591,14 +591,14 @@ function getPurchaseOrderLineItemFilters() {
|
||||
return {
|
||||
pending: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Pending" %}',
|
||||
title: '{% jstrans "Pending" %}',
|
||||
},
|
||||
received: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Received" %}',
|
||||
title: '{% jstrans "Received" %}',
|
||||
},
|
||||
order_status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
title: '{% jstrans "Order status" %}',
|
||||
options: purchaseOrderCodes,
|
||||
},
|
||||
};
|
||||
@@ -610,20 +610,20 @@ function getPurchaseOrderFilters() {
|
||||
|
||||
var filters = {
|
||||
status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
title: '{% jstrans "Order status" %}',
|
||||
options: purchaseOrderCodes,
|
||||
},
|
||||
outstanding: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
title: '{% jstrans "Outstanding" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
title: '{% jstrans "Overdue" %}',
|
||||
},
|
||||
assigned_to_me: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assigned to me" %}',
|
||||
title: '{% jstrans "Assigned to me" %}',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -641,7 +641,7 @@ function getSalesOrderAllocationFilters() {
|
||||
return {
|
||||
outstanding: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
title: '{% jstrans "Outstanding" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -651,20 +651,20 @@ function getSalesOrderAllocationFilters() {
|
||||
function getSalesOrderFilters() {
|
||||
var filters = {
|
||||
status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
title: '{% jstrans "Order status" %}',
|
||||
options: salesOrderCodes,
|
||||
},
|
||||
outstanding: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
title: '{% jstrans "Outstanding" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
title: '{% jstrans "Overdue" %}',
|
||||
},
|
||||
assigned_to_me: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assigned to me" %}',
|
||||
title: '{% jstrans "Assigned to me" %}',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -682,7 +682,7 @@ function getSalesOrderLineItemFilters() {
|
||||
return {
|
||||
completed: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Completed" %}',
|
||||
title: '{% jstrans "Completed" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -693,7 +693,7 @@ function getSupplierPartFilters() {
|
||||
return {
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active parts" %}',
|
||||
title: '{% jstrans "Active parts" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -704,75 +704,75 @@ function getPartTableFilters() {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include subcategories" %}',
|
||||
description: '{% trans "Include parts in subcategories" %}',
|
||||
title: '{% jstrans "Include subcategories" %}',
|
||||
description: '{% jstrans "Include parts in subcategories" %}',
|
||||
},
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
description: '{% trans "Show active parts" %}',
|
||||
title: '{% jstrans "Active" %}',
|
||||
description: '{% jstrans "Show active parts" %}',
|
||||
},
|
||||
assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembly" %}',
|
||||
title: '{% jstrans "Assembly" %}',
|
||||
},
|
||||
unallocated_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Available stock" %}',
|
||||
title: '{% jstrans "Available stock" %}',
|
||||
},
|
||||
component: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Component" %}',
|
||||
title: '{% jstrans "Component" %}',
|
||||
},
|
||||
has_units: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has Units" %}',
|
||||
description: '{% trans "Part has defined units" %}',
|
||||
title: '{% jstrans "Has Units" %}',
|
||||
description: '{% jstrans "Part has defined units" %}',
|
||||
},
|
||||
has_ipn: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has IPN" %}',
|
||||
description: '{% trans "Part has internal part number" %}',
|
||||
title: '{% jstrans "Has IPN" %}',
|
||||
description: '{% jstrans "Part has internal part number" %}',
|
||||
},
|
||||
has_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "In stock" %}',
|
||||
title: '{% jstrans "In stock" %}',
|
||||
},
|
||||
low_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Low stock" %}',
|
||||
title: '{% jstrans "Low stock" %}',
|
||||
},
|
||||
purchaseable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Purchasable" %}',
|
||||
title: '{% jstrans "Purchasable" %}',
|
||||
},
|
||||
salable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Salable" %}',
|
||||
title: '{% jstrans "Salable" %}',
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Subscribed" %}',
|
||||
title: '{% jstrans "Subscribed" %}',
|
||||
},
|
||||
stocktake: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has stocktake entries" %}',
|
||||
title: '{% jstrans "Has stocktake entries" %}',
|
||||
},
|
||||
is_template: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
title: '{% jstrans "Template" %}',
|
||||
},
|
||||
trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
title: '{% jstrans "Trackable" %}',
|
||||
},
|
||||
virtual: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Virtual" %}',
|
||||
title: '{% jstrans "Virtual" %}',
|
||||
},
|
||||
has_pricing: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has Pricing" %}',
|
||||
title: '{% jstrans "Has Pricing" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -789,15 +789,15 @@ function getCompanyFilters() {
|
||||
return {
|
||||
is_manufacturer: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Manufacturer" %}',
|
||||
title: '{% jstrans "Manufacturer" %}',
|
||||
},
|
||||
is_supplier: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Supplier" %}',
|
||||
title: '{% jstrans "Supplier" %}',
|
||||
},
|
||||
is_customer: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Customer" %}',
|
||||
title: '{% jstrans "Customer" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -814,15 +814,15 @@ function getPartParameterTemplateFilters() {
|
||||
return {
|
||||
checkbox: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Checkbox" %}',
|
||||
title: '{% jstrans "Checkbox" %}',
|
||||
},
|
||||
has_choices: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has Choices" %}',
|
||||
title: '{% jstrans "Has Choices" %}',
|
||||
},
|
||||
has_units: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has Units" %}',
|
||||
title: '{% jstrans "Has Units" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ function constructOrderTableButtons(options={}) {
|
||||
// Calendar view button
|
||||
if (!options.disableCalendarView) {
|
||||
buttons.push({
|
||||
html: `<button type='button' name='${idx++}' class='btn ${class_calendar}' title='{% trans "Display calendar view" %}'><span class='fas fa-calendar-alt'></span></button>`,
|
||||
html: `<button type='button' name='${idx++}' class='btn ${class_calendar}' title='{% jstrans "Display calendar view" %}'><span class='fas fa-calendar-alt'></span></button>`,
|
||||
event: function() {
|
||||
buttonCallback('calendar');
|
||||
}
|
||||
@@ -99,7 +99,7 @@ function constructOrderTableButtons(options={}) {
|
||||
// List view button
|
||||
if (!options.disableListView) {
|
||||
buttons.push({
|
||||
html: `<button type='button' name='${idx++}' class='btn ${class_list}' title='{% trans "Display list view" %}'><span class='fas fa-th-list'></span></button>`,
|
||||
html: `<button type='button' name='${idx++}' class='btn ${class_list}' title='{% jstrans "Display list view" %}'><span class='fas fa-th-list'></span></button>`,
|
||||
event: function() {
|
||||
buttonCallback('list');
|
||||
}
|
||||
@@ -109,7 +109,7 @@ function constructOrderTableButtons(options={}) {
|
||||
// Tree view button
|
||||
if (!options.disableTreeView) {
|
||||
buttons.push({
|
||||
html: `<button type='button' name='${idx++}' class='btn ${class_tree}' title='{% trans "Display tree view" %}'><span class='fas fa-sitemap'></span></button>`,
|
||||
html: `<button type='button' name='${idx++}' class='btn ${class_tree}' title='{% jstrans "Display tree view" %}'><span class='fas fa-sitemap'></span></button>`,
|
||||
event: function() {
|
||||
buttonCallback('tree');
|
||||
}
|
||||
@@ -127,13 +127,13 @@ function constructExpandCollapseButtons(table, idx=0) {
|
||||
|
||||
return [
|
||||
{
|
||||
html: `<button type='button' name='${idx++}' class='btn btn-outline-secondary' title='{% trans "Expand all rows" %}'><span class='fas fa-expand'></span></button>`,
|
||||
html: `<button type='button' name='${idx++}' class='btn btn-outline-secondary' title='{% jstrans "Expand all rows" %}'><span class='fas fa-expand'></span></button>`,
|
||||
event: function() {
|
||||
$(table).bootstrapTable('expandAllRows');
|
||||
}
|
||||
},
|
||||
{
|
||||
html: `<button type='button' name='${idx++}' class='btn btn-outline-secondary' title='{% trans "Collapse all rows" %}'><span class='fas fa-compress'></span></button>`,
|
||||
html: `<button type='button' name='${idx++}' class='btn btn-outline-secondary' title='{% jstrans "Collapse all rows" %}'><span class='fas fa-compress'></span></button>`,
|
||||
event: function() {
|
||||
$(table).bootstrapTable('collapseAllRows');
|
||||
}
|
||||
@@ -183,11 +183,11 @@ function downloadTableData(table, opts={}) {
|
||||
url += '?';
|
||||
|
||||
constructFormBody({}, {
|
||||
title: opts.title || '{% trans "Export Table Data" %}',
|
||||
title: opts.title || '{% jstrans "Export Table Data" %}',
|
||||
fields: {
|
||||
format: {
|
||||
label: '{% trans "Format" %}',
|
||||
help_text: '{% trans "Select File Format" %}',
|
||||
label: '{% jstrans "Format" %}',
|
||||
help_text: '{% jstrans "Select File Format" %}',
|
||||
required: true,
|
||||
type: 'choice',
|
||||
value: 'csv',
|
||||
@@ -526,39 +526,39 @@ function customGroupSorter(sortName, sortOrder, sortData) {
|
||||
|
||||
$.fn.bootstrapTable.locales['en-US-custom'] = {
|
||||
formatLoadingMessage: function() {
|
||||
return '{% trans "Loading data" %}';
|
||||
return '{% jstrans "Loading data" %}';
|
||||
},
|
||||
formatRecordsPerPage: function(pageNumber) {
|
||||
return `${pageNumber} {% trans "rows per page" %}`;
|
||||
return `${pageNumber} {% jstrans "rows per page" %}`;
|
||||
},
|
||||
formatShowingRows: function(pageFrom, pageTo, totalRows) {
|
||||
|
||||
if (totalRows === undefined || isNaN(totalRows)) {
|
||||
return '{% trans "Showing all rows" %}';
|
||||
return '{% jstrans "Showing all rows" %}';
|
||||
} else {
|
||||
return `{% trans "Showing" %} ${pageFrom} {% trans "to" %} ${pageTo} {% trans "of" %} ${totalRows} {% trans "rows" %}`;
|
||||
return `{% jstrans "Showing" %} ${pageFrom} {% jstrans "to" %} ${pageTo} {% jstrans "of" %} ${totalRows} {% jstrans "rows" %}`;
|
||||
}
|
||||
},
|
||||
formatSearch: function() {
|
||||
return '{% trans "Search" %}';
|
||||
return '{% jstrans "Search" %}';
|
||||
},
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No matching results" %}';
|
||||
return '{% jstrans "No matching results" %}';
|
||||
},
|
||||
formatPaginationSwitch: function() {
|
||||
return '{% trans "Hide/Show pagination" %}';
|
||||
return '{% jstrans "Hide/Show pagination" %}';
|
||||
},
|
||||
formatRefresh: function() {
|
||||
return '{% trans "Refresh" %}';
|
||||
return '{% jstrans "Refresh" %}';
|
||||
},
|
||||
formatToggle: function() {
|
||||
return '{% trans "Toggle" %}';
|
||||
return '{% jstrans "Toggle" %}';
|
||||
},
|
||||
formatColumns: function() {
|
||||
return '{% trans "Columns" %}';
|
||||
return '{% jstrans "Columns" %}';
|
||||
},
|
||||
formatAllRows: function() {
|
||||
return '{% trans "All" %}';
|
||||
return '{% jstrans "All" %}';
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ from rest_framework.authtoken.models import Token as AuthToken
|
||||
import common.models as common_models
|
||||
import InvenTree.helpers
|
||||
import InvenTree.models
|
||||
from InvenTree.ready import canAppAccessDatabase
|
||||
from InvenTree.ready import canAppAccessDatabase, isImportingData
|
||||
|
||||
logger = logging.getLogger("inventree")
|
||||
|
||||
@@ -892,7 +892,9 @@ class Owner(models.Model):
|
||||
@receiver(post_save, sender=get_user_model(), dispatch_uid='create_owner')
|
||||
def create_owner(sender, instance, **kwargs):
|
||||
"""Callback function to create a new owner instance after either a new group or user instance is saved."""
|
||||
Owner.create(obj=instance)
|
||||
# Ignore during data import process to avoid data duplication
|
||||
if not isImportingData():
|
||||
Owner.create(obj=instance)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Group, dispatch_uid='delete_owner')
|
||||
|
||||
@@ -51,6 +51,7 @@ def check_prohibited_tags(data):
|
||||
'for',
|
||||
'endfor',
|
||||
'trans',
|
||||
'jstrans',
|
||||
'load',
|
||||
'include',
|
||||
'url',
|
||||
@@ -89,18 +90,18 @@ for filename in pathlib.Path(js_dynamic_dir).rglob('*.js'):
|
||||
with open(filename, 'r') as js_file:
|
||||
data = js_file.readlines()
|
||||
|
||||
pattern = r'{% trans '
|
||||
invalid_tags = ['trans', 'jstrans']
|
||||
|
||||
err_count = 0
|
||||
|
||||
for idx, line in enumerate(data):
|
||||
for tag in invalid_tags:
|
||||
tag = '{% ' + tag
|
||||
if tag in line:
|
||||
err_count += 1
|
||||
|
||||
results = re.findall(pattern, line)
|
||||
print(f" > Error on line {idx+1}: Prohibited tag '{tag}' found")
|
||||
|
||||
if len(results) > 0:
|
||||
errors += 1
|
||||
|
||||
print(f" > prohibited {{% trans %}} tag found at line {idx + 1}")
|
||||
|
||||
if errors > 0:
|
||||
print(f"Found {errors} incorrect template tags")
|
||||
|
||||
@@ -48,7 +48,7 @@ services:
|
||||
- POSTGRES_DB=${INVENTREE_DB_NAME:?You must provide the 'INVENTREE_DB_NAME' variable in the .env file}
|
||||
volumes:
|
||||
# Map 'data' volume such that postgres database is stored externally
|
||||
- inventree_data:/var/lib/postgresql/data/
|
||||
- inventree_data:/var/lib/postgresql/data/:z
|
||||
restart: unless-stopped
|
||||
|
||||
# redis acts as database cache manager
|
||||
@@ -82,7 +82,7 @@ services:
|
||||
- .env
|
||||
volumes:
|
||||
# Data volume must map to /home/inventree/data
|
||||
- inventree_data:/home/inventree/data
|
||||
- inventree_data:/home/inventree/data:z
|
||||
restart: unless-stopped
|
||||
|
||||
# Background worker process handles long-running or periodic tasks
|
||||
@@ -96,7 +96,7 @@ services:
|
||||
- .env
|
||||
volumes:
|
||||
# Data volume must map to /home/inventree/data
|
||||
- inventree_data:/home/inventree/data
|
||||
- inventree_data:/home/inventree/data:z
|
||||
restart: unless-stopped
|
||||
|
||||
# nginx acts as a reverse proxy
|
||||
@@ -116,9 +116,9 @@ services:
|
||||
volumes:
|
||||
# Provide nginx configuration file to the container
|
||||
# Refer to the provided example file as a starting point
|
||||
- ./nginx.prod.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./nginx.prod.conf:/etc/nginx/conf.d/default.conf:ro,z
|
||||
# nginx proxy needs access to static and media files
|
||||
- inventree_data:/var/www
|
||||
- inventree_data:/var/www:z
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
|
||||
@@ -81,7 +81,7 @@ django==3.2.23
|
||||
# djangorestframework
|
||||
# djangorestframework-simplejwt
|
||||
# drf-spectacular
|
||||
django-allauth==0.59.0
|
||||
django-allauth==0.60.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-allauth-2fa
|
||||
|
||||
532
tasks.py
532
tasks.py
@@ -18,11 +18,10 @@ def checkPythonVersion():
|
||||
|
||||
If the python version is not sufficient, exits with a non-zero exit code.
|
||||
"""
|
||||
|
||||
REQ_MAJOR = 3
|
||||
REQ_MINOR = 9
|
||||
|
||||
version = sys.version.split(" ")[0]
|
||||
version = sys.version.split(' ')[0]
|
||||
|
||||
valid = True
|
||||
|
||||
@@ -33,8 +32,8 @@ def checkPythonVersion():
|
||||
valid = False
|
||||
|
||||
if not valid:
|
||||
print(f"The installed python version ({version}) is not supported!")
|
||||
print(f"InvenTree requires Python {REQ_MAJOR}.{REQ_MINOR} or above")
|
||||
print(f'The installed python version ({version}) is not supported!')
|
||||
print(f'InvenTree requires Python {REQ_MAJOR}.{REQ_MINOR} or above')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -59,29 +58,57 @@ def apps():
|
||||
]
|
||||
|
||||
|
||||
def content_excludes():
|
||||
"""Returns a list of content types to exclude from import/export."""
|
||||
def content_excludes(
|
||||
allow_auth: bool = True,
|
||||
allow_tokens: bool = True,
|
||||
allow_plugins: bool = True,
|
||||
allow_sso: bool = True,
|
||||
):
|
||||
"""Returns a list of content types to exclude from import/export.
|
||||
|
||||
Arguments:
|
||||
allow_auth (bool): Allow user/group information to be exported/imported
|
||||
allow_tokens (bool): Allow tokens to be exported/importe
|
||||
allow_plugins (bool): Allow plugin information to be exported/imported
|
||||
allow_sso (bool): Allow SSO tokens to be exported/imported
|
||||
"""
|
||||
excludes = [
|
||||
"contenttypes",
|
||||
"auth.permission",
|
||||
"users.apitoken",
|
||||
"error_report.error",
|
||||
"admin.logentry",
|
||||
"django_q.schedule",
|
||||
"django_q.task",
|
||||
"django_q.ormq",
|
||||
"users.owner",
|
||||
"exchange.rate",
|
||||
"exchange.exchangebackend",
|
||||
"common.notificationentry",
|
||||
"common.notificationmessage",
|
||||
"user_sessions.session",
|
||||
'contenttypes',
|
||||
'auth.permission',
|
||||
'error_report.error',
|
||||
'admin.logentry',
|
||||
'django_q.schedule',
|
||||
'django_q.task',
|
||||
'django_q.ormq',
|
||||
'exchange.rate',
|
||||
'exchange.exchangebackend',
|
||||
'common.notificationentry',
|
||||
'common.notificationmessage',
|
||||
'user_sessions.session',
|
||||
]
|
||||
|
||||
output = ""
|
||||
# Optionally exclude user auth data
|
||||
if not allow_auth:
|
||||
excludes.append('auth.group')
|
||||
excludes.append('auth.user')
|
||||
|
||||
# Optionally exclude user token information
|
||||
if not allow_tokens:
|
||||
excludes.append('users.apitoken')
|
||||
|
||||
# Optionally exclude plugin information
|
||||
if not allow_plugins:
|
||||
excludes.append('plugin.pluginconfig')
|
||||
excludes.append('plugin.pluginsetting')
|
||||
|
||||
# Optionally exclude SSO application information
|
||||
if not allow_sso:
|
||||
excludes.append('socialaccount.socialapp')
|
||||
|
||||
output = ''
|
||||
|
||||
for e in excludes:
|
||||
output += f"--exclude {e} "
|
||||
output += f'--exclude {e} '
|
||||
|
||||
return output
|
||||
|
||||
@@ -113,10 +140,10 @@ def manage(c, cmd, pty: bool = False):
|
||||
cmd: Django command to run.
|
||||
pty (bool, optional): Run an interactive session. Defaults to False.
|
||||
"""
|
||||
c.run('cd "{path}" && python3 manage.py {cmd}'.format(
|
||||
path=managePyDir(),
|
||||
cmd=cmd
|
||||
), pty=pty)
|
||||
c.run(
|
||||
'cd "{path}" && python3 manage.py {cmd}'.format(path=managePyDir(), cmd=cmd),
|
||||
pty=pty,
|
||||
)
|
||||
|
||||
|
||||
def yarn(c, cmd, pty: bool = False):
|
||||
@@ -133,6 +160,7 @@ def yarn(c, cmd, pty: bool = False):
|
||||
|
||||
def node_available(versions: bool = False, bypass_yarn: bool = False):
|
||||
"""Checks if the frontend environment (ie node and yarn in bash) is available."""
|
||||
|
||||
def ret(val, val0=None, val1=None):
|
||||
if versions:
|
||||
return val, val0, val1
|
||||
@@ -140,7 +168,10 @@ def node_available(versions: bool = False, bypass_yarn: bool = False):
|
||||
|
||||
def check(cmd):
|
||||
try:
|
||||
return str(subprocess.check_output([cmd], stderr=subprocess.STDOUT, shell=True), encoding='utf-8').strip()
|
||||
return str(
|
||||
subprocess.check_output([cmd], stderr=subprocess.STDOUT, shell=True),
|
||||
encoding='utf-8',
|
||||
).strip()
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
except FileNotFoundError:
|
||||
@@ -154,7 +185,9 @@ def node_available(versions: bool = False, bypass_yarn: bool = False):
|
||||
|
||||
# Print a warning if node is available but yarn is not
|
||||
if node_version and not yarn_passes:
|
||||
print('Node is available but yarn is not. Install yarn if you wish to build the frontend.')
|
||||
print(
|
||||
'Node is available but yarn is not. Install yarn if you wish to build the frontend.'
|
||||
)
|
||||
|
||||
# Return the result
|
||||
return ret(yarn_passes and node_version, node_version, yarn_version)
|
||||
@@ -168,11 +201,13 @@ def check_file_existance(filename: str, overwrite: bool = False):
|
||||
overwrite (bool, optional): Overwrite the file without asking. Defaults to False.
|
||||
"""
|
||||
if Path(filename).is_file() and overwrite is False:
|
||||
response = input("Warning: file already exists. Do you want to overwrite? [y/N]: ")
|
||||
response = input(
|
||||
'Warning: file already exists. Do you want to overwrite? [y/N]: '
|
||||
)
|
||||
response = str(response).strip().lower()
|
||||
|
||||
if response not in ['y', 'yes']:
|
||||
print("Cancelled export operation")
|
||||
print('Cancelled export operation')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -198,7 +233,9 @@ def install(c):
|
||||
# Install required Python packages with PIP
|
||||
c.run('pip3 install --upgrade pip')
|
||||
c.run('pip3 install --upgrade setuptools')
|
||||
c.run('pip3 install --no-cache-dir --disable-pip-version-check -U -r requirements.txt')
|
||||
c.run(
|
||||
'pip3 install --no-cache-dir --disable-pip-version-check -U -r requirements.txt'
|
||||
)
|
||||
|
||||
|
||||
@task(help={'tests': 'Set up test dataset at the end'})
|
||||
@@ -210,12 +247,12 @@ def setup_dev(c, tests=False):
|
||||
c.run('pip3 install -U -r requirements-dev.txt')
|
||||
|
||||
# Install pre-commit hook
|
||||
print("Installing pre-commit for checks before git commits...")
|
||||
print('Installing pre-commit for checks before git commits...')
|
||||
c.run('pre-commit install')
|
||||
|
||||
# Update all the hooks
|
||||
c.run('pre-commit autoupdate')
|
||||
print("pre-commit set up is done...")
|
||||
print('pre-commit set up is done...')
|
||||
|
||||
# Set up test-data if flag is set
|
||||
if tests:
|
||||
@@ -232,19 +269,19 @@ def superuser(c):
|
||||
@task
|
||||
def rebuild_models(c):
|
||||
"""Rebuild database models with MPTT structures."""
|
||||
manage(c, "rebuild_models", pty=True)
|
||||
manage(c, 'rebuild_models', pty=True)
|
||||
|
||||
|
||||
@task
|
||||
def rebuild_thumbnails(c):
|
||||
"""Rebuild missing image thumbnails."""
|
||||
manage(c, "rebuild_thumbnails", pty=True)
|
||||
manage(c, 'rebuild_thumbnails', pty=True)
|
||||
|
||||
|
||||
@task
|
||||
def clean_settings(c):
|
||||
"""Clean the setting tables of old settings."""
|
||||
manage(c, "clean_settings")
|
||||
manage(c, 'clean_settings')
|
||||
|
||||
|
||||
@task(help={'mail': "mail of the user who's MFA should be disabled"})
|
||||
@@ -253,20 +290,16 @@ def remove_mfa(c, mail=''):
|
||||
if not mail:
|
||||
print('You must provide a users mail')
|
||||
|
||||
manage(c, f"remove_mfa {mail}")
|
||||
manage(c, f'remove_mfa {mail}')
|
||||
|
||||
|
||||
@task(
|
||||
help={
|
||||
'frontend': 'Build the frontend',
|
||||
}
|
||||
)
|
||||
@task(help={'frontend': 'Build the frontend'})
|
||||
def static(c, frontend=False):
|
||||
"""Copies required static files to the STATIC_ROOT directory, as per Django requirements."""
|
||||
manage(c, "prerender")
|
||||
manage(c, 'prerender')
|
||||
if frontend and node_available():
|
||||
frontend_build(c)
|
||||
manage(c, "collectstatic --no-input")
|
||||
manage(c, 'collectstatic --no-input')
|
||||
|
||||
|
||||
@task
|
||||
@@ -280,48 +313,49 @@ def translate_stats(c):
|
||||
try:
|
||||
manage(c, 'compilemessages', pty=True)
|
||||
except Exception:
|
||||
print("WARNING: Translation files could not be compiled:")
|
||||
print('WARNING: Translation files could not be compiled:')
|
||||
|
||||
path = Path('InvenTree', 'script', 'translation_stats.py')
|
||||
c.run(f'python3 {path}')
|
||||
|
||||
|
||||
@task(post=[translate_stats])
|
||||
def translate(c):
|
||||
def translate(c, ignore_static=False, no_frontend=False):
|
||||
"""Rebuild translation source files. Advanced use only!
|
||||
|
||||
Note: This command should not be used on a local install,
|
||||
it is performed as part of the InvenTree translation toolchain.
|
||||
"""
|
||||
# Translate applicable .py / .html / .js / .tsx files
|
||||
manage(c, "makemessages --all -e py,html,js --no-wrap")
|
||||
manage(c, "compilemessages")
|
||||
# Translate applicable .py / .html / .js files
|
||||
manage(c, 'makemessages --all -e py,html,js --no-wrap')
|
||||
manage(c, 'compilemessages')
|
||||
|
||||
if node_available():
|
||||
if not no_frontend and node_available():
|
||||
frontend_install(c)
|
||||
frontend_trans(c)
|
||||
frontend_build(c)
|
||||
|
||||
# Update static files
|
||||
static(c)
|
||||
if not ignore_static:
|
||||
static(c)
|
||||
|
||||
|
||||
@task
|
||||
def backup(c):
|
||||
"""Backup the database and media files."""
|
||||
print("Backing up InvenTree database...")
|
||||
manage(c, "dbbackup --noinput --clean --compress")
|
||||
print("Backing up InvenTree media files...")
|
||||
manage(c, "mediabackup --noinput --clean --compress")
|
||||
print('Backing up InvenTree database...')
|
||||
manage(c, 'dbbackup --noinput --clean --compress')
|
||||
print('Backing up InvenTree media files...')
|
||||
manage(c, 'mediabackup --noinput --clean --compress')
|
||||
|
||||
|
||||
@task
|
||||
def restore(c):
|
||||
"""Restore the database and media files."""
|
||||
print("Restoring InvenTree database...")
|
||||
manage(c, "dbrestore --noinput --uncompress")
|
||||
print("Restoring InvenTree media files...")
|
||||
manage(c, "mediarestore --noinput --uncompress")
|
||||
print('Restoring InvenTree database...')
|
||||
manage(c, 'dbrestore --noinput --uncompress')
|
||||
print('Restoring InvenTree media files...')
|
||||
manage(c, 'mediarestore --noinput --uncompress')
|
||||
|
||||
|
||||
@task(post=[rebuild_models, rebuild_thumbnails])
|
||||
@@ -330,16 +364,16 @@ def migrate(c):
|
||||
|
||||
This is a critical step if the database schema have been altered!
|
||||
"""
|
||||
print("Running InvenTree database migrations...")
|
||||
print("========================================")
|
||||
print('Running InvenTree database migrations...')
|
||||
print('========================================')
|
||||
|
||||
manage(c, "makemigrations")
|
||||
manage(c, "migrate --noinput")
|
||||
manage(c, "migrate --run-syncdb")
|
||||
manage(c, "check")
|
||||
manage(c, 'makemigrations')
|
||||
manage(c, 'migrate --noinput')
|
||||
manage(c, 'migrate --run-syncdb')
|
||||
manage(c, 'check')
|
||||
|
||||
print("========================================")
|
||||
print("InvenTree database migrations completed!")
|
||||
print('========================================')
|
||||
print('InvenTree database migrations completed!')
|
||||
|
||||
|
||||
@task(
|
||||
@@ -347,8 +381,8 @@ def migrate(c):
|
||||
help={
|
||||
'skip_backup': 'Skip database backup step (advanced users)',
|
||||
'frontend': 'Force frontend compilation/download step (ignores INVENTREE_DOCKER)',
|
||||
'no_frontend': 'Skip frontend compilation/download step'
|
||||
}
|
||||
'no_frontend': 'Skip frontend compilation/download step',
|
||||
},
|
||||
)
|
||||
def update(c, skip_backup=False, frontend: bool = False, no_frontend: bool = False):
|
||||
"""Update InvenTree installation.
|
||||
@@ -390,13 +424,27 @@ def update(c, skip_backup=False, frontend: bool = False, no_frontend: bool = Fal
|
||||
|
||||
|
||||
# Data tasks
|
||||
@task(help={
|
||||
'filename': "Output filename (default = 'data.json')",
|
||||
'overwrite': "Overwrite existing files without asking first (default = off/False)",
|
||||
'include_permissions': "Include user and group permissions in the output file (filename) (default = off/False)",
|
||||
'delete_temp': "Delete temporary files (containing permissions) at end of run. Note that this will delete temporary files from previous runs as well. (default = off/False)"
|
||||
})
|
||||
def export_records(c, filename='data.json', overwrite=False, include_permissions=False, delete_temp=False):
|
||||
@task(
|
||||
help={
|
||||
'filename': "Output filename (default = 'data.json')",
|
||||
'overwrite': 'Overwrite existing files without asking first (default = False)',
|
||||
'include_permissions': 'Include user and group permissions in the output file (default = False)',
|
||||
'include_tokens': 'Include API tokens in the output file (default = False)',
|
||||
'exclude_plugins': 'Exclude plugin data from the output file (default = False)',
|
||||
'include_sso': 'Include SSO token data in the output file (default = False)',
|
||||
'retain_temp': 'Retain temporary files (containing permissions) at end of process (default = False)',
|
||||
}
|
||||
)
|
||||
def export_records(
|
||||
c,
|
||||
filename='data.json',
|
||||
overwrite=False,
|
||||
include_permissions=False,
|
||||
include_tokens=False,
|
||||
exclude_plugins=False,
|
||||
include_sso=False,
|
||||
retain_temp=False,
|
||||
):
|
||||
"""Export all database records to a file.
|
||||
|
||||
Write data to the file defined by filename.
|
||||
@@ -422,44 +470,58 @@ def export_records(c, filename='data.json', overwrite=False, include_permissions
|
||||
|
||||
check_file_existance(filename, overwrite)
|
||||
|
||||
tmpfile = f"{filename}.tmp"
|
||||
tmpfile = f'{filename}.tmp'
|
||||
|
||||
cmd = f"dumpdata --indent 2 --output '{tmpfile}' {content_excludes()}"
|
||||
excludes = content_excludes(
|
||||
allow_tokens=include_tokens,
|
||||
allow_plugins=not exclude_plugins,
|
||||
allow_sso=include_sso,
|
||||
)
|
||||
|
||||
cmd = f"dumpdata --natural-foreign --indent 2 --output '{tmpfile}' {excludes}"
|
||||
|
||||
# Dump data to temporary file
|
||||
manage(c, cmd, pty=True)
|
||||
|
||||
print("Running data post-processing step...")
|
||||
print('Running data post-processing step...')
|
||||
|
||||
# Post-process the file, to remove any "permissions" specified for a user or group
|
||||
with open(tmpfile, "r") as f_in:
|
||||
with open(tmpfile, 'r') as f_in:
|
||||
data = json.loads(f_in.read())
|
||||
|
||||
if include_permissions is False:
|
||||
for entry in data:
|
||||
if "model" in entry:
|
||||
|
||||
if 'model' in entry:
|
||||
# Clear out any permissions specified for a group
|
||||
if entry["model"] == "auth.group":
|
||||
entry["fields"]["permissions"] = []
|
||||
if entry['model'] == 'auth.group':
|
||||
entry['fields']['permissions'] = []
|
||||
|
||||
# Clear out any permissions specified for a user
|
||||
if entry["model"] == "auth.user":
|
||||
entry["fields"]["user_permissions"] = []
|
||||
if entry['model'] == 'auth.user':
|
||||
entry['fields']['user_permissions'] = []
|
||||
|
||||
# Write the processed data to file
|
||||
with open(filename, "w") as f_out:
|
||||
with open(filename, 'w') as f_out:
|
||||
f_out.write(json.dumps(data, indent=2))
|
||||
|
||||
print("Data export completed")
|
||||
print('Data export completed')
|
||||
|
||||
if delete_temp is True:
|
||||
print("Removing temporary file")
|
||||
if not retain_temp:
|
||||
print('Removing temporary files')
|
||||
os.remove(tmpfile)
|
||||
|
||||
|
||||
@task(help={'filename': 'Input filename', 'clear': 'Clear existing data before import'}, post=[rebuild_models, rebuild_thumbnails])
|
||||
def import_records(c, filename='data.json', clear=False):
|
||||
@task(
|
||||
help={
|
||||
'filename': 'Input filename',
|
||||
'clear': 'Clear existing data before import',
|
||||
'retain_temp': 'Retain temporary files at end of process (default = False)',
|
||||
},
|
||||
post=[rebuild_models, rebuild_thumbnails],
|
||||
)
|
||||
def import_records(
|
||||
c, filename='data.json', clear: bool = False, retain_temp: bool = False
|
||||
):
|
||||
"""Import database records from a file."""
|
||||
# Get an absolute path to the supplied filename
|
||||
if not os.path.isabs(filename):
|
||||
@@ -474,32 +536,69 @@ def import_records(c, filename='data.json', clear=False):
|
||||
|
||||
print(f"Importing database records from '{filename}'")
|
||||
|
||||
# Pre-process the data, to remove any "permissions" specified for a user or group
|
||||
tmpfile = f"{filename}.tmp.json"
|
||||
# We need to load 'auth' data (users / groups) *first*
|
||||
# This is due to the users.owner model, which has a ContentType foreign key
|
||||
authfile = f'{filename}.auth.json'
|
||||
|
||||
with open(filename, "r") as f_in:
|
||||
data = json.loads(f_in.read())
|
||||
# Pre-process the data, to remove any "permissions" specified for a user or group
|
||||
datafile = f'{filename}.data.json'
|
||||
|
||||
with open(filename, 'r') as f_in:
|
||||
try:
|
||||
data = json.loads(f_in.read())
|
||||
except json.JSONDecodeError as exc:
|
||||
print(f'Error: Failed to decode JSON file: {exc}')
|
||||
sys.exit(1)
|
||||
|
||||
auth_data = []
|
||||
load_data = []
|
||||
|
||||
for entry in data:
|
||||
if "model" in entry:
|
||||
|
||||
if 'model' in entry:
|
||||
# Clear out any permissions specified for a group
|
||||
if entry["model"] == "auth.group":
|
||||
entry["fields"]["permissions"] = []
|
||||
if entry['model'] == 'auth.group':
|
||||
entry['fields']['permissions'] = []
|
||||
|
||||
# Clear out any permissions specified for a user
|
||||
if entry["model"] == "auth.user":
|
||||
entry["fields"]["user_permissions"] = []
|
||||
if entry['model'] == 'auth.user':
|
||||
entry['fields']['user_permissions'] = []
|
||||
|
||||
# Save auth data for later
|
||||
if entry['model'].startswith('auth.'):
|
||||
auth_data.append(entry)
|
||||
else:
|
||||
load_data.append(entry)
|
||||
else:
|
||||
print('Warning: Invalid entry in data file')
|
||||
print(entry)
|
||||
|
||||
# Write the auth file data
|
||||
with open(authfile, 'w') as f_out:
|
||||
f_out.write(json.dumps(auth_data, indent=2))
|
||||
|
||||
# Write the processed data to the tmp file
|
||||
with open(tmpfile, "w") as f_out:
|
||||
f_out.write(json.dumps(data, indent=2))
|
||||
with open(datafile, 'w') as f_out:
|
||||
f_out.write(json.dumps(load_data, indent=2))
|
||||
|
||||
cmd = f"loaddata '{tmpfile}' -i {content_excludes()}"
|
||||
excludes = content_excludes(allow_auth=False)
|
||||
|
||||
# Import auth models first
|
||||
print('Importing user auth data...')
|
||||
cmd = f"loaddata '{authfile}'"
|
||||
manage(c, cmd, pty=True)
|
||||
|
||||
# Import everything else next
|
||||
print('Importing database records...')
|
||||
cmd = f"loaddata '{datafile}' -i {excludes}"
|
||||
|
||||
manage(c, cmd, pty=True)
|
||||
|
||||
print("Data import completed")
|
||||
if not retain_temp:
|
||||
print('Removing temporary files')
|
||||
os.remove(datafile)
|
||||
os.remove(authfile)
|
||||
|
||||
print('Data import completed')
|
||||
|
||||
|
||||
@task
|
||||
@@ -508,7 +607,7 @@ def delete_data(c, force=False):
|
||||
|
||||
Warning: This will REALLY delete all records in the database!!
|
||||
"""
|
||||
print("Deleting all data from InvenTree database...")
|
||||
print('Deleting all data from InvenTree database...')
|
||||
|
||||
if force:
|
||||
manage(c, 'flush --noinput')
|
||||
@@ -530,32 +629,26 @@ def import_fixtures(c):
|
||||
fixtures = [
|
||||
# Build model
|
||||
'build',
|
||||
|
||||
# Common models
|
||||
'settings',
|
||||
|
||||
# Company model
|
||||
'company',
|
||||
'price_breaks',
|
||||
'supplier_part',
|
||||
|
||||
# Order model
|
||||
'order',
|
||||
|
||||
# Part model
|
||||
'bom',
|
||||
'category',
|
||||
'params',
|
||||
'part',
|
||||
'test_templates',
|
||||
|
||||
# Stock model
|
||||
'location',
|
||||
'stock_tests',
|
||||
'stock',
|
||||
|
||||
# Users
|
||||
'users'
|
||||
'users',
|
||||
]
|
||||
|
||||
command = 'loaddata ' + ' '.join(fixtures)
|
||||
@@ -567,16 +660,16 @@ def import_fixtures(c):
|
||||
@task
|
||||
def wait(c):
|
||||
"""Wait until the database connection is ready."""
|
||||
return manage(c, "wait_for_db")
|
||||
return manage(c, 'wait_for_db')
|
||||
|
||||
|
||||
@task(pre=[wait], help={'address': 'Server address:port (default=127.0.0.1:8000)'})
|
||||
def server(c, address="127.0.0.1:8000"):
|
||||
def server(c, address='127.0.0.1:8000'):
|
||||
"""Launch a (development) server using Django's in-built webserver.
|
||||
|
||||
Note: This is *not* sufficient for a production installation.
|
||||
"""
|
||||
manage(c, "runserver {address}".format(address=address), pty=True)
|
||||
manage(c, 'runserver {address}'.format(address=address), pty=True)
|
||||
|
||||
|
||||
@task(pre=[wait])
|
||||
@@ -589,7 +682,7 @@ def worker(c):
|
||||
@task
|
||||
def render_js_files(c):
|
||||
"""Render templated javascript files (used for static testing)."""
|
||||
manage(c, "test InvenTree.ci_render_js")
|
||||
manage(c, 'test InvenTree.ci_render_js')
|
||||
|
||||
|
||||
@task(post=[translate_stats, static, server])
|
||||
@@ -607,40 +700,44 @@ def test_translations(c):
|
||||
django.setup()
|
||||
|
||||
# Add language
|
||||
print("Add dummy language...")
|
||||
print("========================================")
|
||||
manage(c, "makemessages -e py,html,js --no-wrap -l xx")
|
||||
print('Add dummy language...')
|
||||
print('========================================')
|
||||
manage(c, 'makemessages -e py,html,js --no-wrap -l xx')
|
||||
|
||||
# change translation
|
||||
print("Fill in dummy translations...")
|
||||
print("========================================")
|
||||
print('Fill in dummy translations...')
|
||||
print('========================================')
|
||||
|
||||
file_path = pathlib.Path(settings.LOCALE_PATHS[0], 'xx', 'LC_MESSAGES', 'django.po')
|
||||
new_file_path = str(file_path) + '_new'
|
||||
|
||||
# compile regex
|
||||
reg = re.compile(
|
||||
r"[a-zA-Z0-9]{1}" + # match any single letter and number # noqa: W504
|
||||
r"(?![^{\(\<]*[}\)\>])" + # that is not inside curly brackets, brackets or a tag # noqa: W504
|
||||
r"(?<![^\%][^\(][)][a-z])" + # that is not a specially formatted variable with singles # noqa: W504
|
||||
r"(?![^\\][\n])" # that is not a newline
|
||||
r'[a-zA-Z0-9]{1}' + # match any single letter and number # noqa: W504
|
||||
r'(?![^{\(\<]*[}\)\>])' + # that is not inside curly brackets, brackets or a tag # noqa: W504
|
||||
r'(?<![^\%][^\(][)][a-z])' + # that is not a specially formatted variable with singles # noqa: W504
|
||||
r'(?![^\\][\n])' # that is not a newline
|
||||
)
|
||||
last_string = ''
|
||||
|
||||
# loop through input file lines
|
||||
with open(file_path, "rt") as file_org:
|
||||
with open(new_file_path, "wt") as file_new:
|
||||
with open(file_path, 'rt') as file_org:
|
||||
with open(new_file_path, 'wt') as file_new:
|
||||
for line in file_org:
|
||||
if line.startswith('msgstr "'):
|
||||
# write output -> replace regex matches with x in the read in (multi)string
|
||||
file_new.write(f'msgstr "{reg.sub("x", last_string[7:-2])}"\n')
|
||||
last_string = "" # reset (multi)string
|
||||
last_string = '' # reset (multi)string
|
||||
elif line.startswith('msgid "'):
|
||||
last_string = last_string + line # a new translatable string starts -> start append
|
||||
last_string = (
|
||||
last_string + line
|
||||
) # a new translatable string starts -> start append
|
||||
file_new.write(line)
|
||||
else:
|
||||
if last_string:
|
||||
last_string = last_string + line # a string is being read in -> continue appending
|
||||
last_string = (
|
||||
last_string + line
|
||||
) # a string is being read in -> continue appending
|
||||
file_new.write(line)
|
||||
|
||||
# change out translation files
|
||||
@@ -648,9 +745,9 @@ def test_translations(c):
|
||||
new_file_path.rename(file_path)
|
||||
|
||||
# compile languages
|
||||
print("Compile languages ...")
|
||||
print("========================================")
|
||||
manage(c, "compilemessages")
|
||||
print('Compile languages ...')
|
||||
print('========================================')
|
||||
manage(c, 'compilemessages')
|
||||
|
||||
# reset cwd
|
||||
os.chdir(base_path)
|
||||
@@ -668,7 +765,9 @@ def test_translations(c):
|
||||
'coverage': 'Run code coverage analysis (requires coverage package)',
|
||||
}
|
||||
)
|
||||
def test(c, disable_pty=False, runtest='', migrations=False, report=False, coverage=False):
|
||||
def test(
|
||||
c, disable_pty=False, runtest='', migrations=False, report=False, coverage=False
|
||||
):
|
||||
"""Run unit-tests for InvenTree codebase.
|
||||
|
||||
To run only certain test, use the argument --runtest.
|
||||
@@ -713,7 +812,7 @@ def test(c, disable_pty=False, runtest='', migrations=False, report=False, cover
|
||||
|
||||
|
||||
@task(help={'dev': 'Set up development environment at the end'})
|
||||
def setup_test(c, ignore_update=False, dev=False, path="inventree-demo-dataset"):
|
||||
def setup_test(c, ignore_update=False, dev=False, path='inventree-demo-dataset'):
|
||||
"""Setup a testing environment."""
|
||||
from InvenTree.InvenTree.config import get_media_dir
|
||||
|
||||
@@ -722,41 +821,43 @@ def setup_test(c, ignore_update=False, dev=False, path="inventree-demo-dataset")
|
||||
|
||||
# Remove old data directory
|
||||
if os.path.exists(path):
|
||||
print("Removing old data ...")
|
||||
print('Removing old data ...')
|
||||
c.run(f'rm {path} -r')
|
||||
|
||||
# Get test data
|
||||
print("Cloning demo dataset ...")
|
||||
print('Cloning demo dataset ...')
|
||||
c.run(f'git clone https://github.com/inventree/demo-dataset {path} -v --depth=1')
|
||||
print("========================================")
|
||||
print('========================================')
|
||||
|
||||
# Make sure migrations are done - might have just deleted sqlite database
|
||||
if not ignore_update:
|
||||
migrate(c)
|
||||
|
||||
# Load data
|
||||
print("Loading database records ...")
|
||||
print('Loading database records ...')
|
||||
import_records(c, filename=f'{path}/inventree_data.json', clear=True)
|
||||
|
||||
# Copy media files
|
||||
print("Copying media files ...")
|
||||
print('Copying media files ...')
|
||||
src = Path(path).joinpath('media').resolve()
|
||||
dst = get_media_dir()
|
||||
|
||||
shutil.copytree(src, dst, dirs_exist_ok=True)
|
||||
|
||||
print("Done setting up test environment...")
|
||||
print("========================================")
|
||||
print('Done setting up test environment...')
|
||||
print('========================================')
|
||||
|
||||
# Set up development setup if flag is set
|
||||
if dev:
|
||||
setup_dev(c)
|
||||
|
||||
|
||||
@task(help={
|
||||
'filename': "Output filename (default = 'schema.yml')",
|
||||
'overwrite': "Overwrite existing files without asking first (default = off/False)",
|
||||
})
|
||||
@task(
|
||||
help={
|
||||
'filename': "Output filename (default = 'schema.yml')",
|
||||
'overwrite': 'Overwrite existing files without asking first (default = off/False)',
|
||||
}
|
||||
)
|
||||
def schema(c, filename='schema.yml', overwrite=False):
|
||||
"""Export current API schema."""
|
||||
check_file_existance(filename, overwrite)
|
||||
@@ -773,7 +874,8 @@ def version(c):
|
||||
# Gather frontend version information
|
||||
_, node, yarn = node_available(versions=True)
|
||||
|
||||
print(f"""
|
||||
print(
|
||||
f"""
|
||||
InvenTree - inventree.org
|
||||
The Open-Source Inventory Management System\n
|
||||
|
||||
@@ -792,13 +894,16 @@ Node {node if node else 'N/A'}
|
||||
Yarn {yarn if yarn else 'N/A'}
|
||||
|
||||
Commit hash:{InvenTreeVersion.inventreeCommitHash()}
|
||||
Commit date:{InvenTreeVersion.inventreeCommitDate()}""")
|
||||
Commit date:{InvenTreeVersion.inventreeCommitDate()}"""
|
||||
)
|
||||
if len(sys.argv) == 1 and sys.argv[0].startswith('/opt/inventree/env/lib/python'):
|
||||
print("""
|
||||
print(
|
||||
"""
|
||||
You are probably running the package installer / single-line installer. Please mentioned that in any bug reports!
|
||||
|
||||
Use '--list' for a list of available commands
|
||||
Use '--help' for help on a specific command""")
|
||||
Use '--help' for help on a specific command"""
|
||||
)
|
||||
|
||||
|
||||
@task()
|
||||
@@ -826,8 +931,8 @@ def frontend_install(c):
|
||||
Args:
|
||||
c: Context variable
|
||||
"""
|
||||
print("Installing frontend dependencies")
|
||||
yarn(c, "yarn install")
|
||||
print('Installing frontend dependencies')
|
||||
yarn(c, 'yarn install')
|
||||
|
||||
|
||||
@task
|
||||
@@ -837,9 +942,9 @@ def frontend_trans(c):
|
||||
Args:
|
||||
c: Context variable
|
||||
"""
|
||||
print("Compiling frontend translations")
|
||||
yarn(c, "yarn run extract")
|
||||
yarn(c, "yarn run compile")
|
||||
print('Compiling frontend translations')
|
||||
yarn(c, 'yarn run extract')
|
||||
yarn(c, 'yarn run compile')
|
||||
|
||||
|
||||
@task
|
||||
@@ -849,8 +954,8 @@ def frontend_build(c):
|
||||
Args:
|
||||
c: Context variable
|
||||
"""
|
||||
print("Building frontend")
|
||||
yarn(c, "yarn run build --emptyOutDir")
|
||||
print('Building frontend')
|
||||
yarn(c, 'yarn run build --emptyOutDir')
|
||||
|
||||
|
||||
@task
|
||||
@@ -860,19 +965,29 @@ def frontend_dev(c):
|
||||
Args:
|
||||
c: Context variable
|
||||
"""
|
||||
print("Starting frontend development server")
|
||||
yarn(c, "yarn run dev")
|
||||
print('Starting frontend development server')
|
||||
yarn(c, 'yarn run dev')
|
||||
|
||||
|
||||
@task(help={
|
||||
'ref': "git ref, default: current git ref",
|
||||
'tag': "git tag to look for release",
|
||||
'file': "destination to frontend-build.zip file",
|
||||
'repo': "GitHub repository, default: InvenTree/inventree",
|
||||
'extract': "Also extract and place at the correct destination, default: True",
|
||||
'clean': "Delete old files from InvenTree/web/static/web first, default: True",
|
||||
})
|
||||
def frontend_download(c, ref=None, tag=None, file=None, repo="InvenTree/inventree", extract=True, clean=True):
|
||||
@task(
|
||||
help={
|
||||
'ref': 'git ref, default: current git ref',
|
||||
'tag': 'git tag to look for release',
|
||||
'file': 'destination to frontend-build.zip file',
|
||||
'repo': 'GitHub repository, default: InvenTree/inventree',
|
||||
'extract': 'Also extract and place at the correct destination, default: True',
|
||||
'clean': 'Delete old files from InvenTree/web/static/web first, default: True',
|
||||
}
|
||||
)
|
||||
def frontend_download(
|
||||
c,
|
||||
ref=None,
|
||||
tag=None,
|
||||
file=None,
|
||||
repo='InvenTree/inventree',
|
||||
extract=True,
|
||||
clean=True,
|
||||
):
|
||||
"""Download a pre-build frontend from GitHub if you dont want to install nodejs on your machine.
|
||||
|
||||
There are 3 possibilities to install the frontend:
|
||||
@@ -894,7 +1009,7 @@ def frontend_download(c, ref=None, tag=None, file=None, repo="InvenTree/inventre
|
||||
import requests
|
||||
|
||||
# globals
|
||||
default_headers = {"Accept": "application/vnd.github.v3+json"}
|
||||
default_headers = {'Accept': 'application/vnd.github.v3+json'}
|
||||
|
||||
# helper functions
|
||||
def find_resource(resource, key, value):
|
||||
@@ -908,30 +1023,34 @@ def frontend_download(c, ref=None, tag=None, file=None, repo="InvenTree/inventre
|
||||
if not extract:
|
||||
return
|
||||
|
||||
dest_path = Path(__file__).parent / "InvenTree/web/static/web"
|
||||
dest_path = Path(__file__).parent / 'InvenTree/web/static/web'
|
||||
|
||||
# if clean, delete static/web directory
|
||||
if clean:
|
||||
shutil.rmtree(dest_path, ignore_errors=True)
|
||||
os.makedirs(dest_path)
|
||||
print(f"Cleaned directory: {dest_path}")
|
||||
print(f'Cleaned directory: {dest_path}')
|
||||
|
||||
# unzip build to static folder
|
||||
with ZipFile(file, "r") as zip_ref:
|
||||
with ZipFile(file, 'r') as zip_ref:
|
||||
zip_ref.extractall(dest_path)
|
||||
|
||||
print(f"Unzipped downloaded frontend build to: {dest_path}")
|
||||
print(f'Unzipped downloaded frontend build to: {dest_path}')
|
||||
|
||||
def handle_download(url):
|
||||
# download frontend-build.zip to temporary file
|
||||
with requests.get(url, headers=default_headers, stream=True, allow_redirects=True) as response, NamedTemporaryFile(suffix=".zip") as dst:
|
||||
with requests.get(
|
||||
url, headers=default_headers, stream=True, allow_redirects=True
|
||||
) as response, NamedTemporaryFile(suffix='.zip') as dst:
|
||||
response.raise_for_status()
|
||||
|
||||
# auto decode the gzipped raw data
|
||||
response.raw.read = functools.partial(response.raw.read, decode_content=True)
|
||||
with open(dst.name, "wb") as f:
|
||||
response.raw.read = functools.partial(
|
||||
response.raw.read, decode_content=True
|
||||
)
|
||||
with open(dst.name, 'wb') as f:
|
||||
shutil.copyfileobj(response.raw, f)
|
||||
print(f"Downloaded frontend build to temporary file: {dst.name}")
|
||||
print(f'Downloaded frontend build to temporary file: {dst.name}')
|
||||
|
||||
handle_extract(dst.name)
|
||||
|
||||
@@ -942,51 +1061,72 @@ def frontend_download(c, ref=None, tag=None, file=None, repo="InvenTree/inventre
|
||||
|
||||
# check arguments
|
||||
if ref is not None and tag is not None:
|
||||
print("[ERROR] Do not set ref and tag.")
|
||||
print('[ERROR] Do not set ref and tag.')
|
||||
return
|
||||
|
||||
if ref is None and tag is None:
|
||||
try:
|
||||
ref = subprocess.check_output(["git", "rev-parse", "HEAD"], encoding="utf-8").strip()
|
||||
ref = subprocess.check_output(
|
||||
['git', 'rev-parse', 'HEAD'], encoding='utf-8'
|
||||
).strip()
|
||||
except Exception:
|
||||
print("[ERROR] Cannot get current ref via 'git rev-parse HEAD'")
|
||||
return
|
||||
|
||||
if ref is None and tag is None:
|
||||
print("[ERROR] Either ref or tag needs to be set.")
|
||||
print('[ERROR] Either ref or tag needs to be set.')
|
||||
|
||||
if tag:
|
||||
tag = tag.lstrip("v")
|
||||
tag = tag.lstrip('v')
|
||||
try:
|
||||
handle_download(f"https://github.com/{repo}/releases/download/{tag}/frontend-build.zip")
|
||||
handle_download(
|
||||
f'https://github.com/{repo}/releases/download/{tag}/frontend-build.zip'
|
||||
)
|
||||
except Exception as e:
|
||||
if not isinstance(e, requests.HTTPError):
|
||||
raise e
|
||||
print(f"""[ERROR] An Error occurred. Unable to download frontend build, release or build does not exist,
|
||||
print(
|
||||
f"""[ERROR] An Error occurred. Unable to download frontend build, release or build does not exist,
|
||||
try downloading the frontend-build.zip yourself via: https://github.com/{repo}/releases
|
||||
Then try continuing by running: invoke frontend-download --file <path-to-downloaded-zip-file>""")
|
||||
Then try continuing by running: invoke frontend-download --file <path-to-downloaded-zip-file>"""
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
if ref:
|
||||
# get workflow run from all workflow runs on that particular ref
|
||||
workflow_runs = requests.get(f"https://api.github.com/repos/{repo}/actions/runs?head_sha={ref}", headers=default_headers).json()
|
||||
workflow_runs = requests.get(
|
||||
f'https://api.github.com/repos/{repo}/actions/runs?head_sha={ref}',
|
||||
headers=default_headers,
|
||||
).json()
|
||||
|
||||
if not (qc_run := find_resource(workflow_runs["workflow_runs"], "name", "QC")):
|
||||
print("[ERROR] Cannot find any workflow runs for current sha")
|
||||
if not (qc_run := find_resource(workflow_runs['workflow_runs'], 'name', 'QC')):
|
||||
print('[ERROR] Cannot find any workflow runs for current sha')
|
||||
return
|
||||
print(f"Found workflow {qc_run['name']} (run {qc_run['run_number']}-{qc_run['run_attempt']})")
|
||||
print(
|
||||
f"Found workflow {qc_run['name']} (run {qc_run['run_number']}-{qc_run['run_attempt']})"
|
||||
)
|
||||
|
||||
# get frontend-build artifact from all artifacts available for this workflow run
|
||||
artifacts = requests.get(qc_run["artifacts_url"], headers=default_headers).json()
|
||||
if not (frontend_artifact := find_resource(artifacts["artifacts"], "name", "frontend-build")):
|
||||
print("[ERROR] Cannot find frontend-build.zip attachment for current sha")
|
||||
artifacts = requests.get(
|
||||
qc_run['artifacts_url'], headers=default_headers
|
||||
).json()
|
||||
if not (
|
||||
frontend_artifact := find_resource(
|
||||
artifacts['artifacts'], 'name', 'frontend-build'
|
||||
)
|
||||
):
|
||||
print('[ERROR] Cannot find frontend-build.zip attachment for current sha')
|
||||
return
|
||||
print(f"Found artifact {frontend_artifact['name']} with id {frontend_artifact['id']} ({frontend_artifact['size_in_bytes']/1e6:.2f}MB).")
|
||||
print(
|
||||
f"Found artifact {frontend_artifact['name']} with id {frontend_artifact['id']} ({frontend_artifact['size_in_bytes']/1e6:.2f}MB)."
|
||||
)
|
||||
|
||||
print(f"""
|
||||
print(
|
||||
f"""
|
||||
GitHub doesn't allow artifact downloads from anonymous users. Either download the following file
|
||||
via your signed in browser, or consider using a point release download via invoke frontend-download --tag <git-tag>
|
||||
|
||||
Download: https://github.com/{repo}/suites/{qc_run['check_suite_id']}/artifacts/{frontend_artifact['id']} manually and
|
||||
continue by running: invoke frontend-download --file <path-to-downloaded-zip-file>""")
|
||||
continue by running: invoke frontend-download --file <path-to-downloaded-zip-file>"""
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user