ui(products-list): убрать фильтр «Со штрихкодом», добавить «Закупочная цена от/до»
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 29s
CI / Web (React + Vite) (push) Successful in 25s
Docker Images / API image (push) Successful in 42s
Docker Images / Web image (push) Successful in 26s
Docker Images / Deploy stage (push) Successful in 13s
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 29s
CI / Web (React + Vite) (push) Successful in 25s
Docker Images / API image (push) Successful in 42s
Docker Images / Web image (push) Successful in 26s
Docker Images / Deploy stage (push) Successful in 13s
Поскольку штрихкод теперь обязательный (минимум 1 у каждого товара), фильтр «Со штрихкодом» бессмыслен — убран из UI и контроллера. Вместо него — два MoneyInput «Закупочная цена от/до» в панели фильтров. Использует символ валюты по умолчанию из настроек организации и уважает AllowFractionalPrices. Backend: ProductsController.List принимает purchasePriceFrom / purchasePriceTo (decimal?), применяет ≥ / ≤ к PurchasePrice; параметр hasBarcode удалён. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a49db1c90d
commit
6b3491056b
|
|
@ -79,7 +79,8 @@ private async Task<decimal> ResolveDefaultVatAsync(CancellationToken ct)
|
|||
[FromQuery] Packaging? packaging,
|
||||
[FromQuery] bool? isMarked,
|
||||
[FromQuery] bool? isActive,
|
||||
[FromQuery] bool? hasBarcode,
|
||||
[FromQuery] decimal? purchasePriceFrom,
|
||||
[FromQuery] decimal? purchasePriceTo,
|
||||
CancellationToken ct)
|
||||
{
|
||||
var q = QueryIncludes().AsNoTracking();
|
||||
|
|
@ -102,8 +103,8 @@ private async Task<decimal> ResolveDefaultVatAsync(CancellationToken ct)
|
|||
if (packaging is not null) q = q.Where(p => p.Packaging == packaging);
|
||||
if (isMarked is not null) q = q.Where(p => p.IsMarked == isMarked);
|
||||
if (isActive is not null) q = q.Where(p => p.IsActive == isActive);
|
||||
if (hasBarcode is not null)
|
||||
q = hasBarcode == true ? q.Where(p => p.Barcodes.Any()) : q.Where(p => !p.Barcodes.Any());
|
||||
if (purchasePriceFrom is not null) q = q.Where(p => p.PurchasePrice >= purchasePriceFrom);
|
||||
if (purchasePriceTo is not null) q = q.Where(p => p.PurchasePrice <= purchasePriceTo);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { Plus, Filter, X, FolderTree } from 'lucide-react'
|
|||
import { useCatalogList } from '@/lib/useCatalog'
|
||||
import { useOrgSettings } from '@/lib/useOrgSettings'
|
||||
import { ProductGroupTree } from '@/components/ProductGroupTree'
|
||||
import { MoneyInput } from '@/components/Field'
|
||||
import { packagingLabel, type Product } from '@/lib/types'
|
||||
|
||||
const URL = '/api/catalog/products'
|
||||
|
|
@ -20,7 +21,8 @@ interface Filters {
|
|||
isService: TriFilter
|
||||
packaging: number | null // null = все, 1/2/3 = Piece/Weight/Liquid
|
||||
isMarked: TriFilter
|
||||
hasBarcode: TriFilter
|
||||
purchasePriceFrom: number | null
|
||||
purchasePriceTo: number | null
|
||||
}
|
||||
|
||||
const defaultFilters: Filters = {
|
||||
|
|
@ -29,7 +31,8 @@ const defaultFilters: Filters = {
|
|||
isService: 'all',
|
||||
packaging: null,
|
||||
isMarked: 'all',
|
||||
hasBarcode: 'all',
|
||||
purchasePriceFrom: null,
|
||||
purchasePriceTo: null,
|
||||
}
|
||||
|
||||
const toExtra = (f: Filters): Record<string, string | number | boolean | undefined> => {
|
||||
|
|
@ -39,7 +42,8 @@ const toExtra = (f: Filters): Record<string, string | number | boolean | undefin
|
|||
if (f.isService !== 'all') e.isService = f.isService === 'yes'
|
||||
if (f.packaging) e.packaging = f.packaging
|
||||
if (f.isMarked !== 'all') e.isMarked = f.isMarked === 'yes'
|
||||
if (f.hasBarcode !== 'all') e.hasBarcode = f.hasBarcode === 'yes'
|
||||
if (f.purchasePriceFrom != null) e.purchasePriceFrom = f.purchasePriceFrom
|
||||
if (f.purchasePriceTo != null) e.purchasePriceTo = f.purchasePriceTo
|
||||
return e
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +54,8 @@ const activeFilterCount = (f: Filters) => {
|
|||
if (f.isService !== 'all') n++
|
||||
if (f.packaging) n++
|
||||
if (f.isMarked !== 'all') n++
|
||||
if (f.hasBarcode !== 'all') n++
|
||||
if (f.purchasePriceFrom != null) n++
|
||||
if (f.purchasePriceTo != null) n++
|
||||
return n
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +222,29 @@ export function ProductsPage() {
|
|||
{showMarked && (
|
||||
<Tri label="Маркируемый" value={filters.isMarked} onChange={(v) => { setFilters({ ...filters, isMarked: v }); setPage(1) }} />
|
||||
)}
|
||||
<Tri label="Со штрихкодом" value={filters.hasBarcode} onChange={(v) => { setFilters({ ...filters, hasBarcode: v }); setPage(1) }} yesLabel="есть" noLabel="нет" />
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<span className="text-slate-500">Закупочная цена</span>
|
||||
<div className="w-32">
|
||||
<MoneyInput
|
||||
value={filters.purchasePriceFrom}
|
||||
onChange={(n) => { setFilters({ ...filters, purchasePriceFrom: n }); setPage(1) }}
|
||||
currencyCode={org.data?.defaultCurrencyCode ?? undefined}
|
||||
currencySymbol={org.data?.defaultCurrencySymbol ?? undefined}
|
||||
allowFractional={org.data?.allowFractionalPrices ?? false}
|
||||
placeholder="от"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-32">
|
||||
<MoneyInput
|
||||
value={filters.purchasePriceTo}
|
||||
onChange={(n) => { setFilters({ ...filters, purchasePriceTo: n }); setPage(1) }}
|
||||
currencyCode={org.data?.defaultCurrencyCode ?? undefined}
|
||||
currencySymbol={org.data?.defaultCurrencySymbol ?? undefined}
|
||||
allowFractional={org.data?.allowFractionalPrices ?? false}
|
||||
placeholder="до"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{activeCount > 0 && (
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
Loading…
Reference in a new issue