fix(supply-lines): show both article and barcode in line subtitle
Some checks are pending
CI / POS (WPF, Windows) (push) Waiting to run
CI / Backend (.NET 8) (push) Successful in 42s
CI / Web (React + Vite) (push) Successful in 35s
Docker API / Build + push API (push) Successful in 44s
Docker Web / Build + push Web (push) Successful in 26s
Docker API / Deploy API on stage (push) Successful in 17s
Docker Web / Deploy Web on stage (push) Successful in 11s

В таблице позиций приёмки под названием товара теперь выводится
и артикул, и основной штрихкод сразу — раньше показывалось что-то
одно (артикул или ничего, без штрихкода).

Формат: «Арт: 17933 · ШК: 4870144022958». Если только одно из двух
— префикс соответствующий, без точки-разделителя. Если ни того ни
другого — subtitle не рендерится. Шрифт мелкий моно серый.

API:
- SupplyLineDto расширен полем ProductBarcode (основной по
  IsPrimary, иначе первый по порядку).
- В проекции GetInternal штрихкод подтягивается через
  p.Barcodes.OrderByDescending(IsPrimary).Select(Code).First().

Frontend:
- types.ts.SupplyLineDto, LineRow в SupplyEditPage и AddedProduct
  в SupplyLineQuickAdd получили поле productBarcode/barcode.
- При добавлении строки через ProductPicker, sticky-input или
  quick-create — primary barcode достаётся из p.barcodes одинаковой
  логикой (sort by IsPrimary desc, [0]).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
nns 2026-04-26 03:03:43 +05:00
parent f9a17ad5c2
commit 290a95c54c
4 changed files with 25 additions and 3 deletions

View file

