コード例 #1
0
        public DataSet GetContributionPersonGroupAddress([FromBody] Rock.Net.RestParameters.ContributionStatementOptions options)
        {
            var user = CurrentUser();

            if (user == null)
            {
                // unable to determine user
                throw new HttpResponseException(HttpStatusCode.Unauthorized);
            }

            if (!new FinancialTransaction().IsAuthorized("View", user.Person))
            {
                // user can't view FinancialTransactions
                throw new HttpResponseException(HttpStatusCode.Unauthorized);
            }

            Service service = new Service();
            Dictionary <string, object> parameters = new Dictionary <string, object>();

            parameters.Add("startDate", options.StartDate);
            if (options.EndDate.HasValue)
            {
                parameters.Add("endDate", options.EndDate.Value);
            }
            else
            {
                parameters.Add("endDate", DateTime.MaxValue);
            }

            if (options.AccountIds != null)
            {
                parameters.Add("accountIds", options.AccountIds.AsDelimited(","));
            }
            else
            {
                parameters.Add("accountIds", DBNull.Value);
            }

            if (options.PersonId.HasValue)
            {
                parameters.Add("personId", options.PersonId);
            }
            else
            {
                parameters.Add("personId", DBNull.Value);
            }

            parameters.Add("orderByZipCode", options.OrderByZipCode);
            var result = service.GetDataSet("spContributionStatementQuery", System.Data.CommandType.StoredProcedure, parameters);

            if (result.Tables.Count > 0)
            {
                result.Tables[0].TableName = "contribution_person_group_address";
            }

            return(result);
        }
        public DataSet GetContributionPersonGroupAddress([FromBody] Rock.Net.RestParameters.ContributionStatementOptions options)
        {
            Dictionary <string, object> parameters = new Dictionary <string, object>();

            parameters.Add("startDate", options.StartDate);
            if (options.EndDate.HasValue)
            {
                parameters.Add("endDate", options.EndDate.Value);
            }
            else
            {
                parameters.Add("endDate", DateTime.MaxValue);
            }

            if (options.AccountIds != null)
            {
                parameters.Add("accountIds", options.AccountIds.AsDelimited(","));
            }
            else
            {
                parameters.Add("accountIds", DBNull.Value);
            }

            if (options.PersonId.HasValue)
            {
                parameters.Add("personId", options.PersonId);
            }
            else
            {
                parameters.Add("personId", DBNull.Value);
            }

            if (options.IncludeIndividualsWithNoAddress)
            {
                parameters.Add("includeIndividualsWithNoAddress", options.IncludeIndividualsWithNoAddress);
            }
            else
            {
                parameters.Add("includeIndividualsWithNoAddress", false);
            }

            parameters.Add("orderByPostalCode", options.OrderByPostalCode);
            var result = DbService.GetDataSet("spFinance_ContributionStatementQuery", System.Data.CommandType.StoredProcedure, parameters);

            if (result.Tables.Count > 0)
            {
                result.Tables[0].TableName = "contribution_person_group_address";
            }

            return(result);
        }
