Автоматизация слияния

Автор Sirius34, 13 мая 2026, 11:11

0 Пользователи и 1 гость просматривают эту тему.

Sirius34

Всем доброго времени суток!

Озадачили тут меня на работе. Сменился сотрудник, который массово направляет запросы.
Новая "специалист", мягко скажем, не сильно шарит в нюансах слияния, источников данных и т.п.
Собственно возникла проблема.

Есть файл шаблонов "reqTemplates.odt" и таблица с исходными данными для слияния ("dataForReqs.ods").
В .odt-файле (12 типовых запросов) все поля уже подставлены, просто нажимай кнопку и работай с готовым документом.
Но... Обычным-то сотрудникам не всегда можно "донести" принцип работы слияния и рассылки, а уж некоторым просто никак. Нет у меня педагогического таланта, наверное.

В общем, нужно сделать так, чтобы при открытии документа пользователю вообще ничего не требовалось вводить дополнительно, "вручную" привязываться к исходному списку данных и так далее. Всё должно быть максимально автоматизировано.
Хочу сделать, если можно так выразиться, "отдельный файл-страницу с кнопкой START", при нажатии на которую будет выдаваться результат обработки файла "reqTemplates.odt" через слияние с источником "dataForReqs.ods".
По умолчанию предполагается, что оба файла находятся в той же папке, откуда запущен файл "с кнопкой START".
Подкиньте идею, пожалуйста: как лучше реализовать?
Заранее спасибо.

На конечном компьютере Astra Linux 1.7.8 SE + LO 25.x (точную версию не помню).

p.s. Я пока распечатал пачку картинок с пошаговой инструкцией, но ведь накосячит же обязательно, а крайним оставаться неохота...

economist

#1
Есть несколько расширений для этого. Искать по mail merge

Есть на Форуме пара макросов - в Поиск

Промышленный путь с "необычными сотрудниками" вижу так:
- ставим uv и в нем uv python install (чтобы уйти от системного Python в Astra)
- Заменяем в шаблонах Поля на <Местозаполнители>
- Пишем с ИИ простой скрипт на внешнем Python, который читает ODS как таблицу, а ODT - как архив и
меняет местозаполнитель обычным replace(<Местозаполнители>, ods_value)
- Необычный сотрудник запускает файл "Слияние.bat" в котором однострочник вида "uv run Слияние.py" и получает стопку DOCX ODT PDF

PS: На деле все-таки проще научить в "штатный функционал":
- жать Ctrl+Sрift+F4
- выбирать одну нужную строку вверху
- нажать одну кнопку Данные в поля. Всё, результат готов для Ctrl+P
Прокачивайте скилл общения, всё получится.   
Пить не буду коньяка - читану Питоньяка!

Sirius34

Намастрячил по-быстрому.
Воспользовался советом по использованию replace.
Готов выслушать критику.

Option VBASupport 1

Sub START_MERGE()

    Dim oDoc As Object
    Dim oSheetDoc As Object
    Dim oSheet As Object
    Dim oTemplate As Object
    Dim oResult As Object

    Dim sBasePath As String
    Dim sTemplatePath As String
    Dim sDataPath As String
    Dim sResultPath As String

    Dim iRow As Long
    Dim sURL As String

    Dim sFIOip As String
    Dim sFIOrp As String
    Dim sBornCity As String
    Dim sAddress As String
    Dim sPassSer As String
    Dim sPassNum As String
    Dim sPassOrgan As String
    Dim sINN As String
    Dim sSNILS As String
    Dim sClaimDebt As String
    Dim sNumPost As String
    Dim sToday As String
    Dim sBornDate As String
    Dim sPassDate As String
    Dim sTransport As String
    Dim sTransportYear As String
    Dim sTransportNum As String

    oDoc = ThisComponent

    ' ------------------------------------------------------------
    ' Папка, где лежит START.odt
    ' ------------------------------------------------------------

    sBasePath = Left(oDoc.URL, InStrRev(oDoc.URL, "/"))

    sTemplatePath = sBasePath & "шаблон запросов.odt"
    sDataPath = sBasePath & "для запросов.ods"

    ' ------------------------------------------------------------
    ' Открываем ODS
    ' ------------------------------------------------------------

    oSheetDoc = StarDesktop.loadComponentFromURL(sDataPath, "_blank", 0, Array())

    oSheet = oSheetDoc.Sheets.getByIndex(0)

    ' ------------------------------------------------------------
    ' Обработка строк
    ' ------------------------------------------------------------

    iRow = 1

    Do While Trim(oSheet.getCellByPosition(0, iRow).String) <> ""

        sFIOip = oSheet.getCellByPosition(1, iRow).String
        sFIOrp = oSheet.getCellByPosition(2, iRow).String
        sBornCity = oSheet.getCellByPosition(4, iRow).String
        sAddress = oSheet.getCellByPosition(5, iRow).String
        sPassSer = oSheet.getCellByPosition(8, iRow).String
        sPassNum = oSheet.getCellByPosition(9, iRow).String
        sPassOrgan = oSheet.getCellByPosition(11, iRow).String
        sINN = oSheet.getCellByPosition(12, iRow).String
        sSNILS = oSheet.getCellByPosition(13, iRow).String
        sToday = oSheet.getCellByPosition(16, iRow).String
        sBornDate = oSheet.getCellByPosition(17, iRow).String
        sPassDate = oSheet.getCellByPosition(19, iRow).String
        sTransport = oSheet.getCellByPosition(20, iRow).String
        sTransportYear = oSheet.getCellByPosition(21, iRow).String
        sTransportNum = oSheet.getCellByPosition(22, iRow).String

        ' --------------------------------------------------------
        ' Открываем шаблон
        ' --------------------------------------------------------

        oTemplate = StarDesktop.loadComponentFromURL(sTemplatePath, "_blank", 0, Array())

        ' --------------------------------------------------------
        ' Замена полей
        ' --------------------------------------------------------

        Call ReplaceText(oTemplate, "<ФИО_в_именительном_падеже>", sFIOip)
        Call ReplaceText(oTemplate, "<ФИО_в_родительном_падеже>", sFIOrp)
        Call ReplaceText(oTemplate, "<Дата_рождения>", sBornDate)
        Call ReplaceText(oTemplate, "<Уроженец>", sBornCity)
        Call ReplaceText(oTemplate, "<Адрес>", sAddress)
        Call ReplaceText(oTemplate, "<Паспорт_серия>", sPassSer)
        Call ReplaceText(oTemplate, "<Паспорт_номер>", sPassNum)
        Call ReplaceText(oTemplate, "<Паспорт_кем_выдан>", sPassOrgan)
        Call ReplaceText(oTemplate, "<Паспорт_когда_выдан>", sPassDate)
        Call ReplaceText(oTemplate, "<ИНН>", sINN)
        Call ReplaceText(oTemplate, "<СНИЛС>", sSNILS)
        Call ReplaceText(oTemplate, "<Текущая_дата>", sToday)
        Call ReplaceText(oTemplate, "<Транспорт_модель>", sTransport)
        Call ReplaceText(oTemplate, "<Транспорт_год_выпуска>", sTransportYear)
        Call ReplaceText(oTemplate, "<Транспорт_госномер>", sTransportNum)

        ' --------------------------------------------------------
        ' Сохраняем результат
        ' --------------------------------------------------------

        sResultPath = sBasePath & "готовые запросы " & sToday & "/"

        ' ------------------------------------------------------------
        ' Создаём папку с результатами, если её нет
        ' ------------------------------------------------------------

        CreateResultFolder(sResultPath)

        sURL = sResultPath & (sFIOip & "_" & sID & "запросы.odt")

        oTemplate.storeAsURL(sURL, Array())

        oTemplate.close(True)

        iRow = iRow + 1

    Loop

    oSheetDoc.close(True)

    MsgBox "Готово!" & Chr(10) & _
          "Файлы сохранены в папке 'готовые запросы " & sToday & "'", _
          64, "Слияние завершено"

