Feat ERP 2

This commit is contained in:
2026-01-07 17:52:10 -03:00
parent fdb221d0fa
commit 29aa8e30e7
54 changed files with 3035 additions and 275 deletions

View File

@@ -0,0 +1,43 @@
using Dapper;
using SIGCM.Domain.Entities;
using SIGCM.Domain.Interfaces;
using SIGCM.Infrastructure.Data;
namespace SIGCM.Infrastructure.Repositories;
public class CalendarRepository : ICalendarRepository
{
private readonly IDbConnectionFactory _db;
public CalendarRepository(IDbConnectionFactory db) => _db = db;
public async Task AddBlackoutAsync(PublicationBlackout blackout)
{
using var conn = _db.CreateConnection();
await conn.ExecuteAsync(
"INSERT INTO PublicationBlackouts (CompanyId, BlackoutDate, Reason) VALUES (@CompanyId, @BlackoutDate, @Reason)",
blackout);
}
public async Task DeleteBlackoutAsync(int id)
{
using var conn = _db.CreateConnection();
await conn.ExecuteAsync("DELETE FROM PublicationBlackouts WHERE Id = @Id", new { Id = id });
}
public async Task<IEnumerable<PublicationBlackout>> GetBlackoutsAsync(int companyId, int year)
{
using var conn = _db.CreateConnection();
return await conn.QueryAsync<PublicationBlackout>(
"SELECT * FROM PublicationBlackouts WHERE CompanyId = @CompanyId AND YEAR(BlackoutDate) = @Year ORDER BY BlackoutDate",
new { CompanyId = companyId, Year = year });
}
public async Task<bool> IsDateBlockedAsync(int companyId, DateTime date)
{
using var conn = _db.CreateConnection();
var count = await conn.ExecuteScalarAsync<int>(
"SELECT COUNT(1) FROM PublicationBlackouts WHERE CompanyId = @CompanyId AND BlackoutDate = @Date",
new { CompanyId = companyId, Date = date.Date });
return count > 0;
}
}

View File

@@ -0,0 +1,53 @@
using Dapper;
using SIGCM.Domain.Entities;
using SIGCM.Domain.Interfaces;
using SIGCM.Infrastructure.Data;
namespace SIGCM.Infrastructure.Repositories;
public class CompanyRepository : ICompanyRepository
{
private readonly IDbConnectionFactory _db;
public CompanyRepository(IDbConnectionFactory db) => _db = db;
public async Task<IEnumerable<Company>> GetAllAsync()
{
using var conn = _db.CreateConnection();
// Traemos todas para el admin, ordenadas por nombre
return await conn.QueryAsync<Company>("SELECT * FROM Companies ORDER BY Name");
}
public async Task<Company?> GetByIdAsync(int id)
{
using var conn = _db.CreateConnection();
return await conn.QuerySingleOrDefaultAsync<Company>("SELECT * FROM Companies WHERE Id = @Id", new { Id = id });
}
public async Task<int> CreateAsync(Company company)
{
using var conn = _db.CreateConnection();
var sql = @"
INSERT INTO Companies (Name, TaxId, LegalAddress, LogoUrl, ExternalSystemId, IsActive)
VALUES (@Name, @TaxId, @LegalAddress, @LogoUrl, @ExternalSystemId, @IsActive);
SELECT CAST(SCOPE_IDENTITY() as int);";
return await conn.QuerySingleAsync<int>(sql, company);
}
public async Task UpdateAsync(Company company)
{
using var conn = _db.CreateConnection();
var sql = @"
UPDATE Companies
SET Name = @Name, TaxId = @TaxId, LegalAddress = @LegalAddress,
LogoUrl = @LogoUrl, ExternalSystemId = @ExternalSystemId, IsActive = @IsActive
WHERE Id = @Id";
await conn.ExecuteAsync(sql, company);
}
public async Task DeleteAsync(int id)
{
using var conn = _db.CreateConnection();
// Soft delete para mantener integridad referencial con productos/ventas históricas
await conn.ExecuteAsync("UPDATE Companies SET IsActive = 0 WHERE Id = @Id", new { Id = id });
}
}

View File

@@ -126,4 +126,39 @@ public class ProductRepository : IProductRepository
"DELETE FROM ProductBundles WHERE ParentProductId = @ParentId AND ChildProductId = @ChildId",
new { ParentId = bundleId, ChildId = childProductId });
}
// Obtener el precio vigente para una fecha dada
public async Task<decimal> GetCurrentPriceAsync(int productId, DateTime date)
{
using var conn = _db.CreateConnection();
// Buscamos el precio específico vigente. Si no hay, fallback al BasePrice del producto.
var sql = @"
SELECT TOP 1 Price
FROM ProductPrices
WHERE ProductId = @ProductId
AND ValidFrom <= @Date
AND (ValidTo IS NULL OR ValidTo >= @Date)
ORDER BY ValidFrom DESC";
var specificPrice = await conn.ExecuteScalarAsync<decimal?>(sql, new { ProductId = productId, Date = date });
if (specificPrice.HasValue) return specificPrice.Value;
// Fallback
return await conn.ExecuteScalarAsync<decimal>("SELECT BasePrice FROM Products WHERE Id = @Id", new { Id = productId });
}
// Crear nuevo precio histórico
public async Task AddPriceAsync(ProductPrice price)
{
using var conn = _db.CreateConnection();
// Opcional: Cerrar vigencia del precio anterior automáticamente
await conn.ExecuteAsync(
"UPDATE ProductPrices SET ValidTo = GETUTCDATE() WHERE ProductId = @ProductId AND ValidTo IS NULL",
new { price.ProductId });
var sql = @"INSERT INTO ProductPrices (ProductId, Price, ValidFrom, ValidTo, CreatedByUserId)
VALUES (@ProductId, @Price, @ValidFrom, @ValidTo, @CreatedByUserId)";
await conn.ExecuteAsync(sql, price);
}
}

View File

@@ -0,0 +1,37 @@
using Dapper;
using SIGCM.Domain.Interfaces;
using SIGCM.Infrastructure.Data;
namespace SIGCM.Infrastructure.Repositories;
public class ReportRepository : IReportRepository
{
private readonly IDbConnectionFactory _db;
public ReportRepository(IDbConnectionFactory db)
{
_db = db;
}
public async Task<IEnumerable<SettlementItem>> GetInterCompanySettlementAsync(DateTime from, DateTime to)
{
using var conn = _db.CreateConnection();
var sql = @"
SELECT
b.Name as BillingCompany,
s.Name as ServiceCompany,
COUNT(*) as TransactionCount,
SUM(oi.SubTotal) as TotalAmount
FROM OrderItems oi
JOIN Orders o ON oi.OrderId = o.Id
JOIN Companies b ON oi.BillingCompanyId = b.Id
JOIN Companies s ON oi.ServiceCompanyId = s.Id
WHERE oi.BillingCompanyId <> oi.ServiceCompanyId
AND o.PaymentStatus = 'Paid' -- Solo liquidamos dinero efectivamente cobrado
AND o.CreatedAt >= @From AND o.CreatedAt <= @To
GROUP BY b.Name, s.Name
ORDER BY b.Name, s.Name";
return await conn.QueryAsync<SettlementItem>(sql, new { From = from, To = to });
}
}