using KarleyLibrary.Erweiterungen;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using WK5.Core.Basis.Erweiterungen;
using WK5.Core.Basis.Filter;
using WK5.Core.Models;
using WK5.Core.Models.Lager;

namespace WK5.Core.Services
{
    /// <summary>
    /// Stellt Funktionen für Zugänge, Abgänge, Rechnungsprüfung und die Zahlungsprüfung zur verfügung.
    /// </summary>
    public class LagerService
    {
        #region Zugänge
        /// <summary>
        /// Legt einen neuen Zugang auf Basis eines Lieferschein von einem Lieferanten an.
        /// </summary>
        /// <param name="userId"></param>
        /// <returns>Liefert bei erfolg die ID des Zugangs zurück.</returns>
        public async Task<int> CreateZugangAsync(FbController2 fbController, int userId, LieferscheinBuchenInput zugang, Lieferant lieferant)
        {
            int neueZugangsnummer = 0;

            neueZugangsnummer = Convert.ToInt32(await fbController.FetchObjectAsync(@"select first 1
  ZUGA_N_NR + 1
from
  ZUGAENGE
order by
  ZUGA_N_NR
desc "));

            fbController.AddParameter("@ZUGA_N_NR", neueZugangsnummer);
            fbController.AddParameter("@ZUGA_N_LIEF", lieferant.LIEF_N_NR);
            fbController.AddParameter("@ZUGA_L_BESTELLUNG", "N");
            fbController.AddParameter("@ZUGA_A_LS_NR", zugang.LieferscheinNummer);
            fbController.AddParameter("@ZUGA_D_LS_DATUM", zugang.LieferscheinDatum);
            fbController.AddParameter("@ZUGA_L_GEBUCHT", "Y");
            fbController.AddParameter("@ZUGA_L_MWST", lieferant.LIEF_L_MWST);
            fbController.AddParameter("@ZUGA_N_TRANSPORT", zugang.Zusatzkosten);
            fbController.AddParameter("@ZUGA_A_WAEHRUNG", lieferant.LIEF_A_WAEHRUNG);
            fbController.AddParameter("@ZUGA_N_ZABD", lieferant.LIEF_N_ZABD);
            fbController.AddParameter("@ZUGA_A_NOTIZ", zugang.Notiz);
            fbController.AddParameter("@ZUGA_N_NR_RMA", zugang.RmaNummerVerknüpfung);
            fbController.AddParameter("@ZUGA_N_LASTUSER", userId);



            await fbController.QueryAsync(@"INSERT INTO ZUGAENGE
(
ZUGA_N_NR, 
ZUGA_N_LIEF, 
ZUGA_L_BESTELLUNG, 
ZUGA_D_DATUM,
ZUGA_A_LS_NR, 
ZUGA_D_LS_DATUM, 
ZUGA_L_GEBUCHT, 
ZUGA_L_MWST,
ZUGA_N_TRANSPORT, 
ZUGA_A_WAEHRUNG, 
ZUGA_N_ZABD, 
ZUGA_A_NOTIZ,
ZUGA_N_NR_RMA,
ZUGA_TIMESTAMP, 
ZUGA_N_LASTUSER
)
VALUES
(
@ZUGA_N_NR, 
@ZUGA_N_LIEF, 
@ZUGA_L_BESTELLUNG, 
CURRENT_DATE,
@ZUGA_A_LS_NR, 
@ZUGA_D_LS_DATUM, 
@ZUGA_L_GEBUCHT, 
@ZUGA_L_MWST,
@ZUGA_N_TRANSPORT, 
@ZUGA_A_WAEHRUNG, 
@ZUGA_N_ZABD, 
@ZUGA_A_NOTIZ,
@ZUGA_N_NR_RMA,
CURRENT_TIMESTAMP, 
@ZUGA_N_LASTUSER
)
");

            foreach (var pos in zugang.Positionen)
            {

                int neueChargennummer = Convert.ToInt32(await fbController.FetchObjectAsync("SELECT NEXT VALUE FOR GEN_CHAR_N_NR FROM RDB$DATABASE;"));


                fbController.AddParameter("@CHAR_N_NR", neueChargennummer);
                fbController.AddParameter("@CHAR_N_STAPELNR", neueZugangsnummer);
                fbController.AddParameter("@CHAR_A_ARTINR", pos.Artikelnummer);
                fbController.AddParameter("@CHAR_N_MENGE", pos.Menge);
                fbController.AddParameter("@CHAR_N_MENGEOFFEN", pos.Menge);
                fbController.AddParameter("@CHAR_N_EKPREIS", pos.Preis);
                fbController.AddParameter("@CHAR_N_EKPREIS_ORG", pos.Preis);
                fbController.AddParameter("@CHAR_N_RABATT", 0);
                fbController.AddParameter("@CHAR_N_RABATT2", 0);
                fbController.AddParameter("@CHAR_N_TRANSPORTK", 0);
                fbController.AddParameter("@CHAR_N_STEUERSATZ", pos.MwstSatz);
                fbController.AddParameter("@CHAR_A_NOTIZ", String.Empty);
                fbController.AddParameter("@CHAR_N_BESTNR", pos.Bestellnummer);
                fbController.AddParameter("@CHAR_N_BESTPOS", pos.BestellungPosId);
                fbController.AddParameter("@CHAR_N_BESTMENGE", pos.Menge);
                fbController.AddParameter("@CHAR_N_MENGENFAKTOR", 1);
                fbController.AddParameter("@CHAR_N_LASTUSER", userId);
                fbController.AddParameter("@CHAR_N_AB_NR", pos.Auftragsnummer);
                fbController.AddParameter("@CHAR_L_GEBRAUCHT", pos.Gebrauchtware);

                Convert.ToInt32(await fbController.FetchObjectAsync(@"INSERT INTO CHARGEN
(
CHAR_N_NR, 
CHAR_N_STAPELNR, 
CHAR_A_ARTINR, 
CHAR_N_MENGE, 
CHAR_N_MENGEOFFEN,
CHAR_N_EKPREIS, 
CHAR_N_EKPREIS_ORG, 
CHAR_N_RABATT, 
CHAR_N_RABATT2, 
CHAR_N_TRANSPORTK,
CHAR_A_NOTIZ, 
CHAR_N_BESTNR, 
CHAR_N_BESTPOS, 
CHAR_N_BESTMENGE, 
CHAR_N_MENGENFAKTOR,
CHAR_N_AB_NR,
CHAR_L_GEBRAUCHT,
CHAR_TIMESTAMP, 
CHAR_N_LASTUSER,
CHAR_N_STEUERSATZ
)
VALUES
(
@CHAR_N_NR, 
@CHAR_N_STAPELNR, 
@CHAR_A_ARTINR, 
@CHAR_N_MENGE, 
@CHAR_N_MENGEOFFEN,
@CHAR_N_EKPREIS, 
@CHAR_N_EKPREIS_ORG, 
@CHAR_N_RABATT, 
@CHAR_N_RABATT2, 
@CHAR_N_TRANSPORTK,
@CHAR_A_NOTIZ, 
@CHAR_N_BESTNR, 
@CHAR_N_BESTPOS, 
@CHAR_N_BESTMENGE, 
@CHAR_N_MENGENFAKTOR,
@CHAR_N_AB_NR,
@CHAR_L_GEBRAUCHT,
CURRENT_TIMESTAMP, 
@CHAR_N_LASTUSER,
@CHAR_N_STEUERSATZ
) 
RETURNING CHAR_N_NR"));

                pos.Chargennummer = neueChargennummer;
                // Seriennummern
                foreach (var sn in pos.Seriennummern)
                {
                    fbController.AddParameter("@SNNR_A_SN", sn.Seriennummer);
                    fbController.AddParameter("@SNNR_N_CHARGE", neueChargennummer);
                    fbController.AddParameter("@SNNR_N_MENGE", 1);
                    fbController.AddParameter("@SNNR_L_AUSGELIEFERT", false);
                    fbController.AddParameter("@SNNR_N_LAENGE", 0);
                    fbController.AddParameter("@SNNR_N_LASTUSER", userId);
                    fbController.AddParameter("@SNNR_A_KUNDE", sn.Kundenname);

                    await fbController.QueryAsync(@"INSERT INTO SN
(
SNNR_A_SN, SNNR_N_CHARGE, SNNR_N_MENGE, SNNR_L_AUSGELIEFERT, 
SNNR_N_LAENGE, SNNR_TIMESTAMP, SNNR_N_LASTUSER, SNNR_A_KUNDE
)
VALUES
(
@SNNR_A_SN, @SNNR_N_CHARGE, @SNNR_N_MENGE, @SNNR_L_AUSGELIEFERT, 
@SNNR_N_LAENGE, CURRENT_TIMESTAMP, @SNNR_N_LASTUSER, @SNNR_A_KUNDE
)");
                }

                if (pos.Bestellnummer > 0)
                {
                    fbController.AddParameter("@INP_CHARGE", neueChargennummer);
                    fbController.AddParameter("@INP_MENGE", pos.Menge);
                    await fbController.RunProcedureAsync("ZUGANG_POS_BESTMENGE");
                }


            }

            if(zugang.BestellungNotiz is not null)
            {
                fbController.AddParameter("@BEST_B_NOTIZ", zugang.BestellungNotiz.Notiz);
                fbController.AddParameter("@BEST_N_NR", zugang.BestellungNotiz.Bestellnummer);
                await fbController.QueryAsync("UPDATE BESTELLUNGEN SET BEST_B_NOTIZ = @BEST_B_NOTIZ WHERE BEST_N_NR = @BEST_N_NR");
            }

            fbController.AddParameter("@ARZU_NR", neueZugangsnummer);
            fbController.AddParameter("@USER_NO", userId);
            await fbController.RunProcedureAsync("ZUGANG_BUCHEN");

            fbController.AddParameter("@ARZU_NR", neueZugangsnummer);
            fbController.AddParameter("@USER_NO", userId);
            await fbController.RunProcedureAsync("ZUGANG_BUCHEN_ARTIKEL");

            fbController.AddParameter("@INP_ZUGANG", neueZugangsnummer);
            await fbController.RunProcedureAsync("ZUGANG_BUCHEN_STLUPDATE");

            return neueZugangsnummer;
        }
        /// <summary>
        /// Aktualisiert einen bestehenden Zugang auf Basis einer Lieferantenrechnung.
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="userId"></param>
        /// <param name="zugang"></param>
        /// <param name="lieferant"></param>
        /// <returns></returns>
        public async Task UpdateZugangAsync(FbController2 fbController, RechnungBuchenInput zugang, Lieferant lieferant)
        {
            decimal mwst = 0;
            decimal gesamtNetto = zugang.Positionen.Sum(x => x.PreisGesamt) + zugang.Zusatzkosten;
            Mehrwertsteuer? versandMehrwertsteuer = await Mehrwertsteuer.GetVersandMehrwertsteuerAsync();

            if (versandMehrwertsteuer is null)
            {
                throw new ArgumentNullException(nameof(versandMehrwertsteuer));
            }

            if (lieferant.LIEF_L_MWST)
            {
                mwst += zugang.Zusatzkosten * versandMehrwertsteuer.MWST_N_PROZENT / 100;
            }


            foreach (var pos in zugang.Positionen)
            {

                decimal MWST_N_PROZENT = 0;

                if (lieferant.LIEF_L_MWST)
                {
                    fbController.AddParameter("@ARTI_A_NR", pos.Artikelnummer);
                    MWST_N_PROZENT = Convert.ToDecimal(await fbController.FetchObjectAsync("select MWST_N_PROZENT from ARTIKELFULL where ARTI_A_NR = @ARTI_A_NR"));
                }

                mwst += pos.PreisGesamt * MWST_N_PROZENT / 100;



                // Die Transportkosten werden auf Basis des Preises berechnet
                decimal netto = gesamtNetto - zugang.Zusatzkosten;
                decimal prozent_anteil = 1;
                if (netto is not 0)
                {
                    prozent_anteil = pos.Preis * pos.Menge / netto;
                }

                decimal transportKosten = zugang.Zusatzkosten;
                decimal zollkosten = zugang.Zollkosten;



                transportKosten = transportKosten * prozent_anteil / pos.Menge;
                zollkosten = zollkosten * prozent_anteil / pos.Menge;







                // Wenn Währung ungleich != EUR, dann Währung umrechnen in EUR
                fbController.AddParameter("@CHAR_N_EKPREIS", pos.Preis + transportKosten + zollkosten);
                fbController.AddParameter("@CHAR_N_EKPREIS_ORG", pos.Preis);
                fbController.AddParameter("@CHAR_N_RABATT", 0);
                fbController.AddParameter("@CHAR_N_RABATT2", 0);
                fbController.AddParameter("@CHAR_N_TRANSPORTK", transportKosten);
                fbController.AddParameter("@CHAR_B_NOTIZEN", pos.ChargenNotiz);
                fbController.AddParameter("@CHAR_N_NR", pos.Chargennummer);
                fbController.AddParameter("@CHAR_N_STEUERSATZ", pos.MwstSatz);
                fbController.AddParameter("@CHAR_N_AB_NR", pos.Auftragsnummer);
                fbController.AddParameter("@CHAR_L_GEBRAUCHT", pos.Gebrauchtware);
                await fbController.QueryAsync(@"UPDATE CHARGEN SET 
CHAR_N_EKPREIS = @CHAR_N_EKPREIS, 
CHAR_N_EKPREIS_ORG = @CHAR_N_EKPREIS_ORG, 
CHAR_N_RABATT = @CHAR_N_RABATT, 
CHAR_N_RABATT2 = @CHAR_N_RABATT2,
CHAR_N_TRANSPORTK = @CHAR_N_TRANSPORTK, 
CHAR_B_NOTIZEN = @CHAR_B_NOTIZEN, 
CHAR_N_STEUERSATZ = @CHAR_N_STEUERSATZ, 
CHAR_N_AB_NR = @CHAR_N_AB_NR,
CHAR_L_GEBRAUCHT = @CHAR_L_GEBRAUCHT
WHERE CHAR_N_NR = @CHAR_N_NR");
            }

            Währung? währung = await Währung.GetWährungAsync(lieferant.LIEF_A_WAEHRUNG);
            decimal währungskurs = 1;
            // Wir müssen den Wechselkurs von anderen Währungen berücksichtigen
            if (währung is not null && währung.WAEH_N_WECHSELKURS > 0)
            {
                gesamtNetto = gesamtNetto * währung.WAEH_N_WECHSELKURS;
                mwst = mwst * währung.WAEH_N_WECHSELKURS;
                währungskurs = währung.WAEH_N_WECHSELKURS;
            }


            fbController.AddParameter("@ZUGA_N_NETTO", zugang.GesamtbetragNetto);
            fbController.AddParameter("@ZUGA_N_TRANSPORT", zugang.Zusatzkosten);
            fbController.AddParameter("@ZUGA_N_MWST", mwst);
            fbController.AddParameter("@ZUGA_N_NR", zugang.Zugangsnummer);
            fbController.AddParameter("@ZUGA_A_RE_NR", zugang.Rechnungsnummer);
            fbController.AddParameter("@ZUGA_D_RE_DATUM", zugang.Rechnungsdatum);
            fbController.AddParameter("@ZUGA_A_LS_NR", zugang.LieferscheinNummer);
            fbController.AddParameter("@ZUGA_D_LS_DATUM", zugang.LieferscheinDatum);
            fbController.AddParameter("@WK5_ZUGA_N_MWST", mwst);
            fbController.AddParameter("@WK5_ZUGA_L_TEILZUGANG", zugang.TeilRechnung);
            fbController.AddParameter("@WK5_ZUGA_N_NETTO_GESAMTRECHNUNG", zugang.TeilRechnung ? zugang.NettoGesamtRechnung : zugang.GesamtbetragNetto);
            fbController.AddParameter("@ZUGA_N_KURS", währungskurs);
            fbController.AddParameter("@ZUGA_A_NOTIZ", zugang.Notiz);
            fbController.AddParameter("@ZUGA_N_ZOLLKOSTEN", zugang.Zollkosten);

            await fbController.QueryAsync(@"UPDATE ZUGAENGE SET
ZUGA_N_NETTO = @ZUGA_N_NETTO, ZUGA_N_TRANSPORT = @ZUGA_N_TRANSPORT, ZUGA_N_MWST = @ZUGA_N_MWST,
ZUGA_A_RE_NR = @ZUGA_A_RE_NR, ZUGA_D_RE_DATUM = @ZUGA_D_RE_DATUM,
ZUGA_A_LS_NR = @ZUGA_A_LS_NR, ZUGA_D_LS_DATUM = @ZUGA_D_LS_DATUM,
WK5_ZUGA_N_MWST = @WK5_ZUGA_N_MWST, 
WK5_ZUGA_N_NETTO_GESAMTRECHNUNG = @WK5_ZUGA_N_NETTO_GESAMTRECHNUNG, 
WK5_ZUGA_L_TEILZUGANG = @WK5_ZUGA_L_TEILZUGANG,
ZUGA_N_KURS = @ZUGA_N_KURS,
ZUGA_A_NOTIZ = @ZUGA_A_NOTIZ,
ZUGA_N_ZOLLKOSTEN = @ZUGA_N_ZOLLKOSTEN
WHERE ZUGA_N_NR = @ZUGA_N_NR");

        }
        /// <summary>
        /// Lädt alle Zugänge anhand von bestimmten Suchkriterien
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async IAsyncEnumerable<Zugang> GetZugängeAsync(ZugangFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            using FbController2 fbController = new FbController2();
            var data = await fbController.SelectDataAsync(filter.ToSqlQuery(fbController));

            foreach (DataRow row in data.Rows)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var zugang = ObjectErweiterung.DataRowZuObjekt(new Zugang(), row);

                fbController.AddParameter("@CHAR_N_STAPELNR", zugang.ZUGA_N_NR);
                var posData = await fbController.SelectDataAsync(@"SELECT C.*, ARTI_A_BEZ1, ARTI_A_BEZ2 FROM CHARGEN C 
LEFT JOIN ARTIKEL A ON ARTI_A_NR = CHAR_A_ARTINR
WHERE CHAR_N_STAPELNR = @CHAR_N_STAPELNR");
                foreach (DataRow posRow in posData.Rows)
                {
                    zugang.Positionen.Add(ObjectErweiterung.DataRowZuObjekt(new Charge(), posRow));
                }

                yield return zugang;
            }
        }
        /// <summary>
        /// Ruft die Anzahl alle Zugänge für eine bestimmte Suche ab.
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<int> GetAnzahlZugängeAsnyc(ZugangFilter filter, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return 0;
            }

            using FbController2 fbController = new FbController2();
            return Convert.ToInt32(await fbController.FetchObjectAsync(filter.ToCountQuery(fbController)));
        }
        /// <summary>
        /// Setzt die ecoDMS DocId für den Zugang auf den angegeben Wert.
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="zugangsnummer"></param>
        /// <param name="docId"></param>
        /// <returns></returns>
        public Task SetEcoDmsDocIdAsync(FbController2 fbController, int zugangsnummer, int docId, bool istLieferscheinDocId = false)
        {
            if (istLieferscheinDocId)
            {
                fbController.AddParameter("@ZUGA_N_NR", zugangsnummer);
                fbController.AddParameter("@ZUGA_N_ECO_LS_DOCID", docId);
                return fbController.QueryAsync("UPDATE ZUGAENGE SET ZUGA_N_ECO_LS_DOCID = @ZUGA_N_ECO_LS_DOCID WHERE ZUGA_N_NR = @ZUGA_N_NR");
            }
            else
            {
                fbController.AddParameter("@ZUGA_N_NR", zugangsnummer);
                fbController.AddParameter("@WK5_ZUGA_N_ECO_DOCID", docId);
                return fbController.QueryAsync("UPDATE ZUGAENGE SET WK5_ZUGA_N_ECO_DOCID = @WK5_ZUGA_N_ECO_DOCID WHERE ZUGA_N_NR = @ZUGA_N_NR");
            }
        }
        /// <summary>
        /// Setzt alle Zugänge für eine RMA auf Geprüft, Ereldigt, Fibu und Bezahlt, damit diese nicht in der Prüfung auftauchen
        /// </summary>
        /// <param name="rmaId"></param>
        /// <param name="fbController"></param>
        /// <returns></returns>
        public async Task CloseRmaZugangAsync(int rmaId, FbController2 fbController)
        {
            fbController.AddParameter("@ZUGA_N_NR_RMA", rmaId);
            await fbController.QueryAsync("UPDATE ZUGAENGE SET ZUGA_L_GEPRUEFT = 'Y', ZUGA_L_ERLEDIGT = 'Y', ZUGA_L_FIBU = 'Y', WK5_ZUGA_L_BEZAHLT = 'Y' WHERE ZUGA_N_NR_RMA = @ZUGA_N_NR_RMA");
        }

        public async Task ZugangAbschließen(int zugangId, FbController2 fbController)
        {
            fbController.AddParameter("@ZUGA_N_NR", zugangId);
            await fbController.QueryAsync("UPDATE ZUGAENGE SET ZUGA_L_GEPRUEFT = 'Y', ZUGA_L_ERLEDIGT = 'Y', ZUGA_L_FIBU = 'Y', WK5_ZUGA_L_BEZAHLT = 'Y' WHERE ZUGA_N_NR = @ZUGA_N_NR");
        }
        public async Task UpdateZugangAsync(FbController2 fbController, Zugang zugang)
        {

            fbController.AddParameter("@ZUGA_N_NR", zugang.ZUGA_N_NR);
            fbController.AddParameter("@ZUGA_N_LIEF", zugang.ZUGA_N_LIEF);
            fbController.AddParameter("@ZUGA_D_DATUM", zugang.ZUGA_D_DATUM);
            fbController.AddParameter("@ZUGA_A_LS_NR", zugang.ZUGA_A_LS_NR);
            fbController.AddParameter("@ZUGA_D_LS_DATUM", zugang.ZUGA_D_LS_DATUM);
            fbController.AddParameter("@ZUGA_D_RE_DATUM", zugang.ZUGA_D_RE_DATUM);
            fbController.AddParameter("@ZUGA_N_NETTO", zugang.ZUGA_N_NETTO);
            fbController.AddParameter("@ZUGA_N_TRANSPORT", zugang.ZUGA_N_TRANSPORT);
            fbController.AddParameter("@ZUGA_N_MWST", zugang.ZUGA_N_MWST);
            fbController.AddParameter("@ZUGA_L_MWST", zugang.ZUGA_L_MWST);
            fbController.AddParameter("@ZUGA_N_ZAHLTBETR", zugang.ZUGA_N_ZAHLTBETR);
            fbController.AddParameter("@ZUGA_D_ZAHLDATUM", zugang.ZUGA_D_ZAHLDATUM);
            fbController.AddParameter("@ZUGA_L_GEBUCHT", zugang.ZUGA_L_GEBUCHT);
            fbController.AddParameter("@ZUGA_L_INPRUEFUNG", zugang.ZUGA_L_INPRUEFUNG);
            fbController.AddParameter("@ZUGA_D_INPRUEFUNG", zugang.ZUGA_D_INPRUEFUNG);
            fbController.AddParameter("@ZUGA_L_GEPRUEFT", zugang.ZUGA_L_GEPRUEFT);
            fbController.AddParameter("@ZUGA_L_GESPERRT", zugang.ZUGA_L_GESPERRT);
            fbController.AddParameter("@ZUGA_L_BETRIEBSBEDARF", zugang.ZUGA_L_BETRIEBSBEDARF);
            fbController.AddParameter("@ZUGA_L_FIBU", zugang.ZUGA_L_FIBU);
            fbController.AddParameter("@ZUGA_D_FIBU", zugang.ZUGA_D_FIBU);
            fbController.AddParameter("@ZUGA_A_NOTIZ", zugang.ZUGA_A_NOTIZ);
            fbController.AddParameter("@ZUGA_N_BESTELLNR", zugang.ZUGA_N_BESTELLNR);
            fbController.AddParameter("@ZUGA_A_WAEHRUNG", zugang.ZUGA_A_WAEHRUNG);
            fbController.AddParameter("@ZUGA_N_ZABD", zugang.ZUGA_N_ZABD);
            fbController.AddParameter("@ZUGA_N_AB_NR", zugang.ZUGA_N_AB_NR);
            fbController.AddParameter("@ZUGA_N_RABATT", zugang.ZUGA_N_RABATT);
            fbController.AddParameter("@ZUGA_TIMESTAMP", zugang.ZUGA_TIMESTAMP);
            fbController.AddParameter("@ZUGA_N_LASTUSER", zugang.ZUGA_N_LASTUSER);
            fbController.AddParameter("@ZUGA_A_RE_NR", zugang.ZUGA_A_RE_NR);
            fbController.AddParameter("@WK5_ZUGA_N_MWST", zugang.WK5_ZUGA_N_MWST);
            fbController.AddParameter("@WK5_ZUGA_L_TEILZUGANG", zugang.WK5_ZUGA_L_TEILZUGANG);
            fbController.AddParameter("@WK5_ZUGA_L_BEZAHLT", zugang.WK5_ZUGA_L_BEZAHLT);
            fbController.AddParameter("@ZUGA_L_ERLEDIGT", zugang.ZUGA_L_ERLEDIGT);
            fbController.AddParameter("@WK5_ZUGA_N_NETTO_GESAMTRECHNUNG", zugang.WK5_ZUGA_N_NETTO_GESAMTRECHNUNG);

            string sql = @"UPDATE ZUGAENGE SET
ZUGA_N_LIEF = @ZUGA_N_LIEF,
ZUGA_D_DATUM = @ZUGA_D_DATUM,
ZUGA_A_LS_NR = @ZUGA_A_LS_NR,
ZUGA_D_LS_DATUM = @ZUGA_D_LS_DATUM,
ZUGA_D_RE_DATUM = @ZUGA_D_RE_DATUM,
ZUGA_N_NETTO = @ZUGA_N_NETTO,
ZUGA_N_TRANSPORT = @ZUGA_N_TRANSPORT,
ZUGA_N_MWST = @ZUGA_N_MWST,
ZUGA_L_MWST = @ZUGA_L_MWST,
ZUGA_N_ZAHLTBETR = @ZUGA_N_ZAHLTBETR,
ZUGA_D_ZAHLDATUM = @ZUGA_D_ZAHLDATUM,
ZUGA_L_GEBUCHT = @ZUGA_L_GEBUCHT,
ZUGA_L_INPRUEFUNG = @ZUGA_L_INPRUEFUNG,
ZUGA_D_INPRUEFUNG = @ZUGA_D_INPRUEFUNG,
ZUGA_L_GEPRUEFT = @ZUGA_L_GEPRUEFT,
ZUGA_L_GESPERRT = @ZUGA_L_GESPERRT,
ZUGA_L_BETRIEBSBEDARF = @ZUGA_L_BETRIEBSBEDARF,
ZUGA_L_FIBU = @ZUGA_L_FIBU,
ZUGA_D_FIBU = @ZUGA_D_FIBU,
ZUGA_A_NOTIZ = @ZUGA_A_NOTIZ,
ZUGA_N_BESTELLNR = @ZUGA_N_BESTELLNR,
ZUGA_A_WAEHRUNG = @ZUGA_A_WAEHRUNG,
ZUGA_N_ZABD = @ZUGA_N_ZABD,
ZUGA_N_AB_NR = @ZUGA_N_AB_NR,
ZUGA_N_RABATT = @ZUGA_N_RABATT,
ZUGA_TIMESTAMP = @ZUGA_TIMESTAMP,
ZUGA_N_LASTUSER = @ZUGA_N_LASTUSER,
ZUGA_A_RE_NR = @ZUGA_A_RE_NR,
WK5_ZUGA_N_MWST = @WK5_ZUGA_N_MWST,
WK5_ZUGA_L_TEILZUGANG = @WK5_ZUGA_L_TEILZUGANG,
WK5_ZUGA_L_BEZAHLT = @WK5_ZUGA_L_BEZAHLT,
ZUGA_L_ERLEDIGT = @ZUGA_L_ERLEDIGT,
WK5_ZUGA_N_NETTO_GESAMTRECHNUNG = @WK5_ZUGA_N_NETTO_GESAMTRECHNUNG
WHERE ZUGA_N_NR = @ZUGA_N_NR";

            await fbController.QueryAsync(sql);
        }

        public async Task<Zugang?> GetZugangByChargenId(int chargenId, FbController2 fbController)
        {
            fbController.AddParameter("CHAR_N_NR", chargenId);
            DataRow? row = await fbController.SelectRowAsync(@"SELECT 
Z.*, LIEF_A_NAME1, LIEF_A_KUNDENNR, LIEF_A_LIEFLAND, LIEF_A_BIC, LIEF_A_IBAN 
FROM CHARGEN C 
LEFT JOIN ZUGAENGE Z ON Z.ZUGA_N_NR = C.CHAR_N_STAPELNR
LEFT JOIN LIEFERANTEN L ON L.LIEF_N_NR = Z.ZUGA_N_LIEF 
WHERE CHAR_N_NR = @CHAR_N_NR");
            return row is null ? null : ObjectErweiterung.DataRowZuObjekt(new Zugang(), row);
        }


        #endregion
        #region Abgänge
        /// <summary>
        /// Lädt alle Abgänge für einen bestimmten <see cref="AbgangFilter"/>
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async IAsyncEnumerable<Abgang> GetAbgängeAsync(AbgangFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                using FbController2 fbController = new FbController2();
                var data = await fbController.SelectDataAsync(filter.ToSqlQuery(fbController));

                foreach (DataRow row in data.Rows)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }
                    Abgang abgang = ObjectErweiterung.DataRowZuObjekt(new Abgang(), row);
                    yield return abgang;
                }
            }


        }
        /// <summary>
        /// Ruft die Anzahl der Abgänge für einen bestimmten <see cref="AbgangFilter"/> ab
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<int> GetAnzahlAbgängeAsync(AbgangFilter filter, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return 0;
            }

            using FbController2 fbController = new FbController2();
            return Convert.ToInt32(await fbController.FetchObjectAsync(filter.ToCountQuery(fbController)));
        }
        /// <summary>
        /// Erstellt einen neuen Abgang
        /// </summary>
        /// <param name="abgang"></param>
        /// <param name="userId"></param>
        /// <param name="fbController"></param>
        /// <returns>Liefert bei Erfolg die ID des neuen Abgangs zurück.</returns>
        public async Task<int> CreateAbgangAsync(AbgangBuchenInput abgang, int userId, FbController2 fbController)
        {
            fbController.AddParameter("@ARAB_D_DATUM", abgang.Datum);
            fbController.AddParameter("@ARAB_N_MENGE", abgang.Menge);
            fbController.AddParameter("@ARAB_A_ARTIKEL", abgang.Artikelnummer);
            fbController.AddParameter("@ARAB_N_CHARGE", abgang.Charge);
            fbController.AddParameter("@ARAB_A_SN", abgang.Seriennummer);
            fbController.AddParameter("@ARAB_N_KOSTENSTELL", abgang.Kostenstelle);
            fbController.AddParameter("@ARAB_A_NOTIZ", abgang.Notiz);
            fbController.AddParameter("@ARAB_N_LASTUSER", userId);
            int abgangsnummer = Convert.ToInt32(await fbController.FetchObjectAsync(@"INSERT INTO ABGAENGE
(
ARAB_N_NR, ARAB_D_DATUM, ARAB_N_MENGE, ARAB_N_EINZELPREIS, 
ARAB_N_CHARGE, ARAB_A_SN, ARAB_N_KOSTENSTELL, ARAB_A_NOTIZ, 
ARAB_A_ARTIKEL, ARAB_TIMESTAMP, ARAB_N_LASTUSER
)
VALUES
(
(SELECT MAX(ARAB_N_NR) + 1 FROM ABGAENGE), @ARAB_D_DATUM, @ARAB_N_MENGE, (SELECT CHAR_N_EKPREIS_ORG FROM CHARGEN WHERE CHAR_N_NR = @ARAB_N_CHARGE), 
@ARAB_N_CHARGE, @ARAB_A_SN, @ARAB_N_KOSTENSTELL, @ARAB_A_NOTIZ, 
@ARAB_A_ARTIKEL, CURRENT_TIMESTAMP, @ARAB_N_LASTUSER
) RETURNING ARAB_N_NR"));

            if (abgang.BenötigtSeriennummer)
            {
                fbController.AddParameter("@SNNR_N_LASTUSER", userId);
                fbController.AddParameter("@SNNR_A_SN", abgang.Seriennummer);
                fbController.AddParameter("@SNNR_N_CHARGE", abgang.Charge);
                await fbController.QueryAsync(@"update SN set SNNR_L_AUSGELIEFERT = 'Y',
                      SNNR_TIMESTAMP = CURRENT_TIMESTAMP,
                      SNNR_N_LASTUSER = @SNNR_N_LASTUSER
                      where SNNR_A_SN = @SNNR_A_SN
                         and SNNR_N_CHARGE = @SNNR_N_CHARGE");

                fbController.AddParameter("@ABGANGSNUMMER", abgangsnummer);
                fbController.AddParameter("@SNHI_A_SN", abgang.Seriennummer);
                fbController.AddParameter("@SNHI_N_CHARGE", abgang.Charge);
                fbController.AddParameter("@SNHI_N_LASTUSER", userId);
                await fbController.QueryAsync(@"insert into SNHISTORIE
             (SNHI_N_ID, SNHI_A_SN, SNHI_N_CHARGE, SNHI_N_AENDERUNG,
             SNHI_N_NOTIZ,SNHI_N_BELENR,SNHI_A_BELEART,SNHI_TIMESTAMP, SNHI_N_LASTUSER)
             values
             ((SELECT GEN_ID(Gen_SNHI_N_ID, 1) NextId FROM RDB$DATABASE), @SNHI_A_SN, @SNHI_N_CHARGE, -1, 'Abgang'||@ABGANGSNUMMER, @ABGANGSNUMMER,'ABG',
              CURRENT_TIMESTAMP,
             @SNHI_N_LASTUSER)");
            }

            fbController.AddParameter("@MENGE", abgang.Menge);
            fbController.AddParameter("@CHAR_N_LASTUSER", userId);
            fbController.AddParameter("@CHAR_N_NR", abgang.Charge);
            await fbController.QueryAsync(@"update CHARGEN set CHAR_N_MENGEOFFEN = CHAR_N_MENGEOFFEN - @MENGE,
                          CHAR_TIMESTAMP = CURRENT_TIMESTAMP,
                          CHAR_N_LASTUSER = @CHAR_N_LASTUSER
                          where CHAR_N_NR = @CHAR_N_NR");

            string notiz2 = abgang.Notiz.Length > 20 ? abgang.Notiz[..20] : abgang.Notiz;
            if (abgang.BenötigtSeriennummer)
            {
                notiz2 = $"SN{(abgang.Seriennummer.Length > 18 ? abgang.Seriennummer[..18] : abgang.Seriennummer)}";
            }

            fbController.AddParameter("@ARBE_A_ARTINR", abgang.Artikelnummer);
            fbController.AddParameter("@MENGE", abgang.Menge);
            fbController.AddParameter("@ARBE_N_CHARGE", abgang.Charge);
            fbController.AddParameter("@ABGANGSNUMMER", abgangsnummer);
            fbController.AddParameter("@SERIENNUMMER", abgang.Seriennummer);
            fbController.AddParameter("@ARBE_A_NOTIZ", "Lagerabgang");
            fbController.AddParameter("@ARBE_A_NOTIZ2", notiz2);
            fbController.AddParameter("@ARBE_N_LASTUSER", userId);
            await fbController.QueryAsync(@"insert into ARTIKELBEWEGUNG
              (ARBE_A_ARTINR,
              ARBE_N_BESTAND,
              ARBE_N_AENDERUNG,
              ARBE_A_ART,
              ARBE_N_CHARGE,
              ARBE_A_BELEG,
              ARBE_A_NOTIZ,
              ARBE_A_NOTIZ2,
              ARBE_N_EKPREIS,
              ARBE_N_EKPREIS_LAG,
              ARBE_N_LASTUSER) 
           values
             (@ARBE_A_ARTINR,
              (select sum(iif(CHAR_N_MENGEOFFEN > 0,
                coalesce(CHAR_N_MENGEOFFEN,0),0))
                from chargen where CHAR_A_ARTINR = @ARBE_A_ARTINR
                and CHAR_N_MENGEOFFEN > 0),
                (0 - @MENGE),
                'ABGA',
                @ARBE_N_CHARGE ,
                @ABGANGSNUMMER,
                @ARBE_A_NOTIZ,
                @ARBE_A_NOTIZ2,        
                 (select FIRST 1 x1.ARBE_N_EKPREIS from ARTIKELBEWEGUNG x1
                  where x1.ARBE_A_ARTINR = @ARBE_A_ARTINR order
                  by x1.ARBE_TIMESTAMP DESC, x1.ARBE_N_ID DESC),
                 (select FIRST 1 x2.ARBE_N_EKPREIS_LAG from ARTIKELBEWEGUNG x2
                  where x2.ARBE_A_ARTINR = @ARBE_A_ARTINR order
                  by x2.ARBE_TIMESTAMP DESC,
                  x2.ARBE_N_ID DESC),
                  @ARBE_N_LASTUSER)");

            fbController.AddParameter("@INP_ARTIKELNUMMER", abgang.Artikelnummer);
            await fbController.RunProcedureAsync("WK5_CALC_STUECKLISTEN_BESTAND");


            return abgangsnummer;

        }
        /// <summary>
        /// Ruft die Liste aller Abgänge für einen bestimmten Zeitraum ab.
        /// </summary>
        /// <param name="von"></param>
        /// <param name="bis"></param>
        /// <param name="fbController"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async IAsyncEnumerable<AbgangslisteView> GetAbgangslisteViewAsync(DateTime von, DateTime bis, FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                fbController.AddParameter("@VON", von.Date);
                fbController.AddParameter("@BIS", bis.Date);
                DataTable data = await fbController.SelectDataAsync("SELECT * FROM VIEW_LAGERABGANG WHERE ARAB_D_DATUM BETWEEN @VON AND @BIS ORDER BY ARAB_N_NR DESC");
                foreach (DataRow row in data.Rows)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    yield return ObjectErweiterung.DataRowZuObjekt(new AbgangslisteView(), row);
                }
            }
        }
        #endregion
        #region Ware einlagern
        public async Task WareEinlagernAsync(WareEinlagernInput input, FbController2 fbController)
        {
            foreach (var pos in input.Positionen)
            {
                fbController.AddParameter("@CHAR_A_LAGERPLATZ", pos.Charge.CHAR_A_LAGERPLATZ);
                fbController.AddParameter("@CHAR_N_NR", pos.Charge.CHAR_N_NR);
                await fbController.QueryAsync("UPDATE CHARGEN SET CHAR_A_LAGERPLATZ = @CHAR_A_LAGERPLATZ WHERE CHAR_N_NR = @CHAR_N_NR");

                // Wenn der Lagerplatz von derzeiten Lagerplatz abweicht, dann wird hier ein neuer Lagerplatz festgelegt
                if (pos.Charge.CHAR_A_LAGERPLATZ != pos.Charge.AktuellerLagerplatz)
                {
                    fbController.AddParameter("@ARTI_A_LAGER", pos.Charge.CHAR_A_LAGERPLATZ);
                    fbController.AddParameter("@ARTI_A_NR", pos.Charge.CHAR_A_ARTINR);
                    await fbController.QueryAsync("UPDATE ARTIKEL SET ARTI_A_LAGER = @ARTI_A_LAGER WHERE ARTI_A_NR = @ARTI_A_NR");
                }
            }
        }
        #endregion
        #region Rechnungsprüfung
        /// <summary>
        /// Ruft alle Zugänge ab, die noch geprüft werden müssen.
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async IAsyncEnumerable<Zugang> GetZugängeZuPrüfenAsync(FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                DataTable data = await fbController.SelectDataAsync(@"SELECT Z.*, 
LIEF_A_NAME1, LIEF_A_KUNDENNR, LIEF_A_LIEFLAND, LIEF_A_BIC, LIEF_A_IBAN
FROM ZUGAENGE Z 
LEFT JOIN LIEFERANTEN L ON L.LIEF_N_NR = Z.ZUGA_N_LIEF 
WHERE ZUGA_L_GEPRUEFT = 'N' AND COALESCE(ZUGA_A_RE_NR, '') <> ''
ORDER BY ZUGA_N_NR DESC");

                if (data.Rows.Count > 0)
                {
                    int[] zugangsnummern = data.AsEnumerable().Select(s => s.Field<int>("ZUGA_N_NR"))
                        .Distinct()
                        .ToArray();

                    DataTable chargenData = await GetChargenAsync(fbController, zugangsnummern);


                    List<Zugang> zugänge = KombiniereZugängeMitSelberRechnungsnummer(data, chargenData, cancellationToken);

                    foreach (var item in zugänge)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }
                        yield return item;
                    }
                }
            }
        }

        public async IAsyncEnumerable<Zugang> GetZugängeZuExportierenAsync(FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                DataTable data = await fbController.SelectDataAsync(@"SELECT Z.*, 
LIEF_A_NAME1, LIEF_A_KUNDENNR, LIEF_A_LIEFLAND, LIEF_A_BIC, LIEF_A_IBAN
FROM ZUGAENGE Z 
LEFT JOIN LIEFERANTEN L ON L.LIEF_N_NR = Z.ZUGA_N_LIEF 
WHERE ZUGA_L_GEPRUEFT = 'Y' AND ZUGA_L_FIBU = 'N' AND COALESCE(ZUGA_A_RE_NR, '') <> ''
ORDER BY ZUGA_N_NR DESC");

                if (data.Rows.Count > 0)
                {
                    int[] zugangsnummern = data.AsEnumerable().Select(s => s.Field<int>("ZUGA_N_NR"))
                        .Distinct()
                        .ToArray();

                    DataTable chargenData = await GetChargenAsync(fbController, zugangsnummern);


                    List<Zugang> zugänge = KombiniereZugängeMitSelberRechnungsnummer(data, chargenData, cancellationToken);

                    foreach (var item in zugänge)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }
                        yield return item;
                    }
                }
            }
        }
        /// <summary>
        /// Setzt den Status eines Zugangs auf exportiert.
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="ZUGA_N_NR"></param>
        /// <returns></returns>
        public Task SetGeprueft(FbController2 fbController, int ZUGA_N_NR)
        {
            fbController.AddParameter("@ZUGA_N_NR", ZUGA_N_NR);
            return fbController.QueryAsync("UPDATE ZUGAENGE SET ZUGA_L_GEPRUEFT = 'Y', ZUGA_D_GEPRUEFT = CURRENT_TIMESTAMP WHERE ZUGA_N_NR = @ZUGA_N_NR");
        }

        public Task SetDatevExportiert(FbController2 fbController, int ZUGA_N_NR)
        {
            fbController.AddParameter("@ZUGA_N_NR", ZUGA_N_NR);
            return fbController.QueryAsync("UPDATE ZUGAENGE SET ZUGA_L_FIBU = 'Y', ZUGA_D_FIBU = CURRENT_TIMESTAMP WHERE ZUGA_N_NR = @ZUGA_N_NR");
        }
        /// <summary>
        /// Aktualisiert die Zahlungskonditionen für einen bestimmten Zugang
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="zugang"></param>
        /// <returns></returns>
        public Task UpdateZahlungskonditionenZugangAsync(FbController2 fbController, Zugang zugang)
        {
            fbController.AddParameter("@ZUGA_N_ZABD", zugang.ZUGA_N_ZABD);
            fbController.AddParameter("@ZUGA_N_NR", zugang.ZUGA_N_NR);
            fbController.AddParameter("@ZUGA_D_RE_DATUM", zugang.ZUGA_D_RE_DATUM);
            fbController.AddParameter("@ZUGA_A_RE_NR", zugang.InputRechnungsnummer);
            return fbController.QueryAsync("UPDATE ZUGAENGE SET ZUGA_N_ZABD = @ZUGA_N_ZABD, ZUGA_D_RE_DATUM = @ZUGA_D_RE_DATUM, ZUGA_A_RE_NR = @ZUGA_A_RE_NR  WHERE ZUGA_N_NR = @ZUGA_N_NR");
        }
        /// <summary>
        /// Aktualisiert die Zahlungskonditionen des Lieferanten auf Basis der Zahlungskonditionen des Zugangs
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="zugang"></param>
        /// <param name="updateZahlungsbedingung"></param>
        /// <returns></returns>
        public Task UpdateZahlungskonditionenLieferantAsync(FbController2 fbController, Zugang zugang, bool updateZahlungsbedingung)
        {
            fbController.AddParameter("@LIEF_A_IBAN", zugang.LIEF_A_IBAN);
            fbController.AddParameter("@LIEF_A_BIC", zugang.LIEF_A_BIC);
            if (updateZahlungsbedingung)
            {
                fbController.AddParameter("@LIEF_N_ZABD", zugang.ZUGA_N_ZABD);
            }
            fbController.AddParameter("@LIEF_N_NR", zugang.ZUGA_N_LIEF);

            return fbController.QueryAsync($"UPDATE LIEFERANTEN SET LIEF_A_IBAN = @LIEF_A_IBAN, LIEF_A_BIC = @LIEF_A_BIC {(updateZahlungsbedingung ? ", LIEF_N_ZABD = @LIEF_N_ZABD" : String.Empty)} WHERE LIEF_N_NR = @LIEF_N_NR");
        }
        #endregion
        #region Zahlungsprüfung
        /// <summary>
        /// Ruft alle Zugänge ab, die bereits geprüft wurden und noch bezahlt werden müssen.
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async IAsyncEnumerable<Zugang> GetZugängeZuZahlenAsync(FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {

                DataTable data = await fbController.SelectDataAsync(@"SELECT Z.*, 
LIEF_A_NAME1, LIEF_A_KUNDENNR, LIEF_A_LIEFLAND, LIEF_A_BIC, LIEF_A_IBAN
FROM ZUGAENGE Z
LEFT JOIN LIEFERANTEN L ON L.LIEF_N_NR = Z.ZUGA_N_LIEF
LEFT JOIN ZAHLUNGSBEDINGUNG ZAB ON ZAB.ZABD_N_NR = Z.ZUGA_N_ZABD
WHERE ZUGA_L_GEPRUEFT = 'Y' AND COALESCE(ZUGA_A_RE_NR, '') <> '' AND WK5_ZUGA_L_BEZAHLT = 'N'
AND ZABD_L_FACTORING = 'Y'
ORDER BY ZUGA_D_RE_DATUM DESC");

                if (data.Rows.Count > 0)
                {
                    int[] zugangsnummern = data.AsEnumerable().Select(s => s.Field<int>("ZUGA_N_NR"))
                        .Distinct()
                        .ToArray();

                    DataTable chargenData = await GetChargenAsync(fbController, zugangsnummern);

                    List<Zugang> zugänge = KombiniereZugängeMitSelberRechnungsnummer(data, chargenData, cancellationToken);

                    foreach (var item in zugänge)
                    {
                        if (cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }
                        yield return item;
                    }

                }
            }
        }
        /// <summary>
        /// Setzt den Status eines Zugangs auf bezahlt
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="ZUGA_N_NR"></param>
        /// <returns></returns>
        public Task SetBezahltAsync(FbController2 fbController, int ZUGA_N_NR)
        {
            fbController.AddParameter("@ZUGA_N_NR", ZUGA_N_NR);
            return fbController.QueryAsync("UPDATE ZUGAENGE SET WK5_ZUGA_L_BEZAHLT = 'Y' WHERE ZUGA_N_NR = @ZUGA_N_NR");
        }
        #endregion
        #region Chargen Abwerten
        public async Task ChargeAbwertenAsync(LagerabwertungInput input, FbController2 fbController, ChargenService chargenService)
        {
            Charge charge = await Charge.GetChargeAsync(input.Charge, fbController) ?? throw new NullReferenceException(nameof(charge));

            // Wenn wir weniger als die volle Charge abwerten wollen, dann muss eine Splittung der Chargen geschehen, damit die Preise noch korrekt sind.
            if (charge.CHAR_N_MENGE != input.Menge)
            {

                Charge neueCharge = (Charge)charge.Clone();
                neueCharge.CHAR_N_EKPREIS = input.Preis;
                neueCharge.CHAR_N_MENGE = input.Menge;
                neueCharge.CHAR_N_MENGEOFFEN = input.Menge;
                neueCharge.CHAR_A_NOTIZ = "Lagerabwertung";

                await chargenService.CreateAsync(neueCharge, fbController);

                // Wir müssen auch berücksichtigen, dass bei einer Splittung die Chargennummer der Seriennummer aktualisiert werden muss.
                if (!string.IsNullOrWhiteSpace(input.Seriennummer))
                {
                    fbController.AddParameter("@NEUE_CHARGENID", neueCharge.CHAR_N_NR);
                    fbController.AddParameter("@ALTE_CHARGENID", input.Charge);
                    fbController.AddParameter("@SERIENNUMMER", input.Seriennummer);
                    await fbController.QueryAsync("UPDATE SN SET SNNR_N_CHARGE = @NEUE_CHARGENID WHERE SNNR_N_CHARGE = @ALTE_CHARGENID AND SNNR_A_SN = @SERIENNUMMER");
                }

                charge.CHAR_N_MENGE -= input.Menge;
                charge.CHAR_N_MENGEOFFEN -= input.Menge;


            }
            else
            {
                charge.CHAR_A_NOTIZ = "Lagerabwertung";
                charge.CHAR_N_EKPREIS = input.Preis;
            }

            await chargenService.UpdateChargeAsync(charge, fbController);

            // Wir sperren den Zugang, damit kein Unsinn damit gemacht wird.
            fbController.AddParameter("@ZUGA_N_NR", charge.CHAR_N_STAPELNR);
            await fbController.QueryAsync("UPDATE ZUGAENGE SET ZUGA_L_GESPERRT = 'Y' WHERE ZUGA_N_NR = @ZUGA_N_NR");

            await BelegChange.InsertBelegChangeAsync(fbController, new BelegChange()
            {
                BCNG_A_BELEGTYP = "AR",
                BCNG_A_WERTALT = "Lagerabwertung",
                BCNG_A_WERTNEU = string.IsNullOrWhiteSpace(input.Seriennummer) ? $"Menge: {input.Menge}; Preis: {input.Preis}" : $"SN: {input.Seriennummer}; Preis: {input.Preis}",
                BCNG_N_BELEGNR = input.Artikelnummer,
                BCNG_N_ART = 1237
            });

        }
        #endregion
        #region Hilfsmethoden
        /// <summary>
        /// Lädt alle Chargen für alle übergebenen Rechnungsnummern
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="zugangsnummern"></param>
        /// <returns></returns>
        private Task<DataTable> GetChargenAsync(FbController2 fbController, params int[] zugangsnummern)
        {
            return fbController.SelectDataAsync(@$"
SELECT C.*, ARTI_N_ERLOESKONTO
FROM CHARGEN C
LEFT JOIN ARTIKEL A ON A.ARTI_A_NR = C.CHAR_A_ARTINR
WHERE CHAR_N_STAPELNR IN ({String.Join(',', zugangsnummern)})");
        }
        /// <summary>
        /// Kombiniert mehrere Zugänge mit der selben Rechnungsnummer zu einem Zugang.
        /// </summary>
        /// <param name="zugangData"></param>
        /// <param name="chargenData"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private List<Zugang> KombiniereZugängeMitSelberRechnungsnummer(DataTable zugangData, DataTable chargenData, CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                List<Zugang> zugänge = new List<Zugang>();
                foreach (DataRow row in zugangData.Rows)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    Zugang zugang = ObjectErweiterung.DataRowZuObjekt(new Zugang(), row);

                    var chargen = chargenData.AsEnumerable()
                        .Where(x => x.Field<int>("CHAR_N_STAPELNR") == zugang.ZUGA_N_NR);

                    foreach (DataRow chargenRow in chargen)
                    {
                        zugang.Positionen.Add(ObjectErweiterung.DataRowZuObjekt(new Charge(), chargenRow));
                    }

                    Zugang? sucheZugang = zugänge.Where(x => x.ZUGA_A_RE_NR!.Equals(zugang!.ZUGA_A_RE_NR)).FirstOrDefault();
                    if (sucheZugang is not null)
                    {
                        sucheZugang.Positionen.AddRange(zugang.Positionen);
                        sucheZugang.ZUGA_N_NETTO += zugang.ZUGA_N_NETTO;
                        sucheZugang.WK5_ZUGA_N_MWST += zugang.WK5_ZUGA_N_MWST;
                        sucheZugang.AnzahlZugängeZurRechnungsnummer++;
                        sucheZugang.WeitereZugangsnummern.Add(zugang.ZUGA_N_NR);
                    }
                    else
                    {
                        zugänge.Add(zugang);
                    }
                }

                return zugänge;
            }
            return new List<Zugang>();
        }
        #endregion
    }

}
