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] Packaging? packaging,
|
||||||
[FromQuery] bool? isMarked,
|
[FromQuery] bool? isMarked,
|
||||||
[FromQuery] bool? isActive,
|
[FromQuery] bool? isActive,
|
||||||
[FromQuery] bool? hasBarcode,
|
[FromQuery] decimal? purchasePriceFrom,
|
||||||
|
[FromQuery] decimal? purchasePriceTo,
|
||||||
CancellationToken ct)
|
CancellationToken ct)
|
||||||
{
|
{
|
||||||
var q = QueryIncludes().AsNoTracking();
|
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 (packaging is not null) q = q.Where(p => p.Packaging == packaging);
|
||||||
if (isMarked is not null) q = q.Where(p => p.IsMarked == isMarked);
|
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 (isActive is not null) q = q.Where(p => p.IsActive == isActive);
|
||||||
if (hasBarcode is not null)
|
if (purchasePriceFrom is not null) q = q.Where(p => p.PurchasePrice >= purchasePriceFrom);
|
||||||
q = hasBarcode == true ? q.Where(p => p.Barcodes.Any()) : q.Where(p => !p.Barcodes.Any());
|
if (purchasePriceTo is not null) q = q.Where(p => p.PurchasePrice <= purchasePriceTo);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(req.Search))
|
if (!string.IsNullOrWhiteSpace(req.Search))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { Plus, Filter, X, FolderTree } from 'lucide-react'
|
||||||
import { useCatalogList } from '@/lib/useCatalog'
|
import { useCatalogList } from '@/lib/useCatalog'
|
||||||
import { useOrgSettings } from '@/lib/useOrgSettings'
|
import { useOrgSettings } from '@/lib/useOrgSettings'
|
||||||
import { ProductGroupTree } from '@/components/ProductGroupTree'
|
import { ProductGroupTree } from '@/components/ProductGroupTree'
|
||||||
|
import { MoneyInput } from '@/components/Field'
|
||||||
import { packagingLabel, type Product } from '@/lib/types'
|
import { packagingLabel, type Product } from '@/lib/types'
|
||||||
|
|
||||||
const URL = '/api/catalog/products'
|
const URL = '/api/catalog/products'
|
||||||
|
|
@ -20,7 +21,8 @@ interface Filters {
|
||||||
isService: TriFilter
|
isService: TriFilter
|
||||||
packaging: number | null // null = все, 1/2/3 = Piece/Weight/Liquid
|
packaging: number | null // null = все, 1/2/3 = Piece/Weight/Liquid
|
||||||
isMarked: TriFilter
|
isMarked: TriFilter
|
||||||
hasBarcode: TriFilter
|
purchasePriceFrom: number | null
|
||||||
|
purchasePriceTo: number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultFilters: Filters = {
|
const defaultFilters: Filters = {
|
||||||
|
|
@ -29,7 +31,8 @@ const defaultFilters: Filters = {
|
||||||
isService: 'all',
|
isService: 'all',
|
||||||
packaging: null,
|
packaging: null,
|
||||||
isMarked: 'all',
|
isMarked: 'all',
|
||||||
hasBarcode: 'all',
|
purchasePriceFrom: null,
|
||||||
|
purchasePriceTo: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
const toExtra = (f: Filters): Record<string, string | number | boolean | undefined> => {
|
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.isService !== 'all') e.isService = f.isService === 'yes'
|
||||||
if (f.packaging) e.packaging = f.packaging
|
if (f.packaging) e.packaging = f.packaging
|
||||||
if (f.isMarked !== 'all') e.isMarked = f.isMarked === 'yes'
|
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
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +54,8 @@ const activeFilterCount = (f: Filters) => {
|
||||||
if (f.isService !== 'all') n++
|
if (f.isService !== 'all') n++
|
||||||
if (f.packaging) n++
|
if (f.packaging) n++
|
||||||
if (f.isMarked !== 'all') n++
|
if (f.isMarked !== 'all') n++
|
||||||
if (f.hasBarcode !== 'all') n++
|
if (f.purchasePriceFrom != null) n++
|
||||||
|
if (f.purchasePriceTo != null) n++
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,7 +222,29 @@ export function ProductsPage() {
|
||||||
{showMarked && (
|
{showMarked && (
|
||||||
<Tri label="Маркируемый" value={filters.isMarked} onChange={(v) => { setFilters({ ...filters, isMarked: v }); setPage(1) }} />
|
<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 && (
|
{activeCount > 0 && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue