2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-07-05 06:32:55 +00:00

Merge branch 'master' into custom-states

This commit is contained in:
Oliver
2024-11-11 09:56:14 +11:00
committed by GitHub
136 changed files with 10650 additions and 9666 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
+3 -3
View File
@@ -39,7 +39,7 @@ jobs:
docker: ${{ steps.filter.outputs.docker }}
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
id: filter
with:
@@ -66,9 +66,9 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Set Up Python ${{ env.python_version }}
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # pin@v5.2.0
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # pin@v5.3.0
with:
python-version: ${{ env.python_version }}
- name: Version Check
+16 -16
View File
@@ -38,7 +38,7 @@ jobs:
force: ${{ steps.force.outputs.force }}
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # pin@v3.0.2
id: filter
with:
@@ -70,7 +70,7 @@ jobs:
needs: ["pre-commit"]
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -92,9 +92,9 @@ jobs:
if: needs.paths-filter.outputs.server == 'true' || needs.paths-filter.outputs.frontend == 'true' || needs.paths-filter.outputs.force == 'true'
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Set up Python ${{ env.python_version }}
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # pin@v5.2.0
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # pin@v5.3.0
with:
python-version: ${{ env.python_version }}
cache: "pip"
@@ -113,9 +113,9 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Set up Python ${{ env.python_version }}
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # pin@v5.2.0
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # pin@v5.3.0
with:
python-version: ${{ env.python_version }}
- name: Check Config
@@ -149,7 +149,7 @@ jobs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -211,7 +211,7 @@ jobs:
version: ${{ needs.schema.outputs.version }}
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
name: Checkout Code
with:
repository: inventree/schema
@@ -250,7 +250,7 @@ jobs:
INVENTREE_SITE_URL: http://127.0.0.1:12345
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -292,7 +292,7 @@ jobs:
python_version: ${{ matrix.python_version }}
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -346,7 +346,7 @@ jobs:
- 6379:6379
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -390,7 +390,7 @@ jobs:
- 3306:3306
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -429,7 +429,7 @@ jobs:
- 5432:5432
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -460,7 +460,7 @@ jobs:
INVENTREE_PLUGINS_ENABLED: false
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
name: Checkout Code
- name: Environment Setup
uses: ./.github/actions/setup
@@ -517,7 +517,7 @@ jobs:
VITE_COVERAGE: true
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -565,7 +565,7 @@ jobs:
timeout-minutes: 60
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
+3 -3
View File
@@ -18,7 +18,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout Code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Version Check
run: |
pip install --require-hashes -r contrib/dev_reqs/requirements.txt
@@ -39,7 +39,7 @@ jobs:
contents: write
attestations: write
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -49,7 +49,7 @@ jobs:
- name: Build frontend
run: cd src/frontend && npm run compile && npm run build
- name: Create SBOM for frontend
uses: anchore/sbom-action@8d0a6505bf28ced3e85154d13dc6af83299e13f1 # pin@v0
uses: anchore/sbom-action@251a468eed47e5082b105c3ba6ee500c0e65a764 # pin@v0
with:
artifact-name: frontend-build.spdx
path: src/frontend
+2 -2
View File
@@ -32,7 +32,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
@@ -67,6 +67,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
with:
sarif_file: results.sarif
+2 -2
View File
@@ -31,7 +31,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # pin@v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2
- name: Environment Setup
uses: ./.github/actions/setup
with:
@@ -51,7 +51,7 @@ jobs:
git reset --hard
git reset HEAD~
- name: crowdin action
uses: crowdin/github-action@95d6e895e871c3c7acf0cfb962f296baa41e63c6 # pin@v2
uses: crowdin/github-action@2d540f18b0a416b1fbf2ee5be35841bd380fc1da # pin@v2
with:
upload_sources: true
upload_translations: false
Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

