/*
** Verwaltung von Artikeln
**
** 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 <proto/locale.h>
#include <clib/alib_protos.h>

#include "LocaleSupport.h"
#include "MiscSupport.h"
#include "Artikel.h"

/* der Filename für die Artikelliste */

#define ARTICLE_FILENAME "Artikelliste.dat"
#define FILENAME_CATEGORY "Artikelkategorien.dat"

/* Vergleichsfunktion für die sortierte Liste */

static int CompareArticles (struct artNode *l, struct artNode *r);
static int CompareCategories (struct categoryNode *l, struct categoryNode *r);


/* jeder Artikel wird in der Liste als Node gespeichert */

struct artNode
  {
    struct Node Node;
    struct Article article;
  };

struct categoryNode
  {
    struct Node Node;
    char category[MAX_ART_LEN];
  };

static struct List categoryList;
static struct categoryNode *aktCategoryNode;
static UWORD categoryCount;	/* Anzahl Kategorien in der Liste */


static struct List artlist;	/* die Artikel-Liste als Exec-Liste */
static struct artNode *aktNode;	/* der aktuelle Eintrag in der Liste */
static UWORD itemCount;		/* die Anzahl der Einträge in der Liste */

static void *poolHeader = NULL;	/* Zeiger für Memory-Pool */
static BOOL status;		/* Status, ob Liste geändert wurde */


static UWORD GetNextArticleNumber (void);
static BOOL NumberNotExists (STRPTR);

/*
** Der Fehlercode, wenn etwas passiert ist, wird hier nochmals gespeichert.
** Damit man später den Fehlerstring abholen kann.
*/

static int error;

static const STRPTR errormsg[] =
{
  "",				//   ERR_ART_OK
   "kein memorypool",		//   ERR_ART_NOPOOL
   "kein Speicher",		//   ERR_ART_NOMEM
   "Liste ist leer",		//   ERR_ART_LISTEMPTY
   "Eintrag nicht in Liste",	//   ERR_ART_NOITEM
   "File nicht gefunden",	//   ERR_ART_FILENOTFOUND
   "konnte File nicht erzeugen",	//   ERR_ART_CANTCREATEFILE
   "Fehler beim speichern",	//   ERR_ART_SAVE
};

/*
** Initialisierungen für die Artikelliste
*/

int
ConstructArticle (void)
{
  NewList (&artlist);
  NewList (&categoryList);
  aktNode = NULL;
  itemCount = 0;
  categoryCount = 0;
  aktCategoryNode = NULL;

  error = ERR_ART_OK;
  status = FALSE;

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

  A_LoadArticles ();

  return (error = ERR_ART_OK);
}

/*
** Speicher freigeben und sonstige Aufräumarbeiten
*/

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


/* füge einen neuen Artikel in die Liste ein */

int
A_AddItem (struct Article *item)
{
  struct artNode *newitem;

  if (!(newitem = LibAllocPooled (poolHeader, sizeof (struct artNode))))
      return (error = ERR_ART_NOMEM);

  status = TRUE;

  newitem->article.Betrag = item->Betrag;
  newitem->article.Menge = item->Menge;
  newitem->article.Preistabelle = item->Preistabelle;
  newitem->article.EKBetrag = item->EKBetrag;
  newitem->article.VGLBetrag = item->VGLBetrag;
  newitem->article.EKVersand = item->EKVersand;



  if (strchr (item->Artikelnummer, '?'))
    {
      sprintf (newitem->article.Artikelnummer, "%d",
	       GetNextArticleNumber () + 1);
    }
  else
    {
      /* prüfe, ob Artikelnummer bereits existiert */
      if (NumberNotExists (item->Artikelnummer))
	{
	  strcpy (newitem->article.Artikelnummer, item->Artikelnummer);
	}
      else
	{
	  sprintf (newitem->article.Artikelnummer, "%d",
		   GetNextArticleNumber () + 1);
	}
    }

  strcpy (newitem->article.Bezeichnung, item->Bezeichnung);
  strcpy (newitem->article.Lieferfirma, item->Lieferfirma);
  strcpy (newitem->article.Kategorie, item->Kategorie);
  strcpy (newitem->article.Notiz, item->Notiz);
  strcpy (newitem->article.Datum, item->Datum);

  InsertSorted (&artlist, (struct Node *) newitem,
		(struct Node *) aktNode, CompareArticles);

  aktNode = newitem;
  itemCount++;

  return (error = ERR_ART_OK);
}


