/*
** Verwaltung der Rechnung
**
** Copyright (C) 1996 by Stefan Scherer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for non-commerical use and without fee is hereby granted,
** provided that the above copyright notice appear in all copies and that
** both that copyright notice and this permission notice appear in
** supporting documentation.  This software is provided "as is" without
** express or implied warranty.
*/

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <exec/lists.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <proto/exec.h>
#include <clib/alib_protos.h>

#include "Rechnungen.h"
#include "LocaleSupport.h"

/* die Rechnung erhält allgemeine Daten
** prinzipiell könnte man daraus auch schon eine Rechnungsliste
** machen und alle Funktionen erweitern, so daß sie nur mit Zeigern
** auf solche Strukturen arbeiten.
*/

struct Invoice
  {
    struct MinCustomer customer;	/* der Kunde dieser Rechnung */

    UWORD invoiceMode;		/* Rechnung, Lieferschein, ... */
    UWORD invoiceNumber;
    UWORD invoiceYear;		/* bilden beide die komplette Rech-Nr. */
    char invoiceDate[12];	/* Tag, an dem Rechnung erstellt wurde */

    char printDate[12];		/* Tag, an dem Rechnung gedruckt wurde */

    double payment;		/* der bisher bezahlte (Teil-)Betrag */
    char payDate[12];		/* Tag, an dem letzte Zahlung erfolgte */
    UWORD payMode;		/* Zahlungsart */

    UWORD orderNumber;
    UWORD orderYear;		/* bilden beide die komplette Auftrags-Nr. */
    char orderDate[12];		/* Tag, an dem Auftrag erstellt wurde */

    double MwSt1, MwSt2;	/* die für diese Rechnung gültige MwSt */

/* alle Artikel werden in einer Liste gehalten. Zusätzlich werden noch ein
** paar Informationen mitgeführt 
*/
    struct List invoiceList;	/* Liste aller Artikel in der Rechnung */
    struct invoiceNode *aktNode;	/* Zeiger auf aktuellen Artikel */
    UWORD aktNummer;		/* Nummer des aktuellen Eintrages in Liste */
    UWORD itemCount;		/* Anzahl der Listeneinträge */

/* 17.11.94: neue Einträge */

    char Unit[8];		/* Währung als String, DM */
    char GroupSeparator[8], DecimalPoint[8], FracGroupSeparator[8];
  };


/* jeder Artikel in der Rechnung hat folgenden Aufbau */

struct invoiceNode
  {
    struct Node Node;		/* Node zum verketten */
    struct MinArticle item;
  };


/* Länge einer Zeile im Rechnungsfile (Bezeichnung + Rest in einer Zeile ) */

#define MAX_LINE_LEN 200


/* intern wird nur eine Rechnung im Speicher gehalten. */

static struct Invoice inv;


/* Zeiger auf den Memory-Pool */

static void *poolHeader = NULL;	/* Zeiger für Memory-Pool */


/* der Status, ob die Rechnung verändert wurde
** TRUE  - geändert (-> muß noch gespeichert werden...)
** FALSE - nicht geändert
*/

static BOOL status;


/* Nummer der höchsten gespeicherten Rechnung */

static double MwSt1, MwSt2;


/* Prototypen für private Funktionen */

static double GetBruttoSum (void);


/* Fehlercode und Fehlerstrings */

static int error;
static const STRPTR errormsg[] =
{
  "",				//   ERR_INV_OK
   "kein memorypool",		//   ERR_INV_NOPOOL
   "kein Speicher",		//   ERR_INV_NOMEM
   "Liste ist leer",		//   ERR_INV_LISTEMPTY
   "Eintrag nicht in Liste",	//   ERR_INV_NOITEM
   "File nicht gefunden",	//   ERR_INV_FILENOTFOUND
   "Konnte File nicht erzeugen",	//   ERR_INV_CANTCREATEFILE
   "Fehler beim laden",		//   ERR_INV_LOAD
   "Fehler beim schreiben",	//   ERR_INV_SAVE
};

/*
** Konstruktor-Funktion zum Initialisieren 
*/

int 
ConstructInvoice (void)
{
  /* Mehrwertsteuer defaultmäßig setzen */

  MwSt1 = 15.0;
  MwSt2 = 7.0;

  return (I_NewInvoice ());
}


/*
** Destruktor-Funktion
*/

void 
DestructInvoice (void)
{
  if (poolHeader)
    {
      LibDeletePool (poolHeader);
      poolHeader = NULL;
    }
}


/*
** neue Rechnung beginnen. Alle Daten werden zurückgesetzt.
*/

int 
I_NewInvoice (void)
{
  status = FALSE;

  inv.customer.Adresse1[0] = 0;
  inv.customer.Adresse2[0] = 0;
  inv.customer.Adresse3[0] = 0;
  inv.customer.Adresse4[0] = 0;
  inv.customer.Adresse5[0] = 0;
  inv.customer.KundenNummer = 0;
  inv.customer.Zeichen[0] = 0;

  inv.invoiceMode = MODE_INVOICE;

  inv.invoiceNumber = 0;
  inv.invoiceYear = GetThisYear ();

  /* Rechnungsdatum auf heutigen Tag setzen */
  inv.invoiceDate[0] = 0;
  CorrectDate (inv.invoiceDate);

  inv.printDate[0] = 0;
  inv.payment = 0.0;
  inv.payDate[0] = 0;
  inv.payMode = PAY_BAR;

  inv.orderYear = 0;
  inv.orderNumber = 0;
  inv.orderDate[0] = 0;

  inv.MwSt1 = MwSt1;
  inv.MwSt2 = MwSt2;

  strcpy (inv.Unit, Locale->loc_MonCS);
  strcpy (inv.GroupSeparator, Locale->loc_MonGroupSeparator);
  strcpy (inv.DecimalPoint, Locale->loc_MonDecimalPoint);
  strcpy (inv.FracGroupSeparator, Locale->loc_MonFracGroupSeparator);


  /* Memory-Pool mit der Liste entfernen */
  NewList (&inv.invoiceList);
  inv.aktNode = NULL;
  inv.aktNummer = 0;
  inv.itemCount = 0;

  if (poolHeader)
    {
      LibDeletePool (poolHeader);
      poolHeader = NULL;
    }

  /* neuen Memory-Pool anfordern */
  if (!(poolHeader = LibCreatePool (MEMF_CLEAR, 4000, 4000)))
    return (error = ERR_INV_NOPOOL);

  return (error = ERR_INV_OK);
}


