diff --git a/LiteCharms.Features.MidrandBooks/Abstractions/IService.cs b/LiteCharms.Features.MidrandBooks/Abstractions/IService.cs new file mode 100644 index 0000000..6218faf --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Abstractions/IService.cs @@ -0,0 +1,3 @@ +namespace LiteCharms.Features.MidrandBooks.Abstractions; + +public interface IService; diff --git a/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs b/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs index 2fdfe2b..26a60ec 100644 --- a/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs +++ b/LiteCharms.Features.MidrandBooks/AuthorBooks/BooksService.cs @@ -1,10 +1,11 @@ -using LiteCharms.Features.MidrandBooks.AuthorBooks.Models; +using LiteCharms.Features.MidrandBooks.Abstractions; +using LiteCharms.Features.MidrandBooks.AuthorBooks.Models; using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Postgres; namespace LiteCharms.Features.MidrandBooks.AuthorBooks; -public class BooksService(IDbContextFactory contextFactory) +public class BooksService(IDbContextFactory contextFactory) : IService { public async ValueTask UpdateBookStatusAsync(long bookId, bool isEnabled, CancellationToken cancellationToken) { diff --git a/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs b/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs index 3897349..43ec4aa 100644 --- a/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs +++ b/LiteCharms.Features.MidrandBooks/Authors/AuthorService.cs @@ -1,13 +1,13 @@ -using LiteCharms.Features.MidrandBooks.AuthorBooks.Models; +using LiteCharms.Features.MidrandBooks.Abstractions; +using LiteCharms.Features.MidrandBooks.AuthorBooks.Models; using LiteCharms.Features.MidrandBooks.Authors.Models; using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Postgres; -using LiteCharms.Features.MidrandBooks.Products.Models; using LiteCharms.Features.Models; namespace LiteCharms.Features.MidrandBooks.Authors; -public class AuthorService(IDbContextFactory contextFactory) +public class AuthorService(IDbContextFactory contextFactory) : IService { public async ValueTask> GetAuthorBooksAsync(long authorId, CancellationToken cancellationToken) { diff --git a/LiteCharms.Features.MidrandBooks/Customers/CustomerService.cs b/LiteCharms.Features.MidrandBooks/Customers/CustomerService.cs new file mode 100644 index 0000000..fdb7451 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/CustomerService.cs @@ -0,0 +1,408 @@ +using LiteCharms.Features.MidrandBooks.Abstractions; +using LiteCharms.Features.MidrandBooks.Customers.Models; +using LiteCharms.Features.MidrandBooks.Extensions; +using LiteCharms.Features.MidrandBooks.Postgres; + +namespace LiteCharms.Features.MidrandBooks.Customers; + +public class CustomerService(IDbContextFactory contextFactory) : IService +{ + public async ValueTask> CreateCustomerAsync(CreateCustomer request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (await context.Customers.AnyAsync(c => c.Email!.Equals(request.Email, StringComparison.OrdinalIgnoreCase), cancellationToken)) + return Result.Fail(new Error($"Customer with email '{request.Email}' already exists.")); + + var customer = context.Customers.Add(new Entities.Customer + { + Company = request.Company, + VatNumber = request.VatNumber, + Email = request.Email, + Website = request.Website, + Phone = request.Phone, + SocialMedia = request.SocialMedia, + Enabled = true + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(customer.Entity.Id) + : Result.Fail(new Error("Failed to create customer.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> CreateCustomerContactAsync(long customerId, CreateCustomerContact request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with ID '{customerId}' does not exist.")); + + if (await context.Contacts.AnyAsync(cc => cc.CustomerId == customerId && cc.Email!.Equals(request.Email, StringComparison.OrdinalIgnoreCase), cancellationToken)) + return Result.Fail(new Error($"Contact with email '{request.Email}' already exists for this customer.")); + + var contact = context.Contacts.Add(new Entities.Contact + { + CustomerId = customerId, + Name = request.Name, + Email = request.Email, + Phone = request.Phone, + LastName = request.LastName, + Type = request.Type, + Enabled = true + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(contact.Entity.Id) + : Result.Fail(new Error("Failed to create customer contact.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> CreateCustomerAddressAsync(long customerId, CreateCustomerAddress request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with ID '{customerId}' does not exist.")); + + var address = context.Addresses.Add(new Entities.Address + { + CustomerId = customerId, + Street = request.Street, + City = request.City, + State = request.State, + PostalCode = request.PostalCode, + Country = request.Country, + Type = request.Type, + Enabled = true, + BuildingType = request.BuildingType, + IsPrimary = request.IsPrimary, + Name = request.Name + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(address.Entity.Id) + : Result.Fail(new Error("Failed to create customer address.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateCustomerAsync(long customerId, UpdateCustomer request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == customerId, cancellationToken); + + if (customer is null) + return Result.Fail(new Error($"Customer with ID '{customerId}' does not exist.")); + + customer.UpdatedAt = DateTime.UtcNow; + customer.Company = request.Company; + customer.VatNumber = request.VatNumber; + customer.Email = request.Email; + customer.Website = request.Website; + customer.Phone = request.Phone; + customer.SocialMedia = request.SocialMedia; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to update customer.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateCustomerContactAsync(long contactId, UpdateCustomerContact request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var contact = await context.Contacts.FirstOrDefaultAsync(cc => cc.Id == contactId, cancellationToken); + + if (contact is null) + return Result.Fail(new Error($"Contact with ID '{contactId}' does not exist.")); + + contact.UpdatedAt = DateTime.UtcNow; + contact.Name = request.Name; + contact.LastName = request.LastName; + contact.Email = request.Email; + contact.Phone = request.Phone; + contact.Type = request.Type; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to update customer contact.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateCustomerAddressAsync(long addressId, UpdateCustomerAddress request, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var address = await context.Addresses.FirstOrDefaultAsync(a => a.Id == addressId, cancellationToken); + + if (address is null) + return Result.Fail(new Error($"Address with ID '{addressId}' does not exist.")); + + address.UpdatedAt = DateTime.UtcNow; + address.Street = request.Street; + address.City = request.City; + address.State = request.State; + address.PostalCode = request.PostalCode; + address.Country = request.Country; + address.Type = request.Type; + address.BuildingType = request.BuildingType; + address.IsPrimary = request.IsPrimary; + address.Name = request.Name; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to update customer address.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateCustomerStatusAsync(long customerId, bool enabled, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == customerId, cancellationToken); + + if (customer is null) + return Result.Fail(new Error($"Customer with ID '{customerId}' does not exist.")); + + customer.Enabled = enabled; + customer.UpdatedAt = DateTime.UtcNow; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to update customer status.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateCustomerContactStatusAsync(long contactId, bool enabled, bool isPrimary, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var contact = await context.Contacts.FirstOrDefaultAsync(cc => cc.Id == contactId, cancellationToken); + + if (contact is null) + return Result.Fail(new Error($"Contact with ID '{contactId}' does not exist.")); + + contact.Enabled = enabled; + contact.IsPrimary = isPrimary; + contact.UpdatedAt = DateTime.UtcNow; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to update customer contact status.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateCustomerAddressStatusAsync(long addressId, bool enabled, bool isPrimary, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var address = await context.Addresses.FirstOrDefaultAsync(a => a.Id == addressId, cancellationToken); + + if (address is null) + return Result.Fail(new Error($"Address with ID '{addressId}' does not exist.")); + + address.Enabled = enabled; + address.IsPrimary = isPrimary; + address.UpdatedAt = DateTime.UtcNow; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to update customer address status.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomersAsync(CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customers = await context.Customers + .AsNoTracking() + .Include(c => c.Contacts) + .Include(c => c.Addresses) + .OrderByDescending(c => c.CreatedAt) + .ThenByDescending(c => c.UpdatedAt) + .AsSplitQuery() + .ToListAsync(cancellationToken); + + return customers?.Count > 0 + ? Result.Ok(customers.Select(c => c.ToModel()).ToArray()) + : Result.Fail(new Error("No customers found.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerContactsAsync(long customerId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with ID '{customerId}' does not exist.")); + + var contacts = await context.Contacts + .AsNoTracking() + .Where(cc => cc.CustomerId == customerId) + .OrderByDescending(cc => cc.CreatedAt) + .ThenByDescending(cc => cc.UpdatedAt) + .ToListAsync(cancellationToken); + + return contacts?.Count > 0 + ? Result.Ok(contacts.Select(cc => cc.ToModel()).ToArray()) + : Result.Fail(new Error("No contacts found for the specified customer.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerAddressesAsync(long customerId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with ID '{customerId}' does not exist.")); + + var addresses = await context.Addresses + .AsNoTracking() + .Where(a => a.CustomerId == customerId) + .OrderByDescending(a => a.CreatedAt) + .ThenByDescending(a => a.UpdatedAt) + .ToListAsync(cancellationToken); + + return addresses?.Count > 0 + ? Result.Ok(addresses.Select(a => a.ToModel()).ToArray()) + : Result.Fail(new Error($"No addresses found for customer with ID '{customerId}'.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerAsync(long customerId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customer = await context.Customers + .AsNoTracking() + .Include(c => c.Contacts) + .Include(c => c.Addresses) + .FirstOrDefaultAsync(c => c.Id == customerId, cancellationToken); + + return customer is not null + ? Result.Ok(customer.ToModel()) + : Result.Fail(new Error($"Customer with ID '{customerId}' does not exist.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerContactAsync(long contactId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var contact = await context.Contacts + .AsNoTracking() + .FirstOrDefaultAsync(cc => cc.Id == contactId, cancellationToken); + + return contact is not null + ? Result.Ok(contact.ToModel()) + : Result.Fail(new Error($"Contact with ID '{contactId}' does not exist.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerAddressAsync(long addressId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var address = await context.Addresses + .AsNoTracking() + .FirstOrDefaultAsync(a => a.Id == addressId, cancellationToken); + + return address is not null + ? Result.Ok(address.ToModel()) + : Result.Fail
(new Error($"Address with ID '{addressId}' does not exist.")); + } + catch (Exception ex) + { + return Result.Fail
(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/Address.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/Address.cs new file mode 100644 index 0000000..d54f7cd --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/Address.cs @@ -0,0 +1,7 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Entities; + +[EntityTypeConfiguration] +public class Address : Models.Address +{ + public virtual Customer? Customer { get; set; } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/AddressConfiguration.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/AddressConfiguration.cs new file mode 100644 index 0000000..6b6997d --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/AddressConfiguration.cs @@ -0,0 +1,29 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Entities; + +public class AddressConfiguration : IEntityTypeConfiguration
+{ + public void Configure(EntityTypeBuilder
builder) + { + builder.ToTable("Addresses"); + + builder.HasKey(a => a.Id); + builder.Property(a => a.CustomerId).IsRequired(); + builder.Property(a => a.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(a => a.UpdatedAt).HasDefaultValueSql("now()"); + builder.Property(a => a.Name).IsRequired(); + builder.Property(a => a.Type).IsRequired(); + builder.Property(a => a.BuildingType).IsRequired(); + builder.Property(a => a.Street).IsRequired(); + builder.Property(a => a.City).IsRequired(); + builder.Property(a => a.State).IsRequired(); + builder.Property(a => a.PostalCode).IsRequired(); + builder.Property(a => a.Country).IsRequired(); + builder.Property(a => a.IsPrimary).HasDefaultValue(false); + builder.Property(a => a.Enabled).HasDefaultValue(true); + + builder.HasOne(a => a.Customer) + .WithMany(c => c.Addresses) + .HasForeignKey(a => a.CustomerId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/Contact.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/Contact.cs new file mode 100644 index 0000000..3d5509e --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/Contact.cs @@ -0,0 +1,7 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Entities; + +[EntityTypeConfiguration] +public class Contact : Models.Contact +{ + public virtual Customer? Customer { get; set; } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/ContactConfiguration.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/ContactConfiguration.cs new file mode 100644 index 0000000..8d6fac0 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/ContactConfiguration.cs @@ -0,0 +1,26 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Entities; + +public class ContactConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Contacts"); + + builder.HasKey(c => c.Id); + builder.Property(c => c.CustomerId).IsRequired(); + builder.Property(c => c.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(c => c.UpdatedAt).HasDefaultValueSql("now()"); + builder.Property(c => c.Name).IsRequired(); + builder.Property(c => c.LastName).IsRequired(); + builder.Property(c => c.Type).IsRequired(); + builder.Property(c => c.Phone).IsRequired(); + builder.Property(c => c.Email).IsRequired(); + builder.Property(c => c.IsPrimary).HasDefaultValue(false); + builder.Property(c => c.Enabled).HasDefaultValue(true); + + builder.HasOne(c => c.Customer) + .WithMany(c => c.Contacts) + .HasForeignKey(c => c.CustomerId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/Customer.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/Customer.cs new file mode 100644 index 0000000..4443225 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/Customer.cs @@ -0,0 +1,9 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Entities; + +[EntityTypeConfiguration] +public class Customer : Models.Customer +{ + public virtual ICollection Contacts { get; set; } = []; + + public virtual ICollection
Addresses { get; set; } = []; +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Entities/CustomerConfiguration.cs b/LiteCharms.Features.MidrandBooks/Customers/Entities/CustomerConfiguration.cs new file mode 100644 index 0000000..2b05c59 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Entities/CustomerConfiguration.cs @@ -0,0 +1,20 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Entities; + +public class CustomerConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Customers"); + + builder.HasKey(c => c.Id); + builder.Property(c => c.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(c => c.UpdatedAt).HasDefaultValueSql("now()"); + builder.Property(c => c.Company).IsRequired(false); + builder.Property(c => c.VatNumber).IsRequired(false); + builder.Property(c => c.Email).IsRequired(); + builder.Property(c => c.Phone).IsRequired(); + builder.Property(c => c.Website).IsRequired(); + builder.Property(c => c.SocialMedia).IsRequired(false).HasColumnType("jsonb"); + builder.Property(c => c.Enabled).HasDefaultValue(true); + } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Models/Address.cs b/LiteCharms.Features.MidrandBooks/Customers/Models/Address.cs new file mode 100644 index 0000000..35c7816 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Models/Address.cs @@ -0,0 +1,32 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Models; + +public class Address +{ + public long Id { get; set; } + + public long CustomerId { get; set; } + + public string? Name { get; set; } + + public DateTime CreatedAt { get; set; } + + public DateTime? UpdatedAt { get; set; } + + public AddressType Type { get; set; } + + public AddressBuildingTypes BuildingType { get; set; } + + public string? Street { get; set; } + + public string? City { get; set; } + + public string? State { get; set; } + + public string? PostalCode { get; set; } + + public string? Country { get; set; } + + public bool IsPrimary { get; set; } + + public bool Enabled { get; set; } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Models/Contact.cs b/LiteCharms.Features.MidrandBooks/Customers/Models/Contact.cs new file mode 100644 index 0000000..7eed90e --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Models/Contact.cs @@ -0,0 +1,26 @@ +namespace LiteCharms.Features.MidrandBooks.Customers.Models; + +public class Contact +{ + public long Id { get; set; } + + public long CustomerId { get; set; } + + public DateTime CreatedAt { get; set; } + + public DateTime? UpdatedAt { get; set; } + + public ContactTypes Type { get; set; } + + public string? Name { get; set; } + + public string? LastName { get; set; } + + public string? Email { get; set; } + + public string? Phone { get; set; } + + public bool IsPrimary { get; set; } + + public bool Enabled { get; set; } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Models/Customer.cs b/LiteCharms.Features.MidrandBooks/Customers/Models/Customer.cs new file mode 100644 index 0000000..13204ce --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Models/Customer.cs @@ -0,0 +1,26 @@ +using LiteCharms.Features.Models; + +namespace LiteCharms.Features.MidrandBooks.Customers.Models; + +public class Customer +{ + public long Id { get; set; } + + public DateTime CreatedAt { get; set; } + + public DateTime? UpdatedAt { get; set; } + + public string? Company { get; set; } + + public string? VatNumber { get; set; } + + public string? Email { get; set; } + + public string? Website { get; set; } + + public string? Phone { get; set; } + + public SocialMedia[]? SocialMedia { get; set; } + + public bool Enabled { get; set; } +} diff --git a/LiteCharms.Features.MidrandBooks/Customers/Models/Records.cs b/LiteCharms.Features.MidrandBooks/Customers/Models/Records.cs new file mode 100644 index 0000000..ceb022c --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Customers/Models/Records.cs @@ -0,0 +1,60 @@ +using LiteCharms.Features.Models; + +namespace LiteCharms.Features.MidrandBooks.Customers.Models; + +public record CreateCustomer +{ + public string? Company { get; set; } + + public string? VatNumber { get; set; } + + public string? Email { get; set; } + + public string? Website { get; set; } + + public string? Phone { get; set; } + + public SocialMedia[]? SocialMedia { get; set; } +} + +public record UpdateCustomer : CreateCustomer; + +public record CreateCustomerContact +{ + public ContactTypes Type { get; set; } + + public string? Name { get; set; } + + public string? LastName { get; set; } + + public string? Email { get; set; } + + public string? Phone { get; set; } +} + +public record UpdateCustomerContact : CreateCustomerContact; + +public record CreateCustomerAddress +{ + public string? Name { get; set; } + + public AddressType Type { get; set; } + + public AddressBuildingTypes BuildingType { get; set; } + + public string? Street { get; set; } + + public string? City { get; set; } + + public string? State { get; set; } + + public string? PostalCode { get; set; } + + public string? Country { get; set; } + + public bool IsPrimary { get; set; } + + public bool Enabled { get; set; } +} + +public record UpdateCustomerAddress : CreateCustomerAddress; \ No newline at end of file diff --git a/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs b/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs index 4ec3897..1703710 100644 --- a/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs +++ b/LiteCharms.Features.MidrandBooks/Extensions/Mappers.cs @@ -1,5 +1,6 @@ using LiteCharms.Features.MidrandBooks.AuthorBooks.Models; using LiteCharms.Features.MidrandBooks.Authors.Models; +using LiteCharms.Features.MidrandBooks.Customers.Models; using LiteCharms.Features.MidrandBooks.Pages.Models; using LiteCharms.Features.MidrandBooks.Products.Models; @@ -7,6 +8,52 @@ namespace LiteCharms.Features.MidrandBooks.Extensions; public static class Mappers { + public static Customer ToModel(this Customers.Entities.Customer entiry) => new() + { + Id = entiry.Id, + Company = entiry.Company, + CreatedAt = entiry.CreatedAt, + Email = entiry.Email, + Enabled = entiry.Enabled, + Phone = entiry.Phone, + SocialMedia = entiry.SocialMedia, + UpdatedAt = entiry.UpdatedAt, + VatNumber = entiry.VatNumber, + Website = entiry.Website + }; + + public static Address ToModel(this Customers.Entities.Address entity) => new() + { + Id = entity.Id, + BuildingType = entity.BuildingType, + CreatedAt = entity.CreatedAt, + CustomerId = entity.CustomerId, + Enabled = entity.Enabled, + IsPrimary = entity.IsPrimary, + Name = entity.Name, + PostalCode = entity.PostalCode, + Type = entity.Type, + UpdatedAt = entity.UpdatedAt, + Street = entity.Street, + City = entity.City, + State = entity.State, + Country = entity.Country + }; + + public static Contact ToModel(this Customers.Entities.Contact entity) => new() + { + Id = entity.Id, + Type = entity.Type, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt, + CustomerId = entity.CustomerId, + Email = entity.Email, + Enabled = entity.Enabled, + LastName = entity.LastName, + Name = entity.Name, + Phone = entity.Phone + }; + public static BookPage ToModel(this Pages.Entities.BookPage entity) => new() { Id = entity.Id, diff --git a/LiteCharms.Features.MidrandBooks/Pages/PagesService.cs b/LiteCharms.Features.MidrandBooks/Pages/PageService.cs similarity index 97% rename from LiteCharms.Features.MidrandBooks/Pages/PagesService.cs rename to LiteCharms.Features.MidrandBooks/Pages/PageService.cs index 18c822b..5c6f98a 100644 --- a/LiteCharms.Features.MidrandBooks/Pages/PagesService.cs +++ b/LiteCharms.Features.MidrandBooks/Pages/PageService.cs @@ -1,10 +1,11 @@ -using LiteCharms.Features.MidrandBooks.Extensions; +using LiteCharms.Features.MidrandBooks.Abstractions; +using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Pages.Models; using LiteCharms.Features.MidrandBooks.Postgres; namespace LiteCharms.Features.MidrandBooks.Pages; -public class PagesService(IDbContextFactory contextFactory) +public class PageService(IDbContextFactory contextFactory) : IService { public async ValueTask DeleteAllAsync(long authorBookId, CancellationToken cancellationToken = default) { diff --git a/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs b/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs index 1bb907d..d786b93 100644 --- a/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs +++ b/LiteCharms.Features.MidrandBooks/Postgres/MidrandBooksDbContext.cs @@ -1,5 +1,6 @@ using LiteCharms.Features.MidrandBooks.AuthorBooks.Entities; using LiteCharms.Features.MidrandBooks.Authors.Entities; +using LiteCharms.Features.MidrandBooks.Customers.Entities; using LiteCharms.Features.MidrandBooks.Pages.Entities; using LiteCharms.Features.MidrandBooks.Products.Entities; @@ -16,4 +17,10 @@ public class MidrandBooksDbContext(DbContextOptions optio public DbSet Books => Set(); public DbSet Pages => Set(); + + public DbSet Contacts => Set(); + + public DbSet
Addresses => Set
(); + + public DbSet Customers => Set(); } diff --git a/LiteCharms.Features.MidrandBooks/Products/ProductService.cs b/LiteCharms.Features.MidrandBooks/Products/ProductService.cs index 4cc30ab..0232048 100644 --- a/LiteCharms.Features.MidrandBooks/Products/ProductService.cs +++ b/LiteCharms.Features.MidrandBooks/Products/ProductService.cs @@ -1,11 +1,12 @@ -using LiteCharms.Features.MidrandBooks.Extensions; +using LiteCharms.Features.MidrandBooks.Abstractions; +using LiteCharms.Features.MidrandBooks.Extensions; using LiteCharms.Features.MidrandBooks.Postgres; using LiteCharms.Features.MidrandBooks.Products.Models; using LiteCharms.Features.Models; namespace LiteCharms.Features.MidrandBooks.Products; -public class ProductService(IDbContextFactory contextFactory) +public class ProductService(IDbContextFactory contextFactory) : IService { public async ValueTask UpdateProductPriceStatusAsync(long productPriceId, bool isEnabled, CancellationToken cancellationToken = default) { diff --git a/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj b/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj index a8478ad..0318fd4 100644 --- a/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj +++ b/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj @@ -23,6 +23,11 @@ utility;dotnet icon.png + + + + + diff --git a/LiteCharms.Features/Enums.cs b/LiteCharms.Features/Enums.cs index 510d826..951c3ff 100644 --- a/LiteCharms.Features/Enums.cs +++ b/LiteCharms.Features/Enums.cs @@ -1,5 +1,30 @@ namespace LiteCharms.Features; +public enum ContactTypes : int +{ + Personal = 0, + Business = 1, + Other = 2 +} + +public enum AddressType +{ + Billing = 1, + Shipping = 2, + Other = 3 +} + +public enum AddressBuildingTypes : int +{ + Residential = 0, + Commercial = 1, + Industrial = 2, + MixedUse = 3, + Agricultural = 4, + Institutional = 5, + Recreational = 6, +} + public enum SocialMediaTypes : int { Twitter = 0,