diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..642259b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Django: Runserver", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/manage.py", + "args": [ + "runserver" + ], + "django": true, + "justMyCode": true, + // Это заставит сервер перезапускаться при изменении кода + "autoReload": { + "enable": true + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 7e68766..20fa176 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,43 @@ { - "python-envs.pythonProjects": [] + // --- ПИТОН И АВТОМАТИКА --- + "python.analysis.typeCheckingMode": "basic", // Подсказки по типам данных + "editor.formatOnSave": true, // Форматировать код при сохранении (маст-хэв!) + "python.formatting.provider": "black", // Использовать Black для красоты кода + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" // Сам расставит импорты по алфавиту + }, + + // --- DJANGO И HTML --- + "files.associations": { + "**/*.html": "django-html", // Чтобы VS Code понимал синтаксис {% if %} + "**/templates/**/*.html": "django-html" + }, + "emmet.includeLanguages": { + "django-html": "html" // Чтобы Emmet (развертывание тегов через Tab) работал в шаблонах + }, + + // --- ИНТЕРФЕЙС И КОМФОРТ --- + "editor.bracketPairColorization.enabled": true, // Цветные скобочки (чтобы не путаться в вложенности) + "editor.guide.bracketPairs": "active", + "editor.fontSize": 14, // Подбери под свои глаза + "editor.tabSize": 4, + "editor.renderWhitespace": "boundary", // Видеть лишние пробелы в конце строк + "files.autoSave": "onFocusChange", // Сохранять файл, когда переключаешься в браузер + + // --- ЧИСТОТА В ПРОЕКТЕ --- + "files.exclude": { + "**/.git": true, + "**/__pycache__": true, // Прячем мусорные папки питона + "**/*.pyc": true, + "**/node_modules": true, + "**/.DS_Store": true + }, + + // --- ТЕРМИНАЛ --- + "terminal.integrated.fontSize": 13, + "terminal.integrated.cursorStyle": "line", + + // --- ГИТ --- + "git.autofetch": true, // Проверять обновления в репозитории самостоятльно + "git.confirmSync": false } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3128fff..5c9eb9d 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/shiftflow/forms.py b/shiftflow/forms.py index 851d6cd..aed8d7d 100644 --- a/shiftflow/forms.py +++ b/shiftflow/forms.py @@ -26,4 +26,26 @@ class ProductionTaskCreateForm(forms.Form): queryset=Material.objects.all().order_by("full_name"), required=True, empty_label="— выбрать —", - ) \ No newline at end of file + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + existing = self.fields["drawing_name"].widget.attrs.get("class", "") + self.fields["drawing_name"].widget.attrs["class"] = (existing + " w-100").strip() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Приводим поля формы к единому Bootstrap-оформлению. + # Это решает проблему, когда input «Наименование» выглядит как стандартный HTML и не занимает всю ширину. + for name, field in self.fields.items(): + w = field.widget + if isinstance(w, forms.CheckboxInput): + w.attrs.setdefault('class', 'form-check-input') + elif isinstance(w, (forms.Select, forms.SelectMultiple)): + w.attrs.setdefault('class', 'form-select border-secondary') + else: + w.attrs.setdefault('class', 'form-control border-secondary') + + # Явно делаем поле «Наименование детали» растягиваемым на всю ширину. + self.fields['drawing_name'].widget.attrs['class'] += ' w-100' \ No newline at end of file diff --git a/shiftflow/migrations/0010_productiontask_preview_image.py b/shiftflow/migrations/0010_productiontask_preview_image.py new file mode 100644 index 0000000..fe09ba5 --- /dev/null +++ b/shiftflow/migrations/0010_productiontask_preview_image.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.3 on 2026-04-02 19:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shiftflow', '0009_deal_status'), + ] + + operations = [ + migrations.AddField( + model_name='productiontask', + name='preview_image', + field=models.ImageField(blank=True, null=True, upload_to='task_previews/%Y/%m/', verbose_name='Превью DXF (PNG)'), + ), + ] diff --git a/shiftflow/migrations/0011_productiontask_blank_dimensions.py b/shiftflow/migrations/0011_productiontask_blank_dimensions.py new file mode 100644 index 0000000..9c6d0e3 --- /dev/null +++ b/shiftflow/migrations/0011_productiontask_blank_dimensions.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.3 on 2026-04-02 20:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shiftflow', '0010_productiontask_preview_image'), + ] + + operations = [ + migrations.AddField( + model_name='productiontask', + name='blank_dimensions', + field=models.CharField(blank=True, default='', max_length=64, verbose_name='Габариты заготовки'), + ), + ] diff --git a/shiftflow/migrations/0012_dxfpreviewsettings.py b/shiftflow/migrations/0012_dxfpreviewsettings.py new file mode 100644 index 0000000..aaa1fbb --- /dev/null +++ b/shiftflow/migrations/0012_dxfpreviewsettings.py @@ -0,0 +1,28 @@ +# Generated by Django 6.0.3 on 2026-04-02 20:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shiftflow', '0011_productiontask_blank_dimensions'), + ] + + operations = [ + migrations.CreateModel( + name='DxfPreviewSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('line_color', models.CharField(default='#006400', help_text='Напр: #006400 (тёмно-зелёный)', max_length=16, verbose_name='Цвет линий превью (HEX)')), + ('lineweight_scaling', models.FloatField(default=1.0, help_text='1.0 = как в DXF, 2.0 = толще, 0.5 = тоньше', verbose_name='Коэффициент толщины линий')), + ('min_lineweight', models.FloatField(default=0.1, help_text='Если в DXF нет lineweight — используем минимум, чтобы линии были видимы', verbose_name='Минимальная толщина (мм)')), + ('keep_original_colors', models.BooleanField(default=False, help_text='Если включено — не перекрашиваем линии, берём цвета из DXF', verbose_name='Оставить цвета оригинальные')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлено')), + ], + options={ + 'verbose_name': 'Настройки превью DXF', + 'verbose_name_plural': 'Настройки превью DXF', + }, + ), + ] diff --git a/shiftflow/models.py b/shiftflow/models.py index 72ee6cf..30ad510 100644 --- a/shiftflow/models.py +++ b/shiftflow/models.py @@ -64,13 +64,15 @@ class ProductionTask(models.Model): Создается технологом или мастером на основе заказа. """ deal = models.ForeignKey(Deal, on_delete=models.CASCADE, verbose_name="Сделка") - + drawing_name = models.CharField("Название детали", max_length=255, blank=True, default="Б/ч") size_value = models.FloatField("Размер детали", help_text="Длина (мм) или Толщина (мм)") - + drawing_file = models.FileField("Исходник (DXF/IGES)", upload_to="drawings/%Y/%m/", blank=True, null=True) extra_drawing = models.FileField("Доп. чертеж (PDF)", upload_to="extra_drawings/%Y/%m/", blank=True, null=True) - + preview_image = models.ImageField("Превью DXF (PNG)", upload_to="task_previews/%Y/%m/", blank=True, null=True) + blank_dimensions = models.CharField("Габариты заготовки", max_length=64, blank=True, default="") + material = models.ForeignKey(WarehouseMaterial, on_delete=models.PROTECT, verbose_name="Материал") quantity_ordered = models.PositiveIntegerField("Заказано всего, шт") is_bend = models.BooleanField("Гибка", default=False) @@ -84,6 +86,47 @@ class ProductionTask(models.Model): def __str__(self): return f"{self.drawing_name} (Заказ {self.deal.number})" +class DxfPreviewSettings(models.Model): + """Настройки генерации превью для DXF. + + Храним в БД, чтобы админ мог менять параметры через страницу «Обслуживание сервера» + без правок кода. + + Сделано как singleton: ожидается одна строка (обычно pk=1). + """ + + line_color = models.CharField( + "Цвет линий превью (HEX)", + max_length=16, + default="#006400", + help_text="Напр: #006400 (тёмно-зелёный)", + ) + lineweight_scaling = models.FloatField( + "Коэффициент толщины линий", + default=1.0, + help_text="1.0 = как в DXF, 2.0 = толще, 0.5 = тоньше", + ) + min_lineweight = models.FloatField( + "Минимальная толщина (мм)", + default=0.1, + help_text="Если в DXF нет lineweight — используем минимум, чтобы линии были видимы", + ) + keep_original_colors = models.BooleanField( + "Оставить цвета оригинальные", + default=False, + help_text="Если включено — не перекрашиваем линии, берём цвета из DXF", + ) + + updated_at = models.DateTimeField("Обновлено", auto_now=True) + + class Meta: + verbose_name = "Настройки превью DXF" + verbose_name_plural = "Настройки превью DXF" + + def __str__(self): + return "Настройки превью DXF" + + class Item(models.Model): """ Единица сменного задания. Определяет КТО, КОГДА и СКОЛЬКО сделал. diff --git a/shiftflow/templates/shiftflow/item_detail.html b/shiftflow/templates/shiftflow/item_detail.html index bb1d5d6..43c744f 100644 --- a/shiftflow/templates/shiftflow/item_detail.html +++ b/shiftflow/templates/shiftflow/item_detail.html @@ -25,7 +25,7 @@ {% endif %} -