コード例 #3
0
        /// <summary>
        /// Creates the document.
        /// </summary>
        /// <param name="financialTransactionQry">The financial transaction qry.</param>
        /// <returns></returns>
        public Document RunReport()
        {
            UpdateProgress( "Connecting..." );

            // Login and setup options for REST calls
            RockConfig rockConfig = RockConfig.Load();

            _rockRestClient = new RockRestClient( rockConfig.RockBaseUrl );
            _rockRestClient.Login( rockConfig.Username, rockConfig.Password );

            // shouldn't happen, but just in case the StartDate isn't set, set it to the first day of the current year
            DateTime firstDayOfYear = new DateTime( DateTime.Now.Year, 1, 1 );

            // note: if a specific person is specified, get them even if they don't have an address. 
            _contributionStatementOptionsREST = new Rock.Net.RestParameters.ContributionStatementOptions
            {
                StartDate = Options.StartDate ?? firstDayOfYear,
                EndDate = Options.EndDate,
                AccountIds = Options.AccountIds,
                IncludeIndividualsWithNoAddress = Options.PersonId.HasValue || Options.IncludeIndividualsWithNoAddress,
                PersonId = Options.PersonId,
                OrderByPostalCode = true
            };

            var organizationAddressAttribute = _rockRestClient.GetData<List<Rock.Client.Attribute>>( "api/attributes", "Key eq 'OrganizationAddress'" ).FirstOrDefault();
            if ( organizationAddressAttribute != null )
            {
                var organizationAddressAttributeValue = _rockRestClient.GetData<List<Rock.Client.AttributeValue>>( "api/AttributeValues", string.Format( "AttributeId eq {0}", organizationAddressAttribute.Id ) ).FirstOrDefault();

                Guid locationGuid = Guid.Empty;
                if ( Guid.TryParse( organizationAddressAttributeValue.Value, out locationGuid ) )
                {
                    _organizationAddressLocation = _rockRestClient.GetData<List<Rock.Client.Location>>( "api/locations", string.Format( "Guid eq guid'{0}'", locationGuid ) ).FirstOrDefault();
                }
            }

            // If we don't have a _organizationAddressLocation, just create an empty location
            _organizationAddressLocation = _organizationAddressLocation ?? new Rock.Client.Location();

            // setup report layout and events
            DocumentLayout report = new DocumentLayout( this.Options.LayoutFile );

            //// if there is an imgLogo and the path is "logo.jpg", use the logo specified in rockconfig.  
            //// We have to read the layout as Xml first to figure out what the Path of the imgLogo
            XmlDocument layoutXmlDoc = new XmlDocument();
            layoutXmlDoc.Load( this.Options.LayoutFile );
            var imageNodes = layoutXmlDoc.GetElementsByTagName( "image" );
            foreach ( var imageNode in imageNodes.OfType<XmlNode>() )
            {
                string imagePath = imageNode.Attributes["path"].Value;
                string imageId = imageNode.Attributes["id"].Value;
                if ( imageId.Equals( "imgLogo" ) && imagePath.Equals( RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase ) )
                {
                    Image imgLogo = report.GetReportElementById( "imgLogo" ) as Image;
                    if ( imgLogo != null )
                    {
                        try
                        {
                            if ( !rockConfig.LogoFile.Equals( RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase ) )
                            {
                                imgLogo.ImageData = ceTe.DynamicPDF.Imaging.ImageData.GetImage( rockConfig.LogoFile );
                            }
                        }
                        catch ( Exception ex )
                        {
                            throw new Exception( "Error loading Logo Image: " + rockConfig.LogoFile + "\n\n" + ex.Message );
                        }
                    }
                }
            }

            Query query = report.GetQueryById( "OuterQuery" );
            if ( query == null )
            {
                throw new MissingReportElementException( "Report requires a QueryElement named 'OuterQuery'" );
            }

            query.OpeningRecordSet += mainQuery_OpeningRecordSet;

            Query orgInfoQuery = report.GetQueryById( "OrgInfoQuery" );
            if ( orgInfoQuery == null )
            {
                throw new MissingReportElementException( "Report requires a QueryElement named 'OrgInfoQuery'" );
            }

            orgInfoQuery.OpeningRecordSet += orgInfoQuery_OpeningRecordSet;

            _accountSummaryQuery = report.GetQueryById( "AccountSummaryQuery" );

            if ( _accountSummaryQuery == null )
            {
                // not required.  Just don't do anything if it isn't there
            }
            else
            {
                _accountSummaryQuery.OpeningRecordSet += delegate( object s, OpeningRecordSetEventArgs ee )
                {
                    // create a recordset for the _accountSummaryQuery which is the GroupBy summary of AccountName, Amount
                    /*
                     The structure of _transactionsDataTable is
                     
                     DateTime TransactionDateTime
                     string CurrencyTypeValueName
                     string Summary (main transaction summary)
                     DataTable Details {
                          int AccountId
                          string AccountName
                          string Summary (detail summary)
                          decimal Amount
                     }
                     */

                    var detailsData = new DataTable();
                    detailsData.Columns.Add( "AccountId", typeof( int ) );
                    detailsData.Columns.Add( "AccountName" );
                    detailsData.Columns.Add( "Amount", typeof( decimal ) );

                    foreach ( var details in _transactionsDataTable.AsEnumerable().Select( a => ( a["Details"] as DataTable ) ) )
                    {
                        foreach ( var row in details.AsEnumerable() )
                        {
                            detailsData.Rows.Add( row["AccountId"], row["AccountName"], row["Amount"] );
                        }
                    }

                    var summaryTable = detailsData.AsEnumerable().GroupBy( g => g["AccountId"] ).Select( a => new
                    {
                        AccountName = a.Max( x => x["AccountName"].ToString() ),
                        Amount = a.Sum( x => decimal.Parse( x["Amount"].ToString() ) )
                    } ).OrderBy( o => o.AccountName );

                    ee.RecordSet = new EnumerableRecordSet( summaryTable );
                };
            }

            UpdateProgress( "Getting Data..." );

            // get outer query data from Rock database via REST now vs in mainQuery_OpeningRecordSet to make sure we have data
            DataSet personGroupAddressDataSet = _rockRestClient.PostDataWithResult<Rock.Net.RestParameters.ContributionStatementOptions, DataSet>( "api/FinancialTransactions/GetContributionPersonGroupAddress", _contributionStatementOptionsREST );
            _personGroupAddressDataTable = personGroupAddressDataSet.Tables[0];
            RecordCount = _personGroupAddressDataTable.Rows.Count;

            if ( RecordCount > 0 )
            {
                Document doc = report.Run();
                return doc;
            }
            else
            {
                return null;
            }
        }
