Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Payments.Jobs.AutoApprove/Payments.Jobs.AutoApprove.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\src\Payments.Core\Payments.Core.csproj" />
<ProjectReference Include="..\src\Payments.Jobs.Core\Payments.Jobs.Core.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<None Update="run.cmd">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="settings.job">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
113 changes: 113 additions & 0 deletions Payments.Jobs.AutoApprove/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Payments.Core.Data;
using Payments.Core.Domain;
using Payments.Core.Jobs;
using Payments.Core.Models.Configuration;
using Payments.Core.Models.History;
using Payments.Core.Services;
using Payments.Jobs.Core;
using Serilog;
using System;

namespace Payments.Jobs.MoneyMovement
{
public class Program : JobBase
{
private static ILogger _log;

public static void Main(string[] args)
{
// setup env
Configure();


_log = Log.Logger
.ForContext("jobname", "AutoApprove");

var assembyName = typeof(Program).Assembly.GetName();
_log.Information("Running {job} build {build}", assembyName.Name, assembyName.Version);
_log.Information("AutoApprove Version 1");

// setup di
var provider = ConfigureServices();
var dbContext = provider.GetService<ApplicationDbContext>();


try
{
if(dbContext == null)
{
throw new InvalidOperationException("Failed to obtain ApplicationDbContext from service provider.");
}
var financeSettings = provider.GetService<IOptions<FinanceSettings>>()?.Value;
if (financeSettings == null)
{
throw new InvalidOperationException("FinanceSettings configuration is missing or invalid.");
}
var daysToAutoApprove = financeSettings.RechargeAutoApproveDays;
var dateThreshold = DateTime.UtcNow.AddDays(-daysToAutoApprove);

var invoices = dbContext.Invoices
.Include(i => i.RechargeAccounts)
.Where(a => a.Type == Invoice.InvoiceTypes.Recharge && a.Status == Invoice.StatusCodes.PendingApproval &&
a.PaidAt != null && a.PaidAt <= dateThreshold).ToList();
_log.Information("Found {count} invoices to auto-approve", invoices.Count);
foreach (var invoice in invoices)
{
invoice.Status = Invoice.StatusCodes.Approved;
foreach (var ra in invoice.RechargeAccounts.Where(a => a.Direction == RechargeAccount.CreditDebit.Debit && string.IsNullOrWhiteSpace( a.ApprovedByKerb)))
{
ra.ApprovedByKerb = "Automated";
ra.ApprovedByName = "System";
}

invoice.Paid = true;

var approvalAction = new History()
{
Type = HistoryActionTypes.RechargeApprovedByFinancialApprover.TypeCode,
ActionDateTime = DateTime.UtcNow,
Actor = "System",
Data = "All debit recharge accounts have been approved."
};

invoice.History.Add(approvalAction);
dbContext.Invoices.Update(invoice);
_log.Information("Auto-approved invoice {invoiceId}", invoice.Id);

dbContext.SaveChanges();
}
}
catch (Exception ex)
{

_log.Error("Error running AutoApprove job", ex);
throw;
}

_log.Information("AutoApprove job completed");

}

private static ServiceProvider ConfigureServices()
{
IServiceCollection services = new ServiceCollection();

// options files
services.Configure<FinanceSettings>(Configuration.GetSection("Finance"));


// db service
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);



return services.BuildServiceProvider();
}
}
}
10 changes: 10 additions & 0 deletions Payments.Jobs.AutoApprove/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"profiles": {
"Payments.Jobs.AutoApprove": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
18 changes: 18 additions & 0 deletions Payments.Jobs.AutoApprove/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=.\\SQLExpress;Initial Catalog=Payments;Integrated Security=True;MultipleActiveResultSets=True"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"Finance": {
"RechargeAutoApproveDays": 7
},
"Stackify": {
"AppName": "payments.jobs.autoapprove",
"ApiKey": "[External]",
"Environment": "[External]"
}
}
3 changes: 3 additions & 0 deletions Payments.Jobs.AutoApprove/run.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

