private void SerializeTransactionBody( XmlWriter xmlWriter, Transaction trans )
        {
            xmlWriter.WriteElementString( "Date", trans.Date.ToShortDateString() );
            xmlWriter.WriteWhitespace( Environment.NewLine );
            xmlWriter.WriteElementString( "Amount", trans.Amount.ToString() );
            xmlWriter.WriteWhitespace( Environment.NewLine );
            if( trans.Description != null && trans.Description.Length > 0 )
            {
                xmlWriter.WriteElementString( "Description", trans.Description );
                xmlWriter.WriteWhitespace( Environment.NewLine );
            }
            /*
            // TODO: Remove Transaction.Number
            if( trans.HasNumber )
            {
                xmlWriter.WriteElementString( "Number", trans.Number );
                xmlWriter.WriteWhitespace( Environment.NewLine );
            }
            */
            if( trans.Memo != null && trans.Memo.Length > 0 )
            {
                xmlWriter.WriteElementString( "Memo", trans.Memo );
                xmlWriter.WriteWhitespace( Environment.NewLine );
            }

            bool canUseShortcut = false;

            LineItem[] credits = trans.GetCreditLineItems();
            LineItem[] debits = trans.GetDebitLineItems();

            /*
            if( credits.Length != 1 || debits.Length != 1 )
                canUseShortcut = false;
            else
            {
                if( credits[0].IsReconciled )
                    canUseShortcut = false;
                if( debits[0].IsReconciled )
                    canUseShortcut = false;
                if( debits[0].Account.AccountType != AccountType.Expense )
                    canUseShortcut = false;
            }
            */

            if( canUseShortcut )
            {
                xmlWriter.WriteElementString( "AccountId", credits[0].Account.Id.ToString() );
                xmlWriter.WriteWhitespace( Environment.NewLine );
                if( debits[0].CategoryObject != null )
                {
                    xmlWriter.WriteElementString( "Category", debits[0].CategoryObject.Name );
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                }
            }
            else
            {
                xmlWriter.WriteStartElement( "LineItems" );
                xmlWriter.WriteWhitespace( Environment.NewLine );
                foreach( LineItem lineItem in trans.LineItems )
                {
                    // <Credit> or <Debit>
                    xmlWriter.WriteStartElement( lineItem.TransactionType.ToString() );
                    // Line items now have unique identifiers, too.
                    if( lineItem.Id > 0 )
                        xmlWriter.WriteAttributeString( "Id", lineItem.Id.ToString() );
                    xmlWriter.WriteAttributeString( "AccountId", lineItem.Account.Id.ToString() );
                    // Number is new in 1.2 format
                    if( lineItem.Number != null && lineItem.Number.Length > 0 )
                        xmlWriter.WriteAttributeString( "Number", lineItem.Number );
                    // Memo became an attribute in 1.2 format
                    if( lineItem.CategoryObject != null )
                        xmlWriter.WriteAttributeString( "Category", lineItem.CategoryObject.Name );
                    if( lineItem.Memo != null && lineItem.Memo != trans.Memo )
                        xmlWriter.WriteAttributeString( "Memo", lineItem.Memo );
                    if( lineItem.Amount != trans.Amount )
                        xmlWriter.WriteAttributeString( "Amount", lineItem.Amount.ToString() );
                    if( lineItem.IsReconciled )
                        xmlWriter.WriteAttributeString( "Reconciled", "true" );

                    // New: Write the associated online transaction for this line item.
                    if( lineItem.OnlineTransaction != null )
                    {
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                        OnlineTransactionStorageXml ostorage = new OnlineTransactionStorageXml();
                        ostorage.SerializeTransaction( xmlWriter, lineItem.OnlineTransaction );
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                    }

                    xmlWriter.WriteEndElement(); // </Credit> or </Debit>
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                }
                xmlWriter.WriteEndElement(); // </LineItems>
                xmlWriter.WriteWhitespace( Environment.NewLine );
            }
        }
        public void SaveLedger( Ledger ledger )
        {
            ledger.ValidateReferentialIntegrity();

            // Ensure that identifiers match appropriately before saving.
            ValidateIdentifierLinks( ledger );

            /*
            // TODO: Remove this after testing
            ledger.RecurringTransactions.Clear();
            RecurringTransaction r = new RecurringTransaction();
            r.StartingDate = new DateTime( 2007, 10, 15 );
            r.EndingDate = DateTime.MaxValue;
            r.Date = new DateTime( 2007, 10, 15 );
            r.Amount = 129.69M;
            r.Description = "Ameriprise Financial";
            r.Memo = "Life insurance payment";
            r.LineItems.Add( new LineItemCredit( ledger.GetAccount( "Suntrust" ), r.Amount ) );
            r.LineItems.Add( new LineItemDebit( ledger.GetAccount( "Expenses" ), r.Amount ) );
            r.AddMonths = 1;
            ledger.RecurringTransactions.Add( r );
            */

            StreamWriter writer = File.CreateText( filename );
            try
            {
                XmlTextWriter xmlWriter = new XmlTextWriter( writer );
                try
                {
                    xmlWriter.WriteStartDocument();
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    xmlWriter.WriteStartElement( "Ledger" );
                    xmlWriter.WriteAttributeString( "xmlns", "http://www.uvteksoftware.com/xsd/UvMoneyLedger.xsd" );
                    xmlWriter.WriteAttributeString( "Version", "1.2" );
                    xmlWriter.WriteWhitespace( Environment.NewLine );

                    xmlWriter.WriteElementString( "MergeDate", ledger.MergeDate.ToString() );
                    xmlWriter.WriteWhitespace( Environment.NewLine );

                    // Write accounts
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    xmlWriter.WriteStartElement( "Accounts" );
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    foreach( Account account in ledger.Accounts )
                    {
                        SerializeAccount( xmlWriter, account );
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                    }
                    xmlWriter.WriteEndElement(); // </Accounts>
                    xmlWriter.WriteWhitespace( Environment.NewLine );

                    // Write recurring transactions
                    if( ledger.RecurringTransactions.Count > 0 )
                    {
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                        xmlWriter.WriteStartElement( "RecurringTransactions" );
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                        foreach( RecurringTransaction retrans in ledger.RecurringTransactions )
                        {
                            SerializeRecurringTransaction( xmlWriter, retrans );
                            xmlWriter.WriteWhitespace( Environment.NewLine );
                        }
                        xmlWriter.WriteEndElement(); // </RecurringTransactions>
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                    }

                    // Write transactions
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    xmlWriter.WriteStartElement( "Transactions" );
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    foreach( Transaction trans in ledger.Transactions )
                    {
                        // Do not serialize auto-generated transactions
                        if( !( trans is MissingTransaction ) )
                        {
                            trans.Validate();
                            SerializeTransaction( xmlWriter, trans );
                            xmlWriter.WriteWhitespace( Environment.NewLine );
                        }
                    }

                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    xmlWriter.WriteComment( "Insert new transactions here." );
                    xmlWriter.WriteWhitespace( Environment.NewLine );

                    // Write out a transaction template
                    StringBuilder sb = new StringBuilder();
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Transaction>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Date></Date>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Number></Number>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Amount>0.00</Amount>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Description>Payee</Description>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Memo></Memo>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<LineItems>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Credit Account=\"\" />" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "<Debit Account=\"Expenses\" Category=\"\" />" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "</LineItems>" );
                    sb.Append( Environment.NewLine );
                    sb.Append( "</Transaction>" );
                    sb.Append( Environment.NewLine );
                    xmlWriter.WriteComment( sb.ToString() );
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    xmlWriter.WriteWhitespace( Environment.NewLine );

                    xmlWriter.WriteEndElement(); // </Transactions>
                    xmlWriter.WriteWhitespace( Environment.NewLine );

                    OnlineTransactionStorageXml ostorage = new OnlineTransactionStorageXml();

                    // Write online transactions that are missing from the ledger
                    if( ledger.MissingTransactions.Count > 0 )
                    {
                        xmlWriter.WriteStartElement( "MissingOnlineTransactions" );
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                        foreach( OnlineTransaction otrans in ledger.MissingTransactions )
                        {
                            ostorage.SerializeTransaction( xmlWriter, otrans );
                            xmlWriter.WriteWhitespace( Environment.NewLine );
                        }
                        xmlWriter.WriteEndElement(); // </MissingOnlineTransactions>
                        xmlWriter.WriteWhitespace( Environment.NewLine );
                    }

                    xmlWriter.WriteEndElement(); // </Ledger>
                    xmlWriter.WriteWhitespace( Environment.NewLine );
                    xmlWriter.WriteEndDocument();
                }
                finally
                {
                    xmlWriter.Close();
                }
            }
            finally
            {
                writer.Close();
            }
        }