/*
** neue Kundenadresse übernehmen
*/

void 
I_SetInvoiceAddress (struct MinCustomer *cust)
{
  status = TRUE;
  strcpy (inv.customer.Adresse1, cust->Adresse1);
  strcpy (inv.customer.Adresse2, cust->Adresse2);
  strcpy (inv.customer.Adresse3, cust->Adresse3);
  strcpy (inv.customer.Adresse4, cust->Adresse4);
  strcpy (inv.customer.Adresse5, cust->Adresse5);
  strcpy (inv.customer.Zeichen, cust->Zeichen);
  inv.customer.KundenNummer = cust->KundenNummer;
}

void 
I_GetInvoiceAddress (struct MinCustomer *cust)
{
  strcpy (cust->Adresse1, inv.customer.Adresse1);
  strcpy (cust->Adresse2, inv.customer.Adresse2);
  strcpy (cust->Adresse3, inv.customer.Adresse3);
  strcpy (cust->Adresse4, inv.customer.Adresse4);
  strcpy (cust->Adresse5, inv.customer.Adresse5);
  strcpy (cust->Zeichen, inv.customer.Zeichen);
  cust->KundenNummer = inv.customer.KundenNummer;
}


/*
** Mehrwertsteuersätze einstellen
*/

void 
I_SetMwSt (double mwst1, double mwst2)
{
  status = TRUE;
  inv.MwSt1 = mwst1;
  inv.MwSt2 = mwst2;
}

/*
** Mehrwertsteuersätze auslesen
**
** wenn Preistab == 2 ist, dann bekommt man den zweiten MwSt-Satz (7%),
** sonst immer den ersten MwSt-Satz (15%)
*/

double 
I_GetMwSt (UWORD preistab)
{
  if (preistab == 2)
    return (inv.MwSt2);
  return (inv.MwSt1);
}



/*
** Rechnungsmodus einstellen
*/

void 
I_SetInvoiceMode (UWORD mode)
{
  status = TRUE;
  if (mode < MODE_MAX)
    inv.invoiceMode = mode;
}

/*
** Rechnungsmodus einstellen
*/

UWORD 
I_GetInvoiceMode (void)
{
  return (inv.invoiceMode);
}


/*
** Einen Artikel in die Rechnungsliste einfügen
**
** Parameter:
**    article - Bezeichnung für Rechnung
**    netto   - Betrag ohne Mehrwertsteuer
**    menge   - Stückzahl
**    mwstnr  - Nummer der Mehrwertsteuer (1=mwst1, 2=mwst2) !! NICHT 15 !!
**
** Rückgabe: Fehlercode oder 0
*/

int 
I_AddInvoiceItem (struct MinArticle *item)
{
  struct invoiceNode *newitem;

  if (!(newitem = LibAllocPooled (poolHeader, sizeof (struct invoiceNode))))
      return (error = ERR_INV_NOMEM);

  status = TRUE;
  newitem->item.Betrag = item->Betrag;
  newitem->item.Menge = item->Menge;
  newitem->item.Preistabelle = item->Preistabelle;
  strcpy (newitem->item.Artikelnummer, item->Artikelnummer);
  strcpy (newitem->item.Bezeichnung, item->Bezeichnung);
  AddTail (&inv.invoiceList, (struct Node *) newitem);

  inv.aktNode = newitem;
  inv.itemCount++;
  inv.aktNummer = inv.itemCount;	/* Zählung erfolgt von 1 ... */

  return (error = ERR_INV_OK);
}


/*
** Entferne den aktuellen Eintrag
*/

int 
I_DelInvoiceItem (void)
{
  struct invoiceNode *newakt;

  if (inv.itemCount == 0)
    return (error = ERR_INV_LISTEMPTY);

  status = TRUE;
  if (inv.aktNummer == inv.itemCount)
    {
      if (inv.itemCount > 1)
	newakt = (struct invoiceNode *) inv.aktNode->Node.ln_Pred;
      else
	newakt = NULL;

      RemTail (&inv.invoiceList);
      LibFreePooled (poolHeader, inv.aktNode, sizeof (struct invoiceNode));
      inv.aktNummer--;
      inv.itemCount--;
      inv.aktNode = newakt;
    }
  else
    {
      if (inv.itemCount > 1)
	newakt = (struct invoiceNode *) inv.aktNode->Node.ln_Succ;
      else
	{
	  newakt = NULL;
	  inv.aktNummer = 0;
	}

      Remove ((struct Node *) inv.aktNode);
      LibFreePooled (poolHeader, inv.aktNode, sizeof (struct invoiceNode));
      inv.itemCount--;
      inv.aktNode = newakt;
    }
  return (error = ERR_INV_OK);
}


/*
**  Ändere den aktuellen Eintrag
*/

