mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-19 05:33:31 +01:00
use more concrete types for price type in app items
This commit is contained in:
parent
33a893ba31
commit
9592a77cff
@ -146,7 +146,7 @@ namespace BTCPayServer.Controllers
|
||||
if (choice == null)
|
||||
return NotFound();
|
||||
title = choice.Title;
|
||||
if (choice.Custom == "topup")
|
||||
if (choice.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup)
|
||||
{
|
||||
price = null;
|
||||
}
|
||||
@ -323,7 +323,7 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound("Incorrect option provided");
|
||||
title = choice.Title;
|
||||
|
||||
if (choice.Custom == "topup")
|
||||
if (choice.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup)
|
||||
{
|
||||
price = null;
|
||||
}
|
||||
|
@ -6,14 +6,18 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Fido2;
|
||||
using BTCPayServer.Fido2.Models;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Models.AppViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using ExchangeSharp;
|
||||
using Fido2NetLib.Objects;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -31,6 +35,7 @@ namespace BTCPayServer.Hosting
|
||||
private readonly StoreRepository _StoreRepository;
|
||||
private readonly BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly SettingsRepository _Settings;
|
||||
private readonly AppService _appService;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public IOptions<LightningNetworkOptions> LightningOptions { get; }
|
||||
@ -41,12 +46,14 @@ namespace BTCPayServer.Hosting
|
||||
ApplicationDbContextFactory dbContextFactory,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IOptions<LightningNetworkOptions> lightningOptions,
|
||||
SettingsRepository settingsRepository)
|
||||
SettingsRepository settingsRepository,
|
||||
AppService appService)
|
||||
{
|
||||
_DBContextFactory = dbContextFactory;
|
||||
_StoreRepository = storeRepository;
|
||||
_NetworkProvider = networkProvider;
|
||||
_Settings = settingsRepository;
|
||||
_appService = appService;
|
||||
_userManager = userManager;
|
||||
LightningOptions = lightningOptions;
|
||||
}
|
||||
@ -134,6 +141,12 @@ namespace BTCPayServer.Hosting
|
||||
settings.MigrateHotwalletProperty = true;
|
||||
await _Settings.UpdateSetting(settings);
|
||||
}
|
||||
if (!settings.MigrateAppCustomOption)
|
||||
{
|
||||
await MigrateAppCustomOption();
|
||||
settings.MigrateAppCustomOption = true;
|
||||
await _Settings.UpdateSetting(settings);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -142,6 +155,42 @@ namespace BTCPayServer.Hosting
|
||||
}
|
||||
}
|
||||
|
||||
private async Task MigrateAppCustomOption()
|
||||
{
|
||||
await using var ctx = _DBContextFactory.CreateContext();
|
||||
foreach (var app in await ctx.Apps.AsQueryable().ToArrayAsync())
|
||||
{
|
||||
ViewPointOfSaleViewModel.Item[] items;
|
||||
string newTemplate;
|
||||
switch (app.AppType)
|
||||
{
|
||||
case nameof(AppType.Crowdfund):
|
||||
var settings1 = app.GetSettings<CrowdfundSettings>();
|
||||
items = _appService.Parse(settings1.PerksTemplate, settings1.TargetCurrency);
|
||||
newTemplate = _appService.SerializeTemplate(items);
|
||||
if (settings1.PerksTemplate != newTemplate)
|
||||
{
|
||||
settings1.PerksTemplate = newTemplate;
|
||||
app.SetSettings(settings1);
|
||||
};
|
||||
break;
|
||||
|
||||
case nameof(AppType.PointOfSale):
|
||||
|
||||
var settings2 = app.GetSettings<AppsController.PointOfSaleSettings>();
|
||||
items = _appService.Parse(settings2.Template, settings2.Currency);
|
||||
newTemplate = _appService.SerializeTemplate(items);
|
||||
if (settings2.Template != newTemplate)
|
||||
{
|
||||
settings2.Template = newTemplate;
|
||||
app.SetSettings(settings2);
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task MigrateHotwalletProperty()
|
||||
{
|
||||
await using var ctx = _DBContextFactory.CreateContext();
|
||||
|
@ -9,15 +9,22 @@ namespace BTCPayServer.Models.AppViewModels
|
||||
{
|
||||
public class ItemPrice
|
||||
{
|
||||
public enum ItemPriceType
|
||||
{
|
||||
Topup,
|
||||
Minimum,
|
||||
Fixed
|
||||
}
|
||||
|
||||
public ItemPriceType Type { get; set; }
|
||||
public string Formatted { get; set; }
|
||||
public decimal Value { get; set; }
|
||||
public decimal? Value { get; set; }
|
||||
}
|
||||
public string Description { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Image { get; set; }
|
||||
public ItemPrice Price { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Custom { get; set; }
|
||||
public string BuyButtonText { get; set; }
|
||||
public int? Inventory { get; set; } = null;
|
||||
public string[] PaymentMethods { get; set; }
|
||||
|
@ -289,7 +289,7 @@ namespace BTCPayServer.Services.Apps
|
||||
{
|
||||
var itemNode = new YamlMappingNode();
|
||||
itemNode.Add("title", new YamlScalarNode(item.Title));
|
||||
if(item.Custom!= "topup")
|
||||
if(item.Price.Type!= ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup)
|
||||
itemNode.Add("price", new YamlScalarNode(item.Price.Value.ToStringInvariant()));
|
||||
if (!string.IsNullOrEmpty(item.Description))
|
||||
{
|
||||
@ -302,7 +302,7 @@ namespace BTCPayServer.Services.Apps
|
||||
{
|
||||
itemNode.Add("image", new YamlScalarNode(item.Image));
|
||||
}
|
||||
itemNode.Add("custom", new YamlScalarNode(item.Custom.ToStringLowerInvariant()));
|
||||
itemNode.Add("price_type", new YamlScalarNode(item.Price.Type.ToStringLowerInvariant()));
|
||||
itemNode.Add("disabled", new YamlScalarNode(item.Disabled.ToStringLowerInvariant()));
|
||||
if (item.Inventory.HasValue)
|
||||
{
|
||||
@ -336,26 +336,50 @@ namespace BTCPayServer.Services.Apps
|
||||
.Children
|
||||
.Select(kv => new PosHolder(_HtmlSanitizer) { Key = _HtmlSanitizer.Sanitize((kv.Key as YamlScalarNode)?.Value), Value = kv.Value as YamlMappingNode })
|
||||
.Where(kv => kv.Value != null)
|
||||
.Select(c => new ViewPointOfSaleViewModel.Item()
|
||||
.Select(c =>
|
||||
{
|
||||
Description = c.GetDetailString("description"),
|
||||
Id = c.Key,
|
||||
Image = c.GetDetailString("image"),
|
||||
Title = c.GetDetailString("title") ?? c.Key,
|
||||
Custom = c.GetDetailString("custom"),
|
||||
Price =
|
||||
c.GetDetailString("custom") == "topup"
|
||||
? null
|
||||
: c.GetDetail("price")
|
||||
.Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice()
|
||||
{
|
||||
Value = decimal.Parse(cc.Value.Value, CultureInfo.InvariantCulture),
|
||||
Formatted = Currencies.FormatCurrency(cc.Value.Value, currency)
|
||||
}).Single(),
|
||||
BuyButtonText = c.GetDetailString("buyButtonText"),
|
||||
Inventory = string.IsNullOrEmpty(c.GetDetailString("inventory")) ? (int?)null : int.Parse(c.GetDetailString("inventory"), CultureInfo.InvariantCulture),
|
||||
PaymentMethods = c.GetDetailStringList("payment_methods"),
|
||||
Disabled = c.GetDetailString("disabled") == "true"
|
||||
ViewPointOfSaleViewModel.Item.ItemPrice price = new ViewPointOfSaleViewModel.Item.ItemPrice();
|
||||
var pValue = c.GetDetail("price")?.FirstOrDefault();
|
||||
|
||||
switch (c.GetDetailString("custom")??c.GetDetailString("price_type")?.ToLowerInvariant())
|
||||
{
|
||||
case "topup":
|
||||
case null when pValue is null:
|
||||
price.Type = ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup;
|
||||
break;
|
||||
case "true":
|
||||
case "minimum":
|
||||
price.Type = ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Minimum;
|
||||
if (pValue != null)
|
||||
{
|
||||
price.Value = decimal.Parse(pValue.Value.Value, CultureInfo.InvariantCulture);
|
||||
price.Formatted = Currencies.FormatCurrency(pValue.Value.Value, currency);
|
||||
}
|
||||
break;
|
||||
case "fixed":
|
||||
case "false":
|
||||
case null:
|
||||
price.Type = ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed;
|
||||
price.Value = decimal.Parse(pValue.Value.Value, CultureInfo.InvariantCulture);
|
||||
price.Formatted = Currencies.FormatCurrency(pValue.Value.Value, currency);
|
||||
break;
|
||||
}
|
||||
|
||||
return new ViewPointOfSaleViewModel.Item()
|
||||
{
|
||||
Description = c.GetDetailString("description"),
|
||||
Id = c.Key,
|
||||
Image = c.GetDetailString("image"),
|
||||
Title = c.GetDetailString("title") ?? c.Key,
|
||||
Price = price,
|
||||
BuyButtonText = c.GetDetailString("buyButtonText"),
|
||||
Inventory =
|
||||
string.IsNullOrEmpty(c.GetDetailString("inventory"))
|
||||
? (int?)null
|
||||
: int.Parse(c.GetDetailString("inventory"), CultureInfo.InvariantCulture),
|
||||
PaymentMethods = c.GetDetailStringList("payment_methods"),
|
||||
Disabled = c.GetDetailString("disabled") == "true"
|
||||
};
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
@ -25,5 +25,6 @@ namespace BTCPayServer.Services
|
||||
|
||||
// Done in DbMigrationsHostedService
|
||||
public int? MigratedInvoiceTextSearchPages { get; set; }
|
||||
public bool MigrateAppCustomOption { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,8 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-3" v-show="editingItem.custom !== 'topup'">
|
||||
<label class="form-label" data-required>Price</label>
|
||||
|
||||
<label class="form-label"> </label>
|
||||
<input class="form-control mb-2"
|
||||
inputmode="numeric"
|
||||
pattern="\d*"
|
||||
@ -121,8 +122,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
items: [],
|
||||
editingItem: null,
|
||||
customPriceOptions: [
|
||||
{ text: 'Fixed', value: false },
|
||||
{ text: 'Minimum', value: true },
|
||||
{ text: 'Fixed', value: "fixed" },
|
||||
{ text: 'Minimum', value: "minimum" },
|
||||
{ text: 'Topup', value: 'topup' },
|
||||
],
|
||||
elementId: "@Model.templateId"
|
||||
@ -207,8 +208,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
if (productProperty.indexOf('image:') !== -1) {
|
||||
image = productProperty.replace('image:', '').trim();
|
||||
}
|
||||
if (productProperty.indexOf('custom:') !== -1) {
|
||||
custom = productProperty.replace('custom:', '').trim();
|
||||
if (productProperty.indexOf('price_type:') !== -1) {
|
||||
custom = productProperty.replace('price_type:', '').trim();
|
||||
}
|
||||
if (productProperty.indexOf('buyButtonText:') !== -1) {
|
||||
buyButtonText = productProperty.replace('buyButtonText:', '').trim();
|
||||
@ -232,7 +233,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
price: price,
|
||||
image: image || null,
|
||||
description: description || '',
|
||||
custom: custom === "topup"? "topup": custom === "true",
|
||||
custom: custom,
|
||||
buyButtonText: buyButtonText,
|
||||
inventory: isNaN(inventory)? null: inventory,
|
||||
paymentMethods: paymentMethods,
|
||||
@ -271,7 +272,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
itemTemplate += ' inventory: ' + inventory + '\n';
|
||||
}
|
||||
if (custom != null) {
|
||||
itemTemplate += ' custom: ' + (custom === "topup"? '"topup"': custom) + '\n';
|
||||
itemTemplate += ' price_type: "' + custom + '" \n';
|
||||
}
|
||||
if (buyButtonText != null && buyButtonText.length > 0) {
|
||||
itemTemplate += ' buyButtonText: ' + buyButtonText + '\n';
|
||||
@ -293,7 +294,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
editItem: function(index){
|
||||
this.errors = [];
|
||||
if(index < 0){
|
||||
this.editingItem = {index:-1, id:"", title: "", price: 0, image: "", description: "", custom: false, inventory: null, paymentMethods: [], disabled: false};
|
||||
this.editingItem = {index:-1, id:"", title: "", price: 0, image: "", description: "", custom: "fixed", inventory: null, paymentMethods: [], disabled: false};
|
||||
}else{
|
||||
this.editingItem = {...this.items[index], index};
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
@using BTCPayServer.Models.AppViewModels
|
||||
@model BTCPayServer.Models.AppViewModels.ContributeToCrowdfund
|
||||
|
||||
@{ var vm = Model.ViewCrowdfundViewModel; }
|
||||
@ -26,20 +27,20 @@
|
||||
@(string.IsNullOrEmpty(item.Title) ? item.Id : item.Title)
|
||||
</label>
|
||||
<span class="text-muted">
|
||||
@if (item.Price?.Value > 0)
|
||||
@if (item.Price.Value > 0)
|
||||
{
|
||||
<span>@item.Price.Value</span>
|
||||
<span>@vm.TargetCurrency</span>
|
||||
|
||||
if (item.Custom == "true")
|
||||
if (item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Minimum)
|
||||
{
|
||||
@Safe.Raw("or more")
|
||||
}
|
||||
}
|
||||
else if (item.Custom == "topup" || item.Custom == "true" )
|
||||
else if (item.Price.Type != ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed )
|
||||
{
|
||||
@Safe.Raw("Any amount")
|
||||
}else if (item.Custom == "false")
|
||||
}else if (item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed)
|
||||
{
|
||||
@Safe.Raw("Free")
|
||||
}
|
||||
@ -54,7 +55,7 @@
|
||||
{
|
||||
case null:
|
||||
break;
|
||||
case int i when i <= 0:
|
||||
case var i when i <= 0:
|
||||
<span>Sold out</span>
|
||||
break;
|
||||
default:
|
||||
|
@ -1,3 +1,4 @@
|
||||
@using BTCPayServer.Models.AppViewModels
|
||||
@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel
|
||||
@{
|
||||
Layout = "_LayoutPos";
|
||||
@ -232,8 +233,8 @@
|
||||
|
||||
<span class="text-muted small">
|
||||
@{
|
||||
var buttonText = string.IsNullOrEmpty(item.BuyButtonText) ? (item.Custom == "true" || item.Custom == "topup") ? Model.CustomButtonText : Model.ButtonText : item.BuyButtonText;
|
||||
if (item.Custom != "topup")
|
||||
var buttonText = string.IsNullOrEmpty(item.BuyButtonText) ? item.Price.Type != ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed ? Model.CustomButtonText : Model.ButtonText : item.BuyButtonText;
|
||||
if (item.Price.Type != ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Topup)
|
||||
{
|
||||
buttonText = buttonText.Replace("{0}",item.Price.Formatted)
|
||||
?.Replace("{Price}",item.Price.Formatted);
|
||||
|
@ -1,3 +1,4 @@
|
||||
@using BTCPayServer.Models.AppViewModels
|
||||
@model BTCPayServer.Models.AppViewModels.ViewPointOfSaleViewModel
|
||||
@{
|
||||
Layout = "_LayoutPos";
|
||||
@ -19,12 +20,9 @@
|
||||
@for (int x = 0; x < Model.Items.Length; x++)
|
||||
{
|
||||
var item = Model.Items[x];
|
||||
var buttonText = string.IsNullOrEmpty(item.BuyButtonText) ? (item.Custom == "true" || item.Custom == "topup") ? Model.CustomButtonText : Model.ButtonText : item.BuyButtonText;
|
||||
if (item.Custom != "topup")
|
||||
{
|
||||
buttonText = buttonText.Replace("{0}",item.Price.Formatted)
|
||||
?.Replace("{Price}",item.Price.Formatted);
|
||||
}
|
||||
var buttonText = string.IsNullOrEmpty(item.BuyButtonText) ? item.Price.Type != ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed ? Model.CustomButtonText : Model.ButtonText : item.BuyButtonText;
|
||||
buttonText = buttonText.Replace("{0}",item.Price.Formatted)
|
||||
?.Replace("{Price}",item.Price.Formatted);
|
||||
|
||||
<div class="card px-0" data-id="@x">
|
||||
@if (!String.IsNullOrWhiteSpace(item.Image))
|
||||
@ -35,7 +33,7 @@
|
||||
<div class="card-footer bg-transparent border-0 pb-3">
|
||||
@if (!item.Inventory.HasValue || item.Inventory.Value > 0)
|
||||
{
|
||||
@if (item.Custom == "true")
|
||||
@if (item.Price.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Fixed)
|
||||
{
|
||||
<form method="post" asp-controller="AppsPublic" asp-action="ViewPointOfSale" asp-route-appId="@Model.AppId" asp-antiforgery="false" data-buy>
|
||||
<input type="hidden" name="choicekey" value="@item.Id"/>
|
||||
|
@ -112,7 +112,7 @@ Cart.prototype.getTotalProducts = function() {
|
||||
typeof this.content[key] != 'undefined' &&
|
||||
!this.content[key].disabled
|
||||
) {
|
||||
var price = this.toCents(this.content[key].price?.value??0);
|
||||
const price = this.toCents(this.content[key].price.value ||0);
|
||||
amount += (this.content[key].count * price);
|
||||
}
|
||||
}
|
||||
@ -439,7 +439,7 @@ Cart.prototype.listItems = function() {
|
||||
'title': this.escape(item.title),
|
||||
'count': this.escape(item.count),
|
||||
'inventory': this.escape(item.inventory < 0? 99999: item.inventory),
|
||||
'price': this.escape(item.price?.formatted??0)
|
||||
'price': this.escape(item.price.formatted || 0)
|
||||
});
|
||||
list.push($(tableTemplate));
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ document.addEventListener("DOMContentLoaded",function (ev) {
|
||||
},
|
||||
computed: {
|
||||
canExpand: function(){
|
||||
return !this.expanded && this.active && (this.perk.custom=="topup" || this.perk.price.value || this.perk.custom == "true") && (this.perk.inventory==null || this.perk.inventory > 0)
|
||||
return !this.expanded && this.active && (this.perk.custom !== "fixed" || this.perk.price.value) && (this.perk.inventory==null || this.perk.inventory > 0)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@ -45,14 +45,14 @@ document.addEventListener("DOMContentLoaded",function (ev) {
|
||||
}
|
||||
},
|
||||
setAmount: function (amount) {
|
||||
this.amount = this.perk.custom == "topup"? null : (amount || 0).noExponents();
|
||||
this.amount = this.perk.custom === "topup"? null : (amount || 0).noExponents();
|
||||
this.expanded = false;
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
mounted: function () {
|
||||
this.setAmount(this.perk.price?.value);
|
||||
this.setAmount(this.perk.price.value);
|
||||
},
|
||||
watch: {
|
||||
perk: function (newValue, oldValue) {
|
||||
|
Loading…
Reference in New Issue
Block a user