PreorderBilling: Added optional pdf format for invoice detail file

This commit is contained in:
Frank Schubert
2025-10-21 18:25:49 +02:00
parent 1319876194
commit ab33924031
5 changed files with 355 additions and 48 deletions

View File

@@ -185,8 +185,11 @@ $pagination_entity_name = "Rechnungen";
<i class="fas fa-fw fa-mail" title="per Email versendet am <?=date("d.m.Y H:i", $invoice->date_delivered)?>"></i>
<?php endif; ?>
<?php if($invoice->csv): ?>
<a class="ml-2" href="<?=self::getUrl("PreorderBillingInvoice", "downloadCsv", ["id" => $invoice->id])?>">
<i class="fas fa-fw fa-file-csv" title="CSV-Datei herunterladen"></i>
<a class="ml-2" href="<?=self::getUrl("PreorderBillingInvoice", "downloadDetailCsv", ["id" => $invoice->id])?>">
<i class="far fa-fw fa-file-csv" title="Rechnungsgetails als CSV-Datei herunterladen"></i>
</a>
<a class="ml-1" href="<?=self::getUrl("PreorderBillingInvoice", "downloadDetailPdf", ["id" => $invoice->id])?>">
<i class="far fa-fw fa-file-pdf text-danger" title="Rechnungsgetails als PDF-Datei herunterladen"></i>
</a>
<?php endif; ?>
</td>

View File

@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html>
<head>
<title>Invoice Detail</title>
<meta charset="utf-8" />
<style>
body {
position: absolute;
width: 100%;
border: 0;
margin: 0;
padding-top: 0;
font-family: "DejaVu Sans Mono", monospace;
font-size: 9px;
}
.separator {
margin-top: 48px;
height: 1px;
}
#topSpacer {
margin-bottom: 32px;
height: 20px;
}
.data-table {
border: 1px solid #aaa;
border-collapse: collapse;
width: 100%;
}
.data-table tbody td,
.data-table tbody th {
margin-left: 4px;
margin-right: 4px;
margin-top: 4px;
margin-bottom: 4px;
border: 1px solid #d9d9d9;
}
.data-table tbody tr.even td {
background-color: #eee;
}
.data-table tbody tr {
border-top: 1px dashed #aaa;
border-bottom: 1px dashed #aaa;
page-break-inside: avoid !important;
}
.data-table tbody tr.top {
page-break-after: avoid !important;
}
.data-table tbody tr.bottom {
page-break-before: avoid !important;
}
.data-table tbody tr.top td,
.data-table tbody tr.top th {
border-top: 1px dashed #aaa;
border-bottom: 1px solid #d9d9d9;
}
.data-table tbody tr.bottom td {
border-bottom: 1px dashed #aaa;
border-top: 1px solid #d7d7d7;
}
.data-table tbody tr.bottom th {
border-bottom: 1px solid #aaa;
border-top: 1px solid #d9d9d9;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.border-right {
border-right: 1px dotted #ddd;
}
</style>
</head>
<body>
<h1>Detailblatt zu Rechnung <?=$rechnum?></h1>
<div>Netzbetreiber: <strong><?=$netzbetreiber?></strong></div>
<table class="data-table">
<tr class="top">
<th class="align-left">Rechnungsnummer</th>
<th class="align-left">Bestelldatum</th>
<th class="align-left" colspan="6">Netzgebiet</th>
</tr>
<tr class="bottom">
<th class="align-left">Rechnungsdatum</th>
<th class="align-left">Extref</th>
<th class="align-left">OAID</th>
<th class="align-left">Periode Von</th>
<th class="align-left">Periode Bis</th>
<th class="align-right">Artikelnummer</th>
<th class="align-left">Produkt</th>
<th class="align-right">Preis Netto</th>
</tr>
<?php $i=0; foreach($positions as $position): ?>
<tr class="top <?=$i%2==0 ? "even" : "odd"?>">
<td class="align-left"><?=$position["rechnum"]?></td>
<td class="align-left"><?=$position["bestdatum"]?></td>
<td class="align-left" colspan="6"><?=$position["netzgebiet"]?></td>
</tr>
<tr class="bottom <?=$i%2==0 ? "even" : "odd"?>">
<td class="align-left"><?=$position["rechdatum"]?></td>
<td class="align-left"><?=$position["extref"]?></td>
<td class="align-left"><?=$position["oaid"]?></td>
<td class="align-left"><?=$position["von"]?></td>
<td class="align-left"><?=$position["bis"]?></td>
<td class="align-right"><?=$position["artnum"]?></td>
<td class="align-left"><?=$position["produkt"]?></td>
<td class="align-right"><?=$position["netto"]?></td>
</tr>
<?php $i++; endforeach; ?>
</table>
</body>
</html>

View File

@@ -90,7 +90,7 @@ class File extends mfBaseModel {
$path .= "/$filename";
if(!file_exists($path)) {
throw new Exception("File not found", 4041);
throw new Exception("File not found $path ", 4041);
}
return $path;

View File

@@ -6,9 +6,11 @@ use chillerlan\QRCode\Output\QROutputInterface;
class PreorderBillingInvoice extends mfBaseModel {
protected $forcestr = ["company", "zip", "email", "phone"];
private $netowner;
private $positions;
private $pdf;
private $csv;
private $detailpdf;
private $emailLog;
private $creator;
@@ -169,8 +171,7 @@ RML Infrastruktur GmbH";
// get pdf file
$ifile = PreorderBillingInvoiceFile::createFromInvoice($this);
if(!$ifile) {
$this->layout()->setFlash("Fehler beim PDF erstellen");
$this->redirect("PreorderBillingInvoice");
return false;
}
$pdf = $ifile->file;
@@ -184,17 +185,31 @@ RML Infrastruktur GmbH";
return false;
}
// get csv file
$csv_filename = false;
$csv = $this->getProperty("csv");
if($csv) {
try {
$csv_filename = $csv->getFullPath();
} catch (\Exception $e) {
$this->log->error("CSV-File for Invoice " . $this->id . " not found");
// get detail file
$detail_file = null;
$netowner = $this->getProperty("netowner");
if(!$netowner || !array_key_exists($netowner->id, TT_PREORDER_BILLING)) {
return false;
}
$netowner_config = TT_PREORDER_BILLING[$netowner->id];
if(array_key_exists($this->owner_id, $netowner_config["netoperators"])) {
$anb_config = $netowner_config["netoperators"][$this->owner_id];
$detail_format = $anb_config["invoice-detail-file-format"];
if($detail_format == "csv") {
$detail_file = $this->getProperty("csv");
}
if($detail_format == "pdf") {
$detail_file = $this->getProperty("detailpdf");
}
}
// get csv file
$tpl = new Layout();
$tpl->setTemplate("Emailtemplates/preorder-invoice/rml/invoice-email.html");
if($this->owner_id) {
@@ -238,8 +253,8 @@ RML Infrastruktur GmbH";
$email->setTo($to);
$email->setHeader("X-".MFAPPNAME."-Iid", $this->id);
$email->addAttachment($pdf_filename, null, $pdf->filename, "application/pdf");
if($csv_filename) {
$email->addAttachment($csv_filename, null, $csv->filename, "text/csv");
if($detail_file) {
$email->addAttachment($detail_file->getFullPath(), null, $detail_file->filename, $detail_file->mimetype);
}
$email->send();
$this->log->info(__METHOD__.": Sending Preorder Invoice ".$this->invoice_number." to $to");
@@ -248,9 +263,152 @@ RML Infrastruktur GmbH";
return true;
}
public function getPdfFromDetailCsv() {
$netowner = $this->getProperty("netowner");
if(!$netowner || !array_key_exists($netowner->id, TT_PREORDER_BILLING)) {
return false;
}
$netowner_config = TT_PREORDER_BILLING[$netowner->id];
$csv_file = $this->getProperty("csv");
$input = fopen($csv_file->getFullPath(), "r");
if(!$input) return false;
$bom = "\xef\xbb\xbf";
if(fgets($input, 4) !== $bom) {
// BOM not found - rewind pointer to start of file.
rewind($input);
}
$headers = [];
$positions = [];
$c = 0;
$i = 0;
while($csv = fgetcsv($input, 0, ";")) {
$i++;
if($i == 1) {
foreach($csv as $key => $name) {
$headers[$name] = $key;
}
continue;
}
if(!trim($csv[1])) {
continue;
}
$data = [];
$data["netzbetreiber"] = trim($csv[$headers["Netzbetreiber"]]);
$data["rechnum"] = trim($csv[$headers["Rechungsnummer"]]);
$data["rechdatum"] = (new DateTime(trim($csv[$headers["Rechnungsdatum"]])))->format("d.m.Y");
$data["netzgebiet"] = trim($csv[$headers["Netzgebiet"]]);
$data["oaid"] = trim($csv[$headers["OAID"]]);
$data["extref"] = trim($csv[$headers["Extref"]]);
$data["bestdatum"] = (new DateTime(trim($csv[$headers["Bestelldatum"]])))->format("d.m.Y");
$data["von"] = trim($csv[$headers["Periode von"]]);
$data["bis"] = trim($csv[$headers["Periode bis"]]);
$data["artnum"] = trim($csv[$headers["Artikelnummer"]]);
$data["produkt"] = trim($csv[$headers["Produkt"]]);
$data["anzahl"] = trim($csv[$headers["Anzahl"]]);
$data["netto"] = trim($csv[$headers["Preis Netto"]]);
$positions[] = $data;
}
if(!count($positions)) {
return false;
}
$invoice_number = $this->invoice_number;
$variables = [
"positions" => $positions,
"netzbetreiber" => $this->getProperty("owner")->getCompanyOrName(),
"rechnum" => $invoice_number,
];
$pdf = new PdfForm("PreorderBillingInvoice/detail.pdf", $variables);
$path = $pdf->render("-O landscape");
if(!$path) return false;
$filename = "$invoice_number-detail.pdf";
$subfolder = $netowner_config["subfolder"];
$new_filepath = MFUPLOAD_FILE_SAVE_PATH."/$subfolder/$filename";
if(!rename($path, $new_filepath)) {
unlink($path);
return false;
}
$file = FileModel::create([
"name" => $filename,
"filename" => $filename,
"store_filename" => $filename,
"orig_filename" => $filename,
"subfolder" => $subfolder,
"mimetype" => "application/pdf",
]);
if(!$file->save()) {
unlink($new_filepath);
return false;
}
$invoice_file = PreorderBillingInvoiceFile::create([
"invoice_id" => $this->id,
"file_id" => $file->id,
"name" => $filename,
]);
if(!$invoice_file->save()) {
$file->delete();
//unlink($new_filepath);
return false;
}
return $invoice_file;
//$pdf->download();
/*
$file = FileModel::create([
"" => "",
]);*/
}
public function getProperty($name) {
if($this->$name == null) {
if($name == "netowner") {
$netowner = new Address($this->netowner_id);
if(!$netowner->id) {
return false;
}
$this->netowner = $netowner;
return $this->netowner;
}
if($name == "owner") {
$owner = new Address($this->owner_id);
if(!$owner->id) {
return false;
}
$this->owner = $owner;
return $this->owner;
}
if($name == "billingaddress") {
$billingaddress = new Address($this->billingaddress_id);
if(!$billingaddress->id) {
return false;
}
$this->billingaddress = $billingaddress;
return $this->billingaddress;
}
if($name == "positions") {
$positions = PreorderBillingInvoiceposition::search(["invoice_id" => $this->id]);
$this->positions = $positions;
@@ -279,6 +437,19 @@ RML Infrastruktur GmbH";
return $this->csv;
}
if($name == "detailpdf") {
// same as csv but in pdf format
$ifile = PreorderBillingInvoiceFile::getFirst(["invoice_id" => $this->id, "name" => "%-detail.pdf"]);
if(!$ifile || !$ifile->file || !$ifile->file->id) {
$ifile = $this->getPdfFromDetailCsv();
}
if(!$ifile || !$ifile->file || !$ifile->file->id) return null;
$this->detailpdf = $ifile->file;
return $this->detailpdf;
}
if($name == "emailLog") {
$emaillog = EmailLog::getFirst(["object_type" => "PreorderBillingInvoice", "object_id" => $this->id]);
if($emaillog) {

View File

@@ -181,7 +181,38 @@ class PreorderBillingInvoiceController extends mfBaseController {
exit;
}
protected function downloadCsv() {
protected function downloadDetailPdf() {
$id = $this->request->id;
if (!is_numeric($id) || !$id) {
$this->layout()->setFlash("Rechnung nicht gefunden", "error");
$this->redirect("Invoice");
}
$invoice = new PreorderBillingInvoice($id);
if (!$invoice->id) {
$this->layout()->setFlash("Rechnung nicht gefunden", "error");
$this->redirect("Invoice");
}
$csv = $invoice->detailpdf;
if(!$csv) {
$this->layout()->setFlash("Fehler beim PDF Generieren", "error");
}
header('Content-Type: application/octet-stream');
header('Content-disposition: attachment; filename="'.$csv->filename.'"');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Length: " . filesize($csv->getFullPath()));
if($csv->mimetype) {
header('Content-Type: '.$csv->mimetype);
}
readfile($csv->getFullPath());
exit;
}
protected function downloadDetailCsv() {
$id = $this->request->id;
if (!is_numeric($id) || !$id) {
$this->layout()->setFlash("Rechnung nicht gefunden", "error");
@@ -704,38 +735,6 @@ class PreorderBillingInvoiceController extends mfBaseController {
return ["sent" => $sent, "defer" => $defer];
}
/*$pdf = $invoice->pdf;
$pdf = false;
if(!$pdf || !$pdf->name) {
$ifile = PreorderBillingInvoiceFile::createFromInvoice($invoice);
if(!$ifile) {
$this->layout()->setFlash("Fehler beim PDF erstellen");
$this->redirect("PreorderBillingInvoice");
}
$pdf = $ifile->file;
}
$pdf_file = false;
try {
$pdf_file = $pdf->getFullPath();
} catch (Exception $e) {
$this->log->error(__METHOD__.": File for Invoice ".$invoice->id." not found");
continue;
}
if(!file_exists($pdf_file)) {
$this->log->error(__METHOD__.": Datei ".$pdf->filename." nicht gefunden");
continue;
}
if($invoice->total == 0) {
$this->log->info(__METHOD__.": Skipping ".$invoice->invoice_number." because total is zero");
$invoice->date_delivered = date("U");
$invoice->save();
continue;
}
*/
if(!$invoice->sendByEmail()) {
$this->log->warning(__METHOD__.": Error sending ".$invoice->invoice_number." to ".$invoice->email);
$invoice->date_delivered = date("U");