dotnet Payments.Jobs.AutoApprove.dll
3 changes: 3 additions & 0 deletions Payments.Jobs.AutoApprove/settings.job
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"schedule": "0 0 6 * * *"
}
19 changes: 17 additions & 2 deletions Payments.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29326.143
# Visual Studio Version 17
VisualStudioVersion = 17.14.36705.20 d17.14
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D1C93383-2244-4990-B2E9-5EF966F0E5A5}"
EndProject
Expand All @@ -25,6 +25,8 @@ Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Payments.Sql", "src\Payment
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Payments.Emails", "src\Payments.Emails\Payments.Emails.csproj", "{7A4BE093-ED90-4464-BF9C-9B5F748D5F10}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Payments.Jobs.AutoApprove", "Payments.Jobs.AutoApprove\Payments.Jobs.AutoApprove.csproj", "{67C14AEE-809F-4F73-8425-FFB06A35D678}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -121,6 +123,18 @@ Global
{7A4BE093-ED90-4464-BF9C-9B5F748D5F10}.Release|x64.Build.0 = Release|Any CPU
{7A4BE093-ED90-4464-BF9C-9B5F748D5F10}.Release|x86.ActiveCfg = Release|Any CPU
{7A4BE093-ED90-4464-BF9C-9B5F748D5F10}.Release|x86.Build.0 = Release|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Debug|x64.ActiveCfg = Debug|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Debug|x64.Build.0 = Debug|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Debug|x86.ActiveCfg = Debug|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Debug|x86.Build.0 = Debug|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Release|Any CPU.Build.0 = Release|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Release|x64.ActiveCfg = Release|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Release|x64.Build.0 = Release|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Release|x86.ActiveCfg = Release|Any CPU
{67C14AEE-809F-4F73-8425-FFB06A35D678}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -133,6 +147,7 @@ Global
{02AA0336-4F06-40C8-98A9-E29F510E3E57} = {515AA768-8E83-4026-AF1B-CAF89624E938}
{1C67A279-CA98-494D-B9A6-DD5D99FD1A8F} = {D1C93383-2244-4990-B2E9-5EF966F0E5A5}
{7A4BE093-ED90-4464-BF9C-9B5F748D5F10} = {D1C93383-2244-4990-B2E9-5EF966F0E5A5}
{67C14AEE-809F-4F73-8425-FFB06A35D678} = {D1C93383-2244-4990-B2E9-5EF966F0E5A5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {34045125-52CB-46C0-BEBC-371EB2F0DFEB}
Expand Down
9 changes: 9 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ stages:
projects: "./src/Payments.Jobs.MoneyMovement/Payments.Jobs.MoneyMovement.csproj"
arguments: "--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/app_data/jobs/triggered"

- task: DotNetCoreCLI@2
displayName: "Publish Payments.Jobs.AutoApprove"
inputs:
command: "publish"
publishWebProjects: false
zipAfterPublish: false
projects: "./src/Payments.Jobs.AutoApprove/Payments.Jobs.AutoApprove.csproj"
arguments: "--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/app_data/jobs/triggered"

- task: PublishBuildArtifacts@1
condition: eq(variables['Build.SourceBranch'], 'refs/heads/master')
displayName: "Publish Build Artifacts for master branch builds"
Expand Down
4 changes: 3 additions & 1 deletion src/Payments.Core/Models/Configuration/FinanceSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public class FinanceSettings

public string RechargeSlothSourceName { get; set; } = "PaymentsRecharge";

public bool ValidateRechargeFinancialSegmentString { get; set; } = false; //Maybe we want this to default to true?
public bool ValidateRechargeFinancialSegmentString { get; set; } = true;

public int RechargeAutoApproveDays { get; set; } = 7;
}
}
3 changes: 0 additions & 3 deletions src/Payments.Core/Models/Configuration/PaymentsApiSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,5 @@ public class PaymentsApiSettings

public string ApiKey { get; set; }

public string RechargeApiKey { get; set; }

public string RechargeSourceName { get; set; }
}
}
1 change: 1 addition & 0 deletions src/Payments.Core/Models/History/HistoryActionTypeCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static class HistoryActionTypes
RechargePaidByCustomer,
RechargeSentToFinancialApprovers,
RechargeRejectedByFinancialApprover,
RechargeApprovedByFinancialApprover,
RechargeRejected,
};

Expand Down
6 changes: 5 additions & 1 deletion src/Payments.Emails/EmailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ public class SparkpostEmailService : IEmailService

private readonly MailAddress _refundAddress;

public SparkpostEmailService(IOptions<SparkpostSettings> sparkpostSettings, IMjmlServices mjmlServices)
private readonly FinanceSettings _financeSettings;

