Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 26s
CI / Web (React + Vite) (push) Successful in 22s
Docker Images / API image (push) Successful in 38s
Docker Images / Web image (push) Successful in 26s
Docker Images / Deploy stage (push) Successful in 18s
Во всех таблицах можно сортировать по клику на заголовок столбца:
первый клик — по возрастанию (↑), второй — по убыванию (↓),
смена колонки сбрасывает предыдущую. Без активной сортировки —
серверный default (обычно по Name ASC).
Реализация:
- PagedRequest: добавлены Sort (ключ колонки) и Order ("asc"/"desc"),
плюс удобное свойство Desc.
- DataTable: Column.sortKey + props sortKey/sortOrder/onSortChange,
в заголовке появляется иконка (ArrowUpDown/ArrowUp/ArrowDown).
- useCatalogList: хранит sortKey/sortOrder, отдаёт setSort, шлёт
?sort=&order= в query-string.
- Все 10 List-эндпоинтов (Countries, Currencies, UnitsOfMeasure,
PriceTypes, Stores, RetailPoints, Counterparties, ProductGroups,
Products, Supplies, RetailSales + Stock/Movements) принимают
параметры и применяют switch-based OrderBy по whitelisted ключам.
- Все страницы со списками прокидывают sort state и sortKey на
колонках, где сортировка имеет смысл (тексты/числа/даты).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
86 lines
3.3 KiB
C#
86 lines
3.3 KiB
C#
using foodmarket.Application.Catalog;
|
|
using foodmarket.Application.Common;
|
|
using foodmarket.Domain.Catalog;
|
|
using foodmarket.Infrastructure.Persistence;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace foodmarket.Api.Controllers.Catalog;
|
|
|
|
[ApiController]
|
|
[Authorize]
|
|
[Route("api/catalog/currencies")]
|
|
public class CurrenciesController : ControllerBase
|
|
{
|
|
private readonly AppDbContext _db;
|
|
|
|
public CurrenciesController(AppDbContext db) => _db = db;
|
|
|
|
[HttpGet]
|
|
public async Task<ActionResult<PagedResult<CurrencyDto>>> List([FromQuery] PagedRequest req, CancellationToken ct)
|
|
{
|
|
var q = _db.Currencies.AsNoTracking().AsQueryable();
|
|
if (!string.IsNullOrWhiteSpace(req.Search))
|
|
{
|
|
var s = req.Search.Trim().ToLower();
|
|
q = q.Where(c => c.Name.ToLower().Contains(s) || c.Code.ToLower().Contains(s));
|
|
}
|
|
var total = await q.CountAsync(ct);
|
|
q = (req.Sort, req.Desc) switch
|
|
{
|
|
("name", false) => q.OrderBy(c => c.Name),
|
|
("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))
|
|
.ToListAsync(ct);
|
|
return new PagedResult<CurrencyDto> { Items = items, Total = total, Page = req.Page, PageSize = req.Take };
|
|
}
|
|
|
|
[HttpGet("{id:guid}")]
|
|
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);
|
|
}
|
|
|
|
[HttpPost, Authorize(Roles = "SuperAdmin")]
|
|
public async Task<ActionResult<CurrencyDto>> Create([FromBody] CurrencyInput input, CancellationToken ct)
|
|
{
|
|
var e = new Currency
|
|
{
|
|
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));
|
|
}
|
|
|
|
[HttpPut("{id:guid}"), Authorize(Roles = "SuperAdmin")]
|
|
public async Task<IActionResult> Update(Guid id, [FromBody] CurrencyInput input, CancellationToken ct)
|
|
{
|
|
var e = await _db.Currencies.FirstOrDefaultAsync(x => x.Id == id, ct);
|
|
if (e is null) return NotFound();
|
|
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();
|
|
}
|
|
}
|