Files
Kundenverwaltung/incs/rechnung.php
T
adminandClaude Opus 5 d8fff4aeb6 Rechnungsmodul auf Prepared Statements umgebaut
- incs/rechnung.php: alle Abfragen mit Platzhaltern. saveRechnung() setzte
  $_POST ungeprueft in SQL (Injection; ein Apostroph im Text brach das
  Speichern ab). Speichern, Kopieren und Loeschen laufen in Transaktionen,
  bei Fehlern bleibt nichts Halbes zurueck. Formularwerte ueber
  formularText/-Zahl/-Ganzzahl (strikter Modus).
- copyRech() trug die Rechnungsnummer ohne typ_id ein (Fehler im strikten
  Modus) und zaehlte typuebergreifend; jetzt wie beim Anlegen pro Typ
  (neueRechnungsnummer()). Artikel und Eigenschaften per INSERT ... SELECT.
- Adressblock-Formatierung aus makeadressblock() und
  makeadressblockUmschlag() in formatiereAdressblock() zusammengefuehrt.
- ajax/autosave.php und ajax/preview.php teilen sich saveTmpRechnung();
  ueberzaehlige Entwuerfe werden samt Artikeln entfernt (vorher FK-Fehler).
- sites/rechnungadd.php: Storno ohne SQL-Injection ueber $_GET["id"],
  Auswahllisten ueber db_zuordnung(), tote Kommissions-Abfrage entfernt.
- Rechnungssuche (createRechQry/createKdnRechQry) liefert SQL und Parameter
  getrennt; Suchbegriffe waren ungeprueft im SQL.
- saveLog() mit Platzhaltern, Protokolltext ueber db_sql_text().
- db.php: db_zuordnung(), db_sql_text(); Helfer liefern Strings wie
  mysqli_query(), damit alter Code sich gleich verhaelt.

Getestet: alter und neuer Code lesen dieselbe migrierte Datenbank, 150 von
155 Pruefungen (10.936 Werte) identisch, die uebrigen 5 sind der
Injection-Suchbegriff (alt: SQL-Fehler). Schreibtests fuer Entwurf,
Anlegen, Nummernkreise, Aendern, Rollback, Kopieren, Adresse, Storno und
Loeschen im strikten Modus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 10:41:43 +02:00

822 lines
33 KiB
PHP