コード例 #4
0
        public DataSet GetContributionTransactions(int groupId, int?personId, [FromBody] Rock.Net.RestParameters.ContributionStatementOptions options)
        {
            var user = CurrentUser();

            if (user == null)
            {
                // unable to determine user
                throw new HttpResponseException(HttpStatusCode.Unauthorized);
            }

            if (!new FinancialTransaction().IsAuthorized("View", user.Person))
            {
                // user can't view FinancialTransactions
                throw new HttpResponseException(HttpStatusCode.Unauthorized);
            }

            var qry = Get()
                      .Where(a => a.TransactionDateTime >= options.StartDate)
                      .Where(a => a.TransactionDateTime < (options.EndDate ?? DateTime.MaxValue));

            if (personId.HasValue)
            {
                // get transactions for a specific person
                qry = qry.Where(a => a.AuthorizedPersonId == personId.Value);
            }
            else
            {
                // get transactions for all the persons in the specified group that have specified that group as their GivingGroup
                GroupMemberService groupMemberService = new GroupMemberService();
                var personIdList = groupMemberService.GetByGroupId(groupId).Where(a => a.Person.GivingGroupId == groupId).Select(s => s.PersonId).ToList();

                qry = qry.Where(a => personIdList.Contains(a.AuthorizedPersonId.Value));
            }

            if (options.AccountIds != null)
            {
                qry = qry.Where(a => options.AccountIds.Contains(a.TransactionDetails.FirstOrDefault().AccountId));
            }

            var selectQry = qry.Select(a => new
            {
                a.TransactionDateTime,
                CurrencyTypeValueName = a.CurrencyTypeValue.Name,
                a.Summary,
                Account = a.TransactionDetails.FirstOrDefault().Account,
                a.Amount
            }).OrderBy(a => a.TransactionDateTime);

            DataTable dataTable = new DataTable("contribution_transactions");

            dataTable.Columns.Add("TransactionDateTime", typeof(DateTime));
            dataTable.Columns.Add("CurrencyTypeValueName");
            dataTable.Columns.Add("Summary");
            dataTable.Columns.Add("AccountId", typeof(int));
            dataTable.Columns.Add("AccountName");
            dataTable.Columns.Add("Amount", typeof(decimal));

            var list = selectQry.ToList();

            dataTable.BeginLoadData();
            foreach (var fieldItems in list)
            {
                var itemArray = new object[] {
                    fieldItems.TransactionDateTime,
                    fieldItems.CurrencyTypeValueName,
                    fieldItems.Summary,
                    fieldItems.Account.Id,
                    fieldItems.Account.Name,
                    fieldItems.Amount
                };

                dataTable.Rows.Add(itemArray);
            }

            dataTable.EndLoadData();

            DataSet dataSet = new DataSet();

            dataSet.Tables.Add(dataTable);

            return(dataSet);
        }