int 
I_ChangeInvoiceItem (struct MinArticle *item)
{
  if (inv.itemCount == 0)
    return (error = ERR_INV_LISTEMPTY);

  status = TRUE;
  inv.aktNode->item.Betrag = item->Betrag;
  inv.aktNode->item.Menge = item->Menge;
  inv.aktNode->item.Preistabelle = item->Preistabelle;
  strcpy (inv.aktNode->item.Artikelnummer, item->Artikelnummer);
  strcpy (inv.aktNode->item.Bezeichnung, item->Bezeichnung);

  return (error = ERR_INV_OK);
}


/*
**  Lese den aktuellen Eintrag aus
*/

int 
I_GetCurrentItem (struct MinArticle *item)
{
  if (inv.itemCount == 0)
    return (error = ERR_INV_LISTEMPTY);

  item->Betrag = inv.aktNode->item.Betrag;
  item->Menge = inv.aktNode->item.Menge;
  item->Preistabelle = inv.aktNode->item.Preistabelle;
  strcpy (item->Artikelnummer, inv.aktNode->item.Artikelnummer);
  strcpy (item->Bezeichnung, inv.aktNode->item.Bezeichnung);

  return (error = ERR_INV_OK);
}


/*
**  Liest einen bestimmten Eintrag (num) aus
**  num - 0..itemCount-1
*/

int 
I_GetInvoiceItem (UWORD num, struct MinArticle *item)
{
  struct invoiceNode *node;
  int i;

  if (inv.itemCount == 0)
    return (error = ERR_INV_LISTEMPTY);

  if (num >= inv.itemCount)
    return (error = ERR_INV_NOITEM);

  for (i = 0, node = (struct invoiceNode *) inv.invoiceList.lh_Head;
       i < num && node->Node.ln_Succ;
       i++, node = (struct invoiceNode *) node->Node.ln_Succ);

  item->Betrag = node->item.Betrag;
  item->Menge = node->item.Menge;
  item->Preistabelle = node->item.Preistabelle;
  strcpy (item->Artikelnummer, node->item.Artikelnummer);
  strcpy (item->Bezeichnung, node->item.Bezeichnung);

  return (error = ERR_INV_OK);
}

/* für Demoversion werden nur beschränkte Daten ausgegeben */

int 
I_GetDummyInvoiceItem (UWORD num, struct MinArticle *item)
{
  struct invoiceNode *node;
  int i;

  if (inv.itemCount == 0)
    return (error = ERR_INV_LISTEMPTY);

  if (num >= inv.itemCount)
    return (error = ERR_INV_NOITEM);

  for (i = 0, node = (struct invoiceNode *) inv.invoiceList.lh_Head;
       i < num && node->Node.ln_Succ;
       i++, node = (struct invoiceNode *) node->Node.ln_Succ);

  item->Betrag = min (node->item.Betrag, 50.0);	 /* maximal 50,- DM Beträge */
  item->Menge = node->item.Menge;
  item->Preistabelle = node->item.Preistabelle;
  strcpy (item->Artikelnummer, "");
  strcpy (item->Bezeichnung, node->item.Bezeichnung);

  return (error = ERR_INV_OK);
}


/*
**  mache den n-ten Eintrag zum aktuellen Eintrag
**
**  Parameter:
**      n - Nummer des gewünschten Eintrages ( 0 ... itemCount-1 )
**
**  Rückgabe:
**      Fehlercode bei Überschreitung, etc. oder 0
*/

int 
I_SetCurrentItemNumber (UWORD number)
{
  int i;
  struct Node *node;

  if (inv.itemCount == 0)
    return (error = ERR_INV_LISTEMPTY);

  if (number >= inv.itemCount)
    return (error = ERR_INV_NOITEM);

  for (i = 0, node = inv.invoiceList.lh_Head; i < number;
       i++, node = node->ln_Succ);

  inv.aktNummer = number + 1;
  inv.aktNode = (struct invoiceNode *) node;

  return (error = ERR_INV_OK);
}

/*
** ermittle die Nummer des aktuellen Eintrags (0..itemCount-1)
*/

int 
I_GetCurrentItemNumber (void)
{
  return (inv.aktNummer - 1);
}

/*
** Anzahl der Einträge ermitteln
*/

int 
I_GetInvoiceItemCount (void)
{
  return (inv.itemCount);
}

/*
**  neue Rechnungsnummer erstellen
**
**  Die Rechnungsnummer wird nach der Form "Jahr/Nr" in den Puffer geschrieben.
*/

void 
I_GetInvoiceNumber (STRPTR buffer)
{
  sprintf (buffer, "%04d/%d", inv.invoiceYear, inv.invoiceNumber);
}


/*
**  Auftragsnummer auslesen
**
**  Die Auftragsnummer wird nach der Form "Jahr/Nr" in den Puffer geschrieben.
*/

void 
I_GetOrderNumber (STRPTR buffer)
{
  sprintf (buffer, "%04d/%d", inv.orderYear, inv.orderNumber);
}


/*
**  setze die Rechnungsnummer auf einen bestimmten Wert
**
**  Der übergebene String wird automatisch korrigiert !!
*/

void 
I_SetInvoiceNumber (STRPTR buffer)
{
  STRPTR p;

  status = TRUE;
  if (p = strchr (buffer, '/'))
    {
      inv.invoiceYear = atol (buffer);
      inv.invoiceNumber = atol (p + 1);
    }
  else
    {
      inv.invoiceYear = GetThisYear ();
      inv.invoiceNumber = atol (buffer);
      I_GetInvoiceNumber (buffer);
    }
}


/*
**  setze die Auftragsnummer auf einen bestimmten Wert
*/

void 
I_SetOrderNumber (STRPTR buffer)
{
  STRPTR p;

  status = TRUE;
  if (p = strchr (buffer, '/'))
    {
      inv.orderYear = atol (buffer);
      inv.orderNumber = atol (p + 1);
    }
  else
    {
      inv.orderYear = 0;	/* kein Auftrag vorhanden */
      inv.orderNumber = 0;
    }
}


