mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
* Add pre-commit to the stack * exclude static * Add locales to excludes * fix style errors * rename pipeline steps * also wait on precommit * make template matching simpler * Use the same code for python setup everywhere * use step and cache for python setup * move regular settings up into general envs * just use full update * Use invoke instead of static references * make setup actions more similar * use python3 * refactor names to be similar * fix runner version * fix references * remove incidential change * use matrix for os * Github can't do this right now * ignore docstyle errors * Add seperate docstring test * update flake call * do not fail on docstring * refactor setup into workflow * update reference * switch to action * resturcture * add bash statements * remove os from cache * update input checks * make code cleaner * fix boolean * no relative paths * install wheel by python * switch to install * revert back to simple wheel * refactor import export tests * move setup keys back to not disturbe tests * remove docstyle till that is fixed * update references * continue on error * add docstring test * use relativ action references * Change step / job docstrings * update to merge * reformat comments 1 * fix docstrings 2 * fix docstrings 3 * fix docstrings 4 * fix docstrings 5 * fix docstrings 6 * fix docstrings 7 * fix docstrings 8 * fix docstirns 9 * fix docstrings 10 * docstring adjustments * update the remaining docstrings * small docstring changes * fix function name * update support files for docstrings * Add missing args to docstrings * Remove outdated function * Add docstrings for the 'build' app * Make API code cleaner * add more docstrings for plugin app * Remove dead code for plugin settings No idea what that was even intended for * ignore __init__ files for docstrings * More docstrings * Update docstrings for the 'part' directory * Fixes for related_part functionality * Fix removed stuff from merge 99676ee * make more consistent * Show statistics for docstrings * add more docstrings * move specific register statements to make them clearer to understant * More docstrings for common * and more docstrings * and more * simpler call * docstrings for notifications * docstrings for common/tests * Add docs for common/models * Revert "move specific register statements to make them clearer to understant" This reverts commit ca9665462202c2d63f34b4fd920013b1457bbb6d. * use typing here * Revert "Make API code cleaner" This reverts commit 24fb68bd3e1ccfea2ee398c9e18afb01eb340fee. * docstring updates for the 'users' app * Add generic Meta info to simple Meta classes * remove unneeded unique_together statements * More simple metas * Remove unnecessary format specifier * Remove extra json format specifiers * Add docstrings for the 'plugin' app * Docstrings for the 'label' app * Add missing docstrings for the 'report' app * Fix build test regression * Fix top-level files * docstrings for InvenTree/InvenTree * reduce unneeded code * add docstrings * and more docstrings * more docstrings * more docstrings for stock * more docstrings * docstrings for order/views * Docstrings for various files in the 'order' app * Docstrings for order/test_api.py * Docstrings for order/serializers.py * Docstrings for order/admin.py * More docstrings for the order app * Add docstrings for the 'company' app * Add unit tests for rebuilding the reference fields * Prune out some more dead code * remove more dead code Co-authored-by: Oliver Walters <oliver.henry.walters@gmail.com>
438 lines
12 KiB
Python
438 lines
12 KiB
Python
"""Provides a JSON API for the Company app."""
|
|
|
|
from django.db.models import Q
|
|
from django.urls import include, re_path
|
|
|
|
from django_filters import rest_framework as rest_filters
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
from rest_framework import filters, generics
|
|
|
|
from InvenTree.api import AttachmentMixin
|
|
from InvenTree.helpers import str2bool
|
|
|
|
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
|
ManufacturerPartParameter, SupplierPart,
|
|
SupplierPriceBreak)
|
|
from .serializers import (CompanySerializer,
|
|
ManufacturerPartAttachmentSerializer,
|
|
ManufacturerPartParameterSerializer,
|
|
ManufacturerPartSerializer, SupplierPartSerializer,
|
|
SupplierPriceBreakSerializer)
|
|
|
|
|
|
class CompanyList(generics.ListCreateAPIView):
|
|
"""API endpoint for accessing a list of Company objects.
|
|
|
|
Provides two methods:
|
|
|
|
- GET: Return list of objects
|
|
- POST: Create a new Company object
|
|
"""
|
|
|
|
serializer_class = CompanySerializer
|
|
queryset = Company.objects.all()
|
|
|
|
def get_queryset(self):
|
|
"""Return annotated queryset for the company list endpoint"""
|
|
queryset = super().get_queryset()
|
|
queryset = CompanySerializer.annotate_queryset(queryset)
|
|
|
|
return queryset
|
|
|
|
filter_backends = [
|
|
DjangoFilterBackend,
|
|
filters.SearchFilter,
|
|
filters.OrderingFilter,
|
|
]
|
|
|
|
filter_fields = [
|
|
'is_customer',
|
|
'is_manufacturer',
|
|
'is_supplier',
|
|
'name',
|
|
]
|
|
|
|
search_fields = [
|
|
'name',
|
|
'description',
|
|
'website',
|
|
]
|
|
|
|
ordering_fields = [
|
|
'name',
|
|
'parts_supplied',
|
|
'parts_manufactured',
|
|
]
|
|
|
|
ordering = 'name'
|
|
|
|
|
|
class CompanyDetail(generics.RetrieveUpdateDestroyAPIView):
|
|
"""API endpoint for detail of a single Company object."""
|
|
|
|
queryset = Company.objects.all()
|
|
serializer_class = CompanySerializer
|
|
|
|
def get_queryset(self):
|
|
"""Return annotated queryset for the company detail endpoint"""
|
|
queryset = super().get_queryset()
|
|
queryset = CompanySerializer.annotate_queryset(queryset)
|
|
|
|
return queryset
|
|
|
|
|
|
class ManufacturerPartFilter(rest_filters.FilterSet):
|
|
"""Custom API filters for the ManufacturerPart list endpoint."""
|
|
|
|
class Meta:
|
|
"""Metaclass options."""
|
|
|
|
model = ManufacturerPart
|
|
fields = [
|
|
'manufacturer',
|
|
'MPN',
|
|
'part',
|
|
]
|
|
|
|
# Filter by 'active' status of linked part
|
|
active = rest_filters.BooleanFilter(field_name='part__active')
|
|
|
|
|
|
class ManufacturerPartList(generics.ListCreateAPIView):
|
|
"""API endpoint for list view of ManufacturerPart object.
|
|
|
|
- GET: Return list of ManufacturerPart objects
|
|
- POST: Create a new ManufacturerPart object
|
|
"""
|
|
|
|
queryset = ManufacturerPart.objects.all().prefetch_related(
|
|
'part',
|
|
'manufacturer',
|
|
'supplier_parts',
|
|
)
|
|
|
|
serializer_class = ManufacturerPartSerializer
|
|
filterset_class = ManufacturerPartFilter
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
"""Return serializer instance for this endpoint"""
|
|
# Do we wish to include extra detail?
|
|
try:
|
|
params = self.request.query_params
|
|
|
|
kwargs['part_detail'] = str2bool(params.get('part_detail', None))
|
|
kwargs['manufacturer_detail'] = str2bool(params.get('manufacturer_detail', None))
|
|
kwargs['pretty'] = str2bool(params.get('pretty', None))
|
|
except AttributeError:
|
|
pass
|
|
|
|
kwargs['context'] = self.get_serializer_context()
|
|
|
|
return self.serializer_class(*args, **kwargs)
|
|
|
|
filter_backends = [
|
|
DjangoFilterBackend,
|
|
filters.SearchFilter,
|
|
filters.OrderingFilter,
|
|
]
|
|
|
|
search_fields = [
|
|
'manufacturer__name',
|
|
'description',
|
|
'MPN',
|
|
'part__IPN',
|
|
'part__name',
|
|
'part__description',
|
|
]
|
|
|
|
|
|
class ManufacturerPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|
"""API endpoint for detail view of ManufacturerPart object.
|
|
|
|
- GET: Retrieve detail view
|
|
- PATCH: Update object
|
|
- DELETE: Delete object
|
|
"""
|
|
|
|
queryset = ManufacturerPart.objects.all()
|
|
serializer_class = ManufacturerPartSerializer
|
|
|
|
|
|
class ManufacturerPartAttachmentList(AttachmentMixin, generics.ListCreateAPIView):
|
|
"""API endpoint for listing (and creating) a ManufacturerPartAttachment (file upload)."""
|
|
|
|
queryset = ManufacturerPartAttachment.objects.all()
|
|
serializer_class = ManufacturerPartAttachmentSerializer
|
|
|
|
filter_backends = [
|
|
DjangoFilterBackend,
|
|
]
|
|
|
|
filter_fields = [
|
|
'manufacturer_part',
|
|
]
|
|
|
|
|
|
class ManufacturerPartAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
|
"""Detail endpooint for ManufacturerPartAttachment model."""
|
|
|
|
queryset = ManufacturerPartAttachment.objects.all()
|
|
serializer_class = ManufacturerPartAttachmentSerializer
|
|
|
|
|
|
class ManufacturerPartParameterList(generics.ListCreateAPIView):
|
|
"""API endpoint for list view of ManufacturerPartParamater model."""
|
|
|
|
queryset = ManufacturerPartParameter.objects.all()
|
|
serializer_class = ManufacturerPartParameterSerializer
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
"""Return serializer instance for this endpoint"""
|
|
# Do we wish to include any extra detail?
|
|
try:
|
|
params = self.request.query_params
|
|
|
|
optional_fields = [
|
|
'manufacturer_part_detail',
|
|
]
|
|
|
|
for key in optional_fields:
|
|
kwargs[key] = str2bool(params.get(key, None))
|
|
|
|
except AttributeError:
|
|
pass
|
|
|
|
kwargs['context'] = self.get_serializer_context()
|
|
|
|
return self.serializer_class(*args, **kwargs)
|
|
|
|
def filter_queryset(self, queryset):
|
|
"""Custom filtering for the queryset."""
|
|
queryset = super().filter_queryset(queryset)
|
|
|
|
params = self.request.query_params
|
|
|
|
# Filter by manufacturer?
|
|
manufacturer = params.get('manufacturer', None)
|
|
|
|
if manufacturer is not None:
|
|
queryset = queryset.filter(manufacturer_part__manufacturer=manufacturer)
|
|
|
|
# Filter by part?
|
|
part = params.get('part', None)
|
|
|
|
if part is not None:
|
|
queryset = queryset.filter(manufacturer_part__part=part)
|
|
|
|
return queryset
|
|
|
|
filter_backends = [
|
|
DjangoFilterBackend,
|
|
filters.SearchFilter,
|
|
filters.OrderingFilter,
|
|
]
|
|
|
|
filter_fields = [
|
|
'name',
|
|
'value',
|
|
'units',
|
|
'manufacturer_part',
|
|
]
|
|
|
|
search_fields = [
|
|
'name',
|
|
'value',
|
|
'units',
|
|
]
|
|
|
|
|
|
class ManufacturerPartParameterDetail(generics.RetrieveUpdateDestroyAPIView):
|
|
"""API endpoint for detail view of ManufacturerPartParameter model."""
|
|
|
|
queryset = ManufacturerPartParameter.objects.all()
|
|
serializer_class = ManufacturerPartParameterSerializer
|
|
|
|
|
|
class SupplierPartList(generics.ListCreateAPIView):
|
|
"""API endpoint for list view of SupplierPart object.
|
|
|
|
- GET: Return list of SupplierPart objects
|
|
- POST: Create a new SupplierPart object
|
|
"""
|
|
|
|
queryset = SupplierPart.objects.all()
|
|
|
|
def filter_queryset(self, queryset):
|
|
"""Custom filtering for the queryset."""
|
|
queryset = super().filter_queryset(queryset)
|
|
|
|
params = self.request.query_params
|
|
|
|
# Filter by manufacturer
|
|
manufacturer = params.get('manufacturer', None)
|
|
|
|
if manufacturer is not None:
|
|
queryset = queryset.filter(manufacturer_part__manufacturer=manufacturer)
|
|
|
|
# Filter by supplier
|
|
supplier = params.get('supplier', None)
|
|
|
|
if supplier is not None:
|
|
queryset = queryset.filter(supplier=supplier)
|
|
|
|
# Filter by EITHER manufacturer or supplier
|
|
company = params.get('company', None)
|
|
|
|
if company is not None:
|
|
queryset = queryset.filter(Q(manufacturer_part__manufacturer=company) | Q(supplier=company))
|
|
|
|
# Filter by parent part?
|
|
part = params.get('part', None)
|
|
|
|
if part is not None:
|
|
queryset = queryset.filter(part=part)
|
|
|
|
# Filter by manufacturer part?
|
|
manufacturer_part = params.get('manufacturer_part', None)
|
|
|
|
if manufacturer_part is not None:
|
|
queryset = queryset.filter(manufacturer_part=manufacturer_part)
|
|
|
|
# Filter by 'active' status of the part?
|
|
active = params.get('active', None)
|
|
|
|
if active is not None:
|
|
active = str2bool(active)
|
|
queryset = queryset.filter(part__active=active)
|
|
|
|
return queryset
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
"""Return serializer instance for this endpoint"""
|
|
|
|
# Do we wish to include extra detail?
|
|
try:
|
|
params = self.request.query_params
|
|
kwargs['part_detail'] = str2bool(params.get('part_detail', None))
|
|
kwargs['supplier_detail'] = str2bool(params.get('supplier_detail', True))
|
|
kwargs['manufacturer_detail'] = str2bool(params.get('manufacturer_detail', None))
|
|
kwargs['pretty'] = str2bool(params.get('pretty', None))
|
|
except AttributeError:
|
|
pass
|
|
|
|
kwargs['context'] = self.get_serializer_context()
|
|
|
|
return self.serializer_class(*args, **kwargs)
|
|
|
|
serializer_class = SupplierPartSerializer
|
|
|
|
filter_backends = [
|
|
DjangoFilterBackend,
|
|
filters.SearchFilter,
|
|
filters.OrderingFilter,
|
|
]
|
|
|
|
filter_fields = [
|
|
]
|
|
|
|
search_fields = [
|
|
'SKU',
|
|
'supplier__name',
|
|
'manufacturer_part__manufacturer__name',
|
|
'description',
|
|
'manufacturer_part__MPN',
|
|
'part__IPN',
|
|
'part__name',
|
|
'part__description',
|
|
]
|
|
|
|
|
|
class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|
"""API endpoint for detail view of SupplierPart object.
|
|
|
|
- GET: Retrieve detail view
|
|
- PATCH: Update object
|
|
- DELETE: Delete object
|
|
"""
|
|
|
|
queryset = SupplierPart.objects.all()
|
|
serializer_class = SupplierPartSerializer
|
|
|
|
read_only_fields = [
|
|
]
|
|
|
|
|
|
class SupplierPriceBreakList(generics.ListCreateAPIView):
|
|
"""API endpoint for list view of SupplierPriceBreak object.
|
|
|
|
- GET: Retrieve list of SupplierPriceBreak objects
|
|
- POST: Create a new SupplierPriceBreak object
|
|
"""
|
|
|
|
queryset = SupplierPriceBreak.objects.all()
|
|
serializer_class = SupplierPriceBreakSerializer
|
|
|
|
filter_backends = [
|
|
DjangoFilterBackend,
|
|
]
|
|
|
|
filter_fields = [
|
|
'part',
|
|
]
|
|
|
|
|
|
class SupplierPriceBreakDetail(generics.RetrieveUpdateDestroyAPIView):
|
|
"""Detail endpoint for SupplierPriceBreak object."""
|
|
|
|
queryset = SupplierPriceBreak.objects.all()
|
|
serializer_class = SupplierPriceBreakSerializer
|
|
|
|
|
|
manufacturer_part_api_urls = [
|
|
|
|
# Base URL for ManufacturerPartAttachment API endpoints
|
|
re_path(r'^attachment/', include([
|
|
re_path(r'^(?P<pk>\d+)/', ManufacturerPartAttachmentDetail.as_view(), name='api-manufacturer-part-attachment-detail'),
|
|
re_path(r'^$', ManufacturerPartAttachmentList.as_view(), name='api-manufacturer-part-attachment-list'),
|
|
])),
|
|
|
|
re_path(r'^parameter/', include([
|
|
re_path(r'^(?P<pk>\d+)/', ManufacturerPartParameterDetail.as_view(), name='api-manufacturer-part-parameter-detail'),
|
|
|
|
# Catch anything else
|
|
re_path(r'^.*$', ManufacturerPartParameterList.as_view(), name='api-manufacturer-part-parameter-list'),
|
|
])),
|
|
|
|
re_path(r'^(?P<pk>\d+)/?', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'),
|
|
|
|
# Catch anything else
|
|
re_path(r'^.*$', ManufacturerPartList.as_view(), name='api-manufacturer-part-list'),
|
|
]
|
|
|
|
|
|
supplier_part_api_urls = [
|
|
|
|
re_path(r'^(?P<pk>\d+)/?', SupplierPartDetail.as_view(), name='api-supplier-part-detail'),
|
|
|
|
# Catch anything else
|
|
re_path(r'^.*$', SupplierPartList.as_view(), name='api-supplier-part-list'),
|
|
]
|
|
|
|
|
|
company_api_urls = [
|
|
re_path(r'^part/manufacturer/', include(manufacturer_part_api_urls)),
|
|
|
|
re_path(r'^part/', include(supplier_part_api_urls)),
|
|
|
|
# Supplier price breaks
|
|
re_path(r'^price-break/', include([
|
|
|
|
re_path(r'^(?P<pk>\d+)/?', SupplierPriceBreakDetail.as_view(), name='api-part-supplier-price-detail'),
|
|
re_path(r'^.*$', SupplierPriceBreakList.as_view(), name='api-part-supplier-price-list'),
|
|
])),
|
|
|
|
re_path(r'^(?P<pk>\d+)/?', CompanyDetail.as_view(), name='api-company-detail'),
|
|
|
|
re_path(r'^.*$', CompanyList.as_view(), name='api-company-list'),
|
|
]
|