using KarleyLibrary.Erweiterungen;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WK5.Core.Basis;
using WK5.Core.Basis.Exceptions;
using WK5.Core.Basis.Filter;
using WK5.Core.Models;
using WK5.Core.Models.Services;
using WK5.Core.Models.Tools.Lagerregal;
using WK5.Core.PageModels.Verkauf.Lieferscheine;

namespace WK5.Core.Services
{
    public class BelegService
    {

        #region Beleganlage
        public async Task CreateAsync<T>(T beleg, Kunde kunde, OptionCollection optionen, FbController2 fbController) where T : Beleg
        {
            int belegnummer = 0;
            BelegTyp anlageTyp;

            ZahlungsbedingungCollection zabd = await Zahlungsbedingung.GetZahlungsbedingungenAsync(fbController);
            if (beleg is Auftrag && kunde.KUND_N_KREDITLIMIT > 0 && zabd.IstRechnung(beleg.ZahlungsbedingungId))
            {
                decimal verfügbarerWert = await GetUnverbrauchtenKreditlimitWert(kunde.KUND_A_NR, fbController);

                OptionCollection optionCollection = await Option.GetOptionenAsync(fbController);
                if (verfügbarerWert < beleg.GetNettoBetrag(optionCollection))
                {
                    beleg.WK5_BELE_L_LIMITPRUEFUNG = true;
                }

            }

            Mehrwertsteuer? versandMehrwertsteuer = await Mehrwertsteuer.GetVersandMehrwertsteuerAsync(fbController);
            if (beleg is Angebot angebot)
            {
                belegnummer = await Beleg.GetNeueBelegnummerAsync(fbController, BelegTyp.Angebot);
                fbController.AddParameter("@BELE_N_NR_AN", belegnummer);
                fbController.AddParameter("@BELE_N_NR_AU", 0);
                fbController.AddParameter("@BELE_N_NR_LS", 0);
                fbController.AddParameter("@BELE_N_NR_RE", 0);
                fbController.AddParameter("@BELE_N_NR_GU", 0);
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", angebot.AnfrageVom);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", angebot.GültigBis);
                fbController.AddParameter("@BELE_A_ANFRADURCH", angebot.AnfrageDurch);
                fbController.AddParameter("@BELE_A_ANFRAGENR", angebot.Anfragennummer);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", angebot.NachzufragenAm);
                fbController.AddParameter("@BELE_A_BESTAET_NR", String.Empty);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Angebot.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", null);
                fbController.AddParameter("@BELE_D_LIEFERDATE", null);
                fbController.AddParameter("@BELE_L_FIXTERMIN", false);

                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", angebot.VersandDoppeltBerechnen);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                anlageTyp = BelegTyp.Angebot;
            }
            else if (beleg is Auftrag auftrag)
            {
                belegnummer = await Beleg.GetNeueBelegnummerAsync(fbController, BelegTyp.Auftrag);
                fbController.AddParameter("@BELE_N_NR_AN", auftrag.AngebotsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_AU", belegnummer);
                fbController.AddParameter("@BELE_N_NR_LS", 0);
                fbController.AddParameter("@BELE_N_NR_RE", 0);
                fbController.AddParameter("@BELE_N_NR_GU", 0);
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", auftrag.Bestellnummer);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", auftrag.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Auftrag.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", auftrag.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", auftrag.BestelltDurch);
                fbController.AddParameter("@BELE_D_LIEFERDATE", auftrag.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", auftrag.BELE_L_FIXTERMIN);

                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", auftrag.VersandDoppeltBerechnen);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", auftrag.WK5_BELE_L_SAMMELRECHNUNG);

                anlageTyp = BelegTyp.Auftrag;


            }
            else if (beleg is Lieferschein lieferschein)
            {
                belegnummer = await Beleg.GetNeueBelegnummerAsync(fbController, BelegTyp.Lieferschein);
                fbController.AddParameter("@BELE_N_NR_AN", lieferschein.AngebotsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_AU", lieferschein.AuftragsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_LS", belegnummer);
                fbController.AddParameter("@BELE_N_NR_RE", 0);
                fbController.AddParameter("@BELE_N_NR_GU", 0);
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", lieferschein.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Lieferschein.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", lieferschein.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", lieferschein.BestelltDurch);
                fbController.AddParameter("@BELE_D_LIEFERDATE", lieferschein.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", lieferschein.BELE_L_FIXTERMIN);


                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                anlageTyp = BelegTyp.Lieferschein;
            }
            else if (beleg is PackLieferschein packLieferschein)
            {
                belegnummer = await Beleg.GetNeueBelegnummerAsync(fbController, BelegTyp.Lieferschein);
                fbController.AddParameter("@BELE_N_NR_AN", packLieferschein.AngebotsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_AU", packLieferschein.AuftragsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_LS", belegnummer);
                fbController.AddParameter("@BELE_N_NR_RE", 0);
                fbController.AddParameter("@BELE_N_NR_GU", 0);
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", packLieferschein.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Lieferschein.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", packLieferschein.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", packLieferschein.BestelltDurch);
                fbController.AddParameter("@BELE_D_LIEFERDATE", packLieferschein.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", packLieferschein.BELE_L_FIXTERMIN);


                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                anlageTyp = BelegTyp.Lieferschein;
            }
            else if (beleg is Rechnung rechnung)
            {
                belegnummer = await Beleg.GetNeueBelegnummerAsync(fbController, BelegTyp.Rechnung);
                fbController.AddParameter("@BELE_N_NR_AN", rechnung.AngebotsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_AU", rechnung.AuftragsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_LS", rechnung.LieferscheinnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_RE", belegnummer);
                fbController.AddParameter("@BELE_N_NR_GU", 0);
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", rechnung.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Rechnung.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", rechnung.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", rechnung.BestelltDurch);
                fbController.AddParameter("@BELE_D_LIEFERDATE", rechnung.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", rechnung.BELE_L_FIXTERMIN);


                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                anlageTyp = BelegTyp.Rechnung;
            }
            else if (beleg is Gutschrift gutschrift)
            {
                belegnummer = await Beleg.GetNeueBelegnummerAsync(fbController, BelegTyp.Gutschrift);
                fbController.AddParameter("@BELE_N_NR_AN", gutschrift.AngebotsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_AU", gutschrift.AuftragsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_LS", gutschrift.LieferscheinnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_RE", gutschrift.RechnungsnummerVerknüpfung);
                fbController.AddParameter("@BELE_N_NR_GU", belegnummer);
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", gutschrift.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Gutschrift.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", gutschrift.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", gutschrift.BestelltDurch);
                fbController.AddParameter("@BELE_D_LIEFERDATE", gutschrift.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", gutschrift.BELE_L_FIXTERMIN);



                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                anlageTyp = BelegTyp.Gutschrift;
            }
            else
            {
                throw new InvalidOperationException();
            }


            beleg.Belegnummer = belegnummer;
            beleg.Belegtyp = EnumHelper.GetBelegTypString(anlageTyp);
            fbController.AddParameter("@BELE_N_NR", belegnummer);
            fbController.AddParameter("@BELE_A_TYP", EnumHelper.GetBelegTypString(anlageTyp));
            fbController.AddParameter("@BELE_A_GRUPPE", String.Empty);
            fbController.AddParameter("@BELE_A_KUNDENNR", kunde.KUND_A_NR);
            fbController.AddParameter("@BELE_A_NAME1", beleg.Name1.Trim());
            fbController.AddParameter("@BELE_A_NAME2", beleg.Name2?.Trim() ?? String.Empty);
            fbController.AddParameter("@BELE_A_NAME3", beleg.Name3?.Trim() ?? String.Empty);
            fbController.AddParameter("@BELE_A_ANREDE", beleg.Anrede.Trim());
            fbController.AddParameter("@BELE_A_ZUHAENDEN", beleg.Ansprechpartner?.Trim() ?? String.Empty);
            fbController.AddParameter("@BELE_A_STRASSE", beleg.Strasse.Trim());
            fbController.AddParameter("@BELE_A_LAND", beleg.Land.Trim());
            fbController.AddParameter("@BELE_A_PLZ", beleg.Postleitzahl.Trim());
            fbController.AddParameter("@BELE_A_ORT", beleg.Ort.Trim());
            fbController.AddParameter("@BELE_N_ZAHLUNG", beleg.ZahlungsbedingungId);
            fbController.AddParameter("@BELE_N_LIEFERUNG", beleg.LieferbedingungId);
            fbController.AddParameter("@BELE_N_LIEFADRESSE", beleg.LieferanschriftId);
            fbController.AddParameter("@BELE_A_WAEHRUNG", "EUR");
            fbController.AddParameter("@BELE_B_NOTIZ", beleg.Notiz?.Trim() ?? String.Empty);

            fbController.AddParameter("@BELE_N_FRACHTEK", beleg.FrachtkostenEk);
            fbController.AddParameter("@BELE_N_FRACHTVK", beleg.Frachtkosten);
            fbController.AddParameter("@BELE_N_VERSICHER", beleg.Versicherungskosten);
            fbController.AddParameter("@BELE_N_VERPACKUNG", beleg.Verpackungskosten);
            fbController.AddParameter("@BELE_N_GEWICHT", beleg.GetBelegGewicht());
            fbController.AddParameter("@BELE_N_NETTO", Math.Round(beleg.GetNettoBetrag(optionen), 2));
            fbController.AddParameter("@BELE_L_MWST", beleg.MwstBerechnen);
            fbController.AddParameter("@BELE_N_MWST_SATZ1", beleg.MwstBerechnen ? versandMehrwertsteuer?.MWST_N_PROZENT ?? 0 : 0);
            fbController.AddParameter("@BELE_N_BRUTTO", Math.Round(beleg.GetBruttoBetrag(optionen, versandMehrwertsteuer), 2));
            fbController.AddParameter("@BELE_N_ROHERTRAG", Math.Round(beleg.GetRoherlös(optionen), 2));
            fbController.AddParameter("@BELE_N_RECHADRESSE", beleg.RechnungsanschriftId);
            fbController.AddParameter("@BELE_B_KOPFTEXT", beleg.Kopftext?.Trim() ?? String.Empty);
            fbController.AddParameter("@BELE_B_FUSSTEXT", beleg.Fußtext?.Trim() ?? String.Empty);
            fbController.AddParameter("@BELE_N_VERTRETER", beleg.VertreterId);
            fbController.AddParameter("@BELE_A_LIEFERZEIT", beleg.Lieferzeit?.Trim() ?? String.Empty);
            fbController.AddParameter("@BELE_L_NEUTRAL", beleg.NeutralerVersender);
            fbController.AddParameter("@BELE_N_NR_RMA", beleg.RmaNummerVerknüpfung);
            fbController.AddParameter("@BELE_L_REPARATUR", beleg.BELE_L_REPARATUR);

            fbController.AddParameter("@BELE_L_BEZAHLT", beleg.BELE_L_BEZAHLT);
            fbController.AddParameter("@WK5_BELE_L_OHNE_BERECHNUNG", beleg.OhneBerechnung);

            fbController.AddParameter("@WK5_BELE_A_ZAHL_KOMMENTAR", beleg.WK5_BELE_A_ZAHL_KOMMENTAR);
            fbController.AddParameter("@WK5_BELE_D_BEZAHLT_DATUM", beleg.WK5_BELE_D_BEZAHLT_DATUM);
            fbController.AddParameter("@WK5_BELE_N_BEZAHLT_WERT", beleg.WK5_BELE_N_BEZAHLT_WERT);
            fbController.AddParameter("@WK5_BELE_L_LIMITPRUEFUNG", beleg.WK5_BELE_L_LIMITPRUEFUNG);
            fbController.AddParameter("@BELE_L_TECHNIKBELEG", beleg.IstTechnikbeleg);
            fbController.AddParameter("@BELE_L_EIGENVERSCHULDEN", beleg.Eigenverschulden);
            fbController.AddParameter("@BELE_A_OHNE_BERECHNUNG_GRUND", beleg.OhneBerechnungGrund);
            fbController.AddParameter("@BELE_B_NEUTRALER_LS", beleg.NeutralerLieferschein);
            fbController.AddParameter("@WK5_BELE_N_NACHZUFRAGEN_BEI", beleg.WK5_BELE_N_NACHZUFRAGEN_BEI);
            fbController.AddParameter("@BELE_N_ABLEHNUNG", beleg.Ablehnungsgrund);
            fbController.AddParameter("@BELE_L_ABGELEHNT", beleg.BELE_L_ABGELEHNT);
            fbController.AddParameter("@BELE_N_NR_WARTUNG", beleg.WartungsnummerVerknüpfung);
            fbController.AddParameter("@WK5_BELE_A_TRANSAKTIONS_ID", beleg.WK5_BELE_A_TRANSAKTIONS_ID);
            fbController.AddParameter("@BELE_L_ABRUF", beleg.BELE_L_ABRUF);
            fbController.AddParameter("@BELE_D_LETZTERABRUF", beleg.BELE_D_LETZTERABRUF);
            fbController.AddParameter("@WK5_BELE_L_DIREKTLIEFERUNG", beleg.WK5_BELE_L_DIREKTLIEFERUNG);
            fbController.AddParameter("@BELE_N_LASTUSER", fbController.UserId);
            fbController.AddParameter("@BELE_A_SPEDITION_AU_NR", beleg.SpeditionsAuftragsnummer);

            await fbController.QueryAsync(@"INSERT INTO BELEGE
(
BELE_A_TYP,
BELE_N_NR,
BELE_D_DATE,
BELE_A_STATUSFELD,
BELE_D_ANLAGEDATUM,
BELE_A_KUNDENNR,
BELE_A_NAME1,
BELE_A_NAME2,
BELE_A_NAME3, 
BELE_A_ANREDE,
BELE_A_ZUHAENDEN,
BELE_A_STRASSE,
BELE_A_LAND,
BELE_A_PLZ,
BELE_A_ORT,
BELE_N_ZAHLUNG,
BELE_N_LIEFERUNG,
BELE_N_LIEFADRESSE,
BELE_A_WAEHRUNG,
BELE_D_ANFRAGEVOM,
BELE_D_GUELTIGBIS,
BELE_A_ANFRADURCH,
BELE_A_ANFRAGENR,
BELE_D_NACHFRAGDAT,
BELE_A_BESTAET_NR,
BELE_B_NOTIZ,
BELE_A_FORMULAR,
BELE_N_FRACHTEK,
BELE_N_FRACHTVK,
BELE_N_VERSICHER,
BELE_N_VERPACKUNG,
BELE_N_GEWICHT,
BELE_N_NETTO,
BELE_L_MWST,
BELE_N_MWST_SATZ1,
BELE_N_BRUTTO,
BELE_N_ROHERTRAG,
BELE_N_RECHADRESSE,
BELE_B_KOPFTEXT,
BELE_B_FUSSTEXT,
BELE_N_VERTRETER, 
BELE_A_LIEFERZEIT,
BELE_A_BESTAET_DURCH,
BELE_L_NEUTRAL, 
BELE_N_NR_RMA,
BELE_L_REPARATUR,
BELE_D_BESTAET_DAT,
BELE_D_LIEFERDATE,
BELE_L_FIXTERMIN,
BELE_N_NR_AN,
BELE_N_NR_AU,
BELE_N_NR_LS,
BELE_N_NR_RE,
BELE_N_NR_GU,
BELE_L_BEZAHLT,
WK5_BELE_L_OHNE_BERECHNUNG,
BELE_TIMESTAMP,
BELE_N_LASTUSER,
WK5_BELE_A_ZAHL_KOMMENTAR,
WK5_BELE_D_BEZAHLT_DATUM,
WK5_BELE_N_BEZAHLT_WERT,
WK5_BELE_L_LIMITPRUEFUNG,
BELE_L_TECHNIKBELEG,
BELE_L_EIGENVERSCHULDEN,
BELE_A_OHNE_BERECHNUNG_GRUND,
BELE_B_NEUTRALER_LS,
WK5_BELE_N_NACHZUFRAGEN_BEI,
BELE_N_ABLEHNUNG,
BELE_L_ABGELEHNT,
BELE_N_NR_WARTUNG,
WK5_BELE_A_TRANSAKTIONS_ID,
BELE_L_ABRUF,
BELE_D_LETZTERABRUF,
WK5_BELE_L_DOPPELT_VERSAND,
WK5_BELE_L_DIREKTLIEFERUNG,
BELE_A_SPEDITION_AU_NR
)
VALUES
(
@BELE_A_TYP,
@BELE_N_NR,
CURRENT_DATE,
'',
CURRENT_DATE,
@BELE_A_KUNDENNR,
@BELE_A_NAME1,
@BELE_A_NAME2,
@BELE_A_NAME3, 
@BELE_A_ANREDE,
@BELE_A_ZUHAENDEN,
@BELE_A_STRASSE,
@BELE_A_LAND,
@BELE_A_PLZ,
@BELE_A_ORT,
@BELE_N_ZAHLUNG,
@BELE_N_LIEFERUNG,
@BELE_N_LIEFADRESSE,
@BELE_A_WAEHRUNG,
@BELE_D_ANFRAGEVOM,
@BELE_D_GUELTIGBIS,
@BELE_A_ANFRADURCH,
@BELE_A_ANFRAGENR,
@BELE_D_NACHFRAGDAT,
@BELE_A_BESTAET_NR,
@BELE_B_NOTIZ,
@BELE_A_FORMULAR,
@BELE_N_FRACHTEK,
@BELE_N_FRACHTVK,
@BELE_N_VERSICHER,
@BELE_N_VERPACKUNG,
@BELE_N_GEWICHT,
@BELE_N_NETTO,
@BELE_L_MWST,
@BELE_N_MWST_SATZ1,
@BELE_N_BRUTTO,
@BELE_N_ROHERTRAG,
@BELE_N_RECHADRESSE,
@BELE_B_KOPFTEXT,
@BELE_B_FUSSTEXT,
@BELE_N_VERTRETER, 
@BELE_A_LIEFERZEIT,
@BELE_A_BESTAET_DURCH,
@BELE_L_NEUTRAL,
@BELE_N_NR_RMA,
@BELE_L_REPARATUR,
@BELE_D_BESTAET_DAT,
@BELE_D_LIEFERDATE,
@BELE_L_FIXTERMIN,
@BELE_N_NR_AN,
@BELE_N_NR_AU,
@BELE_N_NR_LS,
@BELE_N_NR_RE,
@BELE_N_NR_GU,
@BELE_L_BEZAHLT,
@WK5_BELE_L_OHNE_BERECHNUNG,
CURRENT_TIMESTAMP,
@BELE_N_LASTUSER,
@WK5_BELE_A_ZAHL_KOMMENTAR,
@WK5_BELE_D_BEZAHLT_DATUM,
@WK5_BELE_N_BEZAHLT_WERT,
@WK5_BELE_L_LIMITPRUEFUNG,
@BELE_L_TECHNIKBELEG,
@BELE_L_EIGENVERSCHULDEN,
@BELE_A_OHNE_BERECHNUNG_GRUND,
@BELE_B_NEUTRALER_LS,
@WK5_BELE_N_NACHZUFRAGEN_BEI,
@BELE_N_ABLEHNUNG,
@BELE_L_ABGELEHNT,
@BELE_N_NR_WARTUNG,
@WK5_BELE_A_TRANSAKTIONS_ID,
@BELE_L_ABRUF,
@BELE_D_LETZTERABRUF,
@WK5_BELE_L_DOPPELT_VERSAND,
@WK5_BELE_L_DIREKTLIEFERUNG,
@BELE_A_SPEDITION_AU_NR
)");


            int zähler = 0;
            // Positionen einfügen
            foreach (Belegposition pos in beleg.Positionen.Where(x => x.IstBundle || (!x.IstBundle && x.BundleId == 0)))
            {
                pos.BPOS_N_NR = belegnummer;
                pos.BPOS_A_TYP = EnumHelper.GetBelegTypString(anlageTyp);
                pos.PosNr = ++zähler;
                if (pos.IstBundle)
                {
                    // SELECT NEW BUNDLE ID
                    int bundleId = Convert.ToInt32(await fbController.FetchObjectAsync("SELECT MAX(BPOS_N_BUNDLEID) + 1 FROM BELEGPOS"));
                    pos.BundleId = bundleId;
                    // SET BUNDLEID OF POS
                    foreach (var item in pos.BundleArtikel)
                    {
                        item.BPOS_N_NR = belegnummer;
                        item.BPOS_A_TYP = pos.BPOS_A_TYP;
                        item.PosNr = ++zähler;
                        item.BundleId = bundleId;
                        await CreatePositionAsync(item, anlageTyp, true, fbController);
                    }
                }

                await CreatePositionAsync(pos, anlageTyp, true, fbController);

                if (!String.IsNullOrWhiteSpace(pos.Kundensachnummer))
                {
                    fbController.AddParameter("@INP_ARTIKELNUMMER", pos.Artikelnummer);
                    fbController.AddParameter("@INP_KUNDENSACHNUMMER", pos.Kundensachnummer);
                    fbController.AddParameter("@INP_KUNDENNUMMER", kunde.KUND_A_NR);
                    await fbController.RunProcedureAsync("WK5_SAVE_KUNDENSACHNUMMER");
                }
            }
        }

        /// <summary>
        /// Aktualisiert einen Beleg samt seiner Belegpositionen
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="beleg"></param>
        /// <param name="kunde"></param>
        /// <param name="optionen"></param>
        /// <param name="fbController"></param>
        /// <param name="userId"></param>
        /// <exception cref="FibuException">Wird ausgelöst, wenn versucht wird eine Rechnung upzudaten, die bereits an die Finanzbuchhaltung übergeben wurde.</exception>
        /// <exception cref="InvalidOperationException"></exception>
        /// <returns></returns>
        public async Task UpdateAsync<T>(T beleg, Kunde kunde, OptionCollection optionen, FbController2 fbController) where T : Beleg
        {
            if (beleg is null)
            {
                throw new ArgumentNullException(nameof(beleg));
            }

            if (EnumHelper.GetBelegTyp(beleg.Belegtyp) == BelegTyp.Auftrag && kunde.KUND_N_KREDITLIMIT > 0)
            {
                // Wert des Beleg aus der Datenbank holen, in dem Beleg Objekt haben wir jetzt ja wahrscheinlich nur den aktualisierten Wert drin
                fbController.AddParameter("@TYP", beleg.Belegtyp);
                fbController.AddParameter("@NR", beleg.Belegnummer);
                object? alterWertObj = await fbController.FetchObjectAsync("SELECT BELE_N_NETTO FROM BELEGE WHERE BELE_A_TYP = @TYP AND BELE_N_NR = @NR");
                decimal alterWert = 0.0m;
                if (alterWertObj is not null)
                {
                    alterWert = Convert.ToDecimal(alterWertObj);
                }

                OptionCollection optionCollection = await Option.GetOptionenAsync(fbController);
                ZahlungsbedingungCollection zahlungsbedingungen = await Zahlungsbedingung.GetZahlungsbedingungenAsync(fbController);
                decimal nettoBetrag = Math.Round(beleg.GetNettoBetrag(optionCollection), 2);
                bool istRechnungOderLastschrift = zahlungsbedingungen.IstLastschrift(beleg.ZahlungsbedingungId) || zahlungsbedingungen.IstRechnung(beleg.ZahlungsbedingungId);

                // Wenn die Werte sich geändert haben, können wir einmal prüfen ob der jetzt ins Kreditlimit fällt
                if (istRechnungOderLastschrift)
                {
                    if (alterWert != nettoBetrag)
                    {
                        // Laden wie viel wir noch übrig haben
                        decimal verfügbarerWert = await GetUnverbrauchtenKreditlimitWert(beleg.Kundennummer, fbController);
                        // Den alten Wert drauf rechnen, wir brauchen für den Beleg ja nicht doppelt platz
                        verfügbarerWert += alterWert;

                        // Jetzt prüfen wir, ob das verfügbare Kreditlimit für den Auftrag reicht, sollte das nicht der Fall sein, muss er in die kreditlimitprüfung
                        bool sollKreditlimitPrüfungBekommen = verfügbarerWert < nettoBetrag;

                        if (sollKreditlimitPrüfungBekommen != beleg.WK5_BELE_L_LIMITPRUEFUNG)
                        {
                            beleg.WK5_BELE_L_LIMITPRUEFUNG = sollKreditlimitPrüfungBekommen;
                            await BelegChange.InsertBelegChangeAsync(fbController, new BelegChange
                            {
                                BCNG_A_BELEGTYP = "AU",
                                BCNG_N_BELEGNR = beleg.Belegnummer.ToString(),
                                BCNG_A_WERTALT = "Kreditlimitprüfung wurde automatisch beim speichern geändert",
                                BCNG_A_WERTNEU = "",
                                BCNG_N_ART = 911,
                                BCNG_N_POSNR = 0,
                                BCNG_N_USER = 0,
                                BCNG_TIMESTAMP = DateTime.Now,
                                BCNG_A_FELD = null

                            });
                        }

                    }
                }
                else
                {
                    beleg.WK5_BELE_L_LIMITPRUEFUNG = false;
                }
            }



            int belegnummer = beleg.Belegnummer;
            BelegTyp belegTyp;
            Mehrwertsteuer? versandMehrwertsteuer = await Mehrwertsteuer.GetVersandMehrwertsteuerAsync(fbController);
            if (beleg is Angebot angebot)
            {
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", angebot.AnfrageVom);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", angebot.GültigBis);
                fbController.AddParameter("@BELE_A_ANFRADURCH", angebot.AnfrageDurch);
                fbController.AddParameter("@BELE_A_ANFRAGENR", angebot.Anfragennummer);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", angebot.NachzufragenAm);
                fbController.AddParameter("@BELE_A_BESTAET_NR", String.Empty);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Angebot.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", null);
                fbController.AddParameter("@BELE_D_LIEFERDATE", null);
                fbController.AddParameter("@BELE_L_FIXTERMIN", false);


                fbController.AddParameter("@WK5_BELE_L_DIREKTLIEFERUNG", false);
                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", angebot.VersandDoppeltBerechnen);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);
                belegTyp = BelegTyp.Angebot;
            }
            else if (beleg is Auftrag auftrag)
            {
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", auftrag.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Auftrag.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", auftrag.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", auftrag.BestelltDurch);
                fbController.AddParameter("@BELE_D_LIEFERDATE", auftrag.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", auftrag.BELE_L_FIXTERMIN);



                fbController.AddParameter("@WK5_BELE_L_DIREKTLIEFERUNG", auftrag.WK5_BELE_L_DIREKTLIEFERUNG);
                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", auftrag.VersandDoppeltBerechnen);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", auftrag.WK5_BELE_L_SAMMELRECHNUNG);


                belegTyp = BelegTyp.Auftrag;
            }
            else if (beleg is Lieferschein lieferschein)
            {
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", lieferschein.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Lieferschein.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", lieferschein.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", lieferschein.BestelltDurch?.Trim() ?? String.Empty);
                fbController.AddParameter("@BELE_D_LIEFERDATE", lieferschein.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", lieferschein.BELE_L_FIXTERMIN);


                fbController.AddParameter("@WK5_BELE_L_DIREKTLIEFERUNG", false);
                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                belegTyp = BelegTyp.Lieferschein;
            }
            else if (beleg is PackLieferschein packLieferschein)
            {
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", packLieferschein.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Lieferschein.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", packLieferschein.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", packLieferschein.BestelltDurch?.Trim() ?? String.Empty);
                fbController.AddParameter("@BELE_D_LIEFERDATE", packLieferschein.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", packLieferschein.BELE_L_FIXTERMIN);


                fbController.AddParameter("@WK5_BELE_L_DIREKTLIEFERUNG", false);
                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                belegTyp = BelegTyp.Lieferschein;
            }
            else if (beleg is Rechnung rechnung)
            {
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", rechnung.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Rechnung.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", rechnung.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", rechnung.BestelltDurch?.Trim() ?? String.Empty);
                fbController.AddParameter("@BELE_D_LIEFERDATE", rechnung.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", rechnung.BELE_L_FIXTERMIN);


                fbController.AddParameter("@WK5_BELE_L_DIREKTLIEFERUNG", false);
                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                belegTyp = BelegTyp.Rechnung;

                if (rechnung.BELE_L_FIBUUEBERGABE)
                {
                    throw new FibuException();
                }
            }
            else if (beleg is Gutschrift gutschrift)
            {
                fbController.AddParameter("@BELE_D_ANFRAGEVOM", null);
                fbController.AddParameter("@BELE_D_GUELTIGBIS", null);
                fbController.AddParameter("@BELE_A_ANFRADURCH", null);
                fbController.AddParameter("@BELE_A_ANFRAGENR", null);
                fbController.AddParameter("@BELE_D_NACHFRAGDAT", null);
                fbController.AddParameter("@BELE_A_BESTAET_NR", gutschrift.Bestellnummer);
                fbController.AddParameter("@BELE_A_FORMULAR", "Beleg_Gutschrift.lst");
                fbController.AddParameter("@BELE_D_BESTAET_DAT", gutschrift.BestelltAm);
                fbController.AddParameter("@BELE_A_BESTAET_DURCH", gutschrift.BestelltDurch?.Trim() ?? String.Empty);
                fbController.AddParameter("@BELE_D_LIEFERDATE", gutschrift.Liefertermin);
                fbController.AddParameter("@BELE_L_FIXTERMIN", gutschrift.BELE_L_FIXTERMIN);


                fbController.AddParameter("@WK5_BELE_L_DIREKTLIEFERUNG", false);
                fbController.AddParameter("@WK5_BELE_L_DOPPELT_VERSAND", false);
                fbController.AddParameter("@WK5_BELE_L_SAMMELRECHNUNG", false);

                belegTyp = BelegTyp.Gutschrift;

                if (gutschrift.BELE_L_FIBUUEBERGABE)
                {
                    throw new FibuException();
                }
            }
            else
            {
                throw new InvalidOperationException();
            }


            fbController.AddParameter("@BELE_A_GRUPPE", String.Empty);
            fbController.AddParameter("@BELE_A_NAME1", beleg.Name1);
            fbController.AddParameter("@BELE_A_NAME2", beleg.Name2);
            fbController.AddParameter("@BELE_A_NAME3", beleg.Name3);
            fbController.AddParameter("@BELE_A_KUNDENNR", kunde.KUND_A_NR);
            fbController.AddParameter("@BELE_A_ANREDE", beleg.Anrede);
            fbController.AddParameter("@BELE_A_ZUHAENDEN", beleg.Ansprechpartner);
            fbController.AddParameter("@BELE_A_STRASSE", beleg.Strasse);
            fbController.AddParameter("@BELE_A_LAND", beleg.Land);
            fbController.AddParameter("@BELE_A_PLZ", beleg.Postleitzahl);
            fbController.AddParameter("@BELE_A_ORT", beleg.Ort);
            fbController.AddParameter("@BELE_N_ZAHLUNG", beleg.ZahlungsbedingungId);
            fbController.AddParameter("@BELE_N_LIEFERUNG", beleg.LieferbedingungId);
            fbController.AddParameter("@BELE_N_LIEFADRESSE", beleg.LieferanschriftId);

            fbController.AddParameter("@BELE_B_NOTIZ", beleg.Notiz);
            fbController.AddParameter("@BELE_B_KOPFTEXT", beleg.Kopftext);
            fbController.AddParameter("@BELE_B_FUSSTEXT", beleg.Fußtext);
            fbController.AddParameter("@BELE_N_FRACHTVK", beleg.Frachtkosten);
            fbController.AddParameter("@BELE_N_VERSICHER", beleg.Versicherungskosten);
            fbController.AddParameter("@BELE_N_VERPACKUNG", beleg.Verpackungskosten);
            fbController.AddParameter("@BELE_N_GEWICHT", beleg.GetBelegGewicht());
            fbController.AddParameter("@BELE_N_NETTO", Math.Round(beleg.GetNettoBetrag(optionen), 2));
            fbController.AddParameter("@BELE_N_RABATT", 0); // Rabatt gibt es nicht mehr
            fbController.AddParameter("@BELE_L_MWST", beleg.MwstBerechnen);
            fbController.AddParameter("@BELE_N_BRUTTO", Math.Round(beleg.GetBruttoBetrag(optionen, versandMehrwertsteuer), 2));
            fbController.AddParameter("@BELE_N_RECHADRESSE", beleg.RechnungsanschriftId);
            fbController.AddParameter("@BELE_A_LIEFERZEIT", beleg.Lieferzeit);

            fbController.AddParameter("@WK5_BELE_L_OHNE_BERECHNUNG", beleg.OhneBerechnung);
            fbController.AddParameter("@BELE_L_BEZAHLT", beleg.BELE_L_BEZAHLT);

            fbController.AddParameter("@BELE_L_NEUTRAL", beleg.NeutralerVersender);
            fbController.AddParameter("@BELE_N_FRACHTEK", beleg.FrachtkostenEk);
            fbController.AddParameter("@BELE_N_LASTUSER", fbController.UserId);
            fbController.AddParameter("@BELE_A_TYP", EnumHelper.GetBelegTypString(belegTyp));
            fbController.AddParameter("@BELE_N_NR", belegnummer);

            fbController.AddParameter("@WK5_BELE_A_ZAHL_KOMMENTAR", beleg.WK5_BELE_A_ZAHL_KOMMENTAR);
            fbController.AddParameter("@WK5_BELE_D_BEZAHLT_DATUM", beleg.WK5_BELE_D_BEZAHLT_DATUM);
            fbController.AddParameter("@WK5_BELE_N_BEZAHLT_WERT", beleg.WK5_BELE_N_BEZAHLT_WERT);
            fbController.AddParameter("@WK5_BELE_L_LIMITPRUEFUNG", beleg.WK5_BELE_L_LIMITPRUEFUNG);
            fbController.AddParameter("@BELE_L_TECHNIKBELEG", beleg.IstTechnikbeleg);
            fbController.AddParameter("@BELE_L_EIGENVERSCHULDEN", beleg.Eigenverschulden);
            fbController.AddParameter("@BELE_A_OHNE_BERECHNUNG_GRUND", beleg.OhneBerechnungGrund);
            fbController.AddParameter("@BELE_B_NEUTRALER_LS", beleg.NeutralerLieferschein);
            fbController.AddParameter("@WK5_BELE_N_NACHZUFRAGEN_BEI", beleg.WK5_BELE_N_NACHZUFRAGEN_BEI);
            fbController.AddParameter("@BELE_N_ABLEHNUNG", beleg.Ablehnungsgrund);
            fbController.AddParameter("@BELE_L_ABGELEHNT", beleg.BELE_L_ABGELEHNT);
            fbController.AddParameter("@WK5_BELE_A_TRANSAKTIONS_ID", beleg.WK5_BELE_A_TRANSAKTIONS_ID);
            fbController.AddParameter("@BELE_L_ABRUF", beleg.BELE_L_ABRUF);
            fbController.AddParameter("@BELE_D_LETZTERABRUF", beleg.BELE_D_LETZTERABRUF);
            fbController.AddParameter("@BELE_A_SPEDITION_AU_NR", beleg.SpeditionsAuftragsnummer);


            await fbController.QueryAsync(@"UPDATE BELEGE SET
BELE_A_NAME1 = @BELE_A_NAME1, BELE_A_NAME2 = @BELE_A_NAME2, BELE_A_NAME3 = @BELE_A_NAME3, 
BELE_A_KUNDENNR = @BELE_A_KUNDENNR, BELE_A_ANREDE = @BELE_A_ANREDE, BELE_A_ZUHAENDEN = @BELE_A_ZUHAENDEN, 
BELE_A_STRASSE = @BELE_A_STRASSE, BELE_A_LAND = @BELE_A_LAND, BELE_A_PLZ = @BELE_A_PLZ, BELE_A_ORT = @BELE_A_ORT, 
BELE_N_ZAHLUNG = @BELE_N_ZAHLUNG, BELE_N_LIEFERUNG = @BELE_N_LIEFERUNG, BELE_N_LIEFADRESSE = @BELE_N_LIEFADRESSE,
BELE_D_ANFRAGEVOM = @BELE_D_ANFRAGEVOM, BELE_D_GUELTIGBIS = @BELE_D_GUELTIGBIS, BELE_A_ANFRADURCH = @BELE_A_ANFRADURCH, 
BELE_A_ANFRAGENR = @BELE_A_ANFRAGENR, BELE_D_NACHFRAGDAT = @BELE_D_NACHFRAGDAT, BELE_A_BESTAET_NR = @BELE_A_BESTAET_NR,
BELE_B_NOTIZ = @BELE_B_NOTIZ, BELE_B_KOPFTEXT = @BELE_B_KOPFTEXT, BELE_B_FUSSTEXT = @BELE_B_FUSSTEXT, 
BELE_N_FRACHTVK = @BELE_N_FRACHTVK, BELE_N_VERSICHER = @BELE_N_VERSICHER, BELE_N_VERPACKUNG = @BELE_N_VERPACKUNG, BELE_N_GEWICHT = @BELE_N_GEWICHT,
BELE_N_NETTO = @BELE_N_NETTO, 
BELE_N_RABATT = @BELE_N_RABATT,
BELE_L_MWST = @BELE_L_MWST, BELE_N_BRUTTO = @BELE_N_BRUTTO,
BELE_N_RECHADRESSE = @BELE_N_RECHADRESSE, BELE_A_LIEFERZEIT = @BELE_A_LIEFERZEIT, BELE_A_BESTAET_DURCH = @BELE_A_BESTAET_DURCH, 
BELE_L_NEUTRAL = @BELE_L_NEUTRAL, BELE_N_FRACHTEK = @BELE_N_FRACHTEK, 
BELE_D_BESTAET_DAT = @BELE_D_BESTAET_DAT, BELE_D_LIEFERDATE = @BELE_D_LIEFERDATE, BELE_L_FIXTERMIN = @BELE_L_FIXTERMIN,
BELE_A_GRUPPE = @BELE_A_GRUPPE, WK5_BELE_L_DIREKTLIEFERUNG = @WK5_BELE_L_DIREKTLIEFERUNG, WK5_BELE_L_DOPPELT_VERSAND = @WK5_BELE_L_DOPPELT_VERSAND,
WK5_BELE_L_SAMMELRECHNUNG = @WK5_BELE_L_SAMMELRECHNUNG, WK5_BELE_L_OHNE_BERECHNUNG = @WK5_BELE_L_OHNE_BERECHNUNG, BELE_L_BEZAHLT = @BELE_L_BEZAHLT,
BELE_TIMESTAMP = CURRENT_TIMESTAMP, BELE_N_LASTUSER = @BELE_N_LASTUSER,
WK5_BELE_A_ZAHL_KOMMENTAR = @WK5_BELE_A_ZAHL_KOMMENTAR, WK5_BELE_D_BEZAHLT_DATUM = @WK5_BELE_D_BEZAHLT_DATUM, WK5_BELE_N_BEZAHLT_WERT = @WK5_BELE_N_BEZAHLT_WERT,
WK5_BELE_L_LIMITPRUEFUNG = @WK5_BELE_L_LIMITPRUEFUNG, 
BELE_L_TECHNIKBELEG = @BELE_L_TECHNIKBELEG,
BELE_L_EIGENVERSCHULDEN = @BELE_L_EIGENVERSCHULDEN,
BELE_A_OHNE_BERECHNUNG_GRUND = @BELE_A_OHNE_BERECHNUNG_GRUND,
BELE_B_NEUTRALER_LS = @BELE_B_NEUTRALER_LS,
WK5_BELE_N_NACHZUFRAGEN_BEI = @WK5_BELE_N_NACHZUFRAGEN_BEI,
BELE_N_ABLEHNUNG = @BELE_N_ABLEHNUNG,
BELE_L_ABGELEHNT = @BELE_L_ABGELEHNT,
WK5_BELE_A_TRANSAKTIONS_ID = @WK5_BELE_A_TRANSAKTIONS_ID,
BELE_L_ABRUF = @BELE_L_ABRUF,
BELE_D_LETZTERABRUF = @BELE_D_LETZTERABRUF,
BELE_A_SPEDITION_AU_NR = @BELE_A_SPEDITION_AU_NR
WHERE BELE_A_TYP = @BELE_A_TYP AND BELE_N_NR = @BELE_N_NR
");

            int[] positionsIds = beleg.Positionen.Where(x => x.PosId > 0).Select(x => x.PosId).ToArray();
            fbController.AddParameter("@BPOS_A_TYP", EnumHelper.GetBelegTypString(belegTyp));
            fbController.AddParameter("@BPOS_N_NR", belegnummer);
            await fbController.QueryAsync("UPDATE BELEGPOS SET BPOS_N_POS = BPOS_N_POS + 100 WHERE BPOS_A_TYP = @BPOS_A_TYP AND BPOS_N_NR = @BPOS_N_NR");

            if (positionsIds.Length > 0)
            {
                fbController.AddParameter("@BPOS_A_TYP", EnumHelper.GetBelegTypString(belegTyp));
                fbController.AddParameter("@BPOS_N_NR", belegnummer);
                await fbController.QueryAsync($"DELETE FROM BELEGPOS WHERE BPOS_A_TYP = @BPOS_A_TYP AND BPOS_N_NR = @BPOS_N_NR AND BPOS_N_POSID NOT IN({String.Join(',', positionsIds)})");
            }
            else
            {
                fbController.AddParameter("@BPOS_A_TYP", EnumHelper.GetBelegTypString(belegTyp));
                fbController.AddParameter("@BPOS_N_NR", belegnummer);
                await fbController.QueryAsync($"DELETE FROM BELEGPOS WHERE BPOS_A_TYP = @BPOS_A_TYP AND BPOS_N_NR = @BPOS_N_NR");
            }

            foreach (var pos in beleg.Positionen.Where(x => x.PosId > 0))
            {
                await UpdatePositionAsync(pos, belegTyp, fbController);

                if (!String.IsNullOrWhiteSpace(pos.Kundensachnummer))
                {
                    fbController.AddParameter("@INP_ARTIKELNUMMER", pos.Artikelnummer);
                    fbController.AddParameter("@INP_KUNDENSACHNUMMER", pos.Kundensachnummer);
                    fbController.AddParameter("@INP_KUNDENNUMMER", kunde.KUND_A_NR);
                    await fbController.RunProcedureAsync("WK5_SAVE_KUNDENSACHNUMMER");
                }

            }

            // Positionen einfügen


            foreach (var pos in beleg.Positionen.Where(x => x.PosId is 0 && (x.IstBundle || (!x.IstBundle && x.BundleId == 0))))
            {
                pos.BPOS_N_NR = belegnummer;
                pos.BPOS_A_TYP = EnumHelper.GetBelegTypString(belegTyp);

                if (pos.IstBundle)
                {
                    // SELECT NEW BUNDLE ID
                    int bundleId = Convert.ToInt32(await fbController.FetchObjectAsync("SELECT MAX(BPOS_N_BUNDLEID) + 1 FROM BELEGPOS"));
                    pos.BundleId = bundleId;
                    // SET BUNDLEID OF POS
                    foreach (var item in pos.BundleArtikel)
                    {
                        item.BPOS_N_NR = belegnummer;
                        item.BPOS_A_TYP = pos.BPOS_A_TYP;
                        item.BundleId = bundleId;
                        await CreatePositionAsync(item, belegTyp, true, fbController);
                    }
                }

                await CreatePositionAsync(pos, belegTyp, true, fbController);
                if (!String.IsNullOrWhiteSpace(pos.Kundensachnummer))
                {
                    fbController.AddParameter("@INP_ARTIKELNUMMER", pos.Artikelnummer);
                    fbController.AddParameter("@INP_KUNDENSACHNUMMER", pos.Kundensachnummer);
                    fbController.AddParameter("@INP_KUNDENNUMMER", kunde.KUND_A_NR);
                    await fbController.RunProcedureAsync("WK5_SAVE_KUNDENSACHNUMMER");
                }
                // Wenn eine neue Position eine Stückliste ist, dann sollten wir die Stückliste an dieser Stelle auch laden, damit wir diese automatisch verchargen können.
                if (pos.PositionIstStückliste && !pos.StücklistenArtikel.Any())
                {
                    await pos.LadeStücklisteAsync(pos, 0, fbController);
                }
            }




        }
        public async Task UpdatePositionAsync(Belegposition pos, BelegTyp belegTyp, FbController2 fbController)
        {
            fbController.AddParameter("@BPOS_A_BEZ1", pos.Bezeichnung1);
            fbController.AddParameter("@BPOS_A_BEZ2", pos.Bezeichnung2);
            fbController.AddParameter("@BPOS_A_BEZ3", pos.Bezeichnung3);
            fbController.AddParameter("@BPOS_A_BEZ4", pos.Bezeichnung4);
            fbController.AddParameter("@BPOS_A_BEZ5", pos.Bezeichnung5);

            fbController.AddParameter("@BPOS_N_POS", pos.PosNr);
            fbController.AddParameter("@BPOS_A_OPTION", pos.Option);
            fbController.AddParameter("@BPOS_A_RABATTBEZ1", pos.Rabattbezeichnung);
            fbController.AddParameter("@BPOS_A_KUNDENARTIKELNR", pos.Kundensachnummer);


            fbController.AddParameter("@BPOS_B_TEXT", pos.Langtext);
            fbController.AddParameter("@BPOS_L_DRUCKLANGTEXT", pos.LangtextDrucken);
            fbController.AddParameter("@BPOS_L_NICHT_DRUCKEN", pos.PosOhneDruck);
            fbController.AddParameter("@BPOS_N_EK_GES", pos.PosEinkaufspreis * pos.Menge);

            fbController.AddParameter("@BPOS_N_MENGE", pos.Menge);
            fbController.AddParameter("@BPOS_N_MENGENFAKTOR", 1);
            fbController.AddParameter("@BPOS_N_MWSTPROZ", pos.MwstSatz);
            fbController.AddParameter("@BPOS_N_NETTO", pos.PreisMitRabatt * pos.Menge);
            fbController.AddParameter("@BPOS_N_RABATTPROZ", pos.Rabatt1);
            fbController.AddParameter("@BPOS_N_PREIS", pos.Preis);
            fbController.AddParameter("@BPOS_N_RABATTPROZ2", pos.Rabatt2);
            fbController.AddParameter("@WK5_BPOS_A_BILD", pos.Bild);
            fbController.AddParameter("@WK5_BPOS_N_EK", pos.PosEinkaufspreis);
            fbController.AddParameter("@WK5_BPOS_L_BILD_VOLLE_BREITE", pos.DruckBildInVollerBreite);
            fbController.AddParameter("@WK5_BPOS_N_BILD", pos.BildId);

            fbController.AddParameter("@BPOS_N_OLDPOSID", pos.BPOS_N_OLDPOSID);
            fbController.AddParameter("@BPOS_N_POSID_AU", pos.BPOS_N_POSID_AU);

            fbController.AddParameter("@BPOS_N_LASTUSER", fbController.UserId);
            fbController.AddParameter("@BPOS_A_TYP", EnumHelper.GetBelegTypString(belegTyp));
            fbController.AddParameter("@BPOS_N_NR", pos.BPOS_N_NR);
            fbController.AddParameter("@BPOS_N_POSID", pos.PosId);

            fbController.AddParameter("@BPOS_D_MIETE_START", pos.BPOS_D_MIETE_START);
            fbController.AddParameter("@BPOS_D_MIETE_ENDE", pos.BPOS_D_MIETE_ENDE);

            fbController.AddParameter("@BPOS_L_FERTIGUNG", pos.BPOS_L_FERTIGUNG);

            await fbController.QueryAsync(@"UPDATE BELEGPOS SET
BPOS_A_BEZ1 = @BPOS_A_BEZ1, BPOS_A_BEZ2 = @BPOS_A_BEZ2, BPOS_A_BEZ3 = @BPOS_A_BEZ3, BPOS_A_BEZ4 = @BPOS_A_BEZ4, BPOS_A_BEZ5 = @BPOS_A_BEZ5,
BPOS_N_POS = @BPOS_N_POS, BPOS_A_OPTION = @BPOS_A_OPTION, BPOS_A_RABATTBEZ1 = @BPOS_A_RABATTBEZ1, BPOS_A_KUNDENARTIKELNR = @BPOS_A_KUNDENARTIKELNR,
BPOS_L_DRUCKLANGTEXT = @BPOS_L_DRUCKLANGTEXT, BPOS_L_NICHT_DRUCKEN = @BPOS_L_NICHT_DRUCKEN, BPOS_N_EK_GES = @BPOS_N_EK_GES, 
BPOS_N_MENGE = @BPOS_N_MENGE, BPOS_N_MENGENFAKTOR = @BPOS_N_MENGENFAKTOR, BPOS_N_MWSTPROZ = @BPOS_N_MWSTPROZ, BPOS_N_NETTO = @BPOS_N_NETTO, 
BPOS_N_RABATTPROZ = @BPOS_N_RABATTPROZ, BPOS_N_PREIS = @BPOS_N_PREIS, BPOS_N_RABATTPROZ2 = @BPOS_N_RABATTPROZ2, WK5_BPOS_A_BILD = @WK5_BPOS_A_BILD,
WK5_BPOS_N_EK = @WK5_BPOS_N_EK, BPOS_B_TEXT = @BPOS_B_TEXT, WK5_BPOS_L_BILD_VOLLE_BREITE = @WK5_BPOS_L_BILD_VOLLE_BREITE, WK5_BPOS_N_BILD = @WK5_BPOS_N_BILD,
BPOS_N_OLDPOSID = @BPOS_N_OLDPOSID,
BPOS_N_POSID_AU = @BPOS_N_POSID_AU,
BPOS_TIMESTAMP = CURRENT_TIMESTAMP, BPOS_N_LASTUSER = @BPOS_N_LASTUSER,
BPOS_D_MIETE_START = @BPOS_D_MIETE_START,
BPOS_D_MIETE_ENDE = @BPOS_D_MIETE_ENDE,
BPOS_L_FERTIGUNG = @BPOS_L_FERTIGUNG
WHERE BPOS_A_TYP = @BPOS_A_TYP AND BPOS_N_NR = @BPOS_N_NR AND BPOS_N_POSID = @BPOS_N_POSID");

            // Lieferscheine beeinflussen die Bereits gelieferte Menge, sowie den Status des Auftrages, daher muss die Prozedur ausgeführt werden, damit alles korrekt hinterlegt ist.
            if (EnumHelper.GetBelegTyp(pos.BPOS_A_TYP) is BelegTyp.Lieferschein)
            {
                fbController.AddParameter("@INP_POSID", pos.BPOS_N_POSID_AU);
                await fbController.RunProcedureAsync("WK5_AUFTRAGSSTATUS");
            }


        }
        /// <summary>
        /// Legt eine neue Position für einen Beleg an.
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="belegTyp"></param>
        /// <param name="autoStückliste">Gibt an, dass automatisch ein Eintrag in BELEGSTUECKLISTE angelegt werden soll, sofern der Artikel eine Stückliste ist. Dieser Wert sollte immer true sein, außer bei Übernahme von einem Blegtyp in den nächsten</param>
        /// <param name="fbController"></param>
        /// <returns></returns>
        public async Task CreatePositionAsync(Belegposition pos, BelegTyp belegTyp, bool autoStückliste, FbController2 fbController)
        {

            fbController.AddParameter("@BPOS_N_POS", pos.PosNr);
            fbController.AddParameter("@BPOS_N_NR", pos.BPOS_N_NR);
            fbController.AddParameter("@BPOS_A_ARTIKELNR", pos.Artikelnummer);
            fbController.AddParameter("@BPOS_A_KUNDENARTIKELNR", pos.Kundensachnummer);
            fbController.AddParameter("@BPOS_A_BEZ1", pos.Bezeichnung1);
            fbController.AddParameter("@BPOS_A_BEZ2", pos.Bezeichnung2);
            fbController.AddParameter("@BPOS_A_BEZ3", pos.Bezeichnung3);
            fbController.AddParameter("@BPOS_A_BEZ4", pos.Bezeichnung4);
            fbController.AddParameter("@BPOS_A_BEZ5", pos.Bezeichnung5);
            fbController.AddParameter("@BPOS_A_OPTION", pos.Option);
            fbController.AddParameter("@BPOS_A_RABATTBEZ1", pos.Rabattbezeichnung);
            fbController.AddParameter("@BPOS_A_TYP", EnumHelper.GetBelegTypString(belegTyp));
            fbController.AddParameter("@BPOS_B_TEXT", pos.Langtext);
            fbController.AddParameter("@BPOS_L_DRUCKLANGTEXT", pos.LangtextDrucken);
            fbController.AddParameter("@BPOS_L_NICHT_DRUCKEN", pos.PosOhneDruck);
            fbController.AddParameter("@BPOS_N_EK_GES", pos.PosEinkaufspreis * pos.Menge);
            fbController.AddParameter("@BPOS_N_LASTUSER", fbController.UserId);
            fbController.AddParameter("@BPOS_N_MENGE", pos.Menge);
            fbController.AddParameter("@BPOS_N_MENGEGELIEF", 0);
            fbController.AddParameter("@BPOS_N_MENGENFAKTOR", 1);
            fbController.AddParameter("@BPOS_N_MWSTPROZ", pos.MwstSatz);
            fbController.AddParameter("@BPOS_N_NETTO", pos.PreisMitRabatt * pos.Menge);
            fbController.AddParameter("@BPOS_N_PREIS", pos.Preis);
            fbController.AddParameter("@BPOS_N_RABATTPROZ", pos.Rabatt1);
            fbController.AddParameter("@BPOS_N_RABATTPROZ2", pos.Rabatt2);
            fbController.AddParameter("@WK5_BPOS_A_BILD", pos.Bild);
            fbController.AddParameter("@WK5_BPOS_N_EK", pos.PosEinkaufspreis);
            fbController.AddParameter("@WK5_BPOS_L_BILD_VOLLE_BREITE", pos.DruckBildInVollerBreite);
            fbController.AddParameter("@WK5_BPOS_N_BILD", pos.BildId);
            fbController.AddParameter("@BPOS_N_OLDPOSID", pos.BPOS_N_OLDPOSID);
            fbController.AddParameter("@BPOS_N_POSID_AU", pos.BPOS_N_POSID_AU);
            fbController.AddParameter("@BPOS_D_MIETE_START", pos.BPOS_D_MIETE_START);
            fbController.AddParameter("@BPOS_D_MIETE_ENDE", pos.BPOS_D_MIETE_ENDE);
            fbController.AddParameter("@BPOS_L_FERTIGUNG", pos.BPOS_L_FERTIGUNG);
            fbController.AddParameter("@BPOS_N_BUNDLEID", pos.BundleId);
            // TODO: Aktuellen MwstSatz laden
            // (select iif(MWST_D_GUELTIGAB is null or MWST_D_GUELTIGAB < CURRENT_TIMESTAMP or MWST_N_PROZENTALT is null or MWST_N_PROZENTALT = 0,MWST_N_PROZENT,MWST_N_PROZENTALT) from MEHRWERTSTEUER  where MWST_N_NR =  (SELECT ARTI_N_MWSTKENNUNG FROM ARTIKEL WHERE ARTI_A_NR = BPOS_A_ARTIKELNR)),
            pos.PosId = Convert.ToInt32(await fbController.FetchObjectAsync(@"INSERT INTO BELEGPOS
(
BPOS_A_TYP, BPOS_N_NR, BPOS_N_POS, BPOS_A_ARTIKELNR,
BPOS_A_BEZ1, BPOS_A_BEZ2, BPOS_A_BEZ3, BPOS_A_BEZ4, BPOS_A_BEZ5,
BPOS_A_OPTION, BPOS_A_RABATTBEZ1, BPOS_A_KUNDENARTIKELNR,
BPOS_L_DRUCKLANGTEXT, BPOS_L_NICHT_DRUCKEN,
BPOS_N_EK_GES, BPOS_N_LASTUSER, BPOS_N_MENGE, BPOS_N_MENGEGELIEF,
BPOS_N_MENGENFAKTOR, BPOS_N_MWSTPROZ, BPOS_N_NETTO, BPOS_N_RABATTPROZ,
BPOS_N_PREIS, BPOS_N_RABATTPROZ2, WK5_BPOS_A_BILD, WK5_BPOS_N_EK,
BPOS_B_TEXT, WK5_BPOS_L_BILD_VOLLE_BREITE, WK5_BPOS_N_BILD, BPOS_TIMESTAMP,
BPOS_N_OLDPOSID,
BPOS_N_POSID_AU,
BPOS_D_MIETE_START,
BPOS_D_MIETE_ENDE,
BPOS_L_FERTIGUNG,
BPOS_N_BUNDLEID
)
VALUES
(
@BPOS_A_TYP, @BPOS_N_NR, @BPOS_N_POS, @BPOS_A_ARTIKELNR, 
@BPOS_A_BEZ1, @BPOS_A_BEZ2, @BPOS_A_BEZ3, @BPOS_A_BEZ4, @BPOS_A_BEZ5,
@BPOS_A_OPTION, @BPOS_A_RABATTBEZ1, @BPOS_A_KUNDENARTIKELNR,
@BPOS_L_DRUCKLANGTEXT, @BPOS_L_NICHT_DRUCKEN,
@BPOS_N_EK_GES, @BPOS_N_LASTUSER, @BPOS_N_MENGE, @BPOS_N_MENGEGELIEF,
@BPOS_N_MENGENFAKTOR, @BPOS_N_MWSTPROZ, @BPOS_N_NETTO, @BPOS_N_RABATTPROZ,
@BPOS_N_PREIS, @BPOS_N_RABATTPROZ2, @WK5_BPOS_A_BILD, @WK5_BPOS_N_EK,
@BPOS_B_TEXT, @WK5_BPOS_L_BILD_VOLLE_BREITE, @WK5_BPOS_N_BILD, CURRENT_TIMESTAMP,
@BPOS_N_OLDPOSID,
@BPOS_N_POSID_AU,
@BPOS_D_MIETE_START,
@BPOS_D_MIETE_ENDE,
@BPOS_L_FERTIGUNG,
@BPOS_N_BUNDLEID
) 
RETURNING BPOS_N_POSID"));

            if (pos.PosId is 0)
            {
                throw new InvalidOperationException();
            }

            if (autoStückliste)
            {
                fbController.AddParameter("@INP_POSID", pos.PosId);
                fbController.AddParameter("@INP_ARTIKELNUMMER", pos.Artikelnummer);
                fbController.AddParameter("@INP_MENGE", pos.Menge);
                fbController.AddParameter("@INP_SUBLEVEL", 0);
                await fbController.RunProcedureAsync("WK5_BELEGSTUECKLISTE");
            }
        }

        public async Task UpdateGesperrtenBelegAsync<T>(T beleg, FbController2 fbController) where T : Beleg
        {
            StringBuilder sqlBuilder = new StringBuilder();
            sqlBuilder.Append("UPDATE BELEGE SET BELE_B_NOTIZ = @BELE_B_NOTIZ");

            if (beleg is Auftrag)
            {
                sqlBuilder.Append(", BELE_D_LIEFERDATE = @BELE_D_LIEFERDATE");
                sqlBuilder.Append(", BELE_D_LETZTERABRUF = @BELE_D_LETZTERABRUF");

                fbController.AddParameter("@BELE_D_LETZTERABRUF", beleg.BELE_D_LETZTERABRUF);
                fbController.AddParameter("@BELE_D_LIEFERDATE", beleg.Liefertermin);
            }

            sqlBuilder.Append(" WHERE BELE_A_TYP = @BELE_A_TYP AND BELE_N_NR = @BELE_N_NR");

            fbController.AddParameter("@BELE_B_NOTIZ", beleg.Notiz);
            fbController.AddParameter("@BELE_A_TYP", beleg.Belegtyp);
            fbController.AddParameter("@BELE_N_NR", beleg.Belegnummer);

            await fbController.QueryAsync(sqlBuilder.ToString());

            // Positionseinkaufspreise müssen auch anpassbar sein
            foreach (var pos in beleg.Positionen)
            {
                fbController.AddParameter("@WK5_BPOS_N_EK", pos.PosEinkaufspreis);
                fbController.AddParameter("@BPOS_A_TYP", pos.BPOS_A_TYP);
                fbController.AddParameter("@BPOS_N_NR", pos.BPOS_N_NR);
                fbController.AddParameter("@BPOS_N_POSID", pos.PosId);
                await fbController.QueryAsync("UPDATE BELEGPOS SET WK5_BPOS_N_EK = @WK5_BPOS_N_EK WHERE BPOS_A_TYP = @BPOS_A_TYP AND BPOS_N_NR = @BPOS_N_NR AND BPOS_N_POSID = @BPOS_N_POSID");
            }
        }

        public async Task<(T NeuerBeleg, List<AlertBox> Messages)> CopyAsync<T>(T beleg, Kunde kunde, OptionCollection optionen, FbController2 fbController, int userId) where T : Beleg
        {
            BelegTyp belegTyp;
            List<AlertBox> meldungen = new List<AlertBox>();
            Beleg? neuerBeleg = null;
            if (beleg is Angebot angebot)
            {
                belegTyp = BelegTyp.Angebot;
                neuerBeleg = new Angebot
                {
                    GültigBis = DateTime.Now.AddDays(14),
                    NachzufragenAm = DateTime.Now.AddDays(28),
                    AnfrageVom = DateTime.Now
                };
            }
            else if (beleg is Auftrag auftrag)
            {
                belegTyp = BelegTyp.Auftrag;
                neuerBeleg = new Auftrag
                {
                    BestelltAm = DateTime.Now
                };
            }
            else if (beleg is Rechnung rechnung)
            {
                belegTyp = BelegTyp.Rechnung;
                neuerBeleg = new Rechnung();
            }
            else if (beleg is Lieferschein lieferschein)
            {
                belegTyp = BelegTyp.Lieferschein;
                neuerBeleg = new Lieferschein();
            }
            else
            {
                throw new InvalidOperationException();
            }

            neuerBeleg.Name1 = kunde.KUND_A_NAME1;
            neuerBeleg.Name2 = kunde.KUND_A_NAME2 ?? String.Empty;
            neuerBeleg.Name3 = kunde.KUND_A_NAME3 ?? String.Empty;
            neuerBeleg.Ort = kunde.KUND_A_ORT ?? String.Empty;
            neuerBeleg.ZahlungsbedingungId = kunde.KUND_N_ZAHLUNG;
            neuerBeleg.LieferbedingungId = kunde.KUND_N_LIEFERUNG;
            neuerBeleg.Kundennummer = kunde.KUND_A_NR;
            neuerBeleg.MwstBerechnen = kunde.KUND_L_MWST;
            neuerBeleg.NeutralerVersender = kunde.KUND_L_NEUTRALVERSAND;
            neuerBeleg.VertreterId = kunde.KUND_N_VERTRETNUMM;
            neuerBeleg.Land = kunde.KUND_A_LAND;
            neuerBeleg.Strasse = kunde.KUND_A_STRASSE ?? String.Empty;
            neuerBeleg.Postleitzahl = kunde.KUND_A_PLZ ?? String.Empty;
            neuerBeleg.Anrede = kunde.KUND_A_ANREDE ?? String.Empty;
            neuerBeleg.Notiz = beleg.Notiz;
            neuerBeleg.Kopftext = beleg.Kopftext;
            neuerBeleg.Fußtext = beleg.Fußtext;
            neuerBeleg.Frachtkosten = beleg.Frachtkosten;
            neuerBeleg.FrachtkostenEk = beleg.FrachtkostenEk;
            neuerBeleg.Verpackungskosten = beleg.Verpackungskosten;
            neuerBeleg.Versicherungskosten = beleg.Versicherungskosten;
            neuerBeleg.VersandDoppeltBerechnen = beleg.VersandDoppeltBerechnen;
            neuerBeleg.Lieferzeit = beleg.Lieferzeit;
            neuerBeleg.VertreterId = kunde.KUND_N_VERTRETNUMM;


            // Liefer und Rechnungsanschriften kopieren
            neuerBeleg.RechnungsanschriftId = kunde.KUND_N_ABWEICH_RECHN;
            neuerBeleg.LieferanschriftId = kunde.KUND_N_ABWEICHLIEF;

            int zähler = 0;
            ArtikelService artikelService = new ArtikelService();
            foreach (var pos in beleg.Positionen.Where(x => x.IstBundle || (!x.IstBundle && x.BundleId is 0)))
            {

                if (pos.Artikelnummer.Equals("TEXT", StringComparison.OrdinalIgnoreCase))
                {
                    neuerBeleg.Positionen.Add(new Belegposition
                    {
                        PosNr = ++zähler,
                        Artikelnummer = "TEXT",
                        Langtext = pos.Langtext,
                        LangtextDrucken = pos.LangtextDrucken,
                        PosOhneDruck = pos.PosOhneDruck
                    });
                }
                else if (pos.Artikelnummer.Equals("T1", StringComparison.OrdinalIgnoreCase))
                {
                    Artikel? artikel = await artikelService.GetAsync(pos.Artikelnummer, fbController);
                    if (artikel is null)
                    {
                        throw new ArgumentNullException(nameof(artikel));
                    }
                    neuerBeleg.Positionen.Add(new Belegposition()
                    {
                        PosNr = ++zähler,
                        Artikelnummer = "T1",
                        Menge = pos.Menge,
                        Preis = pos.Preis,
                        PosEinkaufspreis = 0,
                        Gewicht = 0,
                        MwstSatz = artikel.MWST_N_PROZENT
                    });
                }
                else
                {

                    Artikel? artikel = await artikelService.GetAsync(pos.Artikelnummer, fbController);

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

                    if (artikel.ARTI_L_INAKTIV)
                    {
                        meldungen.Add(new AlertBox
                        {
                            AlertType = AlertType.Warning,
                            Message = $"Der Artikel {pos.Artikelnummer} wurde nicht kopiert, da der Artikel inaktiv ist."
                        });

                        if (!String.IsNullOrWhiteSpace(artikel.ARTI_A_ERSATZART))
                        {
                            meldungen.Add(new AlertBox
                            {
                                AlertType = AlertType.Info,
                                Message = $"Für den inaktiven Artikel {pos.Artikelnummer} gibt es den Ersatzartikel {artikel.ARTI_A_ERSATZART}"
                            });
                        }

                        continue;
                    }

                    Kundenrabatt? kundenpreise = await Kundenrabatt.GetKundenSachnummerAsync(kunde.KUND_A_NR, artikel.Artikelnummer);
                    List<decimal> preise = new List<decimal>();
                    List<decimal> rabatte = new List<decimal>();
                    var (preis, rabatt, rabattbezeichnung) = artikel.GetPreis(pos.Menge, kunde.KUND_N_PREISLISTNR);
                    if (artikel.LetzePreisÄnderung > beleg.BELE_D_DATE)
                    {
                        preise.Add(preis);
                        rabatte.Add(rabatt);
                    }
                    else
                    {
                        preise.Add(pos.Preis);
                    }

                    if (kundenpreise is not null)
                    {
                        var (rabatt1, _) = kundenpreise.GetRabatte(pos.Menge);
                        rabatte.Add(rabatt1);
                    }

                    if (pos.Rabatt1 > 0)
                    {
                        rabatte.Add(pos.Rabatt1);
                    }
                    else
                    {
                        // Wir fügen 0 hinzu, damit die Kollektion ein Eintrag hat
                        rabatte.Add(0);
                    }

                    decimal posPreis = preise.Min();
                    decimal posRabatt = rabatte.Max();


                    var newPos = new Belegposition
                    {
                        PosNr = ++zähler,
                        Artikelnummer = pos.Artikelnummer,
                        Bezeichnung1 = pos.Bezeichnung1,
                        Bezeichnung2 = pos.Bezeichnung2,
                        Bezeichnung3 = pos.Bezeichnung3,
                        Bezeichnung4 = pos.Bezeichnung4,
                        Bezeichnung5 = pos.Bezeichnung5,
                        Preis = posPreis,
                        Rabatt1 = posRabatt,
                        Rabattbezeichnung = posRabatt == pos.Rabatt1 ? pos.Rabattbezeichnung : string.Empty,
                        MwstSatz = artikel.MWST_N_PROZENT,
                        Gebinde = artikel.WK5_ARTI_N_GEBINDE,
                        AnfrageArtikel = artikel.WK5_ARTI_L_ANFRAGEARTIKEL,
                        Menge = pos.Menge,
                        Bild = pos.Bild,
                        Langtext = pos.Langtext,
                        Option = pos.Option,
                        PosOhneDruck = pos.PosOhneDruck,
                        LangtextDrucken = pos.LangtextDrucken,
                        Vpe = artikel.ARTI_N_COLLI,
                        PosEinkaufspreis = artikel.ARTI_N_EK,
                        Gewicht = artikel.ARTI_N_GEWICHT,
                        DruckBildInVollerBreite = pos.DruckBildInVollerBreite,
                        BildId = pos.BildId
                    };

                    if (pos.IstBundle)
                    {
                        newPos.BundleId = -1;

                        foreach (var bundlePos in pos.BundleArtikel)
                        {
                            newPos.BundleArtikel.Add(new Belegposition
                            {
                                PosNr = ++zähler,
                                Artikelnummer = bundlePos.Artikelnummer,
                                Bezeichnung1 = bundlePos.Bezeichnung1,
                                Bezeichnung2 = bundlePos.Bezeichnung2,
                                Bezeichnung3 = bundlePos.Bezeichnung3,
                                Bezeichnung4 = bundlePos.Bezeichnung4,
                                Bezeichnung5 = bundlePos.Bezeichnung5,
                                MwstSatz = bundlePos.MwstSatz,
                                Gebinde = bundlePos.Gebinde,
                                AnfrageArtikel = bundlePos.AnfrageArtikel,
                                Menge = bundlePos.Menge,
                                Bild = bundlePos.Bild,
                                Langtext = bundlePos.Langtext,
                                Option = bundlePos.Option,
                                PosOhneDruck = bundlePos.PosOhneDruck,
                                LangtextDrucken = bundlePos.LangtextDrucken,
                                Vpe = bundlePos.Vpe,
                                PosEinkaufspreis = 0,
                                Gewicht = bundlePos.Gewicht,
                                DruckBildInVollerBreite = bundlePos.DruckBildInVollerBreite,
                                BildId = bundlePos.BildId,
                                BundleId = -1,
                                BundleParent = newPos
                            });
                        }

                    }

                    neuerBeleg.Positionen.Add(newPos);

                    if (newPos.Preis != pos.Preis || newPos.Rabatt1 != pos.Rabatt1)
                    {
                        meldungen.Add(new AlertBox
                        {
                            AlertType = AlertType.Warning,
                            Message = $"Der Preis oder der Rabatt hat sich für den Artikel {pos.Artikelnummer} geändert!"
                        });
                    }
                }
            }

            await CreateAsync(neuerBeleg, kunde, optionen, fbController);
            return ((T)neuerBeleg, meldungen);
        }

        public async Task<Beleg> ÜbernahmeAsync<T>(T beleg, Kunde kunde, OptionCollection optionen, Mehrwertsteuer versandMehrwertsteuer, FbController2 fbController, int userId, List<int>? posOhneBerechnungÜbernahme = null) where T : Beleg
        {
            Beleg? übernahmeInput = null;

            BelegTyp neuerBelegtyp;
            bool versandBerechnen = true;

            if (beleg is Angebot angebot)
            {
                neuerBelegtyp = BelegTyp.Auftrag;
                übernahmeInput = new Auftrag()
                {
                    BestelltAm = DateTime.Now,
                    BestelltDurch = angebot.AnfrageDurch,
                    Bestellnummer = angebot.Anfragennummer,
                    AngebotsnummerVerknüpfung = angebot.Belegnummer
                };

                var tmp = beleg.Positionen.FirstOrDefault(x => x.ARTI_L_MIETE && x.BPOS_D_MIETE_START != default);

                if (tmp is not null)
                {
                    DateTime liefertermin = tmp.BPOS_D_MIETE_START.AddDays(-1);

                    if (liefertermin.DayOfWeek is DayOfWeek.Sunday)
                    {
                        liefertermin = liefertermin.AddDays(-2);
                    }
                    else if (liefertermin.DayOfWeek is DayOfWeek.Saturday)
                    {
                        liefertermin = liefertermin.AddDays(-1);
                    }

                    übernahmeInput.Liefertermin = liefertermin;
                }
            }
            else if (beleg is Auftrag auftrag)
            {
                neuerBelegtyp = BelegTyp.Lieferschein;
                übernahmeInput = new Lieferschein()
                {
                    BestelltAm = auftrag.BestelltAm,
                    BestelltDurch = auftrag.BestelltDurch,
                    Bestellnummer = auftrag.Bestellnummer,
                    AuftragsnummerVerknüpfung = auftrag.Belegnummer,
                    AngebotsnummerVerknüpfung = auftrag.AngebotsnummerVerknüpfung,
                    BELE_L_BEZAHLT = auftrag.BELE_L_BEZAHLT,
                    OhneBerechnung = auftrag.OhneBerechnung
                };

                // Wir müssen noch berücksichtigen, ob die Option für die doppelte Berechnung der Versandkosten nicht gesetzt ist. 
                // Wenn nicht und wir haben bereits einen Lieferschein, dann dürfen dem Kunden nicht die Frachtkosten erneut berechnet werden
                if (!beleg.VersandDoppeltBerechnen && beleg.LieferscheinnummerVerknüpfung > 0)
                {
                    übernahmeInput.Frachtkosten = 0;
                    übernahmeInput.FrachtkostenEk = 0;
                    versandBerechnen = false;
                }
            }
            else if (beleg is Lieferschein lieferschein)
            {
                neuerBelegtyp = BelegTyp.Rechnung;
                übernahmeInput = new Rechnung()
                {
                    BestelltAm = lieferschein.BestelltAm,
                    BestelltDurch = lieferschein.BestelltDurch,
                    Bestellnummer = lieferschein.Bestellnummer,
                    AuftragsnummerVerknüpfung = lieferschein.AuftragsnummerVerknüpfung,
                    AngebotsnummerVerknüpfung = lieferschein.AngebotsnummerVerknüpfung,
                    LieferscheinnummerVerknüpfung = lieferschein.Belegnummer,
                    Liefertermin = lieferschein.IstAbholung ? DateTime.Now : lieferschein.BELE_D_DATE,
                    BELE_L_BEZAHLT = lieferschein.BELE_L_BEZAHLT
                };
            }
            else if (beleg is Rechnung rechnung)
            {
                neuerBelegtyp = BelegTyp.Gutschrift;
                übernahmeInput = new Gutschrift()
                {
                    BestelltAm = rechnung.BestelltAm,
                    BestelltDurch = rechnung.BestelltDurch,
                    Bestellnummer = rechnung.Bestellnummer,
                    AuftragsnummerVerknüpfung = rechnung.AuftragsnummerVerknüpfung,
                    AngebotsnummerVerknüpfung = rechnung.AngebotsnummerVerknüpfung,
                    LieferscheinnummerVerknüpfung = rechnung.LieferscheinnummerVerknüpfung,
                    RechnungsnummerVerknüpfung = rechnung.Belegnummer,
                    Liefertermin = rechnung.BELE_D_DATE
                };
            }
            else
            {
                throw new InvalidOperationException();
            }

            // 09.07.2021 - MK: Gutschriften müssen 100% mit der Rechnung übereinstimmen
            if (neuerBelegtyp is BelegTyp.Gutschrift)
            {
                übernahmeInput.Name1 = beleg.Name1;
                übernahmeInput.Name2 = beleg.Name2;
                übernahmeInput.Name3 = beleg.Name3;
                übernahmeInput.Strasse = beleg.Strasse;
                übernahmeInput.Ort = beleg.Ort;
                übernahmeInput.Postleitzahl = beleg.Postleitzahl;
                übernahmeInput.Land = beleg.Land;
                übernahmeInput.MwstBerechnen = beleg.MwstBerechnen;
                übernahmeInput.BELE_N_MWST_SATZ1 = beleg.BELE_N_MWST_SATZ1;
            }
            else
            {
                übernahmeInput.Name1 = kunde.KUND_A_NAME1;
                übernahmeInput.Name2 = kunde.KUND_A_NAME2;
                übernahmeInput.Name3 = kunde.KUND_A_NAME3;
                übernahmeInput.Strasse = kunde.KUND_A_STRASSE;
                übernahmeInput.Ort = kunde.KUND_A_ORT;
                übernahmeInput.Postleitzahl = kunde.KUND_A_PLZ;
                übernahmeInput.Land = kunde.KUND_A_LAND;

                übernahmeInput.MwstBerechnen = kunde.KUND_L_MWST;
                übernahmeInput.BELE_N_MWST_SATZ1 = übernahmeInput.MwstBerechnen ? versandMehrwertsteuer.MWST_N_PROZENT : 0;
            }
            übernahmeInput.Anrede = beleg.Anrede;
            übernahmeInput.Ansprechpartner = beleg.Ansprechpartner;
            if (versandBerechnen)
            {
                übernahmeInput.Frachtkosten = beleg.Frachtkosten;
                übernahmeInput.FrachtkostenEk = beleg.FrachtkostenEk;
            }
            übernahmeInput.Kundennummer = beleg.Kundennummer;
            übernahmeInput.LieferanschriftId = beleg.LieferanschriftId;
            übernahmeInput.LieferbedingungId = beleg.LieferbedingungId;
            übernahmeInput.Lieferzeit = beleg.Lieferzeit;
            übernahmeInput.NeutralerVersender = beleg.NeutralerVersender;
            übernahmeInput.Notiz = beleg.Notiz;
            übernahmeInput.RechnungsanschriftId = beleg.RechnungsanschriftId;
            übernahmeInput.Verpackungskosten = beleg.Verpackungskosten;
            übernahmeInput.VersandDoppeltBerechnen = beleg.VersandDoppeltBerechnen;
            übernahmeInput.Versicherungskosten = beleg.Versicherungskosten;
            übernahmeInput.VertreterId = beleg.VertreterId;
            übernahmeInput.ZahlungsbedingungId = beleg.ZahlungsbedingungId;
            übernahmeInput.RmaNummerVerknüpfung = beleg.RmaNummerVerknüpfung;
            übernahmeInput.IstTechnikbeleg = beleg.IstTechnikbeleg;
            übernahmeInput.OhneBerechnungGrund = beleg.OhneBerechnungGrund;
            übernahmeInput.NeutralerLieferschein = beleg.NeutralerLieferschein;
            übernahmeInput.WartungsnummerVerknüpfung = beleg.WartungsnummerVerknüpfung;
            übernahmeInput.SpeditionsAuftragsnummer = beleg.SpeditionsAuftragsnummer;

            await CreateAsync(übernahmeInput, kunde, optionen, fbController);

            // Positionen übernehmen
            await PositionenÜbernehmenAsync(beleg, übernahmeInput, optionen, fbController, posOhneBerechnungÜbernahme);

            fbController.AddParameter("@INP_BELETYP", EnumHelper.GetBelegTypString(neuerBelegtyp));
            fbController.AddParameter("@INP_BELENR", übernahmeInput.Belegnummer);
            fbController.AddParameter("@INP_VORBELEG", beleg.Belegtyp);
            fbController.AddParameter("@INP_OLDBELEG", beleg.Belegnummer);
            await fbController.RunProcedureAsync("WK5_BELEG_STLCOPY");

            await übernahmeInput.LadePositionenAsync(fbController);
            await UpdateAsync(übernahmeInput, kunde, optionen, fbController);

            await SetBelegverknüpfungAsync(übernahmeInput, beleg.Belegnummer, fbController);

            return übernahmeInput;
        }

        public async Task<Beleg> ÜbernahmeAsync<T>(T beleg, OptionCollection optionen, FbController2 fbController) where T : PackBeleg
        {
            Beleg? übernahmeInput = null;
            BelegTyp neuerBelegtyp;

            Kunde kunde = await Kunde.GetKundeAsync(beleg.Kundennummer) ?? throw new NullReferenceException($"{nameof(kunde)} darf nicht null sein. Der Kunde für die Kundennummer {beleg.Kundennummer} konnte nicht gefunden werden.");
            Mehrwertsteuer versandMehrwertsteuer = await Mehrwertsteuer.GetVersandMehrwertsteuerAsync(fbController) ?? throw new NullReferenceException($"{nameof(versandMehrwertsteuer)} darf nicht null sein");
            Lieferanschrift? lieferanschrift = await Lieferanschrift.GetLieferanschriftAsync(beleg.Kundennummer, beleg.LieferanschriftId);
            bool versandBerechnen = true;
            if (beleg is PackAuftrag auftrag)
            {
                neuerBelegtyp = BelegTyp.Lieferschein;
                übernahmeInput = new PackLieferschein(0)
                {
                    BestelltAm = auftrag.BestelltAm,
                    BestelltDurch = auftrag.BestelltDurch,
                    Bestellnummer = auftrag.Bestellnummer,
                    AuftragsnummerVerknüpfung = auftrag.Belegnummer,
                    AngebotsnummerVerknüpfung = auftrag.AngebotsnummerVerknüpfung,
                    BELE_L_BEZAHLT = auftrag.BELE_L_BEZAHLT,
                    OhneBerechnung = auftrag.OhneBerechnung

                };

                // Wir müssen noch berücksichtigen, ob die Option für die doppelte Berechnung der Versandkosten nicht gesetzt ist. 
                // Wenn nicht und wir haben bereits einen Lieferschein, dann dürfen dem Kunden nicht die Frachtkosten erneut berechnet werden
                if (!beleg.VersandDoppeltBerechnen && beleg.LieferscheinnummerVerknüpfung > 0)
                {
                    übernahmeInput.Frachtkosten = 0;
                    übernahmeInput.FrachtkostenEk = 0;
                    versandBerechnen = false;
                }
            }
            else if (beleg is PackLieferschein lieferschein)
            {
                neuerBelegtyp = BelegTyp.Rechnung;
                übernahmeInput = new Rechnung()
                {
                    BestelltAm = lieferschein.BestelltAm,
                    BestelltDurch = lieferschein.BestelltDurch,
                    Bestellnummer = lieferschein.Bestellnummer,
                    AuftragsnummerVerknüpfung = lieferschein.AuftragsnummerVerknüpfung,
                    AngebotsnummerVerknüpfung = lieferschein.AngebotsnummerVerknüpfung,
                    LieferscheinnummerVerknüpfung = lieferschein.Belegnummer,
                    Liefertermin = lieferschein.IstAbholung ? DateTime.Now : lieferschein.BELE_D_DATE,
                    BELE_L_BEZAHLT = lieferschein.BELE_L_BEZAHLT
                };
            }
            else
            {
                throw new InvalidOperationException();
            }

            // 09.07.2021 - MK: Gutschriften müssen 100% mit der Rechnung übereinstimmen
            if (neuerBelegtyp is BelegTyp.Gutschrift)
            {
                übernahmeInput.Name1 = beleg.Name1;
                übernahmeInput.Name2 = beleg.Name2;
                übernahmeInput.Name3 = beleg.Name3;
                übernahmeInput.Strasse = beleg.Strasse;
                übernahmeInput.Ort = beleg.Ort;
                übernahmeInput.Postleitzahl = beleg.Postleitzahl;
                übernahmeInput.Land = beleg.Land;
                übernahmeInput.MwstBerechnen = beleg.MwstBerechnen;
                übernahmeInput.BELE_N_MWST_SATZ1 = beleg.BELE_N_MWST_SATZ1;
            }
            else
            {
                übernahmeInput.Name1 = kunde.KUND_A_NAME1;
                übernahmeInput.Name2 = kunde.KUND_A_NAME2;
                übernahmeInput.Name3 = kunde.KUND_A_NAME3;
                übernahmeInput.Strasse = kunde.KUND_A_STRASSE;
                übernahmeInput.Ort = kunde.KUND_A_ORT;
                übernahmeInput.Postleitzahl = kunde.KUND_A_PLZ;
                übernahmeInput.Land = kunde.KUND_A_LAND;
                if (lieferanschrift is not null)
                {
                    // Für Lieferung in Deutschland immer Mwst!!!!!!!!!
                    if (lieferanschrift.KULA_A_LAND == "DE")
                    {
                        übernahmeInput.MwstBerechnen = true;
                    }
                    else
                    {
                        // Beleg ist hier führend, da die Mwst zuvor durch den Beleg geprüft wurde
                        übernahmeInput.MwstBerechnen = beleg.MwstBerechnen;
                    }

                }
                else
                {
                    übernahmeInput.MwstBerechnen = kunde.KUND_L_MWST;
                }
                übernahmeInput.BELE_N_MWST_SATZ1 = übernahmeInput.MwstBerechnen ? versandMehrwertsteuer.MWST_N_PROZENT : 0;
            }
            übernahmeInput.Anrede = beleg.Anrede;
            übernahmeInput.Ansprechpartner = beleg.Ansprechpartner;
            if (versandBerechnen)
            {
                übernahmeInput.Frachtkosten = beleg.Frachtkosten;
                übernahmeInput.FrachtkostenEk = beleg.FrachtkostenEk;
            }
            übernahmeInput.Kundennummer = beleg.Kundennummer;
            übernahmeInput.LieferanschriftId = beleg.LieferanschriftId;
            übernahmeInput.LieferbedingungId = beleg.LieferbedingungId;
            übernahmeInput.Lieferzeit = beleg.Lieferzeit;
            übernahmeInput.NeutralerVersender = beleg.NeutralerVersender;
            übernahmeInput.Notiz = beleg.Notiz;
            übernahmeInput.RechnungsanschriftId = beleg.RechnungsanschriftId;
            übernahmeInput.Verpackungskosten = beleg.Verpackungskosten;
            übernahmeInput.VersandDoppeltBerechnen = beleg.VersandDoppeltBerechnen;
            übernahmeInput.Versicherungskosten = beleg.Versicherungskosten;
            übernahmeInput.VertreterId = beleg.VertreterId;
            übernahmeInput.ZahlungsbedingungId = beleg.ZahlungsbedingungId;
            übernahmeInput.RmaNummerVerknüpfung = beleg.RmaNummerVerknüpfung;
            übernahmeInput.IstTechnikbeleg = beleg.IstTechnikbeleg;
            übernahmeInput.OhneBerechnungGrund = beleg.OhneBerechnungGrund;
            übernahmeInput.NeutralerLieferschein = beleg.NeutralerLieferschein;
            übernahmeInput.WartungsnummerVerknüpfung = beleg.WartungsnummerVerknüpfung;
            übernahmeInput.SpeditionsAuftragsnummer = beleg.SpeditionsAuftragsnummer;

            await CreateAsync(übernahmeInput, kunde, optionen, fbController);

            // Positionen übernehmen
            await PositionenÜbernehmenAsync(beleg, übernahmeInput, optionen, fbController);

            fbController.AddParameter("@INP_BELETYP", EnumHelper.GetBelegTypString(neuerBelegtyp));
            fbController.AddParameter("@INP_BELENR", übernahmeInput.Belegnummer);
            fbController.AddParameter("@INP_VORBELEG", beleg.Belegtyp);
            fbController.AddParameter("@INP_OLDBELEG", beleg.Belegnummer);
            await fbController.RunProcedureAsync("WK5_BELEG_STLCOPY");

            if (neuerBelegtyp is BelegTyp.Lieferschein)
            {
                await BuchungsdatenKorrigierenAsync(beleg, (PackLieferschein)übernahmeInput, fbController);
            }

            await übernahmeInput.LadePositionenAsync(fbController);
            await UpdateAsync(übernahmeInput, kunde, optionen, fbController);

            await SetBelegverknüpfungAsync(übernahmeInput, beleg.Belegnummer, fbController);

            return übernahmeInput;
        }

        private async Task BuchungsdatenKorrigierenAsync(Beleg auftrag, PackLieferschein lieferschein, FbController2 fbController)
        {
            // Es müssen die Einträge in WK5_BUCHUNG vom Auftrag für den neuen Lieferschein übernommen werden
            int TMP_BUCHUNG_N_BPOSNR = 0;
            int TMP_BUCHUNG_N_STUECKLISTEPOSID = 0;
            decimal TMP_BUCHUNG_N_MENGE = 0;
            int TMP_BUCHUNG_N_BPOSNR_NEU = 0;
            int TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU = 0;
            string? TMP_SERIENNUMMER = "";


            fbController.AddParameter("@BELEGNUMMER_AUFTRAG", auftrag.Belegnummer);
            DataTable buchungsDatenAuftrag = await fbController.SelectDataAsync("SELECT BUCHUNG_N_BPOSNR, BUCHUNG_N_STUECKLISTEPOSID, BUCHUNG_N_MENGE FROM WK5_BUCHUNG WHERE BUCHUNG_N_BELEGNR = @BELEGNUMMER_AUFTRAG AND BUCHUNG_A_BELEGTYP = 'AU'");

            List<int> gebuchtePositionen = new List<int>();
            foreach (DataRow datenAuftragRow in buchungsDatenAuftrag.Rows)
            {
                TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU = 0;
                _ = int.TryParse(datenAuftragRow["BUCHUNG_N_BPOSNR"].ToString(), out TMP_BUCHUNG_N_BPOSNR);
                _ = int.TryParse(datenAuftragRow["BUCHUNG_N_STUECKLISTEPOSID"].ToString(), out TMP_BUCHUNG_N_STUECKLISTEPOSID);
                _ = decimal.TryParse(datenAuftragRow["BUCHUNG_N_MENGE"].ToString(), out TMP_BUCHUNG_N_MENGE);

                fbController.AddParameter("@BPOS_N_POSID_AU", TMP_BUCHUNG_N_BPOSNR);
                TMP_BUCHUNG_N_BPOSNR_NEU = Convert.ToInt32((await fbController.SelectRowAsync("SELECT FIRST 1 BPOS_N_POSID FROM BELEGPOS WHERE BPOS_N_POSID_AU = @BPOS_N_POSID_AU ORDER BY BPOS_N_NR DESC"))!["BPOS_N_POSID"]);
                if (TMP_BUCHUNG_N_STUECKLISTEPOSID != 0)
                {
                    fbController.AddParameter("@BSTU_N_OLDID", TMP_BUCHUNG_N_STUECKLISTEPOSID);
                    TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU = Convert.ToInt32((await fbController.SelectRowAsync("SELECT BSTU_N_POSID FROM BELEGSTUECKLISTE WHERE BSTU_N_OLDID = @BSTU_N_OLDID ORDER BY BSTU_N_POSID DESC"))!["BSTU_N_POSID"]);
                }

                // Die Positionen des Lieferscheins müssen auf die in der WK5 gebuchten Menge angepasst werden.
                // Im ersten Schritt wird die Menge des Artikels im Lieferschein auf die zu buchende Menge angepasst.
                // Stücklisten können nur vollständig inMenge 1 geliefert werden.
                // Muss vor dem Insert passieren, da ein Datenbank Trigger alle Buchungen löscht, wenn die Menge geändert wird
                if (TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU == 0)
                {
                    fbController.AddParameter("@BPOS_N_POSID", TMP_BUCHUNG_N_BPOSNR_NEU);
                    fbController.AddParameter("@MENGE", TMP_BUCHUNG_N_MENGE);
                    await fbController.QueryAsync("UPDATE BELEGPOS SET BPOS_N_MENGE = @MENGE WHERE BPOS_N_POSID = @BPOS_N_POSID");
                }

                fbController.AddParameter("@LIEFERSCHEINNUMMER", lieferschein.Belegnummer);
                fbController.AddParameter("@TMP_BUCHUNG_N_BPOSNR_NEU", TMP_BUCHUNG_N_BPOSNR_NEU);
                fbController.AddParameter("@TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU", TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU);
                fbController.AddParameter("@TMP_BUCHUNG_N_MENGE", TMP_BUCHUNG_N_MENGE);
                await fbController.QueryAsync(@"UPDATE OR INSERT INTO WK5_BUCHUNG(BUCHUNG_A_BELEGTYP, BUCHUNG_N_BELEGNR, BUCHUNG_N_BPOSNR, BUCHUNG_N_STUECKLISTEPOSID, BUCHUNG_N_MENGE)
VALUES('LS', @LIEFERSCHEINNUMMER, @TMP_BUCHUNG_N_BPOSNR_NEU, @TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU, @TMP_BUCHUNG_N_MENGE)");



                // Um alle nicht gebuchten Positionen löschen zu können, merken wir uns alle Positionen, die gebucht wurden
                if (TMP_BUCHUNG_N_MENGE > 0)
                {
                    gebuchtePositionen.Add(TMP_BUCHUNG_N_BPOSNR_NEU);
                }
            }

            // Nicht gebuchte Positionen können aus BELEGPOS und BELEGSTUECKLISTE gelöscht werden.
            fbController.AddParameter("@LIEFERSCHEINNUMMER", lieferschein.Belegnummer);
            DataTable positionenZulöschen = await fbController.SelectDataAsync($"SELECT * FROM BELEGPOS WHERE BPOS_A_TYP = 'LS' AND BPOS_N_NR = @LIEFERSCHEINNUMMER AND BPOS_N_POSID NOT IN ({String.Join(",", gebuchtePositionen)}) AND BPOS_A_ARTIKELNR <> 'TEXT'");

            foreach (DataRow posRow in positionenZulöschen.Rows)
            {
                if (int.TryParse(posRow["BPOS_N_POSID"].ToString(), out int BPOS_N_POSID_ZU_LÖSCHEN))
                {
                    fbController.AddParameter("@BPOS_N_POSID_ZU_LOESCHEN", BPOS_N_POSID_ZU_LÖSCHEN);
                    await fbController.QueryAsync("DELETE FROM BELEGPOS WHERE BPOS_N_POSID = @BPOS_N_POSID_ZU_LOESCHEN");
                    fbController.AddParameter("@BPOS_N_POSID_ZU_LOESCHEN", BPOS_N_POSID_ZU_LÖSCHEN);
                    await fbController.QueryAsync("DELETE FROM BELEGSTUECKLISTE WHERE BSTU_N_BELEPOSID = @BPOS_N_POSID_ZU_LOESCHEN");

                }
            }

            // Bereits Gebuchte Mengen korrigieren
            foreach (var artikel in auftrag.Positionen)
            {
                // Die Anzahl der Gelieferten Menge für den Auftrag muss als nächstes angepasst werden
                fbController.AddParameter("@BPOS_N_POISID_AU", artikel.PosId);
                await fbController.QueryAsync("UPDATE BELEGPOS SET BPOS_N_MENGEGELIEF = (SELECT SUM(BPOS_N_MENGE) AS GELIEFERT FROM BELEGPOS WHERE BPOS_A_TYP = 'LS' AND BPOS_N_POSID_AU = @BPOS_N_POISID_AU) WHERE BPOS_N_POSID = @BPOS_N_POISID_AU");
            }


            // Auch Seriennummern müssen in WK5_BUCHUNG von dem Auftrag für den Lieferschein übernommen werden
            fbController.AddParameter("@BELEGNUMMER_AUFTRAG", auftrag.Belegnummer);
            DataTable datenSeriennummernAuftrag = await fbController.SelectDataAsync("SELECT BUCHUNGSN_N_BPOSNR, BUCHUNGSN_N_STUECKLISTEPOSID, BUCHUNGSN_A_SN FROM WK5_BUCHUNG_SN WHERE BUCHUNGSN_N_BELEGNR = @BELEGNUMMER_AUFTRAG AND BUCHUNGSN_A_BELEGTYP = 'AU'");

            foreach (DataRow datenSeriennummernAuftragRow in datenSeriennummernAuftrag.Rows)
            {
                TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU = 0;
                _ = int.TryParse(datenSeriennummernAuftragRow["BUCHUNGSN_N_BPOSNR"].ToString(), out TMP_BUCHUNG_N_BPOSNR);
                _ = int.TryParse(datenSeriennummernAuftragRow["BUCHUNGSN_N_STUECKLISTEPOSID"].ToString(), out TMP_BUCHUNG_N_STUECKLISTEPOSID);
                TMP_SERIENNUMMER = datenSeriennummernAuftragRow["BUCHUNGSN_A_SN"] as string;

                fbController.AddParameter("@BPOS_N_POSID_AU", TMP_BUCHUNG_N_BPOSNR);
                TMP_BUCHUNG_N_BPOSNR_NEU = Convert.ToInt32((await fbController.SelectRowAsync("SELECT FIRST 1 BPOS_N_POSID FROM BELEGPOS WHERE BPOS_N_POSID_AU = @BPOS_N_POSID_AU ORDER BY BPOS_N_NR DESC"))!["BPOS_N_POSID"]);

                if (TMP_BUCHUNG_N_STUECKLISTEPOSID != 0)
                {
                    fbController.AddParameter("@BSTU_N_OLDID", TMP_BUCHUNG_N_STUECKLISTEPOSID);
                    TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU = Convert.ToInt32((await fbController.SelectRowAsync("SELECT BSTU_N_POSID FROM BELEGSTUECKLISTE WHERE BSTU_N_OLDID = @BSTU_N_OLDID"))!["BSTU_N_POSID"]);
                }

                fbController.AddParameter("@LIEFERSCHEINNUMMER", lieferschein.Belegnummer);
                fbController.AddParameter("@TMP_BUCHUNG_N_BPOSNR_NEU", TMP_BUCHUNG_N_BPOSNR_NEU);
                fbController.AddParameter("@TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU", TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU);
                fbController.AddParameter("@TMP_SERIENNUMMER", TMP_SERIENNUMMER!);
                await fbController.QueryAsync(@"UPDATE OR INSERT INTO WK5_BUCHUNG_SN(BUCHUNGSN_A_BELEGTYP, BUCHUNGSN_N_BELEGNR, BUCHUNGSN_N_BPOSNR, BUCHUNGSN_N_STUECKLISTEPOSID, BUCHUNGSN_A_SN)
VALUES('LS', @LIEFERSCHEINNUMMER, @TMP_BUCHUNG_N_BPOSNR_NEU, @TMP_BUCHUNG_N_STUECKLISTEPOSID_NEU, @TMP_SERIENNUMMER)");

            }
        }
        private async Task PositionenÜbernehmenAsync<T>(T alterBeleg, Beleg neuerBeleg, OptionCollection optionen, FbController2 fbController, List<int>? posOhneBerechnungÜbernahme = null) where T : Beleg
        {

            int zähler = 0;

            bool istPackAuftrag = alterBeleg is PackAuftrag;
            bool istAuftrag = alterBeleg.Belegtyp is "AU";

            bool PositionWirdKopiert(Belegposition pos)
            {
                if (istPackAuftrag)
                {
                    var tmp = (PackBelegposition)pos;

                    if (tmp.BEREITS_GEBUCHT > 0)
                    {
                        return true;
                    }
                }
                else
                {
                    if (pos.BPOS_A_TYP is "AU")
                    {
                        // Texte können ruhig kopiert werden
                        if (pos.Artikelnummer.Equals("TEXT"))
                        {
                            return true;
                        }

                        // Hier ist relevant, ob noch offene Mengen ausstehen
                        if (pos.Menge - pos.BPOS_N_MENGEGELIEF > 0 && (pos.IstBundle || (!pos.IstBundle && pos.BundleId == 0)))
                        {
                            return true;
                        }
                    }
                    else
                    {
                        // Alle anderen Belegtypen ohne Einschränkung kopieren
                        return true;
                    }
                }

                return false;
            }

            foreach (var pos in alterBeleg.Positionen.Where(PositionWirdKopiert))
            {

                // TODO: Aktuell gültigen MwstSatz holen
                Belegposition newPos = new Belegposition()
                {
                    PosNr = ++zähler,
                    BPOS_A_TYP = neuerBeleg.Belegtyp,
                    BPOS_N_NR = neuerBeleg.Belegnummer,
                    Artikelnummer = pos.Artikelnummer,
                    Bezeichnung1 = pos.Bezeichnung1,
                    Bezeichnung2 = pos.Bezeichnung2,
                    Bezeichnung3 = pos.Bezeichnung3,
                    Bezeichnung4 = pos.Bezeichnung4,
                    Bezeichnung5 = pos.Bezeichnung5,
                    Menge = neuerBeleg.Belegtyp is "LS" ? pos.Menge - pos.BPOS_N_MENGEGELIEF : pos.Menge,
                    Preis = pos.Preis,
                    MwstSatz = pos.MwstSatz,
                    BPOS_N_MENGENFAKTOR = pos.BPOS_N_MENGENFAKTOR,
                    Rabattbezeichnung = pos.Rabattbezeichnung,
                    Rabatt1 = pos.Rabatt1,
                    Rabatt2 = pos.Rabatt2,
                    BPOS_N_OLDPOSID = pos.PosId,
                    Option = pos.Option,
                    BPOS_D_LIEFERTERMIN = pos.BPOS_D_LIEFERTERMIN,
                    Langtext = pos.Langtext,
                    LangtextDrucken = pos.LangtextDrucken,
                    PosOhneDruck = pos.PosOhneDruck,
                    PosEinkaufspreis = pos.PosEinkaufspreis,
                    BPOS_N_POSID_AU = pos.BPOS_A_TYP is "AU" ? pos.PosId : pos.BPOS_N_POSID_AU,
                    BPOS_D_MIETE_START = pos.BPOS_D_MIETE_START,
                    BPOS_D_MIETE_ENDE = pos.BPOS_D_MIETE_ENDE
                };

                if (pos.IstBundle)
                {
                    newPos.BundleId = -1;
                    foreach (var bundlePos in pos.BundleArtikel)
                    {
                        newPos.BundleArtikel.Add(new Belegposition
                        {
                            PosNr = ++zähler,
                            BPOS_A_TYP = neuerBeleg.Belegtyp,
                            BPOS_N_NR = neuerBeleg.Belegnummer,
                            Artikelnummer = bundlePos.Artikelnummer,
                            Bezeichnung1 = bundlePos.Bezeichnung1,
                            Bezeichnung2 = bundlePos.Bezeichnung2,
                            Bezeichnung3 = bundlePos.Bezeichnung3,
                            Bezeichnung4 = bundlePos.Bezeichnung4,
                            Bezeichnung5 = bundlePos.Bezeichnung5,
                            Menge = neuerBeleg.Belegtyp is "LS" ? bundlePos.Menge - bundlePos.BPOS_N_MENGEGELIEF : bundlePos.Menge,
                            Preis = bundlePos.Preis,
                            MwstSatz = bundlePos.MwstSatz,
                            BPOS_N_MENGENFAKTOR = bundlePos.BPOS_N_MENGENFAKTOR,
                            Rabattbezeichnung = bundlePos.Rabattbezeichnung,
                            Rabatt1 = bundlePos.Rabatt1,
                            Rabatt2 = bundlePos.Rabatt2,
                            BPOS_N_OLDPOSID = bundlePos.PosId,
                            Option = bundlePos.Option,
                            BPOS_D_LIEFERTERMIN = bundlePos.BPOS_D_LIEFERTERMIN,
                            Langtext = bundlePos.Langtext,
                            LangtextDrucken = bundlePos.LangtextDrucken,
                            PosOhneDruck = bundlePos.PosOhneDruck,
                            PosEinkaufspreis = bundlePos.PosEinkaufspreis,
                            BPOS_N_POSID_AU = bundlePos.BPOS_A_TYP is "AU" ? bundlePos.PosId : bundlePos.BPOS_N_POSID_AU,
                            BPOS_D_MIETE_START = bundlePos.BPOS_D_MIETE_START,
                            BPOS_D_MIETE_ENDE = bundlePos.BPOS_D_MIETE_ENDE,
                            BundleId = newPos.BundleId
                        });
                    }
                }

                Option? option = optionen[pos.Option];
                if (option is not null && !option.OPTI_L_BERECHNEN)
                {
                    if (posOhneBerechnungÜbernahme is not null)
                    {
                        if (posOhneBerechnungÜbernahme.Contains(pos.PosId))
                        {
                            newPos.Option = string.Empty;
                        }
                        else
                        {
                            --zähler;
                            continue;
                        }
                    }
                }

                if (newPos.IstBundle)
                {
                    // SELECT NEW BUNDLE ID
                    int bundleId = Convert.ToInt32(await fbController.FetchObjectAsync("SELECT MAX(BPOS_N_BUNDLEID) + 1 FROM BELEGPOS"));
                    newPos.BundleId = bundleId;
                    // SET BUNDLEID OF POS
                    foreach (var item in newPos.BundleArtikel)
                    {
                        item.BundleId = bundleId;
                        await CreatePositionAsync(item, EnumHelper.GetBelegTyp(neuerBeleg.Belegtyp), true, fbController);
                    }
                }

                await CreatePositionAsync(newPos, EnumHelper.GetBelegTyp(neuerBeleg.Belegtyp), false, fbController);
            }

            if (neuerBeleg.Belegtyp is "RE" && alterBeleg.Belegtyp is "LS")
            {
                fbController.AddParameter("@USER_ID", fbController.UserId);
                fbController.AddParameter("@INP_TYP_NEW", neuerBeleg.Belegtyp);
                fbController.AddParameter("@INP_NR_NEW", neuerBeleg.Belegnummer);
                await fbController.QueryAsync(@"INSERT INTO BELEGSN
(
    BSNR_N_POSID,
    BSNR_A_SN,
    BSNR_N_STLPOSID,
    BSNR_N_CHARGE,
    BSNR_N_MENGE,
    BSNR_TIMESTAMP,
    BSNR_N_LASTUSER
)
SELECT
    BPOS_N_POSID,
    BSNR_A_SN,
    BSNR_N_STLPOSID,
    BSNR_N_CHARGE,
    BSNR_N_MENGE,
    CURRENT_TIMESTAMP,
    @USER_ID
FROM BELEGPOS
LEFT OUTER JOIN BELEGSN ON BSNR_N_POSID = BPOS_N_OLDPOSID
WHERE   
        BPOS_A_TYP = @INP_TYP_NEW 
    AND BPOS_N_NR = @INP_NR_NEW
    AND COALESCE(BSNR_N_CHARGE, 0) <> 0
    AND COALESCE(BPOS_N_OLDPOSID,0) <> 0
    AND COALESCE(BSNR_N_MENGE,0) <> 0
    AND COALESCE(BSNR_A_SN,'') <> ''");


                fbController.AddParameter("@USER_ID", fbController.UserId);
                fbController.AddParameter("@INP_TYP_NEW", neuerBeleg.Belegtyp);
                fbController.AddParameter("@INP_NR_NEW", neuerBeleg.Belegnummer);
                await fbController.QueryAsync(@"INSERT INTO BELEGCHARGEN
(
    BCHA_N_POSID,
    BCHA_N_CHARGE,
    BCHA_N_MENGE,
    BCHA_N_STCKLPOSID,
    BCHA_L_STCKLCHARGE,
    BCHA_N_FERTID,
    BCHA_TIMESTAMP,
    BCHA_N_LASTUSER
)
SELECT
    BPOS_N_POSID,
    BCHA_N_CHARGE,
    BCHA_N_MENGE,
    BCHA_N_STCKLPOSID,
    IIF(COALESCE(BCHA_L_STCKLCHARGE, 'N') <> 'Y', 'N', 'Y'),
    BCHA_N_FERTID,
    CURRENT_TIMESTAMP,
    @USER_ID
FROM BELEGPOS
LEFT OUTER JOIN BELEGCHARGEN ON BCHA_N_POSID = BPOS_N_OLDPOSID
WHERE   
        BPOS_A_TYP = @INP_TYP_NEW 
    AND BPOS_N_NR = @INP_NR_NEW
    AND COALESCE(BCHA_N_CHARGE, 0) <> 0
    AND COALESCE(BPOS_N_OLDPOSID,0) <> 0
    AND COALESCE(BCHA_N_MENGE,0) <> 0
    AND(BCHA_L_STCKLCHARGE = 'N' OR COALESCE(BCHA_N_STCKLPOSID, 0) <> 0)");

            }





        }

        private async Task SetBelegverknüpfungAsync(Beleg neuerBeleg, int basisBelegnummer, FbController2 fbController)
        {
            if (neuerBeleg is Auftrag auftrag)
            {
                fbController.AddParameter("@ANGEBOTSNUMMER", basisBelegnummer);
                fbController.AddParameter("@AUFTRAGSNUMMER", auftrag.Belegnummer);
                await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_AU = @AUFTRAGSNUMMER WHERE BELE_A_TYP = 'AN' AND BELE_N_NR = @ANGEBOTSNUMMER");
            }
            else if (neuerBeleg is Lieferschein lieferschein)
            {
                fbController.AddParameter("@AUFTRAGSNUMMER", basisBelegnummer);
                fbController.AddParameter("@LIEFERSCHEINNUMMER", lieferschein.Belegnummer);
                await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_LS = @LIEFERSCHEINNUMMER WHERE BELE_A_TYP = 'AU' AND BELE_N_NR = @AUFTRAGSNUMMER");

                if (lieferschein.AngebotsnummerVerknüpfung > 0)
                {
                    fbController.AddParameter("@ANGEBOTSNUMMER", lieferschein.AngebotsnummerVerknüpfung);
                    fbController.AddParameter("@LIEFERSCHEINNUMMER", lieferschein.Belegnummer);
                    await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_LS = @LIEFERSCHEINNUMMER WHERE BELE_A_TYP = 'AN' AND BELE_N_NR = @ANGEBOTSNUMMER");
                }
            }
            else if (neuerBeleg is PackLieferschein packLieferschein)
            {
                fbController.AddParameter("@AUFTRAGSNUMMER", basisBelegnummer);
                fbController.AddParameter("@LIEFERSCHEINNUMMER", packLieferschein.Belegnummer);
                await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_LS = @LIEFERSCHEINNUMMER WHERE BELE_A_TYP = 'AU' AND BELE_N_NR = @AUFTRAGSNUMMER");

                if (packLieferschein.AngebotsnummerVerknüpfung > 0)
                {
                    fbController.AddParameter("@ANGEBOTSNUMMER", packLieferschein.AngebotsnummerVerknüpfung);
                    fbController.AddParameter("@LIEFERSCHEINNUMMER", packLieferschein.Belegnummer);
                    await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_LS = @LIEFERSCHEINNUMMER WHERE BELE_A_TYP = 'AN' AND BELE_N_NR = @ANGEBOTSNUMMER");
                }
            }
            else if (neuerBeleg is Rechnung rechnung)
            {
                fbController.AddParameter("@RECHNUNGSNUMMER", rechnung.Belegnummer);
                fbController.AddParameter("@LIEFERSCHEINNUMMER", basisBelegnummer);
                await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_RE = @RECHNUNGSNUMMER WHERE BELE_A_TYP = 'LS' AND BELE_N_NR = @LIEFERSCHEINNUMMER");

                if (rechnung.AngebotsnummerVerknüpfung > 0)
                {
                    fbController.AddParameter("@ANGEBOTSNUMMER", rechnung.AngebotsnummerVerknüpfung);
                    fbController.AddParameter("@RECHNUNGSNUMMER", rechnung.Belegnummer);
                    await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_RE = @RECHNUNGSNUMMER WHERE BELE_A_TYP = 'AN' AND BELE_N_NR = @ANGEBOTSNUMMER");
                }

                if (rechnung.AuftragsnummerVerknüpfung > 0)
                {
                    fbController.AddParameter("@AUFTRAGSNUMMER", rechnung.AuftragsnummerVerknüpfung);
                    fbController.AddParameter("@RECHNUNGSNUMMER", rechnung.Belegnummer);
                    await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_RE = @RECHNUNGSNUMMER WHERE BELE_A_TYP = 'AU' AND BELE_N_NR = @AUFTRAGSNUMMER");
                }
            }
            else if (neuerBeleg is Gutschrift gutschrift)
            {
                fbController.AddParameter("@GUTSCHRIFTNUMMER", gutschrift.Belegnummer);
                fbController.AddParameter("@RECHNUNGSNUMMER", basisBelegnummer);
                await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_GU = @GUTSCHRIFTNUMMER WHERE BELE_A_TYP = 'RE' AND BELE_N_NR = @RECHNUNGSNUMMER");

                if (gutschrift.AngebotsnummerVerknüpfung > 0)
                {
                    fbController.AddParameter("@ANGEBOTSNUMMER", gutschrift.AngebotsnummerVerknüpfung);
                    fbController.AddParameter("@GUTSCHRIFTNUMMER", gutschrift.Belegnummer);
                    await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_GU = @GUTSCHRIFTNUMMER WHERE BELE_A_TYP = 'AN' AND BELE_N_NR = @ANGEBOTSNUMMER");
                }

                if (gutschrift.AuftragsnummerVerknüpfung > 0)
                {
                    fbController.AddParameter("@AUFTRAGSNUMMER", gutschrift.AuftragsnummerVerknüpfung);
                    fbController.AddParameter("@GUTSCHRIFTNUMMER", gutschrift.Belegnummer);
                    await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_GU = @GUTSCHRIFTNUMMER WHERE BELE_A_TYP = 'AU' AND BELE_N_NR = @AUFTRAGSNUMMER");
                }

                if (gutschrift.LieferscheinnummerVerknüpfung > 0)
                {
                    fbController.AddParameter("@LIEFERSCHEINNUMMER", gutschrift.LieferscheinnummerVerknüpfung);
                    fbController.AddParameter("@GUTSCHRIFTNUMMER", gutschrift.Belegnummer);
                    await fbController.QueryAsync("UPDATE BELEGE SET BELE_N_NR_GU = @GUTSCHRIFTNUMMER WHERE BELE_A_TYP = 'LS' AND BELE_N_NR = @LIEFERSCHEINNUMMER");
                }
            }
            else
            {
                throw new InvalidOperationException();
            }
        }

        public async Task<(bool istVerfügbar, int belegnummer, string belegtyp)> IstTerminVerfügbarAsync(Belegposition pos, FbController2 fbController)
        {
            fbController.AddParameter("@START", pos.BPOS_D_MIETE_START);
            fbController.AddParameter("@ENDE", pos.BPOS_D_MIETE_ENDE);
            fbController.AddParameter("@BPOS_A_ARTIKELNR", pos.Artikelnummer);
            string sql = @"SELECT 
BPOS_A_TYP, BPOS_N_NR 
FROM BELEGPOS
LEFT JOIN BELEGE B ON(BELE_A_TYP = BPOS_A_TYP AND BELE_N_NR = BPOS_N_NR)
WHERE(BPOS_D_MIETE_START BETWEEN @START AND @ENDE OR BPOS_D_MIETE_ENDE BETWEEN @START AND @ENDE)
AND BPOS_A_TYP IN('AU', 'LS')
AND BPOS_A_ARTIKELNR = @BPOS_A_ARTIKELNR";
            if (pos.BPOS_A_TYP is "AU")
            {
                sql += " AND BPOS_N_NR != @BPOS_N_NR AND BELE_N_NR_AU != @BPOS_N_NR";
                fbController.AddParameter("@BPOS_N_NR", pos.BPOS_N_NR);
            }
            DataRow? row = await fbController.SelectRowAsync(sql);

            if (row is null)
            {
                return (true, 0, string.Empty);
            }
            else
            {
                return (false, row.Field<int>("BPOS_N_NR"), row.Field<string>("BPOS_A_TYP")!);
            }
        }
        #endregion
        #region Allgemein

        /// <summary>
        /// Lädt sich die Manuell versendeten Belege eines bestimmten Datums
        /// </summary>
        /// <param name="date">Das Datum von dem sich die Belege geladen werden sollen</param>
        /// <returns>Gibt eine <see cref="List{ManuellVersand}"/> zurück die alle Manuell versendeten Belege dieses Datums beinhaltet</returns>
        public async IAsyncEnumerable<ManuellVersand> GetManuellVersendeteBelegeAsync(DateTime date)
        {
            using FbController2 fbController = new FbController2();
            string query = "SELECT * FROM WK5_MANUELL_VERSAND WHERE CAST(MANUELL_T_TIMESTAMP as date) = @DATE";

            fbController.AddParameter("@DATE", date);
            DataTable data = await fbController.SelectDataAsync(query);

            foreach (DataRow row in data.Rows)
            {
                var mv = ObjectErweiterung.DataRowZuObjekt<ManuellVersand>(new ManuellVersand(), row);
                await mv.LadeBelegAsync();
                yield return mv;
            }
        }

        /// <summary>
        /// Kopiert den angegeben Beleg in einen neuen Beleg des selben Typs. Bei dieser Funktion werden keine Preise neuberechnet.
        /// </summary>
        /// <param name="fbController"></param>
        /// <param name="belegnummer"></param>
        /// <param name="belegTyp"></param>
        /// <returns></returns>
        public async Task<Beleg?> KopiereBelegAsync(FbController2 fbController, int belegnummer, BelegTyp belegTyp)
        {
            fbController.AddParameter("@INP_BELEGNR", belegnummer);
            fbController.AddParameter("@INP_BELEGTYP", EnumHelper.GetBelegTypString(belegTyp));

            var neueBelegnummerObj = await fbController.FetchObjectAsync("execute procedure PROZ_BELEGCOPY(@INP_BELEGNR, @INP_BELEGTYP)");

            if (neueBelegnummerObj is null)
            {
                return null;
            }

            _ = int.TryParse(neueBelegnummerObj.ToString(), out int neueBelegnummer);

            if (neueBelegnummer > 0)
            {
                // Wir lesen an dieser Stelle die Gesamtsummen neu ein, da ggf. durch eine Änderung des Steuersatzes sich ein anderer Preis ergibt.
                fbController.AddParameter("@INP_BELEGNR", neueBelegnummer);
                fbController.AddParameter("@INP_BELEGTYP", "RE");
                fbController.AddParameter("@INP_SCZ", "N");
                await fbController.RunProcedureAsync("PROZ_BELEGESAMTSUMMEN");
            }

            return belegTyp switch
            {
                BelegTyp.Rechnung => await Rechnung.GetRechnungAsync(neueBelegnummer, fbController),
                BelegTyp.Auftrag => await Auftrag.GetAuftragAsync(neueBelegnummer, fbController),
                BelegTyp.Lieferschein => await Lieferschein.GetLieferscheinAsync(neueBelegnummer, fbController),
                BelegTyp.Gutschrift => await Gutschrift.GetGutschriftAsync(neueBelegnummer, fbController),
                BelegTyp.Angebot => await Angebot.GetAngebotAsync(neueBelegnummer, fbController),
                _ => null,
            };
        }
        #endregion
        #region Suche
        public async IAsyncEnumerable<Beleg> SucheBelegeAsync(BelegFilter filter, FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            var data = await fbController.SelectDataAsync(filter.ToSqlQuery(fbController), cancellationToken);
            foreach (DataRow row in data.Rows)
            {
                cancellationToken.ThrowIfCancellationRequested();

                Beleg? beleg = filter.BelegTyp switch
                {
                    BelegTyp.Auftrag => ObjectErweiterung.DataRowZuObjekt(new Auftrag(), row),
                    BelegTyp.Lieferschein => ObjectErweiterung.DataRowZuObjekt(new Lieferschein(), row),
                    BelegTyp.Rechnung => ObjectErweiterung.DataRowZuObjekt(new Rechnung(), row),
                    BelegTyp.Angebot => ObjectErweiterung.DataRowZuObjekt(new Angebot(), row),
                    BelegTyp.Gutschrift => ObjectErweiterung.DataRowZuObjekt(new Gutschrift(), row),
                    _ => null
                };

                cancellationToken.ThrowIfCancellationRequested();

                if (beleg is not null)
                {
                    await beleg.LadePositionenAsync(fbController);
                    yield return beleg;
                }
            }

        }

        /// <summary>
        /// Gibt an wie viele Ergebnisse für das Suchkriterium existieren
        /// </summary>
        public async Task<int> GetSuchBelegAmountAsync(BelegFilter filter, CancellationToken cancellationToken)
        {
            using FbController2 fbController = new FbController2();

            var data = await fbController.FetchObjectAsync(filter.ToCountQuery(fbController), cancellationToken);
            if (data is null)
            {
                return 0;
            }

            _ = int.TryParse(data.ToString(), out int count);

            return count;
        }


        public async Task<Dictionary<BelegTyp, List<int>>> GetSubBelege(BelegTyp belegTyp, int belegnummer, FbController2 fbController)
        {
            fbController.AddParameter("@BELEGNUMMER", belegnummer);
            DataTable data = belegTyp switch
            {
                BelegTyp.Rechnung => await fbController.SelectDataAsync("SELECT DISTINCT BELE_N_NR, BELE_A_TYP FROM BELEGE WHERE BELE_N_NR_RE = @BELEGNUMMER"),
                BelegTyp.Auftrag => await fbController.SelectDataAsync("SELECT DISTINCT BELE_N_NR, BELE_A_TYP FROM BELEGE WHERE BELE_N_NR_AU = @BELEGNUMMER"),
                BelegTyp.Lieferschein => await fbController.SelectDataAsync("SELECT DISTINCT BELE_N_NR, BELE_A_TYP FROM BELEGE WHERE BELE_N_NR_LS = @BELEGNUMMER"),
                BelegTyp.Gutschrift => await fbController.SelectDataAsync("SELECT DISTINCT BELE_N_NR, BELE_A_TYP FROM BELEGE WHERE BELE_N_NR_GU = @BELEGNUMMER"),
                BelegTyp.Angebot => await fbController.SelectDataAsync("SELECT DISTINCT BELE_N_NR, BELE_A_TYP FROM BELEGE WHERE BELE_N_NR_AN = @BELEGNUMMER"),
                _ => new DataTable(),
            };

            Dictionary<BelegTyp, List<int>> results = new Dictionary<BelegTyp, List<int>>();

            List<SubBeleg> belege = new List<SubBeleg>();

            foreach (DataRow row in data.Rows)
            {
                belege.Add(ObjectErweiterung.DataRowZuObjekt(new SubBeleg(), row));
            }

            var sucheAngebote = belege.Where(x => x.Belegtyp == "AN").Select(x => x.Belegnummer);
            var sucheAufträge = belege.Where(x => x.Belegtyp == "AU").Select(x => x.Belegnummer);
            var sucheLieferscheine = belege.Where(x => x.Belegtyp == "LS").Select(x => x.Belegnummer);
            var sucheRechnungen = belege.Where(x => x.Belegtyp == "RE").Select(x => x.Belegnummer);
            var sucheGutschriften = belege.Where(x => x.Belegtyp == "GU").Select(x => x.Belegnummer);

            // Es macht keinen Sinn, dass es verknüpfte Belege des selben Typs gibt
            if (belegTyp is not BelegTyp.Angebot && sucheAngebote.Any())
            {
                results.Add(BelegTyp.Angebot, sucheAngebote.ToList());
            }

            if (belegTyp is not BelegTyp.Auftrag && sucheAufträge.Any())
            {
                results.Add(BelegTyp.Auftrag, sucheAufträge.ToList());
            }

            if (belegTyp is not BelegTyp.Lieferschein && sucheLieferscheine.Any())
            {
                results.Add(BelegTyp.Lieferschein, sucheLieferscheine.ToList());
            }

            if (belegTyp is not BelegTyp.Rechnung && sucheRechnungen.Any())
            {
                results.Add(BelegTyp.Rechnung, sucheRechnungen.ToList());
            }

            if (belegTyp is not BelegTyp.Gutschrift && sucheGutschriften.Any())
            {
                results.Add(BelegTyp.Gutschrift, sucheGutschriften.ToList());
            }

            return results;
        }

        public async Task<int> GetAnzahlKundenbelegeAsync(KundenBelegeFilter filter)
        {
            using FbController2 fbController = new FbController2();
            string sql = filter.ToCountQuery(fbController);

            var result = await fbController.FetchObjectAsync(sql);

            if (result is null)
            {
                return 0;
            }

            _ = int.TryParse(result.ToString(), out int anzahl);

            return anzahl;
        }
        public async IAsyncEnumerable<KundenBeleg> SucheKundenbelegeAsync(KundenBelegeFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            using FbController2 fbController = new FbController2();
            string sql = filter.ToSqlQuery(fbController);

            DataTable data = await fbController.SelectDataAsync(sql, cancellationToken);

            foreach (DataRow row in data.Rows)
            {
                cancellationToken.ThrowIfCancellationRequested();
                yield return ObjectErweiterung.DataRowZuObjekt(new KundenBeleg(), row);
            }
        }

        public async IAsyncEnumerable<OffeneRechnung> GetOffeneRechnungen(OffeneRechnungenFilter filter, FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            DataTable data = await fbController.SelectDataAsync(filter.ToSqlQuery(fbController), cancellationToken);

            foreach (DataRow row in data.Rows)
            {
                cancellationToken.ThrowIfCancellationRequested();
                yield return ObjectErweiterung.DataRowZuObjekt(new OffeneRechnung(), row);
            }
        }
        #endregion
        #region Lieferscheine
        public async IAsyncEnumerable<LieferscheinOhneBerechnung> GetLieferscheineOhneBerechnungAsync(LieferscheinOhneBerechnungFilter filter, FbController2 fbController)
        {
            var data = await fbController.SelectDataAsync(filter.ToSqlQuery(fbController));

            foreach (DataRow row in data.Rows)
            {
                yield return ObjectErweiterung.DataRowZuObjekt(new LieferscheinOhneBerechnung(), row);
            }
        }

        public async Task<int> GetLieferscheineOhneBerechnungAnzahlAsync(LieferscheinOhneBerechnungFilter filter, FbController2 fbController)
        {
            int anzahl = Convert.ToInt32(await fbController.FetchObjectAsync(filter.ToCountQuery(fbController)));
            return anzahl;
        }

        public async Task LieferscheinOhneBerechnungGenehmigenAsync(LieferscheinOhneBerechnung lieferschein, OhneBerechnungInput input, FbController2 fbController)
        {
            fbController.AddParameter("@BELE_N_NR", lieferschein.BELE_N_NR);
            fbController.AddParameter("@BELE_A_ERLOESKONTO", input.Erlöskonto.ToString());
            fbController.AddParameter("@BELE_A_OHNE_BERECHNUNG_GRUND", input.Begründung);
            await fbController.QueryAsync("UPDATE BELEGE SET BELE_L_BERECHNET = 'Y', WK5_BELE_L_OHNE_BERECHNUNG = 'Y', BELE_A_ERLOESKONTO = @BELE_A_ERLOESKONTO, BELE_A_OHNE_BERECHNUNG_GRUND = @BELE_A_OHNE_BERECHNUNG_GRUND WHERE BELE_A_TYP = 'LS' AND BELE_N_NR = @BELE_N_NR");

            fbController.AddParameter("@INP_TYP", "LS");
            fbController.AddParameter("@INP_BELEGNR", lieferschein.BELE_N_NR);
            fbController.AddParameter("@INP_ART", 40);
            fbController.AddParameter("@INP_ALT", "Ohne Berechnung Genehmigt");
            fbController.AddParameter("@INP_NEU", string.Empty);
            fbController.AddParameter("@INP_POSITION", 0);
            await fbController.RunProcedureAsync("PROZ_BELEGHIST_INSERT");
        }

        public async Task SetBerechnetAsync(Lieferschein lieferschein, FbController2 fbController)
        {
            fbController.AddParameter("@BELE_N_NR", lieferschein.Belegnummer);
            await fbController.QueryAsync("UPDATE BELEGE SET BELE_L_BERECHNET = 'Y' WHERE BELE_A_TYP = 'LS' AND BELE_N_NR = @BELE_N_NR");
            lieferschein.BELE_L_BERECHNET = true;
        }

        public async IAsyncEnumerable<Lieferschein> GetLieferscheineBereitZurAbholungAsync(FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            DataTable data = await fbController.SelectDataAsync(@"SELECT BELE_A_TYP, BELE_N_NR, BELE_A_NAME1, BELE_A_KUNDENNR FROM BELEGE 
LEFT JOIN LIEFERBEDINGUNG ON LIBD_N_NR = BELE_N_LIEFERUNG
WHERE BELE_A_TYP = 'LS' AND LIBD_L_ABHOLUNG = 'Y' AND COALESCE(BELE_N_NR_RE, 0) = 0
AND BELE_L_ERLEDIGT = 'N'", cancellationToken);

            foreach (DataRow row in data.Rows)
            {
                cancellationToken.ThrowIfCancellationRequested();
                yield return ObjectErweiterung.DataRowZuObjekt(new Lieferschein(), row);
            }
        }

        public Task<List<AuswertungOhneBerechnung>> GetAuswertungOhneBerechnungAsync(OhneBerechnungAuswertungFilter filter, FbController2 fbController)
        {
            return fbController.SelectDataAsync<AuswertungOhneBerechnung>(filter.ToSqlQuery(fbController), new { START = filter.Zeitraum.Von.ToShortDateString(), ENDE = filter.Zeitraum.Bis.ToShortDateString() });
        }
        #endregion
        #region Aufträge
        public async Task AuftragPausierenAsync(int auftragsnummer, bool pausiert, FbController2 fbController)
        {
            fbController.AddParameter("@PAUSIERT", pausiert);
            fbController.AddParameter("@AUFTRAGSNUMMER", auftragsnummer);
            await fbController.QueryAsync("UPDATE BELEGE SET WK5_BELE_L_PAUSIERT = @PAUSIERT WHERE BELE_A_TYP = 'AU' AND BELE_N_NR = @AUFTRAGSNUMMER");
        }

        public async Task AuftragStornierenAsync(Auftrag auftrag, string begründung, int userId, FbController2 fbController)
        {
            if (auftrag.BELE_L_STORNO)
            {
                return;
            }

            auftrag.Notiz = $"{auftrag.Notiz}{Environment.NewLine}Stornobegründung: {begründung}";
            fbController.AddParameter("@AUFTRAGSNUMMER", auftrag.Belegnummer);
            fbController.AddParameter("@BELE_B_NOTIZ", auftrag.Notiz);
            await fbController.QueryAsync("UPDATE BELEGE SET BELE_L_STORNO = 'Y', BELE_B_NOTIZ = @BELE_B_NOTIZ WHERE BELE_N_NR = @AUFTRAGSNUMMER AND BELE_A_TYP = 'AU'");

            fbController.AddParameter("@BSTA_A_BELEGTYP", "AU");
            fbController.AddParameter("@BSTA_N_BELEGNR", auftrag.Belegnummer);
            fbController.AddParameter("@BSTA_N_ART", 5);
            fbController.AddParameter("@BSTA_A_GRUND", begründung);
            fbController.AddParameter("@BSTA_N_USER", userId);
            fbController.AddParameter("@BSTA_A_BEZEICHNUNG", "AU Storniert");
            await fbController.QueryAsync(@"INSERT INTO BELEG_STAT_BUCHUNG
(
BSTA_A_BELEGTYP, BSTA_N_BELEGNR, BSTA_N_ART, 
BSTA_A_GRUND, BSTA_N_USER, BSTA_A_BEZEICHNUNG,
BSTA_TIMESTAMP
)
VALUES
(
@BSTA_A_BELEGTYP, @BSTA_N_BELEGNR, @BSTA_N_ART,
@BSTA_A_GRUND, @BSTA_N_USER, @BSTA_A_BEZEICHNUNG,
CURRENT_TIMESTAMP
)");

            await BelegChange.InsertBelegChangeAsync(fbController, new BelegChange
            {
                BCNG_A_BELEGTYP = "AU",
                BCNG_N_BELEGNR = auftrag.Belegnummer.ToString(),
                BCNG_N_ART = 42,
                BCNG_A_WERTALT = "AU Storniert"
            });

            fbController.AddParameter("@INP_POSID", auftrag.Belegnummer);
            fbController.AddParameter("@INP_MENGE", 0);
            fbController.AddParameter("@INP_NUR_STATUS", "Y");
            fbController.AddParameter("@INP_GROESSEN", "N");
            await fbController.RunProcedureAsync("AUFTRAGSSTATUS");

            fbController.AddParameter("@INP_AUFTRAG", auftrag.Belegnummer);
            fbController.AddParameter("@INP_USER", userId);
            await fbController.RunProcedureAsync("PROZ_AUFTRAGERLEDIGT");
        }

        public async Task AuftragAufErledigtSetzenAsync(Auftrag auftrag, int userId, FbController2 fbController)
        {
            // Aufträge auf Erledigt setzen
            if (!auftrag.BELE_L_ERLEDIGT)
            {
                fbController.AddParameter("@INP_AUFTRAG", auftrag.Belegnummer);
                fbController.AddParameter("@INP_USER", userId);
                await fbController.RunProcedureAsync("PROZ_AUFTRAGERLEDIGT");
            }
        }

        public async Task AuftragAufErledigtSetzenAsync(int auftragsnummer, int userId, FbController2 fbController)
        {
            fbController.AddParameter("@INP_AUFTRAG", auftragsnummer);
            fbController.AddParameter("@INP_USER", userId);
            await fbController.RunProcedureAsync("PROZ_AUFTRAGERLEDIGT");
        }

        public Task EnableDirektlieferungAsync(int auftragsnummer, FbController2 fbController)
        {
            fbController.AddParameter("@BELE_N_NR", auftragsnummer);
            return fbController.QueryAsync("UPDATE BELEGE SET WK5_BELE_L_DIREKTLIEFERUNG = 'Y' WHERE BELE_A_TYP = 'AU' AND BELE_N_NR = @BELE_N_NR");
        }
        #endregion


        public async IAsyncEnumerable<BelegÜbersicht> GetBelegÜbersichtAsync(BelegÜbersichtFilter filter, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                using FbController2 fbController = new FbController2();
                if (!cancellationToken.IsCancellationRequested)
                {
                    List<BelegÜbersicht> suchCache = new List<BelegÜbersicht>();
                    int addedItems = 0;
                    for (int i = BelegÜbersichtFilter.MinPhase; i <= BelegÜbersichtFilter.MaxPhase; i++)
                    {


                        filter.Phase = i;
                        DataTable data = await fbController.SelectDataAsync(filter.ToSqlQuery(fbController), cancellationToken);

                        foreach (DataRow row in data.Rows)
                        {
                            if (addedItems >= filter.Limit)
                            {
                                break;
                            }

                            if (cancellationToken.IsCancellationRequested)
                            {
                                break;
                            }

                            BelegÜbersicht tmp = ObjectErweiterung.DataRowZuObjekt(new BelegÜbersicht(), row);

                            if (!suchCache.Where(x => x.BELU_N_NR == tmp.BELU_N_NR).Any())
                            {
                                suchCache.Add(tmp);
                                addedItems++;
                                yield return tmp;
                            }
                        }

                        if (addedItems >= filter.Limit)
                        {
                            break;
                        }

                        if (!filter.DoPhaseSearch)
                        {
                            break;
                        }

                        if (i <= BelegÜbersichtFilter.AbortableUntilPhase && addedItems > 0)
                        {
                            break;
                        }
                    }
                }
            }
        }

        public async Task<int> GetBelegÜbersichtAnzahlAsync(BelegÜbersichtFilter filter, CancellationToken cancellationToken)
        {
            if (!cancellationToken.IsCancellationRequested)
            {
                using FbController2 fbController = new FbController2();
                if (!cancellationToken.IsCancellationRequested)
                {
                    int tmpcount = 0;
                    for (int i = BelegÜbersichtFilter.MinPhase; i <= BelegÜbersichtFilter.MaxPhase; i++)
                    {
                        filter.Phase = i;
                        object? cObj = await fbController.FetchObjectAsync(filter.ToCountQuery(fbController), cancellationToken);
                        int count = Convert.ToInt32(cObj);
                        tmpcount = count;

                        if (!filter.DoPhaseSearch)
                        {
                            return count;
                        }

                        if (i <= BelegÜbersichtFilter.AbortableUntilPhase && count > 0)
                        {
                            return count;
                        }
                    }
                    return tmpcount;
                }
                else
                {
                    return 0;
                }
            }
            else
            {
                return 0;
            }
        }

        public async IAsyncEnumerable<int> GetAufträgeZuArtikel(string artikelnummer, ZahlungsbedingungCollection zahlungsbedingungen)
        {
            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@BPOS_A_ARTIKELNR", artikelnummer);
            DataTable data = await fbController.SelectDataAsync(@$"SELECT BPOS_N_NR FROM ARTIKEL_BEDARF AB 
LEFT JOIN BELEGE B ON (B.BELE_A_TYP = 'AU' AND B.BELE_N_NR = AB.BPOS_N_NR)
WHERE BPOS_A_ARTIKELNR = @BPOS_A_ARTIKELNR
AND 
(
    (B.BELE_N_ZAHLUNG IN ({String.Join(',', zahlungsbedingungen.GetVorkasseZahlungsbedingungen().Select(x => x.ZABD_N_NR))}) AND (B.BELE_A_GRUPPE = 'ZAHLUNGERHALTEN' OR B.BELE_L_BEZAHLT = 'Y'))
    OR B.BELE_N_ZAHLUNG IN({String.Join(',', zahlungsbedingungen.GetRechnungsUndLastschriftZahlungsbedingungen().Select(x => x.ZABD_N_NR))})
)
AND B.BELE_L_ERLEDIGT = 'N'");

            foreach (DataRow row in data.Rows)
            {
                if (row is not null)
                {
                    yield return row.Field<int>("BPOS_N_NR");
                }
            }
        }

        /// <summary>
        /// Sucht für eine Artikelnummer mit Kunden die letzte Belegposition, die in einer Rechnung vorgekommen ist und gibt diese zurück.
        /// </summary>
        /// <param name="artikelnummer"></param>
        /// <param name="kundennummer"></param>
        /// <returns></returns>
        public async Task<Belegposition?> GetLetztenKundenpreisAsync(string artikelnummer, string kundennummer)
        {
            using FbController2 fbController = new FbController2();
            fbController.AddParameter("@BPOS_A_ARTIKELNR", artikelnummer);
            fbController.AddParameter("@BELE_A_KUNDENNR", kundennummer);
            DataRow? row = await fbController.SelectRowAsync(@"SELECT FIRST 1 BP.* FROM BELEGE B 
LEFT JOIN BELEGPOS BP ON (BP.BPOS_A_TYP = 'RE' AND BP.BPOS_N_NR = B.BELE_N_NR)
WHERE BPOS_A_TYP = 'RE' AND BPOS_A_ARTIKELNR = @BPOS_A_ARTIKELNR
AND B.BELE_A_KUNDENNR = @BELE_A_KUNDENNR
ORDER BY BELE_N_NR DESC");

            return row is null ? null : ObjectErweiterung.DataRowZuObjekt(new Belegposition(), row);
        }

        public async Task SetDruckdatumAsync(BelegTyp belegTyp, int belegnummer, FbController2 fbController)
        {
            fbController.AddParameter("@BELE_A_TYP", EnumHelper.GetBelegTypString(belegTyp));
            fbController.AddParameter("@BELE_N_NR", belegnummer);
            await fbController.QueryAsync("UPDATE BELEGE SET BELE_D_DRUCKDATUM = CURRENT_DATE WHERE BELE_A_TYP = @BELE_A_TYP AND BELE_N_NR = @BELE_N_NR");
        }

        public async IAsyncEnumerable<LastschriftRechnungExport> GetExportLastschriftRechnungenAsync(FbController2 fbController, [EnumeratorCancellation] CancellationToken cancellationToken = default)
        {
            DataTable data = await fbController.SelectDataAsync(@"SELECT 
BELE_N_NR, BELE_N_BRUTTO, BELE_N_NETTO, BELE_D_DATE, BELE_N_ZAHLUNG, KUND_A_NR, 
KUND_A_IBAN, KUND_A_BIC, KUND_A_LAND, KUND_A_NAME1, KUND_WK5_D_AUSSTELLUNG_MANDAT
FROM BELEGE
LEFT JOIN KUNDEN ON KUND_A_NR = BELE_A_KUNDENNR
WHERE BELE_A_TYP = 'RE' 
AND WK5_BELE_L_STARMONEY_EXPORTIERT = 'N'
AND BELE_N_ZAHLUNG IN (SELECT ZABD_N_NR FROM ZAHLUNGSBEDINGUNG WHERE ZABD_L_ABBUCHUNG = 'Y')
AND BELE_L_FIBUUEBERGABE = 'Y'
ORDER BY BELE_N_NR DESC", cancellationToken);

            foreach (DataRow row in data.Rows)
            {
                yield return ObjectErweiterung.DataRowZuObjekt(new LastschriftRechnungExport(), row);
            }
        }
        /// <summary>
        /// Setzt den Status des Lastschrift-Export auf Y für eine bestimmte Rechnung
        /// </summary>
        /// <param name="rechnungsnummer"></param>
        /// <param name="fbController"></param>
        /// <returns></returns>
        public Task SetLastschriftExportedAsync(int rechnungsnummer, FbController2 fbController)
        {
            fbController.AddParameter("@BELE_N_NR", rechnungsnummer);
            return fbController.QueryAsync("UPDATE BELEGE SET WK5_BELE_L_STARMONEY_EXPORTIERT = 'Y' WHERE BELE_A_TYP = 'RE' AND BELE_N_NR = @BELE_N_NR");
        }

        public async IAsyncEnumerable<Lieferschein> GetAlleLieferscheinZuAuftragAsync(int auftragsnummer, FbController2 fbController)
        {
            string sql = "SELECT BELE_N_NR FROM BELEGE WHERE BELE_N_NR_AU = @AUNR AND BELE_A_TYP = 'LS'";
            fbController.AddParameter("@AUNR", auftragsnummer);

            DataTable data = await fbController.SelectDataAsync(sql);


            foreach (DataRow row in data.Rows)
            {
                yield return ObjectErweiterung.DataRowZuObjekt(new Lieferschein(), row);
            }
        }

        public async IAsyncEnumerable<Auftrag> GetLimitPrüfungsAufträge(FbController2 fbController)
        {
            string sql = "SELECT * FROM BELEGE WHERE BELE_A_TYP = 'AU' AND WK5_BELE_L_LIMITPRUEFUNG = 'Y' AND BELE_L_ERLEDIGT = 'N' AND BELE_L_BEZAHLT = 'N' AND BELE_L_STORNO = 'N'";

            DataTable data = await fbController.SelectDataAsync(sql);

            foreach (DataRow row in data.Rows)
            {
                yield return ObjectErweiterung.DataRowZuObjekt(new Auftrag(), row);
            }
        }

        public async Task<decimal> GetUnverbrauchtenKreditlimitWert(string KUND_A_NR, FbController2 fbController)
        {
            string sql = @"SELECT COALESCE(KUND_N_KREDITLIMIT,0)-(
SELECT COALESCE(SUM(COALESCE(BELE_N_NETTO,0)),0) FROM BELEGE WHERE BELE_A_KUNDENNR = KUND_A_NR AND BELE_A_TYP = 'AU' 
AND  BELE_L_ERLEDIGT = 'N'
AND WK5_BELE_L_LIMITPRUEFUNG = 'N'
) FROM KUNDEN WHERE KUND_A_NR = @KUND_A_NR";
            fbController.AddParameter("@KUND_A_NR", KUND_A_NR);
            object? value = await fbController.FetchObjectAsync(sql);
            return value is null ? 0.0m : Convert.ToDecimal(value);
        }

        public async Task<int?> GetGemeinsamerHauptlieferant(Beleg beleg, FbController2 fbController)
        {
            List<int> alleLieferanten = new List<int>();


            Dictionary<int, int> hauptLieferanten = new Dictionary<int, int>();

            IEnumerable<Belegposition>? positionen = beleg.GetEndPositionen().Where(x => x.ARTI_L_LAGERFUEHR);

            foreach (Belegposition pos in positionen)
            {
                await foreach (Lieferantenartikel lfa in Lieferantenartikel.GetLieferantenartikelAsync(pos.Artikelnummer, fbController))
                {
                    if (lfa is null)
                    {
                        return null;
                    }
                    else
                    {
                        if (!alleLieferanten.Contains(lfa.ARLI_N_LIEFNR))
                        {
                            alleLieferanten.Add(lfa.ARLI_N_LIEFNR);
                        }

                        if (lfa.ARLI_L_HAUPTLIEFERANT)
                        {
                            if (hauptLieferanten.ContainsKey(lfa.ARLI_N_LIEFNR))
                            {
                                hauptLieferanten[lfa.ARLI_N_LIEFNR] += 1;
                            }
                            else
                            {
                                hauptLieferanten.Add(lfa.ARLI_N_LIEFNR, 1);
                            }
                        }

                    }
                }
            }

            if (hauptLieferanten.Count > 0)
            {
                foreach (KeyValuePair<int, int> kvp in hauptLieferanten)
                {
                    if (kvp.Value == positionen.Count())
                    {
                        return kvp.Key;
                    }
                }
            }

            if (alleLieferanten.Count == 1)
            {
                return alleLieferanten.First();
            }

            return null;
        }

        public Task<List<int>> GetOffeneAuftragsnummernFürPosition(Belegposition pos, string kundennummer, FbController2 fbController)
        {
            if (pos.BPOS_A_TYP is not "AU")
            {
                return Task.FromResult(new List<int>());
            }

            return fbController.SelectDataAsync<int>(@"SELECT BPOS_N_NR FROM 
BELEGE B
LEFT JOIN BELEGPOS BP ON (BP.BPOS_A_TYP = 'AU' AND BPOS_N_NR = BELE_N_NR)
WHERE BELE_A_TYP = 'AU'
AND BELE_L_ERLEDIGT = 'N'
AND BELE_L_STORNO = 'N'
AND BPOS_N_MENGE > BPOS_N_MENGEGELIEF 
AND BPOS_N_NR != @BPOS_N_NR
AND BELE_A_KUNDENNR = @BELE_A_KUNDENNR
AND BPOS_A_ARTIKELNR = @BPOS_A_ARTIKELNR", new { BPOS_N_NR = pos.BPOS_N_NR, BELE_A_KUNDENNR = kundennummer, BPOS_A_ARTIKELNR = pos.Artikelnummer });
        }

        public async Task UpdateBelegVersandEK(BelegTyp typ, int belegnummer, decimal versandEK, FbController2 fbController)
        {
            string sql = "UPDATE BELEGE SET BELE_N_FRACHTEK = @BELE_N_FRACHTEK WHERE BELE_A_TYP = @BELE_A_TYP AND BELE_N_NR = @BELE_N_NR";

            fbController.AddParameter("@BELE_N_FRACHTEK", versandEK);
            fbController.AddParameter("@BELE_A_TYP", EnumHelper.GetBelegTypString(typ));
            fbController.AddParameter("@BELE_N_NR", belegnummer);

            await fbController.QueryAsync(sql);
        }
    }
}
