public FinancialStatementGeneratorUploadGivingStatementResult UploadGivingStatementDocument([FromBody] FinancialStatementGeneratorUploadGivingStatementData uploadGivingStatementData)
        {
            var rockContext = new RockContext();

            var saveOptions = uploadGivingStatementData?.FinancialStatementIndividualSaveOptions;

            if (saveOptions == null)
            {
                throw new FinancialGivingStatementArgumentException("FinancialStatementIndividualSaveOptions must be specified");
            }

            if (!saveOptions.SaveStatementsForIndividuals)
            {
                throw new FinancialGivingStatementArgumentException("FinancialStatementIndividualSaveOptions.SaveStatementsForIndividuals is not enabled.");
            }

            var documentTypeId = saveOptions.DocumentTypeId;

            if (!documentTypeId.HasValue)
            {
                throw new FinancialGivingStatementArgumentException("Document Type must be specified");
            }

            var documentType = new DocumentTypeService(rockContext)
                               .Queryable()
                               .AsNoTracking()
                               .Where(dt => dt.Id == documentTypeId.Value)
                               .Select(a => new
            {
                BinaryFileTypeId = ( int? )a.BinaryFileTypeId
            })
                               .FirstOrDefault();

            if (documentType == null)
            {
                throw new FinancialGivingStatementArgumentException("DocumentType must be specified");
            }

            if (documentType.BinaryFileTypeId == null)
            {
                throw new FinancialGivingStatementArgumentException("DocumentType.BinaryFileType must be specified");
            }

            var pdfData = uploadGivingStatementData.PDFData;

            string fileName = saveOptions.DocumentName + ".pdf";

            var financialStatementGeneratorRecipient = uploadGivingStatementData.FinancialStatementGeneratorRecipient;

            if (financialStatementGeneratorRecipient == null)
            {
                throw new FinancialGivingStatementArgumentException("FinancialStatementGeneratorRecipient must be specified");
            }

            var documentName = saveOptions.DocumentName;

            List <int> documentPersonIds;

            if (financialStatementGeneratorRecipient.PersonId.HasValue)
            {
                // If we are saving for a person that gives an individual, just give document to that person (ignore the FinancialStatementIndividualSaveOptionsSaveFor option)
                // only upload the document to the individual person
                documentPersonIds = new List <int>();
                documentPersonIds.Add(financialStatementGeneratorRecipient.PersonId.Value);
            }
            else
            {
                var groupId = financialStatementGeneratorRecipient.GroupId;
                var givingFamilyMembersQuery = new GroupMemberService(rockContext).GetByGroupId(groupId, false);

                // limit to family members within the same giving group
                givingFamilyMembersQuery = givingFamilyMembersQuery.Where(a => a.Person.GivingGroupId.HasValue && a.Person.GivingGroupId == groupId);

                if (saveOptions.DocumentSaveFor == FinancialStatementGeneratorOptions.FinancialStatementIndividualSaveOptions.FinancialStatementIndividualSaveOptionsSaveFor.AllActiveAdultsInGivingGroup)
                {
                    documentPersonIds = givingFamilyMembersQuery
                                        .Where(a => a.Person.AgeClassification == AgeClassification.Adult).Select(a => a.PersonId).ToList();
                }
                else if (saveOptions.DocumentSaveFor == FinancialStatementGeneratorOptions.FinancialStatementIndividualSaveOptions.FinancialStatementIndividualSaveOptionsSaveFor.AllActiveFamilyMembersInGivingGroup)
                {
                    documentPersonIds = givingFamilyMembersQuery
                                        .Select(a => a.PersonId).ToList();
                }
                else if (saveOptions.DocumentSaveFor == FinancialStatementGeneratorOptions.FinancialStatementIndividualSaveOptions.FinancialStatementIndividualSaveOptionsSaveFor.PrimaryGiver)
                {
                    // Set document for PrimaryGiver (aka Head of Household).
                    // Note that HeadOfHouseHold would calculated based on family members within the same giving group
                    var headOfHouseHoldPersonId = givingFamilyMembersQuery.GetHeadOfHousehold(s => ( int? )s.PersonId);
                    documentPersonIds = new List <int>();
                    if (headOfHouseHoldPersonId.HasValue)
                    {
                        documentPersonIds.Add(headOfHouseHoldPersonId.Value);
                    }
                }
                else
                {
                    // shouldn't happen
                    documentPersonIds = new List <int>();
                }
            }

            var today    = RockDateTime.Today;
            var tomorrow = today.AddDays(1);

            foreach (var documentPersonId in documentPersonIds)
            {
                // Create the document, linking the entity and binary file.
                if (saveOptions.OverwriteDocumentsOfThisTypeCreatedOnSameDate == true)
                {
                    using (var deleteDocContext = new RockContext())
                    {
                        var deleteDocumentService = new DocumentService(deleteDocContext);

                        // See if there is an existing one.
                        // Note include BinaryFile in the Get since we'll have to mark it temporary if it exists.
                        var existingDocument = deleteDocumentService.Queryable().Where(
                            a => a.DocumentTypeId == documentTypeId.Value &&
                            a.EntityId == documentPersonId &&
                            a.CreatedDateTime.HasValue &&
                            a.CreatedDateTime >= today && a.CreatedDateTime < tomorrow)
                                               .Include(a => a.BinaryFile).FirstOrDefault();

                        // NOTE: Delete vs update since we normally don't change the contents of documents/binary files once they've been created
                        if (existingDocument != null)
                        {
                            deleteDocumentService.Delete(existingDocument);
                            deleteDocContext.SaveChanges();
                        }
                    }
                }

                // Create the binary file.
                var binaryFile = new BinaryFile
                {
                    BinaryFileTypeId = documentType.BinaryFileTypeId,
                    MimeType         = "application/pdf",
                    FileName         = fileName,
                    FileSize         = pdfData.Length,
                    IsTemporary      = false,
                    ContentStream    = new MemoryStream(pdfData)
                };

                new BinaryFileService(rockContext).Add(binaryFile);
                rockContext.SaveChanges();

                Document document = new Document
                {
                    DocumentTypeId = documentTypeId.Value,
                    EntityId       = documentPersonId,
                    PurposeKey     = saveOptions.DocumentPurposeKey,
                    Name           = saveOptions.DocumentName,
                    Description    = saveOptions.DocumentDescription
                };

                document.SetBinaryFile(binaryFile.Id, rockContext);

                var documentService = new DocumentService(rockContext);

                documentService.Add(document);
            }

            rockContext.SaveChanges();

            return(new FinancialStatementGeneratorUploadGivingStatementResult
            {
                NumberOfIndividuals = documentPersonIds.Count
            });
        }
 /// <summary>
 /// Copies the base properties from a source FinancialStatementGeneratorUploadGivingStatementData object
 /// </summary>
 /// <param name="source">The source.</param>
 public void CopyPropertiesFrom(FinancialStatementGeneratorUploadGivingStatementData source)
 {
     this.FinancialStatementGeneratorRecipient    = source.FinancialStatementGeneratorRecipient;
     this.FinancialStatementIndividualSaveOptions = source.FinancialStatementIndividualSaveOptions;
     this.PDFData = source.PDFData;
 }