# DfProforma Shopware: oferty pro forma z akceptacją klienta i automatyczną konwersją

> Co robi DfProforma Shopware 6.7 potrafi wystawiać faktury, dokumenty dostawy i korekty, ale nie oferty pro forma. Tymczasem w niemal każdym kontekście B2B (sprzęt przemysłowy, usługi dla firm, zakupy publiczne,…

- Strona: <https://www.datafirefly.com/pl/documentation/dfproforma-devis-shopware/>
- Język: pl
- Zaktualizowano: 2026-10-10
- Inne języki: [fr](https://www.datafirefly.com/documentation/dfproforma-devis-shopware/index.md), [en](https://www.datafirefly.com/en/documentation/dfproforma-shopware-pro-forma-quote/index.md), [es](https://www.datafirefly.com/es/documentation/dfproforma-shopware-presupuesto-proforma/index.md), [de](https://www.datafirefly.com/de/documentation/dfproforma-shopware-proforma-angebot/index.md), [it](https://www.datafirefly.com/it/documentation/dfproforma-shopware-preventivo-proforma/index.md), [nl](https://www.datafirefly.com/nl/documentation/dfproforma-devis-shopware/index.md), [pt](https://www.datafirefly.com/pt/documentation/dfproforma-devis-shopware/index.md)
- Indeks: <https://www.datafirefly.com/pl/documentation/llms.txt>

## Co robi DfProforma

Shopware 6.7 potrafi wystawiać faktury, dokumenty dostawy i korekty, ale nie oferty pro forma. Tymczasem w niemal każdym kontekście B2B (sprzęt przemysłowy, usługi dla firm, zakupy publiczne, sprzedaż w trybie przetargowym) klient musi otrzymać formalny dokument, który akceptuje _zanim_ zamówienie stanie się wiążące.

DfProforma wypełnia tę lukę bez prowizorki: prawdziwy natywny typ dokumentu Shopware `df_proforma`, własny zakres numeracji `PF{n}`, własny brandowany szablon PDF w Twigu, samodzielny workflow akceptacji klienta z publicznym adresem URL podpisanym HMAC-SHA256 oraz automatyczna konwersja w zamówienie, gdy tylko powiązana transakcja przejdzie w stan opłaconej.

**W skrócie:** handlowiec generuje ofertę z karty zamówienia w administracji, wysyła ją e-mailem, klient klika link, akceptuje online bez konta Shopware, płaci, a oferta automatycznie przechodzi w status _Skonwertowana_. Żadnego ręcznego kliknięcia po płatności.

## Wymagania

- **Shopware** 6.7.0 lub nowszy (plugin nie jest wstecznie zgodny z 6.6 z powodu przebudowy systemu dokumentów)
- **PHP** minimum 8.2
- **MySQL** 8.0+ lub **MariaDB** 10.6+
- Aktywne workery wiadomości Shopware (zalecane do automatycznej obsługi wygaśnięć)
- Skonfigurowany serwis mail Shopware (działający SMTP dla wysyłek transakcyjnych)

## Instalacja

### 1. Przesłanie pluginu

W administracji Shopware przejdź do _Rozszerzenia → Moje rozszerzenia → Prześlij rozszerzenie_ i wybierz plik `DfProforma-1.0.6.zip`.

### 2. Instalacja i aktywacja

Na liście rozszerzeń kliknij _Zainstaluj_, a następnie _Aktywuj_. Migracje Shopware wykonują się automatycznie i tworzą:

- Tabelę SQL `df_proforma` i jej indeksy na `order_id`, `status`, `public_token`
- Typ dokumentu `df_proforma` w `document_type`
- Zakres numeracji `document_df_proforma` w formacie `PF{n}`, konfigurowalny
- Podstawowy transakcyjny szablon e-mail dla generowania, wysyłki i akceptacji

### 3. Rekompilacja administracji

Plugin dostarcza moduł Vite dla administracji, który rozszerza kartę zamówienia (`sw-order-detail-base`). Trzeba przekompilować globalny bundle administracji, aby pojawiła się zakładka _Pro forma_:

```
bin/build-administration.sh
bin/console cache:clear
```

**Pominięcie tego kroku** to pierwsza przyczyna zgłoszeń "zakładka Pro forma nie pojawia się na karcie zamówienia": pamiętaj o rekompilacji po każdej aktualizacji pluginu.

## Konfiguracja

### Parametry globalne

W _Rozszerzenia → Moje rozszerzenia → DfProforma → Konfiguruj_ masz dostęp do następujących ustawień:

- **Domyślna ważność**: liczba dni, przez które link akceptacji pozostaje ważny (domyślnie 30). Każda oferta może nadpisać tę wartość indywidualnie.
- **Automatyczna konwersja przy zaksięgowaniu**: włączona domyślnie. Wyłącz ją, jeśli chcesz zachować ręczną kontrolę nad przejściem _Zaakceptowana → Skonwertowana_.
- **Nazwa nadawcy**: nazwa wyświetlana jako nadawca e-maili transakcyjnych (domyślnie nazwa sales-channelu).
- **Kolor akcentu marki w PDF**: kolor akcentu używany w dostarczonym szablonie PDF (pasek nagłówka, linia separacji, plakietka statusu).

### Konfiguracja per sales-channel

Powyższe parametry można nadpisać per sales-channel w _Ustawienia → Sales channels → [Twój kanał] → Konfiguracja pluginu_. Przydatne, gdy prowadzisz kilka sklepów z różnymi politykami ważności (na przykład 30 dni dla B2C, 60 dni dla B2B).

## Generowanie oferty pro forma

### Z karty zamówienia (administracja)

1. Otwórz zamówienie w _Zamówienia → Przegląd_
2. Kliknij zakładkę _Pro forma_ (obok _Dokumenty_)
3. Kliknij _Wygeneruj ofertę pro forma_
4. PDF zostaje utworzony, oferta pojawia się na liście z numerem `PF-…` i statusem _Szkic_

### Z API administracji

Udostępnione są trzy endpointy do zintegrowania generowania z Twoimi zewnętrznymi workflowami:

```
POST /api/_action/df-proforma/generate
Body: { "orderId": "…" }

POST /api/_action/df-proforma/mark-sent
Body: { "proformaId": "…" }

GET  /api/_action/df-proforma/by-order/{orderId}
```

Standardowe uwierzytelnianie OAuth2 administracji Shopware. Przydatne do podpięcia DfProforma pod zewnętrzny CRM albo pipeline automatyzacji.

## Wysyłka oferty do klienta

### E-mail transakcyjny

Z listy ofert (zakładka Pro forma na karcie zamówienia) kliknij ikonę koperty obok oferty. Moduł:

1. Przenosi ofertę w status _Wysłana_ (ze znacznikiem czasu)
2. Wysyła e-mail do klienta przez Mail Service Shopware, używając szablonu `df_proforma_sent`, w języku sales-channelu
3. Załącza PDF oferty i dołącza publiczny adres URL akceptacji
4. Emituje zdarzenie `ProformaGeneratedEvent` (wyzwalacz Flow Buildera)

### Publiczny adres URL akceptacji

Każda wysłana oferta ma adres URL postaci:

```
https://twoj-sklep.com/proforma/accept/{token}
```

Token jest szyfrowany i podpisany HMAC-SHA256 tajnym kluczem Shopware (`APP_SECRET` / `kernel.secret`). Nie da się go sfałszować ani odgadnąć. Adres URL wygasa po upływie ważności oferty (domyślnie 30 dni).

## Publiczna strona akceptacji klienta

Klient otwiera adres URL **bez wymaganego konta Shopware** (strona omija standardowe uwierzytelnianie konta klienta). Widzi:

- Czytelne podsumowanie zamówienia: pozycje, ceny, VAT, sumy, warunki
- Główny przycisk _Akceptuję tę ofertę_
- Drugorzędny przycisk _Odrzuć z podaniem powodu_
- Wyświetloną ważność ("Ważna do 20.06.2026")

Przy akceptacji:

- Podpis ze znacznikiem czasu z dokładnością do milisekundy zapisany w bazie
- Adres IP klienta zapisany jako dowód
- Status przechodzi na _Zaakceptowana_ z oznaczoną historią przejścia
- Zdarzenie `ProformaAcceptedEvent` wysłane do Flow Buildera

W przypadku odmowy pole _Powód_ jest obowiązkowe, co przydaje się Twoim handlowcom, którzy mogą skontaktować się z klientem z kontrpropozycją.

**Personalizacja:** strona akceptacji korzysta ze standardowych bloków Twig Storefrontu i dziedziczy Twój motyw. Możesz nadpisać `@DfProforma/storefront/page/account/proforma/quote.html.twig` ze swojego motywu.

## Workflow statusów

Sześć statusów pokrywa cały cykl życia oferty:

- **Szkic**: oferta utworzona, ale jeszcze niewysłana do klienta
- **Wysłana**: e-mail wysłany, oczekiwanie na odpowiedź klienta
- **Zaakceptowana**: klient kliknął _Akceptuję_ na publicznej stronie
- **Odrzucona**: klient kliknął _Odrzuć_ z podaniem powodu
- **Wygasła**: ważność przekroczona bez odpowiedzi (automatyczne przejście przez scheduled task)
- **Skonwertowana**: powiązane zamówienie opłacone, konwersja automatyczna

Każde przejście jest zapisywane z datą co do sekundy, identyfikatorem aktora, typem wyzwalacza (_handlowiec_, _klient_, _system_, _płatność_) oraz payloadem JSON na dowolne metadane. Możesz odtworzyć dokładną historię oferty w dowolnym momencie.

## Automatyczna konwersja przy zaksięgowaniu

Moduł rejestruje Subscriber na zdarzeniu `order_transaction.state.paid` maszyny stanów Shopware. Gdy transakcja przechodzi w _opłaconą_ (Stripe, przelew, PayPal itd.), Subscriber:

1. Szuka wszystkich ofert pro forma o statusie _Zaakceptowana_ powiązanych z zamówieniem
2. Przenosi je w status _Skonwertowana_
3. Oznacza historię przejścia typem wyzwalacza _płatność_

Żadnej interwencji człowieka, żadnego crona, żadnego opóźnienia. Twoje raporty handlowe pozostają spójne bez wysiłku.

Aby wyłączyć automatyczną konwersję (jeśli Twój zespół woli kontrolę ręczną), odznacz opcję w _Konfiguracja pluginu → Automatyczna konwersja przy zaksięgowaniu_.

## Flow Builder: zdarzenia Business Event

Moduł emituje dwa standardowe zdarzenia Shopware:

- `ProformaGeneratedEvent`: przy generowaniu oferty (implementuje `BusinessEventInterface`)
- `ProformaAcceptedEvent`: przy akceptacji przez klienta (implementuje `BusinessEventInterface`)

Oba pojawiają się automatycznie na liście wyzwalaczy natywnego Flow Buildera Shopware. Możesz podpiąć pod nie dowolną akcję Flow:

- Powiadomienie na Slacku dla zespołu sprzedaży przy akceptacji
- Wewnętrzny e-mail podsumowujący do odpowiedzialnego handlowca
- Webhook do Twojego CRM (HubSpot, Salesforce, Pipedrive…)
- Aktualizacja pola niestandardowego na kliencie (na przykład tag `quote-accepted`)
- Powiadomienie push na mobile przez usługę zewnętrzną

Nie jest potrzebna żadna ingerencja w kod modułu: wszystko konfiguruje się w _Ustawienia → Sklep → Flow Builder_.

## Personalizacja szablonu PDF

PDF oferty jest renderowany przez `DocumentFileRendererRegistry` (nowy system renderowania plików w Shopware 6.7), na podstawie szablonu Twig `@DfProforma/documents/proforma.html.twig` dostarczonego w module.

Aby go spersonalizować, utwórz własny plugin albo nadpisz go w swoim motywie, respektując standardową hierarchię szablonów Twig Shopware:

```
custom/plugins/YourTheme/src/Resources/views/documents/proforma.html.twig
```

Dostarczony szablon udostępnia następujące bloki Twig:

- Nagłówek z logo i danymi firmy
- Blok klienta
- Podsumowanie pozycji zamówienia
- Sumy netto/brutto z rozbiciem VAT
- Pasek podsumowania w stopce (numer PF, daty wystawienia i wygaśnięcia)
- Znak wodny _PRO FORMA_
- Konfigurowalny akcent marki przez `config.accentColor`

Zmienne dostępne w szablonie: `order` (OrderEntity wczytana ze wszystkimi asocjacjami), `config` (konfiguracja dokumentu, w tym `documentNumber`, `documentDate`, `validUntil`, `validityDays`), `context`.

## Wielojęzyczność

FR, EN, DE i ES są dostarczane domyślnie, snippety Storefrontu i administracji. Aby dodać kolejne języki, utwórz plik snippetów per locale w `src/Resources/snippet/`, zgodnie ze standardową konwencją Shopware.

Transakcyjne szablony e-mail również są wielojęzyczne: właściwy szablon jest wybierany automatycznie zgodnie z językiem sales-channelu klienta w momencie wysyłki. Szablony per język możesz personalizować w _Ustawienia → Sklep → Szablony e-mail_.

## API: dostępne endpointy administracji

```
POST   /api/_action/df-proforma/generate         # Generate a quote for an order
POST   /api/_action/df-proforma/mark-sent        # Mark as sent (manual transition)
GET    /api/_action/df-proforma/by-order/{id}    # List an order's quotes
```

Encje `df_proforma` są także dostępne przez standardowe API DAL Shopware (`/api/df-proforma`) do wyszukiwań, eksportów lub zaawansowanych integracji.

## Odinstalowanie

Domyślnie dane biznesowe są zachowywane przy odinstalowaniu (opcja _Keep User Data_ włączona). Zachowujesz w ten sposób historię audytową wystawionych ofert, przydatną dla zgodności i śledzenia handlowego.

Aby wymusić pełne usunięcie (tabela `df_proforma`, typ dokumentu, zakres numeracji, szablon e-mail), wyłącz opcję _Keep User Data_ w oknie odinstalowania.

**Zalecenie:** zachowaj dane domyślnie. Wymuszaj usunięcie tylko wtedy, gdy masz pewność, że nigdy nie będziesz potrzebować historii ofert z powodów handlowych, księgowych lub prawnych.

## Rozwiązywanie problemów

### Zakładka Pro forma nie pojawia się na karcie zamówienia

Bundle administracji nie został przekompilowany po instalacji. Uruchom `bin/build-administration.sh`, a następnie `bin/console cache:clear`.

### Błąd "Unable to find a document generator with type df_proforma"

Tag serwisu renderera jest nieprawidłowy: ten błąd jest naprawiony od wersji 1.0.2. Upewnij się, że używasz wersji pluginu ≥ 1.0.2.

### Błąd "Call to undefined method Context::getSalesChannelId()"

Przyczyna historyczna: stara sygnatura konstruktora `RenderedDocument`. Naprawione w 1.0.4. Zaktualizuj do 1.0.6.

### Błąd Twig "Cannot rewind a generator that was already run"

Naprawione w 1.0.6: szablon został dostosowany tak, aby nie konsumować dwa razy generatora pochodzącego z `|filter()`.

### Klient otrzymuje e-mail, ale adres URL akceptacji zwraca błąd 404

Sprawdź, czy sales-channel Storefrontu jest zarejestrowany jako domena kanału sprzedaży użytego w zamówieniu. Publiczna trasa `/proforma/accept/{token}` jest zarejestrowana w Storefroncie: nie działa, jeśli wchodzisz na adres URL przez domenę administracji.

### Automatyczne wygasanie nie działa

Sprawdź, czy _message workery_ Shopware działają w tle (`bin/console messenger:consume` albo przez supervisor typu systemd/supervisord). Wygasanie przechodzi przez standardową kolejkę wiadomości Shopware.

## Wsparcie i aktualizacje

12 miesięcy aktualizacji w cenie (zgodność z Shopware, poprawki błędów, drobne dodatki funkcjonalne). Wsparcie e-mail po francusku i angielsku w ciągu 24 godzin roboczych. Kod źródłowy PHP dostarczany jawnie, zgodny z PSR-4, możliwy do audytu i modyfikacji.

W razie pytań lub zgłoszeń: [contact@datafirefly.com](https://www.datafirefly.com/pl/kontakt/).
