feat: initial project setup
- Add .NET 8 backend with Clean Architecture - Add React + Vite + TypeScript frontend - Implement authentication with JWT - Implement Azure Blob Storage client - Implement OCR integration - Implement supplier matching service - Implement voucher generation - Implement Fortnox provider - Add unit and integration tests - Add Docker Compose configuration
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
using InvoiceMaster.Core.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace InvoiceMaster.Infrastructure.Data;
|
||||
|
||||
public class ApplicationDbContext : DbContext
|
||||
{
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<User> Users => Set<User>();
|
||||
public DbSet<AccountingConnection> AccountingConnections => Set<AccountingConnection>();
|
||||
public DbSet<Invoice> Invoices => Set<Invoice>();
|
||||
public DbSet<SupplierCache> SupplierCaches => Set<SupplierCache>();
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using InvoiceMaster.Core.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace InvoiceMaster.Infrastructure.Data.Configurations;
|
||||
|
||||
public class AccountingConnectionConfiguration : IEntityTypeConfiguration<AccountingConnection>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<AccountingConnection> builder)
|
||||
{
|
||||
builder.ToTable("accounting_connections");
|
||||
|
||||
builder.HasKey(e => e.Id);
|
||||
|
||||
builder.Property(e => e.Provider)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.AccessTokenEncrypted)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.RefreshTokenEncrypted)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.CompanyName)
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.CompanyOrgNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(e => e.DefaultVoucherSeries)
|
||||
.HasMaxLength(10)
|
||||
.HasDefaultValue("A");
|
||||
|
||||
builder.Property(e => e.Scope);
|
||||
|
||||
builder.HasIndex(e => new { e.UserId, e.Provider })
|
||||
.IsUnique();
|
||||
|
||||
builder.HasIndex(e => e.Provider);
|
||||
builder.HasIndex(e => e.IsActive);
|
||||
builder.HasIndex(e => e.ExpiresAt);
|
||||
|
||||
builder.HasOne(e => e.User)
|
||||
.WithMany(u => u.Connections)
|
||||
.HasForeignKey(e => e.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using InvoiceMaster.Core.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace InvoiceMaster.Infrastructure.Data.Configurations;
|
||||
|
||||
public class InvoiceConfiguration : IEntityTypeConfiguration<Invoice>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Invoice> builder)
|
||||
{
|
||||
builder.ToTable("invoices");
|
||||
|
||||
builder.HasKey(e => e.Id);
|
||||
|
||||
builder.Property(e => e.Provider)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.OriginalFilename)
|
||||
.IsRequired()
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.StoragePath)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.FileHash)
|
||||
.HasMaxLength(64);
|
||||
|
||||
builder.Property(e => e.ExtractedSupplierName)
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.ExtractedSupplierOrgNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(e => e.ExtractedInvoiceNumber)
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(e => e.ExtractedOcrNumber)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.ExtractedBankgiro)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.ExtractedPlusgiro)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.ExtractedCurrency)
|
||||
.HasMaxLength(3)
|
||||
.HasDefaultValue("SEK");
|
||||
|
||||
builder.Property(e => e.SupplierNumber)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.VoucherSeries)
|
||||
.HasMaxLength(10);
|
||||
|
||||
builder.Property(e => e.VoucherNumber)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.ErrorCode)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.HasIndex(e => e.ConnectionId);
|
||||
builder.HasIndex(e => e.Provider);
|
||||
builder.HasIndex(e => e.Status);
|
||||
builder.HasIndex(e => e.CreatedAt);
|
||||
builder.HasIndex(e => e.FileHash);
|
||||
builder.HasIndex(e => e.ExtractedSupplierOrgNumber);
|
||||
|
||||
builder.HasOne(e => e.Connection)
|
||||
.WithMany(c => c.Invoices)
|
||||
.HasForeignKey(e => e.ConnectionId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using InvoiceMaster.Core.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace InvoiceMaster.Infrastructure.Data.Configurations;
|
||||
|
||||
public class SupplierCacheConfiguration : IEntityTypeConfiguration<SupplierCache>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SupplierCache> builder)
|
||||
{
|
||||
builder.ToTable("supplier_cache");
|
||||
|
||||
builder.HasKey(e => e.Id);
|
||||
|
||||
builder.Property(e => e.SupplierNumber)
|
||||
.IsRequired()
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.Name)
|
||||
.IsRequired()
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.OrganisationNumber)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(e => e.Address1)
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.Address2)
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.Postcode)
|
||||
.HasMaxLength(20);
|
||||
|
||||
builder.Property(e => e.City)
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(e => e.Country)
|
||||
.HasMaxLength(100);
|
||||
|
||||
builder.Property(e => e.Phone)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.Email)
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.BankgiroNumber)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.Property(e => e.PlusgiroNumber)
|
||||
.HasMaxLength(50);
|
||||
|
||||
builder.HasIndex(e => new { e.ConnectionId, e.SupplierNumber })
|
||||
.IsUnique();
|
||||
|
||||
builder.HasIndex(e => e.ConnectionId);
|
||||
builder.HasIndex(e => e.OrganisationNumber);
|
||||
builder.HasIndex(e => e.Name);
|
||||
builder.HasIndex(e => e.ExpiresAt);
|
||||
|
||||
builder.HasOne(e => e.Connection)
|
||||
.WithMany()
|
||||
.HasForeignKey(e => e.ConnectionId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using InvoiceMaster.Core.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace InvoiceMaster.Infrastructure.Data.Configurations;
|
||||
|
||||
public class UserConfiguration : IEntityTypeConfiguration<User>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<User> builder)
|
||||
{
|
||||
builder.ToTable("users");
|
||||
|
||||
builder.HasKey(e => e.Id);
|
||||
|
||||
builder.Property(e => e.Email)
|
||||
.IsRequired()
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.HasIndex(e => e.Email)
|
||||
.IsUnique();
|
||||
|
||||
builder.Property(e => e.HashedPassword)
|
||||
.IsRequired()
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.FullName)
|
||||
.HasMaxLength(255);
|
||||
|
||||
builder.Property(e => e.IsActive)
|
||||
.HasDefaultValue(true);
|
||||
|
||||
builder.Property(e => e.IsSuperuser)
|
||||
.HasDefaultValue(false);
|
||||
|
||||
builder.HasIndex(e => e.IsActive);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user