mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'master' of https://github.com/inventree/InvenTree into plugin-2037
This commit is contained in:
commit
329b00ae07
9
.github/workflows/html.yaml
vendored
9
.github/workflows/html.yaml
vendored
@ -23,11 +23,13 @@ jobs:
|
|||||||
INVENTREE_MEDIA_ROOT: ./media
|
INVENTREE_MEDIA_ROOT: ./media
|
||||||
INVENTREE_STATIC_ROOT: ./static
|
INVENTREE_STATIC_ROOT: ./static
|
||||||
steps:
|
steps:
|
||||||
- name: Install node.js
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
- run: npm install
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
- name: Install node.js
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: npm install
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
@ -41,7 +43,6 @@ jobs:
|
|||||||
invoke static
|
invoke static
|
||||||
- name: Check HTML Files
|
- name: Check HTML Files
|
||||||
run: |
|
run: |
|
||||||
npm install markuplint
|
|
||||||
npx markuplint InvenTree/build/templates/build/*.html
|
npx markuplint InvenTree/build/templates/build/*.html
|
||||||
npx markuplint InvenTree/company/templates/company/*.html
|
npx markuplint InvenTree/company/templates/company/*.html
|
||||||
npx markuplint InvenTree/order/templates/order/*.html
|
npx markuplint InvenTree/order/templates/order/*.html
|
||||||
|
9
.github/workflows/javascript.yaml
vendored
9
.github/workflows/javascript.yaml
vendored
@ -23,11 +23,13 @@ jobs:
|
|||||||
INVENTREE_MEDIA_ROOT: ./media
|
INVENTREE_MEDIA_ROOT: ./media
|
||||||
INVENTREE_STATIC_ROOT: ./static
|
INVENTREE_STATIC_ROOT: ./static
|
||||||
steps:
|
steps:
|
||||||
- name: Install node.js
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
- run: npm install
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
- name: Install node.js
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- run: npm install
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
@ -45,6 +47,5 @@ jobs:
|
|||||||
python check_js_templates.py
|
python check_js_templates.py
|
||||||
- name: Lint Javascript Files
|
- name: Lint Javascript Files
|
||||||
run: |
|
run: |
|
||||||
npm install eslint eslint-config-google
|
|
||||||
invoke render-js-files
|
invoke render-js-files
|
||||||
npx eslint js_tmp/*.js
|
npx eslint js_tmp/*.js
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -77,8 +77,6 @@ dev/
|
|||||||
locale_stats.json
|
locale_stats.json
|
||||||
|
|
||||||
# node.js
|
# node.js
|
||||||
package-lock.json
|
|
||||||
package.json
|
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# maintenance locker
|
# maintenance locker
|
||||||
|
@ -49,6 +49,9 @@ class ReferenceIndexingMixin(models.Model):
|
|||||||
"""
|
"""
|
||||||
A mixin for keeping track of numerical copies of the "reference" field.
|
A mixin for keeping track of numerical copies of the "reference" field.
|
||||||
|
|
||||||
|
!!DANGER!! always add `ReferenceIndexingSerializerMixin`to all your models serializers to
|
||||||
|
ensure the reference field is not too big
|
||||||
|
|
||||||
Here, we attempt to convert a "reference" field value (char) to an integer,
|
Here, we attempt to convert a "reference" field value (char) to an integer,
|
||||||
for performing fast natural sorting.
|
for performing fast natural sorting.
|
||||||
|
|
||||||
@ -69,22 +72,25 @@ class ReferenceIndexingMixin(models.Model):
|
|||||||
|
|
||||||
reference = getattr(self, 'reference', '')
|
reference = getattr(self, 'reference', '')
|
||||||
|
|
||||||
# Default value if we cannot convert to an integer
|
self.reference_int = extract_int(reference)
|
||||||
ref_int = 0
|
|
||||||
|
|
||||||
# Look at the start of the string - can it be "integerized"?
|
reference_int = models.BigIntegerField(default=0)
|
||||||
result = re.match(r"^(\d+)", reference)
|
|
||||||
|
|
||||||
if result and len(result.groups()) == 1:
|
|
||||||
ref = result.groups()[0]
|
|
||||||
try:
|
|
||||||
ref_int = int(ref)
|
|
||||||
except:
|
|
||||||
ref_int = 0
|
|
||||||
|
|
||||||
self.reference_int = ref_int
|
def extract_int(reference):
|
||||||
|
# Default value if we cannot convert to an integer
|
||||||
|
ref_int = 0
|
||||||
|
|
||||||
reference_int = models.IntegerField(default=0)
|
# Look at the start of the string - can it be "integerized"?
|
||||||
|
result = re.match(r"^(\d+)", reference)
|
||||||
|
|
||||||
|
if result and len(result.groups()) == 1:
|
||||||
|
ref = result.groups()[0]
|
||||||
|
try:
|
||||||
|
ref_int = int(ref)
|
||||||
|
except:
|
||||||
|
ref_int = 0
|
||||||
|
return ref_int
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeAttachment(models.Model):
|
class InvenTreeAttachment(models.Model):
|
||||||
|
@ -16,6 +16,7 @@ from django.conf import settings
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
from djmoney.contrib.django_rest_framework.fields import MoneyField
|
from djmoney.contrib.django_rest_framework.fields import MoneyField
|
||||||
from djmoney.money import Money
|
from djmoney.money import Money
|
||||||
@ -27,6 +28,8 @@ from rest_framework.fields import empty
|
|||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.serializers import DecimalField
|
from rest_framework.serializers import DecimalField
|
||||||
|
|
||||||
|
from .models import extract_int
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeMoneySerializer(MoneyField):
|
class InvenTreeMoneySerializer(MoneyField):
|
||||||
"""
|
"""
|
||||||
@ -239,6 +242,17 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class ReferenceIndexingSerializerMixin():
|
||||||
|
"""
|
||||||
|
This serializer mixin ensures the the reference is not to big / small
|
||||||
|
for the BigIntegerField
|
||||||
|
"""
|
||||||
|
def validate_reference(self, value):
|
||||||
|
if extract_int(value) > models.BigIntegerField.MAX_BIGINT:
|
||||||
|
raise serializers.ValidationError('reference is to to big')
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeAttachmentSerializerField(serializers.FileField):
|
class InvenTreeAttachmentSerializerField(serializers.FileField):
|
||||||
"""
|
"""
|
||||||
Override the DRF native FileField serializer,
|
Override the DRF native FileField serializer,
|
||||||
|
18
InvenTree/build/migrations/0034_alter_build_reference_int.py
Normal file
18
InvenTree/build/migrations/0034_alter_build_reference_int.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.2.5 on 2021-12-01 21:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('build', '0033_auto_20211128_0151'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='build',
|
||||||
|
name='reference_int',
|
||||||
|
field=models.BigIntegerField(default=0),
|
||||||
|
),
|
||||||
|
]
|
@ -16,7 +16,7 @@ from rest_framework import serializers
|
|||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
from InvenTree.serializers import InvenTreeModelSerializer, InvenTreeAttachmentSerializer
|
from InvenTree.serializers import InvenTreeModelSerializer, InvenTreeAttachmentSerializer
|
||||||
from InvenTree.serializers import UserSerializerBrief
|
from InvenTree.serializers import UserSerializerBrief, ReferenceIndexingSerializerMixin
|
||||||
|
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
from InvenTree.serializers import InvenTreeDecimalField
|
from InvenTree.serializers import InvenTreeDecimalField
|
||||||
@ -32,7 +32,7 @@ from users.serializers import OwnerSerializer
|
|||||||
from .models import Build, BuildItem, BuildOrderAttachment
|
from .models import Build, BuildItem, BuildOrderAttachment
|
||||||
|
|
||||||
|
|
||||||
class BuildSerializer(InvenTreeModelSerializer):
|
class BuildSerializer(ReferenceIndexingSerializerMixin, InvenTreeModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializes a Build object
|
Serializes a Build object
|
||||||
"""
|
"""
|
||||||
|
23
InvenTree/order/migrations/0054_auto_20211201_2139.py
Normal file
23
InvenTree/order/migrations/0054_auto_20211201_2139.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 3.2.5 on 2021-12-01 21:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('order', '0053_auto_20211128_0151'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='purchaseorder',
|
||||||
|
name='reference_int',
|
||||||
|
field=models.BigIntegerField(default=0),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='reference_int',
|
||||||
|
field=models.BigIntegerField(default=0),
|
||||||
|
),
|
||||||
|
]
|
@ -24,6 +24,7 @@ from InvenTree.serializers import InvenTreeAttachmentSerializer
|
|||||||
from InvenTree.serializers import InvenTreeModelSerializer
|
from InvenTree.serializers import InvenTreeModelSerializer
|
||||||
from InvenTree.serializers import InvenTreeDecimalField
|
from InvenTree.serializers import InvenTreeDecimalField
|
||||||
from InvenTree.serializers import InvenTreeMoneySerializer
|
from InvenTree.serializers import InvenTreeMoneySerializer
|
||||||
|
from InvenTree.serializers import ReferenceIndexingSerializerMixin
|
||||||
from InvenTree.status_codes import StockStatus
|
from InvenTree.status_codes import StockStatus
|
||||||
|
|
||||||
from part.serializers import PartBriefSerializer
|
from part.serializers import PartBriefSerializer
|
||||||
@ -39,7 +40,7 @@ from .models import SalesOrderAllocation
|
|||||||
from users.serializers import OwnerSerializer
|
from users.serializers import OwnerSerializer
|
||||||
|
|
||||||
|
|
||||||
class POSerializer(InvenTreeModelSerializer):
|
class POSerializer(ReferenceIndexingSerializerMixin, InvenTreeModelSerializer):
|
||||||
""" Serializer for a PurchaseOrder object """
|
""" Serializer for a PurchaseOrder object """
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -394,7 +395,7 @@ class POAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderSerializer(InvenTreeModelSerializer):
|
class SalesOrderSerializer(ReferenceIndexingSerializerMixin, InvenTreeModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializers for the SalesOrder object
|
Serializers for the SalesOrder object
|
||||||
"""
|
"""
|
||||||
|
@ -105,6 +105,25 @@ class PurchaseOrderTest(OrderTest):
|
|||||||
self.assertEqual(data['pk'], 1)
|
self.assertEqual(data['pk'], 1)
|
||||||
self.assertEqual(data['description'], 'Ordering some screws')
|
self.assertEqual(data['description'], 'Ordering some screws')
|
||||||
|
|
||||||
|
def test_po_reference(self):
|
||||||
|
"""test that a reference with a too big / small reference is not possible"""
|
||||||
|
# get permissions
|
||||||
|
self.assignRole('purchase_order.add')
|
||||||
|
|
||||||
|
url = reverse('api-po-list')
|
||||||
|
huge_numer = 9223372036854775808
|
||||||
|
|
||||||
|
# too big
|
||||||
|
self.post(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
'supplier': 1,
|
||||||
|
'reference': huge_numer,
|
||||||
|
'description': 'PO not created via the API',
|
||||||
|
},
|
||||||
|
expected_code=400
|
||||||
|
)
|
||||||
|
|
||||||
def test_po_attachments(self):
|
def test_po_attachments(self):
|
||||||
|
|
||||||
url = reverse('api-po-attachment-list')
|
url = reverse('api-po-attachment-list')
|
||||||
|
@ -7,7 +7,6 @@ Stock database model definitions
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.core.exceptions import ValidationError, FieldError
|
from django.core.exceptions import ValidationError, FieldError
|
||||||
@ -39,6 +38,7 @@ import label.models
|
|||||||
from InvenTree.status_codes import StockStatus, StockHistoryCode
|
from InvenTree.status_codes import StockStatus, StockHistoryCode
|
||||||
from InvenTree.models import InvenTreeTree, InvenTreeAttachment
|
from InvenTree.models import InvenTreeTree, InvenTreeAttachment
|
||||||
from InvenTree.fields import InvenTreeModelMoneyField, InvenTreeURLField
|
from InvenTree.fields import InvenTreeModelMoneyField, InvenTreeURLField
|
||||||
|
from InvenTree.serializers import extract_int
|
||||||
|
|
||||||
from users.models import Owner
|
from users.models import Owner
|
||||||
|
|
||||||
@ -236,17 +236,7 @@ class StockItem(MPTTModel):
|
|||||||
serial_int = 0
|
serial_int = 0
|
||||||
|
|
||||||
if serial is not None:
|
if serial is not None:
|
||||||
|
serial_int = extract_int(str(serial))
|
||||||
serial = str(serial)
|
|
||||||
|
|
||||||
# Look at the start of the string - can it be "integerized"?
|
|
||||||
result = re.match(r'^(\d+)', serial)
|
|
||||||
|
|
||||||
if result and len(result.groups()) == 1:
|
|
||||||
try:
|
|
||||||
serial_int = int(result.groups()[0])
|
|
||||||
except:
|
|
||||||
serial_int = 0
|
|
||||||
|
|
||||||
self.serial_int = serial_int
|
self.serial_int = serial_int
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ from company.serializers import SupplierPartSerializer
|
|||||||
|
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
import InvenTree.serializers
|
import InvenTree.serializers
|
||||||
from InvenTree.serializers import InvenTreeDecimalField
|
from InvenTree.serializers import InvenTreeDecimalField, extract_int
|
||||||
|
|
||||||
from part.serializers import PartBriefSerializer
|
from part.serializers import PartBriefSerializer
|
||||||
|
|
||||||
@ -73,6 +73,11 @@ class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'uid',
|
'uid',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def validate_serial(self, value):
|
||||||
|
if extract_int(value) > 2147483647:
|
||||||
|
raise serializers.ValidationError('serial is to to big')
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||||
""" Serializer for a StockItem:
|
""" Serializer for a StockItem:
|
||||||
|
@ -40,8 +40,8 @@
|
|||||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css' %}">
|
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.css' %}">
|
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.css' %}">
|
||||||
<link rel='stylesheet' href='{% static "treegrid/css/jquery.treegrid.css" %}'>
|
<link rel='stylesheet' href='{% static "treegrid/css/jquery.treegrid.css" %}'>
|
||||||
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
|
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">
|
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'select2/css/select2.css' %}">
|
<link rel="stylesheet" href="{% static 'select2/css/select2.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'select2/css/select2-bootstrap-5-theme.css' %}">
|
<link rel="stylesheet" href="{% static 'select2/css/select2-bootstrap-5-theme.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'fullcalendar/main.css' %}">
|
<link rel="stylesheet" href="{% static 'fullcalendar/main.css' %}">
|
||||||
|
@ -153,12 +153,7 @@ function partFields(options={}) {
|
|||||||
delete fields['default_expiry'];
|
delete fields['default_expiry'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional fields when "creating" a new part
|
if (options.create || options.duplicate) {
|
||||||
if (options.create) {
|
|
||||||
|
|
||||||
// No supplier parts available yet
|
|
||||||
delete fields['default_supplier'];
|
|
||||||
|
|
||||||
if (global_settings.PART_CREATE_INITIAL) {
|
if (global_settings.PART_CREATE_INITIAL) {
|
||||||
|
|
||||||
fields.initial_stock = {
|
fields.initial_stock = {
|
||||||
@ -187,6 +182,13 @@ function partFields(options={}) {
|
|||||||
group: 'create',
|
group: 'create',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional fields when "creating" a new part
|
||||||
|
if (options.create) {
|
||||||
|
|
||||||
|
// No supplier parts available yet
|
||||||
|
delete fields['default_supplier'];
|
||||||
|
|
||||||
fields.copy_category_parameters = {
|
fields.copy_category_parameters = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
@ -349,6 +351,10 @@ function duplicatePart(pk, options={}) {
|
|||||||
duplicate: pk,
|
duplicate: pk,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (fields.initial_stock_location) {
|
||||||
|
fields.initial_stock_location.value = data.default_location;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove "default_supplier" field
|
// Remove "default_supplier" field
|
||||||
delete fields['default_supplier'];
|
delete fields['default_supplier'];
|
||||||
|
|
||||||
|
7
package.json
Normal file
7
package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"eslint": "^8.3.0",
|
||||||
|
"eslint-config-google": "^0.14.0",
|
||||||
|
"markuplint": "^1.11.4"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user