@ -34,6 +34,7 @@ public record SupplyListRow(
public record SupplyLineDto( public record SupplyLineDto(
Guid? Id, Guid ProductId, string? ProductName, string? ProductArticle, Guid? Id, Guid ProductId, string? ProductName, string? ProductArticle,
string? ProductBarcode,
string? UnitSymbol, string? UnitSymbol,
decimal Quantity, decimal UnitPrice, decimal LineTotal, int SortOrder, decimal Quantity, decimal UnitPrice, decimal LineTotal, int SortOrder,
bool RetailPriceManuallyOverridden, decimal? RetailPriceOverride, bool RetailPriceManuallyOverridden, decimal? RetailPriceOverride,
@ -384,7 +385,10 @@ private async Task<string> GenerateNumberAsync(DateTime date, CancellationToken
where l.SupplyId == id where l.SupplyId == id
orderby l.SortOrder orderby l.SortOrder
select new SupplyLineDto( select new SupplyLineDto(
l.Id, l.ProductId, p.Name, p.Article, u.Name, l.Id, l.ProductId, p.Name, p.Article,
// Основной штрихкод (IsPrimary=true), иначе первый по порядку.
p.Barcodes.OrderByDescending(b => b.IsPrimary).Select(b => b.Code).FirstOrDefault(),
u.Name,
l.Quantity, l.UnitPrice, l.LineTotal, l.SortOrder, l.Quantity, l.UnitPrice, l.LineTotal, l.SortOrder,
l.RetailPriceManuallyOverridden, l.RetailPriceOverride, l.RetailPriceManuallyOverridden, l.RetailPriceOverride,
p.Prices p.Prices

View file

@ -18,6 +18,8 @@ export interface AddedProduct {
id: string id: string
name: string name: string
article: string | null article: string | null
/** Основной штрихкод (IsPrimary), либо первый по порядку. */
barcode: string | null
referencePrice: number | null referencePrice: number | null
unitName: string | null unitName: string | null
cost: number | null cost: number | null
@ -162,10 +164,12 @@ export function SupplyLineQuickAdd({ storeId, disabled, onPick }: Props) {
refocus() refocus()
try { try {
const full = (await api.get<Product>(`/api/catalog/products/${id}`)).data const full = (await api.get<Product>(`/api/catalog/products/${id}`)).data
const primaryBarcode = (full.barcodes ?? []).slice().sort((a, b) => Number(b.isPrimary) - Number(a.isPrimary))[0]?.code ?? null
const incremented = onPick({ const incremented = onPick({
id: full.id, id: full.id,
name: full.name, name: full.name,
article: full.article, article: full.article,
barcode: primaryBarcode,
referencePrice: full.referencePrice, referencePrice: full.referencePrice,
unitName: full.unitName, unitName: full.unitName,
cost: full.cost, cost: full.cost,
@ -240,10 +244,12 @@ export function SupplyLineQuickAdd({ storeId, disabled, onPick }: Props) {
} }
const onCreated = async (p: Product) => { const onCreated = async (p: Product) => {
const primaryBarcode = (p.barcodes ?? []).slice().sort((a, b) => Number(b.isPrimary) - Number(a.isPrimary))[0]?.code ?? null
onPick({ onPick({
id: p.id, id: p.id,
name: p.name, name: p.name,
article: p.article, article: p.article,
barcode: primaryBarcode,
referencePrice: p.referencePrice, referencePrice: p.referencePrice,
unitName: p.unitName, unitName: p.unitName,
cost: p.cost, cost: p.cost,

View file

@ -92,7 +92,8 @@ export interface SupplyListRow {
export interface SupplyLineDto { export interface SupplyLineDto {
id: string | null; productId: string; id: string | null; productId: string;
productName: string | null; productArticle: string | null; unitName: string | null; productName: string | null; productArticle: string | null; productBarcode: string | null;
unitName: string | null;
quantity: number; unitPrice: number; lineTotal: number; sortOrder: number; quantity: number; unitPrice: number; lineTotal: number; sortOrder: number;
retailPriceManuallyOverridden: boolean; retailPriceOverride: number | null; retailPriceManuallyOverridden: boolean; retailPriceOverride: number | null;
currentRetailPrice: number | null; currentRetailPrice: number | null;

View file

@ -16,6 +16,7 @@ interface LineRow {
productId: string productId: string
productName: string productName: string
productArticle: string | null productArticle: string | null
productBarcode: string | null
unitName: string | null unitName: string | null
quantity: number quantity: number
unitPrice: number unitPrice: number
@ -96,6 +97,7 @@ export function SupplyEditPage() {
productId: l.productId, productId: l.productId,
productName: l.productName ?? '', productName: l.productName ?? '',
productArticle: l.productArticle, productArticle: l.productArticle,
productBarcode: l.productBarcode,
unitName: l.unitName, unitName: l.unitName,
quantity: l.quantity, quantity: l.quantity,
unitPrice: l.unitPrice, unitPrice: l.unitPrice,
@ -196,12 +198,14 @@ export function SupplyEditPage() {
const addLineFromProduct = (p: Product) => { const addLineFromProduct = (p: Product) => {
const defaultRetail = p.prices?.[0]?.amount ?? null const defaultRetail = p.prices?.[0]?.amount ?? null
const primaryBarcode = (p.barcodes ?? []).slice().sort((a, b) => Number(b.isPrimary) - Number(a.isPrimary))[0]?.code ?? null
setForm({ setForm({
...form, ...form,
lines: [...form.lines, { lines: [...form.lines, {
productId: p.id, productId: p.id,
productName: p.name, productName: p.name,
productArticle: p.article, productArticle: p.article,
productBarcode: primaryBarcode,
unitName: p.unitName, unitName: p.unitName,
quantity: 1, quantity: 1,
unitPrice: p.referencePrice ?? p.cost ?? 0, unitPrice: p.referencePrice ?? p.cost ?? 0,
@ -231,6 +235,7 @@ export function SupplyEditPage() {
productId: p.id, productId: p.id,
productName: p.name, productName: p.name,
productArticle: p.article, productArticle: p.article,
productBarcode: p.barcode,
unitName: p.unitName, unitName: p.unitName,
quantity: 1, quantity: 1,
unitPrice: p.referencePrice ?? p.cost ?? 0, unitPrice: p.referencePrice ?? p.cost ?? 0,
@ -399,7 +404,13 @@ export function SupplyEditPage() {
<tr key={i} className="border-b border-slate-100 dark:border-slate-800"> <tr key={i} className="border-b border-slate-100 dark:border-slate-800">
<td className="py-2 pr-3"> <td className="py-2 pr-3">
<div className="font-medium">{l.productName}</div> <div className="font-medium">{l.productName}</div>
{l.productArticle && <div className="text-xs text-slate-400 font-mono">{l.productArticle}</div>} {(l.productArticle || l.productBarcode) && (
<div className="text-xs text-slate-400 font-mono">
{l.productArticle && <span>Арт: {l.productArticle}</span>}
{l.productArticle && l.productBarcode && <span className="mx-1.5">·</span>}
{l.productBarcode && <span>ШК: {l.productBarcode}</span>}
</div>
)}
</td> </td>
<td className="py-2 px-3 text-slate-500">{l.unitName}</td> <td className="py-2 px-3 text-slate-500">{l.unitName}</td>
<td className="py-2 px-3"> <td className="py-2 px-3">