Загрузка данных


from pathlib import Path
import re
from copy import copy

from openpyxl import load_workbook

# ====== Настройки ======
SOURCE_FILE = Path(r"C:\path\to\first.xlsx")       # Первая книга: лист «Лист1»
TARGET_FILE = Path(r"C:\path\to\second.xlsx")       # Вторая книга: лист «Вся База»
OUTPUT_FILE = Path(r"C:\path\to\second_filled.xlsx")

SOURCE_SHEET = "Лист1"
TARGET_SHEET = "Вся База"

# Номера столбцов: A=1, B=2, M=13, AE=31, AF=32, AG=33
COL_SOURCE_A = 1
COL_SOURCE_B = 2
COL_SOURCE_M = 13
COL_TARGET_AE = 31
COL_TARGET_AF = 32
COL_TARGET_AG = 33


# Вставьте сюда вашу уже существующую логику обработки идентификатора M.
def normalize_identifier_from_need(col_m_value):
    """Преобразует идентификатор из M к виду, используемому для поиска в AE.

    Ниже — безопасный пример: строка, удаление пробелов, верхний регистр.
    Замените тело функции на ваш фактический маленький скрипт, если он иной.
    """
    if col_m_value is None:
        return None

    short_id = str(col_m_value).strip()
    if not short_id:
        return None

    short_id = re.sub(r"\s+", "", short_id).upper()
    return short_id


def as_excel_text(value):
    """Возвращает значение как текст, без превращения номера заявки в число."""
    if value is None:
        return ""
    return str(value).strip()


def main():
    if not SOURCE_FILE.exists():
        raise FileNotFoundError(f"Не найден исходный файл: {SOURCE_FILE}")
    if not TARGET_FILE.exists():
        raise FileNotFoundError(f"Не найден целевой файл: {TARGET_FILE}")

    # data_only=False важен, если в ячейках есть формулы и нужно читать сами формулы.
    wb_source = load_workbook(SOURCE_FILE, read_only=True, data_only=False)
    wb_target = load_workbook(TARGET_FILE)

    if SOURCE_SHEET not in wb_source.sheetnames:
        raise ValueError(f"В первой книге нет листа «{SOURCE_SHEET}»")
    if TARGET_SHEET not in wb_target.sheetnames:
        raise ValueError(f"Во второй книге нет листа «{TARGET_SHEET}»")

    ws_source = wb_source[SOURCE_SHEET]
    ws_target = wb_target[TARGET_SHEET]

    # Индекс: нормализованный идентификатор из AE -> список строк во второй книге.
    # Список, а не одна строка: так не пропадут данные при дублях в AE.
    target_index = {}
    for row in range(1, ws_target.max_row + 1):
        raw_ae = ws_target.cell(row=row, column=COL_TARGET_AE).value
        normalized_ae = normalize_identifier_from_need(raw_ae)
        if normalized_ae:
            target_index.setdefault(normalized_ae, []).append(row)

    processed = 0
    matched_source_rows = 0
    updated_cells = 0
    empty_identifier_rows = []
    not_found = []
    duplicate_matches = []

    # Предполагается, что первая строка содержит заголовки.
    for source_row in range(2, ws_source.max_row + 1):
        processed += 1

        value_a = as_excel_text(ws_source.cell(source_row, COL_SOURCE_A).value)
        value_b = as_excel_text(ws_source.cell(source_row, COL_SOURCE_B).value)
        raw_m = ws_source.cell(source_row, COL_SOURCE_M).value
        short_id = normalize_identifier_from_need(raw_m)

        if not short_id:
            empty_identifier_rows.append(source_row)
            continue

        target_rows = target_index.get(short_id, [])
        if not target_rows:
            not_found.append((source_row, short_id))
            continue

        matched_source_rows += 1
        if len(target_rows) > 1:
            duplicate_matches.append((source_row, short_id, target_rows))

        # Если во второй книге найдено несколько строк с одним идентификатором,
        # значения переносятся во все найденные строки.
        for target_row in target_rows:
            cell_af = ws_target.cell(target_row, COL_TARGET_AF)
            cell_ag = ws_target.cell(target_row, COL_TARGET_AG)

            # Формат Text, чтобы Excel не потерял ведущие нули и не представил длинные номера как число.
            cell_af.number_format = "@"
            cell_ag.number_format = "@"
            cell_af.value = value_a
            cell_ag.value = value_b
            updated_cells += 2

    wb_target.save(OUTPUT_FILE)
    wb_source.close()
    wb_target.close()

    print(f"Готово. Результат сохранён: {OUTPUT_FILE}")
    print(f"Проверено строк в «{SOURCE_SHEET}»: {processed}")
    print(f"Строк первой книги с совпадением в AE: {matched_source_rows}")
    print(f"Записано ячеек AF/AG: {updated_cells}")
    print(f"Пустой или некорректный M: {len(empty_identifier_rows)}")
    print(f"Идентификатор не найден в AE: {len(not_found)}")
    print(f"Совпадений с дублями в AE: {len(duplicate_matches)}")

    if not_found:
        print("\nПервые 20 не найденных идентификаторов (строка Лист1, идентификатор):")
        for item in not_found[:20]:
            print(item)

    if duplicate_matches:
        print("\nПервые 20 идентификаторов с несколькими строками в AE:")
        for item in duplicate_matches[:20]:
            print(item)


if __name__ == "__main__":
    main()