Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ab3d8e6e9a | |||
| 97bde73777 | |||
| db4c348288 | |||
| a65e926a53 | |||
| 6683234642 | |||
| 1471d9e597 | |||
| 6ddbb9479a | |||
| e978aa17f8 | |||
| 6c7349a0f8 | |||
| a31f75c5ef | |||
| e97fd6cd3f | |||
| 7f4246ac63 | |||
| 184c7c252a | |||
| dfc62c8fe1 | |||
| bfe8c458d6 | |||
| 0f91f102e5 | |||
| e6e0475db1 | |||
| be9c83c8a3 | |||
| 65687d231e |
+1
-1
@@ -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,4 +1,6 @@
|
|||||||
using LiteCharms.Features.Shop.Notifications;
|
using LiteCharms.Features.Models;
|
||||||
|
using LiteCharms.Features.Shop.Notifications;
|
||||||
|
using LiteCharms.Features.Shop.Notifications.Events;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Tests;
|
namespace LiteCharms.Features.Tests;
|
||||||
|
|
||||||
@@ -32,4 +34,31 @@ public class NotificationsFeatureTests(CommonFixture fixture, ITestOutputHelper
|
|||||||
|
|
||||||
foreach (var error in createResult.Errors) output.WriteLine(error.Message);
|
foreach (var error in createResult.Errors) output.WriteLine(error.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetNotifications_ShouldReturn_AllNotifications()
|
||||||
|
{
|
||||||
|
DateRange range = new()
|
||||||
|
{
|
||||||
|
From = DateOnly.FromDateTime(new DateTime(2026, 05, 01, 0, 0, 0, DateTimeKind.Utc)),
|
||||||
|
To = DateOnly.FromDateTime(DateTime.UtcNow),
|
||||||
|
MaxRecords = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
var getResult = await notificationService.GetNotificationsAsync(range);
|
||||||
|
|
||||||
|
Assert.True(getResult.IsSuccess);
|
||||||
|
|
||||||
|
foreach (var error in getResult.Errors) output.WriteLine(error.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ProcessEmailNotificationsEvent_ShouldSucceed()
|
||||||
|
{
|
||||||
|
var notification = ProcessEmailNotificationsEvent.Create();
|
||||||
|
|
||||||
|
await fixture.Mediator.Publish(notification);
|
||||||
|
|
||||||
|
Assert.True(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,9 @@ public class EmailService(IOptions<SmtpSettings> options) : IDisposable
|
|||||||
|
|
||||||
var bodyBuilder = new BodyBuilder();
|
var bodyBuilder = new BodyBuilder();
|
||||||
|
|
||||||
foreach (var attachment in message.Body?.Attachments!)
|
if (message.Body!.Properties.HasAttachments)
|
||||||
bodyBuilder.Attachments.Add(attachment.Name!, attachment.FileStream!, cancellationToken);
|
foreach (var attachment in message.Body?.Attachments!)
|
||||||
|
bodyBuilder.Attachments.Add(attachment.Name!, attachment.FileStream!, cancellationToken);
|
||||||
|
|
||||||
if (!message.Body.Properties.IsHtml) bodyBuilder.TextBody = message.Body.Message;
|
if (!message.Body.Properties.IsHtml) bodyBuilder.TextBody = message.Body.Message;
|
||||||
if (message.Body.Properties.IsHtml) bodyBuilder.HtmlBody = message.Body.Message;
|
if (message.Body.Properties.IsHtml) bodyBuilder.HtmlBody = message.Body.Message;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public static class Quartz
|
|||||||
storage.UseClustering(cluster =>
|
storage.UseClustering(cluster =>
|
||||||
{
|
{
|
||||||
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
||||||
cluster.CheckinMisfireThreshold = TimeSpan.FromSeconds(2);
|
cluster.CheckinMisfireThreshold = TimeSpan.FromSeconds(90);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -48,6 +48,8 @@ public static class Quartz
|
|||||||
|
|
||||||
services.ConfigureCommon();
|
services.ConfigureCommon();
|
||||||
|
|
||||||
|
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
|
||||||
|
|
||||||
services.AddQuartz(config =>
|
services.AddQuartz(config =>
|
||||||
{
|
{
|
||||||
config.SchedulerName = schedulerName;
|
config.SchedulerName = schedulerName;
|
||||||
@@ -60,6 +62,8 @@ public static class Quartz
|
|||||||
config.UseDefaultThreadPool(options => options.MaxConcurrency = 1);
|
config.UseDefaultThreadPool(options => options.MaxConcurrency = 1);
|
||||||
config.UseTimeZoneConverter();
|
config.UseTimeZoneConverter();
|
||||||
|
|
||||||
|
config.SetProperty("quartz.jobStore.misfireThreshold", TimeSpan.FromMinutes(2).TotalMilliseconds.ToString());
|
||||||
|
|
||||||
config.UsePersistentStore(storage =>
|
config.UsePersistentStore(storage =>
|
||||||
{
|
{
|
||||||
storage.PerformSchemaValidation = false;
|
storage.PerformSchemaValidation = false;
|
||||||
@@ -72,7 +76,7 @@ public static class Quartz
|
|||||||
storage.UseClustering(cluster =>
|
storage.UseClustering(cluster =>
|
||||||
{
|
{
|
||||||
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
cluster.CheckinInterval = TimeSpan.FromSeconds(30);
|
||||||
cluster.CheckinMisfireThreshold = TimeSpan.FromSeconds(2);
|
cluster.CheckinMisfireThreshold = TimeSpan.FromSeconds(90);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -86,13 +90,13 @@ public static class Quartz
|
|||||||
{
|
{
|
||||||
options.Scheduling.IgnoreDuplicates = true;
|
options.Scheduling.IgnoreDuplicates = true;
|
||||||
options.Scheduling.OverWriteExistingData = true;
|
options.Scheduling.OverWriteExistingData = true;
|
||||||
|
|
||||||
options["quartz.plugin.jobHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin, Quartz.Plugins";
|
options["quartz.plugin.jobHistory.type"] = "Quartz.Plugin.History.LoggingJobHistoryPlugin, Quartz.Plugins";
|
||||||
options["quartz.plugin.triggerHistory.type"] = "Quartz.Plugin.History.LoggingTriggerHistoryPlugin, Quartz.Plugins";
|
options["quartz.plugin.triggerHistory.type"] = "Quartz.Plugin.History.LoggingTriggerHistoryPlugin, Quartz.Plugins";
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddTransient<RetryJobListener>();
|
services.AddTransient<RetryJobListener>();
|
||||||
services.AddTransient<IJobOrchestrator, JobOrchestrator>();
|
services.AddTransient<IJobOrchestrator, JobOrchestrator>();
|
||||||
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,13 +46,13 @@ 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}")
|
||||||
.WithCronSchedule(cronExpression, cron => cron.InTimeZone(SouthAfricanTimeZone)
|
.WithCronSchedule(cronExpression, cron => cron
|
||||||
.WithMisfireHandlingInstructionFireAndProceed())
|
.WithMisfireHandlingInstructionIgnoreMisfires())
|
||||||
.StartAt(now)
|
.StartAt(now)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using LiteCharms.Features.Abstractions;
|
using LiteCharms.Features.Abstractions;
|
||||||
|
using LiteCharms.Features.Mediator;
|
||||||
|
|
||||||
namespace LiteCharms.Features.Quartz;
|
namespace LiteCharms.Features.Quartz;
|
||||||
|
|
||||||
@@ -9,13 +10,28 @@ public class MediatorJob<TNotification>(IMediator mediator) : IJob where TNotifi
|
|||||||
{
|
{
|
||||||
var data = context.MergedJobDataMap["Payload"] as string;
|
var data = context.MergedJobDataMap["Payload"] as string;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(data)) return;
|
if (string.IsNullOrWhiteSpace(data))
|
||||||
|
{
|
||||||
|
Trace.WriteLine("Job Payload missing, job ended");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var notification = JsonSerializer.Deserialize<TNotification>(data);
|
var notification = JsonSerializer.Deserialize<TNotification>(data);
|
||||||
|
|
||||||
if(notification is null) return;
|
if (notification is null)
|
||||||
|
{
|
||||||
|
Trace.WriteLine("Notification could not be JSon converted from data string, job ended");
|
||||||
|
|
||||||
if(notification is TNotification)
|
return;
|
||||||
await mediator.Publish(notification, context.CancellationToken);
|
}
|
||||||
|
|
||||||
|
using var activity = MediatorTelemetry.Source.StartActivity($"Quartz: {typeof(TNotification).Name}");
|
||||||
|
|
||||||
|
activity?.SetTag("event.correlation_id", notification.CorrelationId);
|
||||||
|
|
||||||
|
await mediator.Publish(notification, context.CancellationToken);
|
||||||
|
|
||||||
|
Trace.WriteLine("Job published");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
+16
-9
@@ -1,7 +1,7 @@
|
|||||||
using LiteCharms.Features.Email;
|
using k8s.KubeConfigModels;
|
||||||
using LiteCharms.Features.Shop.Notifications.Entities;
|
using LiteCharms.Features.Email;
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -16,17 +16,20 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbConte
|
|||||||
{
|
{
|
||||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (emailService.Status != EmailStatuses.Connected)
|
||||||
|
await emailService.ConnectAsync(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.Platform == NotificationPlatforms.Email &&
|
||||||
.Where(n => n.Direction == NotificationDirection.Outgoing)
|
n.Direction == NotificationDirection.Outgoing && n.Processed == false)
|
||||||
.Take(message.MaxRecords)
|
.Take(message.MaxRecords)
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
foreach (var notification in notifications)
|
foreach (var notification in notifications)
|
||||||
{
|
{
|
||||||
if (dropBatch || cancellationToken.IsCancellationRequested) break;
|
if (dropBatch) break;
|
||||||
|
|
||||||
var sendResult = await SendEmailAsync(notification,emailService, cancellationToken);
|
var sendResult = await SendEmailAsync(notification,emailService, cancellationToken);
|
||||||
|
|
||||||
@@ -44,7 +47,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);
|
||||||
@@ -53,6 +56,10 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory<ShopDbConte
|
|||||||
{
|
{
|
||||||
logger.LogError(ex, ex.Message);
|
logger.LogError(ex, ex.Message);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await emailService.DisconnectAsync(cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Result> SendEmailAsync(Notification notification, EmailService service, CancellationToken cancellationToken = default)
|
private async Task<Result> SendEmailAsync(Notification notification, EmailService service, CancellationToken cancellationToken = default)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ public class ProcessEmailNotificationsEvent : EventBase, IEvent
|
|||||||
|
|
||||||
public int MaxRecords { get; set; }
|
public int MaxRecords { get; set; }
|
||||||
|
|
||||||
|
public ProcessEmailNotificationsEvent() { MaxRecords = 1000; }
|
||||||
|
|
||||||
private ProcessEmailNotificationsEvent(int maxRecords = 1000) => MaxRecords = maxRecords;
|
private ProcessEmailNotificationsEvent(int maxRecords = 1000) => MaxRecords = maxRecords;
|
||||||
|
|
||||||
public static ProcessEmailNotificationsEvent Create(int maxRecords = 1000) => new(maxRecords);
|
public static ProcessEmailNotificationsEvent Create(int maxRecords = 1000) => new(maxRecords);
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ public class NotificationService(IDbContextFactory<ShopDbContext> contextFactory
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var fromDate = range.From.ToDateTime(TimeOnly.MinValue);
|
var fromDate = range.From.ToDateTime(TimeOnly.MinValue, DateTimeKind.Utc);
|
||||||
var toDate = range.To.ToDateTime(TimeOnly.MaxValue);
|
var toDate = range.To.ToDateTime(TimeOnly.MaxValue, DateTimeKind.Utc);
|
||||||
|
|
||||||
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||||
|
|
||||||
@@ -96,6 +96,7 @@ public class NotificationService(IDbContextFactory<ShopDbContext> contextFactory
|
|||||||
return Result.Fail(new Error($"Notification with id {request.NotificationId} not found."));
|
return Result.Fail(new Error($"Notification with id {request.NotificationId} not found."));
|
||||||
|
|
||||||
notification.Processed = request.Processed;
|
notification.Processed = request.Processed;
|
||||||
|
notification.UpdatedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
if (request.HasError)
|
if (request.HasError)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,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()
|
||||||
|
|||||||
Reference in New Issue
Block a user