Files
octopus/myproject/inventory/migrations/0001_initial.py
Andrey Smakotin 94fe363cb1 Рефакторинг: отделение Delivery от Order, обязательные поля доставки, исправление доменов
- Отделена модель Delivery от Order (OneToOne связь)
- Добавлены обязательные поля delivery_date, time_from, time_to в Delivery
- Delivery обязательна при создании заказа (кроме черновиков)
- Добавлены методы calculate_total() и reset_delivery_cost() в Order
- Добавлена валидация полей доставки в OrderForm
- Исправлено создание доменов - убран порт из домена в БД
- Исправлен редирект после установки пароля (правильный формат URL)
- Исправлена ошибка NoReverseMatch в navbar для public схемы
- Удалены все старые миграции (база создается с нуля)
- Обновлены views для работы с новой моделью Delivery
2025-12-23 23:52:59 +03:00

363 lines
27 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Generated by Django 5.0.10 on 2025-12-23 20:38
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='DocumentCounter',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('counter_type', models.CharField(choices=[('transfer', 'Перемещение товара'), ('writeoff', 'Списание товара'), ('incoming', 'Поступление товара'), ('inventory', 'Инвентаризация')], max_length=20, unique=True, verbose_name='Тип счетчика')),
('current_value', models.IntegerField(default=0, verbose_name='Текущее значение')),
],
options={
'verbose_name': 'Счетчик документов',
'verbose_name_plural': 'Счетчики документов',
},
),
migrations.CreateModel(
name='Incoming',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('cost_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Закупочная цена')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
],
options={
'verbose_name': 'Товар в поступлении',
'verbose_name_plural': 'Товары в поступлениях',
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='IncomingBatch',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('document_number', models.CharField(db_index=True, max_length=100, unique=True, verbose_name='Номер документа')),
('receipt_type', models.CharField(choices=[('supplier', 'Поступление от поставщика'), ('inventory', 'Оприходование при инвентаризации'), ('adjustment', 'Оприходование без инвентаризации')], db_index=True, default='supplier', max_length=20, verbose_name='Тип поступления')),
('supplier_name', models.CharField(blank=True, max_length=200, null=True, verbose_name='Наименование поставщика')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
],
options={
'verbose_name': 'Партия поступления',
'verbose_name_plural': 'Партии поступлений',
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='IncomingDocument',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('document_number', models.CharField(db_index=True, max_length=100, unique=True, verbose_name='Номер документа')),
('status', models.CharField(choices=[('draft', 'Черновик'), ('confirmed', 'Проведён'), ('cancelled', 'Отменён')], db_index=True, default='draft', max_length=20, verbose_name='Статус')),
('date', models.DateField(help_text='Дата, к которой относится поступление', verbose_name='Дата документа')),
('receipt_type', models.CharField(choices=[('supplier', 'Поступление от поставщика'), ('inventory', 'Оприходование при инвентаризации'), ('adjustment', 'Оприходование без инвентаризации')], db_index=True, default='supplier', max_length=20, verbose_name='Тип поступления')),
('supplier_name', models.CharField(blank=True, help_text="Заполняется для типа 'Поступление от поставщика'", max_length=200, null=True, verbose_name='Наименование поставщика')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('confirmed_at', models.DateTimeField(blank=True, null=True, verbose_name='Дата проведения')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлён')),
],
options={
'verbose_name': 'Документ поступления',
'verbose_name_plural': 'Документы поступления',
'ordering': ['-date', '-created_at'],
},
),
migrations.CreateModel(
name='IncomingDocumentItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('cost_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Закупочная цена')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлён')),
],
options={
'verbose_name': 'Позиция документа поступления',
'verbose_name_plural': 'Позиции документа поступления',
'ordering': ['id'],
},
),
migrations.CreateModel(
name='Inventory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('document_number', models.CharField(blank=True, db_index=True, max_length=100, null=True, unique=True, verbose_name='Номер документа')),
('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата инвентаризации')),
('status', models.CharField(choices=[('draft', 'Черновик'), ('processing', 'В обработке'), ('completed', 'Завершена')], default='draft', max_length=20, verbose_name='Статус')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
],
options={
'verbose_name': 'Инвентаризация',
'verbose_name_plural': 'Инвентаризации',
'ordering': ['-date'],
},
),
migrations.CreateModel(
name='InventoryLine',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity_system', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество в системе')),
('quantity_fact', models.DecimalField(decimal_places=3, help_text='Количество свободных товаров, подсчитанных физически', max_digits=10, verbose_name='Подсчитано (факт, свободные)')),
('difference', models.DecimalField(decimal_places=3, default=0, editable=False, help_text='(Подсчитано + Зарезервировано) - Всего на складе', max_digits=10, verbose_name='Итоговая разница')),
('processed', models.BooleanField(default=False, verbose_name='Обработана (создана операция)')),
('snapshot_quantity_available', models.DecimalField(blank=True, decimal_places=3, help_text='Всего на складе на момент завершения инвентаризации', max_digits=10, null=True, verbose_name='Всего на складе (snapshot)')),
('snapshot_quantity_reserved', models.DecimalField(blank=True, decimal_places=3, help_text='В резервах на момент завершения инвентаризации', max_digits=10, null=True, verbose_name='В резервах (snapshot)')),
('snapshot_quantity_system', models.DecimalField(blank=True, decimal_places=3, help_text='В системе свободно на момент завершения инвентаризации', max_digits=10, null=True, verbose_name='В системе свободно (snapshot)')),
('snapshot_difference', models.DecimalField(blank=True, decimal_places=3, help_text='Итоговая разница на момент завершения инвентаризации', max_digits=10, null=True, verbose_name='Итоговая разница (snapshot)')),
],
options={
'verbose_name': 'Строка инвентаризации',
'verbose_name_plural': 'Строки инвентаризации',
},
),
migrations.CreateModel(
name='Reservation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('status', models.CharField(choices=[('reserved', 'Зарезервирован'), ('released', 'Освобожден'), ('converted_to_sale', 'Преобразован в продажу'), ('converted_to_writeoff', 'Преобразован в списание')], default='reserved', max_length=25, verbose_name='Статус')),
('reserved_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата резервирования')),
('released_at', models.DateTimeField(blank=True, null=True, verbose_name='Дата освобождения')),
('converted_at', models.DateTimeField(blank=True, help_text='Дата преобразования в продажу или списание', null=True, verbose_name='Дата преобразования')),
('cart_lock_expires_at', models.DateTimeField(blank=True, help_text='Время истечения блокировки в корзине (для витринных комплектов)', null=True, verbose_name='Блокировка корзины истекает')),
('cart_session_id', models.CharField(blank=True, help_text='Дополнительная идентификация сессии для надежности', max_length=100, null=True, verbose_name='ID сессии корзины')),
],
options={
'verbose_name': 'Резервирование',
'verbose_name_plural': 'Резервирования',
'ordering': ['-reserved_at'],
},
),
migrations.CreateModel(
name='Sale',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('sale_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Цена продажи')),
('document_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='Номер документа')),
('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата операции')),
('processed', models.BooleanField(default=False, verbose_name='Обработана (FIFO применена)')),
],
options={
'verbose_name': 'Продажа',
'verbose_name_plural': 'Продажи',
'ordering': ['-date'],
},
),
migrations.CreateModel(
name='SaleBatchAllocation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('cost_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Закупочная цена')),
],
options={
'verbose_name': 'Распределение продажи по партиям',
'verbose_name_plural': 'Распределения продаж по партиям',
},
),
migrations.CreateModel(
name='Showcase',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
('is_active', models.BooleanField(default=True, verbose_name='Активна')),
('is_default', models.BooleanField(default=False, verbose_name='По умолчанию')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
],
options={
'verbose_name': 'Витрина',
'verbose_name_plural': 'Витрины',
'ordering': ['warehouse', 'name'],
},
),
migrations.CreateModel(
name='ShowcaseItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('available', 'Доступен'), ('in_cart', 'В корзине'), ('sold', 'Продан'), ('dismantled', 'Разобран')], db_index=True, default='available', max_length=20, verbose_name='Статус')),
('sold_at', models.DateTimeField(blank=True, null=True, verbose_name='Дата продажи')),
('cart_lock_expires_at', models.DateTimeField(blank=True, null=True, verbose_name='Блокировка истекает')),
('cart_session_id', models.CharField(blank=True, max_length=100, null=True, verbose_name='ID сессии корзины')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлен')),
],
options={
'verbose_name': 'Экземпляр на витрине',
'verbose_name_plural': 'Экземпляры на витрине',
},
),
migrations.CreateModel(
name='Stock',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity_available', models.DecimalField(decimal_places=3, default=0, editable=False, max_digits=10, verbose_name='Доступное количество')),
('quantity_reserved', models.DecimalField(decimal_places=3, default=0, editable=False, max_digits=10, verbose_name='Зарезервированное количество')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
],
options={
'verbose_name': 'Остаток на складе',
'verbose_name_plural': 'Остатки на складе',
},
),
migrations.CreateModel(
name='StockBatch',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('cost_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Закупочная цена')),
('is_active', models.BooleanField(default=True, verbose_name='Активна')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
],
options={
'verbose_name': 'Партия товара',
'verbose_name_plural': 'Партии товаров',
'ordering': ['created_at'],
},
),
migrations.CreateModel(
name='StockMovement',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('change', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Изменение')),
('reason', models.CharField(choices=[('purchase', 'Закупка'), ('sale', 'Продажа'), ('write_off', 'Списание'), ('adjustment', 'Корректировка')], max_length=20, verbose_name='Причина')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
],
options={
'verbose_name': 'Движение товара',
'verbose_name_plural': 'Движения товаров',
},
),
migrations.CreateModel(
name='Transfer',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('document_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='Номер документа')),
('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата операции')),
],
options={
'verbose_name': 'Перемещение',
'verbose_name_plural': 'Перемещения',
'ordering': ['-date'],
},
),
migrations.CreateModel(
name='TransferBatch',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('document_number', models.CharField(db_index=True, max_length=100, unique=True, verbose_name='Номер документа')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
],
options={
'verbose_name': 'Документ перемещения',
'verbose_name_plural': 'Документы перемещения',
'ordering': ['-created_at'],
},
),
migrations.CreateModel(
name='TransferItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
],
options={
'verbose_name': 'Строка перемещения',
'verbose_name_plural': 'Строки перемещения',
'ordering': ['id'],
},
),
migrations.CreateModel(
name='Warehouse',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
('street', models.CharField(blank=True, max_length=255, null=True, verbose_name='Улица')),
('building_number', models.CharField(blank=True, max_length=20, null=True, verbose_name='Номер здания')),
('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region='BY', verbose_name='Телефон')),
('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('is_default', models.BooleanField(default=False, help_text='Автоматически выбирается при создании новых документов', verbose_name='Склад по умолчанию')),
('is_pickup_point', models.BooleanField(default=True, help_text='Можно ли выбрать этот склад как точку самовывоза заказа', verbose_name='Доступен для самовывоза')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
],
options={
'verbose_name': 'Склад',
'verbose_name_plural': 'Склады',
},
),
migrations.CreateModel(
name='WriteOff',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('reason', models.CharField(choices=[('damage', 'Повреждение'), ('spoilage', 'Порча'), ('shortage', 'Недостача'), ('inventory', 'Инвентаризационная недостача'), ('other', 'Другое')], default='other', max_length=20, verbose_name='Причина')),
('cost_price', models.DecimalField(decimal_places=2, editable=False, max_digits=10, verbose_name='Закупочная цена')),
('document_number', models.CharField(blank=True, max_length=100, null=True, verbose_name='Номер документа')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('date', models.DateTimeField(auto_now_add=True, verbose_name='Дата операции')),
],
options={
'verbose_name': 'Списание',
'verbose_name_plural': 'Списания',
'ordering': ['-date'],
},
),
migrations.CreateModel(
name='WriteOffDocument',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('document_number', models.CharField(db_index=True, max_length=100, unique=True, verbose_name='Номер документа')),
('status', models.CharField(choices=[('draft', 'Черновик'), ('confirmed', 'Проведён'), ('cancelled', 'Отменён')], db_index=True, default='draft', max_length=20, verbose_name='Статус')),
('date', models.DateField(help_text='Дата, к которой относится списание', verbose_name='Дата документа')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('confirmed_at', models.DateTimeField(blank=True, null=True, verbose_name='Дата проведения')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Обновлён')),
],
options={
'verbose_name': 'Документ списания',
'verbose_name_plural': 'Документы списания',
'ordering': ['-date', '-created_at'],
},
),
migrations.CreateModel(
name='WriteOffDocumentItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.DecimalField(decimal_places=3, max_digits=10, verbose_name='Количество')),
('reason', models.CharField(choices=[('damage', 'Повреждение'), ('spoilage', 'Порча'), ('shortage', 'Недостача'), ('inventory', 'Инвентаризационная недостача'), ('other', 'Другое')], default='damage', max_length=20, verbose_name='Причина списания')),
('notes', models.TextField(blank=True, null=True, verbose_name='Примечания')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'Позиция документа списания',
'verbose_name_plural': 'Позиции документа списания',
'ordering': ['id'],
},
),
]