diff --git a/assets/pasted-20260127-200020-e0bae63b.png b/assets/pasted-20260127-200020-e0bae63b.png new file mode 100644 index 0000000..1c8cf34 Binary files /dev/null and b/assets/pasted-20260127-200020-e0bae63b.png differ diff --git a/assets/pasted-20260127-200500-2ad7f024.png b/assets/pasted-20260127-200500-2ad7f024.png new file mode 100644 index 0000000..27998c4 Binary files /dev/null and b/assets/pasted-20260127-200500-2ad7f024.png differ diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 96bce55..850453c 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 0b85e94..9d7c63d 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 291d043..cb8d3b1 100644 --- a/config/settings.py +++ b/config/settings.py @@ -149,6 +149,9 @@ STATIC_URL = 'static/' # Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS. STATIC_ROOT = BASE_DIR / 'staticfiles' +MEDIA_URL = 'media/' +MEDIA_ROOT = BASE_DIR / 'media' + STATICFILES_DIRS = [ BASE_DIR / 'static', BASE_DIR / 'assets', diff --git a/config/urls.py b/config/urls.py index bcfc074..1a48858 100644 --- a/config/urls.py +++ b/config/urls.py @@ -27,3 +27,4 @@ urlpatterns = [ if settings.DEBUG: urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets") urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index edb7428..31f12a7 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 91a030d..1ab4b3e 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 7d8dc91..ec14df2 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index b0cfab8..f599451 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index a0a056c..e5cabca 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,15 +1,36 @@ from django.contrib import admin -from .models import Category, FleetUnit, Maintenance, Breakdown, PartRequest, Document +from .models import Category, FleetUnit, Maintenance, Breakdown, PartRequest, Document, Supplier @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ('name',) +@admin.register(Supplier) +class SupplierAdmin(admin.ModelAdmin): + list_display = ('name', 'representative_name', 'phone', 'email', 'contract_number') + search_fields = ('name', 'representative_name', 'contract_number') + @admin.register(FleetUnit) class FleetUnitAdmin(admin.ModelAdmin): list_display = ('name', 'category', 'plate_number', 'status', 'year') list_filter = ('status', 'category') search_fields = ('name', 'vin', 'plate_number') + + fieldsets = ( + ('Основная информация', { + 'fields': ('name', 'category', 'model_name', 'vin', 'plate_number', 'year', 'photo', 'status', 'commissioning_date', 'notes') + }), + ('Страховка', { + 'fields': ('insurance_company', 'insurance_policy_number', 'insurance_start_date', 'insurance_end_date') + }), + ('Снабжение и Поставщик', { + 'fields': ('supplier', 'vehicle_documents', 'supplier_name', 'supplier_contacts') + }), + ('QR-код', { + 'fields': ('qr_code',), + 'classes': ('collapse',) + }), + ) @admin.register(Maintenance) class MaintenanceAdmin(admin.ModelAdmin): diff --git a/core/migrations/0004_fleetunit_insurance_company_and_more.py b/core/migrations/0004_fleetunit_insurance_company_and_more.py new file mode 100644 index 0000000..ab01502 --- /dev/null +++ b/core/migrations/0004_fleetunit_insurance_company_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.2.7 on 2026-01-27 20:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_setup_groups'), + ] + + operations = [ + migrations.AddField( + model_name='fleetunit', + name='insurance_company', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Страховая компания'), + ), + migrations.AddField( + model_name='fleetunit', + name='insurance_end_date', + field=models.DateField(blank=True, null=True, verbose_name='Дата окончания страховки'), + ), + migrations.AddField( + model_name='fleetunit', + name='insurance_start_date', + field=models.DateField(blank=True, null=True, verbose_name='Дата начала страховки'), + ), + migrations.AddField( + model_name='fleetunit', + name='supplier_contacts', + field=models.TextField(blank=True, null=True, verbose_name='Контакты поставщика'), + ), + migrations.AddField( + model_name='fleetunit', + name='supplier_name', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Поставщик / Контрагент'), + ), + migrations.AddField( + model_name='fleetunit', + name='vehicle_documents', + field=models.FileField(blank=True, null=True, upload_to='fleet_docs/', verbose_name='Документы на авто'), + ), + ] diff --git a/core/migrations/0005_supplier_fleetunit_insurance_policy_number_and_more.py b/core/migrations/0005_supplier_fleetunit_insurance_policy_number_and_more.py new file mode 100644 index 0000000..723d669 --- /dev/null +++ b/core/migrations/0005_supplier_fleetunit_insurance_policy_number_and_more.py @@ -0,0 +1,49 @@ +# Generated by Django 5.2.7 on 2026-01-27 20:11 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_fleetunit_insurance_company_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Supplier', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='Наименование компании')), + ('representative_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='ФИО представителя/менеджера')), + ('phone', models.CharField(blank=True, max_length=50, null=True, verbose_name='Телефон')), + ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Электронная почта')), + ('contract_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='Номер договора')), + ], + options={ + 'verbose_name': 'Поставщик / Контрагент', + 'verbose_name_plural': 'Поставщики / Контрагенты', + }, + ), + migrations.AddField( + model_name='fleetunit', + name='insurance_policy_number', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='Номер страхового полиса'), + ), + migrations.AlterField( + model_name='fleetunit', + name='supplier_contacts', + field=models.TextField(blank=True, null=True, verbose_name='Контакты поставщика (старое)'), + ), + migrations.AlterField( + model_name='fleetunit', + name='supplier_name', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Поставщик (старое)'), + ), + migrations.AddField( + model_name='fleetunit', + name='supplier', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.supplier', verbose_name='Поставщик / Контрагент'), + ), + ] diff --git a/core/migrations/__pycache__/0004_fleetunit_insurance_company_and_more.cpython-311.pyc b/core/migrations/__pycache__/0004_fleetunit_insurance_company_and_more.cpython-311.pyc new file mode 100644 index 0000000..4a9a6e9 Binary files /dev/null and b/core/migrations/__pycache__/0004_fleetunit_insurance_company_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0005_supplier_fleetunit_insurance_policy_number_and_more.cpython-311.pyc b/core/migrations/__pycache__/0005_supplier_fleetunit_insurance_policy_number_and_more.cpython-311.pyc new file mode 100644 index 0000000..a9628e3 Binary files /dev/null and b/core/migrations/__pycache__/0005_supplier_fleetunit_insurance_policy_number_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 41ce768..6482c08 100644 --- a/core/models.py +++ b/core/models.py @@ -16,6 +16,20 @@ class Category(models.Model): def __str__(self): return self.name +class Supplier(models.Model): + name = models.CharField(max_length=255, verbose_name="Наименование компании") + representative_name = models.CharField(max_length=255, blank=True, null=True, verbose_name="ФИО представителя/менеджера") + phone = models.CharField(max_length=50, blank=True, null=True, verbose_name="Телефон") + email = models.EmailField(blank=True, null=True, verbose_name="Электронная почта") + contract_number = models.CharField(max_length=100, blank=True, null=True, verbose_name="Номер договора") + + class Meta: + verbose_name = "Поставщик / Контрагент" + verbose_name_plural = "Поставщики / Контрагенты" + + def __str__(self): + return self.name + class FleetUnit(models.Model): STATUS_CHOICES = [ ('active', 'В работе'), @@ -37,6 +51,18 @@ class FleetUnit(models.Model): notes = models.TextField(blank=True, null=True, verbose_name="Примечания") qr_code = models.ImageField(upload_to='qrcodes/', blank=True, null=True, verbose_name="QR-код") + # Insurance + insurance_company = models.CharField(max_length=255, blank=True, null=True, verbose_name="Страховая компания") + insurance_policy_number = models.CharField(max_length=100, blank=True, null=True, verbose_name="Номер страхового полиса") + insurance_start_date = models.DateField(blank=True, null=True, verbose_name="Дата начала страховки") + insurance_end_date = models.DateField(blank=True, null=True, verbose_name="Дата окончания страховки") + + # Supplier + supplier = models.ForeignKey(Supplier, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Поставщик / Контрагент") + supplier_name = models.CharField(max_length=255, blank=True, null=True, verbose_name="Поставщик (старое)") + supplier_contacts = models.TextField(blank=True, null=True, verbose_name="Контакты поставщика (старое)") + vehicle_documents = models.FileField(upload_to="fleet_docs/", blank=True, null=True, verbose_name="Документы на авто") + created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -68,13 +94,8 @@ class FleetUnit(models.Model): self.generate_qr_code() def generate_qr_code(self): - # We need the full URL. In a real app, we'd use the site domain. - # For now, we'll use a relative path or a placeholder if domain is unknown. path = self.get_absolute_url() - # You might want to use a full URL here if you have a domain - # base_url = "https://yourdomain.com" - # qr_data = f"{base_url}{path}" - qr_data = path # Using path for now, or you can try to get site domain + qr_data = path qr = qrcode.QRCode(version=1, box_size=10, border=5) qr.add_data(qr_data) @@ -194,4 +215,4 @@ class Document(models.Model): verbose_name_plural = "Документы" def __str__(self): - return f"{self.get_doc_type_display()} - {self.uploaded_at}" + return f"{self.get_doc_type_display()} - {self.uploaded_at}" \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 7f6a241..8f4dbf0 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -46,6 +46,9 @@