public bool IsReadableByThisReaderVersion(Stream stream, ZugferdDialect zugferdDialect)
        {
            if (!stream.CanRead)
            {
                throw new IllegalStreamException("Cannot read from stream");
            }

            XmlDocument doc = new XmlDocument();

            doc.Load(stream);

            if (doc.DocumentElement?.OwnerDocument?.NameTable == null)
            {
                return(false);
            }
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.DocumentElement.OwnerDocument.NameTable);

            foreach ((string item1, string item2) in zugferdDialect.GetNamespaces())
            {
                nsmgr.AddNamespace(item1, item2);
            }

            string profile = NodeAsString(doc.DocumentElement, "//ram:GuidelineSpecifiedDocumentContextParameter/ram:ID", nsmgr);

            if (profile.StartsWith(zugferdDialect.DocumentId))
            {
                return(true);
            }
            return(false);
        }
        public InvoiceDescriptor Load(Stream stream, ZugferdDialect zugferdDialect)
        {
            if (!stream.CanRead)
            {
                throw new IllegalStreamException("Cannot read from stream");
            }

            XmlDocument doc = new XmlDocument();

            doc.Load(stream);

            if (doc.DocumentElement?.OwnerDocument?.NameTable == null)
            {
                return(null);
            }
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.DocumentElement.OwnerDocument.NameTable);

            foreach ((string item1, string item2) in zugferdDialect.GetNamespaces())
            {
                nsmgr.AddNamespace(item1, item2);
            }

            InvoiceDescriptor retval;

            retval             = new InvoiceDescriptor();
            retval.IsTest      = NodeAsBool(doc.DocumentElement, $"//rsm:{zugferdDialect.ExchangedDocument}/ram:TestIndicator", nsmgr);
            retval.Profile     = default(Profile).FromString(NodeAsString(doc.DocumentElement, "//ram:GuidelineSpecifiedDocumentContextParameter/ram:ID", nsmgr));
            retval.Type        = default(InvoiceType).FromString(NodeAsString(doc.DocumentElement, $"//rsm:{zugferdDialect.ExchangedDocument}/ram:TypeCode", nsmgr));
            retval.InvoiceNo   = NodeAsString(doc.DocumentElement, $"//rsm:{zugferdDialect.ExchangedDocument}/ram:ID", nsmgr);
            retval.InvoiceDate = NodeAsDateTime(doc.DocumentElement, $"//rsm:{zugferdDialect.ExchangedDocument}/ram:IssueDateTime/udt:DateTimeString", nsmgr);

            foreach (XmlNode node in doc.SelectNodes($"//rsm:{zugferdDialect.ExchangedDocument}/ram:IncludedNote", nsmgr))
            {
                string       content         = NodeAsString(node, ".//ram:Content", nsmgr);
                string       subjectCode     = NodeAsString(node, ".//ram:SubjectCode", nsmgr);
                SubjectCodes subjectCodeEnum = default(SubjectCodes).FromString(subjectCode);
                retval.AddNote(content, subjectCodeEnum);
            }

            retval.ReferenceOrderNo = NodeAsString(doc, $"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:BuyerReference", nsmgr);

            retval.Seller = NodeAsParty(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:SellerTradeParty", nsmgr);
            foreach (XmlNode node in doc.SelectNodes($"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:SellerTradeParty/ram:SpecifiedTaxRegistration", nsmgr))
            {
                string id       = NodeAsString(node, ".//ram:ID", nsmgr);
                string schemeID = NodeAsString(node, ".//ram:ID/@schemeID", nsmgr);

                retval.AddSellerTaxRegistration(id, default(TaxRegistrationSchemeID).FromString(schemeID));
            }

            if (doc.SelectSingleNode("//ram:SellerTradeParty/ram:DefinedTradeContact", nsmgr) != null)
            {
                retval.SellerContact = new Contact
                {
                    Name         = NodeAsString(doc.DocumentElement, "//ram:SellerTradeParty/ram:DefinedTradeContact/ram:PersonName", nsmgr),
                    OrgUnit      = NodeAsString(doc.DocumentElement, "//ram:SellerTradeParty/ram:DefinedTradeContact/ram:DepartmentName", nsmgr),
                    PhoneNo      = NodeAsString(doc.DocumentElement, "//ram:SellerTradeParty/ram:DefinedTradeContact/ram:TelephoneUniversalCommunication/ram:CompleteNumber", nsmgr),
                    FaxNo        = NodeAsString(doc.DocumentElement, "//ram:SellerTradeParty/ram:DefinedTradeContact/ram:FaxUniversalCommunication/ram:CompleteNumber", nsmgr),
                    EmailAddress = NodeAsString(doc.DocumentElement, "//ram:SellerTradeParty/ram:DefinedTradeContact/ram:EmailURIUniversalCommunication/ram:CompleteNumber", nsmgr)
                };
            }

            retval.Buyer = NodeAsParty(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:BuyerTradeParty", nsmgr);
            foreach (XmlNode node in doc.SelectNodes($"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:BuyerTradeParty/ram:SpecifiedTaxRegistration", nsmgr))
            {
                string id       = NodeAsString(node, ".//ram:ID", nsmgr);
                string schemeID = NodeAsString(node, ".//ram:ID/@schemeID", nsmgr);

                retval.AddBuyerTaxRegistration(id, default(TaxRegistrationSchemeID).FromString(schemeID));
            }

            if (doc.SelectSingleNode("//ram:BuyerTradeParty/ram:DefinedTradeContact", nsmgr) != null)
            {
                retval.BuyerContact = new Contact
                {
                    Name         = NodeAsString(doc.DocumentElement, "//ram:BuyerTradeParty/ram:DefinedTradeContact/ram:PersonName", nsmgr),
                    OrgUnit      = NodeAsString(doc.DocumentElement, "//ram:BuyerTradeParty/DefinedTradeContact/ram:DepartmentName", nsmgr),
                    PhoneNo      = NodeAsString(doc.DocumentElement, "//ram:BuyerTradeParty/ram:DefinedTradeContact/ram:TelephoneUniversalCommunication/ram:CompleteNumber", nsmgr),
                    FaxNo        = NodeAsString(doc.DocumentElement, "//ram:BuyerTradeParty/ram:DefinedTradeContact/ram:FaxUniversalCommunication/ram:CompleteNumber", nsmgr),
                    EmailAddress = NodeAsString(doc.DocumentElement, "//ram:BuyerTradeParty/ram:DefinedTradeContact/ram:EmailURIUniversalCommunication/ram:CompleteNumber", nsmgr)
                };
            }

            retval.ShipTo             = NodeAsParty(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeDelivery}/ram:ShipToTradeParty", nsmgr);
            retval.ShipFrom           = NodeAsParty(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeDelivery}/ram:ShipFromTradeParty", nsmgr);
            retval.ActualDeliveryDate = NodeAsDateTime(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeDelivery}/ram:ActualDeliverySupplyChainEvent/ram:OccurrenceDateTime/udt:DateTimeString", nsmgr);

            string   deliveryNoteNo   = NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeDelivery}/ram:DeliveryNoteReferencedDocument/ram:ID", nsmgr);
            DateTime?deliveryNoteDate = NodeAsDateTime(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeDelivery}/ram:DeliveryNoteReferencedDocument/ram:IssueDateTime/udt:DateTimeString", nsmgr) ?? NodeAsDateTime(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeDelivery}/ram:DeliveryNoteReferencedDocument/ram:IssueDateTime", nsmgr);

            if (deliveryNoteDate.HasValue || !string.IsNullOrEmpty(deliveryNoteNo))
            {
                retval.DeliveryNoteReferencedDocument = new DeliveryNoteReferencedDocument
                {
                    ID            = deliveryNoteNo,
                    IssueDateTime = deliveryNoteDate
                };
            }

            retval.Invoicee = NodeAsParty(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:InvoiceeTradeParty", nsmgr);
            retval.Payee    = NodeAsParty(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:PayeeTradeParty", nsmgr);

            retval.InvoiceNoAsReference = NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:PaymentReference", nsmgr);
            retval.Currency             = default(CurrencyCodes).FromString(NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:InvoiceCurrencyCode", nsmgr));

            // TODO: Multiple SpecifiedTradeSettlementPaymentMeans can exist for each account/institution (with different SEPA?)
            PaymentMeans tempPaymentMeans = new PaymentMeans
            {
                TypeCode               = default(PaymentMeansTypeCodes).FromString(NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:TypeCode", nsmgr)),
                Information            = NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:Information", nsmgr),
                SEPACreditorIdentifier = NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:ID", nsmgr),
                SEPAMandateReference   = NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:ID/@schemeAgencyID", nsmgr)
            };

            retval.PaymentMeans = tempPaymentMeans;

            XmlNodeList creditorFinancialAccountNodes = doc.SelectNodes($"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeePartyCreditorFinancialAccount", nsmgr);
            XmlNodeList creditorFinancialInstitutions = doc.SelectNodes($"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayeeSpecifiedCreditorFinancialInstitution", nsmgr);

            if (creditorFinancialAccountNodes.Count == creditorFinancialInstitutions.Count)
            {
                for (int i = 0; i < creditorFinancialAccountNodes.Count; i++)
                {
                    BankAccount account = new BankAccount
                    {
                        ID           = NodeAsString(creditorFinancialAccountNodes[0], ".//ram:ProprietaryID", nsmgr),
                        IBAN         = NodeAsString(creditorFinancialAccountNodes[0], ".//ram:IBANID", nsmgr),
                        BIC          = NodeAsString(creditorFinancialInstitutions[0], ".//ram:BICID", nsmgr),
                        Bankleitzahl = NodeAsString(creditorFinancialInstitutions[0], ".//ram:GermanBankleitzahlID", nsmgr),
                        BankName     = NodeAsString(creditorFinancialInstitutions[0], ".//ram:Name", nsmgr),
                        Name         = NodeAsString(creditorFinancialInstitutions[0], ".//ram:AccountName", nsmgr)
                    };

                    retval.CreditorBankAccounts.Add(account);
                }
            }

            XmlNodeList debitorFinancialAccountNodes = doc.SelectNodes($"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayerPartyDebtorFinancialAccount", nsmgr);
            XmlNodeList debitorFinancialInstitutions = doc.SelectNodes($"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:SpecifiedTradeSettlementPaymentMeans/ram:PayerSpecifiedDebtorFinancialInstitution", nsmgr);

            if (debitorFinancialAccountNodes.Count == debitorFinancialInstitutions.Count)
            {
                for (int i = 0; i < debitorFinancialAccountNodes.Count; i++)
                {
                    BankAccount account = new BankAccount
                    {
                        ID           = NodeAsString(debitorFinancialAccountNodes[0], ".//ram:ProprietaryID", nsmgr),
                        IBAN         = NodeAsString(debitorFinancialAccountNodes[0], ".//ram:IBANID", nsmgr),
                        BIC          = NodeAsString(debitorFinancialInstitutions[0], ".//ram:BICID", nsmgr),
                        Bankleitzahl = NodeAsString(debitorFinancialInstitutions[0], ".//ram:GermanBankleitzahlID", nsmgr),
                        BankName     = NodeAsString(debitorFinancialInstitutions[0], ".//ram:Name", nsmgr)
                    };

                    retval.DebitorBankAccounts.Add(account);
                } // !for(i)
            }

            foreach (XmlNode node in doc.SelectNodes($"//ram:{zugferdDialect.ApplicableTradeSettlement}/ram:ApplicableTradeTax", nsmgr))
            {
                retval.AddApplicableTradeTax(NodeAsDecimal(node, ".//ram:BasisAmount", nsmgr, 0).Value,
                                             NodeAsDecimal(node, $".//ram:{zugferdDialect.ApplicablePercent}", nsmgr, 0).Value,
                                             default(TaxTypes).FromString(NodeAsString(node, ".//ram:TypeCode", nsmgr)),
                                             default(TaxCategoryCodes).FromString(NodeAsString(node, ".//ram:CategoryCode", nsmgr)));
            }

            foreach (XmlNode node in doc.SelectNodes("//ram:SpecifiedTradeAllowanceCharge", nsmgr))
            {
                retval.AddTradeAllowanceCharge(!NodeAsBool(node, ".//ram:ChargeIndicator", nsmgr), // wichtig: das not (!) beachten
                                               NodeAsDecimal(node, ".//ram:BasisAmount", nsmgr, 0).Value,
                                               retval.Currency,
                                               NodeAsDecimal(node, ".//ram:ActualAmount", nsmgr, 0).Value,
                                               NodeAsString(node, ".//ram:Reason", nsmgr),
                                               default(TaxTypes).FromString(NodeAsString(node, ".//ram:CategoryTradeTax/ram:TypeCode", nsmgr)),
                                               default(TaxCategoryCodes).FromString(NodeAsString(node, ".//ram:CategoryTradeTax/ram:CategoryCode", nsmgr)),
                                               NodeAsDecimal(node, $".//ram:CategoryTradeTax/ram:{zugferdDialect.ApplicablePercent}", nsmgr, 0).Value);
            }

            foreach (XmlNode node in doc.SelectNodes("//ram:SpecifiedLogisticsServiceCharge", nsmgr))
            {
                retval.AddLogisticsServiceCharge(NodeAsDecimal(node, ".//ram:AppliedAmount", nsmgr, 0).Value,
                                                 NodeAsString(node, ".//ram:Description", nsmgr),
                                                 default(TaxTypes).FromString(NodeAsString(node, ".//ram:AppliedTradeTax/ram:TypeCode", nsmgr)),
                                                 default(TaxCategoryCodes).FromString(NodeAsString(node, ".//ram:AppliedTradeTax/ram:CategoryCode", nsmgr)),
                                                 NodeAsDecimal(node, $".//ram:AppliedTradeTax/ram:{zugferdDialect.ApplicablePercent}", nsmgr, 0).Value);
            }

            retval.PaymentTerms = new PaymentTerms
            {
                Description = NodeAsString(doc.DocumentElement, "//ram:SpecifiedTradePaymentTerms/ram:Description", nsmgr),
                DueDate     = NodeAsDateTime(doc.DocumentElement, "//ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime", nsmgr)
            };

            retval.LineTotalAmount      = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:LineTotalAmount", nsmgr, 0).Value;
            retval.ChargeTotalAmount    = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:ChargeTotalAmount", nsmgr, 0).Value;
            retval.AllowanceTotalAmount = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:AllowanceTotalAmount", nsmgr, 0).Value;
            retval.TaxBasisAmount       = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:TaxBasisTotalAmount", nsmgr, 0).Value;
            retval.TaxTotalAmount       = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:TaxTotalAmount", nsmgr, 0).Value;
            retval.GrandTotalAmount     = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:GrandTotalAmount", nsmgr, 0).Value;
            retval.TotalPrepaidAmount   = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:TotalPrepaidAmount", nsmgr, 0).Value;
            retval.DuePayableAmount     = NodeAsDecimal(doc.DocumentElement, $"//ram:{zugferdDialect.SpecifiedTradeSettlementMonetarySummation}/ram:DuePayableAmount", nsmgr, 0).Value;

            retval.OrderDate = NodeAsDateTime(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:BuyerOrderReferencedDocument/ram:IssueDateTime/udt:DateTimeString", nsmgr) ?? NodeAsDateTime(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:BuyerOrderReferencedDocument/ram:IssueDateTime", nsmgr);
            retval.OrderNo   = NodeAsString(doc.DocumentElement, $"//ram:{zugferdDialect.ApplicableTradeAgreement}/ram:BuyerOrderReferencedDocument/ram:ID", nsmgr);

            foreach (XmlNode node in doc.SelectNodes("//ram:IncludedSupplyChainTradeLineItem", nsmgr))
            {
                retval.TradeLineItems.Add(ParseTradeLineItem(node, nsmgr, zugferdDialect));
            }
            return(retval);
        }