feat(web): supply line retail override column
Some checks are pending
Some checks are pending
В таблице строк приёмки добавлена колонка «Розничная (карточка)». - Значение по умолчанию — текущая дефолтная розничная цена товара (берётся из ProductDto.prices при подборе или из SupplyLineDto. currentRetailPrice при загрузке существующего документа). - Любая ручная правка ставит retailPriceManuallyOverridden=true и записывает retailPriceOverride. При проведении документа этот override применяется к Product.Prices[default] вместо автонаценки. - В payload PUT/POST шлём retailPriceManuallyOverridden и retailPriceOverride (либо null если override снят). - types.SupplyLineDto расширен полями currentRetailPrice / retailPriceManuallyOverridden / retailPriceOverride. - В addLineFromProduct unitPrice fallbacks теперь учитывают cost. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
095ac04d31
commit
23561fca2e
|
|
@ -94,6 +94,8 @@ export interface SupplyLineDto {
|
|||
id: string | null; productId: string;
|
||||
productName: string | null; productArticle: string | null; unitName: string | null;
|
||||
quantity: number; unitPrice: number; lineTotal: number; sortOrder: number;
|
||||
retailPriceManuallyOverridden: boolean; retailPriceOverride: number | null;
|
||||
currentRetailPrice: number | null;
|
||||
}
|
||||
|
||||
export interface SupplyDto {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ interface LineRow {
|
|||
unitName: string | null
|
||||
quantity: number
|
||||
unitPrice: number
|
||||
// Розничная цена с карточки товара на момент загрузки документа
|
||||
// (read-only baseline). Используется как placeholder для ручного override.
|
||||
currentRetailPrice: number | null
|
||||
retailPriceManuallyOverridden: boolean
|
||||
retailPriceOverride: number | null
|
||||
}
|
||||
|
||||
interface Form {
|
||||
|
|
@ -81,6 +86,9 @@ export function SupplyEditPage() {
|
|||
unitName: l.unitName,
|
||||
quantity: l.quantity,
|
||||
unitPrice: l.unitPrice,
|
||||
currentRetailPrice: l.currentRetailPrice ?? null,
|
||||
retailPriceManuallyOverridden: l.retailPriceManuallyOverridden ?? false,
|
||||
retailPriceOverride: l.retailPriceOverride ?? null,
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
|
@ -121,7 +129,13 @@ export function SupplyEditPage() {
|
|||
supplierInvoiceNumber: form.supplierInvoiceNumber || null,
|
||||
supplierInvoiceDate: form.supplierInvoiceDate ? new Date(form.supplierInvoiceDate).toISOString() : null,
|
||||
notes: form.notes || null,
|
||||
lines: form.lines.map((l) => ({ productId: l.productId, quantity: l.quantity, unitPrice: l.unitPrice })),
|
||||
lines: form.lines.map((l) => ({
|
||||
productId: l.productId,
|
||||
quantity: l.quantity,
|
||||
unitPrice: l.unitPrice,
|
||||
retailPriceManuallyOverridden: l.retailPriceManuallyOverridden,
|
||||
retailPriceOverride: l.retailPriceManuallyOverridden ? l.retailPriceOverride : null,
|
||||
})),
|
||||
}
|
||||
if (isNew) {
|
||||
return (await api.post<SupplyDto>('/api/purchases/supplies', payload)).data
|
||||
|
|
@ -167,6 +181,7 @@ export function SupplyEditPage() {
|
|||
const onSubmit = (e: FormEvent) => { e.preventDefault(); save.mutate() }
|
||||
|
||||
const addLineFromProduct = (p: Product) => {
|
||||
const defaultRetail = p.prices?.[0]?.amount ?? null
|
||||
setForm({
|
||||
...form,
|
||||
lines: [...form.lines, {
|
||||
|
|
@ -175,7 +190,10 @@ export function SupplyEditPage() {
|
|||
productArticle: p.article,
|
||||
unitName: p.unitName,
|
||||
quantity: 1,
|
||||
unitPrice: p.referencePrice ?? 0,
|
||||
unitPrice: p.referencePrice ?? p.cost ?? 0,
|
||||
currentRetailPrice: defaultRetail,
|
||||
retailPriceManuallyOverridden: false,
|
||||
retailPriceOverride: null,
|
||||
}],
|
||||
})
|
||||
}
|
||||
|
|
@ -299,6 +317,7 @@ export function SupplyEditPage() {
|
|||
<th className="py-2 px-3 font-medium text-xs uppercase tracking-wide text-slate-500 w-[90px]">Ед.</th>
|
||||
<th className="py-2 px-3 font-medium text-xs uppercase tracking-wide text-slate-500 w-[140px] text-right">Количество</th>
|
||||
<th className="py-2 px-3 font-medium text-xs uppercase tracking-wide text-slate-500 w-[140px] text-right">Цена</th>
|
||||
<th className="py-2 px-3 font-medium text-xs uppercase tracking-wide text-slate-500 w-[160px] text-right">Розничная (карточка)</th>
|
||||
<th className="py-2 px-3 font-medium text-xs uppercase tracking-wide text-slate-500 w-[160px] text-right">Сумма</th>
|
||||
<th className="py-2 pl-3 w-[40px]"></th>
|
||||
</tr>
|
||||
|
|
@ -322,6 +341,18 @@ export function SupplyEditPage() {
|
|||
onChange={(n) => updateLine(i, { unitPrice: n ?? 0 })}
|
||||
currencyCode={currencies.data?.find((c) => c.id === form.currencyId)?.code}
|
||||
currencySymbol={currencies.data?.find((c) => c.id === form.currencyId)?.symbol}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 px-3">
|
||||
<MoneyInput disabled={isPosted}
|
||||
value={l.retailPriceManuallyOverridden ? l.retailPriceOverride : l.currentRetailPrice}
|
||||
onChange={(n) => updateLine(i, {
|
||||
retailPriceManuallyOverridden: true,
|
||||
retailPriceOverride: n,
|
||||
})}
|
||||
currencyCode={currencies.data?.find((c) => c.id === form.currencyId)?.code}
|
||||
currencySymbol={currencies.data?.find((c) => c.id === form.currencyId)?.symbol}
|
||||
placeholder={l.currentRetailPrice != null ? String(l.currentRetailPrice) : '—'}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 px-3 text-right font-mono font-semibold">
|
||||
|
|
@ -339,7 +370,7 @@ export function SupplyEditPage() {
|
|||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colSpan={4} className="py-3 pr-3 text-right text-sm font-semibold text-slate-600 dark:text-slate-300">
|
||||
<td colSpan={5} className="py-3 pr-3 text-right text-sm font-semibold text-slate-600 dark:text-slate-300">
|
||||
Итого:
|
||||
</td>
|
||||
<td className="py-3 px-3 text-right font-mono text-lg font-bold">
|
||||
|
|
|
|||
Loading…
Reference in a new issue