Compare commits

...

5 Commits

Author SHA1 Message Date
khwezi e6e0475db1 Merge pull request 'emailjobs' (#21) from emailjobs into master
Reviewed-on: #21
2026-05-15 08:39:36 +02:00
Khwezi Mngoma be9c83c8a3 Removed Abstractions listing from Git Tag
continuous-integration/drone/pr Build is passing
2026-05-15 08:39:10 +02:00
Khwezi Mngoma 65687d231e Ensured UTC is used 2026-05-15 08:37:58 +02:00
khwezi 5090c60797 Merge pull request 'Fixed Lead->Customer Relationship' (#20) from emailjobs into master
Reviewed-on: #20
2026-05-15 07:55:56 +02:00
Khwezi Mngoma 4523ef6151 Fixed Lead->Customer Relationship
continuous-integration/drone/pr Build is passing
2026-05-15 07:54:35 +02:00
16 changed files with 976 additions and 131 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ steps:
\"tag_name\": \"$VERSION\", \"tag_name\": \"$VERSION\",
\"target_commitish\": \"${DRONE_COMMIT_SHA}\", \"target_commitish\": \"${DRONE_COMMIT_SHA}\",
\"name\": \"Library Suite $VERSION\", \"name\": \"Library Suite $VERSION\",
\"body\": \"### Published NuGet Packages\nAll packages versioned as **$VERSION**:\n* LiteCharms.Abstractions\n* LiteCharms.Features\n\n[View in Nexus](https://nexus.khongisa.co.za/repository/nuget-group/)\", \"body\": \"### Published NuGet Packages\nAll packages versioned as **$VERSION**:\n* LiteCharms.Features\n\n[View in Nexus](https://nexus.khongisa.co.za/repository/nuget-group/)\",
\"draft\": false, \"draft\": false,
\"prerelease\": false \"prerelease\": false
}" }"
@@ -1,6 +1,5 @@
using LiteCharms.Features.Abstractions; using LiteCharms.Features.Abstractions;
using LiteCharms.Features.Quartz.Abstractions; using LiteCharms.Features.Quartz.Abstractions;
using static LiteCharms.Features.Extensions.Timezones;
namespace LiteCharms.Features.Quartz; namespace LiteCharms.Features.Quartz;
@@ -47,12 +46,12 @@ public class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestra
.StoreDurably() .StoreDurably()
.Build(); .Build();
var now = SouthAfricanTimeZone.UtcNow(); var now = DateTime.UtcNow;
var trigger = global::Quartz.TriggerBuilder.Create() var trigger = global::Quartz.TriggerBuilder.Create()
.WithIdentity(triggerKey) .WithIdentity(triggerKey)
.WithDescription($"Scheduled via Main Job at {now:g}") .WithDescription($"Scheduled via Main Job at {now:g} UTC")
.WithCronSchedule(cronExpression, cron => cron.InTimeZone(SouthAfricanTimeZone) .WithCronSchedule(cronExpression, cron => cron
.WithMisfireHandlingInstructionFireAndProceed()) .WithMisfireHandlingInstructionFireAndProceed())
.StartAt(now) .StartAt(now)
.Build(); .Build();
+7 -3
View File
@@ -1,4 +1,5 @@
using LiteCharms.Features.Abstractions; using LiteCharms.Features.Abstractions;
using LiteCharms.Features.Mediator;
namespace LiteCharms.Features.Quartz; namespace LiteCharms.Features.Quartz;
@@ -13,9 +14,12 @@ public class MediatorJob<TNotification>(IMediator mediator) : IJob where TNotifi
var notification = JsonSerializer.Deserialize<TNotification>(data); var notification = JsonSerializer.Deserialize<TNotification>(data);
if(notification is null) return; if (notification is null) return;
if(notification is TNotification) using var activity = MediatorTelemetry.Source.StartActivity($"Quartz: {typeof(TNotification).Name}");
await mediator.Publish(notification, context.CancellationToken);
activity?.SetTag("event.correlation_id", notification.CorrelationId);
await mediator.Publish(notification, context.CancellationToken);
} }
} }
@@ -51,7 +51,6 @@ public class PackageService(IDbContextFactory<ShopDbContext> contextFactory)
var newPackage = context.Packages.Add(new Entities.Package var newPackage = context.Packages.Add(new Entities.Package
{ {
UpdatedAt = null,
Name = name, Name = name,
Summary = summary, Summary = summary,
Description = description, Description = description,
@@ -206,7 +205,7 @@ public class PackageService(IDbContextFactory<ShopDbContext> contextFactory)
package.Summary = summary; package.Summary = summary;
package.Description = description; package.Description = description;
package.ImageUrl = ImageUrl; package.ImageUrl = ImageUrl;
package.UpdatedAt = SouthAfricanTimeZone.UtcNow(); package.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok() ? Result.Ok()
@@ -1,4 +1,4 @@
using LiteCharms.Features.Shop.Customers.Models; using LiteCharms.Features.Shop.Customers.Entities;
namespace LiteCharms.Features.Shop.Leads.Entities; namespace LiteCharms.Features.Shop.Leads.Entities;
@@ -24,7 +24,7 @@ public class LeadConfiguration : IEntityTypeConfiguration<Lead>
builder.Property(f => f.AttributionHash).IsRequired(true); builder.Property(f => f.AttributionHash).IsRequired(true);
builder.HasOne(f => f.Customer) builder.HasOne(f => f.Customer)
.WithMany() .WithMany(f => f.Leads)
.HasForeignKey(f => f.CustomerId) .HasForeignKey(f => f.CustomerId)
.OnDelete(DeleteBehavior.Restrict); .OnDelete(DeleteBehavior.Restrict);
} }
@@ -103,6 +103,7 @@ public class LeadService(IDbContextFactory<ShopDbContext> contextFactory)
return Result.Fail(new Error($"Lead with ID {leadId} not found.")); return Result.Fail(new Error($"Lead with ID {leadId} not found."));
lead.Status = status; lead.Status = status;
lead.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok() ? Result.Ok()
@@ -1,7 +1,6 @@
using LiteCharms.Features.Email; using LiteCharms.Features.Email;
using LiteCharms.Features.Shop.Notifications.Entities; using LiteCharms.Features.Shop.Notifications.Models;
using LiteCharms.Features.Shop.Postgres; using LiteCharms.Features.Shop.Postgres;
using static LiteCharms.Features.Extensions.Timezones;
namespace LiteCharms.Features.Shop.Notifications.Events.Handlers; namespace LiteCharms.Features.Shop.Notifications.Events.Handlers;
@@ -17,8 +16,8 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbConte
using var context = await contextFactory.CreateDbContextAsync(cancellationToken); using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var notifications = await context.Notifications var notifications = await context.Notifications
.OrderByDescending(o => o.Priority) .OrderByDescending(o => o.CreatedAt)
.ThenBy(o => o.CreatedAt) .ThenBy(o => o.Priority)
.Where(n => n.CorrelationIdType == CorrelationIdTypes.Email) .Where(n => n.CorrelationIdType == CorrelationIdTypes.Email)
.Where(n => n.Direction == NotificationDirection.Outgoing) .Where(n => n.Direction == NotificationDirection.Outgoing)
.Take(message.MaxRecords) .Take(message.MaxRecords)
@@ -44,7 +43,7 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbConte
} }
notification.Processed = true; notification.Processed = true;
notification.UpdatedAt = SouthAfricanTimeZone.UtcNow(); notification.UpdatedAt = DateTime.UtcNow;
} }
await context.SaveChangesAsync(cancellationToken); await context.SaveChangesAsync(cancellationToken);
@@ -2,7 +2,6 @@
using LiteCharms.Features.Models; using LiteCharms.Features.Models;
using LiteCharms.Features.Shop.Orders.Models; using LiteCharms.Features.Shop.Orders.Models;
using LiteCharms.Features.Shop.Postgres; using LiteCharms.Features.Shop.Postgres;
using static LiteCharms.Features.Extensions.Timezones;
namespace LiteCharms.Features.Shop.Orders; namespace LiteCharms.Features.Shop.Orders;
@@ -241,7 +240,7 @@ public class OrderService(IDbContextFactory<ShopDbContext> contextFactory)
return Result.Fail(new Error($"Order {request.OrderId} not found")); return Result.Fail(new Error($"Order {request.OrderId} not found"));
order.Status = request.Status; order.Status = request.Status;
order.UpdatedAt = SouthAfricanTimeZone.UtcNow(); order.UpdatedAt = DateTime.UtcNow;
if(!string.IsNullOrWhiteSpace(request.InvoiceUrl)) order.InvoiceUrl = request.InvoiceUrl; if(!string.IsNullOrWhiteSpace(request.InvoiceUrl)) order.InvoiceUrl = request.InvoiceUrl;
@@ -0,0 +1,776 @@
// <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("20260515055221_FixedLeadCustomerRelationship")]
partial class FixedLeadCustomerRelationship
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.Property<DateTime>("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<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("Package", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.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.Features.Shop.Customers.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<DateTime>("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<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Website")
.HasColumnType("text");
b.Property<string>("Whatsapp")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Customers", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.Leads.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<DateTime>("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<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("WebClickId")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CustomerId");
b.ToTable("Leads", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.Notifications.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<DateTime>("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>("RecipientAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("RecipientName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SenderAddress")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SenderName")
.HasColumnType("text");
b.Property<string>("Subject")
.IsRequired()
.HasColumnType("text");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("Notification", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("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<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("CustomerId");
b.ToTable("Orders", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.OrderRefund", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<decimal>("Amount")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)");
b.Property<DateTime>("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("OrderRefunds", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.Products.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("Products", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("Active")
.HasColumnType("boolean");
b.Property<DateTime>("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<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("ProductId");
b.ToTable("ProductPrices", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<Guid>("CustomerId")
.HasColumnType("uuid");
b.Property<DateTime?>("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<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("CustomerId");
b.HasIndex("OrderId")
.IsUnique();
b.HasIndex("ShoppingCartId")
.IsUnique();
b.ToTable("Quotes", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<Guid>("CustomerId")
.HasColumnType("uuid");
b.Property<Guid?>("OrderId")
.HasColumnType("uuid");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("CustomerId");
b.HasIndex("OrderId")
.IsUnique();
b.ToTable("ShoppingCarts", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.ValueGeneratedOnAdd()
.HasColumnType("timestamp with time zone")
.HasDefaultValueSql("now()");
b.Property<Guid>("ProductPriceId")
.HasColumnType("uuid");
b.Property<int>("Quantity")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasDefaultValue(1);
b.Property<Guid>("ShoppingCartId")
.HasColumnType("uuid");
b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.HasIndex("ProductPriceId");
b.HasIndex("ShoppingCartId");
b.ToTable("ShoppingCartItems", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<DateTime>("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("ShoppingCartPackages", (string)null);
});
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.PackageItem", b =>
{
b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package")
.WithMany("PackageItems")
.HasForeignKey("PackageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice")
.WithMany()
.HasForeignKey("ProductPriceId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Package");
b.Navigation("ProductPrice");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b =>
{
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
.WithMany("Leads")
.HasForeignKey("CustomerId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("Customer");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b =>
{
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
.WithMany("Orders")
.HasForeignKey("CustomerId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Customer");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.OrderRefund", b =>
{
b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order")
.WithMany("Refunds")
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Order");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b =>
{
b.HasOne("LiteCharms.Features.Shop.Products.Entities.Product", "Product")
.WithMany("ProductPrices")
.HasForeignKey("ProductId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Product");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b =>
{
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
.WithMany("Quotes")
.HasForeignKey("CustomerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order")
.WithOne("Quote")
.HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "OrderId");
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
.WithOne("Quote")
.HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "ShoppingCartId");
b.Navigation("Customer");
b.Navigation("Order");
b.Navigation("ShoppingCart");
});
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b =>
{
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
.WithMany("ShoppingCarts")
.HasForeignKey("CustomerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order")
.WithOne("ShoppingCart")
.HasForeignKey("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "OrderId")
.OnDelete(DeleteBehavior.SetNull);
b.Navigation("Customer");
b.Navigation("Order");
});
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b =>
{
b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice")
.WithMany()
.HasForeignKey("ProductPriceId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
.WithMany("ShoppingCartItems")
.HasForeignKey("ShoppingCartId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ProductPrice");
b.Navigation("ShoppingCart");
});
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b =>
{
b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package")
.WithMany()
.HasForeignKey("PackageId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
.WithMany("ShoppingCartPackages")
.HasForeignKey("ShoppingCartId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Package");
b.Navigation("ShoppingCart");
});
modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b =>
{
b.Navigation("PackageItems");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Entities.Customer", b =>
{
b.Navigation("Leads");
b.Navigation("Orders");
b.Navigation("Quotes");
b.Navigation("ShoppingCarts");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b =>
{
b.Navigation("Quote");
b.Navigation("Refunds");
b.Navigation("ShoppingCart");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.Product", b =>
{
b.Navigation("ProductPrices");
});
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b =>
{
b.Navigation("Quote");
b.Navigation("ShoppingCartItems");
b.Navigation("ShoppingCartPackages");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,166 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LiteCharms.Features.Shop.Postgres.Migrations
{
/// <inheritdoc />
public partial class FixedLeadCustomerRelationship : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Leads_Customer_CustomerId",
table: "Leads");
migrationBuilder.DropForeignKey(
name: "FK_Leads_Customers_CustomerId1",
table: "Leads");
migrationBuilder.DropForeignKey(
name: "FK_ShoppingCartItems_ProductPrices_ProductPriceId1",
table: "ShoppingCartItems");
migrationBuilder.DropForeignKey(
name: "FK_ShoppingCartItems_ShoppingCarts_ShoppingCartId1",
table: "ShoppingCartItems");
migrationBuilder.DropTable(
name: "Customer");
migrationBuilder.DropIndex(
name: "IX_ShoppingCartItems_ProductPriceId1",
table: "ShoppingCartItems");
migrationBuilder.DropIndex(
name: "IX_ShoppingCartItems_ShoppingCartId1",
table: "ShoppingCartItems");
migrationBuilder.DropIndex(
name: "IX_Leads_CustomerId1",
table: "Leads");
migrationBuilder.DropColumn(
name: "ProductPriceId1",
table: "ShoppingCartItems");
migrationBuilder.DropColumn(
name: "ShoppingCartId1",
table: "ShoppingCartItems");
migrationBuilder.DropColumn(
name: "CustomerId1",
table: "Leads");
migrationBuilder.AddForeignKey(
name: "FK_Leads_Customers_CustomerId",
table: "Leads",
column: "CustomerId",
principalTable: "Customers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Leads_Customers_CustomerId",
table: "Leads");
migrationBuilder.AddColumn<Guid>(
name: "ProductPriceId1",
table: "ShoppingCartItems",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "ShoppingCartId1",
table: "ShoppingCartItems",
type: "uuid",
nullable: true);
migrationBuilder.AddColumn<Guid>(
name: "CustomerId1",
table: "Leads",
type: "uuid",
nullable: true);
migrationBuilder.CreateTable(
name: "Customer",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Active = table.Column<bool>(type: "boolean", nullable: false),
Address = table.Column<string>(type: "text", nullable: true),
City = table.Column<string>(type: "text", nullable: true),
Company = table.Column<string>(type: "text", nullable: true),
Country = table.Column<string>(type: "text", nullable: true),
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
Discord = table.Column<string>(type: "text", nullable: true),
Email = table.Column<string>(type: "text", nullable: true),
LastName = table.Column<string>(type: "text", nullable: true),
LinkedIn = table.Column<string>(type: "text", nullable: true),
Name = table.Column<string>(type: "text", nullable: true),
Phone = table.Column<string>(type: "text", nullable: true),
PostalCode = table.Column<string>(type: "text", nullable: true),
Region = table.Column<string>(type: "text", nullable: true),
Slack = table.Column<string>(type: "text", nullable: true),
Tax = table.Column<string>(type: "text", nullable: true),
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
Website = table.Column<string>(type: "text", nullable: true),
Whatsapp = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Customer", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_ShoppingCartItems_ProductPriceId1",
table: "ShoppingCartItems",
column: "ProductPriceId1");
migrationBuilder.CreateIndex(
name: "IX_ShoppingCartItems_ShoppingCartId1",
table: "ShoppingCartItems",
column: "ShoppingCartId1");
migrationBuilder.CreateIndex(
name: "IX_Leads_CustomerId1",
table: "Leads",
column: "CustomerId1");
migrationBuilder.AddForeignKey(
name: "FK_Leads_Customer_CustomerId",
table: "Leads",
column: "CustomerId",
principalTable: "Customer",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_Leads_Customers_CustomerId1",
table: "Leads",
column: "CustomerId1",
principalTable: "Customers",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_ShoppingCartItems_ProductPrices_ProductPriceId1",
table: "ShoppingCartItems",
column: "ProductPriceId1",
principalTable: "ProductPrices",
principalColumn: "Id");
migrationBuilder.AddForeignKey(
name: "FK_ShoppingCartItems_ShoppingCarts_ShoppingCartId1",
table: "ShoppingCartItems",
column: "ShoppingCartId1",
principalTable: "ShoppingCarts",
principalColumn: "Id");
}
}
}
@@ -166,74 +166,6 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
b.ToTable("Customers", (string)null); b.ToTable("Customers", (string)null);
}); });
modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Models.Customer", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<bool>("Active")
.HasColumnType("boolean");
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<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Discord")
.HasColumnType("text");
b.Property<string>("Email")
.HasColumnType("text");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<string>("LinkedIn")
.HasColumnType("text");
b.Property<string>("Name")
.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<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Website")
.HasColumnType("text");
b.Property<string>("Whatsapp")
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Customer");
});
modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b => modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@@ -270,9 +202,6 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
b.Property<Guid?>("CustomerId") b.Property<Guid?>("CustomerId")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<Guid?>("CustomerId1")
.HasColumnType("uuid");
b.Property<long?>("FeedItemId") b.Property<long?>("FeedItemId")
.HasColumnType("bigint"); .HasColumnType("bigint");
@@ -295,8 +224,6 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
b.HasIndex("CustomerId"); b.HasIndex("CustomerId");
b.HasIndex("CustomerId1");
b.ToTable("Leads", (string)null); b.ToTable("Leads", (string)null);
}); });
@@ -615,9 +542,6 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
b.Property<Guid>("ProductPriceId") b.Property<Guid>("ProductPriceId")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<Guid?>("ProductPriceId1")
.HasColumnType("uuid");
b.Property<int>("Quantity") b.Property<int>("Quantity")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("integer") .HasColumnType("integer")
@@ -626,9 +550,6 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
b.Property<Guid>("ShoppingCartId") b.Property<Guid>("ShoppingCartId")
.HasColumnType("uuid"); .HasColumnType("uuid");
b.Property<Guid?>("ShoppingCartId1")
.HasColumnType("uuid");
b.Property<DateTime?>("UpdatedAt") b.Property<DateTime?>("UpdatedAt")
.HasColumnType("timestamp with time zone"); .HasColumnType("timestamp with time zone");
@@ -636,12 +557,8 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
b.HasIndex("ProductPriceId"); b.HasIndex("ProductPriceId");
b.HasIndex("ProductPriceId1");
b.HasIndex("ShoppingCartId"); b.HasIndex("ShoppingCartId");
b.HasIndex("ShoppingCartId1");
b.ToTable("ShoppingCartItems", (string)null); b.ToTable("ShoppingCartItems", (string)null);
}); });
@@ -692,15 +609,11 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b => modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b =>
{ {
b.HasOne("LiteCharms.Features.Shop.Customers.Models.Customer", "Customer") b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer")
.WithMany() .WithMany("Leads")
.HasForeignKey("CustomerId") .HasForeignKey("CustomerId")
.OnDelete(DeleteBehavior.Restrict); .OnDelete(DeleteBehavior.Restrict);
b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", null)
.WithMany("Leads")
.HasForeignKey("CustomerId1");
b.Navigation("Customer"); b.Navigation("Customer");
}); });
@@ -780,26 +693,18 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations
modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b => modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b =>
{ {
b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", null) b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice")
.WithMany() .WithMany()
.HasForeignKey("ProductPriceId") .HasForeignKey("ProductPriceId")
.OnDelete(DeleteBehavior.Restrict) .OnDelete(DeleteBehavior.Restrict)
.IsRequired(); .IsRequired();
b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice") b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
.WithMany()
.HasForeignKey("ProductPriceId1");
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", null)
.WithMany("ShoppingCartItems") .WithMany("ShoppingCartItems")
.HasForeignKey("ShoppingCartId") .HasForeignKey("ShoppingCartId")
.OnDelete(DeleteBehavior.Cascade) .OnDelete(DeleteBehavior.Cascade)
.IsRequired(); .IsRequired();
b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart")
.WithMany()
.HasForeignKey("ShoppingCartId1");
b.Navigation("ProductPrice"); b.Navigation("ProductPrice");
b.Navigation("ShoppingCart"); b.Navigation("ShoppingCart");
@@ -1,7 +1,6 @@
using LiteCharms.Features.Extensions; using LiteCharms.Features.Extensions;
using LiteCharms.Features.Shop.Postgres; using LiteCharms.Features.Shop.Postgres;
using LiteCharms.Features.Shop.Products.Models; using LiteCharms.Features.Shop.Products.Models;
using static LiteCharms.Features.Extensions.Timezones;
namespace LiteCharms.Features.Shop.Products; namespace LiteCharms.Features.Shop.Products;
@@ -19,7 +18,7 @@ public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
return Result.Fail($"Could not find product price with ID {productPriceId}"); return Result.Fail($"Could not find product price with ID {productPriceId}");
price.Active = active; price.Active = active;
price.UpdatedAt = SouthAfricanTimeZone.UtcNow(); price.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok() ? Result.Ok()
@@ -200,7 +199,7 @@ public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
return Result.Fail($"Could not find product price with ID {productPriceId}"); return Result.Fail($"Could not find product price with ID {productPriceId}");
existingPrice.Active = false; existingPrice.Active = false;
existingPrice.UpdatedAt = SouthAfricanTimeZone.UtcNow(); existingPrice.UpdatedAt = DateTime.UtcNow;
if (!(await context.SaveChangesAsync(cancellationToken) > 0)) if (!(await context.SaveChangesAsync(cancellationToken) > 0))
return Result.Fail<Guid>($"Failed to deactivate existing price of ID {productPriceId}, try again later"); return Result.Fail<Guid>($"Failed to deactivate existing price of ID {productPriceId}, try again later");
@@ -212,7 +211,7 @@ public class ProductService(IDbContextFactory<ShopDbContext> contextFactory)
var deactivatedPrice = await context.ProductPrices.FirstOrDefaultAsync(p => p.Id == productPriceId, cancellationToken); var deactivatedPrice = await context.ProductPrices.FirstOrDefaultAsync(p => p.Id == productPriceId, cancellationToken);
existingPrice.Active = true; existingPrice.Active = true;
existingPrice.UpdatedAt = SouthAfricanTimeZone.UtcNow(); existingPrice.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Fail<Guid>("Reverted to old price, creation of new price failed") ? Result.Fail<Guid>("Reverted to old price, creation of new price failed")
@@ -2,7 +2,6 @@
using LiteCharms.Features.Models; using LiteCharms.Features.Models;
using LiteCharms.Features.Shop.Postgres; using LiteCharms.Features.Shop.Postgres;
using LiteCharms.Features.Shop.Quotes.Models; using LiteCharms.Features.Shop.Quotes.Models;
using static LiteCharms.Features.Extensions.Timezones;
namespace LiteCharms.Features.Shop.Quotes; namespace LiteCharms.Features.Shop.Quotes;
@@ -26,6 +25,7 @@ public class QuoteService(IDbContextFactory<ShopDbContext> contextFactory)
return Result.Fail(new Error($"Quote with id {quoteId} is already assigned to order with id {orderId}")); return Result.Fail(new Error($"Quote with id {quoteId} is already assigned to order with id {orderId}"));
quote.OrderId = orderId; quote.OrderId = orderId;
quote.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok() ? Result.Ok()
@@ -52,6 +52,7 @@ public class QuoteService(IDbContextFactory<ShopDbContext> contextFactory)
return Result.Fail(new Error($"Shopping Cart with id {shoppingCartId} not found")); return Result.Fail(new Error($"Shopping Cart with id {shoppingCartId} not found"));
quote.ShoppingCartId = shoppingCartId; quote.ShoppingCartId = shoppingCartId;
quote.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok() ? Result.Ok()
@@ -140,7 +141,7 @@ public class QuoteService(IDbContextFactory<ShopDbContext> contextFactory)
return Result.Fail(new Error("Quote not found.")); return Result.Fail(new Error("Quote not found."));
quote.Status = status; quote.Status = status;
quote.UpdatedAt = SouthAfricanTimeZone.UtcNow(); quote.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok() ? Result.Ok()
@@ -1,6 +1,4 @@
using LiteCharms.Features.Shop.Products.Entities; namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
namespace LiteCharms.Features.Shop.ShoppingCarts.Entities;
public class ShoppingCartItemConfiguration : IEntityTypeConfiguration<ShoppingCartItem> public class ShoppingCartItemConfiguration : IEntityTypeConfiguration<ShoppingCartItem>
{ {
@@ -15,13 +13,13 @@ public class ShoppingCartItemConfiguration : IEntityTypeConfiguration<ShoppingCa
builder.Property(f => f.ShoppingCartId).IsRequired(); builder.Property(f => f.ShoppingCartId).IsRequired();
builder.Property(f => f.ProductPriceId).IsRequired(); builder.Property(f => f.ProductPriceId).IsRequired();
builder.HasOne<ShoppingCart>() builder.HasOne(f => f.ShoppingCart)
.WithMany(s => s.ShoppingCartItems) .WithMany(s => s.ShoppingCartItems)
.HasForeignKey(f => f.ShoppingCartId) .HasForeignKey(f => f.ShoppingCartId)
.IsRequired() .IsRequired()
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
builder.HasOne<ProductPrice>() builder.HasOne(f => f.ProductPrice)
.WithMany() .WithMany()
.HasForeignKey(f => f.ProductPriceId) .HasForeignKey(f => f.ProductPriceId)
.IsRequired() .IsRequired()
@@ -1,7 +1,6 @@
using LiteCharms.Features.Extensions; using LiteCharms.Features.Extensions;
using LiteCharms.Features.Shop.Postgres; using LiteCharms.Features.Shop.Postgres;
using LiteCharms.Features.Shop.ShoppingCarts.Models; using LiteCharms.Features.Shop.ShoppingCarts.Models;
using static LiteCharms.Features.Extensions.Timezones;
namespace LiteCharms.Features.Shop.ShoppingCarts; namespace LiteCharms.Features.Shop.ShoppingCarts;
@@ -284,7 +283,7 @@ public class ShoppingCartService(IDbContextFactory<ShopDbContext> contextFactory
return Result.Fail($"Shopping cart item could not be found with id {shoppingCartItemId}"); return Result.Fail($"Shopping cart item could not be found with id {shoppingCartItemId}");
item.Quantity = quantity; item.Quantity = quantity;
item.UpdatedAt = SouthAfricanTimeZone.UtcNow(); item.UpdatedAt = DateTime.UtcNow;
return await context.SaveChangesAsync(cancellationToken) > 0 return await context.SaveChangesAsync(cancellationToken) > 0
? Result.Ok() ? Result.Ok()