Przejdź do treści
Poradnik Programy księgowe

Symfonia FK 2026.3: dokument jest w rejestrze VAT, ale nie trafia do JPK_V7

Dokument widać w rejestrze VAT, a mimo to nie wchodzi do deklaracji i pliku JPK_V7. Symfonia FK 2026.3 ma na to poprawkę procedur SQL — pokazuję, jak rozłożyć ją na kilkadziesiąt baz z jednej bazy wzorcowej, z raportem OK / POMINIĘTO / BŁĄD dla każdej.

Komu to pomoże?

Tobie, jeśli obsługujesz wiele firm w Symfonii FK 2026.3 na jednym SQL Serverze i trafiłeś na fakturę, która jest widoczna w rejestrze VAT, ale nie pokazuje się w JPK_V7. Poprawkę producenta trzeba wgrać na każdą bazę z osobna — a przy kilkudziesięciu klientach to całe popołudnie klikania.

Kogo to nie dotyczy?

Masz jedną bazę? Wykonaj po prostu oryginalny skrypt Symfonii i tyle — ten wpis nic Ci nie doda. On jest o robieniu tej samej poprawki hurtem, na wielu bazach naraz.

Na czym polega problem

Dokument jest w rejestrze VAT, ale nie zostaje uwzględniony w deklaracji i w wygenerowanym pliku JPK_V7. Symfonia opisuje to dla faktur, którym ręcznie zmieniono okres rejestru VAT na wcześniejszy niż wynika z daty dokumentu. Taka faktura potrafi być widoczna w rejestrze sprzedaży, a mimo to wypaść z naliczania deklaracji i z JPK_V7.

Producent udostępnił skrypt sla13565_2.sql, który aktualizuje odpowiednie procedury SQL.

Tu nie zmieniamy rekordów — zmieniamy procedury

I to jest kluczowa różnica w stosunku do zwykłych poprawek. Tu nie ruszamy pojedynczych dokumentów w tabeli, tylko definicje trzech procedur SQL. Dlatego nie da się tego zrobić prostym UPDATE po bazach — każda baza musi dostać nową definicję procedury.

Rozwiązuję to przez bazę wzorcową. Brzmi poważnie, a chodzi po prostu o jedną zwykłą bazę Symfonii, na której najpierw wykonuję oryginalną poprawkę producenta ręcznie. Kiedy ta jedna baza działa poprawnie, mój skrypt kopiuje z niej trzy poprawione procedury na wszystkie pozostałe bazy.

Krok 1 — przygotuj bazę wzorcową

Wybierz jedną bazę (np. FirmaA) i wykonaj na niej oryginalny skrypt Symfonii sla13565_2.sql. Jeżeli SQL Server zwróci Commands completed successfully. i po sprawdzeniu wszystko działa — ta baza staje się Twoją bazą wzorcową.

Po wykonaniu poprawki warto sprawdzić, czy zmieniły się trzy procedury:

FK.fk_rp_zestrej
FK.fk_rp_zestrej2
FK.fk_Jpk_GetZestRej2

Szybki podgląd dat modyfikacji:

SELECT
    s.name AS Schemat,
    p.name AS Procedura,
    p.modify_date
FROM sys.procedures AS p
JOIN sys.schemas AS s
    ON s.schema_id = p.schema_id
WHERE s.name = N'FK'
  AND p.name IN
  (
      N'fk_rp_zestrej',
      N'fk_rp_zestrej2',
      N'fk_Jpk_GetZestRej2'
  )
ORDER BY p.name;

Trzy procedury ze świeżą datą modify_date = poprawka się zapisała. Sam fakt aktualizacji to jednak nie wszystko — wygeneruj JPK_V7 dla przypadku, który wcześniej był błędny, i sprawdź, czy dokument już wchodzi.

Krok 2 — rozłóż poprawkę na pozostałe bazy

Teraz mój skrypt. Podajesz w nim tylko nazwę bazy wzorcowej, a on:

  • pobiera definicje trzech poprawionych procedur z tej bazy,
  • przechodzi po pozostałych bazach na serwerze,
  • sprawdza, czy mają wymagany schemat FK, typy i te procedury,
  • aktualizuje procedury w transakcji (albo wszystkie trzy, albo żadna),
  • na końcu pokazuje raport dla każdej bazy.

