Compare commits

..

2 Commits

Author SHA1 Message Date
ack_ik
a22c9d9d82 Merge branch 'main' of https://gitea.tertelius.space/ack/ProdManager1 2026-02-10 13:43:11 +03:00
ack_ik
b27c685eb1 Первая попытка модельки 2026-02-10 13:40:12 +03:00
10 changed files with 241 additions and 1 deletions

33
.ignore/filters.py Normal file
View File

@@ -0,0 +1,33 @@
from django_filters import FilterSet, CharFilter, ModelChoiceFilter
from .models import Part
class PartFilter(FilterSet):
type = ModelChoiceFilter(
field_name='type',
choices=Part.TYPE_CHOICES,
label='Тип заготовки'
)
thickness_min = CharFilter(
field_name='thickness',
label='Минимальная толщина',
widget=forms.NumberInput(attrs={'placeholder': 'От'})
)
thickness_max = CharFilter(
field_name='thickness',
label='Максимальная толщина',
widget=forms.NumberInput(attrs={'placeholder': 'До'})
)
search = CharFilter(
field_name='name',
label='Поиск по наименованию',
widget=forms.TextInput(attrs={'placeholder': 'Введите текст'})
)
decimal_number = CharFilter(
field_name='decimal_number',
label='Поиск по децимальному номеру',
widget=forms.TextInput(attrs={'placeholder': 'Введите номер'})
)
class Meta:
model = Part
fields = ['type', 'thickness_min', 'thickness_max', 'search', 'decimal_number']

39
.ignore/models.py Normal file
View File

@@ -0,0 +1,39 @@
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
from django.utils.text import slugify
TYPE_CHOICES = (
('Лист', 'Лист'),
('Труба', 'Труба'),
('Круг', 'Круг'),
('Уголок', 'Уголок'),
('Профиль', 'Профиль'),
('Плита', 'Плита'),
('Швеллер', 'Швеллер'),
('Ребро', 'Ребро'),
('Тонкий', 'Тонкий'),
('Толстый', 'Толстый'),
)
class Part(models.Model):
name = models.CharField(max_length=255)
decimal_number = models.CharField(max_length=50, unique=True)
type = models.CharField(max_length=50, choices=TYPE_CHOICES)
thickness = models.FloatField(null=True, blank=True)
length = models.FloatField(null=True, blank=True)
weight = models.FloatField(null=True, blank=True)
cut_length = models.FloatField(null=True, blank=True)
number_of_punches = models.IntegerField(null=True, blank=True)
slug = models.SlugField(unique=True, max_length=255)
def save(self, *args, **kwargs):
self.slug = slugify(f"{self.name}-{self.decimal_number}")
super().save(*args, **kwargs)
def __str__(self):
return f"{self.name} ({self.decimal_number})"
class ProductStructure(MPTTModel):
item = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='structures')
quantity = models.FloatField()
parent = TreeForeignKey('self', on_delete=models.CASCADE)

8
.ignore/urls.py Normal file
View File

@@ -0,0 +1,8 @@
from django.contrib import admin
from django.urls import path
from .views import PartList
urlpatterns = [
path('admin/', admin.site.urls),
path('api/parts/', PartList.as_view(), name='part-list'),
]

58
.ignore/views.py Normal file
View File

@@ -0,0 +1,58 @@
from django.shortcuts import render
from django.views import View
from django.http import JsonResponse
from .models import Part, ProductStructure
from .filters import PartFilter
from django.contrib import admin
from django.db import models
from django.forms import ModelForm
from django.utils.html import format_html
class PartList(View):
def get(self, request):
filter = PartFilter(request.GET, queryset=Part.objects.all())
return JsonResponse({
'results': [self.part_to_json(part) for part in filter.qs],
'filters': filter.filters.items()
})
def part_to_json(self, part):
return {
'id': part.id,
'name': part.name,
'decimal_number': part.decimal_number,
'type': part.get_type_display(),
'thickness': part.thickness,
'length': part.length,
'weight': part.weight,
'cut_length': part.cut_length,
'number_of_punches': part.number_of_punches,
'slug': part.slug
}
class PartAdmin(admin.ModelAdmin):
list_display = ('name', 'decimal_number', 'type', 'thickness', 'weight')
search_fields = ('name', 'decimal_number')
list_filter = ('type',)
inlines = [
ProductionOperationInline
]
class ProductionOperationInline(admin.TabularInline):
model = ProductionOperation
extra = 1
class ProductionOperation(models.Model):
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='operations')
operation_type = models.CharField(max_length=50, choices=[
('Лазер', 'Лазер'),
('Сварка', 'Сварка'),
('Покраска', 'Покраска'),
('Обработка', 'Обработка'),
])
time = models.FloatField()
description = models.TextField(blank=True)
def __str__(self):
return f"{self.part.name} - {self.operation_type}"

