Введение

Есть одна мебельная фабрика. Исторически у этой фабрики IT хозяйство было не очень развито и со временем накопилось множество внутренних сервисов для расчета материалов, нагрузки на станки, учета временных затрат работников, доставки и прочего. Исходя из этих расчетов составляется себестоимость продукции. Далее накладывается маржа, и идет расчет доставки и сборки, в зависимости от условий. Фабрика захотела объединить все сервисы в один, основанный на веб, чтобы все расчёты и результаты можно было видеть в одном месте. Было выбрано делать новую веб систему на фреймворке Symfony (PHP).

По задумке данные из разных Excel файлов и локальной базы данных должны стекаться туда, группироваться по проектам и красиво отображаться. Изначально не планировалось изменять какие-либо данные, только просматривать. Также заказчик не хотел полностью отказываться от Excel файлов и переносить формулы в новую систему. Да, это странно со стороны разработчика, но объяснимо со стороны бизнеса: формул очень много, и они очень старые и запутанные. Но они отлично работают и всех устраивают. Их анализ, перенос в новую систему, отладка, и обучение работников пользоваться новой системой не входило в планы и бюджет проекта.

Проблема

На этапе разработки, когда первые Excel файлы были загружены и красиво отображались таблицы с материалами и суммы, клиенту захотелось немного "поиграть" цифрами внутри системы. Вместо одного стола, "поставить" два и посмотреть как изменятся суммы материалов. Естественно, возник вопрос, что мы не можем пересчитать формулы в нашей системе, так как мы их не переносили. Вариант увеличивать итог пропорционально количеству не мог быть использован, так как это не было пропорциональное увеличение. Упрощенный пример: для изготовления стола нужна заготовка столешницы из которой можно сделать пять столов. Но если надо сделать шесть столов, то заготовок нужно уже две. Так же и доставка: если в грузовик влезает сто столов, то чтобы привести сто один стол нужно уже два грузовика. Excel всё это учитывал, а еще скидки для конкретных клиентов, разный расчет стоимости сборки и прочее, прочее.

Первая попытка — PhpSpreadsheet

Кажется встала невыполнимая задача - пересчитывать формулы, которых нет. Но как говорил мой первый наставник и шеф: "Мы можем сделать всё, для программиста нет ничего невозможного". Для импорта Excel файла я использовал библиотеку https://github.com/phpoffice/phpspreadsheet с помощью нее можно прочитать лист внутри файла и получить значение (как саму формулу так и ее результат) из любой ячейки.

Пример кода, как прочитать результат формулы из ячейки
use PhpOffice\PhpSpreadsheet\IOFactory;

$spreadsheet = IOFactory::load('/path/file.xlsx');
$sheet = $spreadsheet->getSheetByName('Totals');
$value = $sheet->getCell('F18')->getCalculatedValue();

У этой библиотеки есть также функция задать значение в ячейку. То есть теоретически можно создать форму на сайте, например с полями "количество товара". При отправке формы, получить данные и поменять в файле количество товара но новое, рассчитать новые значения сумм после этого.

Скрытый текст
use PhpOffice\PhpSpreadsheet\IOFactory;

$newQuantity = 5;

$spreadsheet = IOFactory::load('/path/file.xlsx');
$sheet = $spreadsheet->getSheetByName('Products');
$sheet->setCellValue('D:5', $newQuantity);

Но на практике это не сработало - формулы не могли рассчитаться так как было много листов и перекрестных ссылок. PhpOffice\PhpSpreadsheet это не полноценный движок Excel а лишь библиотека для работы с готовым файлом. А значит, можно попробовать использовать полноценный движок. Что если установить на сервер headless LibreOffice и делать перерасчет формул там? Стал пробовать.

Вторая попытка — использовать настоящий движок Excel

Установил LibreOffice на сервер

sudo apt update
sudo apt install -y libreoffice libreoffice-calc fonts-dejavu fontconfig

И все получилось!

Перерасчет формул в headless LibreOffice
use PhpOffice\PhpSpreadsheet\IOFactory;

$newQuantity = 5;

$spreadsheet = IOFactory::load('/path/file.xlsx');
$sheet = $spreadsheet->getSheetByName('Products');
$sheet->setCellValue('D5', $newQuantity);

$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setPreCalculateFormulas(false);
$writer->save('/path/file.xlsx');

$cmd = sprintf(
  'HOME=/tmp libreoffice --headless --nologo --nofirststartwizard --calc --convert-to xlsx --outdir %s %s',
  escapeshellarg(dirname($absolutePath)),
  escapeshellarg($absolutePath)
);

exec($cmd, $output, $code);

if ($code !== 0) {
  throw new RuntimeException('LibreOffice recalculation failed');
}

$updatedSpreadsheet = IOFactory::load('/path/file.xlsx');
$updatedSheet = $updatedSpreadsheet->getSheetByName('Totals');
$updatedValue = $updatedSheet->getCell('F18')->getCalculatedValue();

В итоге Excel так и остался внутри системы — но уже не как файл, из которого берём данные, а как движок бизнес-логики.

Вывод

Да, решение получилось немного необычным: веб-приложение на Symfony меняет входные данные в Excel, затем headless LibreOffice пересчитывает формулы, и система забирает обратно готовые результаты. На практике это занимает около 15 секунд. Но для пользователя, который делает расчёт проекта мебели, это вполне приемлемо. Ему важнее менять параметры и видеть результат.

Можно ли было переписать все формулы на PHP? Конечно. Но это заняло бы месяцы работы, большой бюджет и риск сломать давно работающую логику.

