Przejdź do treści
Poradnik Programy księgowe

Symfonia FK 2026.3: „Błąd zapisu do bufora" — poprawka na wielu bazach naraz

Symfonia FK 2026.3 przy dokumentach w oknie Faktury KSeF potrafi rzucić „Błąd zapisu do bufora. Nieprawidłowy format ciągu wejściowego". Producent ma na to poprawkę SQL — pokazuję, jak wykonać ją na wielu bazach naraz, bezpiecznie, z testowym przebiegiem przez ROLLBACK.

Komu to pomoże?

Tobie, jeśli masz na jednym SQL Serverze kilkadziesiąt albo kilkaset baz Symfonii FK 2026.3 i przy wprowadzaniu lub aktualizacji dokumentów w oknie Faktury KSeF widzisz:

Błąd zapisu do bufora. Nieprawidłowy format ciągu wejściowego

Poprawkę producenta trzeba wtedy wykonać na każdej bazie z osobna — a przy takiej liczbie klientów to zwykłe marnowanie popołudnia.

Kogo to nie dotyczy?

Masz jedną bazę? Nie kombinuj — wykonaj po prostu oficjalny skrypt Symfonii na tej jednej bazie i tyle. Ten wpis jest o robieniu tego samego hurtem, na wielu bazach.

Na czym polega problem

Błąd pojawia się w Symfonii Finanse i Księgowość 2026.3 podczas wprowadzania do bufora lub aktualizacji dokumentów w oknie Faktury KSeF. Symfonia opublikowała do tego oficjalną poprawkę SQL i zaleca ją wyłącznie dla wersji 2026.3.

Oficjalna instrukcja producenta zakłada wykonanie skryptu na konkretnej bazie firmy. Przy jednej bazie to żaden problem. Gorzej, kiedy na jednym SQL Serverze pracuje kilkadziesiąt albo kilkaset baz klientów — ręczne przechodzenie po każdej z osobna jest po prostu niewygodne.

Co robi mój skrypt

Przygotowałem wersję, która przechodzi po bazach SQL automatycznie. Skrypt:

  • sprawdza wszystkie dostępne bazy użytkownika (pomija systemowe i niedostępne),
  • w każdej bazie szuka w FK.dokumenty rekordów wymagających poprawki,
  • przed zmianą tworzy kopię tylko tych rekordów, które mają zostać zmodyfikowane,
  • dopiero wtedy wykonuje zmianę,
  • pilnuje, żeby liczba rekordów w kopii i liczba zmienionych zgadzały się co do sztuki,
  • na końcu pokazuje raport z każdej bazy.

Nie ma więc potrzeby ręcznego otwierania każdej bazy.

Trzy ustawienia na górze skryptu

Zmieniasz tylko sekcję USTAWIENIA:

DECLARE @NowyZnacznik nvarchar(100) = N'0';
DECLARE @Tryb varchar(8) = 'ROLLBACK';
DECLARE @TabelaKopii sysname = N'dokumenty_backup_20260806';
  • @NowyZnacznik — wartość, którą skrypt wpisuje w brakujący znacznik (zgodnie z poprawką producenta).
  • @TrybROLLBACK (test) albo COMMIT (zapis). O tym niżej.
  • @TabelaKopii — nazwa tabeli kopii, którą skrypt tworzy w schemacie FK (czyli powstanie FK.dokumenty_backup_20260806).

Tryb testowy (ROLLBACK) i zapis (COMMIT)

To jest bezpiecznik. Przy:

DECLARE @Tryb varchar(8) = 'ROLLBACK';

skrypt działa testowo: wchodzi w transakcję, liczy rekordy, próbnie tworzy kopię, wykonuje aktualizację — a na końcu wszystko wycofuje. Nic nie zostaje zapisane, ale w raporcie widzisz, ile rekordów w której bazie by się zmieniło. To Twój przebieg na sucho.

Kiedy wynik wygląda poprawnie, zmieniasz jedną rzecz:

DECLARE @Tryb varchar(8) = 'COMMIT';

i uruchamiasz skrypt ponownie. Teraz kopia zostaje utworzona, a zmiany zapisane.

Kopia zmienianych rekordów

Przy COMMIT skrypt zapisuje do tabeli FK.<@TabelaKopii> rekordy sprzed zmiany — dokładnie te, które modyfikuje, nic więcej. Dzięki temu, obok standardowej kopii zapasowej SQL Server, masz szybką kopię właśnie tych danych.

