Compare commits
No commits in common. "master" and "main" have entirely different histories.
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
14
.idea/backend-fontend.iml
generated
14
.idea/backend-fontend.iml
generated
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
<component name="PyDocumentationSettings">
|
|
||||||
<option name="format" value="PLAIN" />
|
|
||||||
<option name="myDocStringFormat" value="Plain" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
6
.idea/encodings.xml
generated
6
.idea/encodings.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Encoding">
|
|
||||||
<file url="file://$PROJECT_DIR$/barcode/settings.py" charset="UTF-8" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
6
.idea/inspectionProfiles/profiles_settings.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Black">
|
|
||||||
<option name="sdkName" value="Python 3.12 (backend-fontend)" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (backend-fontend)" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/backend-fontend.iml" filepath="$PROJECT_DIR$/.idea/backend-fontend.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
BIN
barcode/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
barcode/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
barcode/__pycache__/celery_app.cpython-312.pyc
Normal file
BIN
barcode/__pycache__/celery_app.cpython-312.pyc
Normal file
Binary file not shown.
BIN
barcode/__pycache__/settings.cpython-312.pyc
Normal file
BIN
barcode/__pycache__/settings.cpython-312.pyc
Normal file
Binary file not shown.
BIN
barcode/__pycache__/urls.cpython-312.pyc
Normal file
BIN
barcode/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
barcode/__pycache__/wsgi.cpython-312.pyc
Normal file
BIN
barcode/__pycache__/wsgi.cpython-312.pyc
Normal file
Binary file not shown.
@ -16,9 +16,6 @@ from pathlib import Path
|
|||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
|
||||||
|
|
||||||
@ -28,10 +25,6 @@ SECRET_KEY = 'django-insecure-i#8aotin&c00-&#v@x!moruf-uimxr#c!8pi9ehltop98-@bzr
|
|||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
if DEBUG: # Убедитесь, что это только для разработки
|
|
||||||
REST_FRAMEWORK = {
|
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': [],
|
|
||||||
}
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
CELERY_BROKER_URL = 'redis://localhost:6379/0'
|
CELERY_BROKER_URL = 'redis://localhost:6379/0'
|
||||||
@ -51,8 +44,6 @@ INSTALLED_APPS = [
|
|||||||
'scan',
|
'scan',
|
||||||
'forms',
|
'forms',
|
||||||
'users',
|
'users',
|
||||||
'issues',
|
|
||||||
'production',
|
|
||||||
"channels",
|
"channels",
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework_simplejwt',
|
'rest_framework_simplejwt',
|
||||||
@ -108,10 +99,10 @@ WSGI_APPLICATION = 'barcode.wsgi.application'
|
|||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.postgresql',
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
'NAME': 'postgres',
|
'NAME': 'mydatabase',
|
||||||
'USER': 'postgres',
|
'USER': 'myuser',
|
||||||
'PASSWORD': '1240069630Bk!',
|
'PASSWORD': 'mypassword',
|
||||||
'HOST': 'localhost',
|
'HOST': '31.130.144.182',
|
||||||
'PORT': '5432',
|
'PORT': '5432',
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'client_encoding': 'UTF8',
|
'client_encoding': 'UTF8',
|
||||||
@ -176,12 +167,7 @@ REST_FRAMEWORK = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
CORS_ALLOW_HEADERS = [
|
CORS_ALLOW_HEADERS = ['Content-Type'] # <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
'authorization',
|
|
||||||
'content-type',
|
|
||||||
'x-csrftoken',
|
|
||||||
'x-requested-with',
|
|
||||||
]
|
|
||||||
|
|
||||||
CORS_ALLOW_ALL_ORIGINS = True # <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
CORS_ALLOW_ALL_ORIGINS = True # <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
# <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> localhost:3000
|
# <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> localhost:3000
|
||||||
|
|||||||
@ -30,6 +30,4 @@ urlpatterns = [
|
|||||||
path("inventory/", include("inventory.urls")),
|
path("inventory/", include("inventory.urls")),
|
||||||
path("forms/", include("forms.urls")),
|
path("forms/", include("forms.urls")),
|
||||||
path("api/users/", include("users.urls")),
|
path("api/users/", include("users.urls")),
|
||||||
path("api/production/", include("production.urls")),
|
|
||||||
path("reference/", include("issues.urls")),
|
|
||||||
]
|
]
|
||||||
BIN
batches/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
batches/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
batches/__pycache__/admin.cpython-312.pyc
Normal file
BIN
batches/__pycache__/admin.cpython-312.pyc
Normal file
Binary file not shown.
BIN
batches/__pycache__/apps.cpython-312.pyc
Normal file
BIN
batches/__pycache__/apps.cpython-312.pyc
Normal file
Binary file not shown.
BIN
batches/__pycache__/models.cpython-312.pyc
Normal file
BIN
batches/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
batches/__pycache__/urls.cpython-312.pyc
Normal file
BIN
batches/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
batches/__pycache__/views.cpython-312.pyc
Normal file
BIN
batches/__pycache__/views.cpython-312.pyc
Normal file
Binary file not shown.
BIN
batches/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
batches/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
forms/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/__pycache__/admin.cpython-312.pyc
Normal file
BIN
forms/__pycache__/admin.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/__pycache__/apps.cpython-312.pyc
Normal file
BIN
forms/__pycache__/apps.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/__pycache__/models.cpython-312.pyc
Normal file
BIN
forms/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/__pycache__/serializers.cpython-312.pyc
Normal file
BIN
forms/__pycache__/serializers.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/__pycache__/urls.cpython-312.pyc
Normal file
BIN
forms/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/__pycache__/views.cpython-312.pyc
Normal file
BIN
forms/__pycache__/views.cpython-312.pyc
Normal file
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.1.5 on 2025-03-21 23:48
|
# Generated by Django 5.1.5 on 2025-03-06 04:31
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|||||||
BIN
forms/migrations/__pycache__/0001_initial.cpython-312.pyc
Normal file
BIN
forms/migrations/__pycache__/0001_initial.cpython-312.pyc
Normal file
Binary file not shown.
BIN
forms/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
forms/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
inventory/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/admin.cpython-312.pyc
Normal file
BIN
inventory/__pycache__/admin.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/apps.cpython-312.pyc
Normal file
BIN
inventory/__pycache__/apps.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/models.cpython-312.pyc
Normal file
BIN
inventory/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/serializers.cpython-312.pyc
Normal file
BIN
inventory/__pycache__/serializers.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/urls.cpython-312.pyc
Normal file
BIN
inventory/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/__pycache__/views.cpython-312.pyc
Normal file
BIN
inventory/__pycache__/views.cpython-312.pyc
Normal file
Binary file not shown.
@ -1,7 +1,6 @@
|
|||||||
# Generated by Django 5.1.5 on 2025-03-21 23:48
|
# Generated by Django 5.1.5 on 2025-03-06 04:31
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
@ -10,7 +9,6 @@ class Migration(migrations.Migration):
|
|||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
@ -46,24 +44,4 @@ class Migration(migrations.Migration):
|
|||||||
('sticker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.sticker')),
|
('sticker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.sticker')),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
|
||||||
name='StickerTransferRequest',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
||||||
('status', models.CharField(choices=[('pending', 'Ожидает'), ('accepted', 'Принята'), ('declined', 'Отклонена'), ('cancelled', 'Отменена')], default='pending', max_length=20)),
|
|
||||||
('comment', models.TextField(blank=True, null=True)),
|
|
||||||
('from_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_transfers', to=settings.AUTH_USER_MODEL)),
|
|
||||||
('to_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='received_transfers', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='StickerTransferItem',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('quantity', models.IntegerField()),
|
|
||||||
('sticker', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='inventory.sticker')),
|
|
||||||
('transfer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='inventory.stickertransferrequest')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|||||||
BIN
inventory/migrations/__pycache__/0001_initial.cpython-312.pyc
Normal file
BIN
inventory/migrations/__pycache__/0001_initial.cpython-312.pyc
Normal file
Binary file not shown.
BIN
inventory/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
inventory/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
@ -1,7 +1,5 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from datetime import timedelta, date
|
from datetime import timedelta, date
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
|
|
||||||
class Nomenclature(models.Model):
|
class Nomenclature(models.Model):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
@ -59,23 +57,3 @@ class StickerMovement(models.Model):
|
|||||||
self.sticker.location = "склад"
|
self.sticker.location = "склад"
|
||||||
self.sticker.save()
|
self.sticker.save()
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class StickerTransferRequest(models.Model):
|
|
||||||
STATUS_CHOICES = [
|
|
||||||
("pending", "Ожидает"),
|
|
||||||
("accepted", "Принята"),
|
|
||||||
("declined", "Отклонена"),
|
|
||||||
("cancelled", "Отменена"),
|
|
||||||
]
|
|
||||||
|
|
||||||
from_user = models.ForeignKey(User, related_name="sent_transfers", on_delete=models.CASCADE)
|
|
||||||
to_user = models.ForeignKey(User, related_name="received_transfers", on_delete=models.CASCADE)
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
|
||||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="pending")
|
|
||||||
comment = models.TextField(blank=True, null=True)
|
|
||||||
|
|
||||||
class StickerTransferItem(models.Model):
|
|
||||||
transfer = models.ForeignKey(StickerTransferRequest, related_name="items", on_delete=models.CASCADE)
|
|
||||||
sticker = models.ForeignKey(Sticker, on_delete=models.CASCADE)
|
|
||||||
quantity = models.IntegerField()
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import Nomenclature, Sticker, StickerMovement
|
from .models import Nomenclature, Sticker, StickerMovement
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from django.contrib.auth.models import User
|
|
||||||
|
|
||||||
|
|
||||||
class NomenclatureSerializer(serializers.ModelSerializer):
|
class NomenclatureSerializer(serializers.ModelSerializer):
|
||||||
@ -33,35 +32,3 @@ class StickerMovementSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = StickerMovement
|
model = StickerMovement
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
""" Transfer sticks """
|
|
||||||
|
|
||||||
|
|
||||||
from rest_framework import serializers
|
|
||||||
from .models import StickerTransferRequest, StickerTransferItem, Sticker
|
|
||||||
|
|
||||||
class StickerTransferItemSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = StickerTransferItem
|
|
||||||
fields = ['id', 'sticker', 'quantity']
|
|
||||||
|
|
||||||
|
|
||||||
class StickerTransferRequestSerializer(serializers.ModelSerializer):
|
|
||||||
items = StickerTransferItemSerializer(many=True)
|
|
||||||
from_user = serializers.StringRelatedField(read_only=True)
|
|
||||||
to_user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all())
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = StickerTransferRequest
|
|
||||||
fields = ['id', 'from_user', 'to_user', 'status', 'created_at', 'items']
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
items_data = validated_data.pop('items')
|
|
||||||
transfer = StickerTransferRequest.objects.create(**validated_data)
|
|
||||||
for item in items_data:
|
|
||||||
StickerTransferItem.objects.create(transfer=transfer, **item)
|
|
||||||
return transfer
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
from inventory.models import StickerTransferItem
|
|
||||||
|
|
||||||
|
|
||||||
class StickerTransferItemSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = StickerTransferItem
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
class StickerTransferRequestSerializer(serializers.ModelSerializer):
|
|
||||||
items = StickerTransferItemSerializer(many=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = StickerTransferRequest
|
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
items_data = validated_data.pop("items")
|
|
||||||
transfer = StickerTransferRequest.objects.create(**validated_data)
|
|
||||||
for item in items_data:
|
|
||||||
StickerTransferItem.objects.create(transfer=transfer, **item)
|
|
||||||
return transfer
|
|
||||||
@ -1,35 +1,13 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from .views import (
|
from .views import NomenclatureViewSet, StickerViewSet, StickerMovementViewSet, ExternalNomenclatureView
|
||||||
NomenclatureViewSet,
|
|
||||||
StickerViewSet,
|
|
||||||
StickerMovementViewSet,
|
|
||||||
ExternalNomenclatureView,
|
|
||||||
)
|
|
||||||
from .views_transfer import StickerTransferRequestViewSet # Импортируем новый ViewSet
|
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'nomenclature', NomenclatureViewSet)
|
router.register(r'nomenclature', NomenclatureViewSet)
|
||||||
router.register(r'stickers', StickerViewSet)
|
router.register(r'stickers', StickerViewSet)
|
||||||
router.register(r'movement', StickerMovementViewSet)
|
router.register(r'movement', StickerMovementViewSet)
|
||||||
router.register(r'transfers', StickerTransferRequestViewSet, basename='stickertransfer') # Добавляем
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('api/', include(router.urls)),
|
path('api/', include(router.urls)),
|
||||||
path('api/external-nomenclature/', ExternalNomenclatureView.as_view(), name="external-nomenclature"),
|
path('api/external-nomenclature/', ExternalNomenclatureView.as_view(), name="external-nomenclature"),
|
||||||
]
|
]
|
||||||
"""Трансфер передачи стикеров"""
|
|
||||||
"""{
|
|
||||||
"to_user": 3, // <-- ID пользователя, а не имя!
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"sticker": 12, // ID стикера
|
|
||||||
"quantity": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"sticker": 15,
|
|
||||||
"quantity": 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
@ -117,11 +117,6 @@ class StickerViewSet(viewsets.ModelViewSet):
|
|||||||
self.perform_destroy(instance)
|
self.perform_destroy(instance)
|
||||||
return Response({"message": "Стикер успешно удалён"}, status=status.HTTP_204_NO_CONTENT)
|
return Response({"message": "Стикер успешно удалён"}, status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""Передача стикеров"""
|
|
||||||
|
|
||||||
class StickerMovementViewSet(viewsets.ModelViewSet):
|
class StickerMovementViewSet(viewsets.ModelViewSet):
|
||||||
queryset = StickerMovement.objects.all()
|
queryset = StickerMovement.objects.all()
|
||||||
serializer_class = StickerMovementSerializer
|
serializer_class = StickerMovementSerializer
|
||||||
|
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
from rest_framework import viewsets, status
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from rest_framework.decorators import action
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from .models import StickerTransferRequest, StickerTransferItem, Sticker, StickerMovement
|
|
||||||
from .serializers import StickerTransferRequestSerializer, StickerTransferItemSerializer
|
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
class StickerTransferRequestViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = StickerTransferRequest.objects.all().select_related('from_user', 'to_user').prefetch_related('items__sticker')
|
|
||||||
serializer_class = StickerTransferRequestSerializer
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
user = self.request.user
|
|
||||||
return StickerTransferRequest.objects.filter(from_user=user) | StickerTransferRequest.objects.filter(to_user=user)
|
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
|
||||||
serializer.save(from_user=self.request.user)
|
|
||||||
|
|
||||||
@action(detail=True, methods=['post'])
|
|
||||||
def accept(self, request, pk=None):
|
|
||||||
transfer = get_object_or_404(StickerTransferRequest, pk=pk)
|
|
||||||
|
|
||||||
if transfer.to_user != request.user:
|
|
||||||
return Response({"error": "Нет прав для принятия этой передачи."}, status=status.HTTP_403_FORBIDDEN)
|
|
||||||
|
|
||||||
if transfer.status != 'pending':
|
|
||||||
return Response({"error": "Передача уже обработана."}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
# Перемещение стикеров
|
|
||||||
for item in transfer.items.all():
|
|
||||||
StickerMovement.objects.create(
|
|
||||||
sticker=item.sticker,
|
|
||||||
from_location='склад',
|
|
||||||
to_location='цех',
|
|
||||||
quantity=item.quantity
|
|
||||||
)
|
|
||||||
item.sticker.location = 'цех'
|
|
||||||
item.sticker.save()
|
|
||||||
|
|
||||||
transfer.status = 'accepted'
|
|
||||||
transfer.save()
|
|
||||||
return Response({"status": "Принята"})
|
|
||||||
|
|
||||||
@action(detail=True, methods=['post'])
|
|
||||||
def decline(self, request, pk=None):
|
|
||||||
transfer = get_object_or_404(StickerTransferRequest, pk=pk)
|
|
||||||
|
|
||||||
if transfer.to_user != request.user:
|
|
||||||
return Response({"error": "Нет прав для отклонения этой передачи."}, status=status.HTTP_403_FORBIDDEN)
|
|
||||||
|
|
||||||
if transfer.status != 'pending':
|
|
||||||
return Response({"error": "Передача уже обработана."}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
transfer.status = 'declined'
|
|
||||||
transfer.save()
|
|
||||||
return Response({"status": "Отклонена"})
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
from .models import IssueCategory, Zone, Issue
|
|
||||||
|
|
||||||
@admin.register(IssueCategory)
|
|
||||||
class IssueCategoryAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ("name",)
|
|
||||||
|
|
||||||
@admin.register(Zone)
|
|
||||||
class ZoneAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ("name",)
|
|
||||||
|
|
||||||
@admin.register(Issue)
|
|
||||||
class IssueAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ("category", "zone", "description", "created_at")
|
|
||||||
list_filter = ("category", "zone")
|
|
||||||
search_fields = ("description",)
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class IssuesConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'issues'
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
# Generated by Django 5.1.5 on 2025-03-23 07:18
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='IssueCategory',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=255, unique=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Message',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('identifier', models.CharField(max_length=100, unique=True)),
|
|
||||||
('text', models.TextField()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Nomenclature',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=255)),
|
|
||||||
('short_name', models.CharField(max_length=100)),
|
|
||||||
('gtin', models.CharField(max_length=14, unique=True)),
|
|
||||||
('groupe_water', models.CharField(max_length=255)),
|
|
||||||
('groupe_names', models.CharField(max_length=255)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Zone',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('name', models.CharField(max_length=255, unique=True)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Issue',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('description', models.TextField()),
|
|
||||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
||||||
('updated_at', models.DateTimeField(auto_now=True)),
|
|
||||||
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issues', to='issues.issuecategory')),
|
|
||||||
('zone', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issues', to='issues.zone')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
|
|
||||||
class IssueCategory(models.Model):
|
|
||||||
"""Категории проблем"""
|
|
||||||
name = models.CharField(max_length=255, unique=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Zone(models.Model):
|
|
||||||
"""Зоны, где возникают проблемы"""
|
|
||||||
name = models.CharField(max_length=255, unique=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
class Issue(models.Model):
|
|
||||||
"""Конкретные проблемы"""
|
|
||||||
category = models.ForeignKey(IssueCategory, on_delete=models.CASCADE, related_name="issues")
|
|
||||||
zone = models.ForeignKey(Zone, on_delete=models.CASCADE, related_name="issues")
|
|
||||||
description = models.TextField()
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.category.name} - {self.zone.name}: {self.description[:50]}"
|
|
||||||
|
|
||||||
|
|
||||||
"""GET /api/categories/ – список категорий
|
|
||||||
POST /api/categories/ – создать категорию
|
|
||||||
GET /api/zones/ – список зон
|
|
||||||
POST /api/zones/ – создать зону
|
|
||||||
GET /api/issues/ – список проблем
|
|
||||||
POST /api/issues/ – добавить проблему
|
|
||||||
DELETE /api/issues/<id>/ – удалить проблему"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""Модели справочника номенклатура и сообщения"""
|
|
||||||
class Nomenclature(models.Model):
|
|
||||||
name = models.CharField(max_length=255)
|
|
||||||
short_name = models.CharField(max_length=100)
|
|
||||||
gtin = models.CharField(max_length=14, unique=True)
|
|
||||||
groupe_water = models.CharField(max_length=255)
|
|
||||||
groupe_names = models.CharField(max_length=255)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class Message(models.Model):
|
|
||||||
identifier = models.CharField(max_length=100, unique=True)
|
|
||||||
text = models.TextField()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.identifier}: {self.text[:30]}"
|
|
||||||
|
|
||||||
class Plant(models.Model):
|
|
||||||
"""Справочник заводов"""
|
|
||||||
name = models.CharField(max_length=255, unique=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
|
|
||||||
class ProductionLine(models.Model):
|
|
||||||
"""Справочник производственных линий"""
|
|
||||||
name = models.CharField(max_length=255, unique=True)
|
|
||||||
plant = models.ForeignKey(Plant, on_delete=models.CASCADE, related_name="lines")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.plant.name} - {self.name}"
|
|
||||||
|
|
||||||
|
|
||||||
class ProductionZone(models.Model):
|
|
||||||
"""Справочник производственных зон"""
|
|
||||||
name = models.CharField(max_length=255)
|
|
||||||
line = models.ForeignKey(ProductionLine, on_delete=models.CASCADE, related_name="zones")
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
unique_together = ('name', 'line') # Зона уникальна в пределах линии
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.line.name} - {self.name}"
|
|
||||||
|
|
||||||
|
|
||||||
class DowntimeReason(models.Model):
|
|
||||||
"""Справочник причин простоев"""
|
|
||||||
name = models.CharField(max_length=255, unique=True)
|
|
||||||
description = models.TextField(blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
from rest_framework import serializers
|
|
||||||
from .models import IssueCategory, Zone, Issue, Nomenclature, Message
|
|
||||||
|
|
||||||
class IssueCategorySerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = IssueCategory
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
class ZoneSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Zone
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
class IssueSerializer(serializers.ModelSerializer):
|
|
||||||
category = IssueCategorySerializer(read_only=True)
|
|
||||||
category_id = serializers.PrimaryKeyRelatedField(queryset=IssueCategory.objects.all(), source='category', write_only=True)
|
|
||||||
zone = ZoneSerializer(read_only=True)
|
|
||||||
zone_id = serializers.PrimaryKeyRelatedField(queryset=Zone.objects.all(), source='zone', write_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Issue
|
|
||||||
fields = ['id', 'category', 'category_id', 'zone', 'zone_id', 'description', 'created_at', 'updated_at']
|
|
||||||
|
|
||||||
|
|
||||||
class NomenclatureSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Nomenclature
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
class MessageSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Message
|
|
||||||
fields = '__all__'
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
from django.urls import path, include
|
|
||||||
from rest_framework.routers import DefaultRouter
|
|
||||||
from inventory.views import NomenclatureViewSet
|
|
||||||
from .serializers import NomenclatureSerializer
|
|
||||||
from .views import IssueCategoryViewSet, ZoneViewSet, IssueViewSet, NomenclatureViewSet, MessageViewSet
|
|
||||||
|
|
||||||
router = DefaultRouter()
|
|
||||||
router.register(r'categories', IssueCategoryViewSet)
|
|
||||||
router.register(r'zones', ZoneViewSet)
|
|
||||||
router.register(r'issues', IssueViewSet)
|
|
||||||
router.register(r'nomenclature', NomenclatureViewSet)
|
|
||||||
router.register(r'messages', MessageViewSet)
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('api/', include(router.urls)),
|
|
||||||
]
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
from rest_framework import viewsets
|
|
||||||
from .models import IssueCategory, Zone, Issue
|
|
||||||
from .serializers import IssueCategorySerializer, ZoneSerializer, IssueSerializer
|
|
||||||
|
|
||||||
class IssueCategoryViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = IssueCategory.objects.all()
|
|
||||||
serializer_class = IssueCategorySerializer
|
|
||||||
|
|
||||||
class ZoneViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Zone.objects.all()
|
|
||||||
serializer_class = ZoneSerializer
|
|
||||||
|
|
||||||
class IssueViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Issue.objects.all().order_by('-created_at')
|
|
||||||
serializer_class = IssueSerializer
|
|
||||||
|
|
||||||
|
|
||||||
"""Справочник номенклатуры и сообщений"""
|
|
||||||
from rest_framework import viewsets
|
|
||||||
from .models import Nomenclature, Message
|
|
||||||
from .serializers import NomenclatureSerializer, MessageSerializer
|
|
||||||
|
|
||||||
class NomenclatureViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Nomenclature.objects.all()
|
|
||||||
serializer_class = NomenclatureSerializer
|
|
||||||
|
|
||||||
class MessageViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Message.objects.all()
|
|
||||||
serializer_class = MessageSerializer
|
|
||||||
@ -1,321 +0,0 @@
|
|||||||
import flet as ft
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def main(page: ft.Page):
|
|
||||||
# Состояние формы
|
|
||||||
state = {
|
|
||||||
"current_step": 0,
|
|
||||||
"operators_name": "",
|
|
||||||
"factory": "",
|
|
||||||
"line": "",
|
|
||||||
"error_zone": "",
|
|
||||||
"problems": set(), # будем накапливать выбранные значения
|
|
||||||
"downtime_reasons": set(),
|
|
||||||
"custom_problem": "",
|
|
||||||
"custom_reason": "",
|
|
||||||
"fix_method": "",
|
|
||||||
"start_at": "",
|
|
||||||
"end_at": "",
|
|
||||||
# Зависимые опции:
|
|
||||||
"line_options": [],
|
|
||||||
"zone_options": [],
|
|
||||||
"problem_options": [],
|
|
||||||
"reason_options": [],
|
|
||||||
}
|
|
||||||
|
|
||||||
# Справочники (аналогичные примерам в React/Ionic)
|
|
||||||
factory_line_map = {
|
|
||||||
"Славда": ["ПЭТ", "19л", "5л"],
|
|
||||||
"Скит": ["Sipa", "Devin", "JR", "19 Литров", "5 Литров"]
|
|
||||||
}
|
|
||||||
|
|
||||||
line_zone_map = {
|
|
||||||
"Sipa": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|
||||||
"Devin": ["Сериализация", "Упаковка", "Сборка палеты"],
|
|
||||||
"JR": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|
||||||
"19 Литров": ["Валидация", "Тонкий клиент"],
|
|
||||||
"5 Литров": ["Валидация", "Тонкий клиент"],
|
|
||||||
"ПЭТ": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|
||||||
"5л": ["Сериализация", "Упаковка", "Сборка палеты", "Закрытие палеты"],
|
|
||||||
"19л": ["Валидация", "Тонкий клиент"],
|
|
||||||
}
|
|
||||||
|
|
||||||
common_problems = {
|
|
||||||
"Славда": ["Не печатает код", "Камера не считывает код", "Сбой принтера"],
|
|
||||||
"Скит": ["Не печатает код", "Камера не считывает код", "Сбой принтера"],
|
|
||||||
}
|
|
||||||
|
|
||||||
common_downtime_reasons = {
|
|
||||||
"Славда": ["Сбой камеры", "Ошибка валидации", "Проблема с печатью"],
|
|
||||||
"Скит": ["Сбой камеры", "Ошибка валидации", "Проблема с печатью"],
|
|
||||||
}
|
|
||||||
|
|
||||||
# Элементы UI, которые будут обновляться
|
|
||||||
operators_name_field = ft.TextField(label="Имя оператора *", width=300)
|
|
||||||
factory_dropdown = ft.Dropdown(
|
|
||||||
label="Завод *",
|
|
||||||
options=[
|
|
||||||
ft.dropdown.Option("Славда"),
|
|
||||||
ft.dropdown.Option("Скит")
|
|
||||||
],
|
|
||||||
width=300
|
|
||||||
)
|
|
||||||
line_dropdown = ft.Dropdown(label="Линия *", width=300)
|
|
||||||
zone_dropdown = ft.Dropdown(label="Зона *", width=300)
|
|
||||||
|
|
||||||
fix_method_field = ft.TextField(label="Как решили проблему? *", width=300)
|
|
||||||
start_at_field = ft.TextField(label="Начало простоя (YYYY-MM-DD HH:MM) *", width=300)
|
|
||||||
end_at_field = ft.TextField(label="Окончание простоя (YYYY-MM-DD HH:MM) *", width=300)
|
|
||||||
|
|
||||||
custom_problem_field = ft.TextField(label="Своя проблема (необязательно)", width=300)
|
|
||||||
custom_reason_field = ft.TextField(label="Своя причина (необязательно)", width=300)
|
|
||||||
|
|
||||||
# Для выбора нескольких вариантов – используем колонки с чекбоксами
|
|
||||||
problems_checkboxes = ft.Column()
|
|
||||||
reasons_checkboxes = ft.Column()
|
|
||||||
|
|
||||||
# Функция для показа уведомлений (SnackBar)
|
|
||||||
def show_snackbar(message, bgcolor):
|
|
||||||
page.snack_bar = ft.SnackBar(ft.Text(message), bgcolor=bgcolor)
|
|
||||||
page.snack_bar.open = True
|
|
||||||
page.update()
|
|
||||||
|
|
||||||
# Обновление зависимых списков при выборе завода
|
|
||||||
def update_factory_dependent():
|
|
||||||
factory = state["factory"]
|
|
||||||
state["line_options"] = factory_line_map.get(factory, [])
|
|
||||||
line_dropdown.options = [ft.dropdown.Option(opt) for opt in state["line_options"]]
|
|
||||||
line_dropdown.value = None
|
|
||||||
state["line"] = ""
|
|
||||||
|
|
||||||
state["problem_options"] = common_problems.get(factory, [])
|
|
||||||
state["reason_options"] = common_downtime_reasons.get(factory, [])
|
|
||||||
update_problems_checkboxes()
|
|
||||||
update_reasons_checkboxes()
|
|
||||||
page.update()
|
|
||||||
|
|
||||||
# Обновление списка зон при выборе линии
|
|
||||||
def update_line_dependent():
|
|
||||||
line = state["line"]
|
|
||||||
state["zone_options"] = line_zone_map.get(line, [])
|
|
||||||
zone_dropdown.options = [ft.dropdown.Option(opt) for opt in state["zone_options"]]
|
|
||||||
zone_dropdown.value = None
|
|
||||||
state["error_zone"] = ""
|
|
||||||
page.update()
|
|
||||||
|
|
||||||
def update_problems_checkboxes():
|
|
||||||
problems_checkboxes.controls.clear()
|
|
||||||
for prob in state["problem_options"]:
|
|
||||||
chk = ft.Checkbox(label=prob, value=False)
|
|
||||||
def on_change(e, label=prob):
|
|
||||||
if e.control.value:
|
|
||||||
state["problems"].add(label)
|
|
||||||
else:
|
|
||||||
state["problems"].discard(label)
|
|
||||||
chk.on_change = on_change
|
|
||||||
problems_checkboxes.controls.append(chk)
|
|
||||||
page.update()
|
|
||||||
|
|
||||||
def update_reasons_checkboxes():
|
|
||||||
reasons_checkboxes.controls.clear()
|
|
||||||
for reason in state["reason_options"]:
|
|
||||||
chk = ft.Checkbox(label=reason, value=False)
|
|
||||||
def on_change(e, label=reason):
|
|
||||||
if e.control.value:
|
|
||||||
state["downtime_reasons"].add(label)
|
|
||||||
else:
|
|
||||||
state["downtime_reasons"].discard(label)
|
|
||||||
chk.on_change = on_change
|
|
||||||
reasons_checkboxes.controls.append(chk)
|
|
||||||
page.update()
|
|
||||||
|
|
||||||
# Функция валидации этапов
|
|
||||||
def validate_step():
|
|
||||||
step = state["current_step"]
|
|
||||||
if step == 0:
|
|
||||||
if not operators_name_field.value.strip() or not factory_dropdown.value:
|
|
||||||
show_snackbar("Введите имя и выберите завод", ft.colors.RED)
|
|
||||||
return False
|
|
||||||
elif step == 1:
|
|
||||||
if not line_dropdown.value or not zone_dropdown.value:
|
|
||||||
show_snackbar("Выберите линию и зону", ft.colors.RED)
|
|
||||||
return False
|
|
||||||
elif step == 2:
|
|
||||||
total_problems = set(state["problems"])
|
|
||||||
if custom_problem_field.value and custom_problem_field.value.strip():
|
|
||||||
total_problems.add(custom_problem_field.value.strip())
|
|
||||||
total_reasons = set(state["downtime_reasons"])
|
|
||||||
if custom_reason_field.value and custom_reason_field.value.strip():
|
|
||||||
total_reasons.add(custom_reason_field.value.strip())
|
|
||||||
if not total_problems:
|
|
||||||
show_snackbar("Укажите хотя бы одну проблему", ft.colors.RED)
|
|
||||||
return False
|
|
||||||
if not total_reasons:
|
|
||||||
show_snackbar("Укажите хотя бы одну причину простоя", ft.colors.RED)
|
|
||||||
return False
|
|
||||||
elif step == 3:
|
|
||||||
if not fix_method_field.value.strip():
|
|
||||||
show_snackbar("Опишите, как решили проблему", ft.colors.RED)
|
|
||||||
return False
|
|
||||||
if not start_at_field.value.strip() or not end_at_field.value.strip():
|
|
||||||
show_snackbar("Укажите время начала и окончания", ft.colors.RED)
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
datetime.strptime(start_at_field.value.strip(), "%Y-%m-%d %H:%M")
|
|
||||||
datetime.strptime(end_at_field.value.strip(), "%Y-%m-%d %H:%M")
|
|
||||||
except ValueError:
|
|
||||||
show_snackbar("Неверный формат даты/времени", ft.colors.RED)
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Контейнер для отображения содержимого этапа
|
|
||||||
content_column = ft.Column()
|
|
||||||
# Прогресс-бар (значение от 0 до 1)
|
|
||||||
progress_bar = ft.ProgressBar(value=(state["current_step"]+1)/4)
|
|
||||||
|
|
||||||
# Кнопки навигации
|
|
||||||
next_button = ft.ElevatedButton(text="Далее")
|
|
||||||
back_button = ft.ElevatedButton(text="Назад")
|
|
||||||
submit_button = ft.ElevatedButton(text="Отправить")
|
|
||||||
|
|
||||||
# Обновление UI в зависимости от этапа
|
|
||||||
def update_ui():
|
|
||||||
content_column.controls.clear()
|
|
||||||
progress_bar.value = (state["current_step"]+1)/4
|
|
||||||
|
|
||||||
if state["current_step"] == 0:
|
|
||||||
content_column.controls.extend([
|
|
||||||
operators_name_field,
|
|
||||||
factory_dropdown,
|
|
||||||
])
|
|
||||||
elif state["current_step"] == 1:
|
|
||||||
content_column.controls.extend([
|
|
||||||
line_dropdown,
|
|
||||||
zone_dropdown,
|
|
||||||
])
|
|
||||||
elif state["current_step"] == 2:
|
|
||||||
content_column.controls.extend([
|
|
||||||
ft.Text("Проблемы (можно выбрать несколько):"),
|
|
||||||
problems_checkboxes,
|
|
||||||
custom_problem_field,
|
|
||||||
ft.Text("Причины простоя (можно выбрать несколько):"),
|
|
||||||
reasons_checkboxes,
|
|
||||||
custom_reason_field,
|
|
||||||
])
|
|
||||||
elif state["current_step"] == 3:
|
|
||||||
content_column.controls.extend([
|
|
||||||
fix_method_field,
|
|
||||||
start_at_field,
|
|
||||||
end_at_field,
|
|
||||||
])
|
|
||||||
|
|
||||||
# Кнопки навигации
|
|
||||||
nav_buttons = []
|
|
||||||
if state["current_step"] > 0:
|
|
||||||
nav_buttons.append(back_button)
|
|
||||||
if state["current_step"] < 3:
|
|
||||||
nav_buttons.append(next_button)
|
|
||||||
if state["current_step"] == 3:
|
|
||||||
nav_buttons.append(submit_button)
|
|
||||||
content_column.controls.append(ft.Row(controls=nav_buttons, spacing=20))
|
|
||||||
page.update()
|
|
||||||
|
|
||||||
# Обработчики кнопок
|
|
||||||
def on_next(e):
|
|
||||||
if validate_step():
|
|
||||||
if state["current_step"] == 0:
|
|
||||||
state["operators_name"] = operators_name_field.value
|
|
||||||
state["factory"] = factory_dropdown.value
|
|
||||||
update_factory_dependent()
|
|
||||||
elif state["current_step"] == 1:
|
|
||||||
state["line"] = line_dropdown.value
|
|
||||||
state["error_zone"] = zone_dropdown.value
|
|
||||||
state["current_step"] += 1
|
|
||||||
update_ui()
|
|
||||||
|
|
||||||
def on_back(e):
|
|
||||||
state["current_step"] -= 1
|
|
||||||
update_ui()
|
|
||||||
|
|
||||||
def on_submit(e):
|
|
||||||
if not validate_step():
|
|
||||||
return
|
|
||||||
state["fix_method"] = fix_method_field.value
|
|
||||||
state["start_at"] = start_at_field.value
|
|
||||||
state["end_at"] = end_at_field.value
|
|
||||||
|
|
||||||
total_problems = list(state["problems"])
|
|
||||||
if custom_problem_field.value and custom_problem_field.value.strip():
|
|
||||||
total_problems.append(custom_problem_field.value.strip())
|
|
||||||
total_reasons = list(state["downtime_reasons"])
|
|
||||||
if custom_reason_field.value and custom_reason_field.value.strip():
|
|
||||||
total_reasons.append(custom_reason_field.value.strip())
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"factory": state["factory"],
|
|
||||||
"line": state["line"],
|
|
||||||
"operators_name": state["operators_name"],
|
|
||||||
"error_zone": state["error_zone"],
|
|
||||||
"problem": ", ".join(total_problems),
|
|
||||||
"downtime_reason": ", ".join(total_reasons),
|
|
||||||
"fix_method": state["fix_method"],
|
|
||||||
"start_at": state["start_at"],
|
|
||||||
"end_at": state["end_at"],
|
|
||||||
}
|
|
||||||
print("[DEBUG] Отправляем JSON:", payload)
|
|
||||||
show_snackbar("Данные успешно отправлены!", ft.colors.GREEN)
|
|
||||||
# Сброс формы
|
|
||||||
state.update({
|
|
||||||
"current_step": 0,
|
|
||||||
"operators_name": "",
|
|
||||||
"factory": "",
|
|
||||||
"line": "",
|
|
||||||
"error_zone": "",
|
|
||||||
"problems": set(),
|
|
||||||
"downtime_reasons": set(),
|
|
||||||
"custom_problem": "",
|
|
||||||
"custom_reason": "",
|
|
||||||
"fix_method": "",
|
|
||||||
"start_at": "",
|
|
||||||
"end_at": "",
|
|
||||||
"line_options": [],
|
|
||||||
"zone_options": [],
|
|
||||||
"problem_options": [],
|
|
||||||
"reason_options": [],
|
|
||||||
})
|
|
||||||
operators_name_field.value = ""
|
|
||||||
factory_dropdown.value = None
|
|
||||||
line_dropdown.value = None
|
|
||||||
zone_dropdown.value = None
|
|
||||||
fix_method_field.value = ""
|
|
||||||
start_at_field.value = ""
|
|
||||||
end_at_field.value = ""
|
|
||||||
custom_problem_field.value = ""
|
|
||||||
custom_reason_field.value = ""
|
|
||||||
problems_checkboxes.controls.clear()
|
|
||||||
reasons_checkboxes.controls.clear()
|
|
||||||
update_ui()
|
|
||||||
|
|
||||||
next_button.on_click = on_next
|
|
||||||
back_button.on_click = on_back
|
|
||||||
submit_button.on_click = on_submit
|
|
||||||
|
|
||||||
def on_factory_change(e):
|
|
||||||
state["factory"] = factory_dropdown.value
|
|
||||||
update_factory_dependent()
|
|
||||||
factory_dropdown.on_change = on_factory_change
|
|
||||||
|
|
||||||
def on_line_change(e):
|
|
||||||
state["line"] = line_dropdown.value
|
|
||||||
update_line_dependent()
|
|
||||||
line_dropdown.on_change = on_line_change
|
|
||||||
|
|
||||||
def on_zone_change(e):
|
|
||||||
state["error_zone"] = zone_dropdown.value
|
|
||||||
zone_dropdown.on_change = on_zone_change
|
|
||||||
|
|
||||||
page.title = "Журнал простоев (мобильная версия) - Flet"
|
|
||||||
page.add(progress_bar, content_column)
|
|
||||||
update_ui()
|
|
||||||
|
|
||||||
ft.app(target=main)
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.contrib import admin
|
|
||||||
|
|
||||||
# Register your models here.
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class ProductionConfig(AppConfig):
|
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
|
||||||
name = 'production'
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 5.1.5 on 2025-03-21 23:48
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='ProductionPlan',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('date', models.DateField(verbose_name='Дата производства')),
|
|
||||||
('line', models.CharField(max_length=100, verbose_name='Линия')),
|
|
||||||
('product', models.CharField(max_length=100, verbose_name='Продукция')),
|
|
||||||
('production', models.PositiveIntegerField(verbose_name='Плановый выпуск продукции')),
|
|
||||||
('shift', models.CharField(max_length=50, verbose_name='Смена')),
|
|
||||||
('employee', models.CharField(max_length=100, verbose_name='Сотрудник')),
|
|
||||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создано')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
from django.db import models
|
|
||||||
|
|
||||||
class ProductionPlan(models.Model):
|
|
||||||
date = models.DateField(verbose_name="Дата производства")
|
|
||||||
line = models.CharField(max_length=100, verbose_name="Линия")
|
|
||||||
product = models.CharField(max_length=100, verbose_name="Продукция")
|
|
||||||
production = models.PositiveIntegerField(verbose_name="Плановый выпуск продукции")
|
|
||||||
shift = models.CharField(max_length=50, verbose_name="Смена")
|
|
||||||
employee = models.CharField(max_length=100, verbose_name="Сотрудник")
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создано")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.date} - {self.line} - {self.product}"
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from rest_framework import serializers
|
|
||||||
from .models import ProductionPlan
|
|
||||||
|
|
||||||
class ProductionPlanSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = ProductionPlan
|
|
||||||
fields = '__all__'
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
from django.urls import path, include
|
|
||||||
from rest_framework.routers import DefaultRouter
|
|
||||||
from .views import ProductionPlanViewSet
|
|
||||||
|
|
||||||
router = DefaultRouter()
|
|
||||||
router.register(r'production', ProductionPlanViewSet, basename='production')
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('calendar/', include(router.urls)),
|
|
||||||
]
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from rest_framework import viewsets
|
|
||||||
from .models import ProductionPlan
|
|
||||||
from .serializers import ProductionPlanSerializer
|
|
||||||
|
|
||||||
class ProductionPlanViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = ProductionPlan.objects.all().order_by('-created_at')
|
|
||||||
serializer_class = ProductionPlanSerializer
|
|
||||||
BIN
scan/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
scan/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scan/__pycache__/admin.cpython-312.pyc
Normal file
BIN
scan/__pycache__/admin.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scan/__pycache__/apps.cpython-312.pyc
Normal file
BIN
scan/__pycache__/apps.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scan/__pycache__/models.cpython-312.pyc
Normal file
BIN
scan/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scan/__pycache__/serializers.cpython-312.pyc
Normal file
BIN
scan/__pycache__/serializers.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scan/__pycache__/urls.cpython-312.pyc
Normal file
BIN
scan/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scan/__pycache__/views.cpython-312.pyc
Normal file
BIN
scan/__pycache__/views.cpython-312.pyc
Normal file
Binary file not shown.
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.1.5 on 2025-03-21 23:48
|
# Generated by Django 5.1.5 on 2025-03-06 04:31
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|||||||
BIN
scan/migrations/__pycache__/0001_initial.cpython-312.pyc
Normal file
BIN
scan/migrations/__pycache__/0001_initial.cpython-312.pyc
Normal file
Binary file not shown.
BIN
scan/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
scan/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
users/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/__pycache__/admin.cpython-312.pyc
Normal file
BIN
users/__pycache__/admin.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/__pycache__/apps.cpython-312.pyc
Normal file
BIN
users/__pycache__/apps.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/__pycache__/models.cpython-312.pyc
Normal file
BIN
users/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/__pycache__/serializers.cpython-312.pyc
Normal file
BIN
users/__pycache__/serializers.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/__pycache__/urls.cpython-312.pyc
Normal file
BIN
users/__pycache__/urls.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/__pycache__/views.cpython-312.pyc
Normal file
BIN
users/__pycache__/views.cpython-312.pyc
Normal file
Binary file not shown.
BIN
users/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
users/migrations/__pycache__/__init__.cpython-312.pyc
Normal file
Binary file not shown.
@ -5,12 +5,8 @@ from rest_framework_simplejwt.tokens import RefreshToken
|
|||||||
|
|
||||||
class UserLoginSerializer(serializers.Serializer):
|
class UserLoginSerializer(serializers.Serializer):
|
||||||
username = serializers.CharField()
|
username = serializers.CharField()
|
||||||
profile_name = serializers.SerializerMethodField() # Добавляем поле для профиля
|
|
||||||
password = serializers.CharField(write_only=True)
|
password = serializers.CharField(write_only=True)
|
||||||
|
|
||||||
def get_profile_name(self, user):
|
|
||||||
return f"{user.first_name} {user.last_name}".strip() or user.username # Если имя пустое, используем username
|
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
|
|
||||||
@ -22,7 +18,6 @@ class UserLoginSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"username": user.username,
|
"username": user.username,
|
||||||
"profile": self.get_profile_name(user), # Возвращаем профильное имя
|
|
||||||
"access": str(refresh.access_token),
|
"access": str(refresh.access_token),
|
||||||
"refresh": str(refresh)
|
"refresh": str(refresh)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user