/* lösche einen Artikel aus der Liste */

int
A_DelItem (void)
{
  struct artNode *newakt;

  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  status = TRUE;
  if (!aktNode->Node.ln_Succ->ln_Succ)
    {
      if (itemCount > 1)
	newakt = (struct artNode *) aktNode->Node.ln_Pred;
      else
	newakt = NULL;

      RemTail (&artlist);
      LibFreePooled (poolHeader, aktNode, sizeof (struct artNode));
      itemCount--;
      aktNode = newakt;
    }
  else
    {
      if (itemCount > 1)
	newakt = (struct artNode *) aktNode->Node.ln_Succ;
      else
	{
	  newakt = NULL;
	}

      Remove ((struct Node *) aktNode);
      itemCount--;
      aktNode = newakt;
    }

  return (error = ERR_ART_OK);
}


/* ändere den aktuellen Eintrag in der Liste */

int
A_ChangeItem (struct Article *item)
{
  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  status = TRUE;
  aktNode->article.Betrag = item->Betrag;
  aktNode->article.Menge = item->Menge;
  aktNode->article.Preistabelle = item->Preistabelle;
  aktNode->article.EKBetrag = item->EKBetrag;
  aktNode->article.VGLBetrag = item->VGLBetrag;
  aktNode->article.EKVersand = item->EKVersand;
  strcpy (aktNode->article.Artikelnummer, item->Artikelnummer);
  strcpy (aktNode->article.Bezeichnung, item->Bezeichnung);
  strcpy (aktNode->article.Lieferfirma, item->Lieferfirma);
  strcpy (aktNode->article.Kategorie, item->Kategorie);
  strcpy (aktNode->article.Notiz, item->Notiz);
  strcpy (aktNode->article.Datum, item->Datum);

  Remove ((struct Node *) aktNode);
  InsertSorted (&artlist, (struct Node *) aktNode,
		NULL, CompareArticles);

  return (error = ERR_ART_OK);
}


/* mache den 'number'ten Eintrag zum aktuellen Eintrag */

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

  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  if (number >= itemCount)
    return (error = ERR_ART_NOITEM);

  for (i = 0, node = artlist.lh_Head; i < number; i++, node = node->ln_Succ);
  aktNode = (struct artNode *) node;

  return (error = ERR_ART_OK);
}


/* mache den Artikel mit der genauen Beschreibung zum aktuellen Eintrag */

int
A_SetCurrentArticle (struct Article *article)
{
  int i;
  struct artNode *node;
  BOOL searching = TRUE;

  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  for (i = 0, node = (struct artNode *) artlist.lh_Head;
       node->Node.ln_Succ && searching;
       i++, node = (struct artNode *) node->Node.ln_Succ)
    {
      if (!stricmp (article->Bezeichnung, node->article.Bezeichnung) &&
	  !stricmp (article->Artikelnummer, node->article.Artikelnummer) &&
	  !stricmp (article->Kategorie, node->article.Kategorie) &&
	  !stricmp (article->Lieferfirma, node->article.Lieferfirma))
	{
	  searching = FALSE;
	  aktNode = node;
	}
    }

  if (searching)
    return (error = ERR_ART_NOITEM);

  return (error = ERR_ART_OK);
}


/* lade die Artikel-Liste aus dem File und baue sie intern als Liste auf. */

