From 1f9f573dafeb43a4c4ef31160223af56bf793bc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C2=A8Rafael?= <¨rafaelkaua97@gmail.com¨>
Date: Sat, 4 Oct 2025 19:58:44 -0300
Subject: [PATCH 1/7] FIN-33 adding TitleCategory
---
.idea/.idea.Fin-Backend/.idea/dataSources.xml | 13 ---
Fin-Backend.sln.DotSettings.user | 4 -
.../TitleCategoryController.cs | 54 +++++++++++
.../Dtos/TitleCategoryGetListInput.cs | 8 ++
.../TitleCategories/TitleCategoryService.cs | 96 +++++++++++++++++++
.../Global/Interfaces/IAuditedEntity.cs | 4 +-
.../Dtos/TitleCategoryInput.cs | 19 ++++
.../Dtos/TitleCategoryOutput.cs | 14 +++
.../TitleCategories/Entities/TitleCategory.cs | 42 ++++++++
.../Enums/TitleCategoryType.cs | 8 ++
10 files changed, 242 insertions(+), 20 deletions(-)
delete mode 100644 .idea/.idea.Fin-Backend/.idea/dataSources.xml
delete mode 100644 Fin-Backend.sln.DotSettings.user
create mode 100644 Fin.Api/TitleCategories/TitleCategoryController.cs
create mode 100644 Fin.Application/TitleCategories/Dtos/TitleCategoryGetListInput.cs
create mode 100644 Fin.Application/TitleCategories/TitleCategoryService.cs
create mode 100644 Fin.Domain/TitleCategories/Dtos/TitleCategoryInput.cs
create mode 100644 Fin.Domain/TitleCategories/Dtos/TitleCategoryOutput.cs
create mode 100644 Fin.Domain/TitleCategories/Entities/TitleCategory.cs
create mode 100644 Fin.Domain/TitleCategories/Enums/TitleCategoryType.cs
diff --git a/.idea/.idea.Fin-Backend/.idea/dataSources.xml b/.idea/.idea.Fin-Backend/.idea/dataSources.xml
deleted file mode 100644
index 5f87bc2..0000000
--- a/.idea/.idea.Fin-Backend/.idea/dataSources.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- postgresql
- true
- true
- org.postgresql.Driver
- jdbc:postgresql://localhost:5432/postgres?password=postgres&user=postgres
- $ProjectFileDir$
-
-
-
\ No newline at end of file
diff --git a/Fin-Backend.sln.DotSettings.user b/Fin-Backend.sln.DotSettings.user
deleted file mode 100644
index 67f132d..0000000
--- a/Fin-Backend.sln.DotSettings.user
+++ /dev/null
@@ -1,4 +0,0 @@
-
- <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
- <Solution />
-</SessionState>
\ No newline at end of file
diff --git a/Fin.Api/TitleCategories/TitleCategoryController.cs b/Fin.Api/TitleCategories/TitleCategoryController.cs
new file mode 100644
index 0000000..3a66689
--- /dev/null
+++ b/Fin.Api/TitleCategories/TitleCategoryController.cs
@@ -0,0 +1,54 @@
+using Fin.Application.TitleCategories;
+using Fin.Application.TitleCategories.Dtos;
+using Fin.Domain.Global.Classes;
+using Fin.Domain.TitleCategories.Dtos;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Fin.Api.TitleCategories;
+
+[Route("title-categories")]
+[Authorize]
+public class TitleCategoryController(ITitleCategoryService service): ControllerBase
+{
+ [HttpGet]
+ public async Task> GetList([FromQuery] TitleCategoryGetListInput input)
+ {
+ return await service.GetList(input);
+ }
+
+ [HttpGet("{id:guid}")]
+ public async Task> Get([FromRoute] Guid id)
+ {
+ var category = await service.Get(id);
+ return category != null ? Ok(category) : NotFound();
+ }
+
+ [HttpPost]
+ public async Task> Create([FromBody] TitleCategoryInput input)
+ {
+ var category = await service.Create(input, autoSave: true);
+ return category != null ? Created($"categories/{category.Id}", category) : UnprocessableEntity();
+ }
+
+ [HttpPut("{id:guid}")]
+ public async Task Update([FromRoute] Guid id, [FromBody] TitleCategoryInput input)
+ {
+ var updated = await service.Update(id, input, autoSave: true);
+ return updated ? Ok() : UnprocessableEntity();
+ }
+
+ [HttpPut("toggle-inactivated/{id:guid}")]
+ public async Task ToggleInactivated([FromRoute] Guid id)
+ {
+ var updated = await service.ToggleInactive(id, autoSave: true);
+ return updated ? Ok() : UnprocessableEntity();
+ }
+
+ [HttpDelete("{id:guid}")]
+ public async Task Delete([FromRoute] Guid id)
+ {
+ var deleted = await service.Delete(id, autoSave: true);
+ return deleted ? Ok() : UnprocessableEntity();
+ }
+}
\ No newline at end of file
diff --git a/Fin.Application/TitleCategories/Dtos/TitleCategoryGetListInput.cs b/Fin.Application/TitleCategories/Dtos/TitleCategoryGetListInput.cs
new file mode 100644
index 0000000..1a82880
--- /dev/null
+++ b/Fin.Application/TitleCategories/Dtos/TitleCategoryGetListInput.cs
@@ -0,0 +1,8 @@
+using Fin.Domain.Global.Classes;
+
+namespace Fin.Application.TitleCategories.Dtos;
+
+public class TitleCategoryGetListInput: PagedFilteredAndSortedInput
+{
+ public bool? Inactivated { get; set; }
+}
\ No newline at end of file
diff --git a/Fin.Application/TitleCategories/TitleCategoryService.cs b/Fin.Application/TitleCategories/TitleCategoryService.cs
new file mode 100644
index 0000000..4a362bf
--- /dev/null
+++ b/Fin.Application/TitleCategories/TitleCategoryService.cs
@@ -0,0 +1,96 @@
+using Fin.Application.TitleCategories.Dtos;
+using Fin.Domain.Global.Classes;
+using Fin.Domain.TitleCategories.Dtos;
+using Fin.Domain.TitleCategories.Entities;
+using Fin.Infrastructure.AutoServices.Interfaces;
+using Fin.Infrastructure.Database.Extensions;
+using Fin.Infrastructure.Database.Repositories;
+using Microsoft.AspNetCore.Http;
+using Microsoft.EntityFrameworkCore;
+
+namespace Fin.Application.TitleCategories;
+
+public interface ITitleCategoryService
+{
+ public Task Get(Guid id);
+ public Task> GetList(TitleCategoryGetListInput input);
+ public Task Create(TitleCategoryInput input, bool autoSave = false);
+ public Task Update(Guid id, TitleCategoryInput input, bool autoSave = false);
+ public Task Delete(Guid id, bool autoSave = false);
+ public Task ToggleInactive(Guid id, bool autoSave = false);
+}
+
+public class TitleCategoryService(
+ IRepository repository
+ ) : ITitleCategoryService, IAutoTransient
+{
+ public async Task Get(Guid id)
+ {
+ var entity = await repository.Query(false).FirstOrDefaultAsync(n => n.Id == id);
+ return entity != null ? new TitleCategoryOutput(entity) : null;
+ }
+
+ public async Task> GetList(TitleCategoryGetListInput input)
+ {
+ return await repository.Query(false)
+ .WhereIf(input.Inactivated.HasValue, n => n.Inactivated == input.Inactivated.Value)
+ .OrderByDescending(m => m.Inactivated)
+ .ThenBy(m => m.Name)
+ .ApplyFilterAndSorter(input)
+ .Select(n => new TitleCategoryOutput(n))
+ .ToPagedResult(input);
+ }
+
+ public async Task Create(TitleCategoryInput input, bool autoSave = false)
+ {
+ ValidarInput(input);
+ var titleCategory = new TitleCategory(input);
+ await repository.AddAsync(titleCategory, autoSave);
+ return new TitleCategoryOutput(titleCategory);
+ }
+
+ public async Task Update(Guid id, TitleCategoryInput input, bool autoSave = false)
+ {
+ ValidarInput(input);
+ var titleCategory = await repository.Query()
+ .FirstOrDefaultAsync(u => u.Id == id);
+ if (titleCategory == null) return false;
+
+ titleCategory.Update(input);
+ await repository.UpdateAsync(titleCategory, autoSave);
+
+ return true;
+ }
+
+ public async Task Delete(Guid id, bool autoSave = false)
+ {
+ var titleCategory = await repository.Query()
+ .FirstOrDefaultAsync(u => u.Id == id);
+ if (titleCategory == null) return false;
+
+ await repository.DeleteAsync(titleCategory, autoSave);
+ return true;
+ }
+
+ public async Task ToggleInactive(Guid id, bool autoSave = false)
+ {
+ var titleCategory = await repository.Query()
+ .FirstOrDefaultAsync(u => u.Id == id);
+ if (titleCategory == null) return false;
+
+ titleCategory.ToggleInactivated();
+ await repository.UpdateAsync(titleCategory, autoSave);
+
+ return true;
+ }
+
+ private static void ValidarInput( TitleCategoryInput input)
+ {
+ if (string.IsNullOrWhiteSpace(input.Color))
+ throw new BadHttpRequestException("FrontRoute is required");
+ if (string.IsNullOrWhiteSpace(input.Name))
+ throw new BadHttpRequestException("Name is required");
+ if (string.IsNullOrWhiteSpace(input.Icon))
+ throw new BadHttpRequestException("Icon is required");
+ }
+}
\ No newline at end of file
diff --git a/Fin.Domain/Global/Interfaces/IAuditedEntity.cs b/Fin.Domain/Global/Interfaces/IAuditedEntity.cs
index cbde6a6..570e202 100644
--- a/Fin.Domain/Global/Interfaces/IAuditedEntity.cs
+++ b/Fin.Domain/Global/Interfaces/IAuditedEntity.cs
@@ -1,6 +1,4 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Fin.Domain.Global.Interfaces;
+namespace Fin.Domain.Global.Interfaces;
public interface IAuditedEntity: IEntity
{
diff --git a/Fin.Domain/TitleCategories/Dtos/TitleCategoryInput.cs b/Fin.Domain/TitleCategories/Dtos/TitleCategoryInput.cs
new file mode 100644
index 0000000..965046b
--- /dev/null
+++ b/Fin.Domain/TitleCategories/Dtos/TitleCategoryInput.cs
@@ -0,0 +1,19 @@
+using System.ComponentModel.DataAnnotations;
+using Fin.Domain.TitleCategories.Enums;
+
+namespace Fin.Domain.TitleCategories.Dtos;
+
+public class TitleCategoryInput
+{
+ [Required]
+ public string Name { get; set; }
+
+ [Required]
+ public string Color { get; set; }
+ [Required]
+
+ public string Icon { get; set; }
+
+ [Required]
+ public TitleCategoryType Type { get; set; }
+}
\ No newline at end of file
diff --git a/Fin.Domain/TitleCategories/Dtos/TitleCategoryOutput.cs b/Fin.Domain/TitleCategories/Dtos/TitleCategoryOutput.cs
new file mode 100644
index 0000000..710e24f
--- /dev/null
+++ b/Fin.Domain/TitleCategories/Dtos/TitleCategoryOutput.cs
@@ -0,0 +1,14 @@
+using Fin.Domain.TitleCategories.Entities;
+using Fin.Domain.TitleCategories.Enums;
+
+namespace Fin.Domain.TitleCategories.Dtos;
+
+public class TitleCategoryOutput(TitleCategory titleCategory)
+{
+ public Guid Id { get; } = titleCategory.Id;
+ public bool Inactivated { get; } = titleCategory.Inactivated;
+ public string Name { get; } = titleCategory.Name;
+ public string Color { get; } = titleCategory.Color;
+ public string Icon { get; } = titleCategory.Icon;
+ public TitleCategoryType Type { get; } = titleCategory.Type;
+}
\ No newline at end of file
diff --git a/Fin.Domain/TitleCategories/Entities/TitleCategory.cs b/Fin.Domain/TitleCategories/Entities/TitleCategory.cs
new file mode 100644
index 0000000..605bbe2
--- /dev/null
+++ b/Fin.Domain/TitleCategories/Entities/TitleCategory.cs
@@ -0,0 +1,42 @@
+using Fin.Domain.Global.Interfaces;
+using Fin.Domain.TitleCategories.Dtos;
+using Fin.Domain.TitleCategories.Enums;
+
+namespace Fin.Domain.TitleCategories.Entities;
+
+public class TitleCategory: IAuditedTenantEntity
+{
+ public bool Inactivated { get; private set; }
+ public string Name { get; private set; }
+ public string Color { get; private set; }
+ public string Icon { get; private set; }
+ public TitleCategoryType Type { get; private set; }
+
+ public Guid Id { get; set; }
+ public Guid CreatedBy { get; set; }
+ public Guid UpdatedBy { get; set; }
+ public DateTime CreatedAt { get; set; }
+ public DateTime UpdatedAt { get; set; }
+ public Guid TenantId { get; set; }
+
+ public TitleCategory()
+ {
+ }
+ public TitleCategory(TitleCategoryInput input)
+ {
+ Name = input.Name;
+ Color = input.Color;
+ Icon = input.Icon;
+ Type = input.Type;
+ }
+
+ public void Update(TitleCategoryInput input)
+ {
+ Name = input.Name;
+ Color = input.Color;
+ Icon = input.Icon;
+ Type = input.Type;
+ }
+
+ public void ToggleInactivated() => Inactivated = !Inactivated;
+}
\ No newline at end of file
diff --git a/Fin.Domain/TitleCategories/Enums/TitleCategoryType.cs b/Fin.Domain/TitleCategories/Enums/TitleCategoryType.cs
new file mode 100644
index 0000000..398ee35
--- /dev/null
+++ b/Fin.Domain/TitleCategories/Enums/TitleCategoryType.cs
@@ -0,0 +1,8 @@
+namespace Fin.Domain.TitleCategories.Enums;
+
+public enum TitleCategoryType: byte
+{
+ Expense = 0,
+ Income = 1,
+ Both = 2
+}
\ No newline at end of file
From e85dae85eb90d0e8de53cc61055b043a13448f88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C2=A8Rafael?= <¨rafaelkaua97@gmail.com¨>
Date: Sat, 4 Oct 2025 20:05:29 -0300
Subject: [PATCH 2/7] FIN-33 adding TitleCategorie to db
---
Fin-Backend.sln.DotSettings.user | 2 +
.../TitleCategorieConfiguration.cs | 17 +
Fin.Infrastructure/Database/FinDbContext.cs | 3 +
...04230243_adding_title_category.Designer.cs | 560 ++++++++++++++++++
.../20251004230243_adding_title_category.cs | 45 ++
.../Migrations/FinDbContextModelSnapshot.cs | 47 ++
6 files changed, 674 insertions(+)
create mode 100644 Fin-Backend.sln.DotSettings.user
create mode 100644 Fin.Infrastructure/Database/Configurations/TitleCategories/TitleCategorieConfiguration.cs
create mode 100644 Fin.Infrastructure/Migrations/20251004230243_adding_title_category.Designer.cs
create mode 100644 Fin.Infrastructure/Migrations/20251004230243_adding_title_category.cs
diff --git a/Fin-Backend.sln.DotSettings.user b/Fin-Backend.sln.DotSettings.user
new file mode 100644
index 0000000..7a2e2ac
--- /dev/null
+++ b/Fin-Backend.sln.DotSettings.user
@@ -0,0 +1,2 @@
+
+ ForceIncluded
\ No newline at end of file
diff --git a/Fin.Infrastructure/Database/Configurations/TitleCategories/TitleCategorieConfiguration.cs b/Fin.Infrastructure/Database/Configurations/TitleCategories/TitleCategorieConfiguration.cs
new file mode 100644
index 0000000..555d78b
--- /dev/null
+++ b/Fin.Infrastructure/Database/Configurations/TitleCategories/TitleCategorieConfiguration.cs
@@ -0,0 +1,17 @@
+using Fin.Domain.TitleCategories.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Fin.Infrastructure.Database.Configurations.TitleCategories;
+
+public class TitleCategoryConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasKey(x => x.Id);
+
+ builder.Property(x => x.Name).HasMaxLength(100).IsRequired();
+ builder.Property(x => x.Icon).HasMaxLength(20).IsRequired();
+ builder.Property(x => x.Color).HasMaxLength(20).IsRequired();
+ }
+}
\ No newline at end of file
diff --git a/Fin.Infrastructure/Database/FinDbContext.cs b/Fin.Infrastructure/Database/FinDbContext.cs
index 527c06c..ab22eb9 100644
--- a/Fin.Infrastructure/Database/FinDbContext.cs
+++ b/Fin.Infrastructure/Database/FinDbContext.cs
@@ -4,6 +4,7 @@
using Fin.Domain.Notifications;
using Fin.Domain.Notifications.Entities;
using Fin.Domain.Tenants.Entities;
+using Fin.Domain.TitleCategories.Entities;
using Fin.Domain.Users.Entities;
using Fin.Infrastructure.AmbientDatas;
using Fin.Infrastructure.Database.Configurations;
@@ -27,6 +28,8 @@ public class FinDbContext : DbContext
public DbSet NotificationUserDeliveries { get; set; }
public DbSet