Package de.willuhn.jameica.hbci.server.hbci

Source Code of de.willuhn.jameica.hbci.server.hbci.HBCIUmsatzJob

/**********************************************************************
* $Source: /cvsroot/hibiscus/hibiscus/src/de/willuhn/jameica/hbci/server/hbci/HBCIUmsatzJob.java,v $
* $Revision: 1.57 $
* $Date: 2012/03/01 22:19:15 $
* $Author: willuhn $
* $Locker:  $
* $State: Exp $
*
* Copyright (c) by willuhn.webdesign
* All rights reserved
*
**********************************************************************/
package de.willuhn.jameica.hbci.server.hbci;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.kapott.hbci.GV_Result.GVRKUms;
import org.kapott.hbci.GV_Result.GVRKUms.UmsLine;

import de.willuhn.datasource.GenericIterator;
import de.willuhn.datasource.GenericObject;
import de.willuhn.datasource.pseudo.PseudoIterator;
import de.willuhn.datasource.rmi.DBIterator;
import de.willuhn.jameica.hbci.HBCI;
import de.willuhn.jameica.hbci.HBCIProperties;
import de.willuhn.jameica.hbci.messaging.ImportMessage;
import de.willuhn.jameica.hbci.rmi.Konto;
import de.willuhn.jameica.hbci.rmi.Protokoll;
import de.willuhn.jameica.hbci.rmi.Umsatz;
import de.willuhn.jameica.hbci.server.Converter;
import de.willuhn.jameica.hbci.server.hbci.rewriter.RewriterRegistry;
import de.willuhn.jameica.hbci.server.hbci.rewriter.UmsatzRewriter;
import de.willuhn.jameica.messaging.StatusBarMessage;
import de.willuhn.jameica.plugin.PluginResources;
import de.willuhn.jameica.system.Application;
import de.willuhn.jameica.system.Settings;
import de.willuhn.jameica.util.DateUtil;
import de.willuhn.logging.Logger;
import de.willuhn.util.ApplicationException;

/**
* Job fuer "Umsatz-Abfrage".
*/
public class HBCIUmsatzJob extends AbstractHBCIJob
{
  private final static Settings settings = Application.getPluginLoader().getPlugin(HBCI.class).getResources().getSettings();

  private Konto konto     = null;
  private Date saldoDatum = null;

  /**
   * ct.
   * @param konto Konto, fuer das die Umsaetze abgerufen werden sollen.
   * @throws ApplicationException
   * @throws RemoteException
   */
  public HBCIUmsatzJob(Konto konto) throws ApplicationException, RemoteException
  {
    try
    {
      PluginResources res = Application.getPluginLoader().getPlugin(HBCI.class).getResources();
      if (konto == null)
        throw new ApplicationException(i18n.tr("Bitte w�hlen Sie ein Konto aus"));

      if (konto.isNewObject())
        konto.store();

      this.konto = konto;
     
      String curr = konto.getWaehrung();
      if (curr == null || curr.length() == 0)
        konto.setWaehrung(HBCIProperties.CURRENCY_DEFAULT_DE);
      setJobParam("my",Converter.HibiscusKonto2HBCIKonto(konto));
     
      this.saldoDatum = konto.getSaldoDatum();
      if (this.saldoDatum != null)
      {
        // BUGZILLA 917 - checken, ob das Datum vielleicht in der Zukunft liegt. Das ist nicht zulaessig
        Date now = new Date();
        if (saldoDatum.after(now))
        {
          Logger.warn("future start date " + saldoDatum + " given. this is not allowed, changing to current date " + now);
          this.saldoDatum = now;
        }
       
        // Mal schauen, ob wir ein konfiguriertes Offset haben
        int offset = res.getSettings().getInt("umsatz.startdate.offset",0);
        if (offset != 0)
        {
          Logger.info("using custom offset for startdate: " + offset);
          Calendar cal = Calendar.getInstance();
          cal.setTime(this.saldoDatum);
          cal.add(Calendar.DATE,offset);
          this.saldoDatum = cal.getTime();
        }
       
        this.saldoDatum = DateUtil.startOfDay(this.saldoDatum);
        Logger.info("startdate: " + HBCI.LONGDATEFORMAT.format(this.saldoDatum));
        setJobParam("startdate", this.saldoDatum);
      }
    }
    catch (RemoteException e)
    {
      throw e;
    }
    catch (ApplicationException e2)
    {
      throw e2;
    }
    catch (Throwable t)
    {
      Logger.error("error while executing job " + getIdentifier(),t);
      throw new ApplicationException(i18n.tr("Fehler beim Erstellen des Auftrags. Fehlermeldung: {0}",t.getMessage()),t);
    }
  }