int
A_LoadArticles (void)
{
  FILE *file;
  struct Article item;
  BOOL ok = TRUE;
  char buffer[MAX_ART_LEN + 1];
  STRPTR p;
  int result;

  NewList (&artlist);
  itemCount = 0;
  aktNode = NULL;

  NewList (&categoryList);
  categoryCount = 0;
  aktCategoryNode = NULL;

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

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

  result = ERR_ART_OK;

  if (file = fopen (ARTICLE_FILENAME, "r"))
    {
      while (ok)
	{
	  /*
	  ** · Bezeichnung
	  ** · Betrag
	  ** · EKBetrag
	  ** · EKVersand
	  ** · VGLBetrag
	  ** · Artikelnummer
	  ** · Preistabelle 
	  ** · Menge TAB Farbe TAB Größe TAB Material
	  ** · Datum
	  ** · Lieferfirma
	  ** · Kategorie
	  ** · Notiz
	  */
	  if (!fgets (item.Bezeichnung, MAX_ART_LEN, file))
	    ok = FALSE;
	  if (p = strchr (item.Bezeichnung, '\n'))
	    *p = 0;

	  if (ok && !fgets (buffer, MAX_ART_LEN, file))
	    ok = FALSE;
	  item.Betrag = ScanMonetaryMulti (buffer);

	  if (ok && !fgets (buffer, MAX_ART_LEN, file))
	    ok = FALSE;
	  item.EKBetrag = ScanMonetaryMulti (buffer);

	  if (ok && !fgets (buffer, MAX_ART_LEN, file))
	    ok = FALSE;
	  item.EKVersand = ScanMonetaryMulti (buffer);

	  if (ok && !fgets (buffer, MAX_ART_LEN, file))
	    ok = FALSE;
	  item.VGLBetrag = ScanMonetaryMulti (buffer);

	  if (ok && !fgets (item.Artikelnummer, MAX_ART_LEN, file))
	    ok = FALSE;
	  if (p = strchr (item.Artikelnummer, '\n'))
	    *p = 0;
	  /* "0" wird zum Leerstring */
	  if (!strcmp ("0", item.Artikelnummer))
	    item.Artikelnummer[0] = 0;

	  /* 3.3.96, ss: leere Artikelnummern werden zu "?" */
	  if (strlen (item.Artikelnummer) == 0)
	    strcpy (item.Artikelnummer, "?");

	  if (ok && !fgets (buffer, MAX_ART_LEN, file))
	    ok = FALSE;
	  item.Preistabelle = atoi (buffer);


	  if (ok && !fgets (buffer, MAX_ART_LEN, file))
	    ok = FALSE;
	  item.Menge = ScanMonetaryMulti (buffer);

	  if (ok && !fgets (item.Datum, MAX_ART_LEN, file))
	    ok = FALSE;
	  if (p = strchr (item.Datum, '\n'))
	    *p = 0;

	  if (ok && !fgets (item.Lieferfirma, MAX_ART_LEN, file))
	    ok = FALSE;
	  if (p = strchr (item.Lieferfirma, '\n'))
	    *p = 0;

	  if (ok && !fgets (item.Kategorie, MAX_ART_LEN, file))
	    ok = FALSE;
	  if (p = strchr (item.Kategorie, '\n'))
	    *p = 0;

	  if (ok && !fgets (item.Notiz, MAX_ART_LEN, file))
	    ok = FALSE;
	  if (p = strchr (item.Notiz, '\n'))
	    *p = 0;

	  /*
	  ** · Leerzeile
	  */

	  if (ok && !fgets (buffer, MAX_ART_LEN, file))
	    ok = FALSE;

	  if (ok && A_AddItem (&item))
	    ok = FALSE;
	}

      fclose (file);
    }

  /* lade Kategorien ein */

  if (result == ERR_ART_OK)
    {
      ok = TRUE;

      if (file = fopen (FILENAME_CATEGORY, "r"))
	{
	  while (ok)
	    {
	      /*
	      ** · Kategorie (usw.)
	      */
	      if (!fgets (buffer, MAX_ART_LEN, file))
		ok = FALSE;
	      if (ok)
		{
		  if (p = strchr (buffer, '\n'))
		    *p = 0;

		  if (result = A_AddCategory (buffer))
		    ok = FALSE;
		}
	    }
	  fclose (file);
	}
      else
	result = ERR_ART_FILENOTFOUND;
    }

  status = FALSE;

  return (error = result);
}


/* speichere die Liste in einem File */