Nie trzeba ręcznie kopiować kilku stron kodu SQL do dynamicznych zapytań — definicje są pobierane prosto z bazy wzorcowej.

Ustawiasz jedną rzecz na górze skryptu:

DECLARE @BazaWzorcowa sysname = N'TWOJA_BAZA_WZORCOWA';

I uruchamiasz. Cały skrypt:

⬇ Pobierz gotowy skrypt (.sql)

USE [master];
GO

SET NOCOUNT ON;
SET XACT_ABORT ON;

/* ============================================================
   USTAWIENIA
   ============================================================ */

-- Baza, w której procedury mają już poprawną definicję
DECLARE @BazaWzorcowa sysname = N'TWOJA_BAZA_WZORCOWA';

-- 1 = pomiń bazę wzorcową
-- 0 = wykonaj ALTER również w bazie wzorcowej
DECLARE @PominBazeWzorcowa bit = 1;

/* ============================================================
   WALIDACJA BAZY WZORCOWEJ
   ============================================================ */

IF DB_ID(@BazaWzorcowa) IS NULL
BEGIN
    THROW 50000, N'Podana baza wzorcowa nie istnieje.', 1;
END;

IF DATABASEPROPERTYEX(@BazaWzorcowa, 'Status') <> 'ONLINE'
BEGIN
    THROW 50001, N'Baza wzorcowa nie jest dostępna w trybie ONLINE.', 1;
END;

/* ============================================================
   POBRANIE DEFINICJI PROCEDUR Z BAZY WZORCOWEJ
   ============================================================ */

DECLARE @Def1 nvarchar(max);
DECLARE @Def2 nvarchar(max);
DECLARE @Def3 nvarchar(max);
DECLARE @Sql nvarchar(max);

SET @Sql = N'
USE ' + QUOTENAME(@BazaWzorcowa) + N';

SELECT
    @pDef1 = OBJECT_DEFINITION(
        OBJECT_ID(N''[FK].[fk_rp_zestrej]'', N''P'')
    ),
    @pDef2 = OBJECT_DEFINITION(
        OBJECT_ID(N''[FK].[fk_rp_zestrej2]'', N''P'')
    ),
    @pDef3 = OBJECT_DEFINITION(
        OBJECT_ID(N''[FK].[fk_Jpk_GetZestRej2]'', N''P'')
    );
';

EXEC sys.sp_executesql
    @Sql,
    N'
        @pDef1 nvarchar(max) OUTPUT,
        @pDef2 nvarchar(max) OUTPUT,
        @pDef3 nvarchar(max) OUTPUT
    ',
    @pDef1 = @Def1 OUTPUT,
    @pDef2 = @Def2 OUTPUT,
    @pDef3 = @Def3 OUTPUT;

/* ============================================================
   SPRAWDZENIE, CZY PROCEDURY ISTNIEJĄ W BAZIE WZORCOWEJ
   ============================================================ */

IF @Def1 IS NULL
BEGIN
    THROW 50002,
        N'W bazie wzorcowej nie znaleziono [FK].[fk_rp_zestrej].',
        1;
END;

IF @Def2 IS NULL
BEGIN
    THROW 50003,
        N'W bazie wzorcowej nie znaleziono [FK].[fk_rp_zestrej2].',
        1;
END;

IF @Def3 IS NULL
BEGIN
    THROW 50004,
        N'W bazie wzorcowej nie znaleziono [FK].[fk_Jpk_GetZestRej2].',
        1;
END;

/* ============================================================
   ZAMIANA CREATE NA ALTER, GDYBY DEFINICJA BYŁA ZAPISANA JAKO CREATE
   ============================================================ */

DECLARE @Pos int;

/* Procedura 1 */

SET @Pos = PATINDEX(
    '%CREATE OR ALTER PROCEDURE%',
    UPPER(@Def1)
);

