Files
octopus/myproject/inventory/utils/document_generator.py
Andrey Smakotin 375ec5366a Унификация генерации номеров документов и оптимизация кода
- Унифицирован формат номеров документов: IN-XXXXXX (6 цифр), как WO-XXXXXX и MOVE-XXXXXX
- Убрано дублирование функции _extract_number_from_document_number
- Оптимизирована инициализация счетчика incoming: быстрая проверка перед полной инициализацией
- Удален неиспользуемый файл utils.py (функциональность перенесена в document_generator.py)
- Все функции генерации номеров используют единый подход через DocumentCounter.get_next_value()
2025-12-21 00:51:08 +03:00

149 lines
6.0 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.
"""
Генератор номеров документов для различных операций в inventory.
"""
from inventory.models import DocumentCounter
def generate_writeoff_document_number():
"""
Генерирует уникальный номер документа списания.
Формат: WO-XXXXXX (6 цифр)
Thread-safe через DocumentCounter.
Returns:
str: Сгенерированный номер документа (например, WO-000001)
"""
next_number = DocumentCounter.get_next_value('writeoff')
return f"WO-{next_number:06d}"
def generate_transfer_document_number():
"""
Генерирует уникальный номер документа перемещения.
Формат: MOVE-XXXXXX (6 цифр)
Returns:
str: Сгенерированный номер документа (например, MOVE-000001)
"""
next_number = DocumentCounter.get_next_value('transfer')
return f"MOVE-{next_number:06d}"
def _extract_number_from_document_number(doc_number):
"""
Извлекает числовое значение из номера документа.
Поддерживает оба формата: IN-0000-0003 и IN-000002.
Для старого формата IN-XXXX-YYYY извлекается YYYY (последние 4 цифры).
Для нового формата IN-XXXXXX извлекается XXXXXX (6 цифр).
Args:
doc_number: строка номера документа (например, 'IN-0000-0003' или 'IN-000002')
Returns:
int: числовое значение или 0 если не удалось распарсить
"""
try:
# Убираем префикс 'IN-'
if not doc_number.startswith('IN-'):
return 0
parts = doc_number[3:].split('-') # ['0000', '0003'] или ['000002']
if len(parts) == 2:
# Старый формат: IN-0000-0003
# Берем последнюю часть (0003) и конвертируем в число
return int(parts[1])
elif len(parts) == 1:
# Новый формат: IN-000002
return int(parts[0])
else:
return 0
except (ValueError, IndexError):
return 0
def _initialize_incoming_counter_if_needed():
"""
Инициализирует DocumentCounter для 'incoming' максимальным номером
из существующих документов, если счетчик еще не инициализирован.
Вызывается только если счетчик равен 0 (не инициализирован).
Thread-safe через select_for_update.
"""
from inventory.models import IncomingBatch, IncomingDocument
from django.db import transaction
# Быстрая проверка без блокировки - если счетчик существует и > 0, выходим
if DocumentCounter.objects.filter(
counter_type='incoming',
current_value__gt=0
).exists():
return
# Только если счетчик не инициализирован - делаем полную проверку с блокировкой
with transaction.atomic():
counter = DocumentCounter.objects.select_for_update().filter(
counter_type='incoming'
).first()
# Двойная проверка: возможно другой поток уже инициализировал
if counter and counter.current_value > 0:
return
# Собираем все номера документов
all_numbers = []
# Номера из IncomingBatch
batch_numbers = IncomingBatch.objects.filter(
document_number__startswith='IN-'
).values_list('document_number', flat=True)
all_numbers.extend(batch_numbers)
# Номера из IncomingDocument
doc_numbers = IncomingDocument.objects.filter(
document_number__startswith='IN-'
).values_list('document_number', flat=True)
all_numbers.extend(doc_numbers)
if all_numbers:
# Извлекаем максимальный номер из всех форматов
max_number = max(_extract_number_from_document_number(num) for num in all_numbers)
else:
# Нет существующих документов - начинаем с 0
max_number = 0
# Создаем или обновляем счетчик
if not counter:
DocumentCounter.objects.create(
counter_type='incoming',
current_value=max_number
)
elif counter.current_value == 0:
counter.current_value = max_number
counter.save(update_fields=['current_value'])
def generate_incoming_document_number():
"""
Генерирует уникальный номер документа поступления.
Формат: IN-XXXXXX (6 цифр) - унифицирован с WO-000001 и MOVE-000001.
Thread-safe через DocumentCounter.
При первом использовании автоматически инициализирует DocumentCounter
максимальным номером из существующих документов (IncomingBatch и IncomingDocument).
Returns:
str: Сгенерированный номер документа (например, IN-000001)
"""
# Инициализируем счетчик, если нужно (только если он равен 0)
_initialize_incoming_counter_if_needed()
# Используем стандартный метод, как и другие функции
next_number = DocumentCounter.get_next_value('incoming')
return f"IN-{next_number:06d}"