Przejdź do treści stopki
KORZYSTANIE Z IRONPDF

Przeglądarka plików PDF dla ASP.NET: Jak wyświetlać pliki PDF w .NET 10

Problem z ręcznym generowaniem wycen

Strona główna IronPDF Każda godzina, którą przedstawiciel handlowy spędza kopiując pozycje z CRM do szablonu Word, to godzina, którą nie sprzedaje. W zespole dziesięciu przedstawicieli, którzy mają po dwadzieścia aktywnych możliwości każdy, ten czas się sumuje, a wynik i tak nie jest niezawodny. Poziomy cenowe są stosowane nieprawidłowo, pola rabatowe pozostają na stawkach z ostatniego kwartału, a język warunków z starej wersji szablonu prześlizguje się do dokumentu, który wychodzi do klienta bez przeglądu.

Problem czasu liniowego potęguje problem dokładności. Potencjalny klient, który prosi o wycenę we wtorek po południu i dostaje ją w środę wieczorem, spędził dzień zastanawiając się, czy umowa jest rzeczywista. Do czasu, gdy plik PDF do niego dotarł, jego uwaga może być już skierowana na konkurenta, który zareagował szybciej.

Istniejące narzędzia CPQ rozwiązują część tego problemu, ale przy cenach dla przedsiębiorstw i harmonogramach wdrożeń, które nie pasują do zespołów, które po prostu potrzebują czystego pliku PDF z danych już istniejącej możliwości. Wysyłanie e-mailem arkusza kalkulacyjnego lub listy cen w postaci zwykłego tekstu zamiast ustrukturyzowanej propozycji sygnalizuje potencjalnemu klientowi, że proces sprzedaży firmy jest improwizowany. A gdy potencjalny klient prosi o poprawki, zamieszanie w wersjach się mnoży, kilkukrotne iteracje unoszą się jako załączniki e-mailowe bez jednego źródła prawdy przytwierdzonego do rekordu w CRM.

Scenariusze się różnią: firma oprogramowania B2B pobierająca wyceny licencji z Salesforce, firma produkcyjna generująca propozycje części i cen z pozycji ERP, agencja tworząca dokumenty określenia zakresu projektu z wewnętrznych szacunków, hurtownik tworzący oferty zniżkowe dla detalistów. Podstawowe tarcia są takie same w każdym przypadku.

Rozwiązanie: Generowanie wycen bezpośrednio z danych możliwości

IronPDF to potężna biblioteka PDF dla .NET, która pozwala aplikacjom .NET generować markowe wyceny PDF i propozycje bezpośrednio z danych możliwości CRM. Gdy przedstawiciel oznacza transakcję jako gotową do wyceny lub kliknie przycisk w wewnętrznym narzędziu, aplikacja pobiera pozycje, ceny, dane klienta i warunki z CRM lub bazy danych, wypełnia szablon HTML lub ciąg zawartości HTML, a ChromePdfRenderer generuje C# PDF z tego HTML.

Wycena jest wysyłana e-mailem do potencjalnego klienta, a wysłany dokument jest automatycznie rejestrowany w rekordzie CRM, bez ręcznego formatowania, bez szablonów Word, bez opłat SaaS za dokument. Renderowanie odbywa się wewnątrz istniejącej aplikacji .NET jako pojedyncza paczka NuGet. Szablon jest własnością Twojego zespołu i jest wersjonowany, aktualizowany w jednym miejscu, co odzwierciedla się w każdej przyszłej wycenie bez rozsyłania plików do zespołu sprzedażowego.

Jak to działa w praktyce

1. Wyzwalacz inicjuje ofertę

Pipeline rozpoczyna się, gdy przedstawiciel kliknie "Generuj ofertę" w interfejsie CRM, możliwość automatycznie wchodzi w określony etap pipeline'u lub zewnętrzna platforma automatyzacji wykonuje wywołanie API do punktu końcowego tworzenia ofert. Wszystkie trzy punkty wejściowe zasilają ten sam przepływ generowania, mechanizm wyzwalacza nie zmienia tego, co dzieje się dalej.

Aplikacja zapytuje CRM lub bazę danych o wszystko, co wymaga oferta: nazwa firmy potencjalnego klienta, imię i nazwisko oraz e-mail kontaktu, pozycje z SKU, opisy, ilości, ceny jednostkowe, zastosowane procenty rabatów i sumy pozycji; łączna suma, obliczenie podatku i suma całkowita; warunki płatności, okres ważności oferty i ewentualne niestandardowe notatki dodane przez przedstawiciela do możliwości.

2. Szablon HTML określa układ oferty