Наверное такой подход можно использовать в MVP где надо много менять формулы, и это должен делать не программист, а например менеджер или сам клиент.

Иногда подходящее решение - не переписать всё заново, а грамотно встроить существующий инструмент в новую систему. И в этот раз Excel оказался не пережитком прошлого, а вполне рабочим микросервисом для расчётов. Вместо того, чтобы бороться с существующим инструментом, проще признать: Excel уже является системой расчётов. И его можно использовать программно.

Комментарии (6)


  1. IvanSTV
    17.04.2026 09:58

    А теперь на тему как вижу этот бизнес-процесс я. Я как бы много-много занимался разработкой всяких макросов и процессов, которые на Экселе сидят, видел массу.

    Эксель хорош гибкостью, но тем же и плох, потому что требует квалифицированного пользователя. Если в той же 1С еще надо умудриться все сломать (тем не менее, ломают все, что может сломаться), то в экселе ломается вообще все, к чему притрагивается пользователь, на раз.

    Типично: в Эксель-файл пользователь вставляет список накладных из другого файла Эксель, макрос хватает данные из справочников, обрабатывает и влупляет в SAP нужные данные. Удаленный пользователь звонит, жалуется - не работает ни рожна, обрабатывает только одну накладную, и все. Я беру список, вставляю - все обрабатывается. Так и отвечаю. Через полдня - нет, не работает. Я опять проделываю ту же операцию. Все работает, те же самые накладные, которые у нее не работают. Перезаписываю файл, прошу заново открыть. Нет, не работает, только первая накладная. Прошу расшарить экран, залезаю к ней, ту же операцию провожу, все работает. "Покажи, как делала?". Она вместо диапазона из Эксель берет из письма строку с накладными через запятую, вставляет. Результат немного предсказуем. Причем уже успела нажаловаться начальнику, что у нее ничего не работает. С средним качеством владения инструментом в российских офисах, когда даже по видеоинструкции оператор не может сформировать банальную сводную таблицу, я бы строить на Экселе процессы не рекомендовал. Одно дело, когда таблицы курсируют между аналитиками и руководителями (причем последние в основном смотрят), а другое - когда они добираются до исполнителей (а они неизбежно добираются), и там уже свет туши.

    Но это заняло бы месяцы работы, большой бюджет и риск сломать давно работающую логику.

    Вообще, я бы настоял на переписывании всех формул. Почему? Потому что со временем в любой таблице, которую легко изменять, накапливаются ошибки - алгоритмов, архитектуры, часть процессов вообще теряют актуальность, а многие надо оптимизировать. Если никто не может логику таблиц описать, то такая таблица - жесточайший бизнес-риск, это ОГРОМНОЕ ПОЛЕ для ошибок и злоупотреблений. Коробка, которая выдает неясно на чем основанные результаты. Например, на одной работе обнаружилось, что себестоимость логистики год считалась в 10 раз меньше факта - ошибка распределения затрат, заложенная в формуле расчета. Причем обнаружилось случайно - кто-то просто протянул до конца формулу, и у маркетологов все распродажи ушли в минус. Стали ковыряться, обнаружили, что формулу никто год не протягивал, а строки добавлялись, и вообще вся формула неверна. Так что переписывание таких инструментов нужно в первую очередь бизнесу. Сломать обычно можно не "работающую" логику, а "криво и непонятно работающую логику", а потом заменить ее на понятную, задокументированную и защищенную от дурака логику. Нужно ли это делать на PHP или еще на чем - вопрос уже технический.


    1. dimas846 Автор
      17.04.2026 09:58

      Согласен на все 100% Excel как черный ящик это ужасно, с какой стороны не посмотри. Все это, конечно было объяснено заказчику. Но вот в реальной жизни бизнес не готов к большим изменениям. У них все хорошо работает (по их мнению). Вкладываться в переделку они не хотят. Я их прекрасно понимаю, и не мне их учить зарабатывать деньги и делать ИХ бизнес.
      С технической стороны это получилось и работает! Клиент доволен. Со стороны этики профессии могут быть вопросы: но даже не могу сформулировать :))) Может, что решение ненадежное? Но нет же. Это обычный файл, открыл, отредактировал, сохранил (программно).


    1. WALL_E
      17.04.2026 09:58

      Почему то бытует мнение о повинности экселей во всех грехах как бизнеса так и разработчиков. Зачастую с обоих сторон собираются дилетанты бизнесовой логики. С другой стороны "разрабы", которые владеют средой по остаточному принципу. На выходе получается нечто неустойчивое ко многим отклонениям от единственно правильного сценария использования. Чтобы создать что либо на любом инструменте и получать крепкий результат - нужна высокая квалификация специалистов и затраты временные. А эксели здесь ни при чем. Это непревзойденный инструмент для малой автоматизации.


  1. purple_elephant
    17.04.2026 09:58

    А это был Эксель чисто на формулах, без макросов, или vba уже так хорошо поддерживается что - заработало все само?


    1. dimas846 Автор
      17.04.2026 09:58

      У меня файлы были без макросов. Пишут, что LibreOffice имеет свой язык макросов, который частично совместим с vba, то есть простой скрипт может сработать, но рассчивать на полную совместимость нельзя.


  1. WALL_E
    17.04.2026 09:58

    Ничего необычного.

    И еще. Если вы на сервере будете держать экземпляр "книги" все время в запущенном состоянии, что отъест немного памяти, то при каждом пересчете не будете тратить время на ее инициализацию. В результате пересчет будет длиться какие-то микросекунды.