+ TIP: Usa un 'Hasta' muy alto (ej: 9999) en el último rango para cubrir todas las palabras extra restantes.
+
+
+ )}
+
diff --git a/src/SIGCM.Domain/Entities/CategoryPricing.cs b/src/SIGCM.Domain/Entities/CategoryPricing.cs
index 36bd351..829b1fb 100644
--- a/src/SIGCM.Domain/Entities/CategoryPricing.cs
+++ b/src/SIGCM.Domain/Entities/CategoryPricing.cs
@@ -10,4 +10,6 @@ public class CategoryPricing
public decimal SpecialCharPrice { get; set; }
public decimal BoldSurcharge { get; set; }
public decimal FrameSurcharge { get; set; }
+
+ public List WordRanges { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/SIGCM.Domain/Entities/WordPricingRange.cs b/src/SIGCM.Domain/Entities/WordPricingRange.cs
new file mode 100644
index 0000000..5c6510a
--- /dev/null
+++ b/src/SIGCM.Domain/Entities/WordPricingRange.cs
@@ -0,0 +1,10 @@
+namespace SIGCM.Domain.Entities;
+
+public class WordPricingRange
+{
+ public int Id { get; set; }
+ public int CategoryPricingId { get; set; }
+ public int FromCount { get; set; }
+ public int ToCount { get; set; } // Use a large number or 0 for "infinity"
+ public decimal PricePerWord { get; set; }
+}
diff --git a/src/SIGCM.Infrastructure/Data/DbInitializer.cs b/src/SIGCM.Infrastructure/Data/DbInitializer.cs
index 0c51252..ef33f1d 100644
--- a/src/SIGCM.Infrastructure/Data/DbInitializer.cs
+++ b/src/SIGCM.Infrastructure/Data/DbInitializer.cs
@@ -245,6 +245,19 @@ END
ALTER TABLE ListingNotes ADD IsFromModerator BIT NOT NULL DEFAULT 1;
END
END
+
+ -- Tabla de Rangos de Precios por Palabra
+ IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'WordPricingRanges') AND type in (N'U'))
+ BEGIN
+ CREATE TABLE WordPricingRanges (
+ Id INT IDENTITY(1,1) PRIMARY KEY,
+ CategoryPricingId INT NOT NULL,
+ FromCount INT NOT NULL,
+ ToCount INT NOT NULL,
+ PricePerWord DECIMAL(18,2) NOT NULL,
+ FOREIGN KEY (CategoryPricingId) REFERENCES CategoryPricing(Id) ON DELETE CASCADE
+ );
+ END
";
await connection.ExecuteAsync(migrationSql);
diff --git a/src/SIGCM.Infrastructure/Repositories/PricingRepository.cs b/src/SIGCM.Infrastructure/Repositories/PricingRepository.cs
index 554ea06..56fe89f 100644
--- a/src/SIGCM.Infrastructure/Repositories/PricingRepository.cs
+++ b/src/SIGCM.Infrastructure/Repositories/PricingRepository.cs
@@ -16,26 +16,35 @@ public class PricingRepository
public async Task GetByCategoryIdAsync(int categoryId)
{
using var conn = _db.CreateConnection();
- return await conn.QuerySingleOrDefaultAsync(
- "SELECT * FROM CategoryPricing WHERE CategoryId = @Id", new { Id = categoryId });
+ var sql = @"
+ SELECT * FROM CategoryPricing WHERE CategoryId = @Id;
+ SELECT * FROM WordPricingRanges WHERE CategoryPricingId = (SELECT Id FROM CategoryPricing WHERE CategoryId = @Id) ORDER BY FromCount;";
+
+ using var multi = await conn.QueryMultipleAsync(sql, new { Id = categoryId });
+ var pricing = await multi.ReadSingleOrDefaultAsync();
+ if (pricing != null)
+ {
+ pricing.WordRanges = (await multi.ReadAsync()).ToList();
+ }
+ return pricing;
}
public async Task UpsertPricingAsync(CategoryPricing pricing)
{
using var conn = _db.CreateConnection();
- // Lógica de "Si existe actualiza, sino inserta"
- var exists = await conn.ExecuteScalarAsync(
- "SELECT COUNT(1) FROM CategoryPricing WHERE CategoryId = @CategoryId", new { pricing.CategoryId });
+ var exists = await conn.QuerySingleOrDefaultAsync(
+ "SELECT Id FROM CategoryPricing WHERE CategoryId = @CategoryId", new { pricing.CategoryId });
- if (exists > 0)
+ if (exists != null)
{
+ pricing.Id = exists.Id;
var updateSql = @"
UPDATE CategoryPricing
SET BaseWordCount = @BaseWordCount,
ExtraWordPrice = @ExtraWordPrice, SpecialChars = @SpecialChars,
SpecialCharPrice = @SpecialCharPrice, BoldSurcharge = @BoldSurcharge,
FrameSurcharge = @FrameSurcharge
- WHERE CategoryId = @CategoryId";
+ WHERE Id = @Id";
await conn.ExecuteAsync(updateSql, pricing);
}
else
@@ -44,8 +53,21 @@ public class PricingRepository
INSERT INTO CategoryPricing
(CategoryId, BaseWordCount, ExtraWordPrice, SpecialChars, SpecialCharPrice, BoldSurcharge, FrameSurcharge)
VALUES
- (@CategoryId, @BaseWordCount, @ExtraWordPrice, @SpecialChars, @SpecialCharPrice, @BoldSurcharge, @FrameSurcharge)";
- await conn.ExecuteAsync(insertSql, pricing);
+ (@CategoryId, @BaseWordCount, @ExtraWordPrice, @SpecialChars, @SpecialCharPrice, @BoldSurcharge, @FrameSurcharge);
+ SELECT CAST(SCOPE_IDENTITY() as int);";
+ pricing.Id = await conn.QuerySingleAsync(insertSql, pricing);
+ }
+
+ // Gestionar Rangos de Palabras
+ await conn.ExecuteAsync("DELETE FROM WordPricingRanges WHERE CategoryPricingId = @Id", new { Id = pricing.Id });
+ if (pricing.WordRanges != null && pricing.WordRanges.Any())
+ {
+ foreach (var range in pricing.WordRanges)
+ {
+ range.CategoryPricingId = pricing.Id;
+ }
+ var rangeSql = "INSERT INTO WordPricingRanges (CategoryPricingId, FromCount, ToCount, PricePerWord) VALUES (@CategoryPricingId, @FromCount, @ToCount, @PricePerWord)";
+ await conn.ExecuteAsync(rangeSql, pricing.WordRanges);
}
}
diff --git a/src/SIGCM.Infrastructure/Services/PricingService.cs b/src/SIGCM.Infrastructure/Services/PricingService.cs
index 8d2969f..76d2be1 100644
--- a/src/SIGCM.Infrastructure/Services/PricingService.cs
+++ b/src/SIGCM.Infrastructure/Services/PricingService.cs
@@ -69,7 +69,30 @@ public class PricingService
// o suman al conteo de palabras. Aquí implemento: Se cobran APARTE.
int extraWords = Math.Max(0, realWordCount - pricing.BaseWordCount);
- decimal extraWordCost = extraWords * pricing.ExtraWordPrice;
+ decimal extraWordCost = 0;
+
+ if (pricing.WordRanges != null && pricing.WordRanges.Any() && extraWords > 0)
+ {
+ // Buscamos el rango aplicable para la cantidad TOTAL de palabras extra
+ // El ToCount = 0 se interpreta como "sin límite superior"
+ var applicableRange = pricing.WordRanges.FirstOrDefault(r =>
+ extraWords >= r.FromCount && (extraWords <= r.ToCount || r.ToCount == 0));
+
+ if (applicableRange != null)
+ {
+ extraWordCost = extraWords * applicableRange.PricePerWord;
+ }
+ else
+ {
+ // Fallback al precio base si no se define rango que cubra la cantidad
+ extraWordCost = extraWords * pricing.ExtraWordPrice;
+ }
+ }
+ else
+ {
+ extraWordCost = extraWords * pricing.ExtraWordPrice;
+ }
+
decimal specialCharCost = specialCharCount * pricing.SpecialCharPrice;
currentCost += extraWordCost + specialCharCost;