IF @Pos > 0
BEGIN
    SET @Def1 =
        STUFF(
            @Def1,
            @Pos,
            LEN('CREATE OR ALTER PROCEDURE'),
            'ALTER PROCEDURE'
        );
END
ELSE
BEGIN
    SET @Pos = PATINDEX(
        '%CREATE PROCEDURE%',
        UPPER(@Def1)
    );

    IF @Pos > 0
    BEGIN
        SET @Def1 =
            STUFF(
                @Def1,
                @Pos,
                LEN('CREATE PROCEDURE'),
                'ALTER PROCEDURE'
            );
    END
    ELSE
    BEGIN
        SET @Pos = PATINDEX(
            '%CREATE PROC%',
            UPPER(@Def1)
        );

        IF @Pos > 0
        BEGIN
            SET @Def1 =
                STUFF(
                    @Def1,
                    @Pos,
                    LEN('CREATE PROC'),
                    'ALTER PROCEDURE'
                );
        END;
    END;
END;

/* Procedura 2 */

SET @Pos = PATINDEX(
    '%CREATE OR ALTER PROCEDURE%',
    UPPER(@Def2)
);

IF @Pos > 0
BEGIN
    SET @Def2 =
        STUFF(
            @Def2,
            @Pos,
            LEN('CREATE OR ALTER PROCEDURE'),
            'ALTER PROCEDURE'
        );
END
ELSE
BEGIN
    SET @Pos = PATINDEX(
        '%CREATE PROCEDURE%',
        UPPER(@Def2)
    );

    IF @Pos > 0
    BEGIN
        SET @Def2 =
            STUFF(
                @Def2,
                @Pos,
                LEN('CREATE PROCEDURE'),
                'ALTER PROCEDURE'
            );
    END
    ELSE
    BEGIN
        SET @Pos = PATINDEX(
            '%CREATE PROC%',
            UPPER(@Def2)
        );

        IF @Pos > 0
        BEGIN
            SET @Def2 =
                STUFF(
                    @Def2,
                    @Pos,
                    LEN('CREATE PROC'),
                    'ALTER PROCEDURE'
                );
        END;
    END;
END;

/* Procedura 3 */

SET @Pos = PATINDEX(
    '%CREATE OR ALTER PROCEDURE%',
    UPPER(@Def3)
);

IF @Pos > 0
BEGIN
    SET @Def3 =
        STUFF(
            @Def3,
            @Pos,
            LEN('CREATE OR ALTER PROCEDURE'),
            'ALTER PROCEDURE'
        );
END
ELSE
BEGIN
    SET @Pos = PATINDEX(
        '%CREATE PROCEDURE%',
        UPPER(@Def3)
    );

    IF @Pos > 0
    BEGIN
        SET @Def3 =
            STUFF(
                @Def3,
                @Pos,
                LEN('CREATE PROCEDURE'),
                'ALTER PROCEDURE'
            );
    END
    ELSE
    BEGIN
        SET @Pos = PATINDEX(
            '%CREATE PROC%',
            UPPER(@Def3)
        );

        IF @Pos > 0
        BEGIN
            SET @Def3 =
                STUFF(
                    @Def3,
                    @Pos,
                    LEN('CREATE PROC'),
                    'ALTER PROCEDURE'
                );
        END;
    END;
END;

/* ============================================================
   TABELA WYNIKÓW
   ============================================================ */

DROP TABLE IF EXISTS #WynikiAktualizacjiProcedur;

CREATE TABLE #WynikiAktualizacjiProcedur
(
    BazaDanych nvarchar(128) COLLATE DATABASE_DEFAULT,
    StatusOperacji nvarchar(20) COLLATE DATABASE_DEFAULT,
    Opis nvarchar(4000) COLLATE DATABASE_DEFAULT
);

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

DECLARE @Baza sysname;
DECLARE @SqlBaza nvarchar(max);

DECLARE KursorBaz CURSOR LOCAL FAST_FORWARD FOR
SELECT d.name
FROM sys.databases AS d
WHERE d.database_id > 4
  AND d.state_desc = N'ONLINE'
  AND d.is_read_only = 0
  AND d.source_database_id IS NULL
  AND HAS_DBACCESS(d.name) = 1
  AND (
        @PominBazeWzorcowa = 0
        OR d.name <> @BazaWzorcowa
      )
