mirror of
https://github.com/D4M13N-D3V/art_platform.git
synced 2025-03-14 07:44:54 +00:00
payment platform
This commit is contained in:
parent
f772acac77
commit
c45c474266
@ -14,6 +14,7 @@
|
|||||||
<PackageReference Include="Auth0.AspNetCore.Authentication" Version="1.4.1" />
|
<PackageReference Include="Auth0.AspNetCore.Authentication" Version="1.4.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1"/>
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1"/>
|
||||||
|
<PackageReference Include="Stripe.net" Version="43.12.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -15,11 +15,11 @@ namespace ArtPlatform.API.Controllers;
|
|||||||
public class OrderController : Controller
|
public class OrderController : Controller
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _dbContext;
|
private readonly ApplicationDbContext _dbContext;
|
||||||
private readonly IStorage _storage;
|
private readonly IStorageService _storageService;
|
||||||
|
|
||||||
public OrderController(ApplicationDbContext dbContext, IStorage storage)
|
public OrderController(ApplicationDbContext dbContext, IStorageService storageService)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_storageService = storageService;
|
||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ public class OrderController : Controller
|
|||||||
if(message==null)
|
if(message==null)
|
||||||
return BadRequest("Message does not exist or does not belong to this order.");
|
return BadRequest("Message does not exist or does not belong to this order.");
|
||||||
|
|
||||||
var url = await _storage.UploadImageAsync(file, Guid.NewGuid().ToString());
|
var url = await _storageService.UploadImageAsync(file, Guid.NewGuid().ToString());
|
||||||
var attachment = new SellerServiceOrderMessageAttachment()
|
var attachment = new SellerServiceOrderMessageAttachment()
|
||||||
{
|
{
|
||||||
SellerServiceOrderMessageId = message.Id,
|
SellerServiceOrderMessageId = message.Id,
|
||||||
@ -326,7 +326,7 @@ public class OrderController : Controller
|
|||||||
if(message==null)
|
if(message==null)
|
||||||
return BadRequest("Message does not exist or does not belong to this order.");
|
return BadRequest("Message does not exist or does not belong to this order.");
|
||||||
|
|
||||||
var content = await _storage.DownloadImageAsync(message.Attachments.First().FileReference);
|
var content = await _storageService.DownloadImageAsync(message.Attachments.First().FileReference);
|
||||||
return new FileStreamResult(content, "application/octet-stream");
|
return new FileStreamResult(content, "application/octet-stream");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@ namespace ArtPlatform.API.Controllers;
|
|||||||
public class SellerOrderController : Controller
|
public class SellerOrderController : Controller
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _dbContext;
|
private readonly ApplicationDbContext _dbContext;
|
||||||
private readonly IStorage _storage;
|
private readonly IStorageService _storageService;
|
||||||
|
|
||||||
public SellerOrderController(IStorage storage, ApplicationDbContext dbContext)
|
public SellerOrderController(IStorageService storageService, ApplicationDbContext dbContext)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_storageService = storageService;
|
||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ public class SellerOrderController : Controller
|
|||||||
if(message==null)
|
if(message==null)
|
||||||
return BadRequest("Message does not exist or does not belong to this order.");
|
return BadRequest("Message does not exist or does not belong to this order.");
|
||||||
|
|
||||||
var url = await _storage.UploadImageAsync(file, Guid.NewGuid().ToString());
|
var url = await _storageService.UploadImageAsync(file, Guid.NewGuid().ToString());
|
||||||
var attachment = new SellerServiceOrderMessageAttachment()
|
var attachment = new SellerServiceOrderMessageAttachment()
|
||||||
{
|
{
|
||||||
SellerServiceOrderMessageId = message.Id,
|
SellerServiceOrderMessageId = message.Id,
|
||||||
@ -279,7 +279,7 @@ public class SellerOrderController : Controller
|
|||||||
var attachment = message.Attachments.FirstOrDefault();
|
var attachment = message.Attachments.FirstOrDefault();
|
||||||
if(attachment==null)
|
if(attachment==null)
|
||||||
return BadRequest("Message does not have an attachment.");
|
return BadRequest("Message does not have an attachment.");
|
||||||
var content = await _storage.DownloadImageAsync(message.Attachments.First().FileReference);
|
var content = await _storageService.DownloadImageAsync(message.Attachments.First().FileReference);
|
||||||
return new FileStreamResult(content, "application/octet-stream");
|
return new FileStreamResult(content, "application/octet-stream");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using ArtPlatform.API.Extensions;
|
using ArtPlatform.API.Extensions;
|
||||||
using ArtPlatform.API.Models.PortfolioModel;
|
using ArtPlatform.API.Models.PortfolioModel;
|
||||||
using ArtPlatform.API.Models.SellerProfile;
|
using ArtPlatform.API.Models.SellerProfile;
|
||||||
|
using ArtPlatform.API.Services.Payment;
|
||||||
using ArtPlatform.API.Services.Storage;
|
using ArtPlatform.API.Services.Storage;
|
||||||
using ArtPlatform.Database;
|
using ArtPlatform.Database;
|
||||||
using ArtPlatform.Database.Entities;
|
using ArtPlatform.Database.Entities;
|
||||||
@ -15,12 +16,14 @@ namespace ArtPlatform.API.Controllers;
|
|||||||
public class SellerProfileController : Controller
|
public class SellerProfileController : Controller
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _dbContext;
|
private readonly ApplicationDbContext _dbContext;
|
||||||
private readonly IStorage _storage;
|
private readonly IStorageService _storageService;
|
||||||
|
private readonly IPaymentService _paymentService;
|
||||||
|
|
||||||
|
|
||||||
public SellerProfileController(ApplicationDbContext dbContext, IStorage storage)
|
public SellerProfileController(ApplicationDbContext dbContext, IPaymentService paymentService, IStorageService storageService)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_paymentService = paymentService;
|
||||||
|
_storageService = storageService;
|
||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +107,7 @@ public class SellerProfileController : Controller
|
|||||||
|
|
||||||
var portfolio = await _dbContext.SellerProfilePortfolioPieces
|
var portfolio = await _dbContext.SellerProfilePortfolioPieces
|
||||||
.FirstAsync(x => x.SellerProfileId == existingSellerProfile.Id && x.Id==portfolioId);
|
.FirstAsync(x => x.SellerProfileId == existingSellerProfile.Id && x.Id==portfolioId);
|
||||||
var content = await _storage.DownloadImageAsync(portfolio.FileReference);
|
var content = await _storageService.DownloadImageAsync(portfolio.FileReference);
|
||||||
return new FileStreamResult(content, "application/octet-stream");
|
return new FileStreamResult(content, "application/octet-stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +145,7 @@ public class SellerProfileController : Controller
|
|||||||
return Unauthorized("Account is not a seller.");
|
return Unauthorized("Account is not a seller.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = await _storage.UploadImageAsync(file, Guid.NewGuid().ToString());
|
var url = await _storageService.UploadImageAsync(file, Guid.NewGuid().ToString());
|
||||||
var portfolio = new SellerProfilePortfolioPiece()
|
var portfolio = new SellerProfilePortfolioPiece()
|
||||||
{
|
{
|
||||||
SellerProfileId = existingSellerProfile.Id,
|
SellerProfileId = existingSellerProfile.Id,
|
||||||
@ -178,4 +181,48 @@ public class SellerProfileController : Controller
|
|||||||
await _dbContext.SaveChangesAsync();
|
await _dbContext.SaveChangesAsync();
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize("write:seller-profile")]
|
||||||
|
[Route("Payment")]
|
||||||
|
public async Task<IActionResult> CreatePaymentAccount()
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
var existingSellerProfile = await _dbContext.UserSellerProfiles.FirstOrDefaultAsync(sellerProfile=>sellerProfile.UserId==userId);
|
||||||
|
if (existingSellerProfile == null)
|
||||||
|
{
|
||||||
|
var sellerProfileRequest = await _dbContext.SellerProfileRequests.FirstOrDefaultAsync(request=>request.UserId==userId && request.Accepted==false);
|
||||||
|
if(sellerProfileRequest!=null)
|
||||||
|
return BadRequest("Account has requested to be a seller and not been approved yet.");
|
||||||
|
return Unauthorized("Account is not a seller.");
|
||||||
|
}
|
||||||
|
if(existingSellerProfile.StripeAccountId!=null)
|
||||||
|
return BadRequest("Account already has a payment account.");
|
||||||
|
var accountId = _paymentService.CreateSellerAccount();
|
||||||
|
existingSellerProfile.StripeAccountId = accountId;
|
||||||
|
existingSellerProfile = _dbContext.UserSellerProfiles.Update(existingSellerProfile).Entity;
|
||||||
|
await _dbContext.SaveChangesAsync();
|
||||||
|
var result = existingSellerProfile.ToModel();
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize("write:seller-profile")]
|
||||||
|
[Route("Payment")]
|
||||||
|
public async Task<IActionResult> GetPaymentAccount()
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
var existingSellerProfile = await _dbContext.UserSellerProfiles.FirstOrDefaultAsync(sellerProfile=>sellerProfile.UserId==userId);
|
||||||
|
if (existingSellerProfile == null)
|
||||||
|
{
|
||||||
|
var sellerProfileRequest = await _dbContext.SellerProfileRequests.FirstOrDefaultAsync(request=>request.UserId==userId && request.Accepted==false);
|
||||||
|
if(sellerProfileRequest!=null)
|
||||||
|
return BadRequest("Account has requested to be a seller and not been approved yet.");
|
||||||
|
return Unauthorized("Account is not a seller.");
|
||||||
|
}
|
||||||
|
if(existingSellerProfile.StripeAccountId==null)
|
||||||
|
return BadRequest("Account does not have a payment account.");
|
||||||
|
var result = _paymentService.CreateSellerAccountOnboardingUrl(existingSellerProfile.StripeAccountId);
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
@ -15,11 +15,11 @@ namespace ArtPlatform.API.Controllers;
|
|||||||
public class SellerServiceController : Controller
|
public class SellerServiceController : Controller
|
||||||
{
|
{
|
||||||
private readonly ApplicationDbContext _dbContext;
|
private readonly ApplicationDbContext _dbContext;
|
||||||
private readonly IStorage _storage;
|
private readonly IStorageService _storageService;
|
||||||
|
|
||||||
public SellerServiceController(ApplicationDbContext dbContext, IStorage storage)
|
public SellerServiceController(ApplicationDbContext dbContext, IStorageService storageService)
|
||||||
{
|
{
|
||||||
_storage = storage;
|
_storageService = storageService;
|
||||||
_dbContext = dbContext;
|
_dbContext = dbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ public class SellerServiceController : Controller
|
|||||||
var portfolio = await _dbContext.SellerProfilePortfolioPieces
|
var portfolio = await _dbContext.SellerProfilePortfolioPieces
|
||||||
.FirstAsync(x => x.SellerProfileId == existingSellerProfile.Id
|
.FirstAsync(x => x.SellerProfileId == existingSellerProfile.Id
|
||||||
&& x.SellerServiceId == sellerServiceId && x.Id==portfolioId);
|
&& x.SellerServiceId == sellerServiceId && x.Id==portfolioId);
|
||||||
var content = await _storage.DownloadImageAsync(portfolio.FileReference);
|
var content = await _storageService.DownloadImageAsync(portfolio.FileReference);
|
||||||
return new FileStreamResult(content, "application/octet-stream");
|
return new FileStreamResult(content, "application/octet-stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +181,7 @@ public class SellerServiceController : Controller
|
|||||||
return Unauthorized("Account is not a seller.");
|
return Unauthorized("Account is not a seller.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = await _storage.UploadImageAsync(file, Guid.NewGuid().ToString());
|
var url = await _storageService.UploadImageAsync(file, Guid.NewGuid().ToString());
|
||||||
var portfolio = new SellerProfilePortfolioPiece()
|
var portfolio = new SellerProfilePortfolioPiece()
|
||||||
{
|
{
|
||||||
SellerProfileId = existingSellerProfile.Id,
|
SellerProfileId = existingSellerProfile.Id,
|
||||||
|
@ -2,6 +2,7 @@ using System.Reflection;
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using ArtPlatform.API.Middleware;
|
using ArtPlatform.API.Middleware;
|
||||||
using ArtPlatform.API.Middleware.Authentication;
|
using ArtPlatform.API.Middleware.Authentication;
|
||||||
|
using ArtPlatform.API.Services.Payment;
|
||||||
using ArtPlatform.API.Services.Storage;
|
using ArtPlatform.API.Services.Storage;
|
||||||
using ArtPlatform.Database;
|
using ArtPlatform.Database;
|
||||||
using Auth0.AspNetCore.Authentication;
|
using Auth0.AspNetCore.Authentication;
|
||||||
@ -15,7 +16,8 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
|
||||||
builder.Services.AddSingleton<IStorage,ImgCdnProvider>();
|
builder.Services.AddSingleton<IStorageService,ImgCdnStorageServiceProvider>();
|
||||||
|
builder.Services.AddSingleton<IPaymentService,StripePaymentServiceProvider>();
|
||||||
|
|
||||||
|
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
|
9
src/ArtPlatform.API/Services/Payment/IPaymentService.cs
Normal file
9
src/ArtPlatform.API/Services/Payment/IPaymentService.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using ArtPlatform.Database.Entities;
|
||||||
|
|
||||||
|
namespace ArtPlatform.API.Services.Payment;
|
||||||
|
|
||||||
|
public interface IPaymentService
|
||||||
|
{
|
||||||
|
string CreateSellerAccount();
|
||||||
|
string CreateSellerAccountOnboardingUrl(string accountId);
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
using ArtPlatform.Database.Entities;
|
||||||
|
using Stripe;
|
||||||
|
|
||||||
|
namespace ArtPlatform.API.Services.Payment;
|
||||||
|
|
||||||
|
public class StripePaymentServiceProvider:IPaymentService
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly string _apiKey;
|
||||||
|
|
||||||
|
|
||||||
|
public StripePaymentServiceProvider(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_apiKey = _configuration.GetValue<string>("Stripe:ApiKey");
|
||||||
|
StripeConfiguration.ApiKey = _apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateSellerAccount()
|
||||||
|
{
|
||||||
|
var accountCreateOptions = new AccountCreateOptions { Type = "express" };
|
||||||
|
var accountService = new AccountService();
|
||||||
|
var account = accountService.Create(accountCreateOptions);
|
||||||
|
return account.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CreateSellerAccountOnboardingUrl(string accountId)
|
||||||
|
{
|
||||||
|
var options = new AccountLinkCreateOptions
|
||||||
|
{
|
||||||
|
Account = accountId,
|
||||||
|
RefreshUrl = "https://example.com/reauth",
|
||||||
|
ReturnUrl = "https://example.com/return",
|
||||||
|
Type = "account_onboarding",
|
||||||
|
};
|
||||||
|
var service = new AccountLinkService();
|
||||||
|
var url = service.Create(options);
|
||||||
|
return url.Url;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
namespace ArtPlatform.API.Services.Storage;
|
namespace ArtPlatform.API.Services.Storage;
|
||||||
|
|
||||||
public interface IStorage
|
public interface IStorageService
|
||||||
{
|
{
|
||||||
public Task<string> UploadImageAsync(IFormFile file, string fileName);
|
public Task<string> UploadImageAsync(IFormFile file, string fileName);
|
||||||
public Task<Stream> DownloadImageAsync(string fileRefrence);
|
public Task<Stream> DownloadImageAsync(string fileRefrence);
|
@ -5,12 +5,12 @@ using Microsoft.AspNetCore.Http;
|
|||||||
|
|
||||||
namespace ArtPlatform.API.Services.Storage
|
namespace ArtPlatform.API.Services.Storage
|
||||||
{
|
{
|
||||||
public class ImgCdnProvider : IStorage
|
public class ImgCdnStorageServiceProvider : IStorageService
|
||||||
{
|
{
|
||||||
private readonly HttpClient _client;
|
private readonly HttpClient _client;
|
||||||
private const string ApiKey = "5386e05a3562c7a8f984e73401540836";
|
private const string ApiKey = "5386e05a3562c7a8f984e73401540836";
|
||||||
|
|
||||||
public ImgCdnProvider()
|
public ImgCdnStorageServiceProvider()
|
||||||
{
|
{
|
||||||
_client = new HttpClient { BaseAddress = new Uri("https://imgcdn.dev/") };
|
_client = new HttpClient { BaseAddress = new Uri("https://imgcdn.dev/") };
|
||||||
}
|
}
|
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"Stripe": {
|
||||||
|
"ApiKey": "sk_test_51OdJ1SLooS0IZqYkx2IdNoLcscm6BisgaUyYVIc5jM1RMmarww2e9hLLQS3Atn6TQi00p9YQkCLGQPhAI2gf9ZSY00HmbQYCvP"
|
||||||
|
},
|
||||||
"Auth0": {
|
"Auth0": {
|
||||||
"Domain": "https://dev-12mb5yq82dow1twh.us.auth0.com/",
|
"Domain": "https://dev-12mb5yq82dow1twh.us.auth0.com/",
|
||||||
"Audience": "https://api.artplatform.com",
|
"Audience": "https://api.artplatform.com",
|
||||||
|
@ -10,7 +10,7 @@ public record UserSellerProfile
|
|||||||
public string Biography { get; set; }
|
public string Biography { get; set; }
|
||||||
public List<string> SocialMediaLinks { get; set; } = new();
|
public List<string> SocialMediaLinks { get; set; } = new();
|
||||||
public bool AgeRestricted { get; set; }
|
public bool AgeRestricted { get; set; }
|
||||||
|
public string? StripeAccountId { get; set; }
|
||||||
public virtual User User { get; set; } = null!;
|
public virtual User User { get; set; } = null!;
|
||||||
|
|
||||||
public virtual ICollection<SellerService> SellerServices { get; set; } = new List<SellerService>();
|
public virtual ICollection<SellerService> SellerServices { get; set; } = new List<SellerService>();
|
||||||
|
507
src/ArtPlatform.Database/Migrations/20240127222945_updateshit.Designer.cs
generated
Normal file
507
src/ArtPlatform.Database/Migrations/20240127222945_updateshit.Designer.cs
generated
Normal file
@ -0,0 +1,507 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ArtPlatform.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ArtPlatform.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20240127222945_updateshit")]
|
||||||
|
partial class updateshit
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.1")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerProfilePortfolioPiece", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("FileReference")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("SellerProfileId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int?>("SellerServiceId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SellerProfileId");
|
||||||
|
|
||||||
|
b.HasIndex("SellerServiceId");
|
||||||
|
|
||||||
|
b.ToTable("SellerProfilePortfolioPieces");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerProfileRequest", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("Accepted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("AcceptedDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RequestDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("SellerProfileRequests");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerService", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
|
b.Property<int>("SellerProfileId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SellerProfileId");
|
||||||
|
|
||||||
|
b.ToTable("SellerServices");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrder", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("BuyerId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EndDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<double>("Price")
|
||||||
|
.HasColumnType("double precision");
|
||||||
|
|
||||||
|
b.Property<int>("SellerId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("SellerServiceId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("TermsAcceptedDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BuyerId");
|
||||||
|
|
||||||
|
b.HasIndex("SellerId");
|
||||||
|
|
||||||
|
b.HasIndex("SellerServiceId");
|
||||||
|
|
||||||
|
b.ToTable("SellerServiceOrders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Message")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("SellerServiceOrderId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("SenderId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("SentAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SellerServiceOrderId");
|
||||||
|
|
||||||
|
b.HasIndex("SenderId");
|
||||||
|
|
||||||
|
b.ToTable("SellerServiceOrderMessages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessageAttachment", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("FileReference")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("SellerServiceOrderMessageId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SellerServiceOrderMessageId");
|
||||||
|
|
||||||
|
b.ToTable("SellerServiceOrderMessageAttachments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderReview", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("Rating")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Review")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ReviewDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("ReviewerId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("SellerServiceId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("SellerServiceOrderId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ReviewerId");
|
||||||
|
|
||||||
|
b.HasIndex("SellerServiceId");
|
||||||
|
|
||||||
|
b.HasIndex("SellerServiceOrderId");
|
||||||
|
|
||||||
|
b.ToTable("SellerServiceOrderReviews");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("AddressCity")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("AddressCountry")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("AddressHouseNumber")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("AddressPostalCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("AddressRegion")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("AddressStreet")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Biography")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("LastName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int?>("UserSellerProfileId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.UserSellerProfile", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<bool>("AgeRestricted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Biography")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<List<string>>("SocialMediaLinks")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<string>("StripeAccountId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("UserSellerProfiles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerProfilePortfolioPiece", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.UserSellerProfile", "SellerProfile")
|
||||||
|
.WithMany("PortfolioPieces")
|
||||||
|
.HasForeignKey("SellerProfileId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.SellerService", "SellerService")
|
||||||
|
.WithMany("PortfolioPieces")
|
||||||
|
.HasForeignKey("SellerServiceId");
|
||||||
|
|
||||||
|
b.Navigation("SellerProfile");
|
||||||
|
|
||||||
|
b.Navigation("SellerService");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerProfileRequest", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.User", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerService", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.UserSellerProfile", "SellerProfile")
|
||||||
|
.WithMany("SellerServices")
|
||||||
|
.HasForeignKey("SellerProfileId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("SellerProfile");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrder", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.User", "Buyer")
|
||||||
|
.WithMany("Orders")
|
||||||
|
.HasForeignKey("BuyerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.UserSellerProfile", "Seller")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SellerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.SellerService", "SellerService")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SellerServiceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Buyer");
|
||||||
|
|
||||||
|
b.Navigation("Seller");
|
||||||
|
|
||||||
|
b.Navigation("SellerService");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.SellerServiceOrder", "SellerServiceOrder")
|
||||||
|
.WithMany("Messages")
|
||||||
|
.HasForeignKey("SellerServiceOrderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.User", "Sender")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SenderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("SellerServiceOrder");
|
||||||
|
|
||||||
|
b.Navigation("Sender");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessageAttachment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.SellerServiceOrderMessage", "SellerServiceOrderMessage")
|
||||||
|
.WithMany("Attachments")
|
||||||
|
.HasForeignKey("SellerServiceOrderMessageId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("SellerServiceOrderMessage");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderReview", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.User", "Reviewer")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ReviewerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.SellerService", "SellerService")
|
||||||
|
.WithMany("Reviews")
|
||||||
|
.HasForeignKey("SellerServiceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.SellerServiceOrder", "SellerServiceOrder")
|
||||||
|
.WithMany("Reviews")
|
||||||
|
.HasForeignKey("SellerServiceOrderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Reviewer");
|
||||||
|
|
||||||
|
b.Navigation("SellerService");
|
||||||
|
|
||||||
|
b.Navigation("SellerServiceOrder");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.UserSellerProfile", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("ArtPlatform.Database.Entities.User", "User")
|
||||||
|
.WithOne("UserSellerProfile")
|
||||||
|
.HasForeignKey("ArtPlatform.Database.Entities.UserSellerProfile", "UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerService", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("PortfolioPieces");
|
||||||
|
|
||||||
|
b.Navigation("Reviews");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrder", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Messages");
|
||||||
|
|
||||||
|
b.Navigation("Reviews");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessage", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Attachments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Orders");
|
||||||
|
|
||||||
|
b.Navigation("UserSellerProfile");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.UserSellerProfile", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("PortfolioPieces");
|
||||||
|
|
||||||
|
b.Navigation("SellerServices");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ArtPlatform.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class updateshit : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "StripeAccountId",
|
||||||
|
table: "UserSellerProfiles",
|
||||||
|
type: "text",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "StripeAccountId",
|
||||||
|
table: "UserSellerProfiles");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -317,6 +317,9 @@ namespace ArtPlatform.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text[]");
|
.HasColumnType("text[]");
|
||||||
|
|
||||||
|
b.Property<string>("StripeAccountId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("UserId")
|
b.Property<string>("UserId")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@ -417,7 +420,7 @@ namespace ArtPlatform.Database.Migrations
|
|||||||
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessageAttachment", b =>
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessageAttachment", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("ArtPlatform.Database.Entities.SellerServiceOrderMessage", "SellerServiceOrderMessage")
|
b.HasOne("ArtPlatform.Database.Entities.SellerServiceOrderMessage", "SellerServiceOrderMessage")
|
||||||
.WithMany()
|
.WithMany("Attachments")
|
||||||
.HasForeignKey("SellerServiceOrderMessageId")
|
.HasForeignKey("SellerServiceOrderMessageId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
@ -477,6 +480,11 @@ namespace ArtPlatform.Database.Migrations
|
|||||||
b.Navigation("Reviews");
|
b.Navigation("Reviews");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ArtPlatform.Database.Entities.SellerServiceOrderMessage", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Attachments");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ArtPlatform.Database.Entities.User", b =>
|
modelBuilder.Entity("ArtPlatform.Database.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Orders");
|
b.Navigation("Orders");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user