Dwa zabezpieczenia, o których warto wiedzieć:

  • Skrypt nie nadpisze istniejącej kopii — jeśli tabela o tej nazwie już jest w danej bazie, baza zostaje pominięta (żeby nie stracić wcześniejszej kopii). Przy powtórnym uruchomieniu zmień @TabelaKopii (np. na inną datę).
  • Jeśli liczba rekordów w kopii nie zgadza się z liczbą do zmiany albo z liczbą faktycznie zmienionych — skrypt przerywa i wycofuje zmianę w tej bazie.

To nie zastępuje pełnego backupu bazy, ale bardzo ułatwia późniejszą weryfikację.

Zanim uruchomisz

Symfonia w swojej instrukcji również zaleca wykonanie kopii bezpieczeństwa bazy oraz zamknięcie aplikacji na wszystkich stanowiskach przed wykonaniem poprawki. Ja robię to samo. Przed uruchomieniem:

  • wykonaj backup baz,
  • zamknij Symfonię na stanowiskach,
  • najpierw uruchom skrypt z @Tryb = 'ROLLBACK',
  • sprawdź wynik,
  • dopiero później zmień na @Tryb = 'COMMIT'.

Jak uruchomić w SQL Server Management Studio

  1. Uruchom SSMS i połącz się z serwerem, na którym stoją bazy Symfonii.
  2. Kliknij New Query / Nowe zapytanie.
  3. Wklej skrypt (poniżej).
  4. Ustaw sekcję USTAWIENIA — zostaw @Tryb = 'ROLLBACK' na pierwszy przebieg.
  5. Naciśnij F5 albo kliknij Execute / Wykonaj.
  6. Sprawdź raport na dole okna.
  7. Jeżeli wszystko wygląda prawidłowo, ustaw @Tryb = 'COMMIT' i uruchom ponownie.

Skrypt dla wielu baz

⬇ Pobierz gotowy skrypt (.sql)

USE [master];
GO

SET NOCOUNT ON;
SET XACT_ABORT ON;

/* ============================================================
   USTAWIENIA — ZMIENIASZ TYLKO TUTAJ
   ============================================================ */

DECLARE @NowyZnacznik nvarchar(100) = N'0';

-- ROLLBACK = test, nic nie zostanie zapisane
-- COMMIT   = utworzenie kopii i zapisanie zmian
DECLARE @Tryb varchar(8) = 'ROLLBACK';

-- Nazwa tabeli kopii tworzonej w schemacie FK
DECLARE @TabelaKopii sysname = N'dokumenty_backup_20260806';

/* ============================================================
   WALIDACJA TRYBU
   ============================================================ */

SET @Tryb = UPPER(LTRIM(RTRIM(@Tryb)));

IF @Tryb NOT IN ('COMMIT', 'ROLLBACK')
BEGIN
    THROW 50000,
        N'Nieprawidłowy tryb. Ustaw @Tryb na COMMIT albo ROLLBACK.',
        1;
END;

DECLARE @CzyCommit bit =
    CASE
        WHEN @Tryb = 'COMMIT' THEN 1
        ELSE 0
    END;

/* ============================================================
   TABELA WYNIKOWA
   ============================================================ */

DROP TABLE IF EXISTS #FK_Wyniki;

CREATE TABLE #FK_Wyniki
(
    BazaDanych         sysname         NULL,
    RekordyDoZmiany    bigint          NOT NULL,
    RekordySkopiowane  bigint          NOT NULL,
    RekordyZmienione   bigint          NOT NULL,

    -- 0 = brak zmian
    -- 1 = wykonano poprawnie
    -- 2 = pominięto
    -- 3 = błąd
    StatusKod          tinyint         NOT NULL,

    StatusOpis         nvarchar(1000)   NULL
);

/* ============================================================
   ZMIENNE KURSORA
   ============================================================ */

DECLARE @Baza sysname;
DECLARE @sql nvarchar(max);
DECLARE @TabelaKopiiQuoted nvarchar(258);

SET @TabelaKopiiQuoted = QUOTENAME(@TabelaKopii);

/* ============================================================
   LISTA BAZ
   ============================================================ */

DECLARE kursor_baz CURSOR LOCAL FAST_FORWARD FOR
SELECT [name]
FROM sys.databases
WHERE database_id > 4
  AND state_desc = N'ONLINE'
  AND is_read_only = 0
  AND source_database_id IS NULL
  AND HAS_DBACCESS([name]) = 1
ORDER BY [name];

OPEN kursor_baz;
FETCH NEXT FROM kursor_baz INTO @Baza;

/* ============================================================
   PRZEJŚCIE PO BAZACH
   ============================================================ */

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @sql = N'
USE ' + QUOTENAME(@Baza) + N';