public SparkpostEmailService(IOptions<SparkpostSettings> sparkpostSettings, IMjmlServices mjmlServices, IOptions<FinanceSettings> financeSettings)
{
_mjmlServices = mjmlServices;
_financeSettings = financeSettings.Value;

_sparkpostSettings = sparkpostSettings.Value;

Expand Down Expand Up @@ -205,6 +208,7 @@ public async Task SendFinancialApprove(Invoice invoice, SendApprovalModel approv
{
var viewbag = GetViewData();
viewbag["Team"] = invoice.Team;
viewbag["DaysToAutoApprove"] = _financeSettings.RechargeAutoApproveDays;

var model = new InvoiceViewModel
{
Expand Down
3 changes: 2 additions & 1 deletion src/Payments.Emails/Views/FinancialApprove.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@{
Layout = "_EmailLayout";
var team = (Team) ViewData["Team"];
var daysToAutoApprove = ViewData["DaysToAutoApprove"] ?? 7;

var invoice = Model.Invoice;
var extraText = "Recharge Invoice # " + invoice.GetFormattedId();
Expand All @@ -17,7 +18,7 @@
<h1>Invoice from @team.Name</h1>
<h3>@extraText</h3>
<div>You are one or more Financial Approvers needed to Approve or Deny the recharge for this invoice.</div>
<div>This will be auto approved in 7 days if no action is taken.</div>
<div>This will be auto approved in @daysToAutoApprove days if no action is taken.</div>
</mj-text>

@if(!string.IsNullOrWhiteSpace( invoice.Memo))
Expand Down
48 changes: 45 additions & 3 deletions src/Payments.Mvc/Controllers/InvoicesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,14 +452,56 @@ public async Task<IActionResult> Send(int id, [FromBody] SendInvoiceModel model)

await _invoiceService.SendInvoice(invoice, model);

if(invoice.Type == Invoice.InvoiceTypes.Recharge && invoice.Status == Invoice.StatusCodes.PendingApproval)
var user = await _userManager.GetUserAsync(User);

// check if the user email is the same as the customer email for recharges so it goes directly to the pending approval.
if (invoice.Type == Invoice.InvoiceTypes.Recharge &&
string.Equals(user.Email, invoice.CustomerEmail, StringComparison.OrdinalIgnoreCase) &&
invoice.Status == Invoice.StatusCodes.Sent &&
invoice.RechargeAccounts.Where(a => a.Direction == RechargeAccount.CreditDebit.Debit).Any())
{
invoice.PaidAt = DateTime.UtcNow;
invoice.Status = Invoice.StatusCodes.PendingApproval;
var approvalAction = new History()
{
Type = HistoryActionTypes.RechargePaidByCustomer.TypeCode,
ActionDateTime = DateTime.UtcNow,
Actor = user.Name,
Data = new RechargePaidByCustomerHistoryActionType().SerializeData(new RechargePaidByCustomerHistoryActionType.DataType
{
RechargeAccounts = invoice.RechargeAccounts.Where(a => a.Direction == RechargeAccount.CreditDebit.Debit).ToArray()
})
};
invoice.History.Add(approvalAction);
}

if (invoice.Type == Invoice.InvoiceTypes.Recharge && invoice.Status == Invoice.StatusCodes.PendingApproval)
{
//Need to resend these ones too
await _invoiceService.SendFinancialApproverEmail(invoice, null); //Will pull them with a private method
var sentTo = await _invoiceService.SendFinancialApproverEmail(invoice, null); //Will pull them with a private method

if (sentTo != null)
{
var approvalSentAction = new History()
{
Type = HistoryActionTypes.RechargeSentToFinancialApprovers.TypeCode,
ActionDateTime = DateTime.UtcNow,
Actor = "System",
Data = new RechargeSentToFinancialApproversHistoryActionType().SerializeData(new RechargeSentToFinancialApproversHistoryActionType.DataType
{
FinancialApprovers = sentTo.emails.Select(a => new RechargeSentToFinancialApproversHistoryActionType.FinancialApprover()
{
Name = a.Name,
Email = a.Email
}).ToArray()
})
};
invoice.History.Add(approvalSentAction);
}
}

// record action
var user = await _userManager.GetUserAsync(User);

var action = new History()
{
Type = HistoryActionTypes.InvoiceSent.TypeCode,
Expand Down
Loading
Loading