Skip to content

Instantly share code, notes, and snippets.

@Al-Muhandis
Created October 23, 2025 21:38
Show Gist options
  • Select an option

  • Save Al-Muhandis/4ccea5793ef0e3b2ed4ac6df2d32abbc to your computer and use it in GitHub Desktop.

Select an option

Save Al-Muhandis/4ccea5793ef0e3b2ed4ac6df2d32abbc to your computer and use it in GitHub Desktop.

Revisions

  1. Al-Muhandis created this gist Oct 23, 2025.
    186 changes: 186 additions & 0 deletions invoice_pdf.pas
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,186 @@
    unit invoice_pdf;

    {$mode objfpc}{$H+}

    interface

    uses
    Classes, SysUtils, fppdf, cmn_globals
    ;

    type

    { TInvoicePDFGenerator }

    TInvoicePDFGenerator = class
    private
    FCurrency: String;
    FFontFilePath: String;
    FFontName: String;
    FPayeeBankBIC: Int64;
    FPayeeBankName: String;
    FPayeeINN: Int64;
    FPayeeKPP: Int64;
    FPayerEmail: String;
    FPayerINN: String;
    FPayerKPP: String;
    FPayerName: String;
    FPayerPhone: String;
    FPayerTypeEnum: TPayerType;
    FPrice: Integer;
    FAcc, FCorrAcc: String;
    FDocNum: Integer;
    FPayeeName: String;
    FPaymentName: String;
    FPage: TPDFPage;
    FXBlockIndent, FXBlockIndent2: Integer;
    FY: Integer;
    procedure DrawLine(X, Y, L: Integer);
    function GetPayerType: String;
    procedure SetPayerType(AValue: String);
    procedure WriteText(X, Y: Integer; const S: string; aFont, aSize: Integer);
    procedure WriteBigText(X, Y: Integer; const S: string; aFont: Integer);
    procedure WriteSmallText(X, Y: Integer; const S: string; aFont: Integer);
    procedure WriteInfoBlockWithLine(const aInfoValue, aInfoName: String; aFont: Integer);
    procedure WriteInfoBlockWithLine(const aInfoValue1, aInfoName1, aInfoValue2, aInfoName2: String; aFont: Integer);
    public
    function Generate: TMemoryStream;
    property DocNum: Integer read FDocNum write FDocNum;
    property PayeeName: String read FPayeeName write FPayeeName;
    property PayeeINN: Int64 read FPayeeINN write FPayeeINN;
    property PayeeKPP: Int64 read FPayeeKPP write FPayeeKPP;
    property PayeeBankName: String read FPayeeBankName write FPayeeBankName;
    property PayeeBankBIC: Int64 read FPayeeBankBIC write FPayeeBankBIC;
    property PaymentName: String read FPaymentName write FPaymentName;
    property PayerType: String read GetPayerType write SetPayerType;
    property PayerTypeEnum: TPayerType read FPayerTypeEnum write FPayerTypeEnum;
    property PayerName: String read FPayerName write FPayerName;
    property PayerEmail: String read FPayerEmail write FPayerEmail;
    property PayerPhone: String read FPayerPhone write FPayerPhone;
    property PayerINN: String read FPayerINN write FPayerINN;
    property PayerKPP: String read FPayerKPP write FPayerKPP;
    property PayeeBankCorrAcc: String read FCorrAcc write FCorrAcc;
    property PayeeBankAcc: String read FAcc write FAcc;
    property Price: Integer read FPrice write FPrice;
    property Currency: String read FCurrency write FCurrency;
    property FontFilePath: String read FFontFilePath write FFontFilePath;
    property FontName: String read FFontName write FFontName;
    end;

    function InvoiceFullID(const aText: String; aDocNum: Integer; aDate: TDate): String;

    implementation

    function InvoiceFullID(const aText: String; aDocNum: Integer; aDate: TDate): String;
    begin
    Result:=Format(aText, [aDocNum, FormatDateTime('dd.mm.yyyy', aDate)]);
    end;

    { TInvoicePDFGenerator }

    procedure TInvoicePDFGenerator.DrawLine(X, Y, L: Integer);
    begin
    FPage.MoveTo(X, Y);
    FPage.DrawLine(X,Y, x+L, Y, 0.5, False);
    FPage.StrokePath;
    end;

    function TInvoicePDFGenerator.GetPayerType: String;
    begin
    Result:=PayerTypeToString(FPayerTypeEnum);
    end;

    procedure TInvoicePDFGenerator.SetPayerType(AValue: String);
    begin
    FPayerTypeEnum:=StringToPayerType(AValue);
    end;

    procedure TInvoicePDFGenerator.WriteText(X, Y: Integer; const S: string; aFont, aSize: Integer);
    begin
    FPage.SetFont(aFont, aSize);
    FPage.WriteText(X, Y, S);
    end;

    procedure TInvoicePDFGenerator.WriteBigText(X, Y: Integer; const S: string; aFont: Integer);
    begin
    WriteText(X, Y, S, aFont, 12);
    end;

    procedure TInvoicePDFGenerator.WriteSmallText(X, Y: Integer; const S: string; aFont: Integer);
    begin
    WriteText(X, Y, S, aFont, 9);
    end;

    procedure TInvoicePDFGenerator.WriteInfoBlockWithLine(const aInfoValue, aInfoName: String; aFont: Integer);
    begin
    WriteBigText(FXBlockIndent, FY, aInfoValue, aFont); FY -= 2;
    DrawLine(FXBlockIndent, FY, 140); FY -= 3;
    WriteSmallText(FXBlockIndent+5, FY, aInfoName, aFont); FY -= 8;
    end;

    procedure TInvoicePDFGenerator.WriteInfoBlockWithLine(const aInfoValue1, aInfoName1, aInfoValue2, aInfoName2: String;
    aFont: Integer);
    begin
    WriteBigText(FXBlockIndent, FY, aInfoValue1, aFont); WriteBigText(FXBlockIndent2, FY, aInfoValue2, aFont); FY -= 2;
    DrawLine(FXBlockIndent, FY, 60); DrawLine(FXBlockIndent2, FY, 60); FY -= 3;
    WriteSmallText(FXBlockIndent+5, FY, aInfoName1, aFont); WriteSmallText(FXBlockIndent2+5, FY, aInfoName2, aFont); FY -= 8;
    end;

    function TInvoicePDFGenerator.Generate: TMemoryStream;
    var
    aDoc: TPDFDocument;
    aFont: Integer;
    aSection: TPDFSection;
    InvoiceNo, aInvoiceNum: string;
    begin
    Result := TMemoryStream.Create;
    aDoc := TPDFDocument.Create(nil);
    try
    aDoc.StartDocument;
    aSection := aDoc.Sections.AddSection;
    FPage := aDoc.Pages.AddPage;
    aSection.AddPage(FPage);

    if FFontFilePath.IsEmpty then
    aFont := aDoc.AddFont(FFontName)
    else
    aFont := aDoc.AddFont(FFontFilePath, FFontName);
    FY := Round(FPage.GetPaperHeight) - 10;

    InvoiceNo := InvoiceFullID('Счет на оплату № %d от %s г.', DocNum, Date);
    WriteBigText(40, FY, InvoiceNo, aFont); FY -= 16;

    FXBlockIndent:=40;
    FXBlockIndent2:=120;
    WriteInfoBlockWithLine(PayeeBankName, 'Банк получателя', aFont);
    WriteInfoBlockWithLine(PayeeBankBIC.ToString, 'БИК банка получателя', PayeeBankCorrAcc,
    'Номер корр.счета банка получателя', aFont);
    WriteInfoBlockWithLine(PayeeINN.ToString, 'ИНН получателя', PayeeKPP.ToString, 'КПП получателя', aFont);
    WriteInfoBlockWithLine(PayeeBankAcc, 'Счет получателя платежа', aFont);
    WriteInfoBlockWithLine(PayeeName, 'Получатель платежа', aFont);
    aInvoiceNum:=Format(PaymentName, [InvoiceFullID('№ %d от %s г.', DocNum, Date)]);
    WriteInfoBlockWithLine(aInvoiceNum, 'Наименование платежа', aFont);

    FY -= 8;
    WriteBigText(FXBlockIndent+40, FY, Format('Сумма платежа: %s', [CurrencyToStr(FPrice, FCurrency)]), aFont); FY -= 8;
    WriteBigText(FXBlockIndent+40, FY, 'НДС не облагается', aFont); FY -= 8;

    FY -= 8;

    WriteBigText(FXBlockIndent+5, FY, 'Плательщик: ' + PayerName, aFont); FY -= 8;
    if FPayerTypeEnum=pteEntity then
    begin
    WriteBigText(FXBlockIndent+5, FY, 'ИНН плательщика: ' + PayerINN, aFont); FY -= 8;
    WriteBigText(FXBlockIndent+5, FY, 'КПП плательщика: ' + PayerKPP, aFont); FY -= 8;
    end;
    WriteBigText(FXBlockIndent+5, FY, 'Email: ' + PayerEmail, aFont);
    WriteBigText(FXBlockIndent2+5, FY, 'Тел.: +' + PayerPhone, aFont); FY -= 12;

    aDoc.SaveToStream(Result);
    finally
    aDoc.Free;
    end;
    Result.Position := 0;
    end;

    end.