DECLARE @DoZmiany bigint = 0;
DECLARE @Skopiowano bigint = 0;
DECLARE @Zmieniono bigint = 0;

BEGIN TRY

    /* Sprawdzenie tabeli oraz wymaganych kolumn */
    IF OBJECT_ID(N''[FK].[dokumenty]'', N''U'') IS NULL
       OR COL_LENGTH(N''[FK].[dokumenty]'', N''zrodlo'') IS NULL
       OR COL_LENGTH(N''[FK].[dokumenty]'', N''znacznik'') IS NULL
    BEGIN
        INSERT INTO #FK_Wyniki
        (
            BazaDanych,
            RekordyDoZmiany,
            RekordySkopiowane,
            RekordyZmienione,
            StatusKod,
            StatusOpis
        )
        VALUES
        (
            DB_NAME(),
            0,
            0,
            0,
            2,
            N''Pominięto: brak tabeli FK.dokumenty lub wymaganych kolumn''
        );
    END
    ELSE
    BEGIN
        /* Policzenie rekordów do zmiany */
        SELECT @DoZmiany = COUNT_BIG(*)
        FROM [FK].[dokumenty]
        WHERE [zrodlo] = 2
          AND
          (
              [znacznik] IS NULL
              OR LTRIM(RTRIM(CONVERT(nvarchar(4000), [znacznik]))) = N''''
          );

        IF @DoZmiany = 0
        BEGIN
            INSERT INTO #FK_Wyniki
            (
                BazaDanych,
                RekordyDoZmiany,
                RekordySkopiowane,
                RekordyZmienione,
                StatusKod,
                StatusOpis
            )
            VALUES
            (
                DB_NAME(),
                0,
                0,
                0,
                0,
                N''Brak rekordów do zmiany''
            );
        END
        ELSE
        BEGIN
            /*
                Jeśli tabela kopii już istnieje, nie wykonujemy UPDATE.
                Zapobiega to nadpisaniu wcześniejszej kopii.
            */
            IF OBJECT_ID(
                N''[FK].' + REPLACE(@TabelaKopiiQuoted, '''', '''''') + N''',
                N''U''
            ) IS NOT NULL
            BEGIN
                INSERT INTO #FK_Wyniki
                (
                    BazaDanych,
                    RekordyDoZmiany,
                    RekordySkopiowane,
                    RekordyZmienione,
                    StatusKod,
                    StatusOpis
                )
                VALUES
                (
                    DB_NAME(),
                    @DoZmiany,
                    0,
                    0,
                    2,
                    N''Pominięto: tabela kopii już istnieje''
                );
            END
            ELSE
            BEGIN
                BEGIN TRANSACTION;

                /* Kopia wyłącznie rekordów, które będą zmieniane */
                SELECT *
                INTO [FK].' + @TabelaKopiiQuoted + N'
                FROM [FK].[dokumenty]
                WHERE [zrodlo] = 2
                  AND
                  (
                      [znacznik] IS NULL
                      OR LTRIM(RTRIM(CONVERT(nvarchar(4000), [znacznik]))) = N''''
                  );

                /* Policzenie rekordów w kopii */
                SELECT @Skopiowano = COUNT_BIG(*)
                FROM [FK].' + @TabelaKopiiQuoted + N';

                IF @Skopiowano <> @DoZmiany
                BEGIN
                    THROW 50001,
                        N''Liczba rekordów w kopii nie zgadza się z liczbą rekordów do zmiany.'',
                        1;
                END;

                /* Aktualizacja */
                UPDATE [FK].[dokumenty]
                SET [znacznik] = @pNowyZnacznik
                WHERE [zrodlo] = 2
                  AND
                  (
                      [znacznik] IS NULL
                      OR LTRIM(RTRIM(CONVERT(nvarchar(4000), [znacznik]))) = N''''
                  );

                SET @Zmieniono = @@ROWCOUNT;

                IF @Zmieniono <> @Skopiowano
                BEGIN
                    THROW 50002,
                        N''Liczba zmienionych rekordów nie zgadza się z liczbą rekordów w kopii.'',
                        1;
                END;

                /* @pCzyCommit jest typu bit — brak konfliktu collation */
                IF @pCzyCommit = 1
                BEGIN
                    COMMIT TRANSACTION;

                    INSERT INTO #FK_Wyniki
                    (
                        BazaDanych,
                        RekordyDoZmiany,
                        RekordySkopiowane,
                        RekordyZmienione,
                        StatusKod,
                        StatusOpis
                    )
                    VALUES
                    (
                        DB_NAME(),
                        @DoZmiany,
                        @Skopiowano,
                        @Zmieniono,
                        1,
                        N''OK: kopia utworzona, zmiany zapisane''
                    );
                END
                ELSE
                BEGIN
                    ROLLBACK TRANSACTION;

                    INSERT INTO #FK_Wyniki
                    (
                        BazaDanych,
                        RekordyDoZmiany,
                        RekordySkopiowane,
                        RekordyZmienione,
                        StatusKod,
                        StatusOpis
                    )
                    VALUES
                    (
                        DB_NAME(),
                        @DoZmiany,
                        @Skopiowano,
                        @Zmieniono,
                        1,
                        N''TEST OK: zmiana i tabela kopii zostały wycofane''
                    );
                END;
            END;
        END;
    END;

