public async Task <DisperseFundsResult> TransferCreated(Transfer transfer) { _logger.LogInformation(GetLogMessage("Transfer ID: {0}"), transfer.Id); var retVal = new DisperseFundsResult() { TransferId = transfer.Id, Amount = 0, }; if (transfer.Metadata.ContainsKey("invoice-id")) { string invoiceId = transfer.Metadata["invoice-id"]; _logger.LogDebug(GetLogMessage("Invoice Id: {0}"), invoiceId); retVal.InvoiceId = invoiceId; var stripeTransfer = new StripeTransfer { ObjectState = ObjectState.Added, Id = transfer.Id, Amount = transfer.Amount / 100m, Created = transfer.Created, DestinationId = transfer.DestinationId, AmountReversed = transfer.AmountReversed / 100m, Description = transfer.Description, InvoiceTransfer = new InvoiceTransfer() { ObjectState = ObjectState.Added, InvoiceId = invoiceId, TransferId = transfer.Id, } }; stripeTransfer.InjectFrom(transfer); var firstUpdate = _transfers.InsertOrUpdateGraph(stripeTransfer, true); _logger.LogDebug(GetLogMessage("{0} Transfer Records Created"), firstUpdate); if (firstUpdate > 0) { stripeTransfer.ObjectState = ObjectState.Modified; if (transfer.Metadata.ContainsKey("individual-payout-ids")) { if (Guid.TryParse(transfer.Metadata["person-id"], out var personId)) { _logger.LogInformation(GetLogMessage("This is an individual payout transaction request")); var payoutIds = transfer.Metadata["individual-payout-ids"].Split(",") .Select(Guid.Parse).ToArray(); _logger.LogDebug(GetLogMessage("Person Payout Ids: {0}"), payoutIds); var payouts = await _individualPayoutIntents.Queryable() .Where(x => payoutIds.Contains(x.Id) && x.InvoiceTransferId == null) .ToListAsync(); _logger.LogDebug(GetLogMessage("{0} Individual Payouts to process"), payouts.Count); foreach (var x in payouts) { _logger.LogInformation(GetLogMessage("Intent Id: {0}; Transfer Id: {1}"), x.Id, transfer.Id); x.InvoiceTransferId = transfer.Id; x.ObjectState = ObjectState.Modified; _individualPayoutIntents.InsertOrUpdateGraph(x); } var updatedPayouts = _individualPayoutIntents.Commit(); _logger.LogDebug(GetLogMessage("{0} Individual Payouts records updated"), updatedPayouts); if (updatedPayouts > 0) { retVal.Succeeded = true; retVal.Amount = stripeTransfer.Amount; await Task.Run(() => { RaiseEvent(new FundsDispersedToPersonEvent() { InvoiceId = invoiceId, PersonId = personId, Amount = stripeTransfer.Amount }); }); } } } var organizationPayouts = 0; if (transfer.Metadata.ContainsKey("organization-payout-ids")) { if (Guid.TryParse(transfer.Metadata["organization-id"], out var organizationId)) { _logger.LogInformation(GetLogMessage("Processing Organization Payouts")); var payoutIds = transfer.Metadata["organization-payout-ids"].Split(",").Select(Guid.Parse).ToArray(); var payouts = await _organizationPayoutIntents.Queryable() .Where(x => payoutIds.Contains(x.Id) && x.OrganizationId == organizationId && x.InvoiceTransferId == null) .ToListAsync(); _logger.LogDebug(GetLogMessage("PayoutIds:{0}"), payoutIds); foreach (var x in payouts) { _logger.LogInformation(GetLogMessage("Intent Id: {0}; Transfer Id: {1}"), x.Id, x.InvoiceTransferId); x.InvoiceTransferId = transfer.Id; x.ObjectState = ObjectState.Modified; _organizationPayoutIntents.InsertOrUpdateGraph(x); } var records = _organizationPayoutIntents.Commit(); _logger.LogDebug(GetLogMessage("{0} Organization Payout records updated"), records); if (records > 0) { organizationPayouts++; } _logger.LogDebug(GetLogMessage("{0} Organization payouts updated"), organizationPayouts); if (organizationPayouts > 0) { retVal.Succeeded = true; retVal.Amount = stripeTransfer.Amount; await Task.Run(() => { RaiseEvent(new FundsDispersedToOrganizationEvent() { InvoiceId = invoiceId, OrganizationId = organizationId, Amount = stripeTransfer.Amount }); }); } } ; } } else { _logger.LogDebug(GetLogMessage("First update failed")); } } else { _logger.LogDebug(GetLogMessage("Not an invoice transfer")); } return(retVal); }
private async Task <DisperseFundsResult> DisperseFundsToOrganization(IProviderAgencyOwner ao, string invoiceId) { _logger.LogInformation(GetLogMessage("Dispersing Funds for invoice: {0}"), invoiceId); var retVal = new DisperseFundsResult { InvoiceId = invoiceId }; var organizationPayouts = await _organizationPayoutIntents .Queryable() .Include(x => x.InvoiceTransfer) .Include(x => x.InvoiceItem) .ThenInclude(x => x.Contract) .Include(x => x.InvoiceItem) .ThenInclude(x => x.TimeEntries) .Include(x => x.Organization) .ThenInclude(x => x.OrganizationFinancialAccount) .ThenInclude(x => x.FinancialAccount) .Where(x => x.InvoiceItem.InvoiceId == invoiceId && x.Organization.OrganizationFinancialAccount.FinancialAccount != null && x.Organization.OrganizationFinancialAccount.FinancialAccount.PayoutsEnabled && x.InvoiceTransfer == null) .ToListAsync(); _logger.LogDebug(GetLogMessage("Payouts ready for payment: {0}"), organizationPayouts.Count); var totalTransfersMade = 0; var totalAmountTransferred = 0m; if (organizationPayouts.Count > 0) { var transfersToMake = new List <TransferCreateOptions>(); var organizations = organizationPayouts.Select(x => x.Organization).Distinct().ToList(); _logger.LogDebug(GetLogMessage("Unique Organizations: {0}"), organizations.Count); foreach (var organization in organizations) { _logger.LogDebug(GetLogMessage("Processing Payouts for Organization: {0}"), organization.Id); var orgPayouts = organizationPayouts .Where(x => x.OrganizationId == organization.Id) .ToList(); _logger.LogDebug(GetLogMessage("Number of Payouts: {0}"), orgPayouts.Count); if (orgPayouts.Count > 0) { var orgPayoutIds = orgPayouts .Select(x => x.Id) .ToArray(); var payoutIdString = string.Join(",", orgPayoutIds); var sb = new StringBuilder(); foreach (var payout in orgPayouts) { sb.AppendLine($@"{payout.Type.GetDescription()}: {payout.Amount:C}"); } transfersToMake.Add(new TransferCreateOptions() { Amount = Convert.ToInt64(orgPayouts.Sum(x => x.Amount) * 100), Currency = "usd", Description = sb.ToString(), Destination = organization.OrganizationFinancialAccount.FinancialAccountId, TransferGroup = invoiceId, Metadata = new Dictionary <string, string>() { { "invoice-id", invoiceId }, { "organization-payout-ids", payoutIdString }, { "organization-id", organization.Id.ToString() } } }); } else { _logger.LogDebug(GetLogMessage("Skipping payouts")); } } _logger.LogDebug(GetLogMessage("{0} Transfers to make"), transfersToMake.Count); foreach (var tran in transfersToMake) { _logger.LogDebug(GetLogMessage("Transfer Amount: {0}"), tran.Amount); var transfer = _transferService.Create(tran); var transferResult = await TransferCreated(transfer); _logger.LogDebug(GetLogMessage("Transfer Result: {@0}"), transferResult); if (transferResult.Succeeded) { _logger.LogDebug(GetLogMessage("Transfer Creation Succeeded: {@result}"), transferResult); totalAmountTransferred += transferResult.Amount; totalTransfersMade += 1; } else { _logger.LogDebug(GetLogMessage("Transfer Creation Failed")); } } if (totalTransfersMade > 0) { retVal.Succeeded = true; retVal.Amount = totalAmountTransferred; retVal.TotalTransfersMade = totalTransfersMade; await Task.Run(() => { RaiseEvent(new FundsDispersedEvent() { InvoiceId = invoiceId, TotalTransfersMade = totalTransfersMade }); }); } } return(await Task.FromResult(retVal)); }