refactor(countries): drop SortOrder, sort by Name, auto-width columns
- Country.SortOrder удалено из домена/DTO/API/seeder/web/UI. - Миграция Phase5b_DropCountrySortOrder дропает колонку. - Список стран сортируется по Name ASC. - В форме: поле «Порядок» убрано. - В таблице: убрана колонка «Порядок», ширины колонок сжаты по содержимому (Код 80px, Валюта 120px, НДС 100px, Название flex). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5bea852b94
commit
31d528d5c2
|
|
@ -28,10 +28,10 @@ public async Task<ActionResult<PagedResult<CountryDto>>> List([FromQuery] PagedR
|
|||
}
|
||||
var total = await q.CountAsync(ct);
|
||||
var items = await q
|
||||
.OrderBy(c => c.SortOrder).ThenBy(c => c.Name)
|
||||
.OrderBy(c => c.Name)
|
||||
.Skip(req.Skip).Take(req.Take)
|
||||
.Select(c => new CountryDto(
|
||||
c.Id, c.Code, c.Name, c.SortOrder,
|
||||
c.Id, c.Code, c.Name,
|
||||
c.DefaultCurrencyId,
|
||||
c.DefaultCurrency != null ? c.DefaultCurrency.Code : null,
|
||||
c.DefaultCurrency != null ? c.DefaultCurrency.Symbol : null,
|
||||
|
|
@ -54,7 +54,6 @@ public async Task<ActionResult<CountryDto>> Create([FromBody] CountryInput input
|
|||
{
|
||||
Code = input.Code.Trim().ToUpper(),
|
||||
Name = input.Name,
|
||||
SortOrder = input.SortOrder,
|
||||
DefaultCurrencyId = input.DefaultCurrencyId,
|
||||
VatRate = input.VatRate,
|
||||
};
|
||||
|
|
@ -71,7 +70,6 @@ public async Task<IActionResult> Update(Guid id, [FromBody] CountryInput input,
|
|||
if (e is null) return NotFound();
|
||||
e.Code = input.Code.Trim().ToUpper();
|
||||
e.Name = input.Name;
|
||||
e.SortOrder = input.SortOrder;
|
||||
e.DefaultCurrencyId = input.DefaultCurrencyId;
|
||||
e.VatRate = input.VatRate;
|
||||
await _db.SaveChangesAsync(ct);
|
||||
|
|
@ -89,6 +87,6 @@ public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
|
|||
}
|
||||
|
||||
private static CountryDto Project(Country c) => new(
|
||||
c.Id, c.Code, c.Name, c.SortOrder,
|
||||
c.Id, c.Code, c.Name,
|
||||
c.DefaultCurrencyId, c.DefaultCurrency?.Code, c.DefaultCurrency?.Symbol, c.VatRate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,22 +31,22 @@ public async Task StartAsync(CancellationToken ct)
|
|||
|
||||
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
|
||||
|
||||
private record CountrySeed(string Code, string Name, int SortOrder, string CurrencyCode, decimal VatRate);
|
||||
private record CountrySeed(string Code, string Name, string CurrencyCode, decimal VatRate);
|
||||
|
||||
private static readonly CountrySeed[] CountrySeeds =
|
||||
{
|
||||
new("KZ", "Казахстан", 1, "KZT", 16m),
|
||||
new("RU", "Россия", 2, "RUB", 20m),
|
||||
new("CN", "Китай", 3, "CNY", 13m),
|
||||
new("TR", "Турция", 4, "TRY", 18m),
|
||||
new("BY", "Беларусь", 5, "BYN", 20m),
|
||||
new("UZ", "Узбекистан", 6, "UZS", 12m),
|
||||
new("KG", "Кыргызстан", 7, "KGS", 12m),
|
||||
new("DE", "Германия", 10, "EUR", 19m),
|
||||
new("US", "США", 11, "USD", 0m),
|
||||
new("KR", "Южная Корея", 12, "KRW", 10m),
|
||||
new("IT", "Италия", 13, "EUR", 22m),
|
||||
new("PL", "Польша", 14, "PLN", 23m),
|
||||
new("KZ", "Казахстан", "KZT", 16m),
|
||||
new("RU", "Россия", "RUB", 20m),
|
||||
new("CN", "Китай", "CNY", 13m),
|
||||
new("TR", "Турция", "TRY", 18m),
|
||||
new("BY", "Беларусь", "BYN", 20m),
|
||||
new("UZ", "Узбекистан", "UZS", 12m),
|
||||
new("KG", "Кыргызстан", "KGS", 12m),
|
||||
new("DE", "Германия", "EUR", 19m),
|
||||
new("US", "США", "USD", 0m),
|
||||
new("KR", "Южная Корея", "KRW", 10m),
|
||||
new("IT", "Италия", "EUR", 22m),
|
||||
new("PL", "Польша", "PLN", 23m),
|
||||
};
|
||||
|
||||
private static readonly Currency[] CurrencySeeds =
|
||||
|
|
@ -83,7 +83,7 @@ private static async Task SeedCountriesAsync(AppDbContext db, CancellationToken
|
|||
{
|
||||
if (!existingCodes.Contains(s.Code))
|
||||
{
|
||||
db.Countries.Add(new Country { Code = s.Code, Name = s.Name, SortOrder = s.SortOrder, VatRate = s.VatRate });
|
||||
db.Countries.Add(new Country { Code = s.Code, Name = s.Name, VatRate = s.VatRate });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
namespace foodmarket.Application.Catalog;
|
||||
|
||||
public record CountryDto(
|
||||
Guid Id, string Code, string Name, int SortOrder,
|
||||
Guid Id, string Code, string Name,
|
||||
Guid? DefaultCurrencyId, string? DefaultCurrencyCode, string? DefaultCurrencySymbol,
|
||||
decimal VatRate);
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ public record ProductDto(
|
|||
|
||||
// Upsert payloads (input)
|
||||
public record CountryInput(
|
||||
string Code, string Name, int SortOrder = 0,
|
||||
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 UnitOfMeasureInput(string Code, string Name, string? Description = null, bool IsActive = true);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ public class Country : Entity
|
|||
{
|
||||
public string Code { get; set; } = null!; // ISO 3166-1 alpha-2, e.g. "KZ"
|
||||
public string Name { get; set; } = null!;
|
||||
public int SortOrder { get; set; }
|
||||
/// <summary>Валюта страны — при выборе страны в настройках организации
|
||||
/// она становится валютой по умолчанию для этой организации.</summary>
|
||||
public Guid? DefaultCurrencyId { get; set; }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace foodmarket.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <summary>Удаляет countries.SortOrder — порядок страны больше не редактируется руками,
|
||||
/// сортировка в UI и API идёт по Name ASC.</summary>
|
||||
public partial class Phase5b_DropCountrySortOrder : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder b)
|
||||
{
|
||||
b.DropColumn(name: "SortOrder", schema: "public", table: "countries");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder b)
|
||||
{
|
||||
b.AddColumn<int>(
|
||||
name: "SortOrder", schema: "public", table: "countries",
|
||||
type: "integer", nullable: false, defaultValue: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -442,9 +442,6 @@ protected override void BuildModel(ModelBuilder modelBuilder)
|
|||
.HasMaxLength(100)
|
||||
.HasColumnType("character varying(100)");
|
||||
|
||||
b.Property<int>("SortOrder")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime?>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const packagingLabel: Record<Packaging, string> = {
|
|||
}
|
||||
|
||||
export interface Country {
|
||||
id: string; code: string; name: string; sortOrder: number
|
||||
id: string; code: string; name: string
|
||||
defaultCurrencyId: string | null
|
||||
defaultCurrencyCode: string | null
|
||||
defaultCurrencySymbol: string | null
|
||||
|
|
|
|||
|
|
@ -17,12 +17,11 @@ interface Form {
|
|||
id?: string
|
||||
code: string
|
||||
name: string
|
||||
sortOrder: number
|
||||
defaultCurrencyId: string | null
|
||||
vatRate: number
|
||||
}
|
||||
|
||||
const blank: Form = { code: '', name: '', sortOrder: 0, defaultCurrencyId: null, vatRate: 0 }
|
||||
const blank: Form = { code: '', name: '', defaultCurrencyId: null, vatRate: 0 }
|
||||
|
||||
export function CountriesPage() {
|
||||
const { data, isLoading, page, setPage, search, setSearch } = useCatalogList<Country>(URL)
|
||||
|
|
@ -58,15 +57,14 @@ export function CountriesPage() {
|
|||
isLoading={isLoading}
|
||||
rowKey={(r) => r.id}
|
||||
onRowClick={(r) => setForm({
|
||||
id: r.id, code: r.code, name: r.name, sortOrder: r.sortOrder,
|
||||
id: r.id, code: r.code, name: r.name,
|
||||
defaultCurrencyId: r.defaultCurrencyId, vatRate: r.vatRate,
|
||||
})}
|
||||
columns={[
|
||||
{ header: 'Код', width: '90px', cell: (r) => <span className="font-mono">{r.code}</span> },
|
||||
{ header: 'Код', width: '80px', cell: (r) => <span className="font-mono">{r.code}</span> },
|
||||
{ header: 'Название', cell: (r) => r.name },
|
||||
{ header: 'Валюта', width: '130px', cell: (r) => r.defaultCurrencyCode ? `${r.defaultCurrencyCode} ${r.defaultCurrencySymbol ?? ''}` : '—' },
|
||||
{ header: 'Валюта', width: '120px', cell: (r) => r.defaultCurrencyCode ? `${r.defaultCurrencyCode} ${r.defaultCurrencySymbol ?? ''}` : '—' },
|
||||
{ header: 'НДС', width: '100px', className: 'text-right', cell: (r) => `${r.vatRate.toFixed(2)}%` },
|
||||
{ header: 'Порядок', width: '100px', className: 'text-right', cell: (r) => r.sortOrder },
|
||||
]}
|
||||
/>
|
||||
</ListPageShell>
|
||||
|
|
@ -94,14 +92,9 @@ export function CountriesPage() {
|
|||
>
|
||||
{form && (
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<Field label="ISO-код (2 буквы)">
|
||||
<TextInput value={form.code} maxLength={2} onChange={(e) => setForm({ ...form, code: e.target.value.toUpperCase() })} />
|
||||
</Field>
|
||||
<Field label="Порядок">
|
||||
<TextInput type="number" value={form.sortOrder} onChange={(e) => setForm({ ...form, sortOrder: Number(e.target.value) })} />
|
||||
</Field>
|
||||
</div>
|
||||
<Field label="ISO-код (2 буквы)">
|
||||
<TextInput value={form.code} maxLength={2} onChange={(e) => setForm({ ...form, code: e.target.value.toUpperCase() })} />
|
||||
</Field>
|
||||
<Field label="Название">
|
||||
<TextInput value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
|
||||
</Field>
|
||||
|
|
|
|||
Loading…
Reference in a new issue