int
A_SaveArticles (void)
{
  FILE *file;
  BOOL ok = TRUE;
  struct artNode *node;
  struct categoryNode *cnode;
  int result;
  char buf[20], ekbuf[20], ekvbuf[20], vglbuf[20], mengebuf[20];

  result = error = ERR_ART_OK;

  if (file = fopen (ARTICLE_FILENAME, "w"))
    {
      for (node = (struct artNode *) artlist.lh_Head;
	   ok && node->Node.ln_Succ;
	   node = (struct artNode *) node->Node.ln_Succ)
	{
	  /*
	  ** · Bezeichnung
	  ** · Betrag
	  ** · EKBetrag
	  ** · EKVersand
	  ** · VGLBetrag
	  ** · Artikelnummer
	  ** · Preistabelle 
	  ** · Menge TAB Farbe TAB Größe TAB Material
	  ** · Datum
	  ** · Lieferfirma
	  ** · Kategorie
	  ** · Notiz
	  ** · Leerzeile
	  */
	  PrintMonetary (buf, node->article.Betrag);
	  PrintMonetary (ekbuf, node->article.EKBetrag);
	  PrintMonetary (vglbuf, node->article.VGLBetrag);
	  PrintMonetary (ekvbuf, node->article.EKVersand);
	  PrintMonetary (mengebuf, node->article.Menge);
	  if (fprintf (file, "%s\n%s\n%s\n%s\n%s\n%s\n%d\n%s"
		       "\n%s\n%s\n%s\n%s\n\n",
		       node->article.Bezeichnung, buf, ekbuf, ekvbuf, vglbuf,
		    node->article.Artikelnummer, node->article.Preistabelle,
		       mengebuf,
		       node->article.Datum,
		       node->article.Lieferfirma, node->article.Kategorie,
		       node->article.Notiz) < 0)
	    ok = FALSE;
	}
      if (!ok)
	result = ERR_ART_SAVE;
      fclose (file);
    }
  else
    result = ERR_ART_CANTCREATEFILE;

  if (result == ERR_ART_OK)
    {
      if (file = fopen (FILENAME_CATEGORY, "w"))
	{
	  for (cnode = (struct categoryNode *) categoryList.lh_Head;
	       ok && cnode->Node.ln_Succ;
	       cnode = (struct categoryNode *) cnode->Node.ln_Succ)
	    {
	      /*
	      ** · Kategorie (usw.)
	      */

	      if (fprintf (file, "%s\n", cnode->category) < 0)
		ok = FALSE;
	    }
	  if (!ok)
	    result = ERR_ART_SAVE;
	  fclose (file);
	}
      else
	result = ERR_ART_CANTCREATEFILE;
    }

  status = FALSE;

  return (error = result);
}


/* lies die Anzahl der Einträge in der Liste */

int
A_GetArticleCount (void)
{
  return (itemCount);
}


/* lies die Nummer des aktuellen Eintrags in der Liste und der Kategorie.
** Berechne die Nummer (0...n) des Eintrags, der zur aktuellen Kategorie
** gehört.
*/

int
A_GetCurrentItemCat (void)
{
  struct artNode *node;
  int i;

  if (aktNode)
    {
      for (i = 0, node = (struct artNode *) artlist.lh_Head;
	   node->Node.ln_Succ && aktNode != node;
	   node = (struct artNode *) node->Node.ln_Succ)
	{
	  if (!stricmp (aktNode->article.Kategorie, node->article.Kategorie))
	    i++;
	}
    }

  return (i);
}


/* lies die Daten des aktuellen Eintrags aus */

int
A_GetCurrentItem (struct Article *item)
{
  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  item->Betrag = aktNode->article.Betrag;
  item->Menge = aktNode->article.Menge;
  item->Preistabelle = aktNode->article.Preistabelle;
  item->EKBetrag = aktNode->article.EKBetrag;
  item->VGLBetrag = aktNode->article.VGLBetrag;
  item->EKVersand = aktNode->article.EKVersand;
  strcpy (item->Artikelnummer, aktNode->article.Artikelnummer);
  strcpy (item->Bezeichnung, aktNode->article.Bezeichnung);
  strcpy (item->Lieferfirma, aktNode->article.Lieferfirma);
  strcpy (item->Kategorie, aktNode->article.Kategorie);
  strcpy (item->Notiz, aktNode->article.Notiz);
  strcpy (item->Datum, aktNode->article.Datum);

  return (error = ERR_ART_OK);
}

int
A_GetItem (UWORD num, struct Article *item)
{
  struct artNode *node;
  int i;

  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  if (num >= itemCount)
    return (error = ERR_ART_NOITEM);

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

  item->Betrag = node->article.Betrag;
  item->Menge = node->article.Menge;
  item->Preistabelle = node->article.Preistabelle;
  item->EKBetrag = node->article.EKBetrag;
  item->VGLBetrag = node->article.VGLBetrag;
  item->EKVersand = node->article.EKVersand;
  strcpy (item->Artikelnummer, node->article.Artikelnummer);
  strcpy (item->Bezeichnung, node->article.Bezeichnung);
  strcpy (item->Lieferfirma, node->article.Lieferfirma);
  strcpy (item->Kategorie, node->article.Kategorie);
  strcpy (item->Notiz, node->article.Notiz);
  strcpy (item->Datum, node->article.Datum);

  return (error = ERR_ART_OK);
}


/* lies den Änderungs-Status aus: TRUE - geändert, FALSE - nicht geändert */

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



