feat: add recurring places and transaction pagination
Build MoneyMap image / build-and-push (push) Failing after 4s
Build MoneyMap image / build-and-push (push) Failing after 4s
This commit is contained in:
@@ -16,7 +16,11 @@
|
|||||||
"Bash(dotnet build:*)",
|
"Bash(dotnet build:*)",
|
||||||
"Bash(dotnet new:*)",
|
"Bash(dotnet new:*)",
|
||||||
"Bash(dotnet add:*)",
|
"Bash(dotnet add:*)",
|
||||||
"Bash(dotnet test:*)"
|
"Bash(dotnet test:*)",
|
||||||
|
"mcp__moneymap__search_transactions",
|
||||||
|
"mcp__moneymap__update_transaction_category",
|
||||||
|
"mcp__moneymap__get_spending_summary",
|
||||||
|
"mcp__moneymap__get_income_summary"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
- "MoneyMap/**"
|
- "MoneyMap/**"
|
||||||
- "MoneyMap.Core/**"
|
- "MoneyMap.Core/**"
|
||||||
- "MoneyMap.Mcp/**"
|
- "MoneyMap.Mcp/**"
|
||||||
- "MoneyMap/Dockerfile"
|
- "Dockerfile"
|
||||||
- ".gitea/workflows/build-moneymap.yml"
|
- ".gitea/workflows/build-moneymap.yml"
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.thecozycat.net -u "${{ gitea.actor }}" --password-stdin
|
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.thecozycat.net -u "${{ gitea.actor }}" --password-stdin
|
||||||
|
|
||||||
- name: Build image
|
- name: Build image
|
||||||
run: docker build -f MoneyMap/Dockerfile -t git.thecozycat.net/${{ gitea.repository_owner }}/moneymap:latest -t git.thecozycat.net/${{ gitea.repository_owner }}/moneymap:${{ gitea.sha }} .
|
run: docker build -f MoneyMap/Dockerfile -t git.thecozycat.net/${{ gitea.repository_owner }}/moneymap:latest -t git.thecozycat.net/${{ gitea.repository_owner }}/moneymap:${{ gitea.sha }} MoneyMap/
|
||||||
|
|
||||||
- name: Push image
|
- name: Push image
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace MoneyMap.Data
|
|||||||
public DbSet<CategoryMapping> CategoryMappings => Set<CategoryMapping>();
|
public DbSet<CategoryMapping> CategoryMappings => Set<CategoryMapping>();
|
||||||
public DbSet<Merchant> Merchants => Set<Merchant>();
|
public DbSet<Merchant> Merchants => Set<Merchant>();
|
||||||
public DbSet<Budget> Budgets => Set<Budget>();
|
public DbSet<Budget> Budgets => Set<Budget>();
|
||||||
|
public DbSet<RecurringPlace> RecurringPlaces => Set<RecurringPlace>();
|
||||||
|
public DbSet<Category> Categories => Set<Category>();
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
@@ -222,6 +224,53 @@ namespace MoneyMap.Data
|
|||||||
.HasFilter("[IsActive] = 1")
|
.HasFilter("[IsActive] = 1")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---------- RECURRING PLACE ----------
|
||||||
|
modelBuilder.Entity<RecurringPlace>(e =>
|
||||||
|
{
|
||||||
|
e.Property(x => x.Frequency).HasMaxLength(50).IsRequired();
|
||||||
|
e.Property(x => x.Notes).HasMaxLength(500);
|
||||||
|
|
||||||
|
// Relationships
|
||||||
|
e.HasOne(x => x.Merchant)
|
||||||
|
.WithMany(m => m.RecurringPlaces)
|
||||||
|
.HasForeignKey(x => x.MerchantId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
e.HasOne(x => x.Card)
|
||||||
|
.WithMany(c => c.RecurringPlaces)
|
||||||
|
.HasForeignKey(x => x.CardId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
e.HasOne(x => x.Category)
|
||||||
|
.WithMany(c => c.RecurringPlaces)
|
||||||
|
.HasForeignKey(x => x.CategoryId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
e.HasIndex(x => new { x.MerchantId, x.CardId, x.Frequency });
|
||||||
|
e.HasIndex(x => x.NextDueDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---------- CATEGORY ----------
|
||||||
|
modelBuilder.Entity<Category>(e =>
|
||||||
|
{
|
||||||
|
e.Property(x => x.Name).HasMaxLength(100).IsRequired();
|
||||||
|
e.Property(x => x.Description).HasMaxLength(500);
|
||||||
|
e.Property(x => x.Type).HasMaxLength(50);
|
||||||
|
|
||||||
|
// Parent-child relationship for hierarchical categories
|
||||||
|
e.HasOne(x => x.ParentCategory)
|
||||||
|
.WithMany(c => c.Children)
|
||||||
|
.HasForeignKey(x => x.ParentCategoryId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired(false);
|
||||||
|
|
||||||
|
e.HasIndex(x => new { x.Name, x.Type });
|
||||||
|
e.HasIndex(x => x.ParentCategoryId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class Card
|
|||||||
public string? Nickname { get; set; } // Optional friendly name
|
public string? Nickname { get; set; } // Optional friendly name
|
||||||
|
|
||||||
public ICollection<Transaction> Transactions { get; set; } = new List<Transaction>();
|
public ICollection<Transaction> Transactions { get; set; } = new List<Transaction>();
|
||||||
|
public ICollection<RecurringPlace> RecurringPlaces { get; set; } = new List<RecurringPlace>();
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string DisplayLabel => string.IsNullOrEmpty(Nickname)
|
public string DisplayLabel => string.IsNullOrEmpty(Nickname)
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace MoneyMap.Models;
|
||||||
|
|
||||||
|
public class Category
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string Type { get; set; } = string.Empty; // e.g., "Expense", "Income", "Transfer"
|
||||||
|
|
||||||
|
public bool IsDefault { get; set; } = false;
|
||||||
|
|
||||||
|
public int? ParentCategoryId { get; set; }
|
||||||
|
public Category? ParentCategory { get; set; }
|
||||||
|
public ICollection<Category> Children { get; set; } = new List<Category>();
|
||||||
|
|
||||||
|
public ICollection<RecurringPlace> RecurringPlaces { get; set; } = new List<RecurringPlace>();
|
||||||
|
public ICollection<CategoryMapping> CategoryMappings { get; set; } = new List<CategoryMapping>();
|
||||||
|
public ICollection<Transaction> Transactions { get; set; } = new List<Transaction>();
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string DisplayLabel => Name;
|
||||||
|
}
|
||||||
@@ -13,4 +13,5 @@ public class Merchant
|
|||||||
|
|
||||||
public ICollection<Transaction> Transactions { get; set; } = new List<Transaction>();
|
public ICollection<Transaction> Transactions { get; set; } = new List<Transaction>();
|
||||||
public ICollection<CategoryMapping> CategoryMappings { get; set; } = new List<CategoryMapping>();
|
public ICollection<CategoryMapping> CategoryMappings { get; set; } = new List<CategoryMapping>();
|
||||||
|
public ICollection<RecurringPlace> RecurringPlaces { get; set; } = new List<RecurringPlace>();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace MoneyMap.Models;
|
||||||
|
|
||||||
|
[Index(nameof(MerchantId), nameof(CardId), nameof(Frequency), IsUnique = true)]
|
||||||
|
public class RecurringPlace
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int MerchantId { get; set; }
|
||||||
|
public Merchant Merchant { get; set; } = null!;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int CardId { get; set; }
|
||||||
|
public Card Card { get; set; } = null!;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public int CategoryId { get; set; }
|
||||||
|
public Category Category { get; set; } = null!;
|
||||||
|
|
||||||
|
[Column(TypeName = "decimal(18,2)")]
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string Frequency { get; set; } = string.Empty; // e.g., "Monthly", "Weekly", "Quarterly"
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public DateTime NextDueDate { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
|
public string Notes { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public string DisplayLabel => $"{Merchant.Name} - {Card.Last4} - {Frequency} - ${Amount:N2}";
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoneyMap.Data;
|
||||||
|
using MoneyMap.Models;
|
||||||
|
|
||||||
|
namespace MoneyMap.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class RecurringPlacesController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly MoneyMapContext _db;
|
||||||
|
|
||||||
|
public RecurringPlacesController(MoneyMapContext db) => _db = db;
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> List()
|
||||||
|
{
|
||||||
|
var recurringPlaces = await _db.RecurringPlaces
|
||||||
|
.Include(rp => rp.Merchant)
|
||||||
|
.Include(rp => rp.Card)
|
||||||
|
.Include(rp => rp.Category)
|
||||||
|
.OrderBy(rp => rp.Merchant.Name)
|
||||||
|
.Select(rp => new
|
||||||
|
{
|
||||||
|
rp.Id,
|
||||||
|
MerchantName = rp.Merchant.Name,
|
||||||
|
rp.MerchantId,
|
||||||
|
rp.CardId,
|
||||||
|
CardLast4 = rp.Card != null ? rp.Card.Last4 : null,
|
||||||
|
CategoryName = rp.Category != null ? rp.Category.Name : null,
|
||||||
|
rp.Amount,
|
||||||
|
rp.Frequency,
|
||||||
|
rp.NextDueDate,
|
||||||
|
rp.Notes
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return Ok(recurringPlaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> Get(int id)
|
||||||
|
{
|
||||||
|
var recurringPlace = await _db.RecurringPlaces
|
||||||
|
.Include(rp => rp.Merchant)
|
||||||
|
.Include(rp => rp.Card)
|
||||||
|
.Include(rp => rp.Category)
|
||||||
|
.FirstOrDefaultAsync(rp => rp.Id == id);
|
||||||
|
|
||||||
|
if (recurringPlace == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
recurringPlace.Id,
|
||||||
|
MerchantName = recurringPlace.Merchant.Name,
|
||||||
|
recurringPlace.MerchantId,
|
||||||
|
recurringPlace.CardId,
|
||||||
|
CardLast4 = recurringPlace.Card != null ? recurringPlace.Card.Last4 : null,
|
||||||
|
CategoryName = recurringPlace.Category != null ? recurringPlace.Category.Name : null,
|
||||||
|
recurringPlace.Amount,
|
||||||
|
recurringPlace.Frequency,
|
||||||
|
recurringPlace.NextDueDate,
|
||||||
|
recurringPlace.Notes
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Create([FromBody] RecurringPlaceCreateModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
var merchant = await _db.Merchants.FindAsync(model.MerchantId);
|
||||||
|
var card = await _db.Cards.FindAsync(model.CardId);
|
||||||
|
var category = await _db.Categories.FindAsync(model.CategoryId);
|
||||||
|
|
||||||
|
if (merchant == null || card == null || category == null)
|
||||||
|
return BadRequest("Invalid merchant, card, or category");
|
||||||
|
|
||||||
|
var recurringPlace = new RecurringPlace
|
||||||
|
{
|
||||||
|
MerchantId = model.MerchantId,
|
||||||
|
CardId = model.CardId,
|
||||||
|
CategoryId = model.CategoryId,
|
||||||
|
Amount = model.Amount,
|
||||||
|
Frequency = model.Frequency,
|
||||||
|
NextDueDate = model.NextDueDate,
|
||||||
|
Notes = model.Notes
|
||||||
|
};
|
||||||
|
|
||||||
|
_db.RecurringPlaces.Add(recurringPlace);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return CreatedAtAction(nameof(Get), new { id = recurringPlace.Id }, recurringPlace);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public async Task<IActionResult> Update(int id, [FromBody] RecurringPlaceUpdateModel model)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
var recurringPlace = await _db.RecurringPlaces.FindAsync(id);
|
||||||
|
if (recurringPlace == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var merchant = await _db.Merchants.FindAsync(model.MerchantId);
|
||||||
|
var card = await _db.Cards.FindAsync(model.CardId);
|
||||||
|
var category = await _db.Categories.FindAsync(model.CategoryId);
|
||||||
|
|
||||||
|
if (merchant == null || card == null || category == null)
|
||||||
|
return BadRequest("Invalid merchant, card, or category");
|
||||||
|
|
||||||
|
recurringPlace.MerchantId = model.MerchantId;
|
||||||
|
recurringPlace.CardId = model.CardId;
|
||||||
|
recurringPlace.CategoryId = model.CategoryId;
|
||||||
|
recurringPlace.Amount = model.Amount;
|
||||||
|
recurringPlace.Frequency = model.Frequency;
|
||||||
|
recurringPlace.NextDueDate = model.NextDueDate;
|
||||||
|
recurringPlace.Notes = model.Notes;
|
||||||
|
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Delete(int id)
|
||||||
|
{
|
||||||
|
var recurringPlace = await _db.RecurringPlaces.FindAsync(id);
|
||||||
|
if (recurringPlace == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
_db.RecurringPlaces.Remove(recurringPlace);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RecurringPlaceCreateModel
|
||||||
|
{
|
||||||
|
public int MerchantId { get; set; }
|
||||||
|
public int CardId { get; set; }
|
||||||
|
public int CategoryId { get; set; }
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
public string Frequency { get; set; }
|
||||||
|
public DateTime NextDueDate { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RecurringPlaceUpdateModel
|
||||||
|
{
|
||||||
|
public int MerchantId { get; set; }
|
||||||
|
public int CardId { get; set; }
|
||||||
|
public int CategoryId { get; set; }
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
public string Frequency { get; set; }
|
||||||
|
public DateTime NextDueDate { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
}
|
||||||
@@ -31,8 +31,11 @@ public class TransactionsController : ControllerBase
|
|||||||
[FromQuery] int? cardId = null,
|
[FromQuery] int? cardId = null,
|
||||||
[FromQuery] string? type = null,
|
[FromQuery] string? type = null,
|
||||||
[FromQuery] bool? uncategorizedOnly = null,
|
[FromQuery] bool? uncategorizedOnly = null,
|
||||||
[FromQuery] int? limit = null)
|
[FromQuery] int? limit = null,
|
||||||
|
[FromQuery] int? offset = null)
|
||||||
{
|
{
|
||||||
|
int pageLimit = Math.Min(limit ?? 50, 500);
|
||||||
|
int pageOffset = offset ?? 0;
|
||||||
var q = _db.Transactions
|
var q = _db.Transactions
|
||||||
.Include(t => t.Merchant)
|
.Include(t => t.Merchant)
|
||||||
.Include(t => t.Card)
|
.Include(t => t.Card)
|
||||||
@@ -75,9 +78,12 @@ public class TransactionsController : ControllerBase
|
|||||||
if (uncategorizedOnly == true)
|
if (uncategorizedOnly == true)
|
||||||
q = q.Where(t => t.Category == null || t.Category == "");
|
q = q.Where(t => t.Category == null || t.Category == "");
|
||||||
|
|
||||||
|
var totalCount = await q.CountAsync();
|
||||||
|
|
||||||
var results = await q
|
var results = await q
|
||||||
.OrderByDescending(t => t.Date).ThenByDescending(t => t.Id)
|
.OrderByDescending(t => t.Date).ThenByDescending(t => t.Id)
|
||||||
.Take(limit ?? 50)
|
.Skip(pageOffset)
|
||||||
|
.Take(pageLimit)
|
||||||
.Select(t => new
|
.Select(t => new
|
||||||
{
|
{
|
||||||
t.Id,
|
t.Id,
|
||||||
@@ -94,7 +100,17 @@ public class TransactionsController : ControllerBase
|
|||||||
})
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Ok(new { Count = results.Count, Transactions = results });
|
int totalPages = (int)Math.Ceiling(totalCount / (double)pageLimit);
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
Count = results.Count,
|
||||||
|
TotalCount = totalCount,
|
||||||
|
Page = (pageOffset / pageLimit) + 1,
|
||||||
|
PageSize = pageLimit,
|
||||||
|
TotalPages = totalPages,
|
||||||
|
Transactions = results
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace MoneyMap.Migrations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds CategoryId foreign key and ParentCategoryId to RecurringPlaces,
|
||||||
|
/// and Category ParentCategoryId column to support hierarchical categories.
|
||||||
|
/// </summary>
|
||||||
|
public partial class AddCategoryToRecurringPlaces : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
// Add ParentCategoryId to Categories table (for hierarchical categories)
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ParentCategoryId",
|
||||||
|
table: "Categories",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
// Add CategoryId to RecurringPlaces table
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "RecurringPlaces",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
// Add index on ParentCategoryId for Categories
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Categories_ParentCategoryId",
|
||||||
|
table: "Categories",
|
||||||
|
column: "ParentCategoryId");
|
||||||
|
|
||||||
|
// Add foreign key from RecurringPlaces.CategoryId to Categories.Id
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_RecurringPlaces_Categories_CategoryId",
|
||||||
|
table: "RecurringPlaces",
|
||||||
|
column: "CategoryId",
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_RecurringPlaces_Categories_CategoryId",
|
||||||
|
table: "RecurringPlaces");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Categories_ParentCategoryId",
|
||||||
|
table: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ParentCategoryId",
|
||||||
|
table: "Categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CategoryId",
|
||||||
|
table: "RecurringPlaces");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user