""" Генератор номеров документов для различных операций в 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 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 # Собираем все номера документов из IncomingDocument all_numbers = list(IncomingDocument.objects.filter( document_number__startswith='IN-' ).values_list('document_number', flat=True)) 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 максимальным номером из существующих документов IncomingDocument. Returns: str: Сгенерированный номер документа (например, IN-000001) """ # Инициализируем счетчик, если нужно (только если он равен 0) _initialize_incoming_counter_if_needed() # Используем стандартный метод, как и другие функции next_number = DocumentCounter.get_next_value('incoming') return f"IN-{next_number:06d}" def generate_inventory_document_number(): """ Генерирует уникальный номер документа инвентаризации. Формат: INV-XXXXXX (6 цифр) Thread-safe через DocumentCounter. Returns: str: Сгенерированный номер документа (например, INV-000001) """ next_number = DocumentCounter.get_next_value('inventory') return f"INV-{next_number:06d}"