/*
** Datum der Rechnungserstellung setzen
*/

void 
I_SetInvoiceDate (STRPTR date)
{
  status = TRUE;
  strncpy (inv.invoiceDate, date, 10);
  inv.invoiceDate[10] = 0;
}

/*
** Datum der Auftragserstellung setzen
*/

void 
I_SetOrderDate (STRPTR date)
{
  status = TRUE;
  strncpy (inv.orderDate, date, 10);
  inv.orderDate[10] = 0;
}

/*
** Datum des Ausdrucks setzen
*/

void 
I_SetPrintDate (STRPTR date)
{
  status = TRUE;
  strncpy (inv.printDate, date, 10);
  inv.printDate[10] = 0;
}


/*
** Datum der Rechnungserstellung lesen
** Zielpuffer muß min. 11 Zeichen lang sein!
*/

void 
I_GetInvoiceDate (STRPTR date)
{
  strncpy (date, inv.invoiceDate, 10);
  date[10] = 0;
}

/*
** Datum der Auftragserstellung lesen
** Zielpuffer muß min. 11 Zeichen lang sein!
*/

void 
I_GetOrderDate (STRPTR date)
{
  strncpy (date, inv.orderDate, 10);
  date[10] = 0;
}

/*
** Datum des Ausdrucks lesen
** Zielpuffer muß min. 11 Zeichen lang sein!
*/

void 
I_GetPrintDate (STRPTR date)
{
  strncpy (date, inv.printDate, 10);
  date[10] = 0;
}


/*
**  Infos über Bezahlung setzen
*/

void 
I_SetPayment (double amount, STRPTR date, UWORD mode)
{
  status = TRUE;
  inv.payment = amount;
  strncpy (inv.payDate, date, 10);
  inv.payDate[10] = 0;
  CorrectDate (inv.payDate);
  if (mode >= PAY_MAX)
    mode = PAY_BAR;
  inv.payMode = mode;
}

/*
**  Infos über Bezahlung auslesen
**  Datumpuffer muß min. 11 Zeichen lang sein!
*/

void 
I_GetPayment (double *amount, STRPTR date, UWORD * mode)
{
  *amount = inv.payment;
  strncpy (date, inv.payDate, 10);
  date[10] = 0;
  *mode = inv.payMode;
}


/*
**  Rechnung laden
**  Die bisherige Rechnung wird gelöscht!
**
** Die Rechnung wird als ASCII-Text gelesen und hat folgenden Inhalt:
**
** Zeile  Inhalt
** 
** 1      Anrede
** 2      Kundenadresse
** 3      Kundenadresse
** 4      Kundenadresse
** 5      Kundenadresse
** 6      Kundennummer
**
** 17.11.94:
**------
** 6a     TAB
**
** 6b     Währung TAB
** 6c     Trenner vorkomma TAB
** 6d     Komma TAB
** 6e     Trenner nachkomma
**------
** 7      Ihre Zeichen
** 8      Rechnungsjahr / Rechnungsnummer
** 9      Datum für Feld "Ihr Auftrag vom"
** 10     Rechnungsmodus
** 
** 11     Artikelbezeichnung TAB
**        Menge TAB
**        Einzel-netto TAB
**        MwSt-Nr TAB
**        Summe-netto TAB
**        Artikelnummer     (optional)
** 
** 14     Abschluß der Liste mit einem TAB + Return oder nur Return
** 
** 15     Nettosumme für MwSt1
** 16     Nettosumme für MwSt2
** 17     MwStsumme 1
** 18     MwStsumme 2
** 19     Bruttosumme
**
** ------- neue Rechnungen haben mehr Informationen -------
** 
** 20     Trennung mit Leerzeile
** 
** 21     Datum der Rechnungserstellung
** 22     Datum des Ausdrucks der Rechnung
** 23     (teilweise) bezahlter Betrag
** 24     Datum der (letzten) Zahlung
** 25     Zahlungsart
** 26     Auftragsjahr / Auftragsnummer
** 27     MwSt 1
** 28     MwSt 2
**
** 
*/