End Sub

' =================================================================
' Замена текста
' =================================================================

Sub ReplaceText(oDoc As Object, sSearch As String, sReplace As String)

    Dim oSearch As Object
    Dim oFound As Object

    oSearch = oDoc.createSearchDescriptor()
    oSearch.SearchString = sSearch

    oFound = oDoc.findFirst(oSearch)

    Do While Not IsNull(oFound)

        oFound.String = sReplace

        oFound = oDoc.findNext(oFound.End, oSearch)

    Loop

End Sub

' =================================================================
' Создание папки с результатами
' =================================================================

Sub CreateResultFolder(sFolderURL As String)

    Dim oSimpleFileAccess As Object

    oSimpleFileAccess = createUnoService( _
        "com.sun.star.ucb.SimpleFileAccess")

    If Not oSimpleFileAccess.exists(sFolderURL) Then
        oSimpleFileAccess.createFolder(sFolderURL)
    End If

End Sub

bigor

Цитата: Sirius34 от 13 мая 2026, 15:49выслушать критику
Когда я тестировал подобное решение на больших таблицах, встроенное слияние работало быстрее.
В LO есть "встроенная" замена 
Sub ReplaceComma
oReplace = ThisComponent.createReplaceDescriptor()
oReplace.SearchString = ","
oReplace.ReplaceString = "..."
ThisComponent.replaceAll(oReplace)
msgbox "Обработка завершена"
End Sub
Поддержать наш форум можно здесь

sokol92

#4
Цитата: Sirius34 от 13 мая 2026, 15:49Готов выслушать критику.
Если результат устраивает, то хорошо.
В технологии "Mail Merge" при наличии на разделяемом ресурсе файлов .ods (источник данных), .odt (шаблон отчета) и  .odb (описание базы данных) макрос для запуска отчета не будет длинным.  Скорость генерации отчета будет намного выше (если это важно).

Можно также динамически создавать файл .odb.
Мы используем подобную технологию, в качестве источника данных могут быть файлы .ods или .xlsx, в качестве шаблона файлы .odt или .docx.

Наиболее серьезное ограничение технологии Mail Merge (на мой взгляд) - отсутствие "штатной" возможности  для генерации текстовых таблиц.

Владимир.

economist

Добавлю что источником данных может быть и таблица Writer .odt, и адресная книга ThunderBird, где у аккуратных юзеров данных больше чем в CRM/1C, включая названия любимых духов секретарей клиентов.

Таблицы мерджить умеет одно из старых расширений, по моему с сайта AOO.

Сложно придумать сценарий с пенсонализованными таблицами, например прайсами.

Народ креативит: использует DDE-связи с Calc, меняя в ней макросом диапазоны ссылки итд. Также для совсем маленьких таблиц - можно в пустую таблицу перетащить отдельные поля до Слияния/Рассылки.

Для небольших компредложений, в которой просто очень хочется опрятную табличку 5*3 - годное решение. Но Поля надо тащить не из таблицы БД, а из view, где строки с помощью SQL трансформированы в столбцы (left join) так что данные для рассылки оказываются в одной строке и легко попадают во Writer по Ctrl+Shift+F4
Пить не буду коньяка - читану Питоньяка!