diff --git a/src/food-market.api/Controllers/Catalog/ProductsController.cs b/src/food-market.api/Controllers/Catalog/ProductsController.cs index be95457..1248014 100644 --- a/src/food-market.api/Controllers/Catalog/ProductsController.cs +++ b/src/food-market.api/Controllers/Catalog/ProductsController.cs @@ -172,6 +172,8 @@ private async Task ResolveDefaultVatAsync(CancellationToken ct) ("packaging", true) => q.OrderByDescending(p => p.Packaging).ThenBy(p => p.Name), ("purchasePrice", false) => q.OrderBy(p => p.ReferencePrice).ThenBy(p => p.Name), ("purchasePrice", true) => q.OrderByDescending(p => p.ReferencePrice).ThenBy(p => p.Name), + ("cost", false) => q.OrderBy(p => p.Cost).ThenBy(p => p.Name), + ("cost", true) => q.OrderByDescending(p => p.Cost).ThenBy(p => p.Name), ("systemPrice", false) => q.OrderBy(p => p.Prices.Where(pr => pr.PriceType!.IsSystem).Select(pr => (decimal?)pr.Amount).FirstOrDefault()).ThenBy(p => p.Name), ("systemPrice", true) => q.OrderByDescending(p => p.Prices.Where(pr => pr.PriceType!.IsSystem).Select(pr => (decimal?)pr.Amount).FirstOrDefault()).ThenBy(p => p.Name), ("vat", false) => q.OrderBy(p => p.Vat).ThenBy(p => p.Name), diff --git a/src/food-market.web/src/pages/ProductEditPage.tsx b/src/food-market.web/src/pages/ProductEditPage.tsx index f65a0b9..b8c1dae 100644 --- a/src/food-market.web/src/pages/ProductEditPage.tsx +++ b/src/food-market.web/src/pages/ProductEditPage.tsx @@ -6,7 +6,7 @@ import { api } from '@/lib/api' import { Button } from '@/components/Button' import { Field, TextInput, TextArea, Select, Checkbox, MoneyInput, NumberInput } from '@/components/Field' import { - useUnits, useProductGroups, useCountries, useCurrencies, usePriceTypes, useSuppliers, + useUnits, useProductGroups, useCountries, useCurrencies, usePriceTypes, } from '@/lib/useLookups' import { useOrgSettings } from '@/lib/useOrgSettings' import { BarcodeType, Packaging, type Product } from '@/lib/types' @@ -24,7 +24,6 @@ interface Form { vat: number vatEnabled: boolean productGroupId: string - defaultSupplierId: string countryOfOriginId: string isService: boolean packaging: Packaging @@ -42,7 +41,7 @@ interface Form { const emptyForm: Form = { name: '', article: '', description: '', unitOfMeasureId: '', vat: 16, vatEnabled: true, - productGroupId: '', defaultSupplierId: '', countryOfOriginId: '', + productGroupId: '', countryOfOriginId: '', isService: false, packaging: Packaging.Piece, isMarked: false, minStock: '', maxStock: '', referencePrice: '', purchaseCurrencyId: '', @@ -63,7 +62,6 @@ export function ProductEditPage() { const currencies = useCurrencies() const priceTypes = usePriceTypes() const org = useOrgSettings() - const suppliers = useSuppliers() const existing = useQuery({ queryKey: ['/api/catalog/products', id], @@ -80,7 +78,7 @@ export function ProductEditPage() { setForm({ name: p.name, article: p.article ?? '', description: p.description ?? '', unitOfMeasureId: p.unitOfMeasureId, vat: p.vat, vatEnabled: p.vatEnabled, - productGroupId: p.productGroupId ?? '', defaultSupplierId: p.defaultSupplierId ?? '', + productGroupId: p.productGroupId ?? '', countryOfOriginId: p.countryOfOriginId ?? '', isService: p.isService, packaging: p.packaging, isMarked: p.isMarked, @@ -145,7 +143,7 @@ export function ProductEditPage() { vat: org.data?.showVatEnabledOnProduct ? form.vat : null, vatEnabled: form.vatEnabled, productGroupId: form.productGroupId || null, - defaultSupplierId: form.defaultSupplierId || null, + defaultSupplierId: null, countryOfOriginId: form.countryOfOriginId || null, isService: form.isService, packaging: form.packaging, @@ -332,65 +330,6 @@ export function ProductEditPage() { )} -
- - - - - - - - - - - - - - - - {org.data?.showCountryOfOriginOnProduct && ( - - - - )} - - {org.data?.showVatEnabledOnProduct && form.vatEnabled && ( - - - setForm({ ...form, vat: n ?? 0 })} - /> - - - )} -
- {org.data?.showVatEnabledOnProduct && ( - setForm({ ...form, vatEnabled: v })} /> - )} - {org.data?.showServiceOnProduct && ( - setForm({ ...form, isService: v })} /> - )} - {org.data?.showMarkedOnProduct && ( - setForm({ ...form, isMarked: v })} /> - )} -
-
-
+
+ + + + + + + + + + + + {org.data?.showCountryOfOriginOnProduct && ( + + + + + + )} + {org.data?.showVatEnabledOnProduct && form.vatEnabled && ( + + + setForm({ ...form, vat: n ?? 0 })} + /> + + + )} +
+ {org.data?.showVatEnabledOnProduct && ( + setForm({ ...form, vatEnabled: v })} /> + )} + {org.data?.showServiceOnProduct && ( + setForm({ ...form, isService: v })} /> + )} + {org.data?.showMarkedOnProduct && ( + setForm({ ...form, isMarked: v })} /> + )} +
+
+ {org.data?.showMinMaxStock && ( diff --git a/src/food-market.web/src/pages/ProductsPage.tsx b/src/food-market.web/src/pages/ProductsPage.tsx index 9b84bb0..72d8814 100644 --- a/src/food-market.web/src/pages/ProductsPage.tsx +++ b/src/food-market.web/src/pages/ProductsPage.tsx @@ -126,6 +126,24 @@ export function ProductsPage() { { header: 'Штрихкод', width: '160px', cell: (r) => ( {r.barcodes[0]?.code ?? '—'} )}, + // Себестоимость — расчётное скользящее среднее. До первой проведённой + // приёмки = 0; в этом случае показываем «—», чтобы визуально отличать + // «не накопилась» от «реально стоит 0». + { + header: 'Себестоимость', + width: '160px', + className: 'text-right font-mono', + sortKey: 'cost', + cell: (r) => { + if (r.cost == null || r.cost === 0) return '—' + const fractional = org.data?.allowFractionalPrices ?? false + const num = r.cost.toLocaleString('ru', + fractional + ? { minimumFractionDigits: 2, maximumFractionDigits: 2 } + : { maximumFractionDigits: 0 }) + return `${num} ${org.data?.defaultCurrencySymbol ?? org.data?.defaultCurrencyCode ?? ''}`.trim() + }, + }, // Колонка системной розничной цены: заголовок = PriceType.Name той записи // что помечена IsSystem (если пользователь её переименовал — заголовок меняется). {