Szablon obsługuje pełną wizualną strukturę oferty: logo firmy i nagłówek, blok "Przygotowany dla" z danymi potencjalnego klienta, tabela z liniami pozycji z kolumnami dla opisu, ilości, ceny jednostkowej, rabatu i sumy pozycji, podsumowanie sumy oraz warunki i stopka z imieniem, nazwiskiem, tytułem i bezpośrednim kontaktem przedstawiciela.

Tabela z liniami pozycji jest tworzona przez iterację nad pozycjami możliwości w handlerze C# i konstruowanie wierszy HTML przed przekazaniem kompletnego szablonu do IronPDF. To utrzymuje logikę w warstwie aplikacji, a nie w samym szablonie.

Przykładowy szablon pliku HTML

Przykładowy szablon HTML

3. ChromePdfRenderer produkuje dokument PDF oferty

Mocny silnik renderujący IronPDF umożliwia prostą konwersję HTML na PDF, zazwyczaj wykonaną w zaledwie kilku liniach kodu.

using IronPdf;

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;

renderer.RenderingOptions.MarginTop = 20;

renderer.RenderingOptions.MarginBottom = 20;

var lineItemRows = string.Concat(opportunity.LineItems.Select(item => $@"
    <tr>
        <td>{item.Description}</td>
        <td style='text-align:center'>{item.Quantity}</td>
        <td style='text-align:right'>{item.UnitPrice:C}</td>
        <td style='text-align:center'>{item.DiscountPct}%</td>
        <td style='text-align:right'>{item.LineTotal:C}</td>
    </tr>"));

string html = $@"
    <h1>Quote — {opportunity.ProspectCompany}</h1>
    <p>Prepared for: {opportunity.ContactName} &lt;{opportunity.ContactEmail}&gt;</p>
    <p>Valid until: {opportunity.QuoteExpiry:MMMM d, yyyy} &nbsp;|&nbsp; Terms: {opportunity.PaymentTerms}</p>
    <table border='1' cellpadding='6' width='100%'>
        <thead><tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Discount</th><th>Total</th></tr></thead>
        <tbody>{lineItemRows}</tbody>
    </table>
    <p style='text-align:right'><strong>Subtotal:</strong> {opportunity.Subtotal:C}</p>
    <p style='text-align:right'><strong>Tax ({opportunity.TaxRatePct}%):</strong> {opportunity.TaxAmount:C}</p>
    <p style='text-align:right'><strong>Grand Total:</strong> {opportunity.GrandTotal:C}</p>
    <hr/><p style='font-size:11px'>{opportunity.TermsAndConditions}</p>";

PdfDocument quote = renderer.RenderHtmlAsPdf(html);
using IronPdf;

var renderer = new ChromePdfRenderer();

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter;

renderer.RenderingOptions.MarginTop = 20;

renderer.RenderingOptions.MarginBottom = 20;

var lineItemRows = string.Concat(opportunity.LineItems.Select(item => $@"
    <tr>
        <td>{item.Description}</td>
        <td style='text-align:center'>{item.Quantity}</td>
        <td style='text-align:right'>{item.UnitPrice:C}</td>
        <td style='text-align:center'>{item.DiscountPct}%</td>
        <td style='text-align:right'>{item.LineTotal:C}</td>
    </tr>"));

string html = $@"
    <h1>Quote — {opportunity.ProspectCompany}</h1>
    <p>Prepared for: {opportunity.ContactName} &lt;{opportunity.ContactEmail}&gt;</p>
    <p>Valid until: {opportunity.QuoteExpiry:MMMM d, yyyy} &nbsp;|&nbsp; Terms: {opportunity.PaymentTerms}</p>
    <table border='1' cellpadding='6' width='100%'>
        <thead><tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Discount</th><th>Total</th></tr></thead>
        <tbody>{lineItemRows}</tbody>
    </table>
    <p style='text-align:right'><strong>Subtotal:</strong> {opportunity.Subtotal:C}</p>
    <p style='text-align:right'><strong>Tax ({opportunity.TaxRatePct}%):</strong> {opportunity.TaxAmount:C}</p>
    <p style='text-align:right'><strong>Grand Total:</strong> {opportunity.GrandTotal:C}</p>
    <hr/><p style='font-size:11px'>{opportunity.TermsAndConditions}</p>";

PdfDocument quote = renderer.RenderHtmlAsPdf(html);
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.Letter

renderer.RenderingOptions.MarginTop = 20

renderer.RenderingOptions.MarginBottom = 20

Dim lineItemRows As String = String.Concat(opportunity.LineItems.Select(Function(item) $"
    <tr>
        <td>{item.Description}</td>
        <td style='text-align:center'>{item.Quantity}</td>
        <td style='text-align:right'>{item.UnitPrice:C}</td>
        <td style='text-align:center'>{item.DiscountPct}%</td>
        <td style='text-align:right'>{item.LineTotal:C}</td>
    </tr>"))

Dim html As String = $"
    <h1>Quote — {opportunity.ProspectCompany}</h1>
    <p>Prepared for: {opportunity.ContactName} &lt;{opportunity.ContactEmail}&gt;</p>
    <p>Valid until: {opportunity.QuoteExpiry:MMMM d, yyyy} &nbsp;|&nbsp; Terms: {opportunity.PaymentTerms}</p>
    <table border='1' cellpadding='6' width='100%'>
        <thead><tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Discount</th><th>Total</th></tr></thead>
        <tbody>{lineItemRows}</tbody>
    </table>
    <p style='text-align:right'><strong>Subtotal:</strong> {opportunity.Subtotal:C}</p>
    <p style='text-align:right'><strong>Tax ({opportunity.TaxRatePct}%):</strong> {opportunity.TaxAmount:C}</p>
    <p style='text-align:right'><strong>Grand Total:</strong> {opportunity.GrandTotal:C}</p>
    <hr/><p style='font-size:11px'>{opportunity.TermsAndConditions}</p>"

Dim quote As PdfDocument = renderer.RenderHtmlAsPdf(html)
$vbLabelText   $csharpLabel

Wygenerowany dokument PDF

Przykładowy wynik działania IronPDF

PoradyOsadź logo swojej firmy i wszelkie pliki czcionek jako dane URI base64 w szablonie, lub ustaw BaseUrlPath jako drugi parametr w RenderHtmlAsPdf(), aby IronPDF rozwiązał względne ścieżki zasobów, niezależnie od miejsca wdrożenia aplikacji.

4. Dostarczenie i zarejestrowanie oferty

using System.Net.Mail;
using System.IO;

// Save versioned copy to document storage
string outputPath = $"quotes/{opportunity.Id}/v{opportunity.QuoteVersion}.pdf";

quote.SaveAs(outputPath);

// Email to prospect
using var stream = new MemoryStream(quote.BinaryData);
using var attachment = new Attachment(stream,
    $"Quote-{opportunity.ProspectCompany}-v{opportunity.QuoteVersion}.pdf",
    "application/pdf");

var message = new MailMessage("sales@yourcompany.com", opportunity.ContactEmail)
{
    Subject = $"Your Quote from {opportunity.RepName} — {opportunity.ProspectCompany}",
    Body = $"Hi {opportunity.ContactName},\n\nPlease find your quote attached. It is valid until {opportunity.QuoteExpiry:MMMM d, yyyy}.\n\n{opportunity.RepName}"
};

message.Attachments.Add(attachment);

using var smtp = new SmtpClient("smtp.yourprovider.com");

await smtp.SendMailAsync(message);

// Log reference back to CRM
await _crmService.LogQuoteSentAsync(opportunity.Id, outputPath, DateTime.UtcNow);
using System.Net.Mail;
using System.IO;

// Save versioned copy to document storage
string outputPath = $"quotes/{opportunity.Id}/v{opportunity.QuoteVersion}.pdf";

quote.SaveAs(outputPath);

// Email to prospect
using var stream = new MemoryStream(quote.BinaryData);
using var attachment = new Attachment(stream,
    $"Quote-{opportunity.ProspectCompany}-v{opportunity.QuoteVersion}.pdf",
    "application/pdf");

var message = new MailMessage("sales@yourcompany.com", opportunity.ContactEmail)
{
    Subject = $"Your Quote from {opportunity.RepName} — {opportunity.ProspectCompany}",
    Body = $"Hi {opportunity.ContactName},\n\nPlease find your quote attached. It is valid until {opportunity.QuoteExpiry:MMMM d, yyyy}.\n\n{opportunity.RepName}"
};

message.Attachments.Add(attachment);

using var smtp = new SmtpClient("smtp.yourprovider.com");

await smtp.SendMailAsync(message);

// Log reference back to CRM
await _crmService.LogQuoteSentAsync(opportunity.Id, outputPath, DateTime.UtcNow);
Imports System.Net.Mail
Imports System.IO

' Save versioned copy to document storage
Dim outputPath As String = $"quotes/{opportunity.Id}/v{opportunity.QuoteVersion}.pdf"

quote.SaveAs(outputPath)

' Email to prospect
Using stream As New MemoryStream(quote.BinaryData)
    Using attachment As New Attachment(stream, $"Quote-{opportunity.ProspectCompany}-v{opportunity.QuoteVersion}.pdf", "application/pdf")
        Dim message As New MailMessage("sales@yourcompany.com", opportunity.ContactEmail) With {
            .Subject = $"Your Quote from {opportunity.RepName} — {opportunity.ProspectCompany}",
            .Body = $"Hi {opportunity.ContactName}," & vbCrLf & vbCrLf & $"Please find your quote attached. It is valid until {opportunity.QuoteExpiry:MMMM d, yyyy}." & vbCrLf & vbCrLf & $"{opportunity.RepName}"
        }

        message.Attachments.Add(attachment)

        Using smtp As New SmtpClient("smtp.yourprovider.com")
            Await smtp.SendMailAsync(message)
        End Using
    End Using
End Using

' Log reference back to CRM
Await _crmService.LogQuoteSentAsync(opportunity.Id, outputPath, DateTime.UtcNow)
$vbLabelText   $csharpLabel

Przykład wysłanego e-maila z załączonym PDF

Email z załączonym zapytaniem o cenę PDF Numer wersji zarówno w ścieżce pliku, jak i w nazwie pliku, daje każdej rewizji własny trwały zapis. Gdy potencjalny klient żąda zmian, przedstawiciel aktualizuje możliwość w CRM, aplikacja regeneruje się z inkrementowaną wersją, a nowa oferta zostaje wysłana, z pełną historią zachowaną w magazynie dokumentów i zarejestrowana w rekordzie CRM.

Korzyści w praktyce

Szybkość do potencjalnego klienta. Oferta generowana jest i wysyłana e-mailem w ciągu kilku sekund po jej zainicjowaniu przez przedstawiciela. Brak opóźnienia w formatowaniu, brak dokumentów siedzących w folderze wersji roboczych i brak opóźnienia między decyzją przedstawiciela o ofercie a jej otrzymaniem przez potencjalnego klienta.

Dokładność cen. Pozycje, poziomy rabatów i sumy całkowite są pobierane bezpośrednio z możliwości CRM. Nie ma kroku ręcznego przepisywania i nie ma ryzyka, że przedstawiciel zastosuje cennik z zeszłego kwartału lub zły harmonogram rabatów.

Spójność marki. Każda oferta wysyłana przez firmę zawiera to samo logo, układ, język warunków i stopkę, bez względu na to, który przedstawiciel ją wygenerował lub z jakiego etapu pipeline'u. Nie ma niekontrolowanych szablonów Word unoszących się na poszczególnych laptopach.

Śledzenie wersji. Każda wygenerowana oferta jest przechowywana z ID możliwości, numerem wersji i znacznikiem czasu, kiedy została wysłana. Rejestr CRM daje menedżerom sprzedaży pełną widoczność na to, co było ofertowane, komu i kiedy, bez przeszukiwania wątków e-mailowych.

Ponowne wykorzystanie szablonów. Gdy zespół prawny aktualizuje język warunków lub zespół marki odświeża logo, jeden plik HTML zmienia się i każda przyszła oferta to odzwierciedla. Nie ma ponownej dystrybucji szablonów Word do zespołu sprzedaży.

Brak kosztów za dokument. Renderowanie odbywa się w procesie wewnątrz aplikacji .NET. Nie ma subskrypcji CPQ, nie ma mierzenia API generowania dokumentów i nie ma modelu kosztów, który rośnie wraz z wolumenem transakcji.

Zakończenie

Luka między danymi możliwości CRM a profesjonalnym PDF w skrzynce odbiorczej potencjalnego klienta jest mniejsza, niż się wydaje, to szablon, wywołanie renderowania i wysłanie e-maila, wszystko wywołane jedną akcją w przepływie pracy sprzedaży. Usunięcie kroku ręcznego formatowania nie tylko oszczędza czas; usuwa kategorię błędów, które mogą kosztować transakcje.

Ten pipeline jest prosty do zbudowania i utrzymania w istniejącej aplikacji .NET. IronPDF obsługuje pełny cykl życia generowania PDF w C# na stronie ironpdf.com, od renderowania szablonów HTML po zapisywanie, strumieniowanie i manipulowanie dokumentami. Darmowy 30-dniowy okres próbny daje wystarczająco dużo czasu na zbudowanie i przetestowanie pełnego pipeline'u generowania ofert z wykorzystaniem własnych danych CRM i przepływu pracy sprzedaży.

Curtis Chau
Autor tekstów technicznych

Curtis Chau posiada tytuł licencjata z informatyki (Uniwersytet Carleton) i specjalizuje się w front-endowym rozwoju, z ekspertką w Node.js, TypeScript, JavaScript i React. Pasjonuje się tworzeniem intuicyjnych i estetycznie przyjemnych interfejsów użytkownika, Curtis cieszy się pracą z nowoczesnymi frameworkami i tworzeniem dobrze zorganizowanych, atrakcyjnych wizualnie podrę...

Czytaj więcej

Zespół wsparcia Iron

Jesteśmy online 24 godziny, 5 dni w tygodniu.
Czat
E-mail
Zadzwoń do mnie