Compare commits

...

25 Commits

Author SHA1 Message Date
github-actions[bot]
0804ecf538 Handle null case for delivery_methods (#6411) (#6413)
(cherry picked from commit fc86064bd1)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-02-05 21:29:24 +11:00
github-actions[bot]
39176d9a19 Use registry.get_plugin() (#6408) (#6409)
- Instead of registry.plugins.get()
- get_plugin checks registry hash
- performs registry reload if necessary

(cherry picked from commit e9c6dd8273)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-02-05 17:18:13 +11:00
github-actions[bot]
75f507d4c7 BuildOrder: Fix bug where test templates are requested for a non-trackable output (#6387) (#6388)
(cherry picked from commit c9c93bce39)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-02-02 16:19:56 +11:00
Lukas
c826b8231f Backport: Fix urls loading for plugin testing (#6383) (#6384) 2024-02-02 07:00:14 +11:00
github-actions[bot]
a05e07b2dd Disable cache for report helpers (#6370) (#6371)
- Can lead to unintended consequences where REPORT_DEBUG_MODE toggles

(cherry picked from commit 2557383892)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-01-31 15:58:45 +11:00
github-actions[bot]
fc4e20f1b7 Enable existing_image on PATCH requests (#6126) (#6369)
* Enable existing_image on PATCH requests

* Fix CI problems

* Solution

* Change check to whitelist and add unit tests

(cherry picked from commit 9db3efa085)

Co-authored-by: Lavissa <lavissawow@gmail.com>
2024-01-31 08:20:50 +11:00
github-actions[bot]
9494a8ad43 Bug fix for javascript rendering (#6362) (#6363)
* Check template name when rendering also

* Update i18n.py

Enforce stringiness

(cherry picked from commit b42f3de357)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-01-30 11:03:35 +11:00
Oliver
c12e6dbf42 Backport of #6335 (#6338)
- Ref: https://github.com/inventree/InvenTree/pull/6335
- Fixes bug with regard to splitting stock items
2024-01-25 08:33:23 +11:00
Oliver
3daf85c3d0 Update version.py (#6329)
Bump version number to 0.13.5
2024-01-24 15:51:51 +11:00
github-actions[bot]
8fe5dcfafb fix(docker): SELinux volume labels (#6330) (#6331)
When mounting volumes into containers with SELinux
enabled on the host the z option must be specified

(cherry picked from commit 0a94758d63)

Co-authored-by: Philipp Fruck <dev@p-fruck.de>
2024-01-24 09:11:12 +11:00
github-actions[bot]
6a66be36f2 Specify empty OIDC prefix (#6324) (#6327)
* Specify empty OIDC prefix

Ref: https://github.com/inventree/InvenTree/discussions/6273

* Add extra comment around version information

* Update InvenTree/InvenTree/settings.py

Co-authored-by: Philipp Fruck <dev@p-fruck.de>

---------

Co-authored-by: Philipp Fruck <dev@p-fruck.de>
(cherry picked from commit d8f69c0609)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-01-24 06:32:55 +11:00
Oliver
12cd6a915b Update version.py (#6313)
Bump version number to 0.13.4
2024-01-22 17:07:05 +11:00
Oliver
e81349e70e SSO Error Improvememts (#6246) (#6304)
* Improve exception handling

* Extract extra information from SSO auth failure

* Revert order of ignore check
2024-01-22 00:41:00 +11:00
github-actions[bot]
2c6bc8852b django-allauth==0.6.1 (#6301) (#6302)
(cherry picked from commit c5d0902379)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-01-21 10:30:07 +11:00
Oliver
774d0f50fd JS translation fix (#6288) (#6289)
* Implement {% jstrans %} template

- Forces string escaping in translated text
- Required for js written in templates

* Fix part_base.html

* Update .html files

- Change any templated javascript code to use {% jstrans %}

* Update .js files

- Explicitly use {% jstrans %}

* Update CI checks
2024-01-19 13:37:48 +11:00
Oliver
3a37947dbb Import Fix (#6274) (#6277)
* Fix check for static dir

* Fix export price field for SalesOrderLineItem

* Automatically detect which non-nullable fields need conversion

* Fix bug during import

- fulfilled_quantity and allocated_quantity must have a pk
- Cannot work before imported!
2024-01-18 22:37:08 +11:00
github-actions[bot]
e0b2895ef5 Do not create owners when importing data (#6271) (#6272)
- Prevent issues due to duplicate Owner objects

(cherry picked from commit c3a5d777b1)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2024-01-18 10:44:11 +11:00
Oliver
9aa859a428 Update version.py (#6266)
Bump version number to 0.13.3
2024-01-17 23:34:17 +11:00
Oliver
ddd65cff5e Fix news feed task timeout (#6250) (#6265)
Co-authored-by: Lavissa <lavissawow@gmail.com>
2024-01-17 21:07:50 +11:00
Oliver
1abdb1fd46 Backport changes to tasks.py (#6256)
- Fixes ongoing issues with import/export
2024-01-16 21:47:41 +11:00
Bobbe
2b0ef2bc61 Fix supplier barcode order numbers (#6158) (#6239)
* Add tme barcode CPO field

* Fix LCSC order number field

* Fix mouser order number field

* Fix get_purchase_orders logic

* Refine get_purchase_orders logic

* Slightly refactor get_purchase_orders logic
2024-01-14 13:05:14 +11:00
github-actions[bot]
f259fa6792 Add tests for get_purchase_orders logic (#6236) (#6238)
(cherry picked from commit d2d59e0709)

Co-authored-by: Bobbe <34186858+30350n@users.noreply.github.com>
2024-01-14 12:49:31 +11:00
Bobbe
55f09d8723 Fix reassigning supplier barcodes (#6162) (#6237)
* Ignore 3rd party barcode plugins when assigning barcode

* Use single quotes
2024-01-14 12:45:31 +11:00
Oliver
4973d9c726 Fix rendering of supplier pack quantity (#6228)
Backport of https://github.com/inventree/InvenTree/pull/6226
2024-01-13 20:01:19 +11:00
Oliver
e22779872e Update version.py (#6220)
Bump version number to 0.13.2
2024-01-13 00:30:41 +11:00
81 changed files with 2674 additions and 2116 deletions

View File

@@ -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:

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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" %}',
}
);
});

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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: {},
},

View File

@@ -203,7 +203,7 @@ $('#parameter-create').click(function() {
hidden: true,
}
},
title: '{% trans "Add Parameter" %}',
title: '{% jstrans "Add Parameter" %}',
refreshTable: '#parameter-table',
});
});

View File

@@ -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();
}

View File

@@ -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 ''

View File

@@ -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']

View File

@@ -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" %}',
}
);
});

View File

@@ -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" %}',
}
);
});

View File

@@ -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" %}',
}
);
});

View File

@@ -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

View File

@@ -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:

View File

@@ -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',
});
});

View File

@@ -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);
});

View File

@@ -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: {},

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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',

View File

@@ -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]:

View File

@@ -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

View File

@@ -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,
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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',
});
});

View File

@@ -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,
});

View File

@@ -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" %}',
}
);
});

View File

@@ -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

View File

@@ -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' %}",
});

View File

@@ -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();
}

View File

@@ -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: {

View File

@@ -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, {

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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" %}',
);
}

View File

@@ -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',
);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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>`;

View File

@@ -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,

View File

@@ -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',
});
}

View File

@@ -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);

View File

@@ -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
);

View File

@@ -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);

View File

@@ -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>`);
}
/**

View File

@@ -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

View File

@@ -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 += `&nbsp;<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: {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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>&nbsp;-&nbsp;${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

View File

@@ -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" %}',
}
};
}

View File

@@ -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" %}';
},
};

View File

@@ -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')

View File

@@ -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")

View File

@@ -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:

View File

@@ -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
View File

@@ -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>"""
)