fix(pwa): в штрафах только факт и сумма, без кухни расчётов
С карточки убраны строки про номинал постановления, скидку и «в ГИБДД постановление закрыто», из списка — абзац о том, кто и как платит ГИБДД. Остаётся то, ради чего экран делался: фотография нарушения, статья, место, скорость, начисленная сумма и погашен ли долг. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -147,17 +147,12 @@ const FineBriefSchema = z.object({
|
||||
article: z.string(),
|
||||
address: z.string().default(""),
|
||||
car_number: z.string().default(""),
|
||||
// Сумма, начисленная ВОДИТЕЛЮ (парк платит ГИБДД со скидкой и выставляет то,
|
||||
// что заплатил). fine_amount — номинал постановления, он бывает другим.
|
||||
// Сумма, начисленная ВОДИТЕЛЮ. Номинала постановления, скидки и расчётов парка
|
||||
// с ГИБДД в приложении нет намеренно — водителю нужен факт штрафа и сумма.
|
||||
amount: z.number(),
|
||||
billed: z.boolean().default(false),
|
||||
fine_amount: z.number().default(0),
|
||||
discount_amount: z.number().default(0),
|
||||
discount_until: z.string().nullable().default(null),
|
||||
// Погашен ли штраф ВОДИТЕЛЕМ перед парком. Не путать с closed_with_gibdd:
|
||||
// постановление парк гасит авансом, долг водителя от этого не исчезает.
|
||||
// Погашен ли штраф водителем перед парком. Постановление парк гасит авансом,
|
||||
// и на этот признак его расчёты не влияют.
|
||||
settled: z.enum(["owed", "paid", "not_charged", "cancelled", "unknown"]),
|
||||
closed_with_gibdd: z.boolean().default(false),
|
||||
cancelled: z.boolean().default(false),
|
||||
has_photo: z.boolean().default(false),
|
||||
});
|
||||
|
||||
@@ -19,6 +19,10 @@ function Row({ k, v }: { k: string; v: string }) {
|
||||
* Карточка штрафа. Главное здесь — фотография с камеры: по ней водитель узнаёт
|
||||
* свою машину и свою поездку либо понимает, что штраф не его. Поэтому она стоит
|
||||
* первой, до реквизитов, и открывается в полный размер по нажатию.
|
||||
*
|
||||
* Что парк заплатил ГИБДД, со скидкой ли и какой номинал у постановления — здесь
|
||||
* не показывается: водителю нужен факт штрафа и сумма к возврату, а разбор
|
||||
* расчётов парка уводит разговор от долга к внутренней кухне.
|
||||
*/
|
||||
export function FineDetailPage() {
|
||||
const { fineId = "" } = useParams();
|
||||
@@ -39,10 +43,6 @@ export function FineDetailPage() {
|
||||
);
|
||||
}
|
||||
|
||||
const discountLive =
|
||||
f.discount_amount > 0 && !!f.discount_until && !f.closed_with_gibdd &&
|
||||
new Date(f.discount_until) >= new Date(new Date().toDateString());
|
||||
|
||||
return (
|
||||
<div className="pt-2 pb-4">
|
||||
<button className="text-muted text-xs mb-3" onClick={() => nav("/fines")}>‹ Штрафы</button>
|
||||
@@ -78,23 +78,6 @@ export function FineDetailPage() {
|
||||
{formatMoney(f.amount)}
|
||||
</p>
|
||||
<p className="text-muted text-xs mt-1">{fineStatusText(f)}</p>
|
||||
{f.billed && f.fine_amount > 0 && f.fine_amount !== f.amount && (
|
||||
// Номинал постановления и начисление расходятся почти всегда: парк
|
||||
// платит со скидкой. Не показать этого — значит получить вопрос «почему
|
||||
// с меня 618, если в постановлении 750».
|
||||
<p className="text-muted text-xs mt-2">
|
||||
Постановление — {formatMoney(f.fine_amount)}, парк оплатил его
|
||||
{f.fine_amount > f.amount ? " со скидкой" : ""} и выставил вам фактическую сумму
|
||||
</p>
|
||||
)}
|
||||
{f.closed_with_gibdd && (
|
||||
<p className="text-muted text-xs mt-2">В ГИБДД постановление закрыто</p>
|
||||
)}
|
||||
{discountLive && !f.billed && (
|
||||
<p className="text-pos text-xs mt-2">
|
||||
До {formatDate(f.discount_until)} — {formatMoney(f.discount_amount)} со скидкой
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{f.speed != null && f.speed_limit != null && (
|
||||
|
||||
@@ -9,9 +9,8 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
const FINE = {
|
||||
id: 12, number: "18810525260813035846", date: "2026-08-12",
|
||||
article: "12.09.2 - Превышение скорости", address: "Владивосток, Некрасовская, 90",
|
||||
car_number: "А947УТ125", amount: 618.75, billed: true, fine_amount: 750,
|
||||
discount_amount: 375, discount_until: "2026-09-01",
|
||||
settled: "owed" as const, closed_with_gibdd: true, cancelled: false, has_photo: true,
|
||||
car_number: "А947УТ125", amount: 618.75,
|
||||
settled: "owed" as const, cancelled: false, has_photo: true,
|
||||
};
|
||||
|
||||
vi.mock("@/api/driver", () => ({
|
||||
@@ -89,15 +88,24 @@ describe("карточка штрафа", () => {
|
||||
expect(img.closest("a")).toHaveAttribute("target", "_blank");
|
||||
});
|
||||
|
||||
it("объясняет, почему начислено меньше номинала постановления", async () => {
|
||||
it("показывает начисленную сумму, скорость и постановление", async () => {
|
||||
wrap("/fines/12");
|
||||
expect(await screen.findByText("618,75 ₽")).toBeInTheDocument();
|
||||
expect(screen.getByText(/Постановление — 750 ₽/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/В ГИБДД постановление закрыто/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/63 км\/ч при разрешённых 40/)).toBeInTheDocument();
|
||||
expect(screen.getByText("18810525260813035846")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("расчёты парка с ГИБДД водителю не показываются", async () => {
|
||||
// Решение владельца: без номинала, скидки и «парк уже оплатил». В начисление
|
||||
// заложена комиссия парка, и любая из этих строк рядом требует объяснений.
|
||||
wrap("/fines/12");
|
||||
await screen.findByText("618,75 ₽");
|
||||
expect(screen.queryByText(/750/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/скидк/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/ГИБДД/)).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/парк/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("неоплаченный штраф ведёт к оплате штрафов", async () => {
|
||||
wrap("/fines/12");
|
||||
await userEvent.click(await screen.findByRole("button", { name: /Оплатить штрафы/ }));
|
||||
|
||||
@@ -41,11 +41,6 @@ export function FinesPage() {
|
||||
: " "}
|
||||
</p>
|
||||
|
||||
<p className="text-muted text-[11px] mb-3 leading-4">
|
||||
Постановление в ГИБДД оплачивает парк — как правило, сразу и со скидкой.
|
||||
Вы возвращаете парку начисленную сумму: она и складывается в долг по штрафам.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-1.5 mb-3">
|
||||
{([["open", "Не погашены"], ["all", "Все"]] as const).map(([key, label]) => (
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user