refactor(currencies): убрать IsActive и MinorUnit из UI/API
- Currency.IsActive удалён полностью (domain/DTO/API/web/миграция). Валюты — глобальный справочник; «архивировать» USD глобально бессмысленно, а per-tenant видимости у валют нет. - MinorUnit остаётся в БД (нужен для форматирования цен), но скрыт из UI: убран CurrencyDto.MinorUnit, CurrencyInput.MinorUnit, колонка «Знаки» из списка. - Форма валюты — 3 поля: Код / Название / Символ. - Миграция Phase5e_DropCurrencyIsActive дропает колонку. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
24dc7fc619
commit
efeeb61e42
|
|
@ -33,14 +33,12 @@ public async Task<ActionResult<PagedResult<CurrencyDto>>> List([FromQuery] Paged
|
|||
("name", true) => q.OrderByDescending(c => c.Name),
|
||||
("symbol", false) => q.OrderBy(c => c.Symbol),
|
||||
("symbol", true) => q.OrderByDescending(c => c.Symbol),
|
||||
("isActive", false) => q.OrderBy(c => c.IsActive).ThenBy(c => c.Code),
|
||||
("isActive", true) => q.OrderByDescending(c => c.IsActive).ThenBy(c => c.Code),
|
||||
("code", true) => q.OrderByDescending(c => c.Code),
|
||||
_ => q.OrderBy(c => c.Code),
|
||||
};
|
||||
var items = await q
|
||||
.Skip(req.Skip).Take(req.Take)
|
||||
.Select(c => new CurrencyDto(c.Id, c.Code, c.Name, c.Symbol, c.MinorUnit, c.IsActive))
|
||||
.Select(c => new CurrencyDto(c.Id, c.Code, c.Name, c.Symbol))
|
||||
.ToListAsync(ct);
|
||||
return new PagedResult<CurrencyDto> { Items = items, Total = total, Page = req.Page, PageSize = req.Take };
|
||||
}
|
||||
|
|
@ -49,7 +47,7 @@ public async Task<ActionResult<PagedResult<CurrencyDto>>> List([FromQuery] Paged
|
|||
public async Task<ActionResult<CurrencyDto>> Get(Guid id, CancellationToken ct)
|
||||
{
|
||||
var c = await _db.Currencies.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id, ct);
|
||||
return c is null ? NotFound() : new CurrencyDto(c.Id, c.Code, c.Name, c.Symbol, c.MinorUnit, c.IsActive);
|
||||
return c is null ? NotFound() : new CurrencyDto(c.Id, c.Code, c.Name, c.Symbol);
|
||||
}
|
||||
|
||||
[HttpPost, Authorize(Roles = "SuperAdmin")]
|
||||
|
|
@ -60,13 +58,11 @@ public async Task<ActionResult<CurrencyDto>> Create([FromBody] CurrencyInput inp
|
|||
Code = input.Code.Trim().ToUpper(),
|
||||
Name = input.Name,
|
||||
Symbol = input.Symbol,
|
||||
MinorUnit = input.MinorUnit,
|
||||
IsActive = input.IsActive,
|
||||
};
|
||||
_db.Currencies.Add(e);
|
||||
await _db.SaveChangesAsync(ct);
|
||||
return CreatedAtAction(nameof(Get), new { id = e.Id },
|
||||
new CurrencyDto(e.Id, e.Code, e.Name, e.Symbol, e.MinorUnit, e.IsActive));
|
||||
new CurrencyDto(e.Id, e.Code, e.Name, e.Symbol));
|
||||
}
|
||||
|
||||
[HttpPut("{id:guid}"), Authorize(Roles = "SuperAdmin")]
|
||||
|
|
@ -77,8 +73,6 @@ public async Task<IActionResult> Update(Guid id, [FromBody] CurrencyInput input,
|
|||
e.Code = input.Code.Trim().ToUpper();
|
||||
e.Name = input.Name;
|
||||
e.Symbol = input.Symbol;
|
||||
e.MinorUnit = input.MinorUnit;
|
||||
e.IsActive = input.IsActive;
|
||||
await _db.SaveChangesAsync(ct);
|
||||
return NoContent();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public record CountryDto(
|
|||
decimal VatRate);
|
||||
|
||||
|
||||
public record CurrencyDto(Guid Id, string Code, string Name, string Symbol, int MinorUnit, bool IsActive);
|
||||
public record CurrencyDto(Guid Id, string Code, string Name, string Symbol);
|
||||
|
||||
public record UnitOfMeasureDto(
|
||||
Guid Id, string Code, string Name, string? Description, bool IsActive);
|
||||
|
|
@ -55,7 +55,7 @@ public record ProductDto(
|
|||
public record CountryInput(
|
||||
string Code, string Name,
|
||||
Guid? DefaultCurrencyId = null, decimal VatRate = 0m);
|
||||
public record CurrencyInput(string Code, string Name, string Symbol, int MinorUnit = 2, bool IsActive = true);
|
||||
public record CurrencyInput(string Code, string Name, string Symbol);
|
||||
public record UnitOfMeasureInput(string Code, string Name, string? Description = null, bool IsActive = true);
|
||||
public record PriceTypeInput(string Name, bool IsDefault = false, bool IsRetail = false, int SortOrder = 0, bool IsActive = true);
|
||||
public record StoreInput(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ public class Currency : Entity
|
|||
public string Code { get; set; } = null!; // ISO 4217, e.g. "KZT"
|
||||
public string Name { get; set; } = null!;
|
||||
public string Symbol { get; set; } = null!; // "₸"
|
||||
public int MinorUnit { get; set; } = 2; // 2 = two decimal places
|
||||
public bool IsActive { get; set; } = true;
|
||||
// Количество знаков после запятой для форматирования цен. Не редактируется
|
||||
// в UI — задаётся сидером/миграцией по ISO 4217.
|
||||
public int MinorUnit { get; set; } = 2;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace foodmarket.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <summary>Удаляет currencies.IsActive. Валюты — глобальный справочник,
|
||||
/// «архивировать» USD не имеет смысла. Если какая-то валюта не нужна
|
||||
/// конкретному магазину — пользователь её просто не выбирает.</summary>
|
||||
public partial class Phase5e_DropCurrencyIsActive : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder b)
|
||||
{
|
||||
b.DropColumn(name: "IsActive", schema: "public", table: "currencies");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder b)
|
||||
{
|
||||
b.AddColumn<bool>(
|
||||
name: "IsActive", schema: "public", table: "currencies",
|
||||
type: "boolean", nullable: false, defaultValue: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ export interface Country {
|
|||
defaultCurrencySymbol: string | null
|
||||
vatRate: number
|
||||
}
|
||||
export interface Currency { id: string; code: string; name: string; symbol: string; minorUnit: number; isActive: boolean }
|
||||
export interface Currency { id: string; code: string; name: string; symbol: string }
|
||||
export interface UnitOfMeasure { id: string; code: string; name: string; description: string | null; isActive: boolean }
|
||||
export interface PriceType { id: string; name: string; isDefault: boolean; isRetail: boolean; sortOrder: number; isActive: boolean }
|
||||
export interface Store {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ export function CurrenciesPage() {
|
|||
{ header: 'Код', width: '90px', sortKey: 'code', cell: (r) => <span className="font-mono">{r.code}</span> },
|
||||
{ header: 'Название', sortKey: 'name', cell: (r) => r.name },
|
||||
{ header: 'Символ', width: '100px', sortKey: 'symbol', cell: (r) => <span className="text-lg">{r.symbol}</span> },
|
||||
{ header: 'Знаки', width: '100px', className: 'text-right', cell: (r) => r.minorUnit },
|
||||
{ header: 'Активна', width: '100px', sortKey: 'isActive', cell: (r) => r.isActive ? '✓' : '—' },
|
||||
]}
|
||||
/>
|
||||
</ListPageShell>
|
||||
|
|
|
|||
Loading…
Reference in a new issue