mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
6a0da08f3f
.github
.pre-commit-config.yamlInvenTree
InvenTree
build
common
company
label
locale
cs/LC_MESSAGES
de/LC_MESSAGES
el/LC_MESSAGES
en/LC_MESSAGES
es/LC_MESSAGES
es_MX/LC_MESSAGES
fa/LC_MESSAGES
fr/LC_MESSAGES
he/LC_MESSAGES
hu/LC_MESSAGES
id/LC_MESSAGES
it/LC_MESSAGES
ja/LC_MESSAGES
ko/LC_MESSAGES
nl/LC_MESSAGES
no/LC_MESSAGES
pl/LC_MESSAGES
pt/LC_MESSAGES
pt_br/LC_MESSAGES
ru/LC_MESSAGES
sv/LC_MESSAGES
th/LC_MESSAGES
tr/LC_MESSAGES
vi/LC_MESSAGES
zh/LC_MESSAGES
order
part
plugin
report
stock
templates
docker
requirements-dev.inrequirements-dev.txtrequirements.inrequirements.txttasks.py
8
.github/actions/setup/action.yaml
vendored
8
.github/actions/setup/action.yaml
vendored
@ -15,6 +15,10 @@ inputs:
|
||||
required: false
|
||||
description: 'Install the InvenTree requirements?'
|
||||
default: 'false'
|
||||
dev-install:
|
||||
required: false
|
||||
description: 'Install the InvenTree development requirements?'
|
||||
default: 'false'
|
||||
update:
|
||||
required: false
|
||||
description: 'Should a full update cycle be run?'
|
||||
@ -72,6 +76,10 @@ runs:
|
||||
sudo apt-get install ${{ inputs.apt-dependency }}
|
||||
|
||||
# Invoke commands
|
||||
- name: Install dev requirements
|
||||
if: ${{ inputs.dev-install == 'true' ||inputs.install == 'true' }}
|
||||
shell: bash
|
||||
run: pip install -r requirements-dev.txt
|
||||
- name: Run invoke install
|
||||
if: ${{ inputs.install == 'true' }}
|
||||
shell: bash
|
||||
|
5
.github/workflows/docker.yaml
vendored
5
.github/workflows/docker.yaml
vendored
@ -39,10 +39,13 @@ jobs:
|
||||
python3 ci/version_check.py
|
||||
echo "git_commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
echo "git_commit_date=$(git show -s --format=%ci)" >> $GITHUB_ENV
|
||||
- name: Run Unit Tests
|
||||
- name: Build Docker Image
|
||||
run: |
|
||||
docker-compose build
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
docker-compose run inventree-dev-server invoke update
|
||||
docker-compose run inventree-dev-server invoke setup-dev
|
||||
docker-compose up -d
|
||||
docker-compose run inventree-dev-server invoke wait
|
||||
docker-compose run inventree-dev-server invoke test
|
||||
|
6
.github/workflows/qc_checks.yaml
vendored
6
.github/workflows/qc_checks.yaml
vendored
@ -34,7 +34,7 @@ jobs:
|
||||
- name: Enviroment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
install: true
|
||||
dev-install: true
|
||||
- name: Run flake8
|
||||
run: flake8 InvenTree --extend-ignore=D
|
||||
|
||||
@ -119,6 +119,7 @@ jobs:
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
apt-dependency: gettext poppler-utils
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Download Python Code For `${{ env.wrapper_name }}`
|
||||
run: git clone --depth 1 https://github.com/inventree/${{ env.wrapper_name }} ./${{ env.wrapper_name }}
|
||||
@ -168,6 +169,7 @@ jobs:
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
apt-dependency: gettext poppler-utils
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Coverage Tests
|
||||
run: invoke coverage
|
||||
@ -218,6 +220,7 @@ jobs:
|
||||
with:
|
||||
apt-dependency: gettext poppler-utils libpq-dev
|
||||
pip-dependency: psycopg2 django-redis>=5.0.0
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
@ -261,6 +264,7 @@ jobs:
|
||||
with:
|
||||
apt-dependency: gettext poppler-utils libmysqlclient-dev
|
||||
pip-dependency: mysqlclient
|
||||
dev-install: true
|
||||
update: true
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
|
@ -28,3 +28,14 @@ repos:
|
||||
rev: '5.10.1'
|
||||
hooks:
|
||||
- id: isort
|
||||
- repo: https://github.com/jazzband/pip-tools
|
||||
rev: 6.6.2
|
||||
hooks:
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements-dev.in
|
||||
args: [--generate-hashes, requirements-dev.in, -o, requirements-dev.txt]
|
||||
files: ^requirements-dev\.(in|txt)$
|
||||
- id: pip-compile
|
||||
name: pip-compile requirements.txt
|
||||
args: [requirements.in, -o, requirements.txt]
|
||||
files: ^requirements\.(in|txt)$
|
||||
|
@ -33,6 +33,7 @@ class InvenTreeMoneySerializer(MoneyField):
|
||||
"""Overrite default values."""
|
||||
kwargs["max_digits"] = kwargs.get("max_digits", 19)
|
||||
kwargs["decimal_places"] = kwargs.get("decimal_places", 4)
|
||||
kwargs["required"] = kwargs.get("required", False)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
@ -253,7 +253,6 @@ INSTALLED_APPS = [
|
||||
'django_cleanup.apps.CleanupConfig', # Automatically delete orphaned MEDIA files
|
||||
'mptt', # Modified Preorder Tree Traversal
|
||||
'markdownify', # Markdown template rendering
|
||||
'django_admin_shell', # Python shell for the admin interface
|
||||
'djmoney', # django-money integration
|
||||
'djmoney.contrib.exchange', # django-money exchange rates
|
||||
'error_report', # Error reporting in the admin interface
|
||||
@ -532,7 +531,7 @@ if "postgres" in db_engine: # pragma: no cover
|
||||
# https://docs.djangoproject.com/en/3.2/ref/databases/#isolation-level
|
||||
if "isolation_level" not in db_options:
|
||||
serializable = _is_true(
|
||||
os.getenv("INVENTREE_DB_ISOLATION_SERIALIZABLE", "true")
|
||||
os.getenv("INVENTREE_DB_ISOLATION_SERIALIZABLE", "false")
|
||||
)
|
||||
db_options["isolation_level"] = (
|
||||
ISOLATION_LEVEL_SERIALIZABLE
|
||||
@ -904,11 +903,13 @@ MARKDOWNIFY_BLEACH = False
|
||||
SENTRY_ENABLED = get_setting('INVENTREE_SENTRY_ENABLED', CONFIG.get('sentry_enabled', False))
|
||||
SENTRY_DSN = get_setting('INVENTREE_SENTRY_DSN', CONFIG.get('sentry_dsn', INVENTREE_DSN))
|
||||
|
||||
SENTRY_SAMPLE_RATE = float(get_setting('INVENTREE_SENTRY_SAMPLE_RATE', CONFIG.get('sentry_sample_rate', 0.1)))
|
||||
|
||||
if SENTRY_ENABLED and SENTRY_DSN: # pragma: no cover
|
||||
sentry_sdk.init(
|
||||
dsn=SENTRY_DSN,
|
||||
integrations=[DjangoIntegration(), ],
|
||||
traces_sample_rate=1.0 if DEBUG else 0.15,
|
||||
traces_sample_rate=1.0 if DEBUG else SENTRY_SAMPLE_RATE,
|
||||
send_default_pii=True
|
||||
)
|
||||
inventree_tags = {
|
||||
|
@ -61,7 +61,7 @@ def raise_warning(msg):
|
||||
warnings.warn(msg)
|
||||
|
||||
|
||||
def offload_task(taskname, *args, force_sync=False, **kwargs):
|
||||
def offload_task(taskname, *args, force_async=False, force_sync=False, **kwargs):
|
||||
"""Create an AsyncTask if workers are running. This is different to a 'scheduled' task, in that it only runs once!
|
||||
|
||||
If workers are not running or force_sync flag
|
||||
@ -79,7 +79,7 @@ def offload_task(taskname, *args, force_sync=False, **kwargs):
|
||||
except (OperationalError, ProgrammingError): # pragma: no cover
|
||||
raise_warning(f"Could not offload task '{taskname}' - database not ready")
|
||||
|
||||
if is_worker_running() and not force_sync: # pragma: no cover
|
||||
if force_async or (is_worker_running() and not force_sync):
|
||||
# Running as asynchronous task
|
||||
try:
|
||||
task = AsyncTask(taskname, *args, **kwargs)
|
||||
|
@ -9,6 +9,7 @@ from unittest import mock
|
||||
import django.core.exceptions as django_exceptions
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
@ -19,9 +20,11 @@ from djmoney.money import Money
|
||||
import InvenTree.tasks
|
||||
from common.models import InvenTreeSetting
|
||||
from common.settings import currency_codes
|
||||
from stock.models import StockLocation
|
||||
from part.models import Part, PartCategory
|
||||
from stock.models import StockItem, StockLocation
|
||||
|
||||
from . import config, helpers, ready, status, version
|
||||
from .tasks import offload_task
|
||||
from .validators import validate_overage, validate_part_name
|
||||
|
||||
|
||||
@ -604,3 +607,61 @@ class TestInstanceName(helpers.InvenTreeTestCase):
|
||||
InvenTreeSetting.set_setting("INVENTREE_INSTANCE", "Testing title", self.user)
|
||||
|
||||
self.assertEqual(version.inventreeInstanceTitle(), 'Testing title')
|
||||
|
||||
# The site should also be changed
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
self.assertEqual(site_obj.name, 'Testing title')
|
||||
|
||||
def test_instance_url(self):
|
||||
"""Test instance url settings."""
|
||||
# Set up required setting
|
||||
InvenTreeSetting.set_setting("INVENTREE_BASE_URL", "http://127.1.2.3", self.user)
|
||||
|
||||
# The site should also be changed
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
self.assertEqual(site_obj.domain, 'http://127.1.2.3')
|
||||
|
||||
|
||||
class TestOffloadTask(helpers.InvenTreeTestCase):
|
||||
"""Tests for offloading tasks to the background worker"""
|
||||
|
||||
fixtures = [
|
||||
'category',
|
||||
'part',
|
||||
'location',
|
||||
'stock',
|
||||
]
|
||||
|
||||
def test_offload_tasks(self):
|
||||
"""Test that we can offload various tasks to the background worker thread.
|
||||
|
||||
This set of tests also ensures that various types of objects
|
||||
can be encoded by the django-q serialization layer!
|
||||
|
||||
Note that as the background worker is not actually running for the tests,
|
||||
the call to 'offload_task' won't really *do* anything!
|
||||
|
||||
However, it serves as a validation that object serialization works!
|
||||
|
||||
Ref: https://github.com/inventree/InvenTree/pull/3273
|
||||
"""
|
||||
|
||||
offload_task(
|
||||
'dummy_tasks.parts',
|
||||
part=Part.objects.get(pk=1),
|
||||
cat=PartCategory.objects.get(pk=1),
|
||||
force_async=True
|
||||
)
|
||||
|
||||
offload_task(
|
||||
'dummy_tasks.stock',
|
||||
item=StockItem.objects.get(pk=1),
|
||||
loc=StockLocation.objects.get(pk=1),
|
||||
force_async=True
|
||||
)
|
||||
|
||||
offload_task(
|
||||
'dummy_task.numbers',
|
||||
1, 2, 3, 4, 5,
|
||||
force_async=True
|
||||
)
|
||||
|
@ -152,7 +152,6 @@ frontendpatterns = [
|
||||
|
||||
# admin sites
|
||||
re_path(f'^{settings.INVENTREE_ADMIN_URL}/error_log/', include('error_report.urls')),
|
||||
re_path(f'^{settings.INVENTREE_ADMIN_URL}/shell/', include('django_admin_shell.urls')),
|
||||
re_path(f'^{settings.INVENTREE_ADMIN_URL}/', admin.site.urls, name='inventree-admin'),
|
||||
|
||||
# DB user sessions
|
||||
|
@ -420,7 +420,7 @@ class BuildItemList(ListCreateAPI):
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'build',
|
||||
'stock_item',
|
||||
'bom_item',
|
||||
@ -438,7 +438,7 @@ class BuildAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'build',
|
||||
]
|
||||
|
||||
|
@ -1018,6 +1018,20 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
||||
"""Returns True if the un-tracked parts are fully allocated for this BuildOrder."""
|
||||
return self.is_fully_allocated(None)
|
||||
|
||||
def has_overallocated_parts(self, output):
|
||||
"""Check if parts have been 'over-allocated' against the specified output.
|
||||
|
||||
Note: If output=None, test un-tracked parts
|
||||
"""
|
||||
|
||||
bom_items = self.tracked_bom_items if output else self.untracked_bom_items
|
||||
|
||||
for bom_item in bom_items:
|
||||
if self.allocated_quantity(bom_item, output) > self.required_quantity(bom_item, output):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def unallocated_bom_items(self, output):
|
||||
"""Return a list of bom items which have *not* been fully allocated against a particular output."""
|
||||
unallocated = []
|
||||
|
@ -466,6 +466,22 @@ class BuildCancelSerializer(serializers.Serializer):
|
||||
class BuildCompleteSerializer(serializers.Serializer):
|
||||
"""DRF serializer for marking a BuildOrder as complete."""
|
||||
|
||||
accept_overallocated = serializers.BooleanField(
|
||||
label=_('Accept Overallocated'),
|
||||
help_text=_('Accept stock items which have been overallocated to this build order'),
|
||||
required=False,
|
||||
default=False,
|
||||
)
|
||||
|
||||
def validate_accept_overallocated(self, value):
|
||||
"""Check if the 'accept_overallocated' field is required"""
|
||||
build = self.context['build']
|
||||
|
||||
if build.has_overallocated_parts(output=None) and not value:
|
||||
raise ValidationError(_('Some stock items have been overallocated'))
|
||||
|
||||
return value
|
||||
|
||||
accept_unallocated = serializers.BooleanField(
|
||||
label=_('Accept Unallocated'),
|
||||
help_text=_('Accept that stock items have not been fully allocated to this build order'),
|
||||
|
@ -289,7 +289,7 @@ class NotificationList(BulkDeleteMixin, ListAPI):
|
||||
'message',
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'category',
|
||||
'read',
|
||||
]
|
||||
|
@ -21,6 +21,7 @@ from django.contrib.auth.models import Group, User
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import AppRegistryNotReady, ValidationError
|
||||
from django.core.validators import MinValueValidator, URLValidator
|
||||
@ -82,6 +83,14 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
super().save()
|
||||
|
||||
# Get after_save action
|
||||
setting = self.get_setting_definition(self.key, *args, **kwargs)
|
||||
after_save = setting.get('after_save', None)
|
||||
|
||||
# Execute if callable
|
||||
if callable(after_save):
|
||||
after_save(self)
|
||||
|
||||
@property
|
||||
def cache_key(self):
|
||||
"""Generate a unique cache key for this settings object"""
|
||||
@ -735,6 +744,20 @@ def settings_group_options():
|
||||
return [('', _('No group')), *[(str(a.id), str(a)) for a in Group.objects.all()]]
|
||||
|
||||
|
||||
def update_instance_url(setting):
|
||||
"""Update the first site objects domain to url."""
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
site_obj.domain = setting.value
|
||||
site_obj.save()
|
||||
|
||||
|
||||
def update_instance_name(setting):
|
||||
"""Update the first site objects name to instance name."""
|
||||
site_obj = Site.objects.all().order_by('id').first()
|
||||
site_obj.name = setting.value
|
||||
site_obj.save()
|
||||
|
||||
|
||||
class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
"""An InvenTreeSetting object is a key:value pair used for storing single values (e.g. one-off settings values).
|
||||
|
||||
@ -782,6 +805,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'name': _('Server Instance Name'),
|
||||
'default': 'InvenTree',
|
||||
'description': _('String descriptor for the server instance'),
|
||||
'after_save': update_instance_name,
|
||||
},
|
||||
|
||||
'INVENTREE_INSTANCE_TITLE': {
|
||||
@ -809,6 +833,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'description': _('Base URL for server instance'),
|
||||
'validator': EmptyURLValidator(),
|
||||
'default': '',
|
||||
'after_save': update_instance_url,
|
||||
},
|
||||
|
||||
'INVENTREE_DEFAULT_CURRENCY': {
|
||||
|
@ -296,6 +296,13 @@ class InvenTreeNotificationBodies:
|
||||
)
|
||||
"""Send when a new order (build, sale or purchase) was created."""
|
||||
|
||||
ItemsReceived = NotificationBody(
|
||||
name=_("Items Received"),
|
||||
slug='purchase_order.items_received',
|
||||
message=_('Items have been received against a purchase order'),
|
||||
template='email/purchase_order_received.html',
|
||||
)
|
||||
|
||||
|
||||
def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
|
||||
"""Send out a notification."""
|
||||
|
@ -133,6 +133,7 @@ class SettingsTest(InvenTreeTestCase):
|
||||
'choices',
|
||||
'units',
|
||||
'requires_restart',
|
||||
'after_save',
|
||||
]
|
||||
|
||||
for k in setting.keys():
|
||||
|
@ -46,7 +46,7 @@ class CompanyList(ListCreateAPI):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'is_customer',
|
||||
'is_manufacturer',
|
||||
'is_supplier',
|
||||
@ -169,7 +169,7 @@ class ManufacturerPartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'manufacturer_part',
|
||||
]
|
||||
|
||||
@ -233,7 +233,7 @@ class ManufacturerPartParameterList(ListCreateDestroyAPIView):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'name',
|
||||
'value',
|
||||
'units',
|
||||
@ -333,7 +333,7 @@ class SupplierPartList(ListCreateDestroyAPIView):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
@ -345,6 +345,7 @@ class SupplierPartList(ListCreateDestroyAPIView):
|
||||
'part__IPN',
|
||||
'part__name',
|
||||
'part__description',
|
||||
'part__keywords',
|
||||
]
|
||||
|
||||
|
||||
@ -377,7 +378,7 @@ class SupplierPriceBreakList(ListCreateAPI):
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'part',
|
||||
]
|
||||
|
||||
|
20
InvenTree/company/migrations/0046_alter_company_image.py
Normal file
20
InvenTree/company/migrations/0046_alter_company_image.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-27 23:08
|
||||
|
||||
import company.models
|
||||
from django.db import migrations
|
||||
import stdimage.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('company', '0045_alter_company_notes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='company',
|
||||
name='image',
|
||||
field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=company.models.rename_company_image, variations={'preview': (256, 256), 'thumbnail': (128, 128)}, verbose_name='Image'),
|
||||
),
|
||||
]
|
@ -14,7 +14,7 @@
|
||||
<h4>{% trans "Supplier Parts" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if roles.purchase_order.add %}
|
||||
{% if roles.purchase_order.add and not part.virtual %}
|
||||
<button class="btn btn-success" id='supplier-part-create' title='{% trans "Create new supplier part" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Supplier Part" %}
|
||||
</button>
|
||||
@ -61,7 +61,7 @@
|
||||
<h4>{% trans "Manufacturer Parts" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if roles.purchase_order.add %}
|
||||
{% if roles.purchase_order.add and not part.virtual %}
|
||||
<button type="button" class="btn btn-success" id='manufacturer-part-create' title='{% trans "Create new manufacturer part" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Manufacturer Part" %}
|
||||
</button>
|
||||
|
@ -31,7 +31,7 @@ class LabelListView(ListAPI):
|
||||
filters.SearchFilter
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'enabled',
|
||||
]
|
||||
|
||||
|
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
@ -70,7 +70,7 @@ class GeneralExtraLineList:
|
||||
'reference'
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'order',
|
||||
]
|
||||
|
||||
@ -540,7 +540,7 @@ class SalesOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
||||
rest_filters.DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'order',
|
||||
]
|
||||
|
||||
@ -672,7 +672,7 @@ class SalesOrderList(APIDownloadMixin, ListCreateAPI):
|
||||
'reference': ['reference_int', 'reference'],
|
||||
}
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'customer',
|
||||
]
|
||||
|
||||
@ -814,7 +814,7 @@ class SalesOrderLineItemList(ListCreateAPI):
|
||||
'reference',
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'order',
|
||||
'part',
|
||||
]
|
||||
@ -990,7 +990,7 @@ class SalesOrderAllocationList(ListAPI):
|
||||
]
|
||||
|
||||
# Default filterable fields
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
]
|
||||
|
||||
|
||||
@ -1069,7 +1069,7 @@ class PurchaseOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
||||
rest_filters.DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'order',
|
||||
]
|
||||
|
||||
|
26
InvenTree/order/migrations/0071_auto_20220628_0133.py
Normal file
26
InvenTree/order/migrations/0071_auto_20220628_0133.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-28 01:33
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('stock', '0077_alter_stockitem_notes'),
|
||||
('part', '0079_alter_part_notes'),
|
||||
('order', '0070_auto_20220620_0728'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='salesorderallocation',
|
||||
name='item',
|
||||
field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'part__salable': True, 'part__virtual': False, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.stockitem', verbose_name='Item'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='salesorderlineitem',
|
||||
name='part',
|
||||
field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True, 'virtual': False}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.part', verbose_name='Part'),
|
||||
),
|
||||
]
|
@ -24,6 +24,7 @@ from mptt.models import TreeForeignKey
|
||||
|
||||
import InvenTree.helpers
|
||||
import InvenTree.ready
|
||||
from common.notifications import InvenTreeNotificationBodies
|
||||
from common.settings import currency_code_default
|
||||
from company.models import Company, SupplierPart
|
||||
from InvenTree.exceptions import log_error
|
||||
@ -560,6 +561,14 @@ class PurchaseOrder(Order):
|
||||
self.received_by = user
|
||||
self.complete_order() # This will save the model
|
||||
|
||||
# Issue a notification to interested parties, that this order has been "updated"
|
||||
notify_responsible(
|
||||
self,
|
||||
PurchaseOrder,
|
||||
exclude=user,
|
||||
content=InvenTreeNotificationBodies.ItemsReceived,
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=PurchaseOrder, dispatch_uid='purchase_order_post_save')
|
||||
def after_save_purchase_order(sender, instance: PurchaseOrder, created: bool, **kwargs):
|
||||
@ -1094,6 +1103,22 @@ class SalesOrderLineItem(OrderLineItem):
|
||||
"""Return the API URL associated with the SalesOrderLineItem model"""
|
||||
return reverse('api-so-line-list')
|
||||
|
||||
def clean(self):
|
||||
"""Perform extra validation steps for this SalesOrderLineItem instance"""
|
||||
|
||||
super().clean()
|
||||
|
||||
if self.part:
|
||||
if self.part.virtual:
|
||||
raise ValidationError({
|
||||
'part': _("Virtual part cannot be assigned to a sales order")
|
||||
})
|
||||
|
||||
if not self.part.salable:
|
||||
raise ValidationError({
|
||||
'part': _("Only salable parts can be assigned to a sales order")
|
||||
})
|
||||
|
||||
order = models.ForeignKey(
|
||||
SalesOrder,
|
||||
on_delete=models.CASCADE,
|
||||
@ -1102,7 +1127,16 @@ class SalesOrderLineItem(OrderLineItem):
|
||||
help_text=_('Sales Order')
|
||||
)
|
||||
|
||||
part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, verbose_name=_('Part'), help_text=_('Part'), limit_choices_to={'salable': True})
|
||||
part = models.ForeignKey(
|
||||
'part.Part', on_delete=models.SET_NULL,
|
||||
related_name='sales_order_line_items',
|
||||
null=True,
|
||||
verbose_name=_('Part'),
|
||||
help_text=_('Part'),
|
||||
limit_choices_to={
|
||||
'salable': True,
|
||||
'virtual': False,
|
||||
})
|
||||
|
||||
sale_price = InvenTreeModelMoneyField(
|
||||
max_digits=19,
|
||||
@ -1400,6 +1434,7 @@ class SalesOrderAllocation(models.Model):
|
||||
related_name='sales_order_allocations',
|
||||
limit_choices_to={
|
||||
'part__salable': True,
|
||||
'part__virtual': False,
|
||||
'belongs_to': None,
|
||||
'sales_order': None,
|
||||
},
|
||||
|
@ -135,7 +135,7 @@ class CategoryList(ListCreateAPI):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
]
|
||||
|
||||
ordering_fields = [
|
||||
@ -281,7 +281,7 @@ class PartSalePriceList(ListCreateAPI):
|
||||
DjangoFilterBackend
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'part',
|
||||
]
|
||||
|
||||
@ -304,7 +304,7 @@ class PartInternalPriceList(ListCreateAPI):
|
||||
DjangoFilterBackend
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'part',
|
||||
]
|
||||
|
||||
@ -319,7 +319,7 @@ class PartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'part',
|
||||
]
|
||||
|
||||
@ -866,7 +866,8 @@ class PartFilter(rest_filters.FilterSet):
|
||||
def filter_in_bom(self, queryset, name, part):
|
||||
"""Limit queryset to parts in the BOM for the specified part"""
|
||||
|
||||
queryset = queryset.filter(id__in=part.get_parts_in_bom())
|
||||
bom_parts = part.get_parts_in_bom()
|
||||
queryset = queryset.filter(id__in=[p.pk for p in bom_parts])
|
||||
return queryset
|
||||
|
||||
is_template = rest_filters.BooleanFilter()
|
||||
@ -1412,7 +1413,7 @@ class PartParameterTemplateList(ListCreateAPI):
|
||||
filters.SearchFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'name',
|
||||
]
|
||||
|
||||
@ -1477,7 +1478,7 @@ class PartParameterList(ListCreateAPI):
|
||||
DjangoFilterBackend
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'part',
|
||||
'template',
|
||||
]
|
||||
@ -1758,7 +1759,7 @@ class BomList(ListCreateDestroyAPIView):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
]
|
||||
|
||||
|
||||
@ -1857,7 +1858,7 @@ class BomItemSubstituteList(ListCreateAPI):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'part',
|
||||
'bom_item',
|
||||
]
|
||||
|
20
InvenTree/part/migrations/0080_alter_part_image.py
Normal file
20
InvenTree/part/migrations/0080_alter_part_image.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-27 23:08
|
||||
|
||||
from django.db import migrations
|
||||
import part.models
|
||||
import stdimage.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('part', '0079_alter_part_notes'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='image',
|
||||
field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=part.models.rename_part_image, variations={'preview': (256, 256), 'thumbnail': (128, 128)}, verbose_name='Image'),
|
||||
),
|
||||
]
|
@ -20,7 +20,7 @@
|
||||
<h4>{% trans "Part Stock" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if roles.stock.add %}
|
||||
{% if roles.stock.add and not part.virtual %}
|
||||
<button type='button' class='btn btn-success' id='new-stock-item' title='{% trans "Create new stock item" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Stock Item" %}
|
||||
</button>
|
||||
|
@ -400,6 +400,21 @@ class PartAPITest(InvenTreeAPITestCase):
|
||||
for part in response.data:
|
||||
self.assertEqual(part['category'], 2)
|
||||
|
||||
def test_filter_by_in_bom(self):
|
||||
"""Test that we can filter part list by the 'in_bom_for' parameter"""
|
||||
|
||||
url = reverse('api-part-list')
|
||||
|
||||
response = self.get(
|
||||
url,
|
||||
{
|
||||
'in_bom_for': 100,
|
||||
},
|
||||
expected_code=200,
|
||||
)
|
||||
|
||||
self.assertEqual(len(response.data), 4)
|
||||
|
||||
def test_filter_by_related(self):
|
||||
"""Test that we can filter by the 'related' status"""
|
||||
url = reverse('api-part-list')
|
||||
|
@ -1,14 +1,13 @@
|
||||
"""Utility file to enable simper imports."""
|
||||
|
||||
from .helpers import MixinImplementationError, MixinNotImplementedError
|
||||
from .plugin import IntegrationPluginBase, InvenTreePlugin
|
||||
from .plugin import InvenTreePlugin
|
||||
from .registry import registry
|
||||
|
||||
__all__ = [
|
||||
'registry',
|
||||
|
||||
'InvenTreePlugin',
|
||||
'IntegrationPluginBase',
|
||||
'MixinNotImplementedError',
|
||||
'MixinImplementationError',
|
||||
]
|
||||
|
@ -62,7 +62,7 @@ class PluginList(ListAPI):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'active',
|
||||
]
|
||||
|
||||
@ -140,7 +140,7 @@ class PluginSettingList(ListAPI):
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'plugin__active',
|
||||
'plugin__key',
|
||||
]
|
||||
|
@ -298,16 +298,3 @@ class InvenTreePlugin(MixinBase, MetaBase):
|
||||
self.package = package
|
||||
self.sign_state = sign_state
|
||||
# endregion
|
||||
|
||||
|
||||
class IntegrationPluginBase(InvenTreePlugin):
|
||||
"""Legacy base class for plugins.
|
||||
|
||||
Do not use!
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Send warning about using this reference."""
|
||||
# TODO remove in 0.8.0
|
||||
warnings.warn("This import is deprecated - use InvenTreePlugin", DeprecationWarning)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -5,7 +5,7 @@ from datetime import datetime
|
||||
from django.test import TestCase
|
||||
|
||||
import plugin.templatetags.plugin_extras as plugin_tags
|
||||
from plugin import IntegrationPluginBase, InvenTreePlugin, registry
|
||||
from plugin import InvenTreePlugin, registry
|
||||
from plugin.samples.integration.another_sample import (NoIntegrationPlugin,
|
||||
WrongIntegrationPlugin)
|
||||
from plugin.samples.integration.sample import SampleIntegrationPlugin
|
||||
@ -162,11 +162,3 @@ class InvenTreePluginTests(TestCase):
|
||||
self.assertEqual(self.plugin_old.slug, 'old')
|
||||
# check default value is used
|
||||
self.assertEqual(self.plugin_old.get_meta_value('ABC', 'ABCD', '123'), '123')
|
||||
|
||||
# check usage of the old class fires
|
||||
class OldPlugin(IntegrationPluginBase):
|
||||
pass
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
plg = OldPlugin()
|
||||
self.assertIsInstance(plg, InvenTreePlugin)
|
||||
|
@ -34,7 +34,7 @@ class ReportListView(ListAPI):
|
||||
filters.SearchFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'enabled',
|
||||
]
|
||||
|
||||
|
@ -283,7 +283,7 @@ class StockLocationList(ListCreateAPI):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
@ -1064,7 +1064,7 @@ class StockAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
||||
filters.SearchFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'stock_item',
|
||||
]
|
||||
|
||||
@ -1095,7 +1095,7 @@ class StockItemTestResultList(ListCreateDestroyAPIView):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'test',
|
||||
'user',
|
||||
'result',
|
||||
@ -1302,7 +1302,7 @@ class StockTrackingList(ListAPI):
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
'item',
|
||||
'user',
|
||||
]
|
||||
|
@ -342,6 +342,7 @@ class StockItem(MetadataMixin, MPTTModel):
|
||||
- Unique serial number requirement
|
||||
- Adds a transaction note when the item is first created.
|
||||
"""
|
||||
|
||||
self.validate_unique()
|
||||
self.clean()
|
||||
|
||||
@ -439,6 +440,7 @@ class StockItem(MetadataMixin, MPTTModel):
|
||||
|
||||
The following validation checks are performed:
|
||||
- The 'part' and 'supplier_part.part' fields cannot point to the same Part object
|
||||
- The 'part' is not virtual
|
||||
- The 'part' does not belong to itself
|
||||
- Quantity must be 1 if the StockItem has a serial number
|
||||
"""
|
||||
@ -453,12 +455,18 @@ class StockItem(MetadataMixin, MPTTModel):
|
||||
self.batch = self.batch.strip()
|
||||
|
||||
try:
|
||||
# Trackable parts must have integer values for quantity field!
|
||||
if self.part.trackable:
|
||||
# Trackable parts must have integer values for quantity field!
|
||||
if self.quantity != int(self.quantity):
|
||||
raise ValidationError({
|
||||
'quantity': _('Quantity must be integer value for trackable parts')
|
||||
})
|
||||
|
||||
# Virtual parts cannot have stock items created against them
|
||||
if self.part.virtual:
|
||||
raise ValidationError({
|
||||
'part': _("Stock item cannot be created for virtual parts"),
|
||||
})
|
||||
except PartModels.Part.DoesNotExist:
|
||||
# For some reason the 'clean' process sometimes throws errors because self.part does not exist
|
||||
# It *seems* that this only occurs in unit testing, though.
|
||||
@ -582,7 +590,8 @@ class StockItem(MetadataMixin, MPTTModel):
|
||||
part = models.ForeignKey(
|
||||
'part.Part', on_delete=models.CASCADE,
|
||||
verbose_name=_('Base Part'),
|
||||
related_name='stock_items', help_text=_('Base part'),
|
||||
related_name='stock_items',
|
||||
help_text=_('Base part'),
|
||||
limit_choices_to={
|
||||
'virtual': False
|
||||
})
|
||||
|
@ -79,6 +79,21 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
- Includes serialization for the item location
|
||||
"""
|
||||
|
||||
part = serializers.PrimaryKeyRelatedField(
|
||||
queryset=part_models.Part.objects.all(),
|
||||
many=False, allow_null=False,
|
||||
help_text=_("Base Part"),
|
||||
label=_("Part"),
|
||||
)
|
||||
|
||||
def validate_part(self, part):
|
||||
"""Ensure the provided Part instance is valid"""
|
||||
|
||||
if part.virtual:
|
||||
raise ValidationError(_("Stock item cannot be created for virtual parts"))
|
||||
|
||||
return part
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""Custom update method to pass the user information through to the instance."""
|
||||
instance._user = self.context['user']
|
||||
@ -168,7 +183,11 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
|
||||
def get_purchase_price_string(self, obj):
|
||||
"""Return purchase price as string."""
|
||||
return str(obj.purchase_price) if obj.purchase_price else '-'
|
||||
if obj.purchase_price:
|
||||
obj.purchase_price.decimal_places_display = 4
|
||||
return str(obj.purchase_price)
|
||||
|
||||
return '-'
|
||||
|
||||
purchase_order_reference = serializers.CharField(source='purchase_order.reference', read_only=True)
|
||||
sales_order_reference = serializers.CharField(source='sales_order.reference', read_only=True)
|
||||
|
23
InvenTree/templates/403_csrf.html
Normal file
23
InvenTree/templates/403_csrf.html
Normal file
@ -0,0 +1,23 @@
|
||||
{% extends "account/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block page_title %}
|
||||
{% inventree_title %} | {% trans "Permission Denied" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{% trans "Authentication Failure" %}</h3>
|
||||
|
||||
<div class='alert alert-danger alert-block'>
|
||||
{% trans "You have been logged out from InvenTree." %}
|
||||
</div>
|
||||
<hr>
|
||||
<div class='btn-group float-right' role='group'>
|
||||
<a type='button' class='btn btn-primary' href='{% url "account_login" %}'>
|
||||
<span class='fas fa-sign-in-alt'></span> {% trans "Login" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
11
InvenTree/templates/email/purchase_order_received.html
Normal file
11
InvenTree/templates/email/purchase_order_received.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "email/email.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block title %}
|
||||
{{ message }}
|
||||
{% if link %}
|
||||
<p>{% trans "Click on the following link to view this order" %}: <a href='{{ link }}'>{{ link }}</a></p>
|
||||
{% endif %}
|
||||
{% endblock title %}
|
@ -284,6 +284,7 @@ function loadAttachmentTable(url, options) {
|
||||
},
|
||||
{
|
||||
field: 'upload_date',
|
||||
sortable: true,
|
||||
title: '{% trans "Upload Date" %}',
|
||||
formatter: function(value, row) {
|
||||
var html = renderDate(value);
|
||||
|
@ -174,6 +174,7 @@ function completeBuildOrder(build_id, options={}) {
|
||||
|
||||
var fields = {
|
||||
accept_unallocated: {},
|
||||
accept_overallocated: {},
|
||||
accept_incomplete: {},
|
||||
};
|
||||
|
||||
|
@ -429,19 +429,27 @@ function getAvailableTableFilters(tableKey) {
|
||||
title: '{% trans "Include subcategories" %}',
|
||||
description: '{% trans "Include parts in subcategories" %}',
|
||||
},
|
||||
has_ipn: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has IPN" %}',
|
||||
description: '{% trans "Part has internal part number" %}',
|
||||
},
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
description: '{% trans "Show active parts" %}',
|
||||
},
|
||||
is_template: {
|
||||
assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
title: '{% trans "Assembly" %}',
|
||||
},
|
||||
unallocated_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Available stock" %}',
|
||||
},
|
||||
component: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Component" %}',
|
||||
},
|
||||
has_ipn: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has IPN" %}',
|
||||
description: '{% trans "Part has internal part number" %}',
|
||||
},
|
||||
has_stock: {
|
||||
type: 'bool',
|
||||
@ -451,34 +459,30 @@ function getAvailableTableFilters(tableKey) {
|
||||
type: 'bool',
|
||||
title: '{% trans "Low stock" %}',
|
||||
},
|
||||
unallocated_stock: {
|
||||
purchaseable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Available stock" %}',
|
||||
},
|
||||
assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembly" %}',
|
||||
},
|
||||
component: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Component" %}',
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Subscribed" %}',
|
||||
title: '{% trans "Purchasable" %}',
|
||||
},
|
||||
salable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Salable" %}',
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Subscribed" %}',
|
||||
},
|
||||
is_template: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
},
|
||||
trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
},
|
||||
purchaseable: {
|
||||
virtual: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Purchasable" %}',
|
||||
},
|
||||
title: '{% trans "Virtual" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -374,6 +374,11 @@ $.fn.inventreeTable = function(options) {
|
||||
options.pageList = [25, 50, 100, 250, 'all'];
|
||||
options.totalField = 'count';
|
||||
options.dataField = 'results';
|
||||
|
||||
if (options.sidePagination == null) {
|
||||
options.sidePagination = 'server';
|
||||
}
|
||||
|
||||
} else {
|
||||
options.pagination = false;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ invoke>=1.4.0 # Invoke build tool
|
||||
psycopg2>=2.9.1
|
||||
mysqlclient>=2.0.3
|
||||
pgcli>=3.1.0
|
||||
mariadb>=1.0.7
|
||||
mariadb>=1.0.7,<1.1.0
|
||||
|
||||
# gunicorn web server
|
||||
gunicorn>=20.1.0
|
||||
|
13
requirements-dev.in
Normal file
13
requirements-dev.in
Normal file
@ -0,0 +1,13 @@
|
||||
# Dev requirements for InvenTree
|
||||
-c requirements.txt
|
||||
coverage # Unit test coverage
|
||||
coveralls==2.1.2 # Coveralls linking (for tracking coverage) # PINNED 2022-06-28 - Old version needed for correct upload
|
||||
django-debug-toolbar # Debug / profiling toolbar
|
||||
django-test-migrations # Unit testing for database migrations
|
||||
flake8 # PEP checking
|
||||
flake8-docstrings # docstring format testing
|
||||
isort # python import sorting
|
||||
pep8-naming # PEP naming convention extension
|
||||
pip-tools # Compile pip requirements
|
||||
pre-commit # Git pre-commit
|
||||
setuptools # Standard depenedency
|
279
requirements-dev.txt
Normal file
279
requirements-dev.txt
Normal file
@ -0,0 +1,279 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with python 3.9
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --generate-hashes --output-file=requirements-dev.txt requirements-dev.in
|
||||
#
|
||||
asgiref==3.5.2 \
|
||||
--hash=sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4 \
|
||||
--hash=sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# django
|
||||
certifi==2022.6.15 \
|
||||
--hash=sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d \
|
||||
--hash=sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
cfgv==3.3.1 \
|
||||
--hash=sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426 \
|
||||
--hash=sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736
|
||||
# via pre-commit
|
||||
charset-normalizer==2.0.12 \
|
||||
--hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \
|
||||
--hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
click==8.1.3 \
|
||||
--hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \
|
||||
--hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48
|
||||
# via pip-tools
|
||||
coverage==5.5 \
|
||||
--hash=sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c \
|
||||
--hash=sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6 \
|
||||
--hash=sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45 \
|
||||
--hash=sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a \
|
||||
--hash=sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03 \
|
||||
--hash=sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529 \
|
||||
--hash=sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a \
|
||||
--hash=sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a \
|
||||
--hash=sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2 \
|
||||
--hash=sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6 \
|
||||
--hash=sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759 \
|
||||
--hash=sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53 \
|
||||
--hash=sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a \
|
||||
--hash=sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4 \
|
||||
--hash=sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff \
|
||||
--hash=sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502 \
|
||||
--hash=sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793 \
|
||||
--hash=sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb \
|
||||
--hash=sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905 \
|
||||
--hash=sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821 \
|
||||
--hash=sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b \
|
||||
--hash=sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81 \
|
||||
--hash=sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0 \
|
||||
--hash=sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b \
|
||||
--hash=sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3 \
|
||||
--hash=sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184 \
|
||||
--hash=sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701 \
|
||||
--hash=sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a \
|
||||
--hash=sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82 \
|
||||
--hash=sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638 \
|
||||
--hash=sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5 \
|
||||
--hash=sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083 \
|
||||
--hash=sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6 \
|
||||
--hash=sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90 \
|
||||
--hash=sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465 \
|
||||
--hash=sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a \
|
||||
--hash=sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3 \
|
||||
--hash=sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e \
|
||||
--hash=sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066 \
|
||||
--hash=sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf \
|
||||
--hash=sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b \
|
||||
--hash=sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae \
|
||||
--hash=sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669 \
|
||||
--hash=sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873 \
|
||||
--hash=sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b \
|
||||
--hash=sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6 \
|
||||
--hash=sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb \
|
||||
--hash=sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160 \
|
||||
--hash=sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c \
|
||||
--hash=sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079 \
|
||||
--hash=sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d \
|
||||
--hash=sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# coveralls
|
||||
coveralls==2.1.2 \
|
||||
--hash=sha256:4430b862baabb3cf090d36d84d331966615e4288d8a8c5957e0fd456d0dd8bd6 \
|
||||
--hash=sha256:b3b60c17b03a0dee61952a91aed6f131e0b2ac8bd5da909389c53137811409e1
|
||||
# via -r requirements-dev.in
|
||||
distlib==0.3.4 \
|
||||
--hash=sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b \
|
||||
--hash=sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579
|
||||
# via virtualenv
|
||||
django==3.2.13 \
|
||||
--hash=sha256:6d93497a0a9bf6ba0e0b1a29cccdc40efbfc76297255b1309b3a884a688ec4b6 \
|
||||
--hash=sha256:b896ca61edc079eb6bbaa15cf6071eb69d6aac08cce5211583cfb41515644fdf
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# django-debug-toolbar
|
||||
django-debug-toolbar==3.5.0 \
|
||||
--hash=sha256:89a52128309eb4da12738801ff0c202d2ff8730d1c3225fac6acf630c303e661 \
|
||||
--hash=sha256:97965f2630692de316ea0c1ca5bfa81660d7ba13146dbc6be2059cf55b35d0e5
|
||||
# via -r requirements-dev.in
|
||||
django-test-migrations==1.2.0 \
|
||||
--hash=sha256:874884ff4e980583cd9f1c986bb9fbfe72b436e759be23004e4f52c26a15f363 \
|
||||
--hash=sha256:9e8b9b4364fef70dde10a5f85c5a75d447ca2189ec648325610fab1268daec97
|
||||
# via -r requirements-dev.in
|
||||
docopt==0.6.2 \
|
||||
--hash=sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491
|
||||
# via coveralls
|
||||
filelock==3.7.1 \
|
||||
--hash=sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404 \
|
||||
--hash=sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04
|
||||
# via virtualenv
|
||||
flake8==4.0.1 \
|
||||
--hash=sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d \
|
||||
--hash=sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d
|
||||
# via
|
||||
# -r requirements-dev.in
|
||||
# flake8-docstrings
|
||||
# pep8-naming
|
||||
flake8-docstrings==1.6.0 \
|
||||
--hash=sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde \
|
||||
--hash=sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b
|
||||
# via -r requirements-dev.in
|
||||
identify==2.5.1 \
|
||||
--hash=sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa \
|
||||
--hash=sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82
|
||||
# via pre-commit
|
||||
idna==3.3 \
|
||||
--hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \
|
||||
--hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
isort==5.10.1 \
|
||||
--hash=sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7 \
|
||||
--hash=sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951
|
||||
# via -r requirements-dev.in
|
||||
mccabe==0.6.1 \
|
||||
--hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \
|
||||
--hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f
|
||||
# via flake8
|
||||
nodeenv==1.7.0 \
|
||||
--hash=sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e \
|
||||
--hash=sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b
|
||||
# via pre-commit
|
||||
pep517==0.12.0 \
|
||||
--hash=sha256:931378d93d11b298cf511dd634cf5ea4cb249a28ef84160b3247ee9afb4e8ab0 \
|
||||
--hash=sha256:dd884c326898e2c6e11f9e0b64940606a93eb10ea022a2e067959f3a110cf161
|
||||
# via pip-tools
|
||||
pep8-naming==0.13.0 \
|
||||
--hash=sha256:069ea20e97f073b3e6d4f789af2a57816f281ca64b86210c7d471117a4b6bfd0 \
|
||||
--hash=sha256:9f38e6dcf867a1fb7ad47f5ff72c0ddae544a6cf64eb9f7600b7b3c0bb5980b5
|
||||
# via -r requirements-dev.in
|
||||
pip-tools==6.6.2 \
|
||||
--hash=sha256:6b486548e5a139e30e4c4a225b3b7c2d46942a9f6d1a91143c21b1de4d02fd9b \
|
||||
--hash=sha256:f638503a9f77d98d9a7d72584b1508d3f82ed019b8fab24f4e5ad078c1b8c95e
|
||||
# via -r requirements-dev.in
|
||||
platformdirs==2.5.2 \
|
||||
--hash=sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788 \
|
||||
--hash=sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19
|
||||
# via virtualenv
|
||||
pre-commit==2.19.0 \
|
||||
--hash=sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10 \
|
||||
--hash=sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615
|
||||
# via -r requirements-dev.in
|
||||
pycodestyle==2.8.0 \
|
||||
--hash=sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20 \
|
||||
--hash=sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f
|
||||
# via flake8
|
||||
pydocstyle==6.1.1 \
|
||||
--hash=sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc \
|
||||
--hash=sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4
|
||||
# via flake8-docstrings
|
||||
pyflakes==2.4.0 \
|
||||
--hash=sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c \
|
||||
--hash=sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e
|
||||
# via flake8
|
||||
pytz==2022.1 \
|
||||
--hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7 \
|
||||
--hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# django
|
||||
pyyaml==6.0 \
|
||||
--hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
|
||||
--hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \
|
||||
--hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \
|
||||
--hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \
|
||||
--hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \
|
||||
--hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \
|
||||
--hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \
|
||||
--hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \
|
||||
--hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \
|
||||
--hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \
|
||||
--hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \
|
||||
--hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \
|
||||
--hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \
|
||||
--hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \
|
||||
--hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \
|
||||
--hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \
|
||||
--hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \
|
||||
--hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \
|
||||
--hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \
|
||||
--hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \
|
||||
--hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \
|
||||
--hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \
|
||||
--hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \
|
||||
--hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \
|
||||
--hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \
|
||||
--hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \
|
||||
--hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \
|
||||
--hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \
|
||||
--hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \
|
||||
--hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \
|
||||
--hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \
|
||||
--hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \
|
||||
--hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# pre-commit
|
||||
requests==2.28.0 \
|
||||
--hash=sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f \
|
||||
--hash=sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# coveralls
|
||||
six==1.16.0 \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# virtualenv
|
||||
snowballstemmer==2.2.0 \
|
||||
--hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \
|
||||
--hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a
|
||||
# via pydocstyle
|
||||
sqlparse==0.4.2 \
|
||||
--hash=sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae \
|
||||
--hash=sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# django
|
||||
# django-debug-toolbar
|
||||
toml==0.10.2 \
|
||||
--hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \
|
||||
--hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f
|
||||
# via pre-commit
|
||||
tomli==2.0.1 \
|
||||
--hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
|
||||
--hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
|
||||
# via pep517
|
||||
typing-extensions==4.2.0 \
|
||||
--hash=sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708 \
|
||||
--hash=sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376
|
||||
# via django-test-migrations
|
||||
urllib3==1.26.9 \
|
||||
--hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \
|
||||
--hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
virtualenv==20.15.0 \
|
||||
--hash=sha256:4c44b1d77ca81f8368e2d7414f9b20c428ad16b343ac6d226206c5b84e2b4fcc \
|
||||
--hash=sha256:804cce4de5b8a322f099897e308eecc8f6e2951f1a8e7e2b3598dff865f01336
|
||||
# via pre-commit
|
||||
wheel==0.37.1 \
|
||||
--hash=sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a \
|
||||
--hash=sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4
|
||||
# via pip-tools
|
||||
|
||||
# WARNING: The following packages were not pinned, but pip requires them to be
|
||||
# pinned when the requirements file includes hashes. Consider using the --allow-unsafe flag.
|
||||
# pip
|
||||
# setuptools
|
37
requirements.in
Normal file
37
requirements.in
Normal file
@ -0,0 +1,37 @@
|
||||
# Please keep this list sorted - if you pin a version provide a reason
|
||||
Django<4 # Django package
|
||||
coreapi # API documentation for djangorestframework
|
||||
django-allauth # SSO for external providers via OpenID
|
||||
django-allauth-2fa # MFA / 2FA
|
||||
django-cleanup # Automated deletion of old / unused uploaded files
|
||||
django-cors-headers # CORS headers extension for DRF
|
||||
django-crispy-forms # Form helpers
|
||||
django-error-report # Error report viewer for the admin interface
|
||||
django-filter # Extended filtering options
|
||||
django-formtools # Form wizard tools
|
||||
django-import-export==2.5.0 # Data import / export for admin interface
|
||||
django-maintenance-mode # Shut down application while reloading etc.
|
||||
django-markdownify # Markdown rendering
|
||||
django-money<3.0.0 # Django app for currency management # FIXED 2022-06-26 to make sure py-moneyed is not conflicting
|
||||
django-mptt==0.11.0 # Modified Preorder Tree Traversal
|
||||
django-redis>=5.0.0 # Redis integration
|
||||
django-q # Background task scheduling
|
||||
django-sql-utils # Advanced query annotation / aggregation
|
||||
django-sslserver # Secure HTTP development server
|
||||
django-stdimage<6.0.0 # Advanced ImageField management # FIXED 2022-06-29 6.0.0 breaks serialization for django-q
|
||||
django-user-sessions # user sessions in DB
|
||||
django-weasyprint # django weasyprint integration
|
||||
djangorestframework # DRF framework
|
||||
django-xforwardedfor-middleware # IP forwarding metadata
|
||||
gunicorn # Gunicorn web server
|
||||
pdf2image # PDF to image conversion
|
||||
pillow # Image manipulation
|
||||
python-barcode[images] # Barcode generator
|
||||
qrcode[pil] # QR code generator
|
||||
rapidfuzz==0.7.6 # Fuzzy string matching
|
||||
sentry-sdk # Error reporting (optional)
|
||||
setuptools # Standard depenedency
|
||||
tablib[xls,xlsx,yaml] # Support for XLS and XLSX formats
|
||||
|
||||
# Fixed sub-dependencies
|
||||
py-moneyed<2.0 # For django-money # FIXED 2022-06-18 as we need `moneyed.localization`
|
300
requirements.txt
300
requirements.txt
@ -1,51 +1,249 @@
|
||||
# Please keep this list sorted
|
||||
Django==3.2.13 # Django package
|
||||
bleach==4.1.0 # HTML santization
|
||||
certifi # Certifi is (most likely) installed through one of the requirements above
|
||||
coreapi==2.3.0 # API documentation
|
||||
coverage==5.3 # Unit test coverage
|
||||
coveralls==2.1.2 # Coveralls linking (for Travis)
|
||||
cryptography==3.4.8 # Cryptography support
|
||||
django-admin-shell==0.1.2 # Python shell for the admin interface
|
||||
django-allauth==0.48.0 # SSO for external providers via OpenID
|
||||
django-allauth-2fa==0.9 # MFA / 2FA # IMPORTANT: Do only change after reviewing GHSA-8j76-mm54-52xq
|
||||
django-cleanup==5.1.0 # Manage deletion of old / unused uploaded files
|
||||
django-cors-headers==3.2.0 # CORS headers extension for DRF
|
||||
django-crispy-forms==1.11.2 # Form helpers
|
||||
django-debug-toolbar==3.2.4 # Debug / profiling toolbar
|
||||
django-error-report==0.2.0 # Error report viewer for the admin interface
|
||||
django-filter==2.4.0 # Extended filtering options
|
||||
django-formtools==2.3 # Form wizard tools
|
||||
django-import-export==2.5.0 # Data import / export for admin interface
|
||||
django-maintenance-mode==0.16.1 # Shut down application while reloading etc.
|
||||
django-markdownify==0.8.0 # Markdown rendering
|
||||
django-money==1.1 # Django app for currency management
|
||||
django-mptt==0.11.0 # Modified Preorder Tree Traversal
|
||||
django-redis>=5.0.0 # Redis integration
|
||||
django-q==1.3.4 # Background task scheduling
|
||||
django-sql-utils==0.5.0 # Advanced query annotation / aggregation
|
||||
django-sslserver==0.22 # Secure HTTP development server
|
||||
django-stdimage==5.1.1 # Advanced ImageField management
|
||||
django-test-migrations==1.1.0 # Unit testing for database migrations
|
||||
django-user-sessions==1.7.1 # user sessions in DB
|
||||
django-weasyprint==2.1.0 # django weasyprint integration
|
||||
djangorestframework==3.12.4 # DRF framework
|
||||
django-xforwardedfor-middleware==2.0 # IP forwarding metadata
|
||||
flake8==3.8.3 # PEP checking
|
||||
flake8-docstrings==1.6.0 # docstring format testing
|
||||
gunicorn>=20.1.0 # Gunicorn web server
|
||||
importlib_metadata # Backport for importlib.metadata
|
||||
isort==5.10.1 # DEV: python import sorting
|
||||
markdown==3.3.4 # Force particular version of markdown
|
||||
pdf2image==1.16.0 # PDF to image conversion
|
||||
pep8-naming==0.11.1 # PEP naming convention extension
|
||||
pre-commit==2.19.0 # Git pre-commit
|
||||
pillow==9.1.1 # Image manipulation
|
||||
py-moneyed==0.8.0 # Specific version requirement for py-moneyed
|
||||
pygments==2.7.4 # Syntax highlighting
|
||||
python-barcode[images]==0.13.1 # Barcode generator
|
||||
qrcode[pil]==6.1 # QR code generator
|
||||
rapidfuzz==0.7.6 # Fuzzy string matching
|
||||
sentry-sdk==1.5.12 # Error reporting (optional)
|
||||
tablib[xls,xlsx,yaml] # Support for XLS and XLSX formats
|
||||
weasyprint==55.0 # PDF generation library
|
||||
#
|
||||
# This file is autogenerated by pip-compile with python 3.9
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile --output-file=requirements.txt requirements.in
|
||||
#
|
||||
arrow==1.2.2
|
||||
# via django-q
|
||||
asgiref==3.5.2
|
||||
# via django
|
||||
babel==2.10.3
|
||||
# via py-moneyed
|
||||
bleach[css]==5.0.0
|
||||
# via django-markdownify
|
||||
blessed==1.19.1
|
||||
# via django-q
|
||||
brotli==1.0.9
|
||||
# via fonttools
|
||||
certifi==2022.6.15
|
||||
# via
|
||||
# requests
|
||||
# sentry-sdk
|
||||
cffi==1.15.0
|
||||
# via
|
||||
# cryptography
|
||||
# weasyprint
|
||||
charset-normalizer==2.0.12
|
||||
# via requests
|
||||
coreapi==2.3.3
|
||||
# via -r requirements.in
|
||||
coreschema==0.0.4
|
||||
# via coreapi
|
||||
cryptography==37.0.2
|
||||
# via pyjwt
|
||||
cssselect2==0.6.0
|
||||
# via weasyprint
|
||||
defusedxml==0.7.1
|
||||
# via
|
||||
# odfpy
|
||||
# python3-openid
|
||||
diff-match-patch==20200713
|
||||
# via django-import-export
|
||||
django==3.2.13
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-allauth
|
||||
# django-allauth-2fa
|
||||
# django-cors-headers
|
||||
# django-error-report
|
||||
# django-filter
|
||||
# django-formtools
|
||||
# django-import-export
|
||||
# django-js-asset
|
||||
# django-markdownify
|
||||
# django-money
|
||||
# django-mptt
|
||||
# django-otp
|
||||
# django-picklefield
|
||||
# django-q
|
||||
# django-redis
|
||||
# django-sql-utils
|
||||
# django-sslserver
|
||||
# django-stdimage
|
||||
# django-user-sessions
|
||||
# django-weasyprint
|
||||
# django-xforwardedfor-middleware
|
||||
# djangorestframework
|
||||
django-allauth==0.51.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-allauth-2fa
|
||||
django-allauth-2fa==0.9
|
||||
# via -r requirements.in
|
||||
django-cleanup==6.0.0
|
||||
# via -r requirements.in
|
||||
django-cors-headers==3.13.0
|
||||
# via -r requirements.in
|
||||
django-crispy-forms==1.14.0
|
||||
# via -r requirements.in
|
||||
django-error-report==0.2.0
|
||||
# via -r requirements.in
|
||||
django-filter==22.1
|
||||
# via -r requirements.in
|
||||
django-formtools==2.3
|
||||
# via -r requirements.in
|
||||
django-import-export==2.5.0
|
||||
# via -r requirements.in
|
||||
django-js-asset==2.0.0
|
||||
# via django-mptt
|
||||
django-maintenance-mode==0.16.3
|
||||
# via -r requirements.in
|
||||
django-markdownify==0.9.2
|
||||
# via -r requirements.in
|
||||
django-money==2.1.1
|
||||
# via -r requirements.in
|
||||
django-mptt==0.11.0
|
||||
# via -r requirements.in
|
||||
django-otp==1.1.3
|
||||
# via django-allauth-2fa
|
||||
django-picklefield==3.1
|
||||
# via django-q
|
||||
django-q==1.3.9
|
||||
# via -r requirements.in
|
||||
django-redis==5.2.0
|
||||
# via -r requirements.in
|
||||
django-sql-utils==0.6.1
|
||||
# via -r requirements.in
|
||||
django-sslserver==0.22
|
||||
# via -r requirements.in
|
||||
django-stdimage==5.3.0
|
||||
# via -r requirements.in
|
||||
django-user-sessions==1.7.1
|
||||
# via -r requirements.in
|
||||
django-weasyprint==2.1.0
|
||||
# via -r requirements.in
|
||||
django-xforwardedfor-middleware==2.0
|
||||
# via -r requirements.in
|
||||
djangorestframework==3.13.1
|
||||
# via -r requirements.in
|
||||
et-xmlfile==1.1.0
|
||||
# via openpyxl
|
||||
fonttools[woff]==4.33.3
|
||||
# via weasyprint
|
||||
gunicorn==20.1.0
|
||||
# via -r requirements.in
|
||||
html5lib==1.1
|
||||
# via weasyprint
|
||||
idna==3.3
|
||||
# via requests
|
||||
importlib-metadata==4.12.0
|
||||
# via markdown
|
||||
itypes==1.2.0
|
||||
# via coreapi
|
||||
jinja2==3.1.2
|
||||
# via coreschema
|
||||
markdown==3.3.7
|
||||
# via django-markdownify
|
||||
markuppy==1.14
|
||||
# via tablib
|
||||
markupsafe==2.1.1
|
||||
# via jinja2
|
||||
oauthlib==3.2.0
|
||||
# via requests-oauthlib
|
||||
odfpy==1.4.1
|
||||
# via tablib
|
||||
openpyxl==3.0.10
|
||||
# via tablib
|
||||
pdf2image==1.16.0
|
||||
# via -r requirements.in
|
||||
pillow==9.1.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-stdimage
|
||||
# pdf2image
|
||||
# python-barcode
|
||||
# qrcode
|
||||
# weasyprint
|
||||
py-moneyed==1.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-money
|
||||
pycparser==2.21
|
||||
# via cffi
|
||||
pydyf==0.2.0
|
||||
# via weasyprint
|
||||
pyjwt[crypto]==2.4.0
|
||||
# via django-allauth
|
||||
pyphen==0.12.0
|
||||
# via weasyprint
|
||||
python-barcode[images]==0.14.0
|
||||
# via -r requirements.in
|
||||
python-dateutil==2.8.2
|
||||
# via arrow
|
||||
python-fsutil==0.6.1
|
||||
# via django-maintenance-mode
|
||||
python3-openid==3.2.0
|
||||
# via django-allauth
|
||||
pytz==2022.1
|
||||
# via
|
||||
# babel
|
||||
# django
|
||||
# djangorestframework
|
||||
pyyaml==6.0
|
||||
# via tablib
|
||||
qrcode[pil]==7.3.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-allauth-2fa
|
||||
rapidfuzz==0.7.6
|
||||
# via -r requirements.in
|
||||
redis==3.5.3
|
||||
# via
|
||||
# django-q
|
||||
# django-redis
|
||||
requests==2.28.0
|
||||
# via
|
||||
# coreapi
|
||||
# django-allauth
|
||||
# requests-oauthlib
|
||||
requests-oauthlib==1.3.1
|
||||
# via django-allauth
|
||||
sentry-sdk==1.6.0
|
||||
# via -r requirements.in
|
||||
six==1.16.0
|
||||
# via
|
||||
# bleach
|
||||
# blessed
|
||||
# html5lib
|
||||
# python-dateutil
|
||||
sqlparse==0.4.2
|
||||
# via
|
||||
# django
|
||||
# django-sql-utils
|
||||
tablib[html,ods,xls,xlsx,yaml]==3.2.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-import-export
|
||||
tinycss2==1.1.1
|
||||
# via
|
||||
# bleach
|
||||
# cssselect2
|
||||
# weasyprint
|
||||
uritemplate==4.1.1
|
||||
# via coreapi
|
||||
urllib3==1.26.9
|
||||
# via
|
||||
# requests
|
||||
# sentry-sdk
|
||||
wcwidth==0.2.5
|
||||
# via blessed
|
||||
weasyprint==55.0
|
||||
# via django-weasyprint
|
||||
webencodings==0.5.1
|
||||
# via
|
||||
# bleach
|
||||
# cssselect2
|
||||
# html5lib
|
||||
# tinycss2
|
||||
xlrd==2.0.1
|
||||
# via tablib
|
||||
xlwt==1.3.0
|
||||
# via tablib
|
||||
zipp==3.8.0
|
||||
# via importlib-metadata
|
||||
zopfli==0.2.1
|
||||
# via fonttools
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
||||
|
4
tasks.py
4
tasks.py
@ -111,10 +111,10 @@ def install(c):
|
||||
@task
|
||||
def setup_dev(c):
|
||||
"""Sets up everything needed for the dev enviroment."""
|
||||
print("Installing required python packages from 'requirements.txt'")
|
||||
print("Installing required python packages from 'requirements-dev.txt'")
|
||||
|
||||
# Install required Python packages with PIP
|
||||
c.run('pip3 install -U -r requirements.txt')
|
||||
c.run('pip3 install -U -r requirements-dev.txt')
|
||||
|
||||
# Install pre-commit hook
|
||||
c.run('pre-commit install')
|
||||
|
Loading…
x
Reference in New Issue
Block a user