<?php
/**
 * @copyright (c) Kufer Software GmbH, Oderstraße 7, D-84453 Mühldorf
 */


class ZahlungAPI
{
  
  public static function getNachlassZahlungen($pZahlungen, $pNachlassBetrag, $pNachlassGrund, $pNachlassArt, $pIsNachlassByAnmeldung)
  {
    $zahlungenNachlass = array();
    $gesamtNachlass = $pNachlassBetrag;
    $zahlungIds = ArrayOperations::mapAttribut($pZahlungen, 'id');
    $negativeMaxZahlungsId = min($zahlungIds);
    $gesamtGebuehr = self::berechneGesamtGebuehr($pZahlungen);

    $summeNachlassBetraege = 0;

    $faktorJeGebuehrenbestandteil = $gesamtNachlass / $gesamtGebuehr;

    
    
    
    foreach ($pZahlungen as $zahlung) {
      if (self::isKeinGebuehrenTeil($zahlung)) {
        continue;
      }

      $nachlassBetrag = round(MathOperations::stringToFloat($zahlung->betrag) * $faktorJeGebuehrenbestandteil, 2);

      $tmpZahlung = clone($zahlung);
      $tmpZahlung->id = --$negativeMaxZahlungsId;
      $tmpZahlung->datum = DateTimeOperations::getDateNow();
      $tmpZahlung->uhrzeit = DateTimeOperations::getTimeNow();
      $tmpZahlung->betrag = MathOperations::floatToString($nachlassBetrag);
      $tmpZahlung->text = self::getNachlassGrund($pNachlassGrund, $pNachlassArt, $tmpZahlung->text, $faktorJeGebuehrenbestandteil * 100); 
      $tmpZahlung->beleg = 'Nachl.';
      $tmpZahlung->bearbdatum = DateTimeOperations::getDateNow();
      $zahlungenNachlass[] = $tmpZahlung;

      $summeNachlassBetraege += $nachlassBetrag;
    }

    
    
    
    
    if ($summeNachlassBetraege != $gesamtNachlass) {
      $korrekturBetrag = MathOperations::stringToFloat($zahlungenNachlass[0]->betrag) + $gesamtNachlass - $summeNachlassBetraege;
      $zahlungenNachlass[0]->betrag = MathOperations::floatToString($korrekturBetrag);
    }

    
    
    
    
    if (!$pIsNachlassByAnmeldung) {
      
      foreach ($zahlungenNachlass as &$zahlungNachlass) {
        $zahlungNachlass = self::setzeEinzelPreisUndFaktor($zahlungNachlass);
      }
    }

    return $zahlungenNachlass;
  }
  

  
  public static function getNachlassZahlung($pZahlungen, float $pNachlassBetrag, $pNachlassGrund, $pNachlassArt, $pIsNachlassByAnmeldung)
  {
    $result = array();
    $zahlungIds = ArrayOperations::mapAttribut($pZahlungen, 'id');
    $negativeMaxZahlungsId = min($zahlungIds);
    $gesamtGebuehr = self::berechneGesamtGebuehr($pZahlungen);

    $faktorJeGebuehrenbestandteil = $pNachlassBetrag / $gesamtGebuehr;

    $relevanteZahlung = null;
    foreach ($pZahlungen as $zahlung) {
      if (is_null($relevanteZahlung) && !self::isKeinGebuehrenTeil($zahlung)) {
        $relevanteZahlung = $zahlung; 
      }
    }

    if (!is_null($relevanteZahlung)) {
      $nachlassBetrag = $pNachlassBetrag;

      $tmpZahlung = clone($relevanteZahlung);
      $tmpZahlung->id = --$negativeMaxZahlungsId;
      $tmpZahlung->datum = DateTimeOperations::getDateNow();
      $tmpZahlung->uhrzeit = DateTimeOperations::getTimeNow();
      $tmpZahlung->betrag = $nachlassBetrag;
      $tmpZahlung->text = self::getNachlassGrund($pNachlassGrund, $pNachlassArt, $tmpZahlung->text, $faktorJeGebuehrenbestandteil * 100); 
      $tmpZahlung->beleg = 'Nachl.';
      $tmpZahlung->bearbdatum = DateTimeOperations::getDateNow();

      
      
      
      
      if (!$pIsNachlassByAnmeldung) {
        $tmpZahlung = self::setzeEinzelPreisUndFaktor($tmpZahlung);
      }

      $result[] = $tmpZahlung;
    }

    return $result;
  }

  
  public static function getBarZahlungen($pZahlungen, $pBelegungen, $pOrigBelegungen)
  {
    $barZahlungen = array();

    foreach ($pOrigBelegungen as $belegung) {
      $belId = $belegung->id;
      
      $aktZahlungAry = array_filter($pZahlungen, function($zahlung) use ($belId) {return $zahlung->belegid == $belId;});
      $zahlungen4Belegung = array_values($aktZahlungAry);
      $zahlungIds4Belegung = ArrayOperations::mapAttribut($zahlungen4Belegung, 'id');
      $negativeMaxZahlungsId4Belegung = min($zahlungIds4Belegung);

      
      
      if ($belegung->bet_offen > 0) {
        foreach ($zahlungen4Belegung as $zahlung) {
          if (self::isBarZahlungGebuehrenTeil($zahlung)) {
            
            $barZahlungen[] = self::getBarZahlungObj($negativeMaxZahlungsId4Belegung, $zahlung);
          }
        }
      } 
    } 

    return $barZahlungen;
  }

  

  
  private static function getBarZahlungObj($pNegMaxZahlungsId, $pZahlung)
  {
    $barZahlungObj = clone($pZahlung);
    
    
    $barZahlungObj->id = --$pNegMaxZahlungsId;
    $barZahlungObj->datum = DateTimeOperations::getDateNow();
    $barZahlungObj->uhrzeit = DateTimeOperations::getTimeNow();
    $barZahlungObj->sollist = 'I';
    $barZahlungObj->abw_typ = 'T';
    
    $barZahlungObj->abw_nr = 0;
    $barZahlungObj->zahlart = 'B'; 
    
    $barZahlungObj->text = $pZahlung->text . ' normale Verbuchung';
    
    $barZahlungObj->betrag = $pZahlung->betrag;
    
    $barZahlungObj->beleg = '';

    
    $barZahlungObj->bearbdatum = DateTimeOperations::getDateNow();

    return $barZahlungObj;
  }

  
  public static function getZahlungen4BelegungAnmelden($pBelegung, $pStatusNeu, $pToolSettings, $pGetGebuehrenCallback = null)
  {
    static $cache = array();

    if (!isset($cache[$pBelegung->id])) {
      $basysbenutzer = \SessionOperations::getCurrentWebToolsMitarbeiterOrEmpty();

      $dbwZahlung = DBWorkZahlung::getDefaultInstance();
      if (System::isKuferSql5() && $pStatusNeu === 'A'
          && isset($pToolSettings->zahlungenAusVorlagenUebernehmen) && $pToolSettings->zahlungenAusVorlagenUebernehmen
          && $dbwZahlung->existZahlungenForKnrAndBelegIdAndSollIst($pBelegung->knr, $pBelegung->id, array('V'))) {
        $zahlungen = self::getZahlungen4BelegungAnmeldenFromVorlagen($pBelegung, $basysbenutzer);
      } else {
        $zahlungen = self::getNormaleZahlungen4BelegungAnmelden($pBelegung, $basysbenutzer, $pGetGebuehrenCallback);
      }

      $kursgebuehrenVorhanden = count($zahlungen) > 0;

      $neutraleZahlungen = self::getNeutraleZahlungen4BelegungAnmelden($pBelegung, $pStatusNeu, $kursgebuehrenVorhanden, $basysbenutzer);

      $cache[$pBelegung->id] = array_merge($zahlungen, $neutraleZahlungen);
    }

    return $cache[$pBelegung->id];
  }

  
  private static function getNormaleZahlungen4BelegungAnmelden($pBelegung, $basysbenutzer, $pGetGebuehrenCallback = null)
  {
    $dbwZahlung = DBWorkZahlung::getDefaultInstance();
    $dbwKursGeb = DBWorkKursgebuehr::getDefaultInstance();
    $dbwStammBv = DBWorkStammBv::getDefaultInstance();

    if (!is_null($pGetGebuehrenCallback)) {
      $kursgebuehren = call_user_func($pGetGebuehrenCallback, $pBelegung);
    } else {
      $zahlplanKursnummer = $pBelegung->abwzpid > 0 ? $pBelegung->knr . '  ' . $pBelegung->abwzpid : '';
      $kursgebuehren = $dbwKursGeb->getKursgebuehrenErmaessigtTab($pBelegung->knr, $pBelegung->tnr, '', '', '', null, $zahlplanKursnummer);
    }

    $zahlungen = array();
    $gebuehrenCount = count($kursgebuehren);

    for ($i = 0; $i < $gebuehrenCount; $i++) {
      $zahlung = $dbwZahlung->getZahlungssatz4AnmeldungNormal($pBelegung->id, $pBelegung->knr, $pBelegung->tnr, $kursgebuehren[$i], $basysbenutzer->kennung);
      if (System::isKuferSql5()) {
        $stammBv = $dbwStammBv->getHauptStammBvByTnr($zahlung->zpf);
        $zahlung->bankid = $stammBv->id;
      }
      $zahlungen[] = $zahlung;
    }

    return $zahlungen;
  }

  
  private static function getNeutraleZahlungen4BelegungAnmelden($pBelegung, $pStatusNeu, $pKursgebuehrenVorhanden, $pBasysbenutzer)
  {
    $neutraleZahlungen = array();
    $dbwZahlung = DBWorkZahlung::getDefaultInstance();

    $statusAlt = $pBelegung->status;
    $statusWechsel = $statusAlt;
    if ($statusWechsel == '' || $statusWechsel == ' ') {
      $statusWechsel = 'A';
    }
    $statusWechsel .= '->' . $pStatusNeu;

    $text = 'Status von ' . $statusAlt . ' auf ' . $pStatusNeu . ' gesetzt';
    $neutraleZahlungen[] = $dbwZahlung->getNeutralenZahlungssatz($pBelegung->knr, $pBelegung->tnr, $pBelegung->id, $pBasysbenutzer, $text, 17, $statusWechsel, $statusWechsel, $pBasysbenutzer->kennung);

    if (!$pKursgebuehrenVorhanden) {
      $neutraleZahlungen[] = $dbwZahlung->getNeutralenZahlungssatz($pBelegung->knr, $pBelegung->tnr, $pBelegung->id, $pBasysbenutzer, 'Anmeldung ohne Gebühr', 19, '', '', $pBasysbenutzer->kennung);
    }
    if ($dbwZahlung->isWiederAnmeldung($pBelegung->knr, $pBelegung->tnr)) {
      $neutraleZahlungen[] = $dbwZahlung->getNeutralenZahlungssatz($pBelegung->knr, $pBelegung->tnr, $pBelegung->id, $pBasysbenutzer, 'Wieder-Anmeldung', 13, '', '', $pBasysbenutzer->kennung);
    }

    return $neutraleZahlungen;
  }

  
  private static function getZahlungen4BelegungAnmeldenFromVorlagen($pBelegung, $pBasysbenutzer)
  {
    $dbwZahlung = DBWorkZahlung::getDefaultInstance();
    $zahlungenAusVorlagen = $dbwZahlung->getZahlungenByKnrAndBelegIdAndSollIst($pBelegung->knr, $pBelegung->id, array('V'));
    $zahlungen = array();

    foreach ($zahlungenAusVorlagen as $zahlungAusVorlage) {
      $zahlungen[] = $dbwZahlung->getZahlungssatzAusVorlage4Anmeldung($zahlungAusVorlage, $pBasysbenutzer->kennung);
    }

    return $zahlungen;
  }

  
  public static function getKursgebuehrFromZahlungen(array $pZahlungen)
  {
    $summeKursgebuehr = 0;
    foreach ($pZahlungen as $zahlung) {
      $summeKursgebuehr += MathOperations::stringToFloat($zahlung->betrag);
    }

    return MathOperations::floatToString($summeKursgebuehr);
  }

  
  public static function getZahlungen4BelegungStatusAendernEinfach($pBelegung, $pStatusNeu, $zielkursnr = '')
  {
    static $cache = array();
    if (!isset($cache[$pBelegung->id])) {
      $dbwBasysUser = DBWorkBASysUser::getDefaultInstance();
      $dbwZahlung = DBWorkZahlung::getDefaultInstance();

      $kennung = '';
      $basysbenutzer = null;
      $benutzer = SessionOperations::getCurrentWebToolsUser();
      if (!$benutzer->isEmpty()) {
        $basysbenutzer = $dbwBasysUser->getBASysUser($benutzer->ben);
        $kennung = $basysbenutzer->kennung;
      }

      $zahlungen = array();

      $statusAlt = $pBelegung->status;
      $statusWechsel = $statusAlt;
      if ($statusWechsel == '' || $statusWechsel == ' ') {
        $statusWechsel = 'A';
      }
      $statusWechsel .= '->' . $pStatusNeu;

      if ($pStatusNeu === 'W') {
        $text = 'Auf Warteliste gesetzt';
      } elseif ($pStatusNeu === 'P') {
        $text = 'Platz reserviert';
      } elseif ($pStatusNeu === 'S') {
        $text = 'Stornierung';
      } elseif ($pStatusNeu === 'U') {
        $text = 'Ummeldung in Kurs ' . $zielkursnr;
      } else {
        $text = 'Auf Status ' . $pStatusNeu . ' gesetzt';
      }

      $zahlungen[] = $dbwZahlung->getNeutralenZahlungssatz($pBelegung->knr, $pBelegung->tnr, $pBelegung->id, $basysbenutzer, $text, 17, $statusWechsel, $statusWechsel, $kennung);

      $cache[$pBelegung->id] = $zahlungen;
    }

    return $cache[$pBelegung->id];
  }

  
  public static function getZahlungen4Storno($pBelegung, $pStatus, $pBearbeiterKuerzel = '')
  {
    $dbwZahlung = DBWorkZahlung::getDefaultInstance(1, true);
    $vorhandeneZahlungen = $dbwZahlung->getZahlungJournalByBelegId($pBelegung->id, true);
    $direktMaKuerzel = \ToolSettingsAPI::getToolSettings(0)->direktMaKuerzel;
    $bearbeiterKuerzel = $pBearbeiterKuerzel != '' ? $pBearbeiterKuerzel : $direktMaKuerzel;

    $alterStatus = $pBelegung->status;
    $statusWechsel = $alterStatus;
    if ($statusWechsel == '' || $statusWechsel == ' ') {
      $statusWechsel = 'A';
    }
    $statusWechsel .= '->' . $pStatus;
    $zahlungenToInsert = array();
    $zahlungenToUpdate = array();

    foreach ($vorhandeneZahlungen as $zahlung) {
      if ($zahlung->sollist != 'N' && $zahlung->sollist != 'V') {
        $zahlung->dp = 'S';
        $zahlung->invisibleDP = 'S'; 

        $neueZahlung = $zahlung->copy(); 
        $neueZahlung->id = 0;
        if ($zahlung->betrag == '0,00') {
          $neueZahlung->betrag = '0,00';
        } else {
          $neueZahlung->betrag = MathOperations::floatToString(-1 * MathOperations::stringToFloat($neueZahlung->betrag));
        }
        $neueZahlung->org_zahlid = $zahlung->id;
        $neueZahlung->text = 'Storno von ' . $zahlung->text;
        $neueZahlung->datum = DateTimeOperations::getDateNow();
        $neueZahlung->uhrzeit = DateTimeOperations::getTimeNow();
        $neueZahlung->bearbdatum = DateTimeOperations::getDateTimeNow();
        $neueZahlung->bearbeiter = $bearbeiterKuerzel;

        $zahlungenToUpdate[] = $zahlung;
        $zahlungenToInsert[] = $neueZahlung;
      }
    }

    if (!empty($vorhandeneZahlungen) && $alterStatus != $pStatus) {
      $zahlungenToInsert[] = $dbwZahlung->getNeutralenZahlungssatz(
        $vorhandeneZahlungen[0]->knr,
        $vorhandeneZahlungen[0]->tnr,
        $vorhandeneZahlungen[0]->belegid,
        null,
        'Stornierung',
        11,
        $statusWechsel,
        $statusWechsel,
        $bearbeiterKuerzel
      );
    }

    return array('insert' => $zahlungenToInsert, 'update' => $zahlungenToUpdate);
  }

  
  public static function getZahlungen4Kursausfall($pBelegId, $pStornoGebuehrenAusbuchen, $pIsErstAusfall, $pDatum, $pUhrzeit, $pZahlungId, $pBearbeiterKuerzel)
  {
    $dbwZahlung = DBWorkZahlung::getDefaultInstance(1, true);
    $vorhandeneZahlungen = $dbwZahlung->getZahlungenForKursausfall($pBelegId, $pStornoGebuehrenAusbuchen, $pIsErstAusfall, $pDatum, $pUhrzeit, $pZahlungId);

    $zahlungenToInsert = array();
    $zahlungenToUpdate = array();

    foreach ($vorhandeneZahlungen as $zahlung) {
      if ($zahlung->zahlnummer == 0) { 
        $zahlung->dp = 'S';
        $zahlungenToUpdate[] = $zahlung;
      }

      $neueZahlung = $zahlung->copy();
      $neueZahlung->id = 0;
      if ($zahlung->betrag == '0,00') {
        $neueZahlung->betrag = '0,00';
      } else {
        $neueZahlung->betrag = MathOperations::floatToString(-1 * MathOperations::stringToFloat($neueZahlung->betrag));
      }
      $neueZahlung->text = StringOperations::substr($zahlung->text . ' bei Kursausfall', 0, 60);
      $neueZahlung->datum = DateTimeOperations::getDateNow();
      $neueZahlung->uhrzeit = DateTimeOperations::getTimeNow();
      $neueZahlung->bearbdatum = DateTimeOperations::getDateTimeNow();
      $neueZahlung->bearbeiter = $pBearbeiterKuerzel;

      $zahlungenToInsert[] = $neueZahlung;
    }

    return array('insert' => $zahlungenToInsert, 'update' => $zahlungenToUpdate);
  }

  
  public static function berechneGesamtGebuehr($pZahlungen): float
  {
    $gesamtGebuehr = 0.0;
    foreach ($pZahlungen as $zahlung) {
      
      if (self::isKeinGebuehrenTeil($zahlung)) {
        continue;
      }
      $gesamtGebuehr += MathOperations::stringToFloat($zahlung->betrag);
    }

    return $gesamtGebuehr;
  }

  
  public static function berechneGesamtGebuehrMitNachlass($pZahlungen): float
  {
    $gesamtGebuehr = 0.0;
    foreach ($pZahlungen as $zahlung) {
      
      if (self::isKeinGebuehrenTeilMitNachlass($zahlung)) {
        continue;
      }
      $gesamtGebuehr += MathOperations::stringToFloat($zahlung->betrag);
    }

    return $gesamtGebuehr;
  }

  
  private static function isKeinGebuehrenTeil($pZahlung)
  {
    
    
    
    return (StringOperations::strtolower($pZahlung->sollist) == 'n' || StringOperations::strtolower($pZahlung->beleg) == 'nachl.'
                || StringOperations::strtolower($pZahlung->beleg) == 'korr.' || StringOperations::strtolower($pZahlung->sollist) == 'i'
                || StringOperations::strtolower($pZahlung->dp) == 's');
  }

  
  private static function isKeinGebuehrenTeilMitNachlass($pZahlung)
  {
    
    
    
    return (StringOperations::strtolower($pZahlung->sollist) == 'n'
      || StringOperations::strtolower($pZahlung->sollist) == 'i'
      || StringOperations::strtolower($pZahlung->dp) == 's');
  }

  
  private static function isBarZahlungGebuehrenTeil($pZahlung)
  {
    
    
    
    
    
    
    
    
    $isKeinRelevanterGebuehrenTeil = (
      StringOperations::strtolower($pZahlung->sollist) == 'n'
                 || StringOperations::strtolower($pZahlung->sollist) == 'i'
                || (StringOperations::strtolower($pZahlung->sollist) == 's' && $pZahlung->betrag == 0)
                  || StringOperations::strtolower($pZahlung->dp) == 's'
    );

    return !$isKeinRelevanterGebuehrenTeil;
  }

  
  public static function setzeEinzelPreisUndFaktor($pZahlung)
  {
    $pZahlung->einzelpreis = 0.0;
    $pZahlung->faktor = 0;

    return $pZahlung;
  }

  
  public static function getNachlassGrund($pNachlassGrund, $pNachlassArt, $pGebuehrenText, $pNachlassInProzent)
  {
    $nachlassGrund = $pNachlassGrund;
    if ($pNachlassArt !== 'Nachlass') {
      $nachlassInProzent = round($pNachlassInProzent);
      $nachlassGrund .= ' ' . $pGebuehrenText . ' (' . $nachlassInProzent . ' %)';
    }

    return $nachlassGrund;
  }

  
  public static function insertZahlungen($pZahlungen, $pLogMessageBegin = 'Nachlass bearbeiten')
  {
    $dbwzahlung = new DBWorkZahlung();
    $allgsettings = AllgSettingsAPI::getAllgSettings();
    if (SessionOperations::isReplikat() && $allgsettings->dmzEnabled) {
      
      
      
      $insertZahlIdAry = array();
      foreach ($pZahlungen as $zahlung) {
        
        $insertId = $dbwzahlung->insertZahlung($zahlung, true);
        $insertZahlIdAry[] = $insertId;
      }
      try {
        self::correctZahlungen($pZahlungen, 'insert');
      } catch (AbgleichApiException $aae) {
        Logger::logg($pLogMessageBegin . ' - folgende Exception ist aufgetreten: ' . $aae->getMessage() . "\n" . ' [' . $aae->getTraceAsString() . ']', 201);
        
        foreach ($insertZahlIdAry as $insertZahlId) {
          $dbwzahlung->deleteZahlung($insertZahlId);
        }

        return false;
      }
    }
    
    else {
      
      foreach ($pZahlungen as $zahlung) {
        
        $dbwzahlung->insertZahlung($zahlung, false);
      }
    }

    return true;
  }

  
  public static function correctZahlungen($pZahlungen, $pCorrectMode = 'insert')
  {
    if (empty($pZahlungen)) {
      return 'noCorrection';
    }
    $abgleichAPI = new AbgleichAPI();
    if (!$abgleichAPI->isAktiviert()) {
      return 'nichtAktiviert';
    }

    $message = new XMLNode('zahlungen');

    
    
    $dbworkWebReplikatSettings = new DBWorkWebReplikatSettings();
    $webreplikatsetting = $dbworkWebReplikatSettings->getMaxPk('zahlung');
    $childNode = new XMLNode('highestpk', '-', 'leer="true"');
    if (isset($webreplikatsetting) && $webreplikatsetting->value != '') {
      $childNode = new XMLNode('highestpk', $webreplikatsetting->value);
    }
    $message->addChild($childNode);
    $nothingToDo = true;

    
    foreach ($pZahlungen as $zahlung) {
      $message->addChild(XMLNode::getXMLNode($zahlung, 'zahlung', array(), true));
      $nothingToDo = false;
    }
    if (!$nothingToDo) {
      
      
      
      
      
      $operation = AbgleichAPI::ABGLEICH_ZAHLUNG; 

      $abgleichAPI = new AbgleichAPI();
      
      $abgleichAPI->sendeNachricht($operation, new Nutzlast($message));

      return 'sendDone';
    } 
    
    return 'noCorrection';
  }