int 
I_LoadInvoice (STRPTR filename, BOOL bCorrectNumbers)
{
  FILE *file;
  int result;
  BOOL ok = TRUE, loop;
  char buffer[3 * MAX_LINE_LEN + 1];
  char *p, *oldp;
  double netto1, netto2, mwst1, mwst2;	/* für alte Rechnungen */


  struct MinArticle item;

  result = I_NewInvoice ();

  if (!result)
    {
      if (file = fopen (filename, "r"))
	{
	  /*
	  ** · Anrede
	  ** · Kundenadresse (4 Zeilen)
	  */
	  if (!fgets (inv.customer.Adresse1, MAX_INV_LEN, file))
	    ok = FALSE;
	  if (p = strchr (inv.customer.Adresse1, '\n'))
	    *p = 0;
	  if (ok && !fgets (inv.customer.Adresse2, MAX_INV_LEN, file))
	    ok = FALSE;
	  if (p = strchr (inv.customer.Adresse2, '\n'))
	    *p = 0;
	  if (ok && !fgets (inv.customer.Adresse3, MAX_INV_LEN, file))
	    ok = FALSE;
	  if (p = strchr (inv.customer.Adresse3, '\n'))
	    *p = 0;
	  if (ok && !fgets (inv.customer.Adresse4, MAX_INV_LEN, file))
	    ok = FALSE;
	  if (p = strchr (inv.customer.Adresse4, '\n'))
	    *p = 0;
	  if (ok && !fgets (inv.customer.Adresse5, MAX_INV_LEN, file))
	    ok = FALSE;
	  if (p = strchr (inv.customer.Adresse5, '\n'))
	    *p = 0;

	  /*
	  ** · Kundennummer
	  */
	  if (ok && !fgets (buffer, MAX_INV_LEN, file))
	    ok = FALSE;
	  if (ok)
	    inv.customer.KundenNummer = atol (buffer);

	  /* alte Rechnung geladen, Währung IST deutsch */
	  strcpy (inv.Unit, "DM");
	  strcpy (inv.GroupSeparator, ".");
	  strcpy (inv.DecimalPoint, ",");
	  strcpy (inv.FracGroupSeparator, ".");

	  if (p = strchr (buffer, '\t'))
	    {
	      oldp = ++p;

	      if (p = strchr (oldp, '\t'))
		{
		  *p++ = 0;
		  strcpy (inv.Unit, oldp);	/* Währung */
		  oldp = p;

		  if (p = strchr (oldp, '\t'))
		    {
		      *p++ = 0;
		      /* Trenner vorkomma */
		      strcpy (inv.GroupSeparator, oldp);
		      oldp = p;

		      if (p = strchr (oldp, '\t'))
			{
			  *p++ = 0;
			  /* Komma */
			  strcpy (inv.DecimalPoint, oldp);
			  oldp = p;

			  if (p = strchr (oldp, '\n'))
			    {
			      *p++ = 0;
			      /* Trenner nachkomma */
			      strcpy (inv.FracGroupSeparator, oldp);
			    }
			}
		    }
		}
	    }



	  /*
	  ** · Ihre Zeichen
	  */
	  if (ok && !fgets (inv.customer.Zeichen, MAX_INV_LEN, file))
	    ok = FALSE;
	  if (p = strchr (inv.customer.Zeichen, '\n'))
	    *p = 0;

	  /*
	  ** · Rechnungsjahr / Rechnungsnummer
	  */
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;
	  if (p = strchr (buffer, '\n'))
	    *p = 0;

	  /* da alte Rechnungen noch kein Jahr in der Rechnungsnummer hatten,
	  ** muß dieses automatisch eingefügt werden. Dies gilt nur für
	  ** Rechnungen aus dem Jahr 1993
	  */
	  if (ok)
	    {
	      STRPTR p;
	      if (p = strchr (buffer, '/'))
		{
		  inv.invoiceYear = atol (buffer);
		  inv.invoiceNumber = atol (p + 1);
		}
	      else
		{
		  inv.invoiceYear = 1993;
		  inv.invoiceNumber = atol (buffer);
		}
	    }

	  /*
	  ** · Datum für Feld "Ihr Auftrag vom"
	  */
	  if (ok && !fgets (inv.orderDate, 12, file))
	    ok = FALSE;
	  if (p = strchr (inv.orderDate, '\n'))
	    *p = 0;

	  /*
	  ** · Rechnungsmodus 
	  */
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;
	  if (ok)
	    inv.invoiceMode = atoi (buffer);
	  if (inv.invoiceMode < 0 || inv.invoiceMode > MODE_MAX)
	    inv.invoiceMode = MODE_INVOICE;

	  /* jetzt kommen die einzelnen Artikel in der Rechnung
	  ** bis eine Leerzeile kommt.
	  */
	  loop = TRUE;
	  while (loop && ok)
	    {
	      /*
	      ** · Artikelbezeichnung  (alles mit TAB getrennt in einer Zeile)
	      ** · Menge
	      ** · Einzel-netto
	      ** · MwSt-Nummer
	      ** · Summe netto
	      ** NEU
	      ** · Artikelnummer (optional)
	      ** · Farbe
	      ** · Größe
	      ** · Material
	      */
	      if (!fgets (buffer, MAX_LINE_LEN, file))
		ok = FALSE;
	      if (ok)
		{
		  if (p = strchr (buffer, '\t'))
		    {
		      if (p != buffer)
			{
			  *p++ = 0;
			  strcpy (item.Bezeichnung, buffer);
			  item.Menge = ScanMonetaryNew (p, inv.GroupSeparator,
			                               inv.DecimalPoint,
			                               inv.FracGroupSeparator);
			  if (p = strchr (p, '\t'))
			    {
			      p++;
			      item.Betrag = ScanMonetaryNew (p,
			                               inv.GroupSeparator,
			                               inv.DecimalPoint,
			                               inv.FracGroupSeparator);
			      if (p = strchr (p, '\t'))
				{
				  p++;
				  item.Preistabelle = atoi (p);

				  item.Artikelnummer[0] = 0;
				  if (p = strchr (p, '\t'))
				    {
				      p++;	/* Summe wird nicht benötigt */
				      if (p = strchr (p, '\t'))
					{
					  /* hinter der Summe ist noch was */
					  p++;

					  strcpy (item.Artikelnummer, p);
					  if (p = strchr (item.Artikelnummer,
					                  '\n'))
					    {
					      *p = 0;
					    }
					}
				    }
				}
			    }

			  if (I_AddInvoiceItem (&item))
			    {
			      ok = FALSE;
			    }

			  /* 3.3.96, ss: korrigiere Menge in Artikelliste */
			  /* Hier wird die Menge hinzugefügt. Beim Speichern */
			  /* wird wieder abgezogen. */

			  if (bCorrectNumbers)
			    A_LoadAddArticle (&item);
			}
		      else
			loop = FALSE;	/* letzter Eintrag erreicht */
		    }
		  else
		    loop = FALSE;	/* kein TAB vorhanden */
		}
	    }		/* liest alle Artikel ein, bis eine Leerzeile kommt */

	  /* 3.3.96, ss: speichere geänderte Artikelliste ab. */
	  if (bCorrectNumbers)
	    A_SaveArticles ();

	  /*
	  ** · Nettosumme 1         überspringen
	  ** · Nettosumme 2
	  ** · MwSt 1
	  ** · MwSt 2
	  ** · Bruttosumme
	  */
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;
	  if (ok)
	    netto1 = ScanMonetaryNew (buffer, inv.GroupSeparator,
	                              inv.DecimalPoint,
	                              inv.FracGroupSeparator);
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;
	  if (ok)
	    netto2 = ScanMonetaryNew (buffer, inv.GroupSeparator,
	                              inv.DecimalPoint,
	                              inv.FracGroupSeparator);
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;
	  if (ok)
	    mwst1 = ScanMonetaryNew (buffer, inv.GroupSeparator,
	                             inv.DecimalPoint,
	                             inv.FracGroupSeparator);
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;
	  if (ok)
	    mwst2 = ScanMonetaryNew (buffer, inv.GroupSeparator,
	                             inv.DecimalPoint, inv.FracGroupSeparator);
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;

	  if (!ok)
	    result = ERR_INV_LOAD;

	  /*
	  ** · Trennung mit Leerzeile
	  */
	  if (ok && !fgets (buffer, MAX_LINE_LEN, file))
	    ok = FALSE;

	  /* neue Rechnungen haben mehr Informationen.
	  ** Bei alten Rechnungen kommt man ans File-Ende, deshalb
	  ** sollte ok = FALSE werden.
	  ** Wenn dies der Fall ist, müssen die zusätzlichen Daten
	  ** vordefiniert werden.
	  */
	  if (!ok)
	    {
	      /* ---------------------------------
	      ** eine alte Rechnung wurde geladen.
	      ** ---------------------------------
	      **
	      ** Deshalb müssen einige Daten automatisch angepaßt werden.
	      */

	      /*
	      ** · Datum der Rechnungserstellung = Datum des Auftrags
	      ** · Datum der Ausdrucks = Datum des Auftrags
	      */
	      strcpy (inv.invoiceDate, inv.orderDate);
	      strcpy (inv.printDate, inv.orderDate);

	      /*
	      ** · bezahlter Betrag = Bruttosumme  -> alles bezahlt
	      ** · Datum der Zahlung = Datum des Auftrags
	      ** · Zahlungsart = Bar
	      **
	      */
	      inv.payment = GetBruttoSum ();
	      strcpy (inv.payDate, inv.orderDate);
	      inv.payMode = PAY_BAR;

	      /*
	      ** · Auftragsjahr / Auftragsnummer = nicht vorhanden, d.h. 0 / 0
	      */
	      inv.orderYear = 0;
	      inv.orderNumber = 0;

/* 23.12.94: hier ist noch nicht alles berücksichtigt !!!! */

	      /*
	      ** · Mehrwertsteuersätze werden aus den Summen berechnet
	      */
	      if (netto1 >= 0.10)
		inv.MwSt1 = (UWORD) ((mwst1 * 100) / netto1 + 0.5);
	      else
		inv.MwSt1 = MwSt1;

	      if (netto2 >= 0.10)
		inv.MwSt2 = (UWORD) ((mwst2 * 100) / netto2 + 0.5);
	      else
		inv.MwSt2 = MwSt2;
	    }
	  else
	    {
	      /* ---------------------------------
	      ** eine neue Rechnung wurde geladen,
	      ** ---------------------------------
	      **
	      ** alle Informationen sollten im File stecken.
	      */

	      /*
	      ** · Datum der Rechnungserstellung
	      */
	      if (ok && !fgets (inv.invoiceDate, 12, file))
		ok = FALSE;
	      if (p = strchr (inv.invoiceDate, '\n'))
		*p = 0;

	      /*
	      ** · Datum des Ausdrucks
	      */
	      if (ok && !fgets (inv.printDate, 12, file))
		ok = FALSE;
	      if (p = strchr (inv.printDate, '\n'))
		*p = 0;

	      /*
	      ** · (teilweise) bezahlter Betrag
	      */
	      if (ok && !fgets (buffer, MAX_LINE_LEN, file))
		ok = FALSE;
	      inv.payment = ScanMonetaryNew (buffer, inv.GroupSeparator,
	                                     inv.DecimalPoint,
	                                     inv.FracGroupSeparator);

	      /*
	      ** · Datum der (letzten) Bezahlung
	      */
	      if (ok && !fgets (inv.payDate, 12, file))
		ok = FALSE;
	      if (p = strchr (inv.payDate, '\n'))
		*p = 0;

	      /*
	      ** · Zahlungsart
	      */
	      if (ok && !fgets (buffer, MAX_INV_LEN, file))
		ok = FALSE;
	      if (ok)
		inv.payMode = atol (buffer);

	      /*
	      ** · Auftragsjahr / Auftragsnummer
	      */
	      if (ok && !fgets (buffer, MAX_LINE_LEN, file))
		ok = FALSE;
	      if (p = strchr (buffer, '\n'))
		*p = 0;

	      if (ok)
		{
		  STRPTR p;
		  if (p = strchr (buffer, '/'))
		    {
		      inv.orderYear = atol (buffer);
		      inv.orderNumber = atol (p + 1);
		    }
		  else
		    /* Fehler, dann kein Auftrag vorhanden */
		    {
		      inv.orderYear = 0;
		      inv.orderNumber = 0;
		    }
		}

	      /*
	      ** · MwSt 1
	      */
	      if (ok && !fgets (buffer, MAX_INV_LEN, file))
		ok = FALSE;
	      if (ok)
		inv.MwSt1 = ScanMonetaryNew (buffer, inv.GroupSeparator,
		                             inv.DecimalPoint,
		                             inv.FracGroupSeparator);

	      /*
	      ** · MwSt 2
	      */
	      if (ok && !fgets (buffer, MAX_INV_LEN, file))
		ok = FALSE;
	      if (ok)
		inv.MwSt2 = ScanMonetaryNew (buffer, inv.GroupSeparator,
		                             inv.DecimalPoint,
		                             inv.FracGroupSeparator);

	      if (!ok)
		result = ERR_INV_LOAD;

	    }

	  status = FALSE;   /* geladene Rechnung wurde noch nicht geändert */

	  /*
	  ** Nun wird die Währung an die aktuelle angepaßt.
	  */

	  strcpy (inv.Unit, Locale->loc_MonCS);
	  strcpy (inv.GroupSeparator, Locale->loc_MonGroupSeparator);
	  strcpy (inv.DecimalPoint, Locale->loc_MonDecimalPoint);
	  strcpy (inv.FracGroupSeparator, Locale->loc_MonFracGroupSeparator);


	  fclose (file);
	}
      else
	result = ERR_INV_FILENOTFOUND;
    }
  return (error = result);
}

