data['defaultTemplates'] ?? new NullCollection(); require_once $collection->findFile('.pdf.php')->getAbsolutePath(); /** @var \Modules\Billing\Models\Bill $bill */ $bill = $this->data['bill'] ?? new NullBill(); // Set up default pdf template /** @phpstan-import-type DefaultPdf from ../../../../Admin/Install/Media/PdfDefaultTemplate/pdfTemplate.pdf.php */ $pdf = new DefaultPdf(); $lang = include __DIR__ . '/lang.php'; $pdf->attributes['title_name'] = $this->data['bill_logo_name'] ?? 'Jingga'; $pdf->attributes['slogan'] = $this->data['bill_slogan'] ?? 'Business solutions made simple.'; $pdf->setHeaderData( __DIR__ . '/logo.png', 15, $pdf->attributes['title_name'], $pdf->attributes['slogan'], ); $pdf->setCreator((string) ($this->data['bill_creator'] ?? 'Jingga')); $pdf->setAuthor((string) ($this->data['bill_creator'] ?? 'Jingga')); $pdf->setTitle((string) ($this->data['bill_title'] ?? $bill->type->getL11n())); $pdf->setSubject((string) ($this->data['bill_subtitle'] ?? '')); $pdf->setKeywords(\implode(', ', (array) ($this->data['keywords'] ?? []))); $pdf->language = $bill->language; $pdf->attributes['legal_name'] = $this->data['legal_company_name'] ?? 'Jingga e. K.'; $pdf->attributes['address'] = $this->data['bill_company_address'] ?? 'Kirchstr. 33'; $pdf->attributes['city'] = $this->data['bill_company_city'] ?? '61191 Rosbach'; $pdf->attributes['ceo'] = $this->data['bill_company_ceo'] ?? 'Dennis Eichhorn'; $pdf->attributes['tax_office'] = $this->data['bill_company_tax_office'] ?? 'HRA 5058'; $pdf->attributes['tax_number'] = $this->data['bill_company_tax_id'] ?? 'DE362646968'; $pdf->attributes['terms'] = $this->data['bill_company_terms'] ?? 'https://jingga.app/terms'; $pdf->attributes['bank_name'] = $this->data['bill_company_bank_name'] ?? 'Volksbank Mittelhessen'; $pdf->attributes['swift'] = $this->data['bill_company_swift'] ?? 'VBMHDE5F'; $pdf->attributes['bank_account'] = $this->data['bill_company_bank_account'] ?? 'DE62 5139 0000 0084 8044 10'; $pdf->attributes['website'] = $this->data['bill_company_website'] ?? 'www.jingga.app'; $pdf->attributes['email'] = $this->data['bill_company_email'] ?? 'info@jingga.app'; $pdf->attributes['phone'] = $this->data['bill_company_phone'] ?? '+49 152 04337728'; $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); // add a page $pdf->AddPage(); $topPos = $pdf->getY(); // Set up default bill template $billTypeName = \strtoupper($bill->type->getL11n()); // Address $pdf->setY(50); $pdf->setFont('helvetica', '', 10); $toCountry = ISO3166NameEnum::getBy2Code($bill->billCountry); $addressString = \trim( $bill->billTo . "\n" . (empty($bill->billAddress) ? '' : ($bill->billAddress . "\n")) . (empty($bill->billCity) ? '' : ($bill->billCity . "\n")) . (empty($toCountry) ? '' : ($toCountry . "\n")), "\n " ); // Count the char "\n" in $addressString $addressLineCount = \substr_count($addressString, "\n") + 1; $lineHeight = $pdf->getY(); $pdf->Write( 0, $addressString, '', false, 'L', false, 0, false, false, 0 ); $lineHeight = ($lineHeight - $pdf->getY()) / $addressLineCount; $pageWidth = $pdf->getPageWidth(); $pageHeight = $pdf->getPageHeight(); // Bill head $pdf->setFont('helvetica', 'B', 16); $titleWidth = $pdf->getStringWidth($billTypeName, 'helvetica', 'B', 16); $titleWidth = \is_array($titleWidth) ? \array_sum($titleWidth) : $titleWidth; $pdf->setXY( $rightPos = ($pageWidth - $titleWidth - \max(60 - $titleWidth, 0) - 15 - 2), $topPos + 50 + $lineHeight * $addressLineCount - 38, true ); $pdf->setTextColor(255, 255, 255); $pdf->setFillColor(255, 162, 7); $pdf->Cell($pageWidth - $rightPos - 15, 0, $billTypeName, 0, 0, 'L', true); $pdf->setFont('helvetica', '', 10); $pdf->setTextColor(255, 162, 7); $pdf->setXY($rightPos, $tempY = $pdf->getY() + 10, true); $pdf->MultiCell( 29, 30, $lang[$pdf->language]['InvoiceNo'] . "\n" . $lang[$pdf->language]['InvoiceDate'] . "\n" . $lang[$pdf->language]['ServiceDate'] . "\n" . $lang[$pdf->language]['CustomerNo'] . "\n" . $lang[$pdf->language]['REF'] . "\n" . $lang[$pdf->language]['DueDate'], 0, 'L' ); //$pdf->setFont('helvetica', '', 10); $pdf->setTextColor(0, 0, 0); $pdf->setXY($rightPos + 29 + 2, $tempY, true); $pdf->MultiCell( 25, 30, $bill->number . "\n" . ($bill->billDate?->format('Y-m-d') ?? '0') . "\n" . ($bill->performanceDate?->format('Y-m-d') ?? '0') . "\n" . $bill->accountNumber . "\n" . $bill->external . "\n" . ($bill->billDate?->format('Y-m-d') ?? '0'), /* Consider to add dueDate in addition */ 0, 'L' ); $pdf->Ln(); $tempY = $pdf->getY(); $height = 0; $pdf->setY($tempY - 20); $header = [ $lang[$pdf->language]['Item'], $lang[$pdf->language]['Quantity'], $lang[$pdf->language]['UnitPrice'], $lang[$pdf->language]['Total'], ]; // Header $headerCount = \count($header); $w = [$pageWidth - 20 - 20 - 20 - 2 * 15, 20, 20, 20]; $pdf->setCellPadding(1); $taxes = []; $first = true; // Data $fill = false; foreach($bill->elements as $line) { // @todo depending on amount of lines, there is a solution (html, or use backtracking of tcpdf) if ($first || $pdf->getY() > $pageHeight - 40) { $pdf->setFillColor(255, 162, 7); $pdf->setTextColor(255); $pdf->setDrawColor(255, 162, 7); //$pdf->SetLineWidth(0.3); $pdf->setFont('helvetica', 'B', 10); if (!$first/* || $row === null*/) { $pdf->AddPage(); $pdf->Ln(); } for($i = 0; $i < $headerCount; ++$i) { $pdf->Cell($w[$i], 7, $header[$i], 1, 0, 'L', true); } $pdf->Ln(); $pdf->setFillColor(245, 245, 245); $pdf->setTextColor(0); $pdf->setFont('helvetica', '', 10); $first = false; } // Discounts are shown below the original price -> additional line // We don't want discount columns because that hints at customers they might be able to get discounts. $lines = 1 + ((int) ($line->discountQ->value > 0)) + ((int) ($line->singleDiscountP->value > 0)) + ((int) ($line->singleDiscountR->value > 0)); $tempY = $pdf->getY(); //$pdf->writeHTMLCell($w[0], 10, null, null, $line->itemNumber . ' ' . $line->itemName, 0, 2, $fill); $pdf->MultiCell($w[0], 10 * $lines, \trim($line->itemNumber . ' ' . $line->itemName), 0, 'L', $fill, 2, null, null, true, 0, true, true, 0, 'M', false); $height = $pdf->getY() - $tempY; $singleListPriceNet = Money::fromFloatInt($line->singleListPriceNet); $totalSalesPriceNet = Money::fromFloatInt($line->totalSalesPriceNet); if ($line->quantity->value === 0) { $pdf->MultiCell($w[1] + $w[2] + $w[3], $height, '', 0, 'L', $fill, 0, 15 + $w[0], $tempY, true, 0, false, true, 0, 'M', true); } else { $pdf->MultiCell($w[1], $height, (string) $line->quantity->getAmount($line->container?->quantityDecimals ?? 0), 0, 'L', $fill, 0, 15 + $w[0], $tempY, true, 0, false, true, 0, 'M', true); $pdf->MultiCell($w[2], $height, $singleListPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 0, 15 + $w[0] + $w[1], $tempY, true, 0, false, true, 0, 'M', true); $pdf->MultiCell($w[3], $height, $totalSalesPriceNet->getCurrency(2, symbol: ''), 0, 'L', $fill, 1, 15 + $w[0] + $w[1] + $w[2], $tempY, true, 0, false, true, 0, 'M', true); } $fill = !$fill; // get taxes if (!isset($taxes[$line->taxR->value / FloatInt::DIVISOR])) { $taxes[$line->taxR->value / FloatInt::DIVISOR] = $line->taxP; } else { $taxes[$line->taxR->value / FloatInt::DIVISOR]->add($line->taxP); } } // We have to do the following because in some cases it doesn't set the Y position correctly after the table // I assume it is related to if/else above. A html multicell might not correctly set the y position. if (!empty($bill->elements)) { $pdf->setY($tempY + $height); $pdf->Cell(\array_sum($w), 0, '', 'T'); $pdf->Ln(); if ($pdf->getY() > $pageHeight - 40) { $pdf->AddPage(); } $pdf->setFillColor(240, 240, 240); $pdf->setTextColor(0); $pdf->setDrawColor(240, 240, 240); $pdf->setFont('helvetica', 'B', 10); $tempY = $pdf->getY(); $netSales = Money::fromFloatInt($bill->netSales); $pdf->setX($w[0] + $w[1] + 12); $pdf->Cell($w[2], 7, $lang[$pdf->language]['Subtotal'], 0, 0, 'L', false); $pdf->Cell($w[3], 7, $netSales->getCurrency(2, symbol: ''), 0, 0, 'L', false); $pdf->Ln(); foreach ($taxes as $rate => $tax) { $tax = Money::fromFloatInt($tax); $pdf->setX($w[0] + $w[1] + 12); $pdf->Cell($w[2], 7, $lang[$pdf->language]['Taxes'] . ' (' . $rate . '%)', 0, 0, 'L', false); $pdf->Cell($w[3], 7, $tax->getCurrency(2, symbol: ''), 0, 0, 'L', false); $pdf->Ln(); } $pdf->setFillColor(255, 162, 7); $pdf->setTextColor(255); $pdf->setDrawColor(255, 162, 7); //$pdf->setFont('helvetica', 'B', 10); $grossSales = Money::fromFloatInt($bill->grossSales); $pdf->setX($w[0] + $w[1] + 12); $pdf->Cell($w[2], 7, \strtoupper($lang[$pdf->language]['Total']), 1, 0, 'L', true); $pdf->Cell($w[3] + 3, 7, $grossSales->getCurrency(2, symbol: ''), 1, 0, 'L', true); $pdf->Ln(); $tempY2 = $pdf->getY(); // @todo fix payment terms $pdf->setTextColor(0); $pdf->setFont('helvetica', 'B', 8); $pdf->setY($tempY); $pdf->Write(0, $lang[$pdf->language]['PaymentTerms'] . ': CreditCard', '', false, 'L', false, 0, false, false, 0); $pdf->setFont('helvetica', '', 8); $pdf->Write(0, $bill->paymentText, '', false, 'L', false, 0, false, false, 0); $pdf->Ln(); // @todo fix terms $pdf->setFont('helvetica', 'B', 8); $pdf->Write(0, $lang[$pdf->language]['Terms'] . ': ' . $pdf->attributes['terms'], '', false, 'L', false, 0, false, false, 0); $pdf->Ln(); //$pdf->setFont('helvetica', 'B', 8); $pdf->Write(0, $lang[$pdf->language]['Currency'] . ': ' . $bill->currency, '', false, 'L', false, 0, false, false, 0); $pdf->Ln(); //$pdf->setFont('helvetica', 'B', 8); $pdf->Write(0, $lang[$pdf->language]['TaxRemark'], '', false, 'L', false, 0, false, false, 0); $pdf->Ln(); $pdf->setFont('helvetica', '', 8); $pdf->Write(0, $bill->termsText, '', false, 'L', false, 0, false, false, 0); //$pdf->Ln(); //$pdf->setY($tempY2); //$pdf->Ln(); } //Close and output PDF document $path = (string) ($this->data['path'] ?? (($bill->billDate?->format('Y-m-d') ?? '0') . '_' . $bill->number . '.pdf')); $pdf->Output($path, 'I');