/*
** hole den Fehlerstring ab.
*/

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

/*
** um die Einträge sortieren zu können, wird eine Vergleichsfunktion
** benötigt. Diese vergleich zwei Kundeneinträge miteinander und
** gibt einen Wert zurück, der dem Vergleich entspricht.
**
** mögliche Rückgabewerte:
**
**    l < r      l = r      l > r
**     -1          0          1
*/

static int
CompareArticles (struct artNode *l, struct artNode *r)
{
  return (StrnCmp (Locale, l->article.Bezeichnung, r->article.Bezeichnung,
		   -1, SC_COLLATE1));
}

static int
CompareCategories (struct categoryNode *l, struct categoryNode *r)
{
  return (StrnCmp (Locale, l->category, r->category, -1, SC_COLLATE1));
}


/*
** neue Kategorie in Liste einfügen
*/

int
A_AddCategory (STRPTR category)
{
  struct categoryNode *newitem;

  if (!(newitem = LibAllocPooled (poolHeader, sizeof (struct categoryNode))))
      return (error = ERR_ART_NOMEM);

  status = TRUE;
  strcpy (newitem->category, category);

  InsertSorted (&categoryList, (struct Node *) newitem,
		(struct Node *) aktCategoryNode, CompareCategories);

  aktCategoryNode = newitem;
  categoryCount++;

  return (error = ERR_ART_OK);
}

/*
** aktuelle Kategorie aus der Liste löschen
*/