/*
**  Rechnung speichern
**
** Die Rechnung wird als ASCII-Text gespeichert und hat folgenden Inhalt:
**
** Zeile  Inhalt
** 
** 1      Anrede
** 2      Kundenadresse
** 3      Kundenadresse
** 4      Kundenadresse
** 5      Kundenadresse
** 6      Kundennummer
**
** 17.11.94:
**------
** 6a     TAB
**
** 6b     Währung TAB
** 6c     Trenner vorkomma TAB
** 6d     Komma TAB
** 6e     Trenner nachkomma
**------
** 7      Ihre Zeichen
** 8      Rechnungsjahr / Rechnungsnummer
** 9      Datum für Feld "Ihr Auftrag vom"
** 10     Rechnungsmodus
** 
** 11     Artikelbezeichnung TAB
**        Menge TAB
**        Einzel-netto TAB
**        MwSt-Nr TAB
**        Summe-netto TAB
**        Artikelnummer TAB Farbe TAB Größe TAB Material
** 
** 14     Abschluß der Liste mit einer Leerzeile
** 
** 15     Nettosumme für MwSt1
** 16     Nettosumme für MwSt2
** 17     MwStsumme 1
** 18     MwStsumme 2
** 19     Bruttosumme
** 
** 20     Trennung mit Leerzeile
** 
** 21     Datum der Rechnungserstellung
** 22     Datum des Ausdrucks der Rechnung
** 23     (teilweise) bezahlter Betrag
** 24     Datum der (letzten) Zahlung
** 25     Zahlungsart
** 26     Auftragsjahr / Auftragsnummer
** 27     MwSt 1
** 28     MwSt 2
**
** 
*/

