Add NbtPercent column to GoodsReceipts and GoodsReceiptItems

This commit is contained in:
2026-10-08 12:44:28 +05:30
parent 702efba62e
commit b12f3c8d08
5 changed files with 4547 additions and 0 deletions
@@ -7,3 +7,6 @@ public record SysConfigResponse(string Key, string Value, DateTime UpdatedAt, Gu
public record SetSysConfigRequest(string Value, Guid? UserId);
public record SetInvoiceCreationModeRequest(InvoiceCreationMode Mode);
/// <summary>Which taxes apply to a GRN, whether each must be filled in, and the rate new GRNs start with.</summary>
public record GrnTaxSettings(TaxMode VatMode, decimal VatDefaultRate, TaxMode NbtMode, decimal NbtDefaultRate);
@@ -14,6 +14,14 @@ public class SysConfigService(ISysConfigRepository repo, IUnitOfWork uow)
public const string KeyCustomWarrantyOverrideEnabled = "Feature.CustomWarrantyOverride";
public const string KeyDefaultWarrantyPeriodDays = "Feature.DefaultWarrantyPeriodDays";
public const string KeyGrnVatMode = "Grn.Tax.Vat.Mode";
public const string KeyGrnVatRate = "Grn.Tax.Vat.DefaultRate";
public const string KeyGrnNbtMode = "Grn.Tax.Nbt.Mode";
public const string KeyGrnNbtRate = "Grn.Tax.Nbt.DefaultRate";
private static readonly string[] TaxModeKeys = [KeyGrnVatMode, KeyGrnNbtMode];
private static readonly string[] TaxRateKeys = [KeyGrnVatRate, KeyGrnNbtRate];
private static readonly string[] BooleanFeatureKeys =
[
KeySerialNumberTrackingEnabled,
@@ -83,6 +91,33 @@ public class SysConfigService(ISysConfigRepository repo, IUnitOfWork uow)
return config is not null && int.TryParse(config.Value, out var days) ? days : 0;
}
/// <summary>
/// GRN tax settings. Unconfigured, VAT is Optional (how GRNs behaved before this setting existed)
/// and NBT is Disabled.
/// </summary>
public async Task<GrnTaxSettings> GetGrnTaxSettingsAsync() => new(
await GetTaxModeAsync(KeyGrnVatMode, TaxMode.Optional),
await GetRateAsync(KeyGrnVatRate),
await GetTaxModeAsync(KeyGrnNbtMode, TaxMode.Disabled),
await GetRateAsync(KeyGrnNbtRate));
private async Task<TaxMode> GetTaxModeAsync(string key, TaxMode fallback)
{
var config = await repo.GetByKeyAsync(key);
return config is not null && Enum.TryParse<TaxMode>(config.Value, out var mode) && Enum.IsDefined(mode)
? mode
: fallback;
}
private async Task<decimal> GetRateAsync(string key)
{
var config = await repo.GetByKeyAsync(key);
return config is not null
&& decimal.TryParse(config.Value, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out var rate)
? rate
: 0m;
}
private async Task<bool> GetBoolAsync(string key)
{
var config = await repo.GetByKeyAsync(key);
@@ -98,6 +133,15 @@ public class SysConfigService(ISysConfigRepository repo, IUnitOfWork uow)
if (BooleanFeatureKeys.Contains(key) && !bool.TryParse(value, out _))
throw new InvalidOperationException($"Invalid value '{value}' for '{key}'. Allowed values: true, false.");
if (TaxModeKeys.Contains(key) && (!Enum.TryParse<TaxMode>(value, out var taxMode) || !Enum.IsDefined(taxMode)))
throw new InvalidOperationException(
$"Invalid value '{value}' for '{key}'. Allowed values: {string.Join(", ", Enum.GetNames<TaxMode>())}.");
if (TaxRateKeys.Contains(key)
&& (!decimal.TryParse(value, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out var rate)
|| rate < 0 || rate > 100))
throw new InvalidOperationException($"Invalid value '{value}' for '{key}'. Must be a percentage from 0 to 100.");
if (key == KeyDefaultWarrantyPeriodDays && (!int.TryParse(value, out var days) || days < 0))
throw new InvalidOperationException($"Invalid value '{value}' for '{key}'. Must be a non-negative integer.");
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,66 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Distribution.Migrations
{
/// <inheritdoc />
public partial class AddGrnNbt : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<decimal>(
name: "NbtPercent",
table: "GoodsReceipts",
type: "decimal(5,2)",
precision: 5,
scale: 2,
nullable: false,
defaultValue: 0m);
migrationBuilder.AddColumn<decimal>(
name: "NbtPercent",
table: "GoodsReceiptItems",
type: "decimal(5,2)",
precision: 5,
scale: 2,
nullable: false,
defaultValue: 0m);
migrationBuilder.UpdateData(
table: "GoodsReceiptItems",
keyColumn: "Id",
keyValue: new Guid("44000000-0000-0000-0000-000000000001"),
column: "NbtPercent",
value: 0m);
migrationBuilder.UpdateData(
table: "GoodsReceiptItems",
keyColumn: "Id",
keyValue: new Guid("44000000-0000-0000-0000-000000000002"),
column: "NbtPercent",
value: 0m);
migrationBuilder.UpdateData(
table: "GoodsReceipts",
keyColumn: "Id",
keyValue: new Guid("43000000-0000-0000-0000-000000000001"),
column: "NbtPercent",
value: 0m);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "NbtPercent",
table: "GoodsReceipts");
migrationBuilder.DropColumn(
name: "NbtPercent",
table: "GoodsReceiptItems");
}
}
}
@@ -446,6 +446,10 @@ namespace Distribution.Migrations
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<decimal>("NbtPercent")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<DateTime>("ReceivedAt")
.HasColumnType("datetime(6)");
@@ -473,6 +477,7 @@ namespace Distribution.Migrations
FK_ReceivedByUserId = new Guid("29000000-0000-0000-0000-000000000001"),
FK_WarehouseId = new Guid("27000000-0000-0000-0000-000000000001"),
IsActive = true,
NbtPercent = 0m,
ReceivedAt = new DateTime(2026, 8, 15, 0, 0, 0, 0, DateTimeKind.Unspecified),
VatPercent = 0m
});
@@ -509,6 +514,10 @@ namespace Distribution.Migrations
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<decimal>("NbtPercent")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<int>("RejectedQuantity")
.HasColumnType("int");
@@ -541,6 +550,7 @@ namespace Distribution.Migrations
FK_PurchaseOrderItemId = new Guid("42000000-0000-0000-0000-000000000001"),
FreeQuantity = 0,
IsActive = true,
NbtPercent = 0m,
RejectedQuantity = 0,
UnitCost = 0m,
VatPercent = 0m
@@ -555,6 +565,7 @@ namespace Distribution.Migrations
FK_PurchaseOrderItemId = new Guid("42000000-0000-0000-0000-000000000002"),
FreeQuantity = 0,
IsActive = true,
NbtPercent = 0m,
RejectedQuantity = 2,
UnitCost = 0m,
VatPercent = 0m