+13
View File
@@ -18,6 +18,19 @@ If there are some icons missing in the tabler icons package, users can even inst
A stock location type represents a specific type of location (e.g. one specific size of drawer, shelf, ... or box) which can be assigned to multiple stock locations. In the first place, it is used to specify an icon and having the icon in sync for all locations that use this location type, but it also serves as a data field to quickly see what type of location this is. It is planned to add e.g. drawer dimension information to the location type to add a "find a matching, empty stock location" tool.
## External Stock Location
An external stock location can be used to indicate that items in there might not be available
for immediate usage. Stock items in an external location are marked with an additional icon
in the build order line items view where the material is allocated.
{% with id="stock_external_icon", url="stock/stock_external_icon.png", description="External stock indication" %}
{% include 'img.html' %}
{% endwith %}
Anyhow there is no limitation on the stock item. It can be allocated as usual.
The external flag does not get inherited to sublocations.
## Stock Item
A *Stock Item* is an actual instance of a [*Part*](../part/part.md) item. It represents a physical quantity of the *Part* in a specific location.
+2 -1
View File
@@ -44,7 +44,8 @@ def top_level_path(path: str) -> str:
path = path.strip()
key = path.split('/')[1]
parts = path.split('/')
key = parts[1] if len(parts) > 1 else parts[0]
if key in SPECIAL_PATHS:
return key
+42 -4
View File
@@ -30,7 +30,6 @@ from .helpers_email import is_email_configured
from .mixins import ListAPI, RetrieveUpdateAPI
from .status import check_system_health, is_worker_running
from .version import inventreeApiText
from .views import AjaxView
logger = logging.getLogger('inventree')
@@ -196,8 +195,35 @@ class VersionTextView(ListAPI):
return JsonResponse(inventreeApiText())
class InfoView(AjaxView):
"""Simple JSON endpoint for InvenTree information.
class InfoApiSerializer(serializers.Serializer):
"""InvenTree server information - some information might be blanked if called without elevated credentials."""
server = serializers.CharField(read_only=True)
version = serializers.CharField(read_only=True)
instance = serializers.CharField(read_only=True)
apiVersion = serializers.IntegerField(read_only=True) # noqa: N815
worker_running = serializers.BooleanField(read_only=True)
worker_count = serializers.IntegerField(read_only=True)
worker_pending_tasks = serializers.IntegerField(read_only=True)
plugins_enabled = serializers.BooleanField(read_only=True)
plugins_install_disabled = serializers.BooleanField(read_only=True)
active_plugins = serializers.JSONField(read_only=True)
email_configured = serializers.BooleanField(read_only=True)
debug_mode = serializers.BooleanField(read_only=True)
docker_mode = serializers.BooleanField(read_only=True)
default_locale = serializers.ChoiceField(
choices=settings.LOCALE_CODES, read_only=True
)
system_health = serializers.BooleanField(read_only=True)
database = serializers.CharField(read_only=True)
platform = serializers.CharField(read_only=True)
installer = serializers.CharField(read_only=True)
target = serializers.CharField(read_only=True)
django_admin = serializers.CharField(read_only=True)
class InfoView(APIView):
"""JSON endpoint for InvenTree server information.
Use to confirm that the server is running, etc.
"""
@@ -208,6 +234,13 @@ class InfoView(AjaxView):
"""Return the current number of outstanding background tasks."""
return OrmQ.objects.count()
@extend_schema(
responses={
200: OpenApiResponse(
response=InfoApiSerializer, description='InvenTree server information'
)
}
)
def get(self, request, *args, **kwargs):
"""Serve current server information."""
is_staff = request.user.is_staff
@@ -261,7 +294,7 @@ class InfoView(AjaxView):
return False
class NotFoundView(AjaxView):
class NotFoundView(APIView):
"""Simple JSON view when accessing an invalid API view."""
permission_classes = [permissions.AllowAny]
@@ -280,22 +313,27 @@ class NotFoundView(AjaxView):
"""Return 404."""
return self.not_found(request)
@extend_schema(exclude=True)
def get(self, request, *args, **kwargs):
"""Return 404."""
return self.not_found(request)
@extend_schema(exclude=True)
def post(self, request, *args, **kwargs):
"""Return 404."""
return self.not_found(request)
@extend_schema(exclude=True)
def patch(self, request, *args, **kwargs):
"""Return 404."""
return self.not_found(request)
@extend_schema(exclude=True)
def put(self, request, *args, **kwargs):
"""Return 404."""
return self.not_found(request)
@extend_schema(exclude=True)
def delete(self, request, *args, **kwargs):
"""Return 404."""
return self.not_found(request)
+15 -1
View File
@@ -1,13 +1,27 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 277
INVENTREE_API_VERSION = 280
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v280 - 2024-11-10 : https://github.com/inventree/InvenTree/pull/8461
- Makes schema for API information endpoint more informing
- Removes general not found endpoint
v279 - 2024-11-09 : https://github.com/inventree/InvenTree/pull/8458
- Adds "order_outstanding" and "part" filters to the BuildLine API endpoint
- Adds "order_outstanding" filter to the SalesOrderLineItem API endpoint
v278 - 2024-11-07 : https://github.com/inventree/InvenTree/pull/8445
- Updates to the SalesOrder API endpoints
- Add "shipment count" information to the SalesOrder API endpoints
- Allow null value for SalesOrderAllocation.shipment field
- Additional filtering options for allocation endpoints
v277 - 2024-11-01 : https://github.com/inventree/InvenTree/pull/8278
- Allow build order list to be filtered by "outstanding" (alias for "active")
+18 -17
View File
@@ -36,6 +36,24 @@ def get_token_from_request(request):
return None
# List of target URL endpoints where *do not* want to redirect to
urls = [
reverse_lazy('account_login'),
reverse_lazy('account_logout'),
reverse_lazy('admin:login'),
reverse_lazy('admin:logout'),
]
# Do not redirect requests to any of these paths
paths_ignore = [
'/api/',
'/auth/',
'/js/', # TODO - remove when CUI is removed
settings.MEDIA_URL,
settings.STATIC_URL,
]
class AuthRequiredMiddleware:
"""Check for user to be authenticated."""
@@ -108,23 +126,6 @@ class AuthRequiredMiddleware:
if not authorized:
path = request.path_info
# List of URL endpoints we *do not* want to redirect to
urls = [
reverse_lazy('account_login'),
reverse_lazy('account_logout'),
reverse_lazy('admin:login'),
reverse_lazy('admin:logout'),
]
# Do not redirect requests to any of these paths
paths_ignore = [
'/api/',
'/auth/',
'/js/',
settings.MEDIA_URL,
settings.STATIC_URL,
]
if path not in urls and not any(
path.startswith(p) for p in paths_ignore
):
@@ -2,7 +2,6 @@
from django.conf import settings
from django.http import Http404
from django.test import tag
from django.urls import reverse
from error_report.models import Error
@@ -11,8 +10,6 @@ from InvenTree.exceptions import log_error
from InvenTree.unit_test import InvenTreeTestCase
# TODO change test to not rely on CUI
@tag('cui')
class MiddlewareTests(InvenTreeTestCase):
"""Test for middleware functions."""
@@ -32,25 +29,17 @@ class MiddlewareTests(InvenTreeTestCase):
# logout
self.client.logout()
# check that static files go through
# TODO @matmair re-enable this check
# self.check_path('/static/css/inventree.css', 302)
# check that account things go through
self.check_path(reverse('account_login'))
# logout goes directly to login
self.check_path(reverse('account_logout'))
# check that frontend code is redirected to login
response = self.check_path(reverse('stats'), 302)
self.assertEqual(response.url, '/accounts/login/?next=/stats/')
# check that a 401 is raised
self.check_path(reverse('settings.js'), 401)
response = self.check_path(reverse('index'), 302)
self.assertEqual(response.url, '/accounts/login/?next=/')
def test_token_auth(self):
"""Test auth with token auth."""
target = reverse('settings.js') # for PUI only use 'api-license'
# get token
response = self.client.get(reverse('api-token'), format='json', data={})
token = response.data['token']
@@ -58,16 +47,17 @@ class MiddlewareTests(InvenTreeTestCase):
# logout
self.client.logout()
# this should raise a 401
self.check_path(reverse('settings.js'), 401)
# request with token
self.check_path(reverse('settings.js'), HTTP_Authorization=f'Token {token}')
self.check_path(target, 401)
# Request with broken token
self.check_path(reverse('settings.js'), 401, HTTP_Authorization='Token abcd123')
self.check_path(target, 401, HTTP_Authorization='Token abcd123')
# should still fail without token
self.check_path(reverse('settings.js'), 401)
self.check_path(target, 401)
# request with token
self.check_path(target, HTTP_Authorization=f'Token {token}')
def test_error_exceptions(self):
"""Test that ignored errors are not logged."""
@@ -80,7 +70,7 @@ class MiddlewareTests(InvenTreeTestCase):
# Test normal setup
check()
response = self.client.get(reverse('part-detail', kwargs={'pk': 9999}))
response = self.client.get(reverse('api-part-detail', kwargs={'pk': 9999}))
self.assertEqual(response.status_code, 404)
check()
@@ -92,13 +82,8 @@ class MiddlewareTests(InvenTreeTestCase):
# Test setup without ignored errors
settings.IGNORED_ERRORS = []
response = self.client.get(reverse('part-detail', kwargs={'pk': 9999}))
self.assertEqual(response.status_code, 404)
check(1)
# Test manual logging
try:
raise Http404
except Http404:
log_error('testpath')
check(2)
check(1)
+1 -3
View File
@@ -11,7 +11,7 @@ from django.conf import settings
from django.contrib.auth import get_user_model
from django.core import mail
from django.core.exceptions import ValidationError
from django.test import TestCase, override_settings, tag
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils import timezone
@@ -1493,8 +1493,6 @@ class MagicLoginTest(InvenTreeTestCase):
self.assertEqual(resp.wsgi_request.user, self.user)
# TODO - refactor to not use CUI
@tag('cui')
class MaintenanceModeTest(InvenTreeTestCase):
"""Unit tests for maintenance mode."""
+3 -1
View File
@@ -477,7 +477,9 @@ if settings.ENABLE_PLATFORM_FRONTEND:
frontendpatterns += [
path(
'accounts/login/',
RedirectView.as_view(url=settings.FRONTEND_URL_BASE, permanent=False),
RedirectView.as_view(
url=f'/{settings.FRONTEND_URL_BASE}', permanent=False
),
name='account_login',
)
]
+33
View File
@@ -357,6 +357,23 @@ class BuildLineFilter(rest_filters.FilterSet):
tracked = rest_filters.BooleanFilter(label=_('Tracked'), field_name='bom_item__sub_part__trackable')
testable = rest_filters.BooleanFilter(label=_('Testable'), field_name='bom_item__sub_part__testable')
part = rest_filters.ModelChoiceFilter(
queryset=part.models.Part.objects.all(),
label=_('Part'),
field_name='bom_item__sub_part',
)
order_outstanding = rest_filters.BooleanFilter(
label=_('Order Outstanding'),
method='filter_order_outstanding'
)
def filter_order_outstanding(self, queryset, name, value):
"""Filter by whether the associated BuildOrder is 'outstanding'."""
if str2bool(value):
return queryset.filter(build__status__in=BuildStatusGroups.ACTIVE_CODES)
return queryset.exclude(build__status__in=BuildStatusGroups.ACTIVE_CODES)
allocated = rest_filters.BooleanFilter(label=_('Allocated'), method='filter_allocated')
def filter_allocated(self, queryset, name, value):
@@ -383,12 +400,28 @@ class BuildLineFilter(rest_filters.FilterSet):
return queryset.exclude(flt)
class BuildLineEndpoint:
"""Mixin class for BuildLine API endpoints."""
queryset = BuildLine.objects.all()
serializer_class = build.serializers.BuildLineSerializer
def get_serializer(self, *args, **kwargs):
"""Return the serializer instance for this endpoint."""
kwargs['context'] = self.get_serializer_context()
try:
params = self.request.query_params
kwargs['part_detail'] = str2bool(params.get('part_detail', True))
kwargs['build_detail'] = str2bool(params.get('build_detail', False))
except AttributeError:
pass
return self.serializer_class(*args, **kwargs)
def get_source_build(self) -> Build:
"""Return the source Build object for the BuildLine queryset.
+23 -2
View File
@@ -1278,8 +1278,6 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
'pk',
'build',
'bom_item',
'bom_item_detail',
'part_detail',
'quantity',
# Build detail fields
@@ -1315,6 +1313,11 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
# Extra fields only for data export
'part_description',
'part_category_name',
# Extra detail (related field) serializers
'bom_item_detail',
'part_detail',
'build_detail',
]
read_only_fields = [
@@ -1323,6 +1326,19 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
'allocations',
]
def __init__(self, *args, **kwargs):
"""Determine which extra details fields should be included"""
part_detail = kwargs.pop('part_detail', True)
build_detail = kwargs.pop('build_detail', False)
super().__init__(*args, **kwargs)
if not part_detail:
self.fields.pop('part_detail', None)
if not build_detail:
self.fields.pop('build_detail', None)
# Build info fields
build_reference = serializers.CharField(source='build.reference', label=_('Build Reference'), read_only=True)
@@ -1362,6 +1378,7 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
)
part_detail = part_serializers.PartBriefSerializer(source='bom_item.sub_part', many=False, read_only=True, pricing=False)
build_detail = BuildSerializer(source='build', part_detail=False, many=False, read_only=True)
# Annotated (calculated) fields
@@ -1404,9 +1421,13 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali
"""
queryset = queryset.select_related(
'build',
'build__part',
'build__part__pricing_data',
'bom_item',
'bom_item__part',
'bom_item__part__pricing_data',
'bom_item__sub_part',
'bom_item__sub_part__pricing_data'
)
# Pre-fetch related fields
+2
View File
@@ -47,6 +47,8 @@ class BuildTestSimple(InvenTreeTestCase):
b1 = Build.objects.get(pk=1)
if settings.ENABLE_CLASSIC_FRONTEND:
self.assertEqual(b1.get_absolute_url(), '/build/1/')
else:
self.assertEqual(b1.get_absolute_url(), '/platform/manufacturing/build-order/1')
def test_is_complete(self):
"""Test build completion status"""
+4
View File
@@ -62,6 +62,10 @@ class CompanySimpleTest(TestCase):
c = Company.objects.get(pk=1)
if settings.ENABLE_CLASSIC_FRONTEND:
self.assertEqual(c.get_absolute_url(), '/company/1/')
else:
self.assertEqual(
c.get_absolute_url(), '/platform/purchasing/manufacturer/1'
)
def test_image_renamer(self):
"""Test the company image upload functionality."""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Language: ar_SA\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "نقطة نهاية API غير موجودة"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "المستخدم ليس لديه الصلاحية لعرض هذا النموذج"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Bulgarian\n"
"Language: bg_BG\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Не е намерена крайна точка на API"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Потребителя няма нужното разрешение, за да вижда този модел"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Czech\n"
"Language: cs_CZ\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API endpoint nebyl nalezen"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Uživatel nemá právo zobrazit tento model"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Danish\n"
"Language: da_DK\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API endpoint ikke fundet"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Bruger har ikke tilladelse til at se denne model"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API-Endpunkt nicht gefunden"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Benutzer hat keine Berechtigung, dieses Modell anzuzeigen"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Greek\n"
"Language: el_GR\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Το API endpoint δε βρέθηκε"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Δεν έχετε δικαιώματα να το δείτε αυτό"
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 10:24+0000\n"
"POT-Creation-Date: 2024-11-08 08:37+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,23 +18,23 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "endpoint API no encontrado"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "El usuario no tiene permiso para ver este modelo"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Spanish, Mexico\n"
"Language: es_MX\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Endpoint de API no encontrado"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "El usuario no tiene permiso para ver este modelo"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Estonian\n"
"Language: et_EE\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Teil ei ole selle lehe vaatamiseks luba"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Persian\n"
"Language: fa_IR\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Address e API peida nashod"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "کاربر سطح دسترسی نمایش این مدل را ندارد"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Finnish\n"
"Language: fi_FI\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API-rajapintaa ei löydy"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Käyttäjän oikeudet eivät riitä kohteen tarkastelemiseen"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 10:27\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Point de terminaison de l'API introuvable"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "L'utilisateur n'a pas la permission de voir ce modèle"
@@ -2249,7 +2249,7 @@ msgstr "Utilisateur ou groupe responsable de ce projet"
#: common/models.py:783 common/models.py:2236 common/models.py:2613
msgid "Settings key"
msgstr ""
msgstr "Paramétrés des touches"
#: common/models.py:787
msgid "Settings value"
@@ -5979,7 +5979,7 @@ msgstr ""
#: order/serializers.py:93
msgid "Copy Lines"
msgstr ""
msgstr "Copier des lignes"
#: order/serializers.py:94
msgid "Copy line items from the original order"
@@ -5987,7 +5987,7 @@ msgstr ""
#: order/serializers.py:100
msgid "Copy Extra Lines"
msgstr ""
msgstr "Copier les lignes supplémentaires"
#: order/serializers.py:101
msgid "Copy extra line items from the original order"
@@ -6168,7 +6168,7 @@ msgstr "Aucune correspondance trouvée pour les numéros de série suivants"
#: order/serializers.py:1678
msgid "The following serial numbers are unavailable"
msgstr "Les numéros de série suivants ne sont pas disponibles"
msgstr "Les numéros de série suivants sont indisponibles"
#: order/serializers.py:1904
msgid "Return order line item"
@@ -7672,7 +7672,7 @@ msgstr ""
#: part/serializers.py:277
msgid "Model ID"
msgstr ""
msgstr "Identifiant du Modèle"
#: part/serializers.py:324
msgid "Number of parts using this template"
@@ -8917,11 +8917,11 @@ msgstr ""
#: plugin/base/ui/serializers.py:30
msgid "Plugin Name"
msgstr ""
msgstr "Nom de l'extension"
#: plugin/base/ui/serializers.py:34
msgid "Feature Type"
msgstr ""
msgstr "Type de fonctionnalité"
#: plugin/base/ui/serializers.py:39
msgid "Feature Label"
@@ -8929,27 +8929,27 @@ msgstr ""
#: plugin/base/ui/serializers.py:44
msgid "Feature Title"
msgstr ""
msgstr "Titre de la fonctionnalité"
#: plugin/base/ui/serializers.py:49
msgid "Feature Description"
msgstr ""
msgstr "Description de la fonctionnalité"
#: plugin/base/ui/serializers.py:54
msgid "Feature Icon"
msgstr ""
msgstr "Icône de fonctionnalité"
#: plugin/base/ui/serializers.py:58
msgid "Feature Options"
msgstr ""
msgstr "Options de fonctionnalité"
#: plugin/base/ui/serializers.py:61
msgid "Feature Context"
msgstr ""
msgstr "Contexte de la fonctionnalité"
#: plugin/base/ui/serializers.py:64
msgid "Feature Source (javascript)"
msgstr ""
msgstr "Source de la fonctionnalité (javascript)"
#: plugin/builtin/barcodes/inventree_barcode.py:27
msgid "InvenTree Barcodes"
@@ -9331,7 +9331,7 @@ msgstr ""
#: plugin/samples/integration/user_interface_sample.py:33
msgid "Enable Purchase Order Panels"
msgstr ""
msgstr "Activer les panneaux de commande"
#: plugin/samples/integration/user_interface_sample.py:34
msgid "Enable custom panels for Purchase Order views"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Hebrew\n"
"Language: he_IL\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "למשתמש אין הרשאה לצפות במוזל הזה"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Hindi\n"
"Language: hi_IN\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Hungarian\n"
"Language: hu_HU\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API funkciót nem találom"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Nincs jogosultságod az adatok megtekintéséhez"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Indonesian\n"
"Language: id_ID\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API endpoint tidak ditemukan"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Pengguna tidak memiliki izin untuk melihat model ini"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Endpoint API non trovato"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "L'utente non ha i permessi per vedere questo modello"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Japanese\n"
"Language: ja_JP\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "APIエンドポイントが見つかりません"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "ユーザーにこのモデルを表示する権限がありません"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Korean\n"
"Language: ko_KR\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Lithuanian\n"
"Language: lt_LT\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Latvian\n"
"Language: lv_LV\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API galapunkts nav atrasts"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Lietotājam nav atļaujas, lai apskatītu šo modeli"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API eindpunt niet gevonden"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr "Ongeldige items lijst verstrekt"
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr "Ongeldige filters opgegeven"
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr "Geen items gevonden om te verwijderen"
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Gebruiker heeft geen rechten om dit model te bekijken"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Norwegian\n"
"Language: no_NO\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API-endepunkt ikke funnet"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Brukeren har ikke rettigheter til å se denne modellen"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Polish\n"
"Language: pl_PL\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Nie znaleziono punktu końcowego API"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Użytkownik nie ma uprawnień do przeglądania tego modelu"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Portuguese\n"
"Language: pt_PT\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API endpoint não encontrado"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Usuário não tem permissão para ver este modelo"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API endpoint não encontrado"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
msgstr "Lista de itens inválida fornecida"
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
msgstr "Filtros inválidos fornecidos"
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
msgstr "Nenhum item encontrado para excluir"
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -150,7 +150,7 @@ msgstr ""
#: InvenTree/helpers.py:509
msgid "Cannot serialize more than 1000 items at once"
msgstr ""
msgstr "Não é possível serializar mais de 1000 itens de uma vez"
#: InvenTree/helpers.py:515
msgid "Empty serial number string"
@@ -164,7 +164,7 @@ msgstr ""
#: InvenTree/helpers.py:644 InvenTree/helpers.py:663
#, python-brace-format
msgid "Invalid group: {group}"
msgstr ""
msgstr "Grupo invalido:{group}"
#: InvenTree/helpers.py:607
#, python-brace-format
@@ -373,7 +373,7 @@ msgstr "Chinês (tradicional)"
#: InvenTree/magic_login.py:28
msgid "Log in to the app"
msgstr ""
msgstr "Entrar no aplicativo"
#: InvenTree/magic_login.py:38 InvenTree/serializers.py:416
#: company/models.py:133 company/templates/company/company_base.html:138
@@ -522,7 +522,7 @@ msgstr ""
#: InvenTree/models.py:1112
msgid "Task Failure"
msgstr ""
msgstr "Falha na Tarefa"
#: InvenTree/models.py:1114
#, python-brace-format
@@ -643,7 +643,7 @@ msgstr ""
#: InvenTree/serializers.py:632 common/files.py:63
msgid "Unsupported file format"
msgstr ""
msgstr "Formato de arquivo não suportado"
#: InvenTree/serializers.py:638
msgid "File is too large"
@@ -689,7 +689,7 @@ msgstr ""
#: InvenTree/serializers.py:917
msgid "Failed to download image from remote URL"
msgstr ""
msgstr "Falha ao baixar a imagem da URL remota"
#: InvenTree/templatetags/inventree_extras.py:184
msgid "Unknown database"
@@ -6136,7 +6136,7 @@ msgstr ""
#: order/serializers.py:1260
msgid "Allocated Items"
msgstr ""
msgstr "Itens Alocados"
#: order/serializers.py:1374
msgid "No shipment details provided"
@@ -6168,7 +6168,7 @@ msgstr ""
#: order/serializers.py:1678
msgid "The following serial numbers are unavailable"
msgstr ""
msgstr "Os seguintes números de série não estão disponíveis"
#: order/serializers.py:1904
msgid "Return order line item"
@@ -7346,7 +7346,7 @@ msgstr ""
#: part/models.py:3626
msgid "Test templates can only be created for testable parts"
msgstr ""
msgstr "Modelos de teste só podem ser criados para partes testáveis"
#: part/models.py:3637
msgid "Test template with the same key already exists for part"
@@ -7668,11 +7668,11 @@ msgstr ""
#: part/serializers.py:268
msgid "Speculative Quantity"
msgstr ""
msgstr "Quantidade Especulativa"
#: part/serializers.py:277
msgid "Model ID"
msgstr ""
msgstr "ID do modelo"
#: part/serializers.py:324
msgid "Number of parts using this template"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Romanian\n"
"Language: ro_RO\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Конечная точка API не обнаружена"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "У пользователя недостаточно прав для просмотра этой модели!"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Slovak\n"
"Language: sk_SK\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr ""
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Slovenian\n"
"Language: sl_SI\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API vmesnik ni najden"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Uporabnik nima dovoljenja pogleda tega modela"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Serbian (Latin)\n"
"Language: sr_CS\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API krajnja tačka nije pronađena"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Korisnik nema dozvolu za pregled ovog modela"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-07 00:24\n"
"Last-Translator: \n"
"Language-Team: Swedish\n"
"Language: sv_SE\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API-slutpunkt hittades inte"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Användaren har inte behörighet att se denna modell"
@@ -373,7 +373,7 @@ msgstr "Kinesiska (Traditionell)"
#: InvenTree/magic_login.py:28
msgid "Log in to the app"
msgstr ""
msgstr "Logga in på appen"
#: InvenTree/magic_login.py:38 InvenTree/serializers.py:416
#: company/models.py:133 company/templates/company/company_base.html:138
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Thai\n"
"Language: th_TH\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "ไม่พบ API endpoint"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr ""
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Turkish\n"
"Language: tr_TR\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API uç noktası bulunamadı"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Kullanıcının bu modeli görüntüleme izni yok"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-07 23:53\n"
"Last-Translator: \n"
"Language-Team: Ukrainian\n"
"Language: uk_UA\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "Кінцева точка API не знайдена"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "У користувача немає дозволу на перегляд цієї моделі"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-05 22:31\n"
"Last-Translator: \n"
"Language-Team: Vietnamese\n"
"Language: vi_VN\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "API endpoint không tồn tại"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr ""
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr ""
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr ""
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "Người dùng không được phân quyền xem mẫu này"
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 10:27\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-07 23:53\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "未找到 API 端点"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr "提供了无效的单位"
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr "提供了无效的过滤器"
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr "未找到要删除的项目"
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "用户没有权限查阅当前模型。"
@@ -905,7 +905,7 @@ msgstr "生产订单"
#: templates/InvenTree/settings/sidebar.html:55
#: templates/js/translated/search.js:186 users/models.py:207
msgid "Build Orders"
msgstr "生產工單"
msgstr "生产订单"
#: build/models.py:136
msgid "Assembly BOM has not been validated"
@@ -4926,7 +4926,7 @@ msgstr "新建采购订单"
#: templates/js/translated/search.js:219 templates/navbar.html:62
#: users/models.py:209
msgid "Sales Orders"
msgstr "銷售訂單"
msgstr "销售订单"
#: company/templates/company/detail.html:105
#: order/templates/order/sales_orders.html:20
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: inventree\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-04 03:11+0000\n"
"PO-Revision-Date: 2024-11-04 03:14\n"
"POT-Creation-Date: 2024-11-05 22:28+0000\n"
"PO-Revision-Date: 2024-11-07 23:53\n"
"Last-Translator: \n"
"Language-Team: Chinese Traditional\n"
"Language: zh_TW\n"
@@ -17,23 +17,23 @@ msgstr ""
"X-Crowdin-File: /src/backend/InvenTree/locale/en/LC_MESSAGES/django.po\n"
"X-Crowdin-File-ID: 250\n"
#: InvenTree/api.py:270
#: InvenTree/api.py:273
msgid "API endpoint not found"
msgstr "未找到 API 端點"
#: InvenTree/api.py:387
#: InvenTree/api.py:390
msgid "Invalid items list provided"
msgstr "提供了無效的單位"
#: InvenTree/api.py:396
#: InvenTree/api.py:399
msgid "Invalid filters provided"
msgstr "提供了無效的過濾器"
#: InvenTree/api.py:401
#: InvenTree/api.py:404
msgid "No items found to delete"
msgstr "未找到要刪除的項目"
#: InvenTree/api.py:515
#: InvenTree/api.py:518
msgid "User does not have permission to view this model"
msgstr "用户沒有權限查閲當前模型。"
+38 -4
View File
@@ -816,6 +816,17 @@ class SalesOrderLineItemFilter(LineItemFilter):
return queryset.exclude(order__status__in=SalesOrderStatusGroups.COMPLETE)
order_outstanding = rest_filters.BooleanFilter(
label=_('Order Outstanding'), method='filter_order_outstanding'
)
def filter_order_outstanding(self, queryset, name, value):
"""Filter by whether the order is 'outstanding' or not."""
if str2bool(value):
return queryset.filter(order__status__in=SalesOrderStatusGroups.OPEN)
return queryset.exclude(order__status__in=SalesOrderStatusGroups.OPEN)
class SalesOrderLineItemMixin:
"""Mixin class for SalesOrderLineItem endpoints."""
@@ -978,7 +989,7 @@ class SalesOrderAllocationFilter(rest_filters.FilterSet):
"""Metaclass options."""
model = models.SalesOrderAllocation
fields = ['shipment', 'item']
fields = ['shipment', 'line', 'item']
order = rest_filters.ModelChoiceFilter(
queryset=models.SalesOrder.objects.all(),
@@ -1034,6 +1045,16 @@ class SalesOrderAllocationFilter(rest_filters.FilterSet):
line__order__status__in=SalesOrderStatusGroups.OPEN,
)
assigned_to_shipment = rest_filters.BooleanFilter(
label=_('Has Shipment'), method='filter_assigned_to_shipment'
)
def filter_assigned_to_shipment(self, queryset, name, value):
"""Filter by whether or not the allocation has been assigned to a shipment."""
if str2bool(value):
return queryset.exclude(shipment=None)
return queryset.filter(shipment=None)
class SalesOrderAllocationMixin:
"""Mixin class for SalesOrderAllocation endpoints."""
@@ -1049,12 +1070,16 @@ class SalesOrderAllocationMixin:
'item',
'item__sales_order',
'item__part',
'line__part',
'item__location',
'line__order',
'line__part',
'line__order__responsible',
'line__order__project_code',
'line__order__project_code__responsible',
'shipment',
'shipment__order',
)
'shipment__checked_by',
).select_related('line__part__pricing_data', 'item__part__pricing_data')
return queryset
@@ -1065,7 +1090,15 @@ class SalesOrderAllocationList(SalesOrderAllocationMixin, ListAPI):
filterset_class = SalesOrderAllocationFilter
filter_backends = SEARCH_ORDER_FILTER_ALIAS
ordering_fields = ['quantity', 'part', 'serial', 'batch', 'location', 'order']
ordering_fields = [
'quantity',
'part',
'serial',
'batch',
'location',
'order',
'shipment_date',
]
ordering_field_aliases = {
'part': 'item__part__name',
@@ -1073,6 +1106,7 @@ class SalesOrderAllocationList(SalesOrderAllocationMixin, ListAPI):
'batch': 'item__batch',
'location': 'item__location__name',
'order': 'line__order__reference',
'shipment_date': 'shipment__shipment_date',
}
search_fields = {'item__part__name', 'item__serial', 'item__batch'}
@@ -0,0 +1,19 @@
# Generated by Django 4.2.16 on 2024-11-06 04:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('order', '0102_purchaseorder_destination_and_more'),
]
operations = [
migrations.AlterField(
model_name='salesorderallocation',
name='shipment',
field=models.ForeignKey(blank=True, help_text='Sales order shipment reference', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.salesordershipment', verbose_name='Shipment'),
),
]
+30 -1
View File
@@ -1105,6 +1105,11 @@ class SalesOrder(TotalPriceMixin, Order):
_('Order cannot be completed as there are incomplete shipments')
)
if self.pending_allocation_count > 0:
raise ValidationError(
_('Order cannot be completed as there are incomplete allocations')
)
if not allow_incomplete_lines and self.pending_line_count > 0:
raise ValidationError(
_('Order cannot be completed as there are incomplete line items')
@@ -1297,6 +1302,23 @@ class SalesOrder(TotalPriceMixin, Order):
"""Return a queryset of the pending shipments for this order."""
return self.shipments.filter(shipment_date=None)
def allocations(self):
"""Return a queryset of all allocations for this order."""
return SalesOrderAllocation.objects.filter(line__order=self)
def pending_allocations(self):
"""Return a queryset of any pending allocations for this order.
Allocations are pending if:
a) They are not associated with a SalesOrderShipment
b) The linked SalesOrderShipment has not been shipped
"""
Q1 = Q(shipment=None)
Q2 = Q(shipment__shipment_date=None)
return self.allocations().filter(Q1 | Q2).distinct()
@property
def shipment_count(self):
"""Return the total number of shipments associated with this order."""
@@ -1312,6 +1334,11 @@ class SalesOrder(TotalPriceMixin, Order):
"""Return the number of pending shipments associated with this order."""
return self.pending_shipments().count()
@property
def pending_allocation_count(self):
"""Return the number of pending (non-shipped) allocations."""
return self.pending_allocations().count()
@receiver(post_save, sender=SalesOrder, dispatch_uid='sales_order_post_save')
def after_save_sales_order(sender, instance: SalesOrder, created: bool, **kwargs):
@@ -2030,7 +2057,7 @@ class SalesOrderAllocation(models.Model):
if self.item.serial and self.quantity != 1:
errors['quantity'] = _('Quantity must be 1 for serialized stock item')
if self.line.order != self.shipment.order:
if self.shipment and self.line.order != self.shipment.order:
errors['line'] = _('Sales order does not match shipment')
errors['shipment'] = _('Shipment does not match sales order')
@@ -2047,6 +2074,8 @@ class SalesOrderAllocation(models.Model):
shipment = models.ForeignKey(
SalesOrderShipment,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='allocations',
verbose_name=_('Shipment'),
help_text=_('Sales order shipment reference'),
+48 -19
View File
@@ -990,6 +990,8 @@ class SalesOrderSerializer(
'shipment_date',
'total_price',
'order_currency',
'shipments_count',
'completed_shipments_count',
])
read_only_fields = ['status', 'creation_date', 'shipment_date']
@@ -1035,12 +1037,26 @@ class SalesOrderSerializer(
)
)
# Annotate shipment details
queryset = queryset.annotate(
shipments_count=SubqueryCount('shipments'),
completed_shipments_count=SubqueryCount(
'shipments', filter=Q(shipment_date__isnull=False)
),
)
return queryset
customer_detail = CompanyBriefSerializer(
source='customer', many=False, read_only=True
)
shipments_count = serializers.IntegerField(read_only=True, label=_('Shipments'))
completed_shipments_count = serializers.IntegerField(
read_only=True, label=_('Completed Shipments')
)
class SalesOrderIssueSerializer(OrderAdjustSerializer):
"""Serializer for issuing a SalesOrder."""
@@ -1246,6 +1262,15 @@ class SalesOrderShipmentSerializer(NotesFieldMixin, InvenTreeModelSerializer):
'notes',
]
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
order_detail = kwargs.pop('order_detail', True)
super().__init__(*args, **kwargs)
if not order_detail:
self.fields.pop('order_detail', None)
@staticmethod
def annotate_queryset(queryset):
"""Annotate the queryset with extra information."""
@@ -1276,22 +1301,26 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
fields = [
'pk',
'line',
'customer_detail',
'serial',
'quantity',
'location',
'location_detail',
'item',
'item_detail',
'order',
'order_detail',
'part',
'part_detail',
'quantity',
'shipment',
# Annotated read-only fields
'line',
'part',
'order',
'serial',
'location',
# Extra detail fields
'item_detail',
'part_detail',
'order_detail',
'customer_detail',
'location_detail',
'shipment_detail',
]
read_only_fields = ['line', '']
def __init__(self, *args, **kwargs):
"""Initialization routine for the serializer."""
order_detail = kwargs.pop('order_detail', False)
@@ -1341,7 +1370,7 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
)
shipment_detail = SalesOrderShipmentSerializer(
source='shipment', many=False, read_only=True
source='shipment', order_detail=False, many=False, read_only=True
)
@@ -1596,8 +1625,8 @@ class SalesOrderSerialAllocationSerializer(serializers.Serializer):
shipment = serializers.PrimaryKeyRelatedField(
queryset=order.models.SalesOrderShipment.objects.all(),
many=False,
allow_null=False,
required=True,
required=False,
allow_null=True,
label=_('Shipment'),
)
@@ -1609,10 +1638,10 @@ class SalesOrderSerialAllocationSerializer(serializers.Serializer):
"""
order = self.context['order']
if shipment.shipment_date is not None:
if shipment and shipment.shipment_date is not None:
raise ValidationError(_('Shipment has already been shipped'))
if shipment.order != order:
if shipment and shipment.order != order:
raise ValidationError(_('Shipment is not associated with this order'))
return shipment
@@ -1720,8 +1749,8 @@ class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
shipment = serializers.PrimaryKeyRelatedField(
queryset=order.models.SalesOrderShipment.objects.all(),
many=False,
allow_null=False,
required=True,
required=False,
allow_null=True,
label=_('Shipment'),
)
@@ -1756,7 +1785,7 @@ class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
data = self.validated_data
items = data['items']
shipment = data['shipment']
shipment = data.get('shipment')
with transaction.atomic():
for entry in items:
-1
View File
@@ -1877,7 +1877,6 @@ class SalesOrderAllocateTest(OrderTest):
response = self.post(self.url, {}, expected_code=400)
self.assertIn('This field is required', str(response.data['items']))
self.assertIn('This field is required', str(response.data['shipment']))
# Test with a single line items
line = self.order.lines.first()
+5
View File
@@ -46,6 +46,11 @@ class OrderTest(TestCase):
self.assertEqual(
order.get_absolute_url(), f'/order/purchase-order/{pk}/'
)
else:
self.assertEqual(
order.get_absolute_url(),
f'/platform/purchasing/purchase-order/{pk}',
)
self.assertEqual(order.reference, f'PO-{pk:04d}')
@@ -130,6 +130,10 @@ class CategoryTest(TestCase):
"""Test that the PartCategory URL works."""
if settings.ENABLE_CLASSIC_FRONTEND:
self.assertEqual(self.capacitors.get_absolute_url(), '/part/category/3/')
else:
self.assertEqual(
self.capacitors.get_absolute_url(), '/platform/part/category/3'
)
def test_part_count(self):
"""Test that the Category part count works."""
+2
View File
@@ -246,6 +246,8 @@ class PartTest(TestCase):
self.assertEqual(self.r1.name, 'R_2K2_0805')
if settings.ENABLE_CLASSIC_FRONTEND:
self.assertEqual(self.r1.get_absolute_url(), '/part/3/')
else:
self.assertEqual(self.r1.get_absolute_url(), '/platform/part/3')
def test_category(self):
"""Test PartCategory path."""
@@ -289,6 +289,10 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase):
self.assertEqual(
response.data['stocklocation']['web_url'], '/stock/location/5/'
)
else:
self.assertEqual(
response.data['stocklocation']['web_url'], '/platform/stock/location/5'
)
self.assertEqual(response.data['plugin'], 'InvenTreeBarcode')
# Scan a Part object
@@ -332,6 +336,10 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase):
self.assertEqual(
response.data['stocklocation']['web_url'], '/stock/location/5/'
)
else:
self.assertEqual(
response.data['stocklocation']['web_url'], '/platform/stock/location/5'
)
self.assertEqual(response.data['plugin'], 'InvenTreeBarcode')
# Scan a Part object
+77 -124
View File
@@ -50,7 +50,7 @@ from InvenTree.mixins import (
RetrieveAPI,
RetrieveUpdateDestroyAPI,
)
from order.models import PurchaseOrder, ReturnOrder, SalesOrder, SalesOrderAllocation
from order.models import PurchaseOrder, ReturnOrder, SalesOrder
from order.serializers import (
PurchaseOrderSerializer,
ReturnOrderSerializer,
@@ -101,55 +101,6 @@ class GenerateSerialNumber(GenericAPIView):
return Response(data, status=status.HTTP_201_CREATED)
class StockDetail(RetrieveUpdateDestroyAPI):
"""API detail endpoint for Stock object.
get:
Return a single StockItem object
post:
Update a StockItem
delete:
Remove a StockItem
"""
queryset = StockItem.objects.all()
serializer_class = StockSerializers.StockItemSerializer
def get_queryset(self, *args, **kwargs):
"""Annotate queryset."""
queryset = super().get_queryset(*args, **kwargs)
queryset = StockSerializers.StockItemSerializer.annotate_queryset(queryset)
return queryset
def get_serializer_context(self):
"""Extend serializer context."""
ctx = super().get_serializer_context()
ctx['user'] = getattr(self.request, 'user', None)
return ctx
def get_serializer(self, *args, **kwargs):
"""Set context before returning serializer."""
kwargs['context'] = self.get_serializer_context()
try:
params = self.request.query_params
kwargs['part_detail'] = str2bool(params.get('part_detail', True))
kwargs['location_detail'] = str2bool(params.get('location_detail', True))
kwargs['supplier_part_detail'] = str2bool(
params.get('supplier_part_detail', True)
)
kwargs['path_detail'] = str2bool(params.get('path_detail', False))
except AttributeError: # pragma: no cover
pass
return self.serializer_class(*args, **kwargs)
class StockItemContextMixin:
"""Mixin class for adding StockItem object to serializer context."""
@@ -531,54 +482,88 @@ class StockFilter(rest_filters.FilterSet):
)
supplier = rest_filters.ModelChoiceFilter(
label='Supplier',
label=_('Supplier'),
queryset=Company.objects.filter(is_supplier=True),
field_name='supplier_part__supplier',
)
include_variants = rest_filters.BooleanFilter(
label=_('Include Variants'), method='filter_include_variants'
)
def filter_include_variants(self, queryset, name, value):
"""Filter by whether or not to include variants of the selected part.
Note:
- This filter does nothing by itself, and requires the 'part' filter to be set.
- Refer to the 'filter_part' method for more information.
"""
return queryset
part = rest_filters.ModelChoiceFilter(
label=_('Part'), queryset=Part.objects.all(), method='filter_part'
)
def filter_part(self, queryset, name, part):
"""Filter StockItem list by provided Part instance.
Note:
- If "part" is a variant, include all variants of the selected part
- Otherwise, filter by the selected part
"""
include_variants = str2bool(self.data.get('include_variants', True))
if include_variants:
return queryset.filter(part__in=part.get_descendants(include_self=True))
else:
return queryset.filter(part=part)
# Part name filters
name = rest_filters.CharFilter(
label='Part name (case insensitive)',
label=_('Part name (case insensitive)'),
field_name='part__name',
lookup_expr='iexact',
)
name_contains = rest_filters.CharFilter(
label='Part name contains (case insensitive)',
label=_('Part name contains (case insensitive)'),
field_name='part__name',
lookup_expr='icontains',
)
name_regex = rest_filters.CharFilter(
label='Part name (regex)', field_name='part__name', lookup_expr='iregex'
label=_('Part name (regex)'), field_name='part__name', lookup_expr='iregex'
)
# Part IPN filters
IPN = rest_filters.CharFilter(
label='Part IPN (case insensitive)',
label=_('Part IPN (case insensitive)'),
field_name='part__IPN',
lookup_expr='iexact',
)
IPN_contains = rest_filters.CharFilter(
label='Part IPN contains (case insensitive)',
label=_('Part IPN contains (case insensitive)'),
field_name='part__IPN',
lookup_expr='icontains',
)
IPN_regex = rest_filters.CharFilter(
label='Part IPN (regex)', field_name='part__IPN', lookup_expr='iregex'
label=_('Part IPN (regex)'), field_name='part__IPN', lookup_expr='iregex'
)
# Part attribute filters
assembly = rest_filters.BooleanFilter(label='Assembly', field_name='part__assembly')
active = rest_filters.BooleanFilter(label='Active', field_name='part__active')
salable = rest_filters.BooleanFilter(label='Salable', field_name='part__salable')
assembly = rest_filters.BooleanFilter(
label=_('Assembly'), field_name='part__assembly'
)
active = rest_filters.BooleanFilter(label=_('Active'), field_name='part__active')
salable = rest_filters.BooleanFilter(label=_('Salable'), field_name='part__salable')
min_stock = rest_filters.NumberFilter(
label='Minimum stock', field_name='quantity', lookup_expr='gte'
label=_('Minimum stock'), field_name='quantity', lookup_expr='gte'
)
max_stock = rest_filters.NumberFilter(
label='Maximum stock', field_name='quantity', lookup_expr='lte'
label=_('Maximum stock'), field_name='quantity', lookup_expr='lte'
)
status = rest_filters.NumberFilter(label='Status Code', method='filter_status')
status = rest_filters.NumberFilter(label=_('Status Code'), method='filter_status')
def filter_status(self, queryset, name, value):
"""Filter by integer status code."""
@@ -860,17 +845,25 @@ class StockFilter(rest_filters.FilterSet):
return queryset.exclude(stale_filter)
class StockList(DataExportViewMixin, ListCreateDestroyAPIView):
"""API endpoint for list view of Stock objects.
- GET: Return a list of all StockItem objects (with optional query filters)
- POST: Create a new StockItem
- DELETE: Delete multiple StockItem objects
"""
class StockApiMixin:
"""Mixin class for StockItem API endpoints."""
serializer_class = StockSerializers.StockItemSerializer
queryset = StockItem.objects.all()
filterset_class = StockFilter
def get_queryset(self, *args, **kwargs):
"""Annotate queryset."""
queryset = super().get_queryset(*args, **kwargs)
queryset = StockSerializers.StockItemSerializer.annotate_queryset(queryset)
return queryset
def get_serializer_context(self):
"""Extend serializer context."""
ctx = super().get_serializer_context()
ctx['user'] = getattr(self.request, 'user', None)
return ctx
def get_serializer(self, *args, **kwargs):
"""Set context before returning serializer.
@@ -899,12 +892,16 @@ class StockList(DataExportViewMixin, ListCreateDestroyAPIView):
return self.serializer_class(*args, **kwargs)
def get_serializer_context(self):
"""Extend serializer context."""
ctx = super().get_serializer_context()
ctx['user'] = getattr(self.request, 'user', None)
return ctx
class StockList(DataExportViewMixin, StockApiMixin, ListCreateDestroyAPIView):
"""API endpoint for list view of Stock objects.
- GET: Return a list of all StockItem objects (with optional query filters)
- POST: Create a new StockItem
- DELETE: Delete multiple StockItem objects
"""
filterset_class = StockFilter
def create(self, request, *args, **kwargs):
"""Create a new StockItem object via the API.
@@ -1079,14 +1076,6 @@ class StockList(DataExportViewMixin, ListCreateDestroyAPIView):
headers=self.get_success_headers(serializer.data),
)
def get_queryset(self, *args, **kwargs):
"""Annotate queryset before returning."""
queryset = super().get_queryset(*args, **kwargs)
queryset = StockSerializers.StockItemSerializer.annotate_queryset(queryset)
return queryset
def filter_queryset(self, queryset):
"""Custom filtering for the StockItem queryset."""
params = self.request.query_params
@@ -1107,46 +1096,6 @@ class StockList(DataExportViewMixin, ListCreateDestroyAPIView):
except (ValueError, StockItem.DoesNotExist): # pragma: no cover
pass
# Exclude StockItems which are already allocated to a particular SalesOrder
exclude_so_allocation = params.get('exclude_so_allocation', None)
if exclude_so_allocation is not None:
try:
order = SalesOrder.objects.get(pk=exclude_so_allocation)
# Grab all the active SalesOrderAllocations for this order
allocations = SalesOrderAllocation.objects.filter(
line__pk__in=[line.pk for line in order.lines.all()]
)
# Exclude any stock item which is already allocated to the sales order
queryset = queryset.exclude(pk__in=[a.item.pk for a in allocations])
except (ValueError, SalesOrder.DoesNotExist): # pragma: no cover
pass
# Does the client wish to filter by the Part ID?
part_id = params.get('part', None)
if part_id:
try:
part = Part.objects.get(pk=part_id)
# Do we wish to filter *just* for this part, or also for parts *under* this one?
include_variants = str2bool(params.get('include_variants', True))
if include_variants:
# Filter by any parts "under" the given part
parts = part.get_descendants(include_self=True)
queryset = queryset.filter(part__in=parts)
else:
queryset = queryset.filter(part=part)
except (ValueError, Part.DoesNotExist):
raise ValidationError({'part': 'Invalid Part ID specified'})
# Does the client wish to filter by stock location?
loc_id = params.get('location', None)
@@ -1212,6 +1161,10 @@ class StockList(DataExportViewMixin, ListCreateDestroyAPIView):
]
class StockDetail(StockApiMixin, RetrieveUpdateDestroyAPI):
"""API detail endpoint for a single StockItem instance."""
class StockItemTestResultMixin:
"""Mixin class for the StockItemTestResult API endpoints."""
+3
View File
@@ -266,6 +266,9 @@ class StockTest(StockTestBase):
if settings.ENABLE_CLASSIC_FRONTEND:
self.assertEqual(it.get_absolute_url(), '/stock/item/2/')
self.assertEqual(self.home.get_absolute_url(), '/stock/location/1/')
else:
self.assertEqual(it.get_absolute_url(), '/platform/stock/item/2')
self.assertEqual(self.home.get_absolute_url(), '/platform/stock/location/1')
def test_strings(self):
"""Test str function."""
@@ -11,6 +11,8 @@
constructField,
constructForm,
constructOrderTableButtons,
disableFormInput,
enableFormInput,
endDate,
formatCurrency,
FullCalendar,
@@ -1559,17 +1561,35 @@ function showAllocationSubTable(index, row, element, options) {
// Add callbacks for 'edit' buttons
table.find('.button-allocation-edit').click(function() {
var pk = $(this).attr('pk');
let pk = $(this).attr('pk');
let allocation = table.bootstrapTable('getRowByUniqueId', pk);
let disableShipment = allocation && allocation.shipment_detail?.shipment_date;
// Edit the sales order allocation
constructForm(
`/api/order/so-allocation/${pk}/`,
{
fields: {
item: {},
quantity: {},
shipment: {
filters: {
order: allocation.order,
shipped: false,
}
}
},
title: '{% trans "Edit Stock Allocation" %}',
refreshTable: options.table,
afterRender: function(fields, opts) {
disableFormInput('item', opts);
if (disableShipment) {
disableFormInput('shipment', opts);
} else {
enableFormInput('shipment', opts);
}
}
},
);
});
@@ -1593,6 +1613,8 @@ function showAllocationSubTable(index, row, element, options) {
table.bootstrapTable({
url: '{% url "api-so-allocation-list" %}',
onPostBody: setupCallbacks,
uniqueId: 'pk',
idField: 'pk',
queryParams: {
...options.queryParams,
part_detail: true,
@@ -1614,7 +1636,11 @@ function showAllocationSubTable(index, row, element, options) {
field: 'shipment',
title: '{% trans "Shipment" %}',
formatter: function(value, row) {
return row.shipment_detail.reference;
if (row.shipment_detail) {
return row.shipment_detail.reference;
} else {
return '{% trans "No shipment" %}';
}
}
},
{
+1 -3
View File
@@ -2,7 +2,7 @@
from django.apps import apps
from django.contrib.auth.models import Group
from django.test import TestCase, tag
from django.test import TestCase
from django.urls import reverse
from common.settings import set_global_setting
@@ -166,8 +166,6 @@ class OwnerModelTest(InvenTreeTestCase):
self.assertEqual(response.status_code, status_code)
return response.data
# TODO: Find out why this depends on CUI
@tag('cui')
def test_owner(self):
"""Tests for the 'owner' model."""
# Check that owner was created for user
+5 -5
View File
@@ -497,9 +497,9 @@ export function useAllocateStockToBuildForm({
lineItems,
onFormSuccess
}: {
buildId: number;
buildId?: number;
outputId?: number | null;
build: any;
build?: any;
lineItems: any[];
onFormSuccess: (response: any) => void;
}) {
@@ -533,8 +533,8 @@ export function useAllocateStockToBuildForm({
}, [lineItems, sourceLocation]);
useEffect(() => {
setSourceLocation(build.take_from);
}, [build.take_from]);
setSourceLocation(build?.take_from);
}, [build?.take_from]);
const sourceLocationField: ApiFormFieldType = useMemo(() => {
return {
@@ -545,7 +545,7 @@ export function useAllocateStockToBuildForm({
label: t`Source Location`,
description: t`Select the source location for the stock allocation`,
name: 'source_location',
value: build.take_from,
value: build?.take_from,
onValueChange: (value: any) => {
setSourceLocation(value);
}
+20 -4
View File
@@ -356,13 +356,29 @@ export function useSalesOrderShipmentCompleteFields({
}
export function useSalesOrderAllocationFields({
shipmentId
orderId,
shipment
}: {
shipmentId?: number;
orderId?: number;
shipment: any | null;
}): ApiFormFieldSet {
return useMemo(() => {
return {
quantity: {}
item: {
// Cannot change item, but display for reference
disabled: true
},
quantity: {},
shipment: {
// Cannot change shipment once it has been shipped
disabled: !!shipment?.shipment_date,
// Order ID is required for this field to be accessed
hidden: !orderId,
filters: {
order: orderId,
shipped: false
}
}
};
}, [shipmentId]);
}, [orderId, shipment]);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More