int 
I_SaveInvoice (STRPTR filename, BOOL bCorrectNumbers)
{
  FILE *file;
  int result = ERR_INV_OK;
  BOOL ok = TRUE;
  char einzbuf[20], sumbuf[20], mengebuf[20];
  struct invoiceNode *node;
  STRPTR str;
  double netto1, netto2, mwst1, mwst2, brutto;

  if (file = fopen (filename, "w"))
    {
      /*
      ** · Anrede
      ** · Kundenadresse
      */
      if (fprintf (file, "%s\n%s\n%s\n%s\n%s\n", inv.customer.Adresse1,
	inv.customer.Adresse2, inv.customer.Adresse3, inv.customer.Adresse4,
		   inv.customer.Adresse5) < 0)
	ok = FALSE;

      /*
      ** · Kundennummer TAB Währung TAB Trenner vor TAB Komma TAB Trenner nach
      ** · Ihre Zeichen
      ** · Rechnungsjahr
      ** · Rechnungsnummer
      */
      if (ok && fprintf (file, "%d\t%s\t%s\t%s\t%s\n%s\n%04d/%d\n",
		         inv.customer.KundenNummer, inv.Unit,
		         inv.GroupSeparator, inv.DecimalPoint,
		         inv.FracGroupSeparator, inv.customer.Zeichen,
		         inv.invoiceYear, inv.invoiceNumber) < 0)
	ok = FALSE;

      /*
      ** · Datum für Feld "Ihr Auftrag vom"
      ** · Rechnungsmodus TAB Kennzeichen TAB Fahrzeugnr TAB KfzZusatz
      */
      if (ok && fprintf (file, "%s\n%d\n", inv.orderDate, inv.invoiceMode) < 0)
	ok = FALSE;

      /* jetzt kommt die Liste aller Artikel */

      for (node = (struct invoiceNode *) inv.invoiceList.lh_Head;
	   ok && node->Node.ln_Succ;
	   node = (struct invoiceNode *) node->Node.ln_Succ)
	{
	  /*
	  ** · Artikelbezeichnung
	  ** · Menge
	  ** · Einzelbetrag netto
	  ** · MwSt-Nummer (1 oder 2)
	  ** · Summe netto
	  ** NEU
	  ** · Artikelnummer
	  ** · Farbe
	  ** · Größe
	  ** · Material
	  */
	  PrintMonetary (einzbuf, node->item.Betrag);
	  PrintMonetary (sumbuf, node->item.Betrag * node->item.Menge);
	  PrintMonetary (mengebuf, node->item.Menge);

	  /* falls die Bezeichnung leer ist, dann ein Space als Bezeichnung
	  ** speichern.
	  */
	  if (node->item.Bezeichnung[0])
	    str = node->item.Bezeichnung;
	  else
	    str = " ";

	  if (fprintf (file, "%s\t\%s\t%s\t%d\t%s\t%s\n",
		       str, mengebuf, einzbuf,
		       node->item.Preistabelle, sumbuf,
		       node->item.Artikelnummer ) < 0)
	    ok = FALSE;

	  /* 3.3.96, ss: korrigiere Menge in Artikelliste */
	  /* Hier wird die Menge hinzugefügt. Beim Speichern */
	  /* wird wieder abgezogen. */

	  if (bCorrectNumbers)
	    A_SaveSubArticle (&node->item);
	}

      /* 3.3.96, ss: speichere geänderte Artikelliste ab. */
      if (bCorrectNumbers)
	A_SaveArticles ();

      /* · Abschluß der Liste mit einer Leerzeile */
      /* zum Schluß noch die Summen, die jedoch nicht verwendet werden. */

      /*
      ** · Nettosumme für MwSt1
      ** · Nettosumme für MwSt2
      ** · MwStsumme 1
      ** · MwStsumme 2
      ** · Bruttosumme
      */
      I_GetInvoiceSum (&netto1, &netto2, &mwst1, &mwst2, &brutto);
      PrintMonetary (einzbuf, netto1);
      if (ok && fprintf (file, "\n%s\n", einzbuf) < 0)
	ok = FALSE;

      PrintMonetary (einzbuf, netto2);
      if (ok && fprintf (file, "%s\n", einzbuf) < 0)
	ok = FALSE;

      PrintMonetary (einzbuf, mwst1);
      if (ok && fprintf (file, "%s\n", einzbuf) < 0)
	ok = FALSE;

      PrintMonetary (einzbuf, mwst2);
      if (ok && fprintf (file, "%s\n", einzbuf) < 0)
	ok = FALSE;

      PrintMonetary (einzbuf, brutto);
      if (ok && fprintf (file, "%s\n", einzbuf) < 0)
	ok = FALSE;


      /* ab jetzt kommen zusätzliche Informationen, die die alten
      ** Rechnungen noch nicht beinhalten.
      */

      /*
      ** · Trennung mit Leerzeile
      ** · Datum der Rechnungserstellung
      ** · Datum des Ausdrucks der Rechnung
      */
      if (ok && fprintf (file, "\n%s\n%s\n", inv.invoiceDate,
			 inv.printDate) < 0)
	ok = FALSE;

      /*
      ** · (teilweise) bezahlter Betrag
      ** · Datum der (letzten) Zahlung
      ** · Zahlungsart
      ** · Auftragsjahr
      ** · Auftragsnummer
      ** · MwSt 1
      ** · MwSt 2
      */
      PrintMonetary (einzbuf, inv.payment);
      if (ok && fprintf (file, "%s\n%s\n%d\n%04d/%d\n", einzbuf,
			 inv.payDate, inv.payMode, inv.orderYear,
			 inv.orderNumber) < 0)
	ok = FALSE;

      PrintMonetary (einzbuf, inv.MwSt1);
      if (ok && fprintf (file, "%s\n", einzbuf) < 0)
	ok = FALSE;

      PrintMonetary (einzbuf, inv.MwSt2);
      if (ok && fprintf (file, "%s\n", einzbuf) < 0)
	ok = FALSE;

      if (ok)
	result = ERR_INV_OK;
      else
	result = ERR_INV_SAVE;

      status = FALSE;	/* gespeicherte Rechnung wurde nicht mehr geändert */

      fclose (file);
    }
  else
    result = ERR_INV_CANTCREATEFILE;

  return (error = result);
}