int
A_DelCategory (void)
{
  struct categoryNode *newakt;

  if (categoryCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  status = TRUE;
  if (!aktCategoryNode->Node.ln_Succ->ln_Succ)
    {
      if (categoryCount > 1)
	newakt = (struct categoryNode *) aktCategoryNode->Node.ln_Pred;
      else
	newakt = NULL;

      RemTail (&categoryList);
      LibFreePooled (poolHeader, aktCategoryNode,
		     sizeof (struct categoryNode));
      categoryCount--;
      aktCategoryNode = newakt;
    }
  else
    {
      if (categoryCount > 1)
	newakt = (struct categoryNode *) aktCategoryNode->Node.ln_Succ;
      else
	{
	  newakt = NULL;
	}

      Remove ((struct Node *) aktCategoryNode);
      categoryCount--;
      aktCategoryNode = newakt;
    }

  return (error = ERR_ART_OK);
}


/*
** Ändere den aktuellen Kundeneintrag
*/

int
A_ChangeCategory (STRPTR category)
{
  struct artNode *node;

  if (categoryCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  status = TRUE;

  /* ändere alle Artikel mit der alten Kategorie auch mit.  */
  /* alte Kategorie: aktCategoryNode->category             */
  /* neue Kategorie: category                              */

  for (node = (struct artNode *) artlist.lh_Head;
       node->Node.ln_Succ;
       node = (struct artNode *) node->Node.ln_Succ)
    {
      if (strcmp (node->article.Kategorie, aktCategoryNode->category) == 0)
	{
	  /* Kunde mit alter Kategorie gefunden -> ändern */
	  strcpy (node->article.Kategorie, category);
	}
    }

  A_DelCategory ();
  A_AddCategory (category);

  return (error = ERR_ART_OK);
}

/*
**  ermittle eine bestimmte Kategorie aus der Liste
**  nummer läuft von 0...count-1
*/

int
A_GetCategory (UWORD num, STRPTR category)
{
  struct categoryNode *node;
  int i;

  if (categoryCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  if (num >= categoryCount)
    return (error = ERR_ART_NOITEM);

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

  strcpy (category, node->category);

  return (error = ERR_ART_OK);
}

/*
** ermittle die Anzahl der Kategorien in der Liste
*/

int
A_GetCategoryCount (void)
{
  return (categoryCount);
}

/*
** Mache den num-ten Kunden zum aktuellen Kunden in der Liste
*/

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

  if (categoryCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  if (number >= categoryCount)
    return (error = ERR_ART_NOITEM);

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

  aktCategoryNode = (struct categoryNode *) node;

  return (error = ERR_ART_OK);
}

int
A_GetCurrentCategoryNumber (void)
{
  int i;
  struct Node *node;

  for (i = 0, node = categoryList.lh_Head;
       ((struct categoryNode *) node != aktCategoryNode) && node->ln_Succ;
       i++, node = node->ln_Succ);

  return (i);
}

/*
** Vergleiche den 'number'ten Artikel aus der Liste mit einem
** (lückenhaften) Pattern. Wenn der Pattern in den Artikel paßt,
** dann gib TRUE zurück, ansonsten gib FALSE zurück.
**
** Der Pattern kann wie folgt belegt sein: ArtNum, Bezeichnung
*/

BOOL
A_ComparePattern (UWORD number, struct Article * art)
{
  struct artNode *node;
  BOOL result = TRUE;
  int i;

  if (itemCount == 0)
    return (FALSE);

  if (number >= itemCount)
    return (FALSE);

  for (i = 0, node = (struct artNode *) artlist.lh_Head;
       i < number && node->Node.ln_Succ;
       i++, node = (struct artNode *) node->Node.ln_Succ);

  if (result && !CheckName (art->Bezeichnung, node->article.Bezeichnung))
    result = FALSE;

  if (result && !CheckName (art->Kategorie, node->article.Kategorie))
    result = FALSE;

  if (result && !CheckName (art->Artikelnummer, node->article.Artikelnummer))
    result = FALSE;

  return (result);
}


/* 3.3.96, ss: zwei Funktionen zum Korrigieren der Menge beim Laden */
/* und Speichern von Rechnungen. */

/* Suche Artikel und addiere die Menge zum "Lagerbestand" hinzu. */
/* Wird beim Laden einer Rechnung verwendet */

int
A_LoadAddArticle (struct MinArticle *item)
{
  int i;
  struct artNode *node;
  BOOL searching = TRUE;

  /* suche zunächst den Artikel in der Liste */

  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  for (i = 0, node = (struct artNode *) artlist.lh_Head;
       node->Node.ln_Succ && searching;
       i++, node = (struct artNode *) node->Node.ln_Succ)
    {
      if (!stricmp (item->Artikelnummer, node->article.Artikelnummer))
	{
	  searching = FALSE;
	  aktNode = node;
	}
    }

  if (searching)
    return (error = ERR_ART_NOITEM);


  /* Artikel gefunden. Füge Menge wieder hinzu. */

  aktNode->article.Menge += item->Menge;

  return (error = ERR_ART_OK);
}


/* Suche Artikel und ziehe die Menge vom "Lagerbestand" ab. */
/* Wird beim Speichern einer Rechnung verwendet */

int
A_SaveSubArticle (struct MinArticle *item)
{
  int i;
  struct artNode *node;
  BOOL searching = TRUE;

  /* suche zunächst den Artikel in der Liste */

  if (itemCount == 0)
    return (error = ERR_ART_LISTEMPTY);

  for (i = 0, node = (struct artNode *) artlist.lh_Head;
       node->Node.ln_Succ && searching;
       i++, node = (struct artNode *) node->Node.ln_Succ)
    {
      if (!stricmp (item->Artikelnummer, node->article.Artikelnummer))
	{
	  searching = FALSE;
	  aktNode = node;
	}
    }

  if (searching)
    return (error = ERR_ART_NOITEM);


  /* Artikel gefunden. Ziehe Menge wieder ab. */

  aktNode->article.Menge -= item->Menge;

  if (aktNode->article.Menge < 0.0)
    aktNode->article.Menge = 0.0;

  status = TRUE;		/* Artikelliste geändert */

  return (error = ERR_ART_OK);
}


/* suche höchste Artikelnummer */

static UWORD
GetNextArticleNumber (void)
{
  struct artNode *node;
  UWORD num;

  if (itemCount == 0)
    num = 0;
  else
    {
      num = 1;
      for (node = (struct artNode *) artlist.lh_Head;
	   node->Node.ln_Succ;
	   node = (struct artNode *) node->Node.ln_Succ)
	{
	  num = max (num, atol (node->article.Artikelnummer));
	}
    }
  return (num);
}

/* prüfe, ob Artikelnummer bereits vorkommt. */
/* TRUE, falls Nummer eindeutig */
/* FALSE, Nummer gibt es bereits */

static BOOL
NumberNotExists (STRPTR pcNumber)
{
  int i;
  struct artNode *node;
  BOOL searching = TRUE;


  if (itemCount == 0)
    return (searching);

  for (i = 0, node = (struct artNode *) artlist.lh_Head;
       node->Node.ln_Succ && searching;
       i++, node = (struct artNode *) node->Node.ln_Succ)
    {
      if (!stricmp (pcNumber, node->article.Artikelnummer))
	{
	  searching = FALSE;
	}
    }

  return (searching);
}