View File

@@ -1,12 +1,13 @@
Задача: Создать Django-проект для учета состава изделий (BOM) с иерархией, техпроцессами и системой фильтрации.
Технологии: Django 5.x, PostgreSQL, django-mptt, django-filter, Bootstrap 5.
игнорируй папки .ignore
1. Модель данных:
Part (Номенклатура):
Поля: Наименование, Децимальный номер, Тип заготовки (Choices: Лист, Труба, Круг, Уголок и др.), Толщина, Длина, Вес, Длина реза, Число проколов.
Поля: Децимальный номер, Наименование, Тип заготовки (Choices: Лист, Труба, Круг, Уголок и др.), Толщина, Длина, Вес, Длина реза, Число проколов.
ProductStructure (Дерево состава): * MPTTModel. Поля: parent, item (FK на Part), quantity.
@@ -35,6 +36,9 @@ FK на Part. Поля: Тип операции (Choices: Лазер, Сварк
Админка: Настроить TabularInline для ProductionOperation внутри PartAdmin.
4. Инструкции по коду:
создай проект с настройками в папке core
Создай приложение Prodman
Напиши models.py, filters.py, views.py и urls.py.

5
requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
Django>=5.0
django-mptt
django-filter
psycopg2-binary
bootstrap-icons

26
templates/_navbar.html Normal file
View File

@@ -0,0 +1,26 @@
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'part-list' %}">BOM Manager</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="{% url 'part-list' %}">Список деталей</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Структура изделия</a>
</li>
</ul>
<div class="d-flex">
<a href="{% url 'admin:index' %}" class="btn btn-outline-secondary me-2">
<i class="fas fa-tools"></i> Админка
</a>
<a href="#" class="btn btn-outline-secondary">
<i class="fas fa-sign-out-alt"></i> Выйти
</a>
</div>
</div>
</div>
</nav>

22
templates/base.html Normal file
View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{% endblock %}</title>
<!-- Bootstrap 5 CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- FontAwesome CDN -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
{% include '_navbar.html' %}
</head>
<body>
<div class="container mt-4">
{% block content %}
{% endblock %}
</div>
{% include '_footer.html' %}
<!-- Bootstrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

45
templates/parts_list.html Normal file
View File

@@ -0,0 +1,45 @@
{% extends "base.html" %}
{% block title %}Список деталей{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-3">
<h4>Фильтры</h4>
<form method="get">
{{ filter.form.as_p }}
<button type="submit" class="btn btn-primary">Применить</button>
</form>
</div>
<div class="col-md-9">
<h4>Результаты</h4>
<table class="table table-striped">
<thead>
<tr>
<th>Наименование</th>
<th>Децимальный номер</th>
<th>Тип</th>
<th>Толщина</th>
<th>Длина</th>
<th>Вес</th>
<th>Длина реза</th>
<th>Число проколов</th>
</tr>
</thead>
<tbody>
{% for part in filter.qs %}
<tr>
<td>{{ part.name }}</td>
<td>{{ part.decimal_number }}</td>
<td>{{ part.get_type_display }}</td>
<td>{{ part.thickness }}</td>
<td>{{ part.length }}</td>
<td>{{ part.weight }}</td>
<td>{{ part.cut_length }}</td>
<td>{{ part.number_of_punches }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}