/*
** Ermittle die Summe
**
** wenn Einträge nicht gewünscht sind, so können diese mit NULL belegt werden.
*/

void 
I_GetInvoiceSum (double *netto1, double *netto2, double *summwst1,
		 double *summwst2, double *brutto)
{
  struct invoiceNode *node;

  double n1, n2, s1, s2, b;

  n1 = n2 = 0.0;
  for (node = (struct invoiceNode *) inv.invoiceList.lh_Head;
       node->Node.ln_Succ;
       node = (struct invoiceNode *) node->Node.ln_Succ)
    {
      if (node->item.Preistabelle == 1)
	n1 += node->item.Betrag * node->item.Menge;
      else
	n2 += node->item.Betrag * node->item.Menge;
    }
  s1 = (double) ((LONG) (n1 * inv.MwSt1 + .5)) / 100;
  s2 = (double) ((LONG) (n2 * inv.MwSt2 + .5)) / 100;
  b = n1 + n2 + s1 + s2;

  if (netto1)
    *netto1 = n1;
  if (netto2)
    *netto2 = n2;
  if (summwst1)
    *summwst1 = s1;
  if (summwst2)
    *summwst2 = s2;
  if (brutto)
    *brutto = b;
}



/*
** lies den Status aus, ob sich die Rechnung geändert hat (TRUE) oder
** nicht (FALSE).
*/

BOOL 
I_GetStatus (void)
{
  return (status);
}


/*
** interne Funktion, um die Bruttosumme zu ermitteln
*/

static double 
GetBruttoSum (void)
{
  double brutto;

  I_GetInvoiceSum (NULL, NULL, NULL, NULL, &brutto);
  return (brutto);
}

/*
** lies die wichtigsten Daten auf einen Schlag
*/

void 
I_GetMinInvoice (struct MinInvoice *mininvoice)
{
  char buffer[100];

  mininvoice->invoiceNumber = inv.invoiceNumber;
  mininvoice->invoiceYear = inv.invoiceYear;
  strcpy (mininvoice->invoiceDate, inv.invoiceDate);

  /* nur den Nachnamen finden */
  C_PrintName (&inv.customer, buffer);
  strcpy (mininvoice->Kunde, buffer);

  mininvoice->orderNumber = inv.orderNumber;
  mininvoice->orderYear = inv.orderYear;
  strcpy (mininvoice->orderDate, inv.orderDate);

  mininvoice->payment = inv.payment;
  mininvoice->sum = GetBruttoSum ();
  strcpy (mininvoice->payDate, inv.payDate);
  strcpy (mininvoice->printDate, inv.printDate);
}


/*
** Fehlerstring abholen
*/

STRPTR 
I_GetErrorMsg (void)
{
  return (errormsg[error]);
}
