From b7288bac1b6da183c2810c7b9e642220c375cea9 Mon Sep 17 00:00:00 2001 From: nns <278048682+nurdotnet@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:28:29 +0500 Subject: [PATCH] =?UTF-8?q?feat(supply):=20=C2=AB=D0=9F=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D0=B4=D0=B5=D0=BD=D0=BE=C2=BB=20=D0=B2=D0=BD=D1=83=D1=82?= =?UTF-8?q?=D1=80=D0=B8=20=D1=84=D0=BE=D1=80=D0=BC=D1=8B=20+=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=8F=D0=B7=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B0=D1=82=D0=B0=20=D0=B8=20=E2=89=A51=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B7=D0=B8=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI: - Чекбокс «Проведено» переехал из шапки в секцию «Реквизиты документа», чтобы было визуально как в сторонняя система. С хинтом «только проведённый документ влияет на остатки и себестоимость». - Поле «Дата» помечено как обязательное (звёздочка + required). - canSave требует form.lines.length > 0; пустое состояние секции «Позиции» теперь красное «должна быть хотя бы одна позиция». - onError приёмки достаёт сообщение из response.data.error. API: - Create/Update приёмки 400-ят без позиций («Приёмка должна содержать хотя бы одну позицию.»). - Post (проведение) уже валидирует это; теперь и на этапе сохранения. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Purchases/SuppliesController.cs | 4 ++ .../src/pages/SupplyEditPage.tsx | 61 +++++++++++-------- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/food-market.api/Controllers/Purchases/SuppliesController.cs b/src/food-market.api/Controllers/Purchases/SuppliesController.cs index f538a24..ffce2c2 100644 --- a/src/food-market.api/Controllers/Purchases/SuppliesController.cs +++ b/src/food-market.api/Controllers/Purchases/SuppliesController.cs @@ -123,6 +123,8 @@ public async Task> Get(Guid id, CancellationToken ct) [HttpPost, Authorize(Roles = "Admin,Manager,Storekeeper")] public async Task> Create([FromBody] SupplyInput input, CancellationToken ct) { + if (input.Lines is null || input.Lines.Count == 0) + return BadRequest(new { error = "Приёмка должна содержать хотя бы одну позицию." }); var number = await GenerateNumberAsync(input.Date, ct); var allowFractional = await _db.Organizations.Select(o => o.AllowFractionalPrices).FirstOrDefaultAsync(ct); var supply = new Supply @@ -164,6 +166,8 @@ public async Task> Create([FromBody] SupplyInput input, [HttpPut("{id:guid}"), Authorize(Roles = "Admin,Manager,Storekeeper")] public async Task Update(Guid id, [FromBody] SupplyInput input, CancellationToken ct) { + if (input.Lines is null || input.Lines.Count == 0) + return BadRequest(new { error = "Приёмка должна содержать хотя бы одну позицию." }); var supply = await _db.Supplies.Include(s => s.Lines).FirstOrDefaultAsync(s => s.Id == id, ct); if (supply is null) return NotFound(); if (supply.Status != SupplyStatus.Draft) diff --git a/src/food-market.web/src/pages/SupplyEditPage.tsx b/src/food-market.web/src/pages/SupplyEditPage.tsx index 2210830..563a21b 100644 --- a/src/food-market.web/src/pages/SupplyEditPage.tsx +++ b/src/food-market.web/src/pages/SupplyEditPage.tsx @@ -4,7 +4,7 @@ import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query' import { ArrowLeft, Plus, Trash2, Save, CheckCircle } from 'lucide-react' import { api } from '@/lib/api' import { Button } from '@/components/Button' -import { Field, TextInput, TextArea, Select, MoneyInput, NumberInput } from '@/components/Field' +import { Field, TextInput, TextArea, Select, Checkbox, MoneyInput, NumberInput } from '@/components/Field' import { ProductPicker } from '@/components/ProductPicker' import { useStores, useCurrencies, useSuppliers } from '@/lib/useLookups' import { useOrgSettings } from '@/lib/useOrgSettings' @@ -162,7 +162,10 @@ export function SupplyEditPage() { qc.invalidateQueries({ queryKey: ['/api/inventory/movements'] }) existing.refetch() }, - onError: (e: Error) => setError(e.message), + onError: (e: Error) => { + const msg = (e as { response?: { data?: { error?: string } } }).response?.data?.error ?? e.message + setError(msg) + }, }) const remove = useMutation({ @@ -195,7 +198,9 @@ export function SupplyEditPage() { const removeLine = (i: number) => setForm({ ...form, lines: form.lines.filter((_, ix) => ix !== i) }) - const canSave = !!form.supplierId && !!form.storeId && !!form.currencyId && isDraft + const canSave = !!form.date && !!form.supplierId && !!form.storeId && !!form.currencyId + && form.lines.length > 0 + && isDraft return (
@@ -217,25 +222,6 @@ export function SupplyEditPage() {
- {!isNew && ( - - )} {isDraft && !isNew && (