コード例 #5
0
 public DataSet GetContributionTransactions(int groupId, [FromBody] Rock.Net.RestParameters.ContributionStatementOptions options)
 {
     return(GetContributionTransactions(groupId, null, options));
 }
コード例 #6
0
        /// <summary>
        /// Creates the document.
        /// </summary>
        /// <param name="financialTransactionQry">The financial transaction qry.</param>
        /// <returns></returns>
        public Document RunReport()
        {
            UpdateProgress("Connecting...");

            // Login and setup options for REST calls
            RockConfig rockConfig = RockConfig.Load();

            _rockRestClient = new RockRestClient(rockConfig.RockBaseUrl);
            _rockRestClient.Login(rockConfig.Username, rockConfig.Password);

            _contributionStatementOptionsREST = new Rock.Net.RestParameters.ContributionStatementOptions
            {
                StartDate      = Options.StartDate,
                EndDate        = Options.EndDate,
                AccountIds     = Options.AccountIds,
                PersonId       = Options.PersonId,
                OrderByZipCode = true
            };

            var organizationAddressAttribute = _rockRestClient.GetData <List <Rock.Model.Attribute> >("api/attributes", "Key eq 'OrganizationAddress'").FirstOrDefault();

            if (organizationAddressAttribute != null)
            {
                Guid locationGuid = Guid.Empty;
                if (Guid.TryParse(organizationAddressAttribute.DefaultValue, out locationGuid))
                {
                    _organizationAddressLocation = _rockRestClient.GetDataByGuid <Rock.Model.Location>("api/locations", locationGuid);
                }
            }

            // If we don't have a _organizationAddressLocation, just create an empty location
            _organizationAddressLocation = _organizationAddressLocation ?? new Rock.Model.Location();

            // setup report layout and events
            DocumentLayout report = new DocumentLayout(this.Options.LayoutFile);

            //// if there is an imgLogo and the path is "logo.jpg", use the logo specified in rockconfig.
            //// We have to read the layout as Xml first to figure out what the Path of the imgLogo
            XmlDocument layoutXmlDoc = new XmlDocument();

            layoutXmlDoc.Load(this.Options.LayoutFile);
            var imageNodes = layoutXmlDoc.GetElementsByTagName("image");

            foreach (var imageNode in imageNodes.OfType <XmlNode>())
            {
                string imagePath = imageNode.Attributes["path"].Value;
                string imageId   = imageNode.Attributes["id"].Value;
                if (imageId.Equals("imgLogo") && imagePath.Equals(RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase))
                {
                    Image imgLogo = report.GetReportElementById("imgLogo") as Image;
                    if (imgLogo != null)
                    {
                        try
                        {
                            if (!rockConfig.LogoFile.Equals(RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase))
                            {
                                imgLogo.ImageData = ceTe.DynamicPDF.Imaging.ImageData.GetImage(rockConfig.LogoFile);
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Error loading Logo Image: " + rockConfig.LogoFile + "\n\n" + ex.Message);
                        }
                    }
                }
            }


            Query query = report.GetQueryById("OuterQuery");

            if (query == null)
            {
                throw new MissingReportElementException("Report requires a QueryElement named 'OuterQuery'");
            }

            query.OpeningRecordSet += mainQuery_OpeningRecordSet;

            Query orgInfoQuery = report.GetQueryById("OrgInfoQuery");

            if (orgInfoQuery == null)
            {
                throw new MissingReportElementException("Report requires a QueryElement named 'OrgInfoQuery'");
            }

            orgInfoQuery.OpeningRecordSet += orgInfoQuery_OpeningRecordSet;

            _accountSummaryQuery = report.GetQueryById("AccountSummaryQuery");

            if (_accountSummaryQuery == null)
            {
                // not required.  Just don't do anything if it isn't there
            }
            else
            {
                _accountSummaryQuery.OpeningRecordSet += delegate(object s, OpeningRecordSetEventArgs ee)
                {
                    // create a recordset for the _accountSummaryQuery which is the GroupBy summary of AccountName, Amount
                    var summaryTable = _transactionsDataTable.AsEnumerable().GroupBy(g => g["AccountId"]).Select(a => new
                    {
                        AccountName = a.Max(x => x["AccountName"].ToString()),
                        Amount      = a.Sum(x => decimal.Parse(x["Amount"].ToString()))
                    }).OrderBy(o => o.AccountName);

                    ee.RecordSet = new EnumerableRecordSet(summaryTable);
                };
            }

            UpdateProgress("Getting Data...");

            // get outer query data from Rock database via REST now vs in mainQuery_OpeningRecordSet to make sure we have data
            DataSet personGroupAddressDataSet = _rockRestClient.PostDataWithResult <Rock.Net.RestParameters.ContributionStatementOptions, DataSet>("api/FinancialTransactions/GetContributionPersonGroupAddress", _contributionStatementOptionsREST);

            _personGroupAddressDataTable = personGroupAddressDataSet.Tables[0];
            RecordCount = _personGroupAddressDataTable.Rows.Count;

            if (RecordCount > 0)
            {
                Document doc = report.Run();
                return(doc);
            }
            else
            {
                return(null);
            }
        }
コード例 #7
0
        /// <summary>
        /// Creates the document.
        /// </summary>
        /// <param name="financialTransactionQry">The financial transaction qry.</param>
        /// <returns></returns>
        public Document RunReport()
        {
            UpdateProgress("Connecting...");

            // Login and setup options for REST calls
            RockConfig rockConfig = RockConfig.Load();

            _rockRestClient = new RockRestClient(rockConfig.RockBaseUrl);
            _rockRestClient.Login(rockConfig.Username, rockConfig.Password);

            // shouldn't happen, but just in case the StartDate isn't set, set it to the first day of the current year
            DateTime firstDayOfYear = new DateTime(DateTime.Now.Year, 1, 1);

            // note: if a specific person is specified, get them even if they don't have an address.
            _contributionStatementOptionsREST = new Rock.Net.RestParameters.ContributionStatementOptions
            {
                StartDate  = Options.StartDate ?? firstDayOfYear,
                EndDate    = Options.EndDate,
                AccountIds = Options.AccountIds,
                IncludeIndividualsWithNoAddress = Options.PersonId.HasValue || Options.IncludeIndividualsWithNoAddress,
                PersonId          = Options.PersonId,
                OrderByPostalCode = true
            };

            var organizationAddressAttribute = _rockRestClient.GetData <List <Rock.Client.Attribute> >("api/attributes", "Key eq 'OrganizationAddress'").FirstOrDefault();

            if (organizationAddressAttribute != null)
            {
                var organizationAddressAttributeValue = _rockRestClient.GetData <List <Rock.Client.AttributeValue> >("api/AttributeValues", string.Format("AttributeId eq {0}", organizationAddressAttribute.Id)).FirstOrDefault();

                Guid locationGuid = Guid.Empty;
                if (Guid.TryParse(organizationAddressAttributeValue.Value, out locationGuid))
                {
                    _organizationAddressLocation = _rockRestClient.GetData <List <Rock.Client.Location> >("api/locations", string.Format("Guid eq guid'{0}'", locationGuid)).FirstOrDefault();
                }
            }

            // If we don't have a _organizationAddressLocation, just create an empty location
            _organizationAddressLocation = _organizationAddressLocation ?? new Rock.Client.Location();

            // setup report layout and events
            DocumentLayout report = new DocumentLayout(this.Options.LayoutFile);

            //// if there is an imgLogo and the path is "logo.jpg", use the logo specified in rockconfig.
            //// We have to read the layout as Xml first to figure out what the Path of the imgLogo
            XmlDocument layoutXmlDoc = new XmlDocument();

            layoutXmlDoc.Load(this.Options.LayoutFile);
            var imageNodes = layoutXmlDoc.GetElementsByTagName("image");

            foreach (var imageNode in imageNodes.OfType <XmlNode>())
            {
                string imagePath = imageNode.Attributes["path"].Value;
                string imageId   = imageNode.Attributes["id"].Value;
                if (imageId.Equals("imgLogo") && imagePath.Equals(RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase))
                {
                    Image imgLogo = report.GetReportElementById("imgLogo") as Image;
                    if (imgLogo != null)
                    {
                        try
                        {
                            if (!rockConfig.LogoFile.Equals(RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase))
                            {
                                imgLogo.ImageData = ceTe.DynamicPDF.Imaging.ImageData.GetImage(rockConfig.LogoFile);
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("Error loading Logo Image: " + rockConfig.LogoFile + "\n\n" + ex.Message);
                        }
                    }
                }
            }

            Query query = report.GetQueryById("OuterQuery");

            if (query == null)
            {
                throw new MissingReportElementException("Report requires a QueryElement named 'OuterQuery'");
            }

            query.OpeningRecordSet += mainQuery_OpeningRecordSet;

            Query orgInfoQuery = report.GetQueryById("OrgInfoQuery");

            if (orgInfoQuery == null)
            {
                throw new MissingReportElementException("Report requires a QueryElement named 'OrgInfoQuery'");
            }

            orgInfoQuery.OpeningRecordSet += orgInfoQuery_OpeningRecordSet;

            _accountSummaryQuery = report.GetQueryById("AccountSummaryQuery");

            if (_accountSummaryQuery == null)
            {
                // not required.  Just don't do anything if it isn't there
            }
            else
            {
                _accountSummaryQuery.OpeningRecordSet += delegate(object s, OpeningRecordSetEventArgs ee)
                {
                    // create a recordset for the _accountSummaryQuery which is the GroupBy summary of AccountName, Amount

                    /*
                     * The structure of _transactionsDataTable is
                     *
                     * DateTime TransactionDateTime
                     * string CurrencyTypeValueName
                     * string Summary (main transaction summary)
                     * DataTable Details {
                     *    int AccountId
                     *    string AccountName
                     *    string Summary (detail summary)
                     *    decimal Amount
                     * }
                     */

                    var detailsData = new DataTable();
                    detailsData.Columns.Add("AccountId", typeof(int));
                    detailsData.Columns.Add("AccountName");
                    detailsData.Columns.Add("Amount", typeof(decimal));

                    foreach (var details in _transactionsDataTable.AsEnumerable().Select(a => (a["Details"] as DataTable)))
                    {
                        foreach (var row in details.AsEnumerable())
                        {
                            detailsData.Rows.Add(row["AccountId"], row["AccountName"], row["Amount"]);
                        }
                    }

                    var summaryTable = detailsData.AsEnumerable().GroupBy(g => g["AccountId"]).Select(a => new
                    {
                        AccountName = a.Max(x => x["AccountName"].ToString()),
                        Amount      = a.Sum(x => decimal.Parse(x["Amount"].ToString()))
                    }).OrderBy(o => o.AccountName);

                    ee.RecordSet = new EnumerableRecordSet(summaryTable);
                };
            }

            UpdateProgress("Getting Data...");

            // get outer query data from Rock database via REST now vs in mainQuery_OpeningRecordSet to make sure we have data
            DataSet personGroupAddressDataSet = _rockRestClient.PostDataWithResult <Rock.Net.RestParameters.ContributionStatementOptions, DataSet>("api/FinancialTransactions/GetContributionPersonGroupAddress", _contributionStatementOptionsREST);

            _personGroupAddressDataTable = personGroupAddressDataSet.Tables[0];
            RecordCount = _personGroupAddressDataTable.Rows.Count;

            if (RecordCount > 0)
            {
                Document doc = report.Run();
                return(doc);
            }
            else
            {
                return(null);
            }
        }
コード例 #8
0
ファイル: ContributionReport.cs プロジェクト: pkdevbox/Rock
        /// <summary>
        /// Creates the document.
        /// </summary>
        /// <param name="financialTransactionQry">The financial transaction qry.</param>
        /// <returns></returns>
        public Document RunReport()
        {
            UpdateProgress( "Connecting..." );

            // Login and setup options for REST calls
            RockConfig rockConfig = RockConfig.Load();

            _rockRestClient = new RockRestClient( rockConfig.RockBaseUrl );
            _rockRestClient.Login( rockConfig.Username, rockConfig.Password );

            _contributionStatementOptionsREST = new Rock.Net.RestParameters.ContributionStatementOptions
            {
                StartDate = Options.StartDate,
                EndDate = Options.EndDate,
                AccountIds = Options.AccountIds,
                PersonId = Options.PersonId,
                OrderByZipCode = true
            };

            var organizationAddressAttribute = _rockRestClient.GetData<List<Rock.Model.Attribute>>( "api/attributes", "Key eq 'OrganizationAddress'" ).FirstOrDefault();
            if ( organizationAddressAttribute != null )
            {
                Guid locationGuid = Guid.Empty;
                if ( Guid.TryParse( organizationAddressAttribute.DefaultValue, out locationGuid ) )
                {
                    _organizationAddressLocation = _rockRestClient.GetDataByGuid<Rock.Model.Location>( "api/locations", locationGuid );
                }
            }

            // If we don't have a _organizationAddressLocation, just create an empty location
            _organizationAddressLocation = _organizationAddressLocation ?? new Rock.Model.Location();

            // setup report layout and events
            DocumentLayout report = new DocumentLayout( this.Options.LayoutFile );

            //// if there is an imgLogo and the path is "logo.jpg", use the logo specified in rockconfig.  
            //// We have to read the layout as Xml first to figure out what the Path of the imgLogo
            XmlDocument layoutXmlDoc = new XmlDocument();
            layoutXmlDoc.Load( this.Options.LayoutFile );
            var imageNodes = layoutXmlDoc.GetElementsByTagName( "image" );
            foreach ( var imageNode in imageNodes.OfType<XmlNode>() )
            {
                string imagePath = imageNode.Attributes["path"].Value;
                string imageId =imageNode.Attributes["id"].Value;
                if (imageId.Equals("imgLogo" ) &&  imagePath.Equals( RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase )  )
                {
                    Image imgLogo = report.GetReportElementById( "imgLogo" ) as Image;
                    if ( imgLogo != null )
                    {
                        try
                        {
                            if ( !rockConfig.LogoFile.Equals( RockConfig.DefaultLogoFile, StringComparison.OrdinalIgnoreCase ) )
                            {
                                imgLogo.ImageData = ceTe.DynamicPDF.Imaging.ImageData.GetImage( rockConfig.LogoFile );
                            }
                        }
                        catch (Exception ex)
                        {
                            throw new Exception( "Error loading Logo Image: " + rockConfig.LogoFile + "\n\n" + ex.Message);
                        }
                    }
                }
            }


            Query query = report.GetQueryById( "OuterQuery" );
            if ( query == null )
            {
                throw new MissingReportElementException( "Report requires a QueryElement named 'OuterQuery'" );
            }

            query.OpeningRecordSet += mainQuery_OpeningRecordSet;

            Query orgInfoQuery = report.GetQueryById( "OrgInfoQuery" );
            if ( orgInfoQuery == null )
            {
                throw new MissingReportElementException( "Report requires a QueryElement named 'OrgInfoQuery'" );
            }

            orgInfoQuery.OpeningRecordSet += orgInfoQuery_OpeningRecordSet;

            _accountSummaryQuery = report.GetQueryById( "AccountSummaryQuery" );

            if ( _accountSummaryQuery == null )
            {
                // not required.  Just don't do anything if it isn't there
            }
            else
            {
                _accountSummaryQuery.OpeningRecordSet += delegate( object s, OpeningRecordSetEventArgs ee )
                {
                    // create a recordset for the _accountSummaryQuery which is the GroupBy summary of AccountName, Amount
                    var summaryTable = _transactionsDataTable.AsEnumerable().GroupBy( g => g["AccountId"] ).Select( a => new
                    {
                        AccountName = a.Max(x => x["AccountName"].ToString()),
                        Amount = a.Sum( x => decimal.Parse( x["Amount"].ToString() ) )
                    } ).OrderBy( o => o.AccountName );

                    ee.RecordSet = new EnumerableRecordSet( summaryTable );
                };
            }

            UpdateProgress( "Getting Data..." );

            // get outer query data from Rock database via REST now vs in mainQuery_OpeningRecordSet to make sure we have data
            DataSet personGroupAddressDataSet = _rockRestClient.PostDataWithResult<Rock.Net.RestParameters.ContributionStatementOptions, DataSet>( "api/FinancialTransactions/GetContributionPersonGroupAddress", _contributionStatementOptionsREST );
            _personGroupAddressDataTable = personGroupAddressDataSet.Tables[0];
            RecordCount = _personGroupAddressDataTable.Rows.Count;

            if ( RecordCount > 0 )
            {
                Document doc = report.Run();
                return doc;
            }
            else
            {
                return null;
            }
        }
        public DataSet GetContributionTransactions(int groupId, int?personId, [FromBody] Rock.Net.RestParameters.ContributionStatementOptions options)
        {
            var qry = Get()
                      .Where(a => a.TransactionDateTime >= options.StartDate)
                      .Where(a => a.TransactionDateTime < (options.EndDate ?? DateTime.MaxValue));

            if (personId.HasValue)
            {
                // get transactions for a specific person
                qry = qry.Where(a => a.AuthorizedPersonAlias.PersonId == personId.Value);
            }
            else
            {
                // get transactions for all the persons in the specified group that have specified that group as their GivingGroup
                GroupMemberService groupMemberService = new GroupMemberService((RockContext)Service.Context);
                var personIdList = groupMemberService.GetByGroupId(groupId).Where(a => a.Person.GivingGroupId == groupId).Select(s => s.PersonId).ToList();

                qry = qry.Where(a => personIdList.Contains(a.AuthorizedPersonAlias.PersonId));
            }

            if (options.AccountIds != null)
            {
                qry = qry.Where(a => options.AccountIds.Contains(a.TransactionDetails.FirstOrDefault().AccountId));
            }

            var selectQry = qry.Select(a => new
            {
                a.TransactionDateTime,
                CurrencyTypeValueName = a.CurrencyTypeValue.Value,
                a.Summary,
                Details = a.TransactionDetails.Select(d => new
                {
                    d.AccountId,
                    AccountName = d.Account.Name,
                    a.Summary,
                    d.Amount
                }).OrderBy(x => x.AccountName),
            }).OrderBy(a => a.TransactionDateTime);

            DataTable dataTable = new DataTable("contribution_transactions");

            dataTable.Columns.Add("TransactionDateTime", typeof(DateTime));
            dataTable.Columns.Add("CurrencyTypeValueName");
            dataTable.Columns.Add("Summary");
            dataTable.Columns.Add("Amount", typeof(decimal));
            dataTable.Columns.Add("Details", typeof(DataTable));

            var list = selectQry.ToList();

            dataTable.BeginLoadData();
            foreach (var fieldItems in list)
            {
                DataTable detailTable = new DataTable("transaction_details");
                detailTable.Columns.Add("AccountId", typeof(int));
                detailTable.Columns.Add("AccountName");
                detailTable.Columns.Add("Summary");
                detailTable.Columns.Add("Amount", typeof(decimal));
                foreach (var detail in fieldItems.Details)
                {
                    var detailArray = new object[] {
                        detail.AccountId,
                        detail.AccountName,
                        detail.Summary,
                        detail.Amount
                    };

                    detailTable.Rows.Add(detailArray);
                }

                var itemArray = new object[] {
                    fieldItems.TransactionDateTime,
                    fieldItems.CurrencyTypeValueName,
                    fieldItems.Summary,
                    fieldItems.Details.Sum(a => a.Amount),
                    detailTable
                };

                dataTable.Rows.Add(itemArray);
            }

            dataTable.EndLoadData();

            DataSet dataSet = new DataSet();

            dataSet.Tables.Add(dataTable);

            return(dataSet);
        }