END TRY
BEGIN CATCH

    IF XACT_STATE() <> 0
        ROLLBACK TRANSACTION;

    INSERT INTO #FK_Wyniki
    (
        BazaDanych,
        RekordyDoZmiany,
        RekordySkopiowane,
        RekordyZmienione,
        StatusKod,
        StatusOpis
    )
    VALUES
    (
        DB_NAME(),
        ISNULL(@DoZmiany, 0),
        ISNULL(@Skopiowano, 0),
        ISNULL(@Zmieniono, 0),
        3,
        N''BŁĄD: '' + ERROR_MESSAGE()
    );

END CATCH;
';

    EXEC sys.sp_executesql
        @sql,
        N'
            @pNowyZnacznik nvarchar(100),
            @pCzyCommit bit
        ',
        @pNowyZnacznik = @NowyZnacznik,
        @pCzyCommit = @CzyCommit;

    FETCH NEXT FROM kursor_baz INTO @Baza;
END;

CLOSE kursor_baz;
DEALLOCATE kursor_baz;

/* ============================================================
   WYNIKI — BAZY Z REKORDAMI LUB BŁĘDAMI
   ============================================================ */

SELECT
    BazaDanych,
    RekordyDoZmiany,
    RekordySkopiowane,
    RekordyZmienione,
    CASE StatusKod
        WHEN 0 THEN N'Brak zmian'
        WHEN 1 THEN N'OK'
        WHEN 2 THEN N'Pominięto'
        WHEN 3 THEN N'Błąd'
    END AS Status,
    StatusOpis
FROM #FK_Wyniki
WHERE RekordyDoZmiany > 0
   OR StatusKod = 3
ORDER BY
    RekordyDoZmiany DESC,
    BazaDanych;

/* ============================================================
   PODSUMOWANIE
   ============================================================ */

SELECT
    @NowyZnacznik AS NowaWartoscZnacznika,
    @Tryb AS TrybWykonania,
    SUM(RekordyDoZmiany) AS RazemRekordowDoZmiany,
    SUM(RekordySkopiowane) AS RazemSkopiowano,
    SUM(RekordyZmienione) AS RazemZmieniono,
    SUM(CASE WHEN StatusKod = 1 THEN 1 ELSE 0 END)
        AS LiczbaPrzetworzonychBaz,
    SUM(CASE WHEN StatusKod = 2 THEN 1 ELSE 0 END)
        AS LiczbaPominietychBaz,
    SUM(CASE WHEN StatusKod = 3 THEN 1 ELSE 0 END)
        AS LiczbaBledow
FROM #FK_Wyniki;

/* ============================================================
   BŁĘDY I POMINIĘTE BAZY
   ============================================================ */

SELECT
    BazaDanych,
    StatusOpis
FROM #FK_Wyniki
WHERE StatusKod IN (2, 3)
ORDER BY BazaDanych;

Oficjalna poprawka Symfonii

Opis problemu i oryginalna poprawka producenta: F50/FKF 2026.3 — Błąd zapisu do bufora. Nieprawidłowy format ciągu wejściowego.

Na koniec

Jeżeli masz jedną bazę, skrypt producenta w zupełności wystarczy. Ale jeśli na jednej instancji SQL trzymasz kilkadziesiąt lub kilkaset baz Symfonii, wygodniej zrobić to raz, centralnie — z raportem i z możliwością wcześniejszego testu przez ROLLBACK.

Coś u Ciebie zjada czas co miesiąc, a wygląda na „program tego nie robi"? Napisz do mnie — zwykle da się dorobić przycisk.

Masz pytanie do tego wpisu?

Napisz w dwóch zdaniach, o co chodzi. Nie musisz znać technicznej nazwy problemu — dopytam o resztę. Pierwsze rozeznanie jest bezpłatne.