  /**
   * @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#getIdentifier()
   */
  public String getIdentifier() {
    return "KUmsAll";
  }

  /**
   * @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#getName()
   */
  public String getName() throws RemoteException
  {
    return i18n.tr("Umsatzabruf {0}",konto.getLongName());
  }
 
  /**
   * @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#markExecuted()
   */
  void markExecuted() throws RemoteException, ApplicationException
  {
    konto.addToProtokoll(i18n.tr("Ums�tze abgerufen"),Protokoll.TYP_SUCCESS);

    // In HBCI gibts fuer Umsaetze ja keine eindeutigen IDs. Daher muessen
    // wir anhand der Eigenschaften vergleichen, ob wir den Umsatz schon
    // haben oder nicht. Mehrere Umsaetze mit gleichen Eigenschaften werden
    // von Hibiscus daher als "der selbe" erkannt und nicht erneut in der Datenbank
    // angelegt. In 99% der Faelle ist das auch korrekt. Unter Umstaenden kann
    // eine Buchung jedoch tatsaechlich identisch aussehen und trotzdem nicht
    // die selbe sein. Da den Banken diese Problematik ebenfalls bekannt ist,
    // verweigern die meisten das Einreichen von mehreren identischen Auftraegen
    // innerhalb eines Tages. Allerdings machen das nicht alle Banken. Und manche
    // tolerieren es auch, wenn man den Auftrag nach Erhalt des Doppel-Einreichungs-
    // Fehlers nochmal einreicht. Wir haben hierzu eine Map, die fuer mehrfach
    // vorhandene Umsaetze zaehlt, wie oft sie bereits lokal in der DB vorliegen.
    // Schickt die Bank mehr, als wir in der DB haben, muessen wir die verbleibenden
    // noch anlegen.
    Map<Umsatz,Integer> duplicates = new HashMap<Umsatz,Integer>();
   
    boolean fetchUnbooked  = settings.getBoolean("umsatz.fetchnotbooked",true);

    GVRKUms result         = (GVRKUms) getJobResult();
    List<UmsLine> booked   = result.getFlatData();
    List<UmsLine> unbooked = fetchUnbooked ? result.getFlatDataUnbooked() : null;
    Date d                 = this.getMergeWindow(booked,unbooked);

    // zu mergende Umsaetze ermitteln
    DBIterator existing = konto.getUmsaetze(d,null);
   
    ////////////////////////////////////////////////////////////////////////////
    // Gebuchte Umsaetze
    if (booked != null && booked.size() > 0)
    {
      int created = 0;
      int skipped = 0;
      Logger.info("applying booked entries");
     
      UmsatzRewriter rewriter = RewriterRegistry.getRewriter(konto.getBLZ(),konto.getKontonummer());
     
      for (int i=0;i<booked.size();++i)
      {
        final Umsatz umsatz = Converter.HBCIUmsatz2HibiscusUmsatz((GVRKUms.UmsLine)booked.get(i));
        umsatz.setKonto(konto); // muessen wir noch machen, weil der Converter das Konto nicht kennt

        if (rewriter != null)
        {
          try
          {
            rewriter.rewrite(umsatz);
          }
          catch (Exception e) {
            Logger.error("error while rewriting umsatz",e);
          }
        }

        Umsatz fromDB = null;
        // Anzahl der vorhandenen Umsaetze in der DB zaehlen
        int counter = 0;
        existing.begin();
        for (int j = 0; j<existing.size(); j++)
        {
          GenericObject dbObject = existing.next();
          if (dbObject.equals(umsatz)) {
            counter++;
            fromDB = (Umsatz) dbObject; //wir merken uns immer den letzten Umsatz
          }
        }
       
        if (fromDB != null)
        {
          // Wir duerfen den Umsatz nur dann ueberspringen, wenn er bereits
          // OFT GENUG in der Datenbank ist. Andernfalls ist er tatsaechlich
          // neu. Dazu zaehlen wir mit, wie oft wir gerade einen "gleichen"
          // Umsatz empfangen haben.
          Integer countInCurrentJobResult = duplicates.get(fromDB);
          if (countInCurrentJobResult == null) {
            duplicates.put(fromDB, 1);
            skipped++;
            continue;
          }
          else if (countInCurrentJobResult <= counter
          {
            // In der Datenbank sind mehr als bislang abgerufen -> Ueberspringen
            duplicates.put(fromDB, countInCurrentJobResult+1);
            skipped++;
            continue;
          }
        }

        // Umsatz neu anlegen
        try
        {
          umsatz.store(); // den Umsatz haben wir noch nicht, speichern!
          Application.getMessagingFactory().sendMessage(new ImportMessage(umsatz));
          created++;
        }
        catch (Exception e2)
        {
          Application.getMessagingFactory().sendMessage(new StatusBarMessage(i18n.tr("Nicht alle empfangenen Ums�tze konnten gespeichert werden. Bitte pr�fen Sie das System-Protokoll"),StatusBarMessage.TYPE_ERROR));
          Logger.error("error while adding umsatz, skipping this one",e2);
        }
      }
      Logger.info("done. new entries: " + created + ", skipped entries (already in database): " + skipped);
    }
    else
    {
      Logger.info("got no new booked entries");
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    duplicates.clear();

    ////////////////////////////////////////////////////////////////////////////
    // Vorgemerkte Umsaetze
    if (fetchUnbooked)
    {
      // Den Abgleich gegen die Vormerkbuchungen machen wir gegen alle
      // vorhandenen Vormerkbuchungen, nicht nur gegen die aus dem Zeitraum
      // der aktuellen Lieferung. Denn hier wollen wir nicht nur Doppler
      // vermeiden sondern ausserdem auch die loeschen, die von der Bank nicht
      // mehr geliefert werden. Die sind zwischenzeitlich valutiert worden
      // und muessen in Hibiscus geloescht werden.
      DBIterator existingUnbooked = konto.getUmsaetze(null,null);
      existingUnbooked.addFilter("flags = " + Umsatz.FLAG_NOTBOOKED);

      if (unbooked != null && unbooked.size() > 0)
      {
        List<Umsatz> fetched = new ArrayList<Umsatz>();
       
        int created = 0;
        int skipped = 0;
        Logger.info("applying not-booked (vorgemerkte) entries");
        for (int i=0;i<unbooked.size();++i)
        {
          final Umsatz umsatz = Converter.HBCIUmsatz2HibiscusUmsatz((GVRKUms.UmsLine)unbooked.get(i));
          umsatz.setFlags(Umsatz.FLAG_NOTBOOKED);
          umsatz.setSaldo(0d); // Muss gemacht werden, weil der Saldo beim naechsten Mal anders lauten koennte
          umsatz.setKonto(konto);
          fetched.add(umsatz);

          Umsatz fromDB = null;
          // Anzahl der vorhandenen Umsaetze in der DB zaehlen
          int counter = 0;
          existingUnbooked.begin();
          for (int j = 0; j<existingUnbooked.size(); j++)
          {
            GenericObject dbObject = existingUnbooked.next();
            if (dbObject.equals(umsatz))
            {
              counter++;
              fromDB = (Umsatz) dbObject; //wir merken uns immer den letzten Umsatz
            }
          }
         
          if (fromDB != null)
          {
            // Wir duerfen den Umsatz nur dann ueberspringen, wenn er bereits
            // OFT GENUG in der Datenbank ist. Andernfalls ist er tatsaechlich
            // neu. Dazu zaehlen wir mit, wie oft wir gerade einen "gleichen"
            // Umsatz empfangen haben.
            Integer countInCurrentJobResult = duplicates.get(fromDB);
            if (countInCurrentJobResult == null)
            {
              duplicates.put(fromDB, 1);
              skipped++;
              continue;
            }
            else if (countInCurrentJobResult <= counter
            {
              // In der Datenbank sind mehr als bislang abgerufen -> Ueberspringen
              duplicates.put(fromDB, countInCurrentJobResult+1);
              skipped++;
              continue;
            }
          }
         
          // Vormerkposten neu anlegen
          try
          {
            umsatz.store();
            Application.getMessagingFactory().sendMessage(new ImportMessage(umsatz));
            created++;
          }
          catch (Exception e2)
          {
            Application.getMessagingFactory().sendMessage(new StatusBarMessage(i18n.tr("Nicht alle empfangenen Ums�tze konnten gespeichert werden. Bitte pr�fen Sie das System-Protokoll"),StatusBarMessage.TYPE_ERROR));
            Logger.error("error while adding umsatz, skipping this one",e2);
          }
        }
       
        // Jetzt loeschen wir all die vorgemerkten Umsaetze des
        // Kontos, die noch in der Datenbank sind, aber im
        // aktuellen Durchlauf nicht mehr uebertragen wurden.
        // Das muessen dann die vom Vortag sein
        Logger.info("clean obsolete notbooked entries");
        GenericIterator newList = PseudoIterator.fromArray((Umsatz[]) fetched.toArray(new Umsatz[fetched.size()]));
        int deleted = 0;
        existingUnbooked.begin();
        while (existingUnbooked.hasNext())
        {
          Umsatz u = (Umsatz) existingUnbooked.next();
         
          if (!u.hasFlag(Umsatz.FLAG_NOTBOOKED))
            continue; // nur zur Sicherheit, dass wir nicht versehentlich welche loeschen, die keine Vormerkbuchungen sind

          // Mal schauen, ob der im aktuellen Durchlauf enthalten war:
          if (newList.contains(u) == null)
          {
            // Wurde nicht mehr von der Bank uebertragen, kann daher raus
            u.delete();
            deleted++;
          }
        }
        Logger.info("removed entries: " + deleted);
        Logger.info("done. new entries: " + created + ", skipped entries (already in database): " + skipped);
      }
      else
      {
        Logger.info("got no new not-booked (vorgemerkte) entries");
       
        // Keine neuen vorgemerkten Umsaetze
        // Dann loeschen wir pauschal alle, die in der Vergangenheit liegen
        // (mindestens gestern).
        Logger.info("clean obsolete not-booked entries");
        Date current = DateUtil.startOfDay(new Date());
        int count = 0;
        existingUnbooked.begin();
        while (existingUnbooked.hasNext())
        {
          Umsatz u = (Umsatz) existingUnbooked.next();
         
          if (!u.hasFlag(Umsatz.FLAG_NOTBOOKED))
            continue; // nur zur Sicherheit, dass wir nicht versehentlich welche loeschen, die keine Vormerkbuchungen sind

          Date test = u.getDatum();
          if (test == null)
            test = u.getValuta();
         
          if (test == null)
          {
            Logger.warn("notbooked entry contains no date, skipping");
            continue; // Das darf eigentlich nicht passieren
          }
         
          // Wenn die Vormerkbuchung nicht von heute ist, loeschen wir sie
          if (test.before(current))
          {
            u.delete();
            count++;
          }
        }
        Logger.info("removed entries: " + count);
      }
    }
    else
    {
      Logger.info("fetching of not-booked (vorgemerkte) entries disabled");
    }
    //
    ////////////////////////////////////////////////////////////////////////////

    Logger.info("umsatz list fetched successfully");
  }
 
  /**
   * @see de.willuhn.jameica.hbci.server.hbci.AbstractHBCIJob#markFailed(java.lang.String)
   */
  String markFailed(String error) throws RemoteException, ApplicationException
  {
    String msg = i18n.tr("Fehler beim Abrufen der Ums�tze: {0}",error);
    konto.addToProtokoll(msg,Protokoll.TYP_ERROR);
    return msg;
  }
 
  /**
   * Liefert das Beginn-Datum des Merge-Window.
   * @param booked Liste der abgerufenen Buchungen. Wenn da welche enthalten
   * sind, wird das Datum der darin befindlichen aeltesten Buchung verwendet.
   * Andernfalls wird das Start-Datum des Abrufs (minus 30 Tage) verwendet.
   * @param unbooked die optionale Liste der Vormerkbuchungen. Insofern die
   * Bank welche geliefert hat und das Abrufen der Vormerkbuchungen aktiviert ist.
   * Andernfalls ist der Parameter NULL.
   * @return das Beginn-Datgum des Merge-Window.
   */
  private Date getMergeWindow(List<UmsLine> booked, List<UmsLine> unbooked)
  {
    Date d = null;
   
    String basedOn = null;
   
    if (booked != null && booked.size() > 0)
    {
      for (UmsLine line:booked)
      {
        if (line.bdate == null)
          continue;
       
        if (d == null || line.bdate.before(d))
        {
          d = line.bdate;
          basedOn = "fetched booked entries";
        }
      }
    }
   
    if (unbooked != null && unbooked.size() > 0)
    {
      for (UmsLine line:unbooked)
      {
        if (line.bdate == null)
          continue;
       
        if (d == null || line.bdate.before(d))
        {
          d = line.bdate;
          basedOn = "fetched not-booked entries";
        }
      }
    }

   
    if (d == null && this.saldoDatum != null)
    {
      Calendar cal = Calendar.getInstance();
      cal.setTime(this.saldoDatum);
      cal.add(Calendar.DATE,settings.getInt("umsatz.mergewindow.offset",-30));
      d = cal.getTime();
      basedOn = "last sync";
    }
   
    if (d == null)
      Logger.info("merge window: not set");
    else
      Logger.info("merge window: " + d + " - now (based on " + basedOn + ")");
   
    return d;
  }
}
TOP

Related Classes of de.willuhn.jameica.hbci.server.hbci.HBCIUmsatzJob

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.