Retructured solution
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
namespace LiteCharms.Features.CartPackages.Commands;
|
||||
|
||||
public class AddPackageItemCommand : IRequest<Result<Guid>>
|
||||
{
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
public Guid ProductPriceId { get; set; }
|
||||
|
||||
private AddPackageItemCommand(Guid packageId, Guid productPriceId)
|
||||
{
|
||||
PackageId = packageId;
|
||||
ProductPriceId = productPriceId;
|
||||
}
|
||||
|
||||
public static AddPackageItemCommand Create(Guid packageId, Guid productPriceId)
|
||||
{
|
||||
if (packageId == Guid.Empty)
|
||||
throw new ArgumentException("Package id is required", nameof(packageId));
|
||||
|
||||
if (productPriceId == Guid.Empty)
|
||||
throw new ArgumentException("Product price id is required", nameof(productPriceId));
|
||||
|
||||
return new(packageId, productPriceId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace LiteCharms.Features.CartPackages.Commands;
|
||||
|
||||
public class CreatePackageCommand : IRequest<Result<Guid>>
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
private CreatePackageCommand(string? name, string? description)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public static CreatePackageCommand Create(string? name, string? description)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name, nameof(name));
|
||||
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(description, nameof(description));
|
||||
|
||||
return new(name, description);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace LiteCharms.Features.CartPackages.Commands;
|
||||
|
||||
public class DeletePackageItemCommand : IRequest<Result>
|
||||
{
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
public Guid PackageItemId { get; set; }
|
||||
|
||||
private DeletePackageItemCommand(Guid packageId, Guid packageItemId)
|
||||
{
|
||||
PackageId = packageId;
|
||||
PackageItemId = packageItemId;
|
||||
}
|
||||
|
||||
public static DeletePackageItemCommand Create(Guid packageId, Guid packageItemId)
|
||||
{
|
||||
if (packageId == Guid.Empty)
|
||||
throw new ArgumentException("Package id is required", nameof(packageId));
|
||||
|
||||
if (packageItemId == Guid.Empty)
|
||||
throw new ArgumentException("Product price id is required", nameof(packageItemId));
|
||||
|
||||
return new(packageId, packageItemId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace LiteCharms.Features.CartPackages.Commands;
|
||||
|
||||
public class DeletePackageItemsCommand : IRequest<Result>
|
||||
{
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
private DeletePackageItemsCommand(Guid packageId) => PackageId = packageId;
|
||||
|
||||
public static DeletePackageItemsCommand Create(Guid packageId)
|
||||
{
|
||||
if (packageId == Guid.Empty)
|
||||
throw new ArgumentException("Package ID is required", nameof(packageId));
|
||||
|
||||
return new(packageId);
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Commands.Handlers;
|
||||
|
||||
public class AddPackageItemCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<AddPackageItemCommand, Result<Guid>>
|
||||
{
|
||||
public async ValueTask<Result<Guid>> Handle(AddPackageItemCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken))
|
||||
return Result.Fail($"Could not find package by ID {request.PackageId}");
|
||||
|
||||
if (!await context.ProductPrices.AnyAsync(p => p.Id == request.ProductPriceId && p.Active == true, cancellationToken))
|
||||
return Result.Fail($"Could not find an active product price by ID {request.ProductPriceId}");
|
||||
|
||||
if (await context.PackageItems.AnyAsync(p => p.ProductPriceId == request.ProductPriceId && p.PackageId == request.PackageId, cancellationToken))
|
||||
return Result.Fail<Guid>($"Product price {request.ProductPriceId} is already added to this package {request.PackageId}");
|
||||
|
||||
var newPackageItem = context.PackageItems.Add(new Entities.PackageItem
|
||||
{
|
||||
PackageId = request.PackageId,
|
||||
ProductPriceId = request.ProductPriceId,
|
||||
Active = true
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(newPackageItem.Entity.Id)
|
||||
: Result.Fail<Guid>($"Failed to add new package item by ID {request.ProductPriceId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Guid>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Commands.Handlers;
|
||||
|
||||
public class CreatePackageCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<CreatePackageCommand, Result<Guid>>
|
||||
{
|
||||
public async ValueTask<Result<Guid>> Handle(CreatePackageCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (await context.Packages.AnyAsync(p => p.Name == request.Name, cancellationToken))
|
||||
return Result.Fail($"A package by the same name already exists: {request.Name}");
|
||||
|
||||
var newPackage = context.Packages.Add(new Entities.Package
|
||||
{
|
||||
Name = request.Name,
|
||||
Description = request.Description,
|
||||
Active = true
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(newPackage.Entity.Id)
|
||||
: Result.Fail($"Failed to create a new package by the name: {request.Name}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Guid>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Commands.Handlers;
|
||||
|
||||
public class DeletePackageItemCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<DeletePackageItemCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(DeletePackageItemCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken))
|
||||
return Result.Fail($"Could not find package by ID {request.PackageId}");
|
||||
|
||||
var item = await context.PackageItems.FirstOrDefaultAsync(p => p.Id == request.PackageItemId && p.PackageId == request.PackageId, cancellationToken);
|
||||
|
||||
if(item is null)
|
||||
return Result.Fail($"Product item {request.PackageItemId} is already added to this package {request.PackageId}");
|
||||
|
||||
context.PackageItems.Remove(item);
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail($"Failed to delete package item by id {request.PackageItemId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Commands.Handlers;
|
||||
|
||||
public class DeletePackageItemsCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<DeletePackageItemsCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(DeletePackageItemsCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken))
|
||||
return Result.Fail($"Could not find package by ID {request.PackageId}");
|
||||
|
||||
var items = await context.PackageItems.Where(i => i.PackageId == request.PackageId).ToArrayAsync(cancellationToken);
|
||||
|
||||
context.PackageItems.RemoveRange(items);
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail($"Failed to delete package {request.PackageId} items");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Commands.Handlers;
|
||||
|
||||
public class UpdatePackageCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<UpdatePackageCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(UpdatePackageCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (await context.Packages.AnyAsync(p => p.Name == request.Name, cancellationToken))
|
||||
return Result.Fail($"A package by the same name already exists: {request.Name}");
|
||||
|
||||
var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken);
|
||||
|
||||
if (package is null)
|
||||
return Result.Fail($"Could not find package by id {request.PackageId}");
|
||||
|
||||
package.Name = request.Name;
|
||||
package.Description = request.Description;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail($"Failed to update package with id {request.PackageId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Commands.Handlers;
|
||||
|
||||
public class UpdatePackageStatusCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<UpdatePackageStatusCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(UpdatePackageStatusCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken);
|
||||
|
||||
if (package is null)
|
||||
return Result.Fail($"Could not find package by id {request.PackageId}");
|
||||
|
||||
package.Active = request.Active;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail($"Failed to update package with id {request.PackageId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace LiteCharms.Features.CartPackages.Commands;
|
||||
|
||||
public class UpdatePackageCommand : IRequest<Result>
|
||||
{
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
private UpdatePackageCommand(Guid packageId, string? name, string? description)
|
||||
{
|
||||
PackageId = packageId;
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public static UpdatePackageCommand Create(Guid packageId, string? name, string? description)
|
||||
{
|
||||
if (packageId == Guid.Empty)
|
||||
throw new ArgumentException($"Package ID is required", nameof(packageId));
|
||||
|
||||
ArgumentNullException.ThrowIfNullOrWhiteSpace(name, nameof(name));
|
||||
ArgumentNullException.ThrowIfNullOrWhiteSpace(description, nameof(description));
|
||||
|
||||
return new(packageId, name, description);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace LiteCharms.Features.CartPackages.Commands;
|
||||
|
||||
public class UpdatePackageStatusCommand : IRequest<Result>
|
||||
{
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
|
||||
private UpdatePackageStatusCommand(Guid packageId, bool active)
|
||||
{
|
||||
PackageId = packageId;
|
||||
Active = active;
|
||||
}
|
||||
|
||||
public static UpdatePackageStatusCommand Create(Guid packageId, bool active)
|
||||
{
|
||||
if(packageId == Guid.Empty)
|
||||
throw new ArgumentException($"Package id is required", nameof(packageId));
|
||||
|
||||
return new(packageId, active);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
|
||||
[EntityTypeConfiguration<PackageConfirguration, Package>]
|
||||
public class Package : Models.Package
|
||||
{
|
||||
public virtual ICollection<PackageItem>? PackageItems { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
|
||||
public class PackageConfirguration : IEntityTypeConfiguration<Package>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Package> builder)
|
||||
{
|
||||
builder.ToTable(nameof(Package));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.Name).IsRequired();
|
||||
builder.Property(f => f.Summary).IsRequired().HasMaxLength(512);
|
||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(2048);
|
||||
builder.Property(f => f.ImageUrl).IsRequired(false).HasMaxLength(2048);
|
||||
builder.Property(f => f.Active);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using LiteCharms.Features.Shop.Products.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
|
||||
[EntityTypeConfiguration<PackageItemConfiguration, PackageItem>]
|
||||
public class PackageItem : Models.PackageItem
|
||||
{
|
||||
public virtual Package? Package { get; set; }
|
||||
|
||||
public virtual ProductPrice? ProductPrice { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
|
||||
public class PackageItemConfiguration : IEntityTypeConfiguration<PackageItem>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PackageItem> builder)
|
||||
{
|
||||
builder.ToTable(nameof(PackageItem));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.PackageId).IsRequired();
|
||||
builder.Property(f => f.ProductPriceId).IsRequired();
|
||||
builder.Property(f => f.Active);
|
||||
|
||||
builder.HasOne(f => f.Package)
|
||||
.WithMany(f => f.PackageItems)
|
||||
.HasForeignKey(pi => pi.PackageId)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasOne(f => f.ProductPrice)
|
||||
.WithMany()
|
||||
.HasForeignKey(pi => pi.ProductPriceId)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Models;
|
||||
|
||||
public class Package
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public DateTimeOffset? UpdatedAt { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Summary { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? ImageUrl { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace LiteCharms.Features.Shop.CartPackages.Models;
|
||||
|
||||
public class PackageItem
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
public Guid ProductPriceId { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Queries;
|
||||
|
||||
public class GetPackageItemsQuery : IRequest<Result<PackageItem[]>>
|
||||
{
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
private GetPackageItemsQuery(Guid packageId) => PackageId = packageId;
|
||||
|
||||
public static GetPackageItemsQuery Create(Guid packageId)
|
||||
{
|
||||
if (packageId == Guid.Empty)
|
||||
throw new ArgumentException("Package ID is required", nameof(packageId));
|
||||
|
||||
return new(packageId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Queries;
|
||||
|
||||
public class GetPackageQuery : IRequest<Result<Package>>
|
||||
{
|
||||
public Guid PackageId { get; set; }
|
||||
|
||||
private GetPackageQuery(Guid packageId) => PackageId = packageId;
|
||||
|
||||
public static GetPackageQuery Create(Guid packageId)
|
||||
{
|
||||
if(packageId == Guid.Empty)
|
||||
throw new ArgumentException("Package ID is required", nameof(packageId));
|
||||
|
||||
return new(packageId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Queries;
|
||||
|
||||
public class GetPackagesQuery : IRequest<Result<Package[]>>
|
||||
{
|
||||
public DateOnly From { get; set; }
|
||||
|
||||
public DateOnly To { get; set; }
|
||||
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
|
||||
private GetPackagesQuery(DateOnly from, DateOnly to, int maxRecords = 1000, bool active = true)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
MaxRecords = maxRecords;
|
||||
Active = active;
|
||||
}
|
||||
|
||||
public static GetPackagesQuery Create(DateOnly from, DateOnly to, int maxRecords = 1000, bool active = true)
|
||||
{
|
||||
if (from > to)
|
||||
throw new ArgumentException("From date cannot be greater than To date.");
|
||||
|
||||
if (maxRecords <= 0)
|
||||
throw new ArgumentException("MaxRecords must be a positive integer.");
|
||||
|
||||
return new(from, to, maxRecords, active);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Queries.Handlers;
|
||||
|
||||
public class GetPackageItemsQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetPackageItemsQuery, Result<PackageItem[]>>
|
||||
{
|
||||
public async ValueTask<Result<PackageItem[]>> Handle(GetPackageItemsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken))
|
||||
return Result.Fail<PackageItem[]>($"Package could not be found with ID {request.PackageId}");
|
||||
|
||||
var items = await context.PackageItems.AsNoTracking()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(p => p.PackageId == request.PackageId)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return items?.Length > 0
|
||||
? Result.Ok(items.Select(i => i.ToModel()).ToArray())
|
||||
: Result.Fail<PackageItem[]>($"Could not find package items by package ID {request.PackageId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<PackageItem[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Queries.Handlers;
|
||||
|
||||
public class GetPackageQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetPackageQuery, Result<Package>>
|
||||
{
|
||||
public async ValueTask<Result<Package>> Handle(GetPackageQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken);
|
||||
|
||||
return package is not null
|
||||
? Result.Ok(package.ToModel())
|
||||
: Result.Fail($"Failed to find package by ID {request.PackageId}");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Package>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.CartPackages.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.CartPackages.Queries.Handlers;
|
||||
|
||||
public class GetPackagesQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetPackagesQuery, Result<Package[]>>
|
||||
{
|
||||
public async ValueTask<Result<Package[]>> Handle(GetPackagesQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fromDate = request.From.ToDateTime(TimeOnly.MinValue);
|
||||
var toDate = request.To.ToDateTime(TimeOnly.MaxValue);
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var packages = await context.Packages
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(p => p.CreatedAt >= fromDate && p.CreatedAt <= toDate)
|
||||
.Where(p => p.Active == request.Active)
|
||||
.Take(request.MaxRecords)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return packages?.Length > 0
|
||||
? Result.Ok(packages.Select(o => o.ToModel()).ToArray())
|
||||
: Result.Fail<Package[]>(new Error($"No packages found for the specified date range {request.From} - {request.To}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Package[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
namespace LiteCharms.Features.Customers.Commands;
|
||||
|
||||
public class CreateCustomerCommand : IRequest<Result<Guid>>
|
||||
{
|
||||
public string? Company { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string LastName { get; set; }
|
||||
|
||||
public string? Tax { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string? Discord { get; set; }
|
||||
|
||||
public string? Slack { get; set; }
|
||||
|
||||
public string? LinkedIn { get; set; }
|
||||
|
||||
public string? Whatsapp { get; set; }
|
||||
|
||||
public string? Website { get; set; }
|
||||
|
||||
public string? Phone { get; set; }
|
||||
|
||||
public string? Address { get; set; }
|
||||
|
||||
public string? City { get; set; }
|
||||
|
||||
public string? Region { get; set; }
|
||||
|
||||
public string? Country { get; set; }
|
||||
|
||||
public string? PostalCode { get; set; }
|
||||
|
||||
private CreateCustomerCommand(string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode)
|
||||
{
|
||||
Name = name;
|
||||
LastName = lastName;
|
||||
Company = company;
|
||||
Tax = tax;
|
||||
Email = email;
|
||||
Discord = discord;
|
||||
Slack = slack;
|
||||
LinkedIn = linkedIn;
|
||||
Whatsapp = whatsapp;
|
||||
Website = website;
|
||||
Phone = phone;
|
||||
Address = address;
|
||||
City = city;
|
||||
Region = region;
|
||||
Country = country;
|
||||
PostalCode = postalCode;
|
||||
}
|
||||
|
||||
public static CreateCustomerCommand Create(string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name) && string.IsNullOrWhiteSpace(lastName) && string.IsNullOrWhiteSpace(email))
|
||||
throw new ArgumentException("At the following fields must be provided: Name, LastName, Email");
|
||||
|
||||
return new(name, lastName, company, tax, email, discord, slack, linkedIn, whatsapp, website, phone, address, city, region, country, postalCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Customers.Commands.Handlers;
|
||||
|
||||
public class CreateCustomerCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<CreateCustomerCommand, Result<Guid>>
|
||||
{
|
||||
public async ValueTask<Result<Guid>> Handle(CreateCustomerCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var customerEmail = request.Email.ToLower().Trim();
|
||||
|
||||
if (await context.Customers.AnyAsync(c => c.Email == customerEmail, cancellationToken))
|
||||
return Result.Fail<Guid>(new Error($"A customer with the email {customerEmail} already exists"));
|
||||
|
||||
var newCustomer = context.Customers.Add(new Entities.Customer
|
||||
{
|
||||
Company = request.Company,
|
||||
Name = request.Name,
|
||||
LastName = request.LastName,
|
||||
Tax = request.Tax,
|
||||
Email = customerEmail,
|
||||
Discord = request.Discord,
|
||||
Slack = request.Slack,
|
||||
LinkedIn = request.LinkedIn,
|
||||
Whatsapp = request.Whatsapp,
|
||||
Website = request.Website,
|
||||
Phone = request.Phone,
|
||||
Address = request.Address,
|
||||
City = request.City,
|
||||
Region = request.Region,
|
||||
Country = request.Country,
|
||||
PostalCode = request.PostalCode,
|
||||
Active = true,
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(newCustomer.Entity.Id)
|
||||
: Result.Fail<Guid>(new Error($"Failed to create customer {customerEmail}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Guid>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Customers.Commands.Handlers;
|
||||
|
||||
public class UpdateCustomerCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<UpdateCustomerCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(UpdateCustomerCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == request.CustomerId, cancellationToken);
|
||||
|
||||
if (customer is null)
|
||||
return Result.Fail(new Error($"Customer with ID {request.CustomerId} not found."));
|
||||
|
||||
customer.Name = request.Name;
|
||||
customer.LastName = request.LastName;
|
||||
customer.Email = request.Email;
|
||||
customer.Company = request.Company;
|
||||
customer.Address = request.Address;
|
||||
customer.City = request.City;
|
||||
customer.Region = request.Region;
|
||||
customer.Country = request.Country;
|
||||
customer.PostalCode = request.PostalCode;
|
||||
customer.Phone = request.Phone;
|
||||
customer.Tax = request.Tax;
|
||||
customer.City = request.City;
|
||||
customer.Discord = request.Discord;
|
||||
customer.Slack = request.Slack;
|
||||
customer.LinkedIn = request.LinkedIn;
|
||||
customer.Whatsapp = request.Whatsapp;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail(new Error($"Failed to update the customer {request.CustomerId}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace LiteCharms.Features.Customers.Commands;
|
||||
|
||||
public class UpdateCustomerCommand : IRequest<Result>
|
||||
{
|
||||
public Guid CustomerId { get; set; }
|
||||
|
||||
public string? Company { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? LastName { get; set; }
|
||||
|
||||
public string? Tax { get; set; }
|
||||
|
||||
public string? Email { get; set; }
|
||||
|
||||
public string? Discord { get; set; }
|
||||
|
||||
public string? Slack { get; set; }
|
||||
|
||||
public string? LinkedIn { get; set; }
|
||||
|
||||
public string? Whatsapp { get; set; }
|
||||
|
||||
public string? Website { get; set; }
|
||||
|
||||
public string? Phone { get; set; }
|
||||
|
||||
public string? Address { get; set; }
|
||||
|
||||
public string? City { get; set; }
|
||||
|
||||
public string? Region { get; set; }
|
||||
|
||||
public string? Country { get; set; }
|
||||
|
||||
public string? PostalCode { get; set; }
|
||||
|
||||
private UpdateCustomerCommand(Guid customerId, string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode)
|
||||
{
|
||||
CustomerId = customerId;
|
||||
Name = name;
|
||||
LastName = lastName;
|
||||
Company = company;
|
||||
Tax = tax;
|
||||
Email = email;
|
||||
Discord = discord;
|
||||
Slack = slack;
|
||||
LinkedIn = linkedIn;
|
||||
Whatsapp = whatsapp;
|
||||
Website = website;
|
||||
Phone = phone;
|
||||
Address = address;
|
||||
City = city;
|
||||
Region = region;
|
||||
Country = country;
|
||||
PostalCode = postalCode;
|
||||
}
|
||||
|
||||
public static UpdateCustomerCommand Create(Guid customerId, string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode)
|
||||
{
|
||||
if (customerId == Guid.Empty)
|
||||
throw new ArgumentException("Customer ID is required.", nameof(customerId));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name) && string.IsNullOrWhiteSpace(lastName) && string.IsNullOrWhiteSpace(email))
|
||||
throw new ArgumentException("At the following fields must be provided: Name, LastName, Email");
|
||||
|
||||
return new(customerId, name, lastName, company, tax, email, discord, slack, linkedIn, whatsapp, website, phone, address, city, region, country, postalCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Leads.Entities;
|
||||
using LiteCharms.Features.Shop.Orders.Entities;
|
||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Customers.Entities;
|
||||
|
||||
[EntityTypeConfiguration<CustomerConfiguration, Customer>]
|
||||
public class Customer : Models.Customer
|
||||
{
|
||||
public virtual ICollection<Lead>? Leads { get; set; }
|
||||
|
||||
public virtual ICollection<Order>? Orders { get; set; }
|
||||
|
||||
public virtual ICollection<Quote>? Quotes { get; set; }
|
||||
|
||||
public virtual ICollection<ShoppingCart>? ShoppingCarts { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace LiteCharms.Features.Shop.Customers.Entities;
|
||||
|
||||
public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Customer> builder)
|
||||
{
|
||||
builder.ToTable(nameof(Customer));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.Company);
|
||||
builder.Property(f => f.Name).IsRequired();
|
||||
builder.Property(f => f.LastName).IsRequired();
|
||||
builder.Property(f => f.Email).IsRequired();
|
||||
builder.Property(f => f.Tax);
|
||||
builder.Property(f => f.Discord);
|
||||
builder.Property(f => f.Slack);
|
||||
builder.Property(f => f.LinkedIn);
|
||||
builder.Property(f => f.Whatsapp);
|
||||
builder.Property(f => f.Website);
|
||||
builder.Property(f => f.Phone);
|
||||
builder.Property(f => f.Address);
|
||||
builder.Property(f => f.City);
|
||||
builder.Property(f => f.Region);
|
||||
builder.Property(f => f.Country);
|
||||
builder.Property(f => f.PostalCode);
|
||||
builder.Property(f => f.Active).HasDefaultValue(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace LiteCharms.Features.Shop.Customers.Models;
|
||||
|
||||
public class Customer
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public DateTimeOffset? UpdatedAt { get; set; }
|
||||
|
||||
public string? Company { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? LastName { get; set; }
|
||||
|
||||
public string? Tax { get; set; }
|
||||
|
||||
public string? Email { get; set; }
|
||||
|
||||
public string? Discord { get; set; }
|
||||
|
||||
public string? Slack { get; set; }
|
||||
|
||||
public string? LinkedIn { get; set; }
|
||||
|
||||
public string? Whatsapp { get; set; }
|
||||
|
||||
public string? Website { get; set; }
|
||||
|
||||
public string? Phone { get; set; }
|
||||
|
||||
public string? Address { get; set; }
|
||||
|
||||
public string? City { get; set; }
|
||||
|
||||
public string? Region { get; set; }
|
||||
|
||||
public string? Country { get; set; }
|
||||
|
||||
public string? PostalCode { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Customers.Models;
|
||||
|
||||
namespace LiteCharms.Features.Customers.Queries;
|
||||
|
||||
public class GetCustomerQuery : IRequest<Result<Customer>>
|
||||
{
|
||||
public Guid CustomerId { get; set; }
|
||||
|
||||
private GetCustomerQuery(Guid customerId) => CustomerId = customerId;
|
||||
|
||||
public static GetCustomerQuery Create(Guid customerId)
|
||||
{
|
||||
if(customerId == Guid.Empty)
|
||||
throw new ArgumentException("Customer ID is required.", nameof(customerId));
|
||||
|
||||
return new(customerId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using LiteCharms.Features.Shop.Customers.Models;
|
||||
|
||||
namespace LiteCharms.Features.Customers.Queries;
|
||||
|
||||
public class GetCustomersQuery : IRequest<Result<Customer[]>>
|
||||
{
|
||||
public DateOnly From { get; set; }
|
||||
|
||||
public DateOnly To { get; set; }
|
||||
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
private GetCustomersQuery(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
MaxRecords = maxRecords;
|
||||
}
|
||||
|
||||
public static GetCustomersQuery Create(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
if (from > to)
|
||||
throw new ArgumentException("From date cannot be greater than To date.");
|
||||
|
||||
if(maxRecords <= 0)
|
||||
throw new ArgumentException("MaxRecords must be a positive integer.");
|
||||
|
||||
return new(from, to, maxRecords);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Customers.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Customers.Queries.Handlers;
|
||||
|
||||
public class GetCustomerQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetCustomerQuery, Result<Customer>>
|
||||
{
|
||||
public async ValueTask<Result<Customer>> Handle(GetCustomerQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == request.CustomerId, cancellationToken);
|
||||
|
||||
return customer is not null
|
||||
? Result.Ok(customer.ToModel())
|
||||
: Result.Fail<Customer>($"Customer not found with id {request.CustomerId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Customer>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Customers.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Customers.Queries.Handlers;
|
||||
|
||||
public class GetCustomersQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetCustomersQuery, Result<Customer[]>>
|
||||
{
|
||||
public async ValueTask<Result<Customer[]>> Handle(GetCustomersQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fromDate = request.From.ToDateTime(TimeOnly.MinValue);
|
||||
var toDate = request.To.ToDateTime(TimeOnly.MaxValue);
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var customers = await context.Customers.AsNoTracking()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(c => c.CreatedAt >= fromDate && c.CreatedAt <= toDate)
|
||||
.Take(request.MaxRecords)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return customers?.Length > 0
|
||||
? Result.Ok(customers.Select(c => c.ToModel()).ToArray())
|
||||
: Result.Fail<Customer[]>(new Error("No customers found in the specified date range."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Customer[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
namespace LiteCharms.Features.Shop;
|
||||
|
||||
public enum EmailStatuses : int
|
||||
{
|
||||
GeneralError = 0,
|
||||
AuthenticationError = 1,
|
||||
ProtocolError = 2,
|
||||
Connected = 3,
|
||||
Disconnected = 4,
|
||||
TooManyConnections = 5,
|
||||
ConnectionAborted = 6,
|
||||
Success = 7
|
||||
}
|
||||
|
||||
public enum CorrelationIdTypes : int
|
||||
{
|
||||
None = 0,
|
||||
Email = 1,
|
||||
Discord = 2,
|
||||
Slack = 3,
|
||||
Whatsapp = 4,
|
||||
Customer = 5,
|
||||
Order = 6,
|
||||
Refund = 7,
|
||||
Lead = 8,
|
||||
Quote = 9,
|
||||
LinkedIn = 10
|
||||
}
|
||||
|
||||
public enum Priorities : int
|
||||
{
|
||||
Low = 0,
|
||||
Medium = 1,
|
||||
High = 2,
|
||||
}
|
||||
|
||||
public enum NotificationPlatforms : int
|
||||
{
|
||||
Email = 1,
|
||||
Discord = 2,
|
||||
Slack = 3,
|
||||
WhatsApp = 4,
|
||||
System = 5
|
||||
}
|
||||
|
||||
public enum QuoteStatus : int
|
||||
{
|
||||
Draft = 0,
|
||||
Sent = 1,
|
||||
Accepted = 2,
|
||||
Rejected = 3,
|
||||
Expired = 4
|
||||
}
|
||||
|
||||
public enum OrderStatus : int
|
||||
{
|
||||
Pending = 0,
|
||||
Completed = 1,
|
||||
Cancelled = 2,
|
||||
Failed = 3,
|
||||
Refunded = 4,
|
||||
Error = 5
|
||||
}
|
||||
|
||||
public enum LeadStatus : int
|
||||
{
|
||||
New = 0,
|
||||
Contacted = 1,
|
||||
Qualified = 2,
|
||||
Unqualified = 3,
|
||||
Converted = 4,
|
||||
Lost = 5
|
||||
}
|
||||
|
||||
public enum NotificationDirection : int
|
||||
{
|
||||
Incoming = 0,
|
||||
Outgoing = 1,
|
||||
Neutral = 2
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
namespace LiteCharms.Features.Leads.Commands;
|
||||
|
||||
public class CreateLeadCommand : IRequest<Result<Guid>>
|
||||
{
|
||||
public Guid? CustomerId { get; set; }
|
||||
|
||||
public string? Source { get; set; }
|
||||
|
||||
public string? ClickId { get; set; }
|
||||
|
||||
public string? WebClickId { get; set; }
|
||||
|
||||
public string? AppClickId { get; set; }
|
||||
|
||||
public long? CampaignId { get; set; }
|
||||
|
||||
public long? AdGroupId { get; set; }
|
||||
|
||||
public long? AdName { get; set; }
|
||||
|
||||
public long? TargetId { get; set; }
|
||||
|
||||
public long? FeedItemId { get; set; }
|
||||
|
||||
public string? ClickLocation { get; set; }
|
||||
|
||||
public string? AttribusionHash { get; set; }
|
||||
|
||||
private CreateLeadCommand(Guid? customerId, string source, string clickId, string webClickId, string appClickId, long? campaignId, long? adGroupId, long? adName, long? targetId, long? feedItemId, string? clickLocation, string? attribusionHash)
|
||||
{
|
||||
CustomerId = customerId;
|
||||
Source = source;
|
||||
ClickId = clickId;
|
||||
WebClickId = webClickId;
|
||||
AppClickId = appClickId;
|
||||
CampaignId = campaignId;
|
||||
AdGroupId = adGroupId;
|
||||
AdName = adName;
|
||||
TargetId = targetId;
|
||||
FeedItemId = feedItemId;
|
||||
ClickLocation = clickLocation;
|
||||
AttribusionHash = attribusionHash;
|
||||
}
|
||||
|
||||
public static CreateLeadCommand Create(Guid? customerId, string source, string clickId, string webClickId, string appClickId, long? campaignId, long? adGroupId, long? adName, long? targetId, long? feedItemId, string? clickLocation, string? attribusionHash)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(source))
|
||||
throw new ArgumentNullException("Lead source is required to create a lead.", nameof(source));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(clickId) || string.IsNullOrWhiteSpace(appClickId) || string.IsNullOrWhiteSpace(webClickId))
|
||||
throw new ArgumentException("ClickId, App ClickId and Web ClickId are required to create a lead.");
|
||||
|
||||
return new(customerId, source, clickId, webClickId, appClickId, campaignId, adGroupId, adName, targetId, feedItemId, clickLocation, attribusionHash);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Features.Utilities.Hash.Commands;
|
||||
|
||||
namespace LiteCharms.Features.Leads.Commands.Handlers;
|
||||
|
||||
public class CreateLeadCommandHandler(IDbContextFactory<ShopDbContext> contextFactory, ISender mediator) : IRequestHandler<CreateLeadCommand, Result<Guid>>
|
||||
{
|
||||
public async ValueTask<Result<Guid>> Handle(CreateLeadCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var hashCommand = ComputeHashCommand.Create($"{request.ClickId}{request.AppClickId}{request.WebClickId}");
|
||||
var hashResult = await mediator.Send(hashCommand, cancellationToken);
|
||||
|
||||
if(hashResult.IsFailed)
|
||||
return Result.Fail<Guid>(new Error($"Failed to compute hash for lead -> Google ClickId: {request.ClickId}, App ClickId: {request.AppClickId}, Web ClickId: {request.WebClickId}")
|
||||
.CausedBy(hashResult.Errors));
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var newLead = context.Leads.Add(new Entities.Lead
|
||||
{
|
||||
WebClickId = request.WebClickId,
|
||||
AppClickId = request.AppClickId,
|
||||
Source = request.Source,
|
||||
ClickId = request.ClickId,
|
||||
AdGroupId = request.AdGroupId,
|
||||
AdName = request.AdName,
|
||||
CampaignId = request.CampaignId,
|
||||
ClickLocation = request.ClickLocation,
|
||||
CustomerId = request.CustomerId,
|
||||
FeedItemId = request.FeedItemId,
|
||||
Status = Models.LeadStatus.New,
|
||||
TargetId = request.TargetId,
|
||||
AttributionHash = hashResult.Value
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(newLead.Entity.Id)
|
||||
: Result.Fail<Guid>(new Error($"Failed to create lead -> Google ClickId: {request.ClickId}, App ClickId: {request.AppClickId}, Web ClickId: {request.WebClickId}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Guid>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Leads.Commands.Handlers;
|
||||
|
||||
public class UpdateLeadCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<UpdateLeadCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(UpdateLeadCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var lead = await context.Leads.FirstOrDefaultAsync(l => l.Id == request.LeadId, cancellationToken);
|
||||
|
||||
if (lead is null)
|
||||
return Result.Fail(new Error($"Lead with ID {request.LeadId} not found."));
|
||||
|
||||
lead.Status = request.Status;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail(new Error($"Failed to update the lead {request.LeadId}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using LiteCharms.Features.Shop;
|
||||
using LiteCharms.Models;
|
||||
|
||||
namespace LiteCharms.Features.Leads.Commands;
|
||||
|
||||
public class UpdateLeadCommand : IRequest<Result>
|
||||
{
|
||||
public Guid LeadId { get; set; }
|
||||
|
||||
public LeadStatus Status { get; set; }
|
||||
|
||||
private UpdateLeadCommand(Guid leadId, LeadStatus status)
|
||||
{
|
||||
LeadId = leadId;
|
||||
Status = status;
|
||||
}
|
||||
|
||||
public static UpdateLeadCommand Create(Guid leadId, LeadStatus status)
|
||||
{
|
||||
if (leadId == Guid.Empty)
|
||||
throw new ArgumentException("Lead ID cannot be empty.", nameof(leadId));
|
||||
|
||||
if (!Enum.IsDefined(typeof(LeadStatus), status))
|
||||
throw new ArgumentException("Invalid lead status.", nameof(status));
|
||||
|
||||
return new(leadId, status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using LiteCharms.Features.Shop.Customers.Models;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Leads.Entities;
|
||||
|
||||
[EntityTypeConfiguration<LeadConfiguration, Lead>]
|
||||
public class Lead : Models.Lead
|
||||
{
|
||||
public virtual Customer? Customer { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace LiteCharms.Features.Shop.Leads.Entities;
|
||||
|
||||
public class LeadConfiguration : IEntityTypeConfiguration<Lead>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Lead> builder)
|
||||
{
|
||||
builder.ToTable(nameof(Lead));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.CustomerId).IsRequired(false);
|
||||
builder.Property(f => f.Source);
|
||||
builder.Property(f => f.ClickId);
|
||||
builder.Property(f => f.WebClickId);
|
||||
builder.Property(f => f.AppClickId);
|
||||
builder.Property(f => f.CampaignId);
|
||||
builder.Property(f => f.AdGroupId);
|
||||
builder.Property(f => f.AdName);
|
||||
builder.Property(f => f.TargetId);
|
||||
builder.Property(f => f.FeedItemId);
|
||||
builder.Property(f => f.ClickLocation);
|
||||
builder.Property(f => f.Status).IsRequired();
|
||||
builder.Property(f => f.AttributionHash).IsRequired(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace LiteCharms.Features.Shop.Leads.Models;
|
||||
|
||||
public class Lead
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public DateTimeOffset? UpdatedAt { get; set; }
|
||||
|
||||
public Guid? CustomerId { get; set; }
|
||||
|
||||
public string? Source { get; set; }
|
||||
|
||||
public string? ClickId { get; set; }
|
||||
|
||||
public string? WebClickId { get; set; }
|
||||
|
||||
public string? AppClickId { get; set; }
|
||||
|
||||
public long? CampaignId { get; set; }
|
||||
|
||||
public long? AdGroupId { get; set; }
|
||||
|
||||
public long? AdName { get; set; }
|
||||
|
||||
public long? TargetId { get; set; }
|
||||
|
||||
public long? FeedItemId { get; set; }
|
||||
|
||||
public string? ClickLocation { get; set; }
|
||||
|
||||
public string? AttributionHash { get; set; }
|
||||
|
||||
public LeadStatus Status { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using LiteCharms.Features.Shop.Leads.Models;
|
||||
|
||||
namespace LiteCharms.Features.Leads.Queries;
|
||||
|
||||
public class GetCustomerLeadsQuery : IRequest<Result<Lead[]>>
|
||||
{
|
||||
public Guid CustomerId { get; }
|
||||
|
||||
public DateOnly From { get; set; }
|
||||
|
||||
public DateOnly To { get; set; }
|
||||
|
||||
private GetCustomerLeadsQuery(Guid customerId, DateOnly from, DateOnly to)
|
||||
{
|
||||
CustomerId = customerId;
|
||||
From = from;
|
||||
To = to;
|
||||
}
|
||||
|
||||
public static GetCustomerLeadsQuery Create(Guid customerId, DateOnly from, DateOnly to)
|
||||
{
|
||||
if(customerId == Guid.Empty)
|
||||
throw new ArgumentException("Customer ID cannot be empty.", nameof(customerId));
|
||||
|
||||
if(from > to)
|
||||
throw new ArgumentException("The 'From' date cannot be later than the 'To' date.");
|
||||
|
||||
return new(customerId, from, to);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using LiteCharms.Features.Shop.Leads.Models;
|
||||
|
||||
namespace LiteCharms.Features.Leads.Queries;
|
||||
|
||||
public class GetLeadsQuery : IRequest<Result<Lead[]>>
|
||||
{
|
||||
public DateOnly From { get; set; }
|
||||
|
||||
public DateOnly To { get; set; }
|
||||
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
private GetLeadsQuery(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
MaxRecords = maxRecords;
|
||||
}
|
||||
|
||||
public static GetLeadsQuery Create(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
if (from > to)
|
||||
throw new ArgumentException("From date cannot be greater than To date.");
|
||||
|
||||
if(maxRecords <= 0)
|
||||
throw new ArgumentException("MaxRecords must be a positive integer.");
|
||||
|
||||
return new(from, to, maxRecords);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Leads.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Leads.Queries.Handlers;
|
||||
|
||||
public class GetCustomerLeadsQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetCustomerLeadsQuery, Result<Lead[]>>
|
||||
{
|
||||
public async ValueTask<Result<Lead[]>> Handle(GetCustomerLeadsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fromDate = request.From.ToDateTime(TimeOnly.MinValue);
|
||||
var toDate = request.To.ToDateTime(TimeOnly.MaxValue);
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var leads = await context.Leads.AsNoTracking()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(lead => lead.CustomerId == request.CustomerId)
|
||||
.Where(lead => lead.CreatedAt.Date >= fromDate && lead.CreatedAt.Date <= toDate)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return leads?.Length > 0
|
||||
? Result.Ok(leads.Select(l => l.ToModel()).ToArray())
|
||||
: Result.Fail(new Error($"No customer {request.CustomerId} leads found for the specified date range {request.From} to {request.To}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Lead[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Leads.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Leads.Queries.Handlers;
|
||||
|
||||
public class GetLeadsQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetLeadsQuery, Result<Lead[]>>
|
||||
{
|
||||
public async ValueTask<Result<Lead[]>> Handle(GetLeadsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fromDate = request.From.ToDateTime(TimeOnly.MinValue);
|
||||
var toDate = request.To.ToDateTime(TimeOnly.MaxValue);
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var leads = await context.Leads.AsNoTracking()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(l => l.CreatedAt.Date >= fromDate && l.CreatedAt.Date <= toDate)
|
||||
.Take(request.MaxRecords)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return leads?.Length > 0
|
||||
? Result.Ok(leads.Select(l => l.ToModel()).ToArray())
|
||||
: Result.Fail(new Error($"No leads found for the specified date range {request.From} to {request.To}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using LiteCharms.Features.Shop;
|
||||
using LiteCharms.Models;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Commands;
|
||||
|
||||
public class CreateNotification : IRequest<Result<Guid>>
|
||||
{
|
||||
public NotificationDirection Direction { get; set; }
|
||||
|
||||
public string? Sender { get; set; }
|
||||
|
||||
public string? SenderAddress { get; set; }
|
||||
|
||||
public string? Subject { get; set; }
|
||||
|
||||
public string? Message { get; set; }
|
||||
|
||||
public NotificationPlatforms Platform { get; set; }
|
||||
|
||||
public Priorities Priority { get; set; }
|
||||
|
||||
public string? Recipient { get; set; }
|
||||
|
||||
public string? RecipientAddress { get; set; }
|
||||
|
||||
public string? CorrelationId { get; set; }
|
||||
|
||||
public CorrelationIdTypes CorrelationIdType { get; set; }
|
||||
|
||||
public bool IsInternal { get; set; }
|
||||
|
||||
public bool IsHtml { get; set; }
|
||||
|
||||
private CreateNotification(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false)
|
||||
{
|
||||
Direction = direction;
|
||||
Sender = sender;
|
||||
SenderAddress = senderAddress;
|
||||
Subject = subject;
|
||||
Message = message;
|
||||
Platform = platform;
|
||||
Priority = priority;
|
||||
Recipient = recipient;
|
||||
RecipientAddress = recipientAddress;
|
||||
CorrelationId = correlationId;
|
||||
CorrelationIdType = correlationIdType;
|
||||
IsInternal = isInternal;
|
||||
IsHtml = isHtml;
|
||||
}
|
||||
|
||||
public static CreateNotification Create(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sender))
|
||||
throw new ArgumentException("Sender name is required.", nameof(sender));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(subject))
|
||||
throw new ArgumentException("Subject is required.", nameof(subject));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
throw new ArgumentException("Message is required.", nameof(message));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(recipient))
|
||||
throw new ArgumentException("Recipient name is required.", nameof(recipient));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(recipientAddress))
|
||||
throw new ArgumentException("Recipient address is required.", nameof(recipientAddress));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(correlationId))
|
||||
throw new ArgumentException("CorrelationId is required.", nameof(correlationId));
|
||||
|
||||
return new(direction, sender, senderAddress, subject, message, platform, priority, recipient, recipientAddress, correlationId, correlationIdType, isInternal, isHtml);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Commands.Handlers;
|
||||
|
||||
public class CreateNotificationCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<CreateNotification, Result<Guid>>
|
||||
{
|
||||
public async ValueTask<Result<Guid>> Handle(CreateNotification request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var newNotification = context.Notifications.Add(new Entities.Notification
|
||||
{
|
||||
Direction = request.Direction,
|
||||
SenderName = request.Sender,
|
||||
Sender = request.SenderAddress,
|
||||
Recipient = request.Recipient,
|
||||
RecipientAddress = request.RecipientAddress,
|
||||
Subject = request.Subject,
|
||||
Message = request.Message,
|
||||
Platform = request.Platform,
|
||||
Priority = request.Priority,
|
||||
CorrelationId = request.CorrelationId,
|
||||
CorrelationIdType = request.CorrelationIdType,
|
||||
IsInternal = request.IsInternal,
|
||||
IsHtml = request.IsHtml,
|
||||
Processed = false
|
||||
});
|
||||
|
||||
return newNotification is not null
|
||||
? Result.Ok(newNotification.Entity.Id)
|
||||
: Result.Fail(new Error("Failed to create notification"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Commands.Handlers;
|
||||
|
||||
public class UpdateNotificationCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<UpdateNotificationCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(UpdateNotificationCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var notification = await context.Notifications.FirstOrDefaultAsync(n => n.Id == request.NotificationId, cancellationToken);
|
||||
|
||||
if(notification is null)
|
||||
return Result.Fail(new Error($"Notification with id {request.NotificationId} not found."));
|
||||
|
||||
notification.Processed = request.Processed;
|
||||
|
||||
if (request.HasError)
|
||||
{
|
||||
notification.HasError = request.HasError;
|
||||
notification.Errors = request.Errors;
|
||||
}
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail(new Error($"Failed to update notification with id {request.NotificationId}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace LiteCharms.Features.Notifications.Commands;
|
||||
|
||||
public class UpdateNotificationCommand : IRequest<Result>
|
||||
{
|
||||
public Guid NotificationId { get; set; }
|
||||
|
||||
public bool Processed { get; set; }
|
||||
|
||||
public bool HasError { get; set; }
|
||||
|
||||
public string[]? Errors { get; set; }
|
||||
|
||||
private UpdateNotificationCommand(Guid notificationId, bool processed, bool hasError = false, string[]? errors = null)
|
||||
{
|
||||
NotificationId = notificationId;
|
||||
Processed = processed;
|
||||
HasError = hasError;
|
||||
Errors = errors;
|
||||
}
|
||||
|
||||
public static UpdateNotificationCommand Create(Guid notificationId, bool processed, bool hasError = false, string[]? errors = null)
|
||||
{
|
||||
if(notificationId == Guid.Empty)
|
||||
throw new ArgumentException("Notification ID cannot be empty.", nameof(notificationId));
|
||||
|
||||
return new(notificationId, processed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Entities;
|
||||
|
||||
[EntityTypeConfiguration<NotificationConfiguration, Notification>]
|
||||
public class Notification : Models.Notification;
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Entities;
|
||||
|
||||
public class NotificationConfiguration : IEntityTypeConfiguration<Notification>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Notification> builder)
|
||||
{
|
||||
builder.ToTable(nameof(Notification));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.Direction).IsRequired().HasConversion<int>();
|
||||
builder.Property(f => f.Platform).IsRequired().HasConversion<int>();
|
||||
builder.Property(f => f.Priority).IsRequired().HasConversion<int>();
|
||||
builder.Property(f => f.CorrelationIdType).IsRequired().HasConversion<int>();
|
||||
builder.Property(f => f.Sender).IsRequired();
|
||||
builder.Property(f => f.Subject).IsRequired();
|
||||
builder.Property(f => f.Message).IsRequired();
|
||||
builder.Property(f => f.Recipient).IsRequired();
|
||||
builder.Property(f => f.RecipientAddress).IsRequired();
|
||||
builder.Property(f => f.CorrelationId).IsRequired();
|
||||
builder.Property(f => f.IsHtml).HasDefaultValue(false);
|
||||
builder.Property(f => f.IsInternal).HasDefaultValue(true);
|
||||
builder.Property(f => f.Processed).HasDefaultValue(false);
|
||||
builder.Property(f => f.HasError).HasDefaultValue(false);
|
||||
builder.Property(f => f.Errors).HasColumnType("jsonb").IsRequired(false);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using LiteCharms.Features.Email.Commands;
|
||||
using LiteCharms.Features.Shop.Notifications.Entities;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using static LiteCharms.Features.ServiceBus.Constants;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Events.Handlers;
|
||||
|
||||
public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbContext> contextFactory, ILogger<ProcessEmailNotificationsEvent> logger, ISender mediator) :
|
||||
INotificationHandler<ProcessEmailNotificationsEvent>
|
||||
{
|
||||
public async ValueTask Handle(ProcessEmailNotificationsEvent message, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var notifications = await context.Notifications
|
||||
.OrderByDescending(o => o.Priority)
|
||||
.ThenBy(o => o.CreatedAt)
|
||||
.Where(n => n.CorrelationIdType == Models.CorrelationIdTypes.Email)
|
||||
.Where(n => n.Direction == Models.NotificationDirection.Outgoing)
|
||||
.Take(message.MaxRecords)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
foreach (var notification in notifications)
|
||||
{
|
||||
var sendResult = await SendEmailAsync(notification, cancellationToken);
|
||||
|
||||
if(sendResult.IsFailed)
|
||||
{
|
||||
var errors = new List<string>(1000);
|
||||
|
||||
errors.AddRange(sendResult.Errors.Select(e => e.Message));
|
||||
|
||||
if (sendResult.Reasons?.Count > 0)
|
||||
errors.AddRange(sendResult.Reasons.Select(e => e.Message));
|
||||
|
||||
notification.HasError = true;
|
||||
notification.Errors = [.. errors];
|
||||
}
|
||||
|
||||
notification.Processed = true;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Result> SendEmailAsync(Notification notification, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = SendEmailCommand.Create(notification.Sender!, notification.SenderName!, ShopEmailFromAddress,
|
||||
ShopEmailFromName, notification.Subject!, notification.Message!);
|
||||
|
||||
var result = await mediator.Send(request, cancellationToken);
|
||||
|
||||
return result.IsFailed
|
||||
? Result.Fail(result.Errors)
|
||||
: Result.Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using LiteCharms.Features.Abstractions;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Events;
|
||||
|
||||
public class ProcessEmailNotificationsEvent : EventBase, IEvent
|
||||
{
|
||||
public string Name { get; set; } = nameof(ProcessEmailNotificationsEvent);
|
||||
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
private ProcessEmailNotificationsEvent(int maxRecords = 1000) => MaxRecords = maxRecords;
|
||||
|
||||
public static ProcessEmailNotificationsEvent Create(int maxRecords = 1000) => new(maxRecords);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using LiteCharms.Features.Shop.Notifications.Models;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Notifications;
|
||||
|
||||
public interface INotificationService
|
||||
{
|
||||
Task<Result<Guid>> CreateNotificationAsync(CreateNotification request, CancellationToken cancellationToken = default);
|
||||
Task<Result> UpdateNotificationAsync(UpdateNotification request, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Models;
|
||||
|
||||
public class Notification
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public DateTimeOffset? UpdatedAt { get; set; }
|
||||
|
||||
public NotificationDirection Direction { get; set; }
|
||||
|
||||
public NotificationPlatforms Platform { get; set; }
|
||||
|
||||
public Priorities Priority { get; set; }
|
||||
|
||||
public CorrelationIdTypes CorrelationIdType { get; set; }
|
||||
|
||||
public string? Sender { get; set; }
|
||||
|
||||
public string? SenderName { get; set; }
|
||||
|
||||
public string? Subject { get; set; }
|
||||
|
||||
public string? Message { get; set; }
|
||||
|
||||
public string? Recipient { get; set; }
|
||||
|
||||
public string? RecipientAddress { get; set; }
|
||||
|
||||
public string? CorrelationId { get; set; }
|
||||
|
||||
public bool IsHtml { get; set; }
|
||||
|
||||
public bool IsInternal { get; set; }
|
||||
|
||||
public bool Processed { get; set; }
|
||||
|
||||
public bool HasError { get; set; }
|
||||
|
||||
public string[]? Errors { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications.Models;
|
||||
|
||||
public record CreateNotification
|
||||
{
|
||||
public NotificationDirection Direction { get; set; }
|
||||
|
||||
public string? Sender { get; set; }
|
||||
|
||||
public string? SenderAddress { get; set; }
|
||||
|
||||
public string? Subject { get; set; }
|
||||
|
||||
public string? Message { get; set; }
|
||||
|
||||
public NotificationPlatforms Platform { get; set; }
|
||||
|
||||
public Priorities Priority { get; set; }
|
||||
|
||||
public string? Recipient { get; set; }
|
||||
|
||||
public string? RecipientAddress { get; set; }
|
||||
|
||||
public string? CorrelationId { get; set; }
|
||||
|
||||
public CorrelationIdTypes CorrelationIdType { get; set; }
|
||||
|
||||
public bool IsInternal { get; set; }
|
||||
|
||||
public bool IsHtml { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateNotification
|
||||
{
|
||||
public Guid NotificationId { get; set; }
|
||||
|
||||
public bool Processed { get; set; }
|
||||
|
||||
public bool HasError { get; set; }
|
||||
|
||||
public string[]? Errors { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace LiteCharms.Features.Shop.Notifications;
|
||||
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Notifications.Models;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Queries;
|
||||
|
||||
public class GetNotificationQuery : IRequest<Result<Notification>>
|
||||
{
|
||||
public Guid NotificationId { get; set; }
|
||||
|
||||
private GetNotificationQuery(Guid notificationId) => NotificationId = notificationId;
|
||||
|
||||
public static GetNotificationQuery Create(Guid notificationId)
|
||||
{
|
||||
if (notificationId == Guid.Empty)
|
||||
throw new ArgumentException("Notification ID is required.", nameof(notificationId));
|
||||
|
||||
return new(notificationId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using LiteCharms.Features.Shop.Notifications.Models;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Queries;
|
||||
|
||||
public class GetNotificationsQuery : IRequest<Result<Notification[]>>
|
||||
{
|
||||
public DateOnly From { get; set; }
|
||||
|
||||
public DateOnly To { get; set; }
|
||||
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
private GetNotificationsQuery(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
MaxRecords = maxRecords;
|
||||
}
|
||||
|
||||
public static GetNotificationsQuery Create(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
if (from > to)
|
||||
throw new ArgumentException("From date cannot be greater than To date.");
|
||||
|
||||
if(maxRecords <= 0)
|
||||
throw new ArgumentException("MaxRecords must be a positive integer.", nameof(maxRecords));
|
||||
|
||||
return new(from, to, maxRecords);
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Notifications.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Queries.Handlers;
|
||||
|
||||
public class GetNotificationQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetNotificationQuery, Result<Notification>>
|
||||
{
|
||||
public async ValueTask<Result<Notification>> Handle(GetNotificationQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var notification = await context.Notifications.FirstOrDefaultAsync(n => n.Id == request.NotificationId, cancellationToken);
|
||||
|
||||
return notification is not null
|
||||
? Result.Ok(notification.ToModel())
|
||||
: Result.Fail<Notification>(new Error($"Notification with id {request.NotificationId} not found"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Notification>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Notifications.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Notifications.Queries.Handlers;
|
||||
|
||||
public class GetNotificationsQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetNotificationsQuery, Result<Notification[]>>
|
||||
{
|
||||
public async ValueTask<Result<Notification[]>> Handle(GetNotificationsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fromDate = request.From.ToDateTime(TimeOnly.MinValue);
|
||||
var toDate = request.To.ToDateTime(TimeOnly.MaxValue);
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var notifications = await context.Notifications.AsNoTracking()
|
||||
.Where(n => n.CreatedAt >= fromDate && n.CreatedAt <= toDate)
|
||||
.OrderByDescending(n => n.CreatedAt)
|
||||
.Take(request.MaxRecords)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return notifications?.Length > 0
|
||||
? Result.Ok(notifications.Select(n => n.ToModel()).ToArray())
|
||||
: Result.Fail(new Error($"No notifications found for the specified date range {request.From} to {request.To}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
namespace LiteCharms.Features.Orders.Commands;
|
||||
|
||||
public class CreateOrderCommand : IRequest<Result<Guid>>
|
||||
{
|
||||
public Guid CustomerId { get; set; }
|
||||
|
||||
public Guid ShoppingCartId { get; set; }
|
||||
|
||||
public Guid? QuoteId { get; set; }
|
||||
|
||||
public string[]? Requirements { get; set; }
|
||||
|
||||
public string[]? Notes { get; set; }
|
||||
|
||||
public string[]? Terms { get; set; }
|
||||
|
||||
public bool DepositRequired { get; set; }
|
||||
|
||||
private CreateOrderCommand(Guid customerId, Guid shoppingCartId, bool depositRequired, Guid? quoteId = null, string[]? requirements = null, string[]? notes = null, string[]? terms = null)
|
||||
{
|
||||
CustomerId = customerId;
|
||||
ShoppingCartId = shoppingCartId;
|
||||
DepositRequired = depositRequired;
|
||||
QuoteId = quoteId;
|
||||
Requirements = requirements;
|
||||
Notes = notes;
|
||||
Terms = terms;
|
||||
}
|
||||
|
||||
public static CreateOrderCommand Create(Guid customerId, Guid shoppingCartId, bool depositRequired, Guid? quoteId = null, string[]? requirements = null, string[]? notes = null, string[]? terms = null)
|
||||
{
|
||||
if (customerId == Guid.Empty)
|
||||
throw new ArgumentException("CustomerId is required.", nameof(customerId));
|
||||
|
||||
if (shoppingCartId == Guid.Empty)
|
||||
throw new ArgumentException("ShoppingCartId is required.", nameof(shoppingCartId));
|
||||
|
||||
return new(customerId, shoppingCartId, depositRequired, quoteId, requirements, notes, terms);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using LiteCharms.Features.Shop;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using LiteCharms.Models;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Commands.Handlers;
|
||||
|
||||
public class CreateOrderCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<CreateOrderCommand, Result<Guid>>
|
||||
{
|
||||
public async ValueTask<Result<Guid>> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if(!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken))
|
||||
return Result.Fail<Guid>(new Error($"Customer {request.CustomerId} does not exist."));
|
||||
|
||||
if(!await context.ShoppingCarts.AnyAsync(sc => sc.Id == request.ShoppingCartId, cancellationToken))
|
||||
return Result.Fail<Guid>(new Error($"Shopping cart {request.ShoppingCartId} does not exist."));
|
||||
|
||||
if(request.QuoteId.HasValue && !await context.Quotes.AnyAsync(q => q.Id == request.QuoteId.Value, cancellationToken))
|
||||
return Result.Fail<Guid>(new Error($"Quote {request.QuoteId.Value} does not exist."));
|
||||
|
||||
var newOrder = context.Orders.Add(new Entities.Order
|
||||
{
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Status = OrderStatus.Pending,
|
||||
CustomerId = request.CustomerId,
|
||||
QuoteId = request.QuoteId,
|
||||
ShoppingCartId = request.ShoppingCartId,
|
||||
DepositRequired = request.DepositRequired,
|
||||
Requirements = request.Requirements,
|
||||
Notes = request.Notes,
|
||||
Terms = request.Terms
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(newOrder.Entity.Id)
|
||||
: Result.Fail<Guid>(new Error($"Failed to create customer {request.CustomerId} order using shopping cart {request.ShoppingCartId}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Guid>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Commands.Handlers;
|
||||
|
||||
public class UpdateOrderStatusCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<UpdateOrderStatusCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(UpdateOrderStatusCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var order = await context.Orders.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken);
|
||||
|
||||
if (order is null)
|
||||
return Result.Fail(new Error($"Order {request.OrderId} not found"));
|
||||
|
||||
order.Status = request.Status;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail(new Error($"Failed to update order {request.OrderId}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using LiteCharms.Features.Shop;
|
||||
using LiteCharms.Models;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Commands;
|
||||
|
||||
public class UpdateOrderStatusCommand : IRequest<Result>
|
||||
{
|
||||
public Guid OrderId { get; set; }
|
||||
|
||||
public OrderStatus Status { get; set; }
|
||||
|
||||
public string? Note { get; set; }
|
||||
|
||||
private UpdateOrderStatusCommand(Guid orderId, OrderStatus status, string? note)
|
||||
{
|
||||
OrderId = orderId;
|
||||
Status = status;
|
||||
Note = note;
|
||||
}
|
||||
|
||||
public static UpdateOrderStatusCommand Create(Guid orderId, OrderStatus status, string? note)
|
||||
{
|
||||
if (orderId == Guid.Empty)
|
||||
throw new ArgumentException("OrderId is required.", nameof(orderId));
|
||||
|
||||
if (!Enum.IsDefined(typeof(OrderStatus), status))
|
||||
throw new ArgumentException("Invalid order status.", nameof(status));
|
||||
|
||||
return new(orderId, status, note);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using LiteCharms.Features.Shop.Customers.Entities;
|
||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
|
||||
[EntityTypeConfiguration<OrderConfiguration, Order>]
|
||||
public class Order : Models.Order
|
||||
{
|
||||
public virtual ICollection<OrderRefund>? Refunds { get; set; }
|
||||
|
||||
public virtual Customer? Customer { get; set; }
|
||||
|
||||
public virtual Quote? Quote { get; set; }
|
||||
|
||||
public virtual ShoppingCart? ShoppingCart { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
|
||||
public class OrderConfiguration : IEntityTypeConfiguration<Order>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Order> builder)
|
||||
{
|
||||
builder.ToTable(nameof(Order));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.CustomerId).IsRequired();
|
||||
builder.Property(f => f.Status).HasConversion<int>().IsRequired();
|
||||
builder.Property(f => f.Requirements).HasColumnType("jsonb").IsRequired(false);
|
||||
builder.Property(f => f.Notes).HasColumnType("jsonb").IsRequired(false);
|
||||
builder.Property(f => f.Terms).HasColumnType("jsonb").IsRequired(false);
|
||||
builder.Property(f => f.InvoiceUrl).IsRequired(false).HasMaxLength(2048);
|
||||
|
||||
builder.HasOne(o => o.Customer)
|
||||
.WithMany(c => c.Orders)
|
||||
.HasForeignKey(o => o.CustomerId)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
|
||||
[EntityTypeConfiguration<OrderRefundConfiguration, OrderRefund>]
|
||||
public class OrderRefund : Models.OrderRefund
|
||||
{
|
||||
public virtual Order? Order { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Entities;
|
||||
|
||||
public class OrderRefundConfiguration : IEntityTypeConfiguration<OrderRefund>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<OrderRefund> builder)
|
||||
{
|
||||
builder.ToTable(nameof(OrderRefund));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.OrderId).IsRequired();
|
||||
builder.Property(f => f.Reason).IsRequired();
|
||||
builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2);
|
||||
|
||||
builder.HasOne(r => r.Order)
|
||||
.WithMany(o => o.Refunds)
|
||||
.HasForeignKey(r => r.OrderId)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Models;
|
||||
|
||||
public class Order
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public DateTimeOffset? UpdatedAt { get; set; }
|
||||
|
||||
public Guid CustomerId { get; set; }
|
||||
|
||||
public OrderStatus Status { get; set; }
|
||||
|
||||
public string[]? Requirements { get; set; }
|
||||
|
||||
public string[]? Notes { get; set; }
|
||||
|
||||
public string[]? Terms { get; set; }
|
||||
|
||||
public string? InvoiceUrl { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Models;
|
||||
|
||||
public class OrderRefund
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public Guid OrderId { get; set; }
|
||||
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Queries;
|
||||
|
||||
public class GetCustomerOrdersQuery : IRequest<Result<Order[]>>
|
||||
{
|
||||
public Guid CustomerId { get; }
|
||||
|
||||
private GetCustomerOrdersQuery(Guid customerId) => CustomerId = customerId;
|
||||
|
||||
public static GetCustomerOrdersQuery Create(Guid customerId)
|
||||
{
|
||||
if (customerId == Guid.Empty)
|
||||
throw new ArgumentException("CustomerId is required.", nameof(customerId));
|
||||
|
||||
return new(customerId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Queries;
|
||||
|
||||
public class GetOrderRefundQuery : IRequest<Result<OrderRefund>>
|
||||
{
|
||||
public Guid OrderId { get; set; }
|
||||
|
||||
public Guid OrderRefundId { get; set; }
|
||||
|
||||
private GetOrderRefundQuery(Guid orderId, Guid orderRefundId)
|
||||
{
|
||||
OrderId = orderId;
|
||||
OrderRefundId = orderRefundId;
|
||||
}
|
||||
|
||||
public static GetOrderRefundQuery Create(Guid orderId, Guid orderRefundId)
|
||||
{
|
||||
if (orderId == Guid.Empty)
|
||||
throw new ArgumentException("OrderId is required.", nameof(orderId));
|
||||
|
||||
if (orderRefundId == Guid.Empty)
|
||||
throw new ArgumentException("OrderRefundId is required.", nameof(orderRefundId));
|
||||
|
||||
return new(orderId, orderRefundId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Queries;
|
||||
|
||||
public class GetOrdersQuery : IRequest<Result<Order[]>>
|
||||
{
|
||||
public DateOnly From { get; set; }
|
||||
|
||||
public DateOnly To { get; set; }
|
||||
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
private GetOrdersQuery(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
From = from;
|
||||
To = to;
|
||||
MaxRecords = maxRecords;
|
||||
}
|
||||
|
||||
public static GetOrdersQuery Create(DateOnly from, DateOnly to, int maxRecords = 1000)
|
||||
{
|
||||
if (from > to)
|
||||
throw new ArgumentException("From date cannot be greater than To date.");
|
||||
|
||||
if(maxRecords <= 0)
|
||||
throw new ArgumentException("MaxRecords must be a positive integer.");
|
||||
|
||||
return new(from, to, maxRecords);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
||||
|
||||
public class GetCustomerOrdersQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetCustomerOrdersQuery, Result<Order[]>>
|
||||
{
|
||||
public async ValueTask<Result<Order[]>> Handle(GetCustomerOrdersQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if(!await context.Customers.AsNoTracking().AnyAsync(c => c.Id == request.CustomerId, cancellationToken))
|
||||
return Result.Fail<Order[]>(new Error($"Customer with Id {request.CustomerId} does not exist."));
|
||||
|
||||
var orders = await context.Orders.AsNoTracking()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(o => o.CustomerId == request.CustomerId)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return orders?.Length > 0
|
||||
? Result.Ok(orders.Select(o => o.ToModel()).ToArray())
|
||||
: Result.Fail<Order[]>(new Error($"No orders found for customer with Id {request.CustomerId}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Order[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
||||
|
||||
public class GetOrderRefundQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetOrderRefundQuery, Result<OrderRefund>>
|
||||
{
|
||||
public async ValueTask<Result<OrderRefund>> Handle(GetOrderRefundQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var refund = await context.OrderRefunds.AsNoTracking()
|
||||
.FirstOrDefaultAsync(r => r.OrderId == request.OrderId && r.Id == request.OrderRefundId, cancellationToken);
|
||||
|
||||
return refund is not null
|
||||
? Result.Ok(refund.ToModel())
|
||||
: Result.Fail<OrderRefund>(new Error($"Refund {request.OrderRefundId} not found for the given OrderId: {request.OrderId}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<OrderRefund>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Orders.Queries.Handlers;
|
||||
|
||||
public class GetOrdersQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetOrdersQuery, Result<Order[]>>
|
||||
{
|
||||
public async ValueTask<Result<Order[]>> Handle(GetOrdersQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fromDate = request.From.ToDateTime(TimeOnly.MinValue);
|
||||
var toDate = request.To.ToDateTime(TimeOnly.MaxValue);
|
||||
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var orders = await context.Orders
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate)
|
||||
.Take(request.MaxRecords)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
return orders?.Length > 0
|
||||
? Result.Ok(orders.Select(o => o.ToModel()).ToArray())
|
||||
: Result.Fail<Order[]>(new Error($"No orders found for the specified date range {request.From} - {request.To}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Order[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using LiteCharms.Features.Shop.Orders.Refunds.Commands;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Commands.Handlers;
|
||||
|
||||
public class RefundCustomerCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<RefundCustomerCommand, Result<Guid>>
|
||||
{
|
||||
public async ValueTask<Result<Guid>> Handle(RefundCustomerCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if(!await context.Orders.AnyAsync(o => o.Id == request.OrderId, cancellationToken))
|
||||
return Result.Fail<Guid>(new Error($"Order with Id: {request.OrderId} does not exist"));
|
||||
|
||||
if (!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken))
|
||||
return Result.Fail<Guid>(new Error($"Customer with Id: {request.CustomerId} does not exist"));
|
||||
|
||||
if(!await context.Orders.AnyAsync(o => o.Id == request.OrderId && o.CustomerId == request.CustomerId, cancellationToken))
|
||||
return Result.Fail<Guid>(new Error($"Order with Id: {request.OrderId} does not belong to Customer with Id: {request.CustomerId}"));
|
||||
|
||||
var refund = context.OrderRefunds.Add(new Entities.OrderRefund
|
||||
{
|
||||
OrderId = request.OrderId,
|
||||
Reason = request.Reason,
|
||||
Amount = request.Amount
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(refund.Entity.Id)
|
||||
: Result.Fail<Guid>(new Error($"Failed to create refund for OrderId: {request.OrderId}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Guid>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using LiteCharms.Features.Shop.Orders.Refunds.Commands;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Commands.Handlers;
|
||||
|
||||
public class UpdateOrderRefundCommandHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<UpdateOrderRefundCommand, Result>
|
||||
{
|
||||
public async ValueTask<Result> Handle(UpdateOrderRefundCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var refund = await context.OrderRefunds.FirstOrDefaultAsync(r => r.Id == request.OrderRefundId, cancellationToken);
|
||||
|
||||
if (refund is null)
|
||||
return Result.Fail($"Order refund not found with id {request.OrderRefundId}");
|
||||
|
||||
refund.Reason = request.Reason;
|
||||
refund.Amount = request.Amount;
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail($"Failed to update order refund {request.OrderRefundId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Commands;
|
||||
|
||||
public class RefundCustomerCommand : IRequest<Result<Guid>>
|
||||
{
|
||||
public Guid OrderId { get; set; }
|
||||
|
||||
public Guid CustomerId { get; set; }
|
||||
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
private RefundCustomerCommand(Guid orderId, Guid customerId, string? reason, decimal amount)
|
||||
{
|
||||
OrderId = orderId;
|
||||
CustomerId = customerId;
|
||||
Reason = reason;
|
||||
Amount = amount;
|
||||
CustomerId = customerId;
|
||||
}
|
||||
|
||||
public static RefundCustomerCommand Create(Guid orderId, Guid customerId, string? reason, decimal amount)
|
||||
{
|
||||
if (orderId == Guid.Empty)
|
||||
throw new ArgumentException("OrderId is required", nameof(orderId));
|
||||
|
||||
if (customerId == Guid.Empty)
|
||||
throw new ArgumentException("CustomerId is required", nameof(customerId));
|
||||
|
||||
if (amount <= 0)
|
||||
throw new ArgumentException("Amount must be greater than zero", nameof(amount));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
throw new ArgumentException("Reason is required", nameof(reason));
|
||||
|
||||
return new(orderId, customerId, reason, amount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Commands;
|
||||
|
||||
public class UpdateOrderRefundCommand : IRequest<Result>
|
||||
{
|
||||
public Guid OrderRefundId { get; set; }
|
||||
|
||||
public string? Reason { get; set; }
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
|
||||
private UpdateOrderRefundCommand(Guid orderRefundId, string? reason, decimal amount)
|
||||
{
|
||||
OrderRefundId = orderRefundId;
|
||||
Reason = reason;
|
||||
Amount = amount;
|
||||
}
|
||||
|
||||
public static UpdateOrderRefundCommand Create(Guid orderRefundId, string? reason, decimal amount)
|
||||
{
|
||||
if (orderRefundId == Guid.Empty)
|
||||
throw new ArgumentException("Order refund id is required.", nameof(orderRefundId));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
throw new ArgumentException("Refund update reason is required");
|
||||
|
||||
return new(orderRefundId, reason, amount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Queries;
|
||||
|
||||
public class GetCustomerRefundsQuery : IRequest<Result<OrderRefund[]>>
|
||||
{
|
||||
public Guid CustomerId { get; set; }
|
||||
|
||||
private GetCustomerRefundsQuery(Guid customerId) => CustomerId = customerId;
|
||||
|
||||
public static GetCustomerRefundsQuery Create(Guid customerId)
|
||||
{
|
||||
if (customerId == Guid.Empty)
|
||||
throw new ArgumentException("CustomerId is required.", nameof(customerId));
|
||||
|
||||
return new(customerId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Queries;
|
||||
|
||||
public class GetRefundQuery : IRequest<Result<OrderRefund>>
|
||||
{
|
||||
public Guid OrderRefundId { get; set; }
|
||||
|
||||
private GetRefundQuery(Guid orderRefundId) => OrderRefundId = orderRefundId;
|
||||
|
||||
public static GetRefundQuery Create(Guid orderRefundId)
|
||||
{
|
||||
if(orderRefundId == Guid.Empty)
|
||||
throw new ArgumentException("Customer ID is required.", nameof(orderRefundId));
|
||||
|
||||
return new(orderRefundId);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.Shop.Orders.Refunds.Queries;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Queries.Handlers;
|
||||
|
||||
public class GetCustomerRefundsQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetCustomerRefundsQuery, Result<OrderRefund[]>>
|
||||
{
|
||||
public async ValueTask<Result<OrderRefund[]>> Handle(GetCustomerRefundsQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if(!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken))
|
||||
return Result.Fail<OrderRefund[]>(new Error($"Customer with Id {request.CustomerId} does not exist."));
|
||||
|
||||
var refunds = await context.OrderRefunds.AsNoTracking().AsSplitQuery()
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.Where(r => r.Order!.CustomerId == request.CustomerId).ToArrayAsync(cancellationToken);
|
||||
|
||||
return refunds?.Length > 0
|
||||
? Result.Ok(refunds.Select(r => r.ToModel()).ToArray())
|
||||
: Result.Fail<OrderRefund[]>(new Error($"No refunds found for customer with Id {request.CustomerId}."));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<OrderRefund[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using LiteCharms.Extensions;
|
||||
using LiteCharms.Features.Shop.Orders.Models;
|
||||
using LiteCharms.Features.Shop.Orders.Refunds.Queries;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Orders.Refunds.Queries.Handlers;
|
||||
|
||||
public class GetRefundQueryHandler(IDbContextFactory<ShopDbContext> contextFactory) : IRequestHandler<GetRefundQuery, Result<OrderRefund>>
|
||||
{
|
||||
public async ValueTask<Result<OrderRefund>> Handle(GetRefundQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var refund = await context.OrderRefunds.AsNoTracking().FirstOrDefaultAsync(r => r.Id == request.OrderRefundId, cancellationToken);
|
||||
|
||||
return refund is not null
|
||||
? Result.Ok(refund.ToModel())
|
||||
: Result.Fail<OrderRefund>($"Order refund could not be found with id {request.OrderRefundId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<OrderRefund>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
+771
@@ -0,0 +1,771 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(ShopDbContext))]
|
||||
[Migration("20260512065421_Init")]
|
||||
partial class Init
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Customer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("City")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Company")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Country")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Discord")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LinkedIn")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PostalCode")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Region")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Slack")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Tax")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Website")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Whatsapp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Customer", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Lead", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long?>("AdGroupId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("AdName")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AppClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("AttributionHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("CampaignId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClickLocation")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid?>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long?>("FeedItemId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Source")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("TargetId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("WebClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.ToTable("Lead", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Notification", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("CorrelationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("CorrelationIdType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<int>("Direction")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.PrimitiveCollection<string>("Errors")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<bool>("HasError")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsHtml")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsInternal")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("Processed")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<string>("Recipient")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RecipientAddress")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Sender")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("SenderName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Notification", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Order", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("InvoiceUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.PrimitiveCollection<string>("Notes")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.PrimitiveCollection<string>("Requirements")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.PrimitiveCollection<string>("Terms")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.ToTable("Order", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal>("Amount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("OrderRefund", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Package", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Package", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.PackageItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("PackageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ProductPriceId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PackageId");
|
||||
|
||||
b.HasIndex("ProductPriceId");
|
||||
|
||||
b.ToTable("PackageItem", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.PrimitiveCollection<string>("Thumbnails")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Product", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<decimal>("Discount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<Guid>("ProductId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProductId");
|
||||
|
||||
b.ToTable("ProductPrice", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Quote", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset?>("ExpiredAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("InvoiceUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<Guid?>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.HasIndex("OrderId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ShoppingCartId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Quote", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.HasIndex("OrderId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ShoppingCart", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("ProductPriceId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProductPriceId");
|
||||
|
||||
b.HasIndex("ShoppingCartId");
|
||||
|
||||
b.ToTable("ShoppingCartItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartPackage", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("PackageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PackageId");
|
||||
|
||||
b.HasIndex("ShoppingCartId");
|
||||
|
||||
b.ToTable("ShoppingCartPackage", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Lead", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("Leads")
|
||||
.HasForeignKey("CustomerId");
|
||||
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Order", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("Orders")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Order", "Order")
|
||||
.WithMany("Refunds")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.PackageItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Package", "Package")
|
||||
.WithMany("PackageItems")
|
||||
.HasForeignKey("PackageId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductPriceId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Package");
|
||||
|
||||
b.Navigation("ProductPrice");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Product", "Product")
|
||||
.WithMany("ProductPrices")
|
||||
.HasForeignKey("ProductId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Product");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Quote", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("Quotes")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.Order", "Order")
|
||||
.WithOne("Quote")
|
||||
.HasForeignKey("LiteCharms.Entities.Quote", "OrderId");
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithOne("Quote")
|
||||
.HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId");
|
||||
|
||||
b.Navigation("Customer");
|
||||
|
||||
b.Navigation("Order");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("ShoppingCarts")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.Order", "Order")
|
||||
.WithOne("ShoppingCart")
|
||||
.HasForeignKey("LiteCharms.Entities.ShoppingCart", "OrderId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Customer");
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductPriceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithMany("ShoppingCartItems")
|
||||
.HasForeignKey("ShoppingCartId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ProductPrice");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartPackage", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Package", "Package")
|
||||
.WithMany()
|
||||
.HasForeignKey("PackageId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithMany("ShoppingCartPackages")
|
||||
.HasForeignKey("ShoppingCartId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Package");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Customer", b =>
|
||||
{
|
||||
b.Navigation("Leads");
|
||||
|
||||
b.Navigation("Orders");
|
||||
|
||||
b.Navigation("Quotes");
|
||||
|
||||
b.Navigation("ShoppingCarts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Order", b =>
|
||||
{
|
||||
b.Navigation("Quote");
|
||||
|
||||
b.Navigation("Refunds");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Package", b =>
|
||||
{
|
||||
b.Navigation("PackageItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Product", b =>
|
||||
{
|
||||
b.Navigation("ProductPrices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.Navigation("Quote");
|
||||
|
||||
b.Navigation("ShoppingCartItems");
|
||||
|
||||
b.Navigation("ShoppingCartPackages");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Init : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Customer",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
Company = table.Column<string>(type: "text", nullable: true),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
LastName = table.Column<string>(type: "text", nullable: false),
|
||||
Tax = table.Column<string>(type: "text", nullable: true),
|
||||
Email = table.Column<string>(type: "text", nullable: false),
|
||||
Discord = table.Column<string>(type: "text", nullable: true),
|
||||
Slack = table.Column<string>(type: "text", nullable: true),
|
||||
LinkedIn = table.Column<string>(type: "text", nullable: true),
|
||||
Whatsapp = table.Column<string>(type: "text", nullable: true),
|
||||
Website = table.Column<string>(type: "text", nullable: true),
|
||||
Phone = table.Column<string>(type: "text", nullable: true),
|
||||
Address = table.Column<string>(type: "text", nullable: true),
|
||||
City = table.Column<string>(type: "text", nullable: true),
|
||||
Region = table.Column<string>(type: "text", nullable: true),
|
||||
Country = table.Column<string>(type: "text", nullable: true),
|
||||
PostalCode = table.Column<string>(type: "text", nullable: true),
|
||||
Active = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Customer", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Notification",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
Direction = table.Column<int>(type: "integer", nullable: false),
|
||||
Platform = table.Column<int>(type: "integer", nullable: false),
|
||||
Priority = table.Column<int>(type: "integer", nullable: false),
|
||||
CorrelationIdType = table.Column<int>(type: "integer", nullable: false),
|
||||
Sender = table.Column<string>(type: "text", nullable: false),
|
||||
SenderName = table.Column<string>(type: "text", nullable: true),
|
||||
Subject = table.Column<string>(type: "text", nullable: false),
|
||||
Message = table.Column<string>(type: "text", nullable: false),
|
||||
Recipient = table.Column<string>(type: "text", nullable: false),
|
||||
RecipientAddress = table.Column<string>(type: "text", nullable: false),
|
||||
CorrelationId = table.Column<string>(type: "text", nullable: false),
|
||||
IsHtml = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||
IsInternal = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true),
|
||||
Processed = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||
HasError = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||
Errors = table.Column<string>(type: "jsonb", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Notification", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Package",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Summary = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
|
||||
Description = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: false),
|
||||
ImageUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||
Active = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Package", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Product",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Summary = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
|
||||
Description = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: false),
|
||||
ImageUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||
Thumbnails = table.Column<string>(type: "jsonb", nullable: true),
|
||||
Active = table.Column<bool>(type: "boolean", nullable: false, defaultValue: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Product", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Lead",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
CustomerId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
Source = table.Column<string>(type: "text", nullable: true),
|
||||
ClickId = table.Column<string>(type: "text", nullable: true),
|
||||
WebClickId = table.Column<string>(type: "text", nullable: true),
|
||||
AppClickId = table.Column<string>(type: "text", nullable: true),
|
||||
CampaignId = table.Column<long>(type: "bigint", nullable: true),
|
||||
AdGroupId = table.Column<long>(type: "bigint", nullable: true),
|
||||
AdName = table.Column<long>(type: "bigint", nullable: true),
|
||||
TargetId = table.Column<long>(type: "bigint", nullable: true),
|
||||
FeedItemId = table.Column<long>(type: "bigint", nullable: true),
|
||||
ClickLocation = table.Column<string>(type: "text", nullable: true),
|
||||
AttributionHash = table.Column<string>(type: "text", nullable: false),
|
||||
Status = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Lead", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Lead_Customer_CustomerId",
|
||||
column: x => x.CustomerId,
|
||||
principalTable: "Customer",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Order",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
CustomerId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Status = table.Column<int>(type: "integer", nullable: false),
|
||||
Requirements = table.Column<string>(type: "jsonb", nullable: true),
|
||||
Notes = table.Column<string>(type: "jsonb", nullable: true),
|
||||
Terms = table.Column<string>(type: "jsonb", nullable: true),
|
||||
InvoiceUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Order", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Order_Customer_CustomerId",
|
||||
column: x => x.CustomerId,
|
||||
principalTable: "Customer",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ProductPrice",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
ProductId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Price = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
Discount = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
|
||||
Active = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ProductPrice", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ProductPrice_Product_ProductId",
|
||||
column: x => x.ProductId,
|
||||
principalTable: "Product",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OrderRefund",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
OrderId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Reason = table.Column<string>(type: "text", nullable: false),
|
||||
Amount = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OrderRefund", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OrderRefund_Order_OrderId",
|
||||
column: x => x.OrderId,
|
||||
principalTable: "Order",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ShoppingCart",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
CustomerId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
OrderId = table.Column<Guid>(type: "uuid", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ShoppingCart", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ShoppingCart_Customer_CustomerId",
|
||||
column: x => x.CustomerId,
|
||||
principalTable: "Customer",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ShoppingCart_Order_OrderId",
|
||||
column: x => x.OrderId,
|
||||
principalTable: "Order",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PackageItem",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
PackageId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ProductPriceId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
Active = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PackageItem", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_PackageItem_Package_PackageId",
|
||||
column: x => x.PackageId,
|
||||
principalTable: "Package",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_PackageItem_ProductPrice_ProductPriceId",
|
||||
column: x => x.ProductPriceId,
|
||||
principalTable: "ProductPrice",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Quote",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
ExpiredAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
|
||||
CustomerId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
OrderId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
ShoppingCartId = table.Column<Guid>(type: "uuid", nullable: true),
|
||||
Status = table.Column<int>(type: "integer", nullable: false),
|
||||
InvoiceUrl = table.Column<string>(type: "character varying(2048)", maxLength: 2048, nullable: true),
|
||||
Reason = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Quote", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Quote_Customer_CustomerId",
|
||||
column: x => x.CustomerId,
|
||||
principalTable: "Customer",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_Quote_Order_OrderId",
|
||||
column: x => x.OrderId,
|
||||
principalTable: "Order",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_Quote_ShoppingCart_ShoppingCartId",
|
||||
column: x => x.ShoppingCartId,
|
||||
principalTable: "ShoppingCart",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ShoppingCartItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ShoppingCartId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ProductPriceId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||
Quantity = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ShoppingCartItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ShoppingCartItems_ProductPrice_ProductPriceId",
|
||||
column: x => x.ProductPriceId,
|
||||
principalTable: "ProductPrice",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ShoppingCartItems_ShoppingCart_ShoppingCartId",
|
||||
column: x => x.ShoppingCartId,
|
||||
principalTable: "ShoppingCart",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ShoppingCartPackage",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"),
|
||||
ShoppingCartId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
PackageId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ShoppingCartPackage", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ShoppingCartPackage_Package_PackageId",
|
||||
column: x => x.PackageId,
|
||||
principalTable: "Package",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_ShoppingCartPackage_ShoppingCart_ShoppingCartId",
|
||||
column: x => x.ShoppingCartId,
|
||||
principalTable: "ShoppingCart",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Lead_CustomerId",
|
||||
table: "Lead",
|
||||
column: "CustomerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Order_CustomerId",
|
||||
table: "Order",
|
||||
column: "CustomerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OrderRefund_OrderId",
|
||||
table: "OrderRefund",
|
||||
column: "OrderId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PackageItem_PackageId",
|
||||
table: "PackageItem",
|
||||
column: "PackageId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_PackageItem_ProductPriceId",
|
||||
table: "PackageItem",
|
||||
column: "ProductPriceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ProductPrice_ProductId",
|
||||
table: "ProductPrice",
|
||||
column: "ProductId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Quote_CustomerId",
|
||||
table: "Quote",
|
||||
column: "CustomerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Quote_OrderId",
|
||||
table: "Quote",
|
||||
column: "OrderId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Quote_ShoppingCartId",
|
||||
table: "Quote",
|
||||
column: "ShoppingCartId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ShoppingCart_CustomerId",
|
||||
table: "ShoppingCart",
|
||||
column: "CustomerId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ShoppingCart_OrderId",
|
||||
table: "ShoppingCart",
|
||||
column: "OrderId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ShoppingCartItems_ProductPriceId",
|
||||
table: "ShoppingCartItems",
|
||||
column: "ProductPriceId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ShoppingCartItems_ShoppingCartId",
|
||||
table: "ShoppingCartItems",
|
||||
column: "ShoppingCartId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ShoppingCartPackage_PackageId",
|
||||
table: "ShoppingCartPackage",
|
||||
column: "PackageId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ShoppingCartPackage_ShoppingCartId",
|
||||
table: "ShoppingCartPackage",
|
||||
column: "ShoppingCartId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Lead");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Notification");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OrderRefund");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PackageItem");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Quote");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ShoppingCartItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ShoppingCartPackage");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ProductPrice");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Package");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ShoppingCart");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Product");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Order");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Customer");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,768 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using LiteCharms.Features.Shop.Postgres;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres.Migrations
|
||||
{
|
||||
[DbContext(typeof(ShopDbContext))]
|
||||
partial class ShopDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.7")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Customer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("City")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Company")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Country")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Discord")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("LinkedIn")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Phone")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("PostalCode")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Region")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Slack")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Tax")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Website")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Whatsapp")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Customer", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Lead", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long?>("AdGroupId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long?>("AdName")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("AppClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("AttributionHash")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<long?>("CampaignId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("ClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ClickLocation")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid?>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long?>("FeedItemId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Source")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long?>("TargetId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("WebClickId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.ToTable("Lead", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Notification", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("CorrelationId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("CorrelationIdType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<int>("Direction")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.PrimitiveCollection<string>("Errors")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<bool>("HasError")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsHtml")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<bool>("IsInternal")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool>("Processed")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<string>("Recipient")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("RecipientAddress")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Sender")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("SenderName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Notification", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Order", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("InvoiceUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.PrimitiveCollection<string>("Notes")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.PrimitiveCollection<string>("Requirements")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.PrimitiveCollection<string>("Terms")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.ToTable("Order", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<decimal>("Amount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("OrderRefund", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Package", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Package", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.PackageItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("PackageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ProductPriceId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PackageId");
|
||||
|
||||
b.HasIndex("ProductPriceId");
|
||||
|
||||
b.ToTable("PackageItem", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Product", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(true);
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("ImageUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.PrimitiveCollection<string>("Thumbnails")
|
||||
.HasColumnType("jsonb");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Product", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<decimal>("Discount")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasPrecision(18, 2)
|
||||
.HasColumnType("numeric(18,2)");
|
||||
|
||||
b.Property<Guid>("ProductId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProductId");
|
||||
|
||||
b.ToTable("ProductPrice", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Quote", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset?>("ExpiredAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("InvoiceUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<Guid?>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Reason")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.HasIndex("OrderId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ShoppingCartId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Quote", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("CustomerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("OrderId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CustomerId");
|
||||
|
||||
b.HasIndex("OrderId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ShoppingCart", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("ProductPriceId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ProductPriceId");
|
||||
|
||||
b.HasIndex("ShoppingCartId");
|
||||
|
||||
b.ToTable("ShoppingCartItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartPackage", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTimeOffset>("CreatedAt")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasDefaultValueSql("now()");
|
||||
|
||||
b.Property<Guid>("PackageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ShoppingCartId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PackageId");
|
||||
|
||||
b.HasIndex("ShoppingCartId");
|
||||
|
||||
b.ToTable("ShoppingCartPackage", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Lead", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("Leads")
|
||||
.HasForeignKey("CustomerId");
|
||||
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Order", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("Orders")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Customer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Order", "Order")
|
||||
.WithMany("Refunds")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.PackageItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Package", "Package")
|
||||
.WithMany("PackageItems")
|
||||
.HasForeignKey("PackageId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductPriceId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Package");
|
||||
|
||||
b.Navigation("ProductPrice");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Product", "Product")
|
||||
.WithMany("ProductPrices")
|
||||
.HasForeignKey("ProductId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Product");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Quote", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("Quotes")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.Order", "Order")
|
||||
.WithOne("Quote")
|
||||
.HasForeignKey("LiteCharms.Entities.Quote", "OrderId");
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithOne("Quote")
|
||||
.HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId");
|
||||
|
||||
b.Navigation("Customer");
|
||||
|
||||
b.Navigation("Order");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Customer", "Customer")
|
||||
.WithMany("ShoppingCarts")
|
||||
.HasForeignKey("CustomerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.Order", "Order")
|
||||
.WithOne("ShoppingCart")
|
||||
.HasForeignKey("LiteCharms.Entities.ShoppingCart", "OrderId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.Navigation("Customer");
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProductPriceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithMany("ShoppingCartItems")
|
||||
.HasForeignKey("ShoppingCartId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("ProductPrice");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCartPackage", b =>
|
||||
{
|
||||
b.HasOne("LiteCharms.Entities.Package", "Package")
|
||||
.WithMany()
|
||||
.HasForeignKey("PackageId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart")
|
||||
.WithMany("ShoppingCartPackages")
|
||||
.HasForeignKey("ShoppingCartId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Package");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Customer", b =>
|
||||
{
|
||||
b.Navigation("Leads");
|
||||
|
||||
b.Navigation("Orders");
|
||||
|
||||
b.Navigation("Quotes");
|
||||
|
||||
b.Navigation("ShoppingCarts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Order", b =>
|
||||
{
|
||||
b.Navigation("Quote");
|
||||
|
||||
b.Navigation("Refunds");
|
||||
|
||||
b.Navigation("ShoppingCart");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Package", b =>
|
||||
{
|
||||
b.Navigation("PackageItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.Product", b =>
|
||||
{
|
||||
b.Navigation("ProductPrices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b =>
|
||||
{
|
||||
b.Navigation("Quote");
|
||||
|
||||
b.Navigation("ShoppingCartItems");
|
||||
|
||||
b.Navigation("ShoppingCartPackages");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using LiteCharms.Features.Shop.CartPackages.Entities;
|
||||
using LiteCharms.Features.Shop.Customers.Entities;
|
||||
using LiteCharms.Features.Shop.Leads.Entities;
|
||||
using LiteCharms.Features.Shop.Notifications.Entities;
|
||||
using LiteCharms.Features.Shop.Orders.Entities;
|
||||
using LiteCharms.Features.Shop.Products.Entities;
|
||||
using LiteCharms.Features.Shop.Quotes.Entities;
|
||||
using LiteCharms.Features.Shop.ShoppingCarts.Entities;
|
||||
|
||||
namespace LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
public class ShopDbContext(DbContextOptions<ShopDbContext> options) : DbContext(options)
|
||||
{
|
||||
public DbSet<Customer> Customers { get; set; }
|
||||
|
||||
public DbSet<Lead> Leads { get; set; }
|
||||
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
|
||||
public DbSet<OrderRefund> OrderRefunds { get; set; }
|
||||
|
||||
public DbSet<Product> Products { get; set; }
|
||||
|
||||
public DbSet<ProductPrice> ProductPrices { get; set; }
|
||||
|
||||
public DbSet<Notification> Notifications { get; set; }
|
||||
|
||||
public DbSet<Quote> Quotes { get; set; }
|
||||
|
||||
public DbSet<ShoppingCart> ShoppingCarts { get; set; }
|
||||
|
||||
public DbSet<ShoppingCartItem> ShoppingCartItems { get; set; }
|
||||
|
||||
public DbSet<Package> Packages { get; set; }
|
||||
|
||||
public DbSet<PackageItem> PackageItems { get; set; }
|
||||
|
||||
public DbSet<ShoppingCartPackage> ShoppingCartPackages { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace LiteCharms.Features.Shop.Postgres;
|
||||
|
||||
public class ShopDbContextFactory : IDesignTimeDbContextFactory<ShopDbContext>
|
||||
{
|
||||
public ShopDbContext CreateDbContext(string[] args)
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddUserSecrets(typeof(ShopDbContext).Assembly)
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<ShopDbContext>();
|
||||
optionsBuilder.UseNpgsql(configuration.GetConnectionString("PostgresShop"));
|
||||
|
||||
return new ShopDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
|
||||
[EntityTypeConfiguration<ProductConfiguration, Product>]
|
||||
public class Product : Models.Product
|
||||
{
|
||||
public virtual ICollection<ProductPrice>? ProductPrices { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
|
||||
public class ProductConfiguration : IEntityTypeConfiguration<Product>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Product> builder)
|
||||
{
|
||||
builder.ToTable(nameof(Product));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.Name).IsRequired();
|
||||
builder.Property(f => f.Summary).IsRequired().HasMaxLength(512);
|
||||
builder.Property(f => f.Description).IsRequired().HasMaxLength(2048);
|
||||
builder.Property(f => f.ImageUrl).IsRequired(false).HasMaxLength(2048);
|
||||
builder.Property(f => f.Thumbnails).HasColumnType("jsonb").IsRequired(false);
|
||||
builder.Property(f => f.Active).HasDefaultValue(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
|
||||
[EntityTypeConfiguration<ProductPriceConfiguration, ProductPrice>]
|
||||
public class ProductPrice : Models.ProductPrice
|
||||
{
|
||||
public virtual Product? Product { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Entities;
|
||||
|
||||
public class ProductPriceConfiguration : IEntityTypeConfiguration<ProductPrice>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<ProductPrice> builder)
|
||||
{
|
||||
builder.ToTable(nameof(ProductPrice));
|
||||
|
||||
builder.HasKey(f => f.Id);
|
||||
builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()");
|
||||
builder.Property(f => f.UpdatedAt).IsRequired(false);
|
||||
builder.Property(f => f.ProductId).IsRequired();
|
||||
builder.Property(f => f.Price).IsRequired().HasPrecision(18, 2);
|
||||
builder.Property(f => f.Discount).HasPrecision(18, 2);
|
||||
builder.Property(f => f.Active);
|
||||
|
||||
builder.HasOne(f => f.Product)
|
||||
.WithMany(f => f.ProductPrices)
|
||||
.HasForeignKey(pp => pp.ProductId)
|
||||
.IsRequired()
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Models;
|
||||
|
||||
public class Product
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Summary { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? ImageUrl { get; set; }
|
||||
|
||||
public string[]? Thumbnails { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace LiteCharms.Features.Shop.Products.Models;
|
||||
|
||||
public class ProductPrice
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public DateTimeOffset CreatedAt { get; set; }
|
||||
|
||||
public DateTimeOffset? UpdatedAt { get; set; }
|
||||
|
||||
public Guid ProductId { get; set; }
|
||||
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public decimal Discount { get; set; }
|
||||
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Products.Models;
|
||||
|
||||
namespace LiteCharms.Features.Products.Queries;
|
||||
|
||||
public class GetProductPriceQuery : IRequest<Result<ProductPrice>>
|
||||
{
|
||||
public Guid ProductId { get; set; }
|
||||
|
||||
private GetProductPriceQuery(Guid productId) => ProductId = productId;
|
||||
|
||||
public static GetProductPriceQuery Create(Guid productId)
|
||||
{
|
||||
if (productId == Guid.Empty)
|
||||
throw new ArgumentException("ProductId is required.", nameof(productId));
|
||||
|
||||
return new(productId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using LiteCharms.Features.Shop.Products.Models;
|
||||
|
||||
namespace LiteCharms.Features.Products.Queries;
|
||||
|
||||
public class GetProductPricesQuery : IRequest<Result<ProductPrice[]>>
|
||||
{
|
||||
public int MaxRecords { get; set; }
|
||||
|
||||
private GetProductPricesQuery(int maxRecords = 1000) => MaxRecords = maxRecords;
|
||||
|
||||
public static GetProductPricesQuery Create(int maxRecords = 1000)
|
||||
{
|
||||
if (maxRecords <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxRecords), "MaxRecords must be greater than zero.");
|
||||
|
||||
return new(maxRecords);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user