mirror of
https://github.com/bitwarden/server.git
synced 2025-01-26 22:31:30 +01:00
remove jsreport pdfs. use stripe invoice pages.
This commit is contained in:
parent
ecd569fa0b
commit
18131ba1e3
@ -12,9 +12,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="jsreport.AspNetCore" Version="1.0.1" />
|
|
||||||
<PackageReference Include="jsreport.Binary" Version="1.10.0" />
|
|
||||||
<PackageReference Include="jsreport.Local" Version="1.1.2" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />
|
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
namespace Bit.Api
|
|
||||||
{
|
|
||||||
public class ApiSettings
|
|
||||||
{
|
|
||||||
public string OurAddress1 { get; set; }
|
|
||||||
public string OurAddress2 { get; set; }
|
|
||||||
public string OurAddress3 { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,9 +10,6 @@ using Bit.Core.Services;
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Api.Utilities;
|
using Bit.Api.Utilities;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using jsreport.AspNetCore;
|
|
||||||
using jsreport.Types;
|
|
||||||
using Bit.Api.Models;
|
|
||||||
using Stripe;
|
using Stripe;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@ -29,7 +26,6 @@ namespace Bit.Api.Controllers
|
|||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly CurrentContext _currentContext;
|
private readonly CurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly ApiSettings _apiSettings;
|
|
||||||
|
|
||||||
public OrganizationsController(
|
public OrganizationsController(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -37,8 +33,7 @@ namespace Bit.Api.Controllers
|
|||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
CurrentContext currentContext,
|
CurrentContext currentContext,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings)
|
||||||
IOptions<ApiSettings> apiSettings)
|
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@ -46,7 +41,6 @@ namespace Bit.Api.Controllers
|
|||||||
_userService = userService;
|
_userService = userService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_apiSettings = apiSettings.Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
@ -100,7 +94,6 @@ namespace Bit.Api.Controllers
|
|||||||
|
|
||||||
[HttpGet("{id}/billing-invoice/{invoiceId}")]
|
[HttpGet("{id}/billing-invoice/{invoiceId}")]
|
||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
[MiddlewareFilter(typeof(JsReportPipeline))]
|
|
||||||
public async Task<IActionResult> GetBillingInvoice(string id, string invoiceId)
|
public async Task<IActionResult> GetBillingInvoice(string id, string invoiceId)
|
||||||
{
|
{
|
||||||
var orgIdGuid = new Guid(id);
|
var orgIdGuid = new Guid(id);
|
||||||
@ -118,21 +111,15 @@ namespace Bit.Api.Controllers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var invoice = await new StripeInvoiceService().GetAsync(invoiceId);
|
var invoice = await new StripeInvoiceService().GetAsync(invoiceId);
|
||||||
if(invoice == null || invoice.CustomerId != organization.GatewayCustomerId)
|
if(invoice != null && invoice.CustomerId == organization.GatewayCustomerId &&
|
||||||
|
!string.IsNullOrWhiteSpace(invoice.HostedInvoiceUrl))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
return new RedirectResult(invoice.HostedInvoiceUrl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch(StripeException) { }
|
||||||
|
|
||||||
var model = new InvoiceModel(organization, invoice, _apiSettings);
|
throw new NotFoundException();
|
||||||
HttpContext.JsReportFeature().Recipe(Recipe.PhantomPdf)
|
|
||||||
.OnAfterRender((r) => HttpContext.Response.Headers["Content-Disposition"] =
|
|
||||||
$"attachment; filename=\"bitwarden_{model.InvoiceNumber}.pdf\"");
|
|
||||||
return View("Invoice", model);
|
|
||||||
}
|
|
||||||
catch(StripeException)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}/license")]
|
[HttpGet("{id}/license")]
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Bit.Core.Models.Table;
|
|
||||||
using Stripe;
|
|
||||||
|
|
||||||
namespace Bit.Api.Models
|
|
||||||
{
|
|
||||||
public class InvoiceModel
|
|
||||||
{
|
|
||||||
public InvoiceModel(Organization organization, StripeInvoice invoice, ApiSettings apiSettings)
|
|
||||||
{
|
|
||||||
OurAddress1 = apiSettings.OurAddress1;
|
|
||||||
OurAddress2 = apiSettings.OurAddress2;
|
|
||||||
OurAddress3 = apiSettings.OurAddress3;
|
|
||||||
|
|
||||||
CustomerName = organization.BusinessName ?? "--";
|
|
||||||
CustomerAddress1 = organization.BusinessAddress1;
|
|
||||||
CustomerAddress2 = organization.BusinessAddress2;
|
|
||||||
CustomerAddress3 = organization.BusinessAddress3;
|
|
||||||
CustomerCountry = organization.BusinessCountry;
|
|
||||||
CustomerVatNumber = organization.BusinessTaxNumber;
|
|
||||||
|
|
||||||
InvoiceDate = invoice.Date?.ToLongDateString();
|
|
||||||
InvoiceDueDate = invoice.DueDate?.ToLongDateString();
|
|
||||||
InvoiceNumber = invoice.Number;
|
|
||||||
Items = invoice.StripeInvoiceLineItems.Select(i => new Item(i));
|
|
||||||
|
|
||||||
SubtotalAmount = (invoice.Total / 100).ToString("C");
|
|
||||||
VatTotalAmount = 0.ToString("C");
|
|
||||||
TotalAmount = SubtotalAmount;
|
|
||||||
Paid = invoice.Paid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string OurAddress1 { get; set; }
|
|
||||||
public string OurAddress2 { get; set; }
|
|
||||||
public string OurAddress3 { get; set; }
|
|
||||||
public string InvoiceDate { get; set; }
|
|
||||||
public string InvoiceDueDate { get; set; }
|
|
||||||
public string InvoiceNumber { get; set; }
|
|
||||||
public string CustomerName { get; set; }
|
|
||||||
public string CustomerVatNumber { get; set; }
|
|
||||||
public string CustomerAddress1 { get; set; }
|
|
||||||
public string CustomerAddress2 { get; set; }
|
|
||||||
public string CustomerAddress3 { get; set; }
|
|
||||||
public string CustomerCountry { get; set; }
|
|
||||||
public IEnumerable<Item> Items { get; set; }
|
|
||||||
public string SubtotalAmount { get; set; }
|
|
||||||
public string VatTotalAmount { get; set; }
|
|
||||||
public string TotalAmount { get; set; }
|
|
||||||
public bool Paid { get; set; }
|
|
||||||
public bool UsesVat => !string.IsNullOrWhiteSpace(CustomerVatNumber);
|
|
||||||
|
|
||||||
public class Item
|
|
||||||
{
|
|
||||||
public Item(StripeInvoiceLineItem item)
|
|
||||||
{
|
|
||||||
Amount = (item.Amount / 100).ToString("F");
|
|
||||||
if(!string.IsNullOrWhiteSpace(item.Description))
|
|
||||||
{
|
|
||||||
Description = item.Description;
|
|
||||||
}
|
|
||||||
else if(!string.IsNullOrWhiteSpace(item.Plan?.Nickname) && item.Quantity.GetValueOrDefault() > 0)
|
|
||||||
{
|
|
||||||
Description = $"{item.Quantity} x {item.Plan.Nickname}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Description = "--";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Description { get; set; }
|
|
||||||
public string Amount { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,7 +13,6 @@ using Serilog.Events;
|
|||||||
using Stripe;
|
using Stripe;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using IdentityModel;
|
using IdentityModel;
|
||||||
using jsreport.AspNetCore;
|
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
|
||||||
namespace Bit.Api
|
namespace Bit.Api
|
||||||
@ -38,7 +37,6 @@ namespace Bit.Api
|
|||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
var globalSettings = services.AddGlobalSettingsServices(Configuration);
|
var globalSettings = services.AddGlobalSettingsServices(Configuration);
|
||||||
services.Configure<ApiSettings>(Configuration.GetSection("apiSettings"));
|
|
||||||
if(!globalSettings.SelfHosted)
|
if(!globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimitOptions"));
|
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimitOptions"));
|
||||||
@ -115,14 +113,6 @@ namespace Bit.Api
|
|||||||
Jobs.JobsHostedService.AddJobsServices(services);
|
Jobs.JobsHostedService.AddJobsServices(services);
|
||||||
services.AddHostedService<Jobs.JobsHostedService>();
|
services.AddHostedService<Jobs.JobsHostedService>();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// PDF generation
|
|
||||||
services.AddJsReport(new jsreport.Local.LocalReporting()
|
|
||||||
.UseBinary(jsreport.Binary.JsReportBinary.GetBinary())
|
|
||||||
.AsUtility()
|
|
||||||
.Create());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(
|
public void Configure(
|
||||||
|
@ -1,147 +0,0 @@
|
|||||||
@model Bit.Api.Models.InvoiceModel
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5 {
|
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table th, table td {
|
|
||||||
text-align: left;
|
|
||||||
padding: 5px 0;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.items {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border: 1px solid black;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.items th, table.items td {
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid darkgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.paid {
|
|
||||||
margin-top: 50px;
|
|
||||||
font-size: 60px;
|
|
||||||
text-align: center;
|
|
||||||
color: darkgreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
.number {
|
|
||||||
float: right;
|
|
||||||
text-align: right;
|
|
||||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="number">@Model.InvoiceNumber</div>
|
|
||||||
<h1>INVOICE</h1>
|
|
||||||
<p>
|
|
||||||
@if(!string.IsNullOrWhiteSpace(Model.InvoiceDate))
|
|
||||||
{
|
|
||||||
<b>Date:</b> @Model.InvoiceDate<br />
|
|
||||||
}
|
|
||||||
@if(!string.IsNullOrWhiteSpace(Model.InvoiceDueDate))
|
|
||||||
{
|
|
||||||
<b>Due Date:</b> @Model.InvoiceDueDate
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td width="50%">
|
|
||||||
<h2>From</h2>
|
|
||||||
<p>
|
|
||||||
<b>8bit Solutions LLC</b><br />
|
|
||||||
@Model.OurAddress1<br />
|
|
||||||
@Model.OurAddress2<br />
|
|
||||||
@Model.OurAddress3
|
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
<td width="50%">
|
|
||||||
<h2>To</h2>
|
|
||||||
<p>
|
|
||||||
<b>@Model.CustomerName</b><br />
|
|
||||||
@if(!string.IsNullOrWhiteSpace(Model.CustomerAddress1))
|
|
||||||
{
|
|
||||||
@Model.CustomerAddress1<br />
|
|
||||||
}
|
|
||||||
@if(!string.IsNullOrWhiteSpace(Model.CustomerAddress2))
|
|
||||||
{
|
|
||||||
@Model.CustomerAddress2<br />
|
|
||||||
}
|
|
||||||
@if(!string.IsNullOrWhiteSpace(Model.CustomerAddress3))
|
|
||||||
{
|
|
||||||
@Model.CustomerAddress3<br />
|
|
||||||
}
|
|
||||||
@if(!string.IsNullOrWhiteSpace(Model.CustomerCountry))
|
|
||||||
{
|
|
||||||
@Model.CustomerCountry
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
@if(Model.UsesVat)
|
|
||||||
{
|
|
||||||
<p>VAT @Model.CustomerVatNumber</p>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<h2>Items</h2>
|
|
||||||
<table class="items">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Description</th>
|
|
||||||
@if(Model.UsesVat)
|
|
||||||
{
|
|
||||||
<th class="right" style="width: 100px;">VAT %</th>
|
|
||||||
<th class="right" style="width: 100px;">VAT</th>
|
|
||||||
}
|
|
||||||
<th class="right" style="width: 100px;">Total</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach(var item in Model.Items)
|
|
||||||
{
|
|
||||||
<tr>
|
|
||||||
<td>@item.Description</td>
|
|
||||||
@if(Model.UsesVat)
|
|
||||||
{
|
|
||||||
<td class="right">0</td>
|
|
||||||
<td class="right">0.00</td>
|
|
||||||
}
|
|
||||||
<td class="right">@item.Amount</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<h2>Totals</h2>
|
|
||||||
<b>Subtotal:</b> @Model.SubtotalAmount<br />
|
|
||||||
@if(Model.UsesVat)
|
|
||||||
{
|
|
||||||
<b>Total VAT:</b> @Model.VatTotalAmount<br />
|
|
||||||
}
|
|
||||||
<br />
|
|
||||||
<b>Total:</b> USD @Model.TotalAmount
|
|
||||||
@if(Model.Paid)
|
|
||||||
{
|
|
||||||
<div class="paid">PAID</div>
|
|
||||||
}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -64,11 +64,6 @@
|
|||||||
"privateKey": "SECRET"
|
"privateKey": "SECRET"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apiSettings": {
|
|
||||||
"ourAddress1": "123 Green St.",
|
|
||||||
"ourAddress2": "New York, NY 10001",
|
|
||||||
"ourAddress3": "United States"
|
|
||||||
},
|
|
||||||
"IpRateLimitOptions": {
|
"IpRateLimitOptions": {
|
||||||
"EnableEndpointRateLimiting": true,
|
"EnableEndpointRateLimiting": true,
|
||||||
"StackBlockedRequests": false,
|
"StackBlockedRequests": false,
|
||||||
|
Loading…
Reference in New Issue
Block a user