Warenkorb & Bestellungen API
This commit is contained in:
@@ -1,6 +1,88 @@
|
|||||||
namespace ShopAPI.Controllers
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ShopAPI.DTOs;
|
||||||
|
using ShopAPI.Models;
|
||||||
|
using ShopAPI.Services;
|
||||||
|
using System.Security.Claims;
|
||||||
|
namespace ShopAPI.Controllers
|
||||||
{
|
{
|
||||||
public class OrderController
|
[Authorize]
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class OrderController : ControllerBase
|
||||||
{
|
{
|
||||||
|
private readonly OrderService _orderService;
|
||||||
|
|
||||||
|
public OrderController(OrderService orderService)
|
||||||
|
{
|
||||||
|
_orderService = orderService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hilfsmethode - eingeloggten User ID bekommen
|
||||||
|
private int GetUserId() => int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
|
||||||
|
|
||||||
|
// Get api/order (Admin -> alle Bestellungen)
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
public async Task<IActionResult> GetAll()
|
||||||
|
{
|
||||||
|
var orders = await _orderService.GetAllAsync();
|
||||||
|
return Ok(orders);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get api/order/my (Customer -> meine Bestellungen)
|
||||||
|
[HttpGet("my")]
|
||||||
|
public async Task<IActionResult> GetMyOrders()
|
||||||
|
{
|
||||||
|
var orders = await _orderService.GetMyOrderAsync(GetUserId());
|
||||||
|
return Ok(orders);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get api/order/{id}
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> GetById(int id)
|
||||||
|
{
|
||||||
|
var order = await _orderService.GetByIdAsync(id);
|
||||||
|
if(order == null) return NotFound(new {message = "Bestellung nicht gefunden"});
|
||||||
|
return Ok(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Create(OrderDto dto)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var order = await _orderService.CreateAsync(GetUserId(), dto);
|
||||||
|
return CreatedAtAction(nameof(GetById), new { id = order.Id }, order);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(new { message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}/cancel")]
|
||||||
|
public async Task<IActionResult> Cancel(int id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var order = await _orderService.CancelAsync(id, GetUserId());
|
||||||
|
if (order == null) return NotFound(new { message = "Bestellung nicht gefunden" });
|
||||||
|
return Ok(order);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(new { message = ex.Message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}/status")]
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
public async Task<IActionResult> UpdateStatus(int id, UpdateStatusDto dto)
|
||||||
|
{
|
||||||
|
var order = await _orderService.UpdatesStatusAsync(id, dto.Status);
|
||||||
|
if (order == null) return NotFound(new { message = "Bestellung nicht gefunden" });
|
||||||
|
return Ok(order);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,37 @@
|
|||||||
namespace ShopAPI.Controllers
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using ShopAPI.DTOs;
|
||||||
|
using ShopAPI.Services;
|
||||||
|
using System.Security.Claims;
|
||||||
|
namespace ShopAPI.Controllers
|
||||||
{
|
{
|
||||||
public class PaymentController
|
[Authorize]
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class PaymentController : ControllerBase
|
||||||
{
|
{
|
||||||
|
private readonly PaymentService _paymentService;
|
||||||
|
|
||||||
|
public PaymentController(PaymentService paymentService)
|
||||||
|
{
|
||||||
|
_paymentService = paymentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetUserId() => int.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
|
||||||
|
|
||||||
|
// Post api/payment/pay/{orderId} (Bestellung bezahlen)
|
||||||
|
[HttpPost("pay/{orderId}")]
|
||||||
|
public async Task<IActionResult> Pay(int orderId, PaymentDto dto)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var payment = await _paymentService.PayAsync(orderId, GetUserId(), dto);
|
||||||
|
return Ok(payment);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
namespace ShopAPI.DTOs
|
using ShopAPI.Models;
|
||||||
|
|
||||||
|
namespace ShopAPI.DTOs
|
||||||
{
|
{
|
||||||
public class OrderDto
|
public class OrderDto
|
||||||
{
|
{
|
||||||
|
public List<OrderItemDto> Items { get; set; } = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
ShopAPI/ShopAPI/DTOs/OrderItemDto.cs
Normal file
8
ShopAPI/ShopAPI/DTOs/OrderItemDto.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace ShopAPI.DTOs
|
||||||
|
{
|
||||||
|
public class OrderItemDto
|
||||||
|
{
|
||||||
|
public int ProductId { get; set; }
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
7
ShopAPI/ShopAPI/DTOs/PaymentDto.cs
Normal file
7
ShopAPI/ShopAPI/DTOs/PaymentDto.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace ShopAPI.DTOs
|
||||||
|
{
|
||||||
|
public class PaymentDto
|
||||||
|
{
|
||||||
|
public string Method { get; set; } = string.Empty; // "CreditCard", "PayPal", "Cach".
|
||||||
|
}
|
||||||
|
}
|
||||||
7
ShopAPI/ShopAPI/DTOs/UpdateStatusDto.cs
Normal file
7
ShopAPI/ShopAPI/DTOs/UpdateStatusDto.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace ShopAPI.DTOs
|
||||||
|
{
|
||||||
|
public class UpdateStatusDto
|
||||||
|
{
|
||||||
|
public string Status { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,13 +40,19 @@ namespace ShopAPI
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddAuthorization();
|
builder.Services.AddAuthorization();
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers()
|
||||||
|
.AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
options.JsonSerializerOptions.ReferenceHandler =
|
||||||
|
System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
|
||||||
|
});
|
||||||
|
|
||||||
// Hier: jeder benutzer bekommt nur seine Daten , und keine vermischung zwischen Requests
|
// Hier: jeder benutzer bekommt nur seine Daten , und keine vermischung zwischen Requests
|
||||||
builder.Services.AddScoped<AuthService>();
|
builder.Services.AddScoped<AuthService>();
|
||||||
builder.Services.AddScoped<ProductService>();
|
builder.Services.AddScoped<ProductService>();
|
||||||
builder.Services.AddScoped<CategoryService>();
|
builder.Services.AddScoped<CategoryService>();
|
||||||
|
builder.Services.AddScoped<OrderService>();
|
||||||
|
builder.Services.AddScoped<PaymentService>();
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|||||||
@@ -1,6 +1,141 @@
|
|||||||
namespace ShopAPI.Services
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using ShopAPI.Data;
|
||||||
|
using ShopAPI.DTOs;
|
||||||
|
using ShopAPI.Models;
|
||||||
|
namespace ShopAPI.Services
|
||||||
{
|
{
|
||||||
public class OrderService
|
public class OrderService
|
||||||
{
|
{
|
||||||
|
public readonly AppDbContext _db;
|
||||||
|
|
||||||
|
public OrderService(AppDbContext db)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ALL Orders for a User
|
||||||
|
public async Task<List<Order>> GetAllAsync()
|
||||||
|
{
|
||||||
|
return await _db.Orders
|
||||||
|
.Include(o => o.User)
|
||||||
|
.Include(o => o.OrderItems)
|
||||||
|
.ThenInclude(oi => oi.Product)
|
||||||
|
.Include(o => o.Payment)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get My Orders (Customer) : Gib mir alle Bestellungen von diesem User
|
||||||
|
public async Task<List<Order>> GetMyOrderAsync(int userId)
|
||||||
|
{
|
||||||
|
return await _db.Orders
|
||||||
|
.Where(o => o.UserId == userId)
|
||||||
|
.Include(o => o.OrderItems)
|
||||||
|
.ThenInclude(oi => oi.Product)
|
||||||
|
.Include(o => o.Payment)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get By ID : Gib mir Die eine Bestellung mit dieser ID
|
||||||
|
public async Task<Order?> GetByIdAsync(int id)
|
||||||
|
{
|
||||||
|
return await _db.Orders
|
||||||
|
.Include(o => o.User)
|
||||||
|
.Include(o => o.OrderItems)
|
||||||
|
.ThenInclude(oi => oi.Product)
|
||||||
|
.Include(o => o.Payment)
|
||||||
|
.FirstOrDefaultAsync(o => o.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Order
|
||||||
|
public async Task<Order> CreateAsync(int userId, OrderDto dto)
|
||||||
|
{
|
||||||
|
foreach (var item in dto.Items)
|
||||||
|
{
|
||||||
|
var product = await _db.Products.FindAsync(item.ProductId);
|
||||||
|
if (product == null)
|
||||||
|
{
|
||||||
|
throw new Exception($"Produkt {item.ProductId} nciht gefunden");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (product.Stock < item.Quantity)
|
||||||
|
{
|
||||||
|
throw new Exception($"Nicht genung lager für {product.Name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bestellung erstellen
|
||||||
|
var order = new Order
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
Createdat = DateTime.UtcNow,
|
||||||
|
Status = "Pending"
|
||||||
|
};
|
||||||
|
|
||||||
|
_db.Orders.Add(order);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
|
// OrderItems
|
||||||
|
decimal total = 0;
|
||||||
|
foreach (var item in dto.Items)
|
||||||
|
{
|
||||||
|
var product = await _db.Products.FindAsync(item.ProductId);
|
||||||
|
|
||||||
|
var orderItem = new OrderItem
|
||||||
|
{
|
||||||
|
OrderId = order.Id,
|
||||||
|
ProductId = item.ProductId,
|
||||||
|
Quantity = item.Quantity,
|
||||||
|
UnitPrice = product!.Price
|
||||||
|
};
|
||||||
|
|
||||||
|
product.Stock -= item.Quantity; // Lager aktualisieren
|
||||||
|
|
||||||
|
_db.OrderItems.Add(orderItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return await GetByIdAsync(order.Id) ?? order;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel order
|
||||||
|
public async Task<Order?> CancelAsync(int id, int userId)
|
||||||
|
{
|
||||||
|
var order = await _db.Orders
|
||||||
|
.Include(o => o.OrderItems)
|
||||||
|
.FirstOrDefaultAsync(o => o.Id == id && o.UserId == userId);
|
||||||
|
|
||||||
|
if (order == null) return null;
|
||||||
|
if (order.Status == "Shipped")
|
||||||
|
throw new Exception("Versendete Bestellung kann nicht storniert werden");
|
||||||
|
|
||||||
|
// lager zurückgeben
|
||||||
|
foreach (var item in order.OrderItems)
|
||||||
|
{
|
||||||
|
var product = await _db.Products.FindAsync(item.ProductId);
|
||||||
|
if(product != null)
|
||||||
|
{
|
||||||
|
product.Stock += item.Quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
order.Status = "Cancelled";
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return order;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update status (Admin)
|
||||||
|
public async Task<Order?> UpdatesStatusAsync(int id, string status)
|
||||||
|
{
|
||||||
|
var order = await _db.Orders.FindAsync(id);
|
||||||
|
if(order == null) return null;
|
||||||
|
|
||||||
|
order.Status = status;
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
53
ShopAPI/ShopAPI/Services/PaymentService.cs
Normal file
53
ShopAPI/ShopAPI/Services/PaymentService.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using ShopAPI.Data;
|
||||||
|
using ShopAPI.DTOs;
|
||||||
|
using ShopAPI.Models;
|
||||||
|
|
||||||
|
namespace ShopAPI.Services
|
||||||
|
{
|
||||||
|
public class PaymentService
|
||||||
|
{
|
||||||
|
public readonly AppDbContext _db;
|
||||||
|
|
||||||
|
public PaymentService(AppDbContext db)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay Order
|
||||||
|
public async Task<Payment> PayAsync(int orderId, int uderId, PaymentDto dto)
|
||||||
|
{
|
||||||
|
var order = await _db.Orders
|
||||||
|
.Include(o => o.OrderItems)
|
||||||
|
.FirstOrDefaultAsync(o => o.Id == orderId && o.UserId == uderId);
|
||||||
|
|
||||||
|
if(order == null)
|
||||||
|
throw new Exception("Order not found");
|
||||||
|
if(order.Status == "Cancelled")
|
||||||
|
throw new Exception("Stornierte Bestellung kann nciht bezahlt werden");
|
||||||
|
|
||||||
|
// Prüfen ob schon bezahlt
|
||||||
|
var existing = await _db.Payments.FirstOrDefaultAsync(p => p.OrderId == orderId);
|
||||||
|
if(existing != null)
|
||||||
|
throw new Exception("Bestellung wurde bereits bezahlt");
|
||||||
|
|
||||||
|
// Gesamtbetrag berechnen
|
||||||
|
var amount = order.OrderItems.Sum(oi => oi.Quantity * oi.UnitPrice);
|
||||||
|
|
||||||
|
var payment = new Payment
|
||||||
|
{
|
||||||
|
|
||||||
|
OrderId = orderId,
|
||||||
|
Amount = amount,
|
||||||
|
Method = dto.Method,
|
||||||
|
Status = "Paid",
|
||||||
|
PaidAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
order.Status = "Paid";
|
||||||
|
_db.Payments.Add(payment);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
return payment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -15,7 +15,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("ShopAPI")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("ShopAPI")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+384d7e0287f40c1ce9e61d3123377dc5917ef6b6")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d297055cc4ab7e3c8a93d2b98beca4da08aba908")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("ShopAPI")]
|
[assembly: System.Reflection.AssemblyProductAttribute("ShopAPI")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("ShopAPI")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("ShopAPI")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
e04359f0bf9216ce6ea4f06fefbd8b64b5dbf14e78bc9a52d8c538097c953810
|
4ace5e3dd2679b7f8d6ba402a9a151576ba9bd2f4940c60938bf7c3a5fa4b2f7
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
76e1eaf859bca5d672b1fac1789f7abe6bda99f7cc154bb78899e5780554f7a0
|
4ae0a6b08c2111e583c69b63b26d342f1e34844cf7b3ca861744608079c9e142
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"UbUS1vR6wGATGwMhgD7zMeswN1vTEJ19YTFurt+qa38=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["qXK9XBukmOKR4bC0/OSWUQmD7NVDnODrTNbVfODoSOA=","RsfaIcSdxUsyxkHT0X7TBDvWAE\u002BekQ7N\u002BCKYPxhpGog=","q4x5tw5ZdEVI3wN3ORljilxzvA3pVT3snlXNH\u002Bh3MZw=","hqxvnAagyda0d6U9WqiUEHNFsi/9qmMo9aedinq8ZQA=","TRUWKVmfrqI3Da5OlFteES1pBI/d\u002Bef88\u002BcbmmwH4mw=","AMewKZACPMF34A9M/c\u002BnjYkuddQPOVXhAdpXf\u002BloS2s=","GrwxjTxxr29ICyUcOpweOHua4V5l\u002BtKseEVsI30NOeU="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"UbUS1vR6wGATGwMhgD7zMeswN1vTEJ19YTFurt+qa38=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["qXK9XBukmOKR4bC0/OSWUQmD7NVDnODrTNbVfODoSOA=","RsfaIcSdxUsyxkHT0X7TBDvWAE\u002BekQ7N\u002BCKYPxhpGog=","mwxpKH5s5WhH59E7QrRxfDY8/18lLYY3KNsyd1uFNcs=","q4x5tw5ZdEVI3wN3ORljilxzvA3pVT3snlXNH\u002Bh3MZw=","hqxvnAagyda0d6U9WqiUEHNFsi/9qmMo9aedinq8ZQA=","TRUWKVmfrqI3Da5OlFteES1pBI/d\u002Bef88\u002BcbmmwH4mw=","AMewKZACPMF34A9M/c\u002BnjYkuddQPOVXhAdpXf\u002BloS2s=","jLjjNkMAcR6ICPvLUJeO8hvccCstVj2PSPKA7Fc04j0="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"8Bes/LQh4hq2du9moDLYcP3dWOcm8118awBX2grZ2jA=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["qXK9XBukmOKR4bC0/OSWUQmD7NVDnODrTNbVfODoSOA=","RsfaIcSdxUsyxkHT0X7TBDvWAE\u002BekQ7N\u002BCKYPxhpGog=","q4x5tw5ZdEVI3wN3ORljilxzvA3pVT3snlXNH\u002Bh3MZw=","hqxvnAagyda0d6U9WqiUEHNFsi/9qmMo9aedinq8ZQA=","TRUWKVmfrqI3Da5OlFteES1pBI/d\u002Bef88\u002BcbmmwH4mw=","AMewKZACPMF34A9M/c\u002BnjYkuddQPOVXhAdpXf\u002BloS2s=","GrwxjTxxr29ICyUcOpweOHua4V5l\u002BtKseEVsI30NOeU="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"8Bes/LQh4hq2du9moDLYcP3dWOcm8118awBX2grZ2jA=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["qXK9XBukmOKR4bC0/OSWUQmD7NVDnODrTNbVfODoSOA=","RsfaIcSdxUsyxkHT0X7TBDvWAE\u002BekQ7N\u002BCKYPxhpGog=","mwxpKH5s5WhH59E7QrRxfDY8/18lLYY3KNsyd1uFNcs=","q4x5tw5ZdEVI3wN3ORljilxzvA3pVT3snlXNH\u002Bh3MZw=","hqxvnAagyda0d6U9WqiUEHNFsi/9qmMo9aedinq8ZQA=","TRUWKVmfrqI3Da5OlFteES1pBI/d\u002Bef88\u002BcbmmwH4mw=","AMewKZACPMF34A9M/c\u002BnjYkuddQPOVXhAdpXf\u002BloS2s=","jLjjNkMAcR6ICPvLUJeO8hvccCstVj2PSPKA7Fc04j0="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
Reference in New Issue
Block a user