dolibarr.steuer/class/euer.class.php
2026-02-03 14:47:27 +01:00

459 lines
20 KiB
PHP

<?php
/**
* EÜR (Einnahmen-Überschuss-Rechnung) Klasse für Deutschland
* Steuerjahr 2025
*
* @package steuer
* @subpackage class
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Klasse zur Verwaltung der EÜR
*/
class EUeR extends CommonObject
{
public $db;
public $error = '';
public $errors = array();
public $jahr;
public $entity;
// EÜR Daten
public $einnahmen = array();
public $ausgaben = array();
public $summe_einnahmen = 0;
public $summe_ausgaben = 0;
public $gewinn = 0;
// USt/VSt Daten
public $ust_summe = 0;
public $vst_summe = 0;
public $ust_zahllast = 0;
/**
* Constructor
*
* @param DoliDB $db Database handler
*/
public function __construct($db)
{
global $conf;
$this->db = $db;
$this->entity = $conf->entity;
}
/**
* Berechnet die EÜR aus den Dolibarr-Buchungen
*
* @param int $jahr Steuerjahr
* @param int $monat_von Monat von (1-12, 0=ganzes Jahr)
* @param int $monat_bis Monat bis (1-12, 0=ganzes Jahr)
* @return int 0 bei Erfolg, <0 bei Fehler
*/
public function berechneAusDolibarr($jahr, $monat_von = 0, $monat_bis = 0)
{
global $conf;
$this->jahr = $jahr;
// Datumsgrenzen
if ($monat_von > 0 && $monat_bis > 0) {
$datum_von = $jahr.'-'.sprintf('%02d', $monat_von).'-01';
$datum_bis = date('Y-m-t', strtotime($jahr.'-'.sprintf('%02d', $monat_bis).'-01'));
} else {
$datum_von = $jahr.'-01-01';
$datum_bis = $jahr.'-12-31';
}
// Reset
$this->einnahmen = array();
$this->ausgaben = array();
$this->summe_einnahmen = 0;
$this->summe_ausgaben = 0;
$this->ust_summe = 0;
$this->vst_summe = 0;
// EINNAHMEN aus Kundenrechnungen (bezahlt)
$this->berechneEinnahmenAusRechnungen($datum_von, $datum_bis);
// AUSGABEN aus Lieferantenrechnungen (bezahlt)
$this->berechneAusgabenAusLieferantenrechnungen($datum_von, $datum_bis);
// Manuelle Buchungen aus steuer_buchung Tabelle
$this->berechneManuelleBuchungen($datum_von, $datum_bis);
// Gewinn berechnen
$this->gewinn = $this->summe_einnahmen - $this->summe_ausgaben;
$this->ust_zahllast = $this->ust_summe - $this->vst_summe;
return 0;
}
/**
* Einnahmen aus bezahlten Kundenrechnungen
*/
private function berechneEinnahmenAusRechnungen($datum_von, $datum_bis)
{
global $conf;
// Bezahlte Rechnungen nach Zahlungsdatum (Zufluss-Prinzip!)
$sql = "SELECT f.rowid, f.ref, f.datef as rechnungsdatum,";
$sql .= " pf.datep as zahlungsdatum, pf.amount as zahlung,";
$sql .= " f.total_ht as netto, f.total_tva as ust, f.total_ttc as brutto,";
$sql .= " s.nom as kunde,";
$sql .= " fd.tva_tx as ust_satz, fd.total_ht as zeile_netto, fd.total_tva as zeile_ust";
$sql .= " FROM ".MAIN_DB_PREFIX."facture as f";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON pf.fk_facture = f.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facturedet as fd ON fd.fk_facture = f.rowid";
$sql .= " WHERE f.entity = ".((int) $conf->entity);
$sql .= " AND f.fk_statut IN (2, 3)"; // Bezahlt oder teilbezahlt
$sql .= " AND pf.datep BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql .= " ORDER BY pf.datep";
$resql = $this->db->query($sql);
if ($resql) {
$einnahmen_details = array();
while ($obj = $this->db->fetch_object($resql)) {
$key = 'ust_'.(int)$obj->ust_satz;
if (!isset($einnahmen_details[$key])) {
$einnahmen_details[$key] = array(
'bezeichnung' => 'Erlöse '.(int)$obj->ust_satz.'% USt',
'netto' => 0,
'ust' => 0,
'brutto' => 0,
'ust_satz' => (int)$obj->ust_satz,
'anzahl' => 0
);
}
$einnahmen_details[$key]['netto'] += $obj->zahlung / (1 + $obj->ust_satz/100);
$einnahmen_details[$key]['ust'] += $obj->zahlung - ($obj->zahlung / (1 + $obj->ust_satz/100));
$einnahmen_details[$key]['brutto'] += $obj->zahlung;
$einnahmen_details[$key]['anzahl']++;
}
foreach ($einnahmen_details as $key => $detail) {
$this->einnahmen[$key] = $detail;
$this->summe_einnahmen += $detail['netto'];
$this->ust_summe += $detail['ust'];
}
}
// Alternative: Wenn keine Zahlungen verknüpft, nimm Rechnungsdatum
$sql2 = "SELECT f.rowid, f.ref, f.datef as datum,";
$sql2 .= " f.total_ht as netto, f.total_tva as ust, f.total_ttc as brutto,";
$sql2 .= " s.nom as kunde";
$sql2 .= " FROM ".MAIN_DB_PREFIX."facture as f";
$sql2 .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
$sql2 .= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON pf.fk_facture = f.rowid";
$sql2 .= " WHERE f.entity = ".((int) $conf->entity);
$sql2 .= " AND f.fk_statut IN (2, 3)";
$sql2 .= " AND f.datef BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql2 .= " AND pf.rowid IS NULL"; // Keine Zahlungsverknüpfung
$sql2 .= " ORDER BY f.datef";
$resql2 = $this->db->query($sql2);
if ($resql2) {
while ($obj = $this->db->fetch_object($resql2)) {
$key = 'einnahmen_ohne_zahlung';
if (!isset($this->einnahmen[$key])) {
$this->einnahmen[$key] = array(
'bezeichnung' => 'Erlöse (Rechnungsdatum)',
'netto' => 0,
'ust' => 0,
'brutto' => 0,
'anzahl' => 0
);
}
$this->einnahmen[$key]['netto'] += $obj->netto;
$this->einnahmen[$key]['ust'] += $obj->ust;
$this->einnahmen[$key]['brutto'] += $obj->brutto;
$this->einnahmen[$key]['anzahl']++;
$this->summe_einnahmen += $obj->netto;
$this->ust_summe += $obj->ust;
}
}
}
/**
* Ausgaben aus bezahlten Lieferantenrechnungen
*/
private function berechneAusgabenAusLieferantenrechnungen($datum_von, $datum_bis)
{
global $conf;
// Bezahlte Lieferantenrechnungen nach Zahlungsdatum (Abfluss-Prinzip!)
$sql = "SELECT f.rowid, f.ref, f.ref_supplier, f.datef as rechnungsdatum,";
$sql .= " pf.datep as zahlungsdatum, pf.amount as zahlung,";
$sql .= " f.total_ht as netto, f.total_tva as vst, f.total_ttc as brutto,";
$sql .= " s.nom as lieferant,";
$sql .= " fd.tva_tx as vst_satz";
$sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON pf.fk_facturefourn = f.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn_det as fd ON fd.fk_facture_fourn = f.rowid";
$sql .= " WHERE f.entity = ".((int) $conf->entity);
$sql .= " AND f.fk_statut IN (2)"; // Bezahlt
$sql .= " AND pf.datep BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql .= " ORDER BY pf.datep";
$resql = $this->db->query($sql);
if ($resql) {
$ausgaben_details = array();
while ($obj = $this->db->fetch_object($resql)) {
$vst_satz = isset($obj->vst_satz) ? (int)$obj->vst_satz : 19;
$key = 'vst_'.$vst_satz;
if (!isset($ausgaben_details[$key])) {
$ausgaben_details[$key] = array(
'bezeichnung' => 'Betriebsausgaben '.$vst_satz.'% VSt',
'netto' => 0,
'vst' => 0,
'brutto' => 0,
'vst_satz' => $vst_satz,
'anzahl' => 0
);
}
$ausgaben_details[$key]['brutto'] += $obj->zahlung;
$ausgaben_details[$key]['netto'] += $obj->zahlung / (1 + $vst_satz/100);
$ausgaben_details[$key]['vst'] += $obj->zahlung - ($obj->zahlung / (1 + $vst_satz/100));
$ausgaben_details[$key]['anzahl']++;
}
foreach ($ausgaben_details as $key => $detail) {
$this->ausgaben[$key] = $detail;
$this->summe_ausgaben += $detail['netto'];
$this->vst_summe += $detail['vst'];
}
}
// Alternative: Ohne Zahlungsverknüpfung
$sql2 = "SELECT f.rowid, f.ref, f.datef as datum,";
$sql2 .= " f.total_ht as netto, f.total_tva as vst, f.total_ttc as brutto,";
$sql2 .= " s.nom as lieferant";
$sql2 .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
$sql2 .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
$sql2 .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON pf.fk_facturefourn = f.rowid";
$sql2 .= " WHERE f.entity = ".((int) $conf->entity);
$sql2 .= " AND f.fk_statut IN (2)";
$sql2 .= " AND f.datef BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql2 .= " AND pf.rowid IS NULL";
$sql2 .= " ORDER BY f.datef";
$resql2 = $this->db->query($sql2);
if ($resql2) {
while ($obj = $this->db->fetch_object($resql2)) {
$key = 'ausgaben_ohne_zahlung';
if (!isset($this->ausgaben[$key])) {
$this->ausgaben[$key] = array(
'bezeichnung' => 'Betriebsausgaben (Rechnungsdatum)',
'netto' => 0,
'vst' => 0,
'brutto' => 0,
'anzahl' => 0
);
}
$this->ausgaben[$key]['netto'] += $obj->netto;
$this->ausgaben[$key]['vst'] += $obj->vst;
$this->ausgaben[$key]['brutto'] += $obj->brutto;
$this->ausgaben[$key]['anzahl']++;
$this->summe_ausgaben += $obj->netto;
$this->vst_summe += $obj->vst;
}
}
}
/**
* Manuelle Buchungen aus steuer_buchung Tabelle
*/
private function berechneManuelleBuchungen($datum_von, $datum_bis)
{
global $conf;
$sql = "SELECT b.*, k.kontonummer, k.bezeichnung as konto_bezeichnung, k.kategorie";
$sql .= " FROM ".MAIN_DB_PREFIX."steuer_buchung as b";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."steuer_konto as k ON b.fk_konto = k.rowid";
$sql .= " WHERE b.entity = ".((int) $conf->entity);
$sql .= " AND b.status = 1";
$sql .= " AND b.datum BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql .= " ORDER BY b.datum";
$resql = $this->db->query($sql);
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
$key = 'manuell_'.$obj->kontonummer;
if ($obj->typ == 'einnahme') {
if (!isset($this->einnahmen[$key])) {
$this->einnahmen[$key] = array(
'bezeichnung' => $obj->konto_bezeichnung,
'kontonummer' => $obj->kontonummer,
'netto' => 0,
'ust' => 0,
'brutto' => 0,
'ust_satz' => (int)$obj->ust_satz,
'anzahl' => 0
);
}
$this->einnahmen[$key]['netto'] += $obj->betrag_netto;
$this->einnahmen[$key]['ust'] += $obj->betrag_ust;
$this->einnahmen[$key]['brutto'] += $obj->betrag_brutto;
$this->einnahmen[$key]['anzahl']++;
$this->summe_einnahmen += $obj->betrag_netto;
$this->ust_summe += $obj->betrag_ust;
} else {
if (!isset($this->ausgaben[$key])) {
$this->ausgaben[$key] = array(
'bezeichnung' => $obj->konto_bezeichnung,
'kontonummer' => $obj->kontonummer,
'netto' => 0,
'vst' => 0,
'brutto' => 0,
'vst_satz' => (int)$obj->ust_satz,
'anzahl' => 0
);
}
$this->ausgaben[$key]['netto'] += $obj->betrag_netto;
$this->ausgaben[$key]['vst'] += $obj->betrag_ust;
$this->ausgaben[$key]['brutto'] += $obj->betrag_brutto;
$this->ausgaben[$key]['anzahl']++;
$this->summe_ausgaben += $obj->betrag_netto;
$this->vst_summe += $obj->betrag_ust;
}
}
}
}
/**
* Holt detaillierte Buchungsliste für einen Zeitraum
*
* @param string $datum_von Von-Datum
* @param string $datum_bis Bis-Datum
* @param string $typ 'einnahme', 'ausgabe' oder 'alle'
* @return array Buchungsliste
*/
public function getBuchungsliste($datum_von, $datum_bis, $typ = 'alle')
{
global $conf;
$buchungen = array();
// Kundenrechnungen (Einnahmen)
if ($typ == 'alle' || $typ == 'einnahme') {
$sql = "SELECT 'einnahme' as buchungstyp, f.rowid, f.ref,";
$sql .= " COALESCE(pf.datep, f.datef) as datum,";
$sql .= " CONCAT('Rechnung ', f.ref, ' - ', s.nom) as beschreibung,";
$sql .= " f.total_ht as netto, f.total_tva as steuer, f.total_ttc as brutto,";
$sql .= " s.nom as partner, 'facture' as quelle";
$sql .= " FROM ".MAIN_DB_PREFIX."facture as f";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON pf.fk_facture = f.rowid";
$sql .= " WHERE f.entity = ".((int) $conf->entity);
$sql .= " AND f.fk_statut IN (2, 3)";
$sql .= " AND COALESCE(pf.datep, f.datef) BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql .= " GROUP BY f.rowid";
$sql .= " ORDER BY datum";
$resql = $this->db->query($sql);
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
$buchungen[] = (array)$obj;
}
}
}
// Lieferantenrechnungen (Ausgaben)
if ($typ == 'alle' || $typ == 'ausgabe') {
$sql = "SELECT 'ausgabe' as buchungstyp, f.rowid, f.ref,";
$sql .= " COALESCE(pf.datep, f.datef) as datum,";
$sql .= " CONCAT('Lieferantenrechnung ', COALESCE(f.ref_supplier, f.ref), ' - ', s.nom) as beschreibung,";
$sql .= " f.total_ht as netto, f.total_tva as steuer, f.total_ttc as brutto,";
$sql .= " s.nom as partner, 'facture_fourn' as quelle";
$sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON pf.fk_facturefourn = f.rowid";
$sql .= " WHERE f.entity = ".((int) $conf->entity);
$sql .= " AND f.fk_statut IN (2)";
$sql .= " AND COALESCE(pf.datep, f.datef) BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql .= " GROUP BY f.rowid";
$sql .= " ORDER BY datum";
$resql = $this->db->query($sql);
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
$buchungen[] = (array)$obj;
}
}
}
// Manuelle Buchungen
$sql = "SELECT b.typ as buchungstyp, b.rowid, b.ref,";
$sql .= " b.datum, b.beschreibung,";
$sql .= " b.betrag_netto as netto, b.betrag_ust as steuer, b.betrag_brutto as brutto,";
$sql .= " COALESCE(s.nom, '') as partner, 'manuell' as quelle";
$sql .= " FROM ".MAIN_DB_PREFIX."steuer_buchung as b";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON b.fk_soc = s.rowid";
$sql .= " WHERE b.entity = ".((int) $conf->entity);
$sql .= " AND b.status = 1";
if ($typ != 'alle') {
$sql .= " AND b.typ = '".$this->db->escape($typ)."'";
}
$sql .= " AND b.datum BETWEEN '".$this->db->escape($datum_von)."' AND '".$this->db->escape($datum_bis)."'";
$sql .= " ORDER BY b.datum";
$resql = $this->db->query($sql);
if ($resql) {
while ($obj = $this->db->fetch_object($resql)) {
$buchungen[] = (array)$obj;
}
}
// Nach Datum sortieren
usort($buchungen, function($a, $b) {
return strcmp($a['datum'], $b['datum']);
});
return $buchungen;
}
/**
* Generiert das EÜR-Format für die Anlage EÜR
*
* @return array EÜR-Zeilen
*/
public function getAnlageEUeR()
{
$zeilen = array();
// EINNAHMEN
$zeilen[10] = array('zeile' => 10, 'bezeichnung' => 'Steuerfreie Betriebseinnahmen', 'betrag' => 0);
$zeilen[11] = array('zeile' => 11, 'bezeichnung' => 'Innergemeinschaftliche Lieferungen', 'betrag' => 0);
$zeilen[12] = array('zeile' => 12, 'bezeichnung' => 'Ausfuhrlieferungen', 'betrag' => 0);
$zeilen[14] = array('zeile' => 14, 'bezeichnung' => 'Umsatzsteuerpflichtige Betriebseinnahmen', 'betrag' => $this->summe_einnahmen);
$zeilen[16] = array('zeile' => 16, 'bezeichnung' => 'Sonstige Betriebseinnahmen', 'betrag' => 0);
$zeilen[19] = array('zeile' => 19, 'bezeichnung' => 'Private Kfz-/Telefonnutzung', 'betrag' => 0);
// Summe Einnahmen
$zeilen[22] = array('zeile' => 22, 'bezeichnung' => 'Summe Betriebseinnahmen', 'betrag' => $this->summe_einnahmen, 'summe' => true);
// AUSGABEN
$zeilen[26] = array('zeile' => 26, 'bezeichnung' => 'Waren, Roh- und Hilfsstoffe', 'betrag' => 0);
$zeilen[31] = array('zeile' => 31, 'bezeichnung' => 'Löhne und Gehälter', 'betrag' => 0);
$zeilen[32] = array('zeile' => 32, 'bezeichnung' => 'Gesetzliche Sozialaufwendungen', 'betrag' => 0);
$zeilen[34] = array('zeile' => 34, 'bezeichnung' => 'Raumkosten', 'betrag' => 0);
$zeilen[36] = array('zeile' => 36, 'bezeichnung' => 'AfA auf Sachanlagen', 'betrag' => 0);
$zeilen[38] = array('zeile' => 38, 'bezeichnung' => 'Leasing, GWG', 'betrag' => 0);
$zeilen[45] = array('zeile' => 45, 'bezeichnung' => 'Schuldzinsen', 'betrag' => 0);
$zeilen[49] = array('zeile' => 49, 'bezeichnung' => 'Übrige Betriebsausgaben', 'betrag' => $this->summe_ausgaben);
$zeilen[51] = array('zeile' => 51, 'bezeichnung' => 'Fahrzeugkosten', 'betrag' => 0);
// Summe Ausgaben
$zeilen[67] = array('zeile' => 67, 'bezeichnung' => 'Summe Betriebsausgaben', 'betrag' => $this->summe_ausgaben, 'summe' => true);
// GEWINN
$zeilen[87] = array('zeile' => 87, 'bezeichnung' => 'Gewinn/Verlust', 'betrag' => $this->gewinn, 'ergebnis' => true);
return $zeilen;
}
}