ORDER BY d.name;

OPEN KursorBaz;

FETCH NEXT FROM KursorBaz INTO @Baza;

WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        /*
          Najpierw sprawdzamy, czy w bazie istnieją:
          - schemat FK,
          - typy FK.tData i FK.tString,
          - trzy procedury przeznaczone do ALTER.
        */

        SET @SqlBaza = N'
USE ' + QUOTENAME(@Baza) + N';

DECLARE @Braki nvarchar(2000) = N'''';

IF SCHEMA_ID(N''FK'') IS NULL
    SET @Braki += N''Brak schematu FK; '';

IF TYPE_ID(N''[FK].[tData]'') IS NULL
    SET @Braki += N''Brak typu FK.tData; '';

IF TYPE_ID(N''[FK].[tString]'') IS NULL
    SET @Braki += N''Brak typu FK.tString; '';

IF OBJECT_ID(N''[FK].[fk_rp_zestrej]'', N''P'') IS NULL
    SET @Braki += N''Brak procedury FK.fk_rp_zestrej; '';

IF OBJECT_ID(N''[FK].[fk_rp_zestrej2]'', N''P'') IS NULL
    SET @Braki += N''Brak procedury FK.fk_rp_zestrej2; '';

IF OBJECT_ID(N''[FK].[fk_Jpk_GetZestRej2]'', N''P'') IS NULL
    SET @Braki += N''Brak procedury FK.fk_Jpk_GetZestRej2; '';

SELECT @pBraki = @Braki;
';

        DECLARE @Braki nvarchar(2000);

        EXEC sys.sp_executesql
            @SqlBaza,
            N'@pBraki nvarchar(2000) OUTPUT',
            @pBraki = @Braki OUTPUT;

        IF ISNULL(@Braki, N'') <> N''
        BEGIN
            INSERT INTO #WynikiAktualizacjiProcedur
            (
                BazaDanych,
                StatusOperacji,
                Opis
            )
            VALUES
            (
                @Baza,
                N'POMINIĘTO',
                @Braki
            );

            FETCH NEXT FROM KursorBaz INTO @Baza;
            CONTINUE;
        END;

        /*
          Transakcja obejmuje wszystkie trzy ALTER PROCEDURE.
          Jeżeli jedna procedura się nie zmieni, całość dla danej
          bazy zostanie wycofana.
        */

        BEGIN TRANSACTION;

        /*
          Każda instrukcja ALTER PROCEDURE jest wykonywana jako
          osobny batch. Jest to konieczne, ponieważ ALTER PROCEDURE
          musi być pierwszą instrukcją w swoim batchu.
        */

        SET @SqlBaza = N'
USE ' + QUOTENAME(@Baza) + N';
EXEC sys.sp_executesql @pDefinicja;
';

        EXEC sys.sp_executesql
            @SqlBaza,
            N'@pDefinicja nvarchar(max)',
            @pDefinicja = @Def1;

        EXEC sys.sp_executesql
            @SqlBaza,
            N'@pDefinicja nvarchar(max)',
            @pDefinicja = @Def2;

        EXEC sys.sp_executesql
            @SqlBaza,
            N'@pDefinicja nvarchar(max)',
            @pDefinicja = @Def3;

        COMMIT TRANSACTION;

        INSERT INTO #WynikiAktualizacjiProcedur
        (
            BazaDanych,
            StatusOperacji,
            Opis
        )
        VALUES
        (
            @Baza,
            N'OK',
            N'Zaktualizowano wszystkie trzy procedury.'
        );
    END TRY
    BEGIN CATCH
        IF XACT_STATE() <> 0
            ROLLBACK TRANSACTION;

        INSERT INTO #WynikiAktualizacjiProcedur
        (
            BazaDanych,
            StatusOperacji,
            Opis
        )
        VALUES
        (
            @Baza,
            N'BŁĄD',
            N'Błąd '
            + CONVERT(nvarchar(20), ERROR_NUMBER())
            + N', linia '
            + CONVERT(nvarchar(20), ERROR_LINE())
            + N': '
            + ERROR_MESSAGE()
        );
    END CATCH;

    FETCH NEXT FROM KursorBaz INTO @Baza;
END;

CLOSE KursorBaz;
DEALLOCATE KursorBaz;

/* ============================================================
   WYNIKI
   ============================================================ */

SELECT
    BazaDanych,
    StatusOperacji,
    Opis
FROM #WynikiAktualizacjiProcedur
ORDER BY
    CASE StatusOperacji
        WHEN N'BŁĄD' THEN 1
        WHEN N'POMINIĘTO' THEN 2
        ELSE 3
    END,
    BazaDanych;

/* Podsumowanie */

SELECT
    SUM(CASE WHEN StatusOperacji = N'OK' THEN 1 ELSE 0 END)
        AS LiczbaBazZaktualizowanych,

    SUM(CASE WHEN StatusOperacji = N'POMINIĘTO' THEN 1 ELSE 0 END)
        AS LiczbaBazPominietych,

    SUM(CASE WHEN StatusOperacji = N'BŁĄD' THEN 1 ELSE 0 END)
        AS LiczbaBledow
FROM #WynikiAktualizacjiProcedur;
GO

Co jeśli któraś baza ma problem

Aktualizacja trzech procedur dla każdej bazy idzie w transakcji. Jeżeli wszystkie trzy zaktualizują się poprawnie — zmiany zostają zatwierdzone. Jeżeli któraś operacja padnie — zmiany dla tej konkretnej bazy są wycofywane, a pozostałe bazy lecą dalej.

Na końcu dostajesz zestawienie, np.:

Baza        Status
FirmaB      OK
FirmaC      OK
FirmaD      POMINIĘTO
FirmaE      BŁĄD

Od razu widać, które bazy wymagają ręcznego sprawdzenia. POMINIĘTO to zwykle baza, która nie ma schematu FK albo tych procedur (np. baza niebędąca bazą FK) — skrypt jej nie rusza.

Zanim uruchomisz

Tak samo jak przy każdej poprawce SQL, producent zaleca kopię bezpieczeństwa oraz zamknięcie Symfonii na wszystkich stanowiskach przed wykonaniem. Ja robię to samo.

Kolejność, której się trzymam:

  • wykonaj backup baz,
  • wybierz jedną bazę do testu,
  • wykonaj na niej oryginalną poprawkę Symfonii (sla13565_2.sql),
  • sprawdź raport JPK_V7 dla wcześniej błędnego dokumentu,
  • dopiero wtedy użyj tej bazy jako wzorcowej w moim skrypcie.

Jak uruchomić w SQL Server Management Studio

  1. Najpierw na jednej bazie wykonaj oficjalny skrypt Symfonii.
  2. Otwórz SSMS i połącz się z serwerem, na którym stoją bazy Symfonii.
  3. Kliknij New Query / Nowe zapytanie.
  4. Wklej mój skrypt do masowej aktualizacji.
  5. Wpisz nazwę przygotowanej bazy wzorcowej:
DECLARE @BazaWzorcowa sysname = N'TWOJA_BAZA_WZORCOWA';
  1. Naciśnij F5.
  2. Po zakończeniu sprawdź zestawienie wyników (OK / POMINIĘTO / BŁĄD).

Oficjalna poprawka Symfonii

Opis problemu i skrypt producenta: Dokument widoczny w rejestrze VAT nie jest uwzględniony w raporcie JPK_V7 — poprawka.

Na koniec

Przy jednej bazie najprościej wykonać poprawkę dokładnie zgodnie z instrukcją Symfonii. Ale gdy na jednym serwerze siedzi kilkadziesiąt baz klientów, przygotowanie jednej sprawdzonej bazy wzorcowej i skopiowanie z niej procedur na resztę jest dużo szybsze niż wklepywanie tego samego ALTER PROCEDURE kilkadziesiąt razy.

Najważniejsza jest kolejność:

backup → poprawka na jednej bazie → test → baza wzorcowa → masowa aktualizacja reszty.

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.