SOQL w PDF Butlerze
Jak pisać zapytania do źródeł danych SOQL w PDF Butlerze: :recordId, pola rekordów nadrzędnych, listy do tabel, agregaty z aliasami i bezpieczne testowanie.
PDF Butler to aplikacja z AppExchange do generowania dokumentów z danych Salesforce: ofert, umów, faktur czy kart produktu. Szablon przygotowujesz w Wordzie (albo w Excelu lub PowerPoincie), a dane do niego dostarczają źródła danych. Najbardziej elastyczne z nich to źródło typu SOQL, czyli zapytanie, które piszesz sam.
W tej lekcji skupiamy się na tym, jak pisać takie zapytania. Konfigurację całego dokumentu opisuje oficjalna PDF Butler Academy.
Uwaga o wersjach: nazwy pól i przycisków w PDF Butlerze zmieniają się między wersjami. Jeśli coś wygląda u Ciebie inaczej niż w opisie, sprawdź aktualną stronę w PDF Butler Academy.
Jak dane trafiają do dokumentu
- Doc Config to konfiguracja jednego dokumentu: szablon, źródła danych i mapowania.
- Data Source (źródło danych) pobiera dane z Salesforce. Przy typie SOQL wykonuje Twoje zapytanie.
- Config Type łączy pole ze źródła danych ze znacznikiem w szablonie, np.
[[!PROP_Price!]]. - Podczas generowania PDF Butler wstawia w miejsce znaczników wartości z wyników zapytania.
Nazwa znacznika jest dowolna: to Ty decydujesz, z którym polem ją połączyć. PDF Butler zaleca konwencję [[!OBIEKT_Pole!]], np. [[!ACC_Name!]]. Dzięki niej od razu widać, skąd pochodzi wartość.
Źródło danych typu SOQL
Przy tworzeniu źródła danych wybierasz typ rekordu SOQL i jeden z dwóch trybów:
- jeden rekord (w PDF Butlerze zwykle Single sObject): wynik to jeden rekord, np. sama oferta,
- lista rekordów (List of sObjects): wynik to wiele rekordów, np. pozycje oferty do tabeli.
Zapytanie możesz wyklikać w SOQL Builderze albo napisać sam. Żeby edytować je ręcznie, odznacz pole SOQL Builder Managed. Wtedy całe zapytanie staje się w pełni edytowalne.
:recordId, czyli rekord, z którego generujesz dokument
Dokument zwykle powstaje z konkretnego rekordu, np. z nieruchomości. Jego Id podstawiasz w zapytaniu zmienną :recordId (z dwukropkiem na początku):
SELECT Id, Name, Price__c, Area_m2__c, Status__c,
Seller__r.FirstName, Seller__r.LastName, Seller__r.Email
FROM Property__c
WHERE Id = :recordId
Do tabeli z ofertami tej nieruchomości użyj osobnego źródła danych w trybie listy. :recordId filtruje teraz rekordy podrzędne po polu relacji:
SELECT Name, Amount__c, CreatedDate
FROM Offer__c
WHERE Property__c = :recordId
ORDER BY Amount__c DESC
Ten sam wzór działa dla obiektów standardowych, np. dla produktów szansy sprzedaży:
SELECT Product2.Name, Quantity, UnitPrice, TotalPrice
FROM OpportunityLineItem
WHERE OpportunityId = :recordId
ORDER BY SortOrder
Pola rekordów nadrzędnych
Dane z rekordu nadrzędnego pobierasz kropką, tak jak w lekcji o relacjach: Account.Name, Owner.Email, Seller__r.Email. W PDF Butlerze każde takie pole mapujesz potem na własny znacznik, np. Seller__r.Email na [[!PROP_SellerEmail!]].
Jedno zapytanie z kilkoma polami rodzica jest lepsze niż kilka osobnych źródeł danych: dokument generuje się szybciej i łatwiej go utrzymać.
Listy i tabele w dokumencie
Rekordy podrzędne do tabeli pobierasz osobnym źródłem danych w trybie listy, tak jak oferty w przykładzie powyżej. W szablonie wiersz tabeli powtarza się dzięki typowi mapowania TABLE_ROW. Każda kolumna to osobne pole z typem SINGLE.
Podzapytania czy osobna lista? W lekcji o relacjach pobieraliśmy rekordy podrzędne podzapytaniem w nawiasie. Dokumentacja PDF Butlera opisuje do tego osobne źródła danych typu lista (także zagnieżdżone, gdy dzieci mają własne dzieci). Trzymaj się tej drogi, bo tak działają mapowania tabel.
Agregaty w źródle danych
PDF Butler obsługuje w zapytaniach funkcje agregujące (SUM, COUNT, MAX, MIN, AVG), a także GROUP BY i HAVING. Jest jeden twardy warunek: każdy agregat musi mieć alias, bo bez niego nie da się odwołać do wyniku.
SELECT COUNT(Id) LiczbaOfert, MAX(Amount__c) NajwyzszaOferta, AVG(Amount__c) SredniaOferta
FROM Offer__c
WHERE Property__c = :recordId
Pola wyliczane, które nie pojawią się automatycznie, dodajesz ręcznie w konfiguracji źródła danych i wybierasz ich typ (Text, Number, Currency).
Własne parametry z Flow
Oprócz :recordId możesz używać własnych parametrów, np. :startdate i :enddate w zestawieniu za wybrany okres. Ich wartości przekazuje Flow, który wywołuje generowanie dokumentu akcją PDF Butlera. Nazwa parametru w zapytaniu musi być taka sama jak klucz przekazany z Flow. Konfigurację krok po kroku opisuje PDF Butler Academy w części o automatyzacji.
Formatowanie i uprawnienia
- Formatowanie dat, kwot i liczb ustawiasz w mapowaniu (Config Type), a nie w zapytaniu. Zapytanie pobiera surowe wartości.
- Uprawnienia: dane pobierane są z uprawnieniami użytkownika, który generuje dokument. Jeśli ktoś nie ma dostępu do pola, nie zobaczy go też w dokumencie. Dlatego testuj dokumenty także na koncie zwykłego użytkownika, nie tylko administratora.
Jak bezpiecznie testować zapytanie
Ten schemat pracy to nasza rekomendacja. Oszczędza dużo czasu przy szukaniu błędów:
- Najpierw w Salesforce Inspectorze. Skopiuj zapytanie i zamień
:recordIdna prawdziwe Id rekordu w apostrofach, np.WHERE Id = 'a01...'. Sprawdź, czy wynik zawiera to, czego oczekujesz. - Potem w PDF Butlerze. Wklej zapytanie do źródła danych i przywróć
:recordId. - Zapisz i odśwież konfigurację. Po zapisaniu zapytania odśwież Doc Config przyciskiem przeładowania danych z Salesforce (np. Reload from SFDC), żeby nowe pola były dostępne w mapowaniach.
- Wygeneruj podgląd na prawdziwym rekordzie. PDF Butler ma do tego komponent PDF Butler Previewer, który dodajesz do strony rekordu.
Do diagnozowania samych źródeł danych PDF Butler poleca też własną wtyczkę do Chrome, Butler Inspector.
Ćwiczenie
Przygotuj dane do dokumentu „Karta nieruchomości”. Jeśli nie masz PDF Butlera, przetestuj zapytania w Salesforce Inspectorze, podstawiając Id wybranej nieruchomości zamiast :recordId.
- Źródło typu „jeden rekord”: nazwa, cena, powierzchnia, status oraz imię, nazwisko i telefon sprzedającego.
- Źródło typu „lista”: wszystkie oferty dla nieruchomości, od najwyższej, z numerem, kwotą i datą utworzenia.
- Źródło z agregatami: liczba ofert, najwyższa i średnia kwota oferty, każda z aliasem.
Sprawdź się
1. Co oznacza :recordId w zapytaniu PDF Butlera?
Pokaż odpowiedź
To Id rekordu, z którego generowany jest dokument. PDF Butler podstawia je w miejsce zmiennej podczas generowania.
2. Dlaczego zapytanie SELECT MAX(Amount__c) FROM Offer__c WHERE Property__c = :recordId sprawi kłopot w PDF Butlerze?
Pokaż odpowiedź
Agregat nie ma aliasu, więc nie da się odwołać do jego wyniku w mapowaniu. Poprawnie: SELECT MAX(Amount__c) NajwyzszaOferta FROM ....
3. Jak przetestować zapytanie z :recordId poza PDF Butlerem?
Pokaż odpowiedź
W Salesforce Inspectorze albo Developer Console, zamieniając :recordId na prawdziwe Id rekordu w apostrofach, np. WHERE Id = 'a01...'.
Następny krok: zbierz całą wiedzę z kursu w jednym miejscu. Kolejna lekcja to ściąga SOQL z najczęstszymi błędami.