<?php
require_once("saveNchange.php");
/* ---------------------------------------------------------------------
* Hilfsfunktionen für Formularwerte
* ------------------------------------------------------------------ */
/* Getrimmter Text aus $_POST, fehlend oder kein Text ergibt "" */
function formularText($name)
{
return isset($_POST[$name]) && is_string($_POST[$name]) ? trim($_POST[$name]) : "";
}
/*
* Zahl aus $_POST, Komma als Dezimaltrenner. Leere oder ungültige Eingaben
* werden 0 - so hat sie der frühere, nicht strikte Modus gespeichert.
*/
function formularZahl($name)
{
$wert = str_replace(",", ".", formularText($name));
return is_numeric($wert) ? $wert : "0";
}
/* Ganzzahl aus $_POST, gerundet wie MariaDB beim Speichern */
function formularGanzzahl($name)
{
return (int)round((float)formularZahl($name));
}
/* Die zwölf Ankreuzfelder für den Adressblock als 0/1-Zeichenkette */
function formularAdressfelder()
{
$show = "";
foreach(array("firmaanr", "firma", "anr", "titel", "name", "anr2", "titel2", "name2", "str", "ort", "land", "zusatz") as $feld)
$show .= formularText("show_".$feld) ? "1" : "0";
return $show;
}
/* ---------------------------------------------------------------------
* Adressblock
* ------------------------------------------------------------------ */
function anredenUndTitel($dbconn)
{
return array(
db_zuordnung($dbconn, "SELECT id, anrede FROM anrede") ?: array(),
db_zuordnung($dbconn, "SELECT id, titel FROM titel") ?: array(),
);
}
/*
* Baut den Adressblock als HTML-Zeilen. $adressfields ist die 12-stellige
* 0/1-Zeichenkette aus den Ankreuzfeldern (Positionen: ADRFIELD_*).
*/
function formatiereAdressblock($adresse, $adressfields, $anrs, $titels)
{
$firmaanr = substr($adressfields,ADRFIELD_FIRMAANR,1);
$firma = substr($adressfields,ADRFIELD_FIRMA,1);
$anr1 = substr($adressfields,ADRFIELD_ANR1,1);
$titel1 = substr($adressfields,ADRFIELD_TITEL1,1);
$name1 = substr($adressfields,ADRFIELD_NAME1,1);
$anr2 = substr($adressfields,ADRFIELD_ANR2,1);
$titel2 = substr($adressfields,ADRFIELD_TITEL2,1);
$name2 = substr($adressfields,ADRFIELD_NAME2,1);
$str = substr($adressfields,ADRFIELD_STR,1);
$ort = substr($adressfields,ADRFIELD_ORT,1);
$land = substr($adressfields,ADRFIELD_LAND,1);
$zusatz = substr($adressfields,ADRFIELD_ZUSATZ,1);
$adrstr="";
if($firmaanr)
{
$adrstr .= "Firma";
}
if($firma && trim($adresse["firma"]))
{
$adrstr .= "<br />".trim($adresse["firma"]);
}
if($anr1 && $anr2 && $name1 && !$name2)
{
if($adrstr)
$adrstr .= "<br />";
$adrstr .= $anrs[$adresse["anrede_id"]];
if($titel1)
$adrstr .= " ".$titels[$adresse["titel_id"]];
$adrstr .= " und ";
$adrstr .= $anrs[$adresse["anrede2_id"]];
if($titel2)
$adrstr .= " ".$titels[$adresse["titel2_id"]];
$adrstr .= "<br />";
if($adresse["vname"])
$adrstr .= trim($adresse["vname"])." ".trim($adresse["nname"]);
else
$adrstr .= trim($adresse["nname"]);
if($zusatz)
$adrstr .= "<br />".trim($adresse["zusatz"]);
}
elseif($anr1 && $anr2 && $name2 && !$name1)
{
if($adrstr)
$adrstr .= "<br />";
$adrstr .= $anrs[$adresse["anrede_id"]];
if($titel1)
$adrstr .= " ".$titels[$adresse["titel_id"]];
$adrstr .= " und ";
$adrstr .= $anrs[$adresse["anrede2_id"]];
if($titel2)
$adrstr .= " ".$titels[$adresse["titel2_id"]];
$adrstr .= "<br />";
if($adresse["vname2"])
$adrstr .= trim($adresse["vname2"])." ".trim($adresse["nname2"]);
else
$adrstr .= trim($adresse["nname2"]);
if($zusatz)
$adrstr .= "<br />".trim($adresse["zusatz"]);
}
else
{
if($anr1)
{
if($adrstr)
$adrstr .= "<br />";
$adrstr .= $anrs[$adresse["anrede_id"]]." ";
}
if($titel1)
{
if($adrstr)
$adrstr .= "<br />";
if($titels[$adresse["titel_id"]])
$adrstr .= $titels[$adresse["titel_id"]]." ";
}
if($name1)
{
if(!$titel1 && !$firma && !$firmaanr && !$name2 && $adrstr)
$adrstr .= "<br />";
if(!$anr1 && !$titel1 && $adrstr)
$adrstr .= "<br />";
if($adresse["vname"])
$adrstr .= trim($adresse["vname"])." ".trim($adresse["nname"]);
else
$adrstr .= trim($adresse["nname"]);
}
if($anr2 && $anrs[$adresse["anrede2_id"]])
{
if($adrstr)
$adrstr .= "<br />";
$adrstr .= $anrs[$adresse["anrede2_id"]]." ";
}
if($titel2 && $titels[$adresse["titel2_id"]])
{
if($adrstr)
$adrstr .= "<br />";
$adrstr .= $titels[$adresse["titel2_id"]]." ";
}
if(trim($name2))
{
if(!$titel2 && !$firma && !$firmaanr && !$name1 && !$anr1 && $adrstr)
$adrstr .= "<br />";
if(!$anr2 && !$titel2 && $adrstr)
$adrstr .= "<br />";
if($adresse["vname2"])
$adrstr .= trim($adresse["vname2"])." ".trim($adresse["nname2"]);
else
$adrstr .= trim($adresse["nname2"]);
}
if($zusatz)
$adrstr .= "<br />".trim($adresse["zusatz"]);
}
if(trim($adresse["adresserweiterung"])){
$adrstr .= "<br />".trim($adresse["adresserweiterung"]);
}
if($str)
{
if($adrstr)
$adrstr .= "<br />";
$adrstr .= trim($adresse["str"])." ".trim($adresse["hnr"]);
}
if($ort)
{
if($adrstr)
$adrstr .= "<br />";
if($land)
$adresse["ort"] = strtoupper($adresse["ort"]);
$adrstr .= trim($adresse["plz"])." ".trim($adresse["ort"]);
}
if($land)
{
if($adrstr)
$adrstr .= "<br />";
$adrstr .= trim(strtoupper($adresse["land"]));
}
$lines=explode("<br />",$adrstr);
$lines = count($lines);
return array("lines"=>$lines,"adresse"=>$adrstr);
}
function makeadressblock($dbconn,$rechid,$tmp)
{
$tabelle = $tmp ? "tmp_rechnung" : "rechnung";
$sks = $tmp ? "0 AS sks" : "sks";
$adresse = db_zeile($dbconn, "SELECT adressfields, adresserweiterung, nname, $sks, titel_id, titel2_id, zusatz, vname, nname2, vname2, str, ort, plz, hnr, land, firma, anrede_id, anrede2_id FROM $tabelle
JOIN adresse ON $tabelle.adresse_id = adresse.id
JOIN strasse ON strasse.id = str_id
JOIN ort ON ort.id = ort_id JOIN land ON land.id = land_id
WHERE $tabelle.id = ?", array($rechid));
if($adresse === false)
error("Fehler beim ermitteln der Adressdaten<br />".mysqli_error($dbconn));
list($anrs, $titels) = anredenUndTitel($dbconn);
$adressfields = $adresse["adressfields"];
if($adresse["sks"])
$adressfields = "011111111100";
return formatiereAdressblock($adresse, $adressfields, $anrs, $titels);
}
function makeadressblockUmschlag($dbconn,$adressid,$adressfields,$adresserweiterung)
{
$adresse = db_zeile($dbconn, "SELECT * FROM adresse
JOIN strasse ON strasse.id = str_id
JOIN ort ON ort.id = ort_id JOIN land ON land.id = land_id
WHERE adresse.id = ?", array($adressid));
if($adresse === false)
error("Fehler beim ermitteln der Adressdaten<br />".mysqli_error($dbconn));
$adresse["adresserweiterung"] = $adresserweiterung;
list($anrs, $titels) = anredenUndTitel($dbconn);
return formatiereAdressblock($adresse, $adressfields, $anrs, $titels);
}
/* ---------------------------------------------------------------------
* Lesen
* ------------------------------------------------------------------ */
function makerechnungsarray($dbconn,$rechid,$tmp)
{
$tabelle = $tmp ? "tmp_rechnung" : "rechnung";
$sks = $tmp ? "0 AS sks" : "sks";
$rechnung = db_zeile($dbconn, "SELECT adressfields, $sks, intro, adresserweiterung, lieferdatum, outro, anrede1, storno, anrede2, rabatt, rechnr, sub_mwst, kommission, rabattdesc, partner_id, mwst, ziel as zahlziel, vname, nname, user_id, user.vname AS uvname, user.nname AS unname, typ, typ_id, datetime, wahrung, wahrung_id, mwst_id, zahlziel_id, kurs, buchungsKto_id
FROM $tabelle
JOIN wahrung ON wahrung_id = wahrung.id
JOIN rechtyp ON typ_id = rechtyp.id
JOIN zahlziel ON zahlziel_id = zahlziel.id
JOIN mwst ON mwst_id = mwst.id
JOIN user ON user_id = user.id
WHERE $tabelle.id = ?", array($rechid));
if($rechnung === false)
error("Fehler beim ermitteln der Rechnungsdaten<br />".mysqli_error($dbconn));
$ktonr = db_wert($dbconn, "SELECT ktonr FROM konto WHERE partner_id = ?", array($rechnung["partner_id"]));
if($ktonr !== null && $ktonr !== false)
$rechnung["ktonr"] = $ktonr;
if($rechnung["sks"])
$rechnung["mwst"] = "19";
if($rechnung["rechnr"])
$rechnung["drechnr"] = substr($rechnung["rechnr"],0,4)."-".substr($rechnung["rechnr"],4,2)."-".substr($rechnung["rechnr"],6,2).".".substr($rechnung["rechnr"],8,2);
else
$rechnung["drechnr"] = "(Noch nicht vergeben)";
switch($rechnung["typ_id"])
{
case 1:
$rechnung["drechnr"] = "R".$rechnung["drechnr"];
break;
case 2:
$rechnung["drechnr"] = "G".$rechnung["drechnr"];
break;
case 3:
$rechnung["drechnr"] = "A".$rechnung["drechnr"];
break;
case 4:
$rechnung["drechnr"] = "B".$rechnung["drechnr"];
break;
case 6:
$rechnung["drechnr"] = "M".$rechnung["drechnr"];
break;
}
return $rechnung;
}
function makeadressarray($dbconn,$adressid)
{
$kdnres = db_zeile($dbconn, "SELECT plz, ortsteil, ort, str, zusatz, adresstyp.typ AS adresstyp, hnr, vname, partner_id, nname, vname2, nname2, firma, adresse.id, land, anrede_id, titel_id, anrede2_id, titel2_id
FROM adresse
LEFT JOIN strasse ON str_id = strasse.id
LEFT JOIN ort ON ort_id = ort.id
LEFT JOIN land ON land_id = land.id
JOIN adresstyp ON adresse.typ_id = adresstyp.id
WHERE adresse.id = ?
ORDER BY typ_id ASC", array((string)$adressid));
if($kdnres === false)
error("Fehler beim abrufen der adressdaten ".mysqli_error($dbconn));
return $kdnres;
}
function makeartikelarray($dbconn,$rechid,$tmp)
{
$artikel = array();
if($tmp)
{
$zeilen = db_zeilen($dbconn, "SELECT tmp_artikel.bezeichn as bezeichn, preis, wanr, pagebreak, mwst AS artmwst, rabatt, rabatttyp, artikeltyp.bezeichn as typ, typ_id, einheit, einheit_id, menge, angtyp, tmp_artikel.id as id
FROM tmp_artikel
JOIN artikeltyp ON typ_id = artikeltyp.id
JOIN einheit ON einheit_id = einheit.id
JOIN mwst ON mwst_id = mwst.id
WHERE rech_id = ? ORDER BY posnr ASC", array($rechid));
if($zeilen === false)
error("Fehler beim ermitteln der Artikeldaten<br />".mysqli_error($dbconn));
foreach($zeilen ?: array() as $art)
{
$eigenschaften = db_zeilen($dbconn, "SELECT bezeichn, wert, einheit, einheit_id
FROM tmp_eigensch
JOIN einheit ON einheit_id = einheit.id
WHERE art_id = ? ORDER BY posnr ASC", array($art["id"]));
if($eigenschaften === false)
error("Fehler beim ermitteln der Eigenschaften<br />".mysqli_error($dbconn));
$artikel[]=array("art"=>$art,"eig"=>$eigenschaften ?: array());
}
}
else
{
$zeilen = db_zeilen($dbconn, "SELECT artikel.bezeichn as bezeichn, preis, wanr, posnr, pagebreak, mwst AS artmwst, rabatt, rabatttyp, artikeltyp.bezeichn AS typ, typ_id, einheit, einheit_id, menge, angtyp, artikel.id as id
FROM artikel
JOIN artikeltyp ON typ_id = artikeltyp.id
JOIN einheit ON einheit_id = einheit.id
JOIN artikel_rechn ON artikel.id = art_id
JOIN mwst ON artmwst = mwst.id
WHERE rech_id = ? ORDER BY posnr ASC", array($rechid));
if($zeilen === false)
error("Fehler beim ermitteln der Artikeldaten<br />".mysqli_error($dbconn));
foreach($zeilen ?: array() as $art)
{
$eigenschaften = db_zeilen($dbconn, "SELECT bezeichn, wert, einheit, einheit_id
FROM eigensch
JOIN einheit ON einheit_id = einheit.id
JOIN artikel_eigensch ON eigensch.id = eig_id
WHERE art_id = ? AND rech_id = ? AND artposnr = ? ORDER BY posnr ASC", array($art["id"], $rechid, $art["posnr"]));
if($eigenschaften === false)
error("Fehler beim ermitteln der Eigenschaften<br />".mysqli_error($dbconn));
$artikel[]=array("art"=>$art,"eig"=>$eigenschaften ?: array());
}
}
return $artikel;
}
function makesksartikelarray($dbconn,$rechnr)
{
$zeilen = db_zeilen($dbconn, "SELECT bezeichn1 as bezeichn, preis, rabatt, menge, id, bezeichn2
FROM artikelsks
WHERE rechnr = ? ORDER BY pos ASC", array($rechnr));
if($zeilen === false)
error("Fehler beim ermitteln der SKS Artikeldaten<br />".mysqli_error($dbconn));
$artikel = array();
foreach($zeilen ?: array() as $art)
{
// Die zweite Bezeichnung wird wie eine Eigenschaft ausgegeben
$eigenschaften = array(array("bezeichn" => $art["bezeichn2"]));
unset($art["bezeichn2"]);
$art["artmwst"] = 0;
$artikel[]=array("art"=>$art,"eig"=>$eigenschaften);
}
return $artikel;
}
/* ---------------------------------------------------------------------
* Schreiben
* ------------------------------------------------------------------ */
/*
* Nächste freie Dokumentnummer des Tages für einen Dokumenttyp
* (JJJJMMTTnn) und trägt sie in rechnr ein. Gibt array(nummer, fehler).
*/
function neueRechnungsnummer($dbconn, $typ)
{
$heute = date("Ymd");
$letzte = db_wert($dbconn, "SELECT rechnr FROM rechnr WHERE SUBSTRING(rechnr,1,8) = ? AND typ_id = ? ORDER BY rechnr DESC LIMIT 1", array($heute, $typ));
if($letzte === false)
return array("", error("Fehler beim ermitteln der Rechnungsnummer<br />".mysqli_error($dbconn)));
if($letzte === null)
$rechnr = $heute."01";
elseif(substr($letzte,8,2) < 99)
$rechnr = $letzte + 1;
else
return array("", error("Es wurden bereits 99 Rechnungen heute geschrieben. Das rechnungsnummern-Format lässt nicht mehr Rechnungen pro tag zu..."));
if(!db_abfrage($dbconn, "INSERT INTO rechnr SET rechnr = ?, typ_id = ?", array($rechnr, $typ)))
return array("", error("Rechnungsnummer wurde nicht eingetragen...<br />".mysqli_error($dbconn)));
return array($rechnr, 0);
}
function saveRechnung($dbconn,$update)
{
$show = formularAdressfelder();
$type = formularGanzzahl("type");
// Kopfdaten, identisch für Anlegen und Ändern
$felder = array(
"user_id" => $_SESSION["user"],
"adressfields" => $show,
"anrede1" => formularText("anr1"),
"anrede2" => formularText("anr2"),
"intro" => formularText("intro"),
"outro" => formularText("outro"),
"typ_id" => $type,
"adresse_id" => formularGanzzahl("adressid"),
"partner_id" => formularGanzzahl("partnerid"),
"mwst_id" => formularGanzzahl("mwst"),
"zahlziel_id" => formularGanzzahl("zahlziel"),
"wahrung_id" => formularGanzzahl("wahrung"),
"sub_mwst" => formularGanzzahl("sub_mwst"),
"rabatt" => formularGanzzahl("rabatt"),
"rabattdesc" => formularText("rabattdesc"),
"adresserweiterung" => formularText("adresserweiterung"),
"lieferdatum" => formularText("lieferdatum"),
"buchungsKto_id" => formularGanzzahl("buchKto"),
);
$set = implode(", ", array_map(function($spalte) { return "`$spalte` = ?"; }, array_keys($felder)));
// Alles oder nichts: bricht ein Schritt ab, bleibt keine halbe Rechnung
// zurück. Der Entwurf in tmp_rechnung bleibt dann erhalten.
mysqli_begin_transaction($dbconn);
$err = 0;
if($update)
{
$rechid = $update;
$alt = db_zeile($dbconn, "SELECT * FROM rechnung WHERE id = ?", array($rechid));
if($alt === false)
$err = error("Fehler beim einholen der Informationen für die LOG<br />".mysqli_error($dbconn));
if(!$err)
{
$sql = "UPDATE rechnung SET $set, ".(formularText("change_date") == "1" ? "datetime = NOW(), " : "")."changetime = NOW() WHERE id = ?";
$werte = array_merge(array_values($felder), array($rechid));
db_abfrage($dbconn, $sql, $werte) or $err = error("Fehler beim aktualisieren der Rechnung<br />".mysqli_error($dbconn));
}
if(!$err && $alt)
{
$altWerte = array();
foreach(array_keys($felder) as $spalte)
$altWerte[] = $alt[$spalte];
$undo = db_sql_text($dbconn, "UPDATE rechnung SET $set, changetime = ? WHERE id = ?", array_merge($altWerte, array($alt["changetime"], $rechid)));
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $werte)." [rechnung change]", $undo, "ändern");
}
// Artikel- und Eigenschaftsverknüpfungen werden unten neu angelegt
foreach(array("artikel_rechn", "artikel_eigensch") as $tabelle)
{
if(!$err)
{
$sql = "DELETE FROM $tabelle WHERE rech_id = ?";
db_abfrage($dbconn, $sql, array($rechid)) or $err = error("Fehler beim löschen der Verknüpfungen<br />".mysqli_error($dbconn));
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, array($rechid))." [rechnung change]", "", "löschen");
}
}
}
else
{
list($rechnr, $err) = neueRechnungsnummer($dbconn, $type);
if(!$err)
{
$sql = "INSERT INTO rechnung SET $set, datetime = NOW(), rechnr = ?";
$werte = array_merge(array_values($felder), array($rechnr));
db_abfrage($dbconn, $sql, $werte) or $err = error("Fehler beim einfügen der Rechnung<br />".mysqli_error($dbconn));
$rechid = mysqli_insert_id($dbconn);
}
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $werte)." [rechnung add]", "DELETE FROM rechnung WHERE id = '".$rechid."'", "anlegen");
}
if(!$err)
{
$i = 1;
while(!empty($_POST["arthead".$i]) && !$err)
{
if(!empty($_POST["artact".$i]))
{
$arthead = formularText("arthead".$i);
$arttyp = formularGanzzahl("arttyp".$i);
$artmeneinh = formularGanzzahl("artmeneinh".$i);
// Artikel wiederverwenden, wenn Bezeichnung (exakt), Typ und Einheit passen
$artid = db_wert($dbconn, "SELECT id FROM artikel WHERE bezeichn = BINARY ? AND typ_id = ? AND einheit_id = ?", array($arthead, $arttyp, $artmeneinh));
if($artid === false)
$err = error("Fehler beim ermitteln vorhandener Artikel <br />".mysqli_error($dbconn));
elseif($artid === null)
{
$sql = "INSERT INTO artikel SET bezeichn = ?, typ_id = ?, einheit_id = ?, datetime = NOW(), user_id = ?";
$werte = array($arthead, $arttyp, $artmeneinh, $_SESSION["user"]);
db_abfrage($dbconn, $sql, $werte) or $err = error("Fehler beim erstellen neuer Artikel <br />".mysqli_error($dbconn));
$artid = mysqli_insert_id($dbconn);
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $werte)." [rechnung change]", "DELETE FROM artikel WHERE id ='$artid'", "anlegen");
}
if(!$err)
{
db_abfrage($dbconn, "INSERT INTO artikel_rechn SET angtyp = ?, wanr = ?, menge = ?, rabatt = ?, posnr = ?, art_id = ?, artmwst = ?, rech_id = ?, rabatttyp = ?, pagebreak = ?, preis = ?",
array(formularGanzzahl("angtyp".$i), formularText("artwa".$i), formularZahl("artmen".$i), formularGanzzahl("artrab".$i), $i, $artid,
formularGanzzahl("artmwst".$i), $rechid, formularGanzzahl("artrabtyp".$i), formularGanzzahl("pagebreak".$i), formularZahl("artpr".$i)))
or $err = error("Fehler beim erstellen der Artikel-Rechnung Beziehung <br />".mysqli_error($dbconn));
}
$ii = 1;
while(!$err && !empty($_POST["arteig".$i."_".$ii]))
{
$arteig = formularText("arteig".$i."_".$ii);
$artval = formularText("artval".$i."_".$ii);
$arteinh = formularGanzzahl("arteinh".$i."_".$ii);
$eigid = db_wert($dbconn, "SELECT id FROM eigensch WHERE bezeichn = BINARY ? AND wert = ? AND einheit_id = ?", array($arteig, $artval, $arteinh));
if($eigid === false)
$err = error("Fehler beim ermitteln vorhandener Eigenschaften <br />".mysqli_error($dbconn));
elseif($eigid === null)
{
$sql = "INSERT INTO eigensch SET bezeichn = ?, einheit_id = ?, wert = ?, datetime = NOW(), user_id = ?";
$werte = array($arteig, $arteinh, $artval, $_SESSION["user"]);
db_abfrage($dbconn, $sql, $werte) or $err = error("Fehler beim erstellen neuer Eigenschaften <br />".mysqli_error($dbconn));
$eigid = mysqli_insert_id($dbconn);
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $werte)." [rechnung change]", "DELETE FROM eigensch WHERE id ='$eigid'", "anlegen");
}
if(!$err)
{
db_abfrage($dbconn, "INSERT INTO artikel_eigensch SET posnr = ?, artposnr = ?, rech_id = ?, eig_id = ?, art_id = ?", array($ii, $i, $rechid, $eigid, $artid))
or $err = error("Fehler beim anlegen der Eigenschaften-Artikel-Beziehung <br />".mysqli_error($dbconn));
$ii++;
}
}
}
$i++;
}
}
if(!$err)
$err = loescheEntwurf($dbconn, $type);
if(!$err)
{
mysqli_commit($dbconn);
}
else
{
mysqli_rollback($dbconn);
$err = error("Es ist ein Fehler beim Speichern aufgetreten, <br />
Ihre Rechnung ist aber noch vorhanden, <br />
Bitte gehen Sie <a href='?action=rechnungadd&type=$type'>zurück zum Rechnungsformular</a> <br />
und wählen Sie in der auftauchenden Meldung die Schaltfläche <b>JA</b>");
}
return $err;
}
/* Entwurf (tmp_rechnung samt Artikeln und Eigenschaften) des Benutzers für einen Typ löschen */
function loescheEntwurf($dbconn, $type)
{
$err = 0;
db_abfrage($dbconn, "DELETE tmp_eigensch FROM tmp_eigensch JOIN tmp_artikel ON tmp_eigensch.art_id = tmp_artikel.id JOIN tmp_rechnung ON tmp_artikel.rech_id = tmp_rechnung.id WHERE tmp_rechnung.user_id = ? AND tmp_rechnung.typ_id = ?", array($_SESSION["user"], $type))
or $err = error("Fehler beim löschen der Temp Eigenschaften <br />".mysqli_error($dbconn));
if(!$err)
db_abfrage($dbconn, "DELETE tmp_artikel FROM tmp_artikel JOIN tmp_rechnung ON tmp_artikel.rech_id = tmp_rechnung.id WHERE tmp_rechnung.user_id = ? AND tmp_rechnung.typ_id = ?", array($_SESSION["user"], $type))
or $err = error("Fehler beim löschen der Temp Artikel <br />".mysqli_error($dbconn));
if(!$err)
db_abfrage($dbconn, "DELETE FROM tmp_rechnung WHERE user_id = ? AND typ_id = ?", array($_SESSION["user"], $type))
or $err = error("Fehler beim löschen der Temp Rechnung <br />".mysqli_error($dbconn));
return $err;
}
/*
* Zwischenspeichern des Rechnungsformulars (ajax/autosave.php,
* ajax/preview.php) als Entwurf. Pro Benutzer und Dokumenttyp gibt es einen
* Entwurf; seine Artikel werden jedes Mal neu geschrieben. Mit
* $nurAktive = true werden nur Artikel mit gesetztem artact übernommen.
* Gibt array(entwurf_id, fehler) zurück.
*/
function saveTmpRechnung($dbconn, $nurAktive)
{
$type = formularGanzzahl("type");
$felder = array(
"user_id" => $_SESSION["user"],
"adressfields" => formularAdressfelder(),
"anrede1" => formularText("anr1"),
"anrede2" => formularText("anr2"),
"intro" => formularText("intro"),
"outro" => formularText("outro"),
"typ_id" => $type,
"adresse_id" => formularGanzzahl("adressid"),
"partner_id" => formularGanzzahl("partnerid"),
"mwst_id" => formularGanzzahl("mwst"),
"zahlziel_id" => formularGanzzahl("zahlziel"),
"wahrung_id" => formularGanzzahl("wahrung"),
"sub_mwst" => formularGanzzahl("sub_mwst"),
"rabatt" => formularGanzzahl("rabatt"),
"rabattdesc" => formularText("rabattdesc"),
"adresserweiterung" => formularText("adresserweiterung"),
"lieferdatum" => formularText("lieferdatum"),
"buchungsKto_id" => formularGanzzahl("buchKto"),
);
$set = implode(", ", array_map(function($spalte) { return "`$spalte` = ?"; }, array_keys($felder)));
mysqli_begin_transaction($dbconn);
$err = 0;
$progressid = db_wert($dbconn, "SELECT id FROM tmp_rechnung WHERE user_id = ? AND typ_id = ? ORDER BY id LIMIT 1", array($_SESSION["user"], $type));
if($progressid === false)
$err = error("Fehler beim ermitteln der Rechnung <br />".mysqli_error($dbconn));
elseif($progressid === null)
{
db_abfrage($dbconn, "INSERT INTO tmp_rechnung SET $set, datetime = NOW()", array_values($felder))
or $err = error("Fehler beim erstellen der Rechnung <br />".mysqli_error($dbconn));
$progressid = mysqli_insert_id($dbconn);
}
else
{
db_abfrage($dbconn, "UPDATE tmp_rechnung SET $set, datetime = NOW() WHERE id = ?", array_merge(array_values($felder), array($progressid)))
or $err = error("Fehler beim aktualisieren der Rechnung <br />".mysqli_error($dbconn));
// Überzählige Entwürfe desselben Typs aufräumen
if(!$err)
db_abfrage($dbconn, "DELETE tmp_eigensch FROM tmp_eigensch JOIN tmp_artikel ON tmp_eigensch.art_id = tmp_artikel.id JOIN tmp_rechnung ON tmp_artikel.rech_id = tmp_rechnung.id WHERE tmp_rechnung.user_id = ? AND tmp_rechnung.typ_id = ? AND tmp_rechnung.id != ?", array($_SESSION["user"], $type, $progressid))
&& db_abfrage($dbconn, "DELETE tmp_artikel FROM tmp_artikel JOIN tmp_rechnung ON tmp_artikel.rech_id = tmp_rechnung.id WHERE tmp_rechnung.user_id = ? AND tmp_rechnung.typ_id = ? AND tmp_rechnung.id != ?", array($_SESSION["user"], $type, $progressid))
&& db_abfrage($dbconn, "DELETE FROM tmp_rechnung WHERE user_id = ? AND typ_id = ? AND id != ?", array($_SESSION["user"], $type, $progressid))
or $err = error("Fehler beim löschen alter tmp Rechnungen <br />".mysqli_error($dbconn));
}
if(!$err)
db_abfrage($dbconn, "DELETE tmp_eigensch FROM tmp_eigensch JOIN tmp_artikel ON tmp_eigensch.art_id = tmp_artikel.id WHERE tmp_artikel.rech_id = ?", array($progressid))
&& db_abfrage($dbconn, "DELETE FROM tmp_artikel WHERE rech_id = ?", array($progressid))
or $err = error("Fehler beim löschen der Artikel <br />".mysqli_error($dbconn));
$i = 1;
while(!$err && !empty($_POST["arthead".$i]))
{
if(!$nurAktive || !empty($_POST["artact".$i]))
{
db_abfrage($dbconn, "INSERT INTO tmp_artikel SET bezeichn = ?, preis = ?, mwst_id = ?, pagebreak = ?, einheit_id = ?, typ_id = ?, datetime = NOW(), wanr = ?, rech_id = ?, posnr = ?, rabatt = ?, rabatttyp = ?, menge = ?, angtyp = ?",
array(formularText("arthead".$i), formularZahl("artpr".$i), formularGanzzahl("artmwst".$i), formularGanzzahl("pagebreak".$i), formularGanzzahl("artmeneinh".$i),
formularGanzzahl("arttyp".$i), formularText("artwa".$i), $progressid, $i, formularGanzzahl("artrab".$i), formularGanzzahl("artrabtyp".$i),
formularZahl("artmen".$i), formularGanzzahl("angtyp".$i)))
or $err = error("Fehler beim anlegen der Artikel <br />".mysqli_error($dbconn));
$artid = mysqli_insert_id($dbconn);
$ii = 1;
while(!$err && !empty($_POST["arteig".$i."_".$ii]))
{
db_abfrage($dbconn, "INSERT INTO tmp_eigensch SET bezeichn = ?, wert = ?, einheit_id = ?, datetime = NOW(), posnr = ?, art_id = ?",
array(formularText("arteig".$i."_".$ii), formularText("artval".$i."_".$ii), formularGanzzahl("arteinh".$i."_".$ii), $ii, $artid))
or $err = error("Fehler beim anlegen der Eigenschaften <br />".mysqli_error($dbconn));
$ii++;
}
}
$i++;
}
if($err)
mysqli_rollback($dbconn);
else
mysqli_commit($dbconn);
return array($progressid, $err);
}
function delRechnung($dbconn,$id)
{
$err = 0;
mysqli_begin_transaction($dbconn);
db_abfrage($dbconn, "DELETE FROM artikel_rechn WHERE rech_id = ?", array($id)) or $err = error("Fehler beim löschen der Artikel-Rechnung Beziehung <br />".mysqli_error($dbconn));
if(!$err)
db_abfrage($dbconn, "DELETE FROM artikel_eigensch WHERE rech_id = ?", array($id)) or $err = error("Fehler beim löschen der Artikel-Eigenschaften Beziehung <br />".mysqli_error($dbconn));
if(!$err)
db_abfrage($dbconn, "DELETE FROM rechnung WHERE id = ? LIMIT 1", array($id)) or $err = error("Fehler beim löschen der Rechnung <br />".mysqli_error($dbconn));
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, "DELETE FROM rechnung WHERE id = ? LIMIT 1", array($id))." [rechnung DEL]", "", "löschen");
$err ? mysqli_rollback($dbconn) : mysqli_commit($dbconn);
return $err;
}
/* Storno setzen (1) oder zurücknehmen (0) */
function stornoRechnung($dbconn, $id, $storno)
{
$err = 0;
if($storno)
$sql = "UPDATE rechnung SET storno = 1, stornodate = NOW() WHERE id = ?";
else
$sql = "UPDATE rechnung SET storno = 0 WHERE id = ?";
db_abfrage($dbconn, $sql, array($id)) or $err = error("Fehler beim aktualisieren der Rechnung<br />".mysqli_error($dbconn));
return $err;
}
function changeRechAdr($dbconn,$id,$adrid)
{
$err = 0;
$res = db_zeile($dbconn, "SELECT adresse_id, partner_id FROM rechnung WHERE id = ?", array($id));
if($res === false)
return error("Fehler beim ermitteln der Rechnungsadresse für die LOG <br />".mysqli_error($dbconn));
if(!$res || !$res["adresse_id"])
return error("Rechnung nicht gefunden!!");
$partnerid = db_wert($dbconn, "SELECT partner_id FROM adresse WHERE id = ?", array($adrid));
if($partnerid === false)
return error("Fehler beim ermitteln der Rechnungsadresse für die LOG <br />".mysqli_error($dbconn));
$sql = "UPDATE rechnung SET adresse_id = ?, partner_id = ? WHERE id = ?";
$werte = array($adrid, $partnerid, $id);
db_abfrage($dbconn, $sql, $werte) or $err = error("Fehler beim ändern der Rechnungsadresse <br />".mysqli_error($dbconn));
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $werte)." [rechnungsadr change]", db_sql_text($dbconn, $sql, array($res["adresse_id"], $res["partner_id"], $id)), "ändern");
return $err;
}
/* Kopiert ein Dokument samt Artikeln und Eigenschaften auf eine andere Adresse, mit neuer Nummer */
function copyRech($dbconn,$id,$adrid)
{
$err = 0;
$res = db_zeile($dbconn, "SELECT * FROM rechnung WHERE id = ?", array($id));
if($res === false)
return error("Fehler beim ermitteln der Rechnungsdaten <br />".mysqli_error($dbconn));
if(!$res)
return error("Rechnung nicht gefunden!!");
$partnerid = db_wert($dbconn, "SELECT partner_id FROM adresse WHERE id = ?", array($adrid));
if($partnerid === false)
return error("Fehler beim ermitteln der Adressdaten <br />".mysqli_error($dbconn));
mysqli_begin_transaction($dbconn);
list($rechnr, $err) = neueRechnungsnummer($dbconn, $res["typ_id"]);
if(!$err)
{
$sql = "INSERT INTO rechnung SET adressfields = ?, anrede1 = ?, anrede2 = ?, partner_id = ?, mwst_id = ?, user_id = ?, typ_id = ?, zahlziel_id = ?, adresse_id = ?, wahrung_id = ?, adresserweiterung = ?, datetime = NOW(), intro = ?, outro = ?, rechnr = ?, lieferdatum = ?, buchungsKto_id = ?, sub_mwst = ?";
$werte = array($res["adressfields"], $res["anrede1"], $res["anrede2"], $partnerid, $res["mwst_id"], $_SESSION["user"], $res["typ_id"], $res["zahlziel_id"], $adrid,
$res["wahrung_id"], $res["adresserweiterung"], $res["intro"], $res["outro"], $rechnr, $res["lieferdatum"], $res["buchungsKto_id"], $res["sub_mwst"]);
db_abfrage($dbconn, $sql, $werte) or $err = error("Fehler beim Kopieren der Rechnungsdaten <br />".mysqli_error($dbconn));
$rechid = mysqli_insert_id($dbconn);
}
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, $werte)." [rechnung copy]", "DELETE FROM rechnung WHERE id = $rechid", "anlegen");
if(!$err)
{
$sql = "INSERT INTO artikel_rechn (art_id, rech_id, posnr, rabatt, rabatttyp, menge, preis, angtyp, artmwst, wanr, pagebreak)
SELECT art_id, ?, posnr, rabatt, rabatttyp, menge, preis, angtyp, artmwst, wanr, pagebreak FROM artikel_rechn WHERE rech_id = ? ORDER BY id";
db_abfrage($dbconn, $sql, array($rechid, $id)) or $err = error("Fehler beim Kopieren der Dokument-Artikelbeziehnung <br />".mysqli_error($dbconn));
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, array($rechid, $id))." [rechnung copy]", "DELETE FROM artikel_rechn WHERE rech_id = $rechid", "anlegen");
}
if(!$err)
{
$sql = "INSERT INTO artikel_eigensch (art_id, eig_id, rech_id, posnr, artposnr)
SELECT art_id, eig_id, ?, posnr, artposnr FROM artikel_eigensch WHERE rech_id = ?";
db_abfrage($dbconn, $sql, array($rechid, $id)) or $err = error("Fehler beim Kopieren der Artikel-Eigenschaftenbeziehung <br />".mysqli_error($dbconn));
if(!$err)
$err = saveLog($dbconn, db_sql_text($dbconn, $sql, array($rechid, $id))." [rechnung copy]", "DELETE FROM artikel_eigensch WHERE rech_id = $rechid", "anlegen");
}
$err ? mysqli_rollback($dbconn) : mysqli_commit($dbconn);
return $err;
}
function calculateRechnungsSum($dbconn, $rechid, $inclmwst)
{
$res = db_zeile($dbconn, "SELECT mwst, rabatt, sub_mwst, sks FROM rechnung JOIN mwst ON mwst.id = mwst_id WHERE rechnung.id = ?", array($rechid));
if($res["sks"] == 1)
$artikel = db_zeilen($dbconn, "SELECT menge, preis, '19' AS artmwst FROM artikelsks WHERE rechnr = ?", array($rechid));
else
$artikel = db_zeilen($dbconn, "SELECT rabatt AS artrabatt, menge, preis, mwst AS artmwst, artikel_rechn.id AS id FROM artikel_rechn JOIN mwst ON artikel_rechn.artmwst = mwst.id WHERE rech_id = ?", array($rechid));
$sum = Array();
$mixedmwst = 0;
foreach($artikel ?: array() as $artres)
{
if($artres["artmwst"] > 0 && $artres["artmwst"] != 999 && $artres["artmwst"] != $res["mwst"])
{
$mixedmwst = 1;
break;
}
if($artres["artmwst"] == 999 || !$artres["artmwst"])
$artres["artmwst"] = $res["mwst"];
$artmwst=$artres["artmwst"];
if($res["sub_mwst"])
$artres["preis"] = round(100*$artres["preis"]/(100+$artmwst),2);
if(!isset($sum[$artres["artmwst"]]))
$sum[$artres["artmwst"]] = 0;
if(!empty($artres["artrabatt"]))
{
$sum[$artres["artmwst"]] += round(-1*$artres["preis"]*$artres["menge"]*($artres["artrabatt"]/100),2);
}
$sum[$artres["artmwst"]] += $artres["preis"]*$artres["menge"];
}
if($mixedmwst == 0 && $res["mwst"] == 0)
{
//RABATT
if ($res["rabatt"] != "0") {
$multi=$res["rabatt"]/100;
foreach($sum AS $mwst => $ubertr_mwst)
{
$sum[$mwst]=$sum[$mwst]-$sum[$mwst]*$multi;
}
}
//Mwst ausweisen
$sum_all = 0;
foreach($sum as $mwst => $ubertr_mwst){
if($inclmwst){
$sum_all += $ubertr_mwst*((100+$mwst)/100);
}
else{
$sum_all += $ubertr_mwst;
}
}
}
else
{
$multi=$res["rabatt"]/100;
foreach($sum AS $mwst => $ubertr_mwst)
{
$sum[$mwst]= round($sum[$mwst]-$sum[$mwst]*$multi,2);
}
$sum_all = 0;
foreach($sum as $mwst => $ubertr_mwst){
if($inclmwst){
$sum_all += $ubertr_mwst*((100+$mwst)/100);
}
else{
$sum_all += $ubertr_mwst;
}
}
}
return round($sum_all,2);
}
?>