  public static function updateZahlungenNurPerDMZ($zahlungenTupelMitOrig, $pNoMapping = false)
  {
    $message = new XMLNode('zahlungen');

    foreach ($zahlungenTupelMitOrig as $zahlungenMitOrig) {
      $zahlungNeu = $zahlungenMitOrig->getFirst();
      $zahlungOrig = $zahlungenMitOrig->getSecond();

      $diffFields = $zahlungNeu->getDiffFields($zahlungOrig);
      $diffFields[] = 'id';

      $msgZahlung = XMLNode::getXmlNodeOnlySpecificFields($zahlungNeu, 'zahlung', $diffFields, true);
      $msgZahlung->addChild(XMLNode::getXmlNodeOnlySpecificFields($zahlungOrig, 'oldobject', $diffFields, true));
      $message->addChild($msgZahlung);
    }

    $nutzlast = new Nutzlast($message);
    if ($pNoMapping) {
      $nutzlast->addNode(new XMLNode('nomapping', 'W'));
    }

    $abgleichAPI = new AbgleichAPI();
    try {
      $abgleichAPI->sendeNachricht(
        AbgleichAPI::ABGLEICH_ZAHLUNG_UPDATE,
        $nutzlast
      );
    } catch (\Exception $e) {
      Logger::logg('Fehler beim Ändern der Zahlungen per DMZ: ' . $e->getMessage(), 410);

      return false;
    }

    return true;
  }
}