feat(orders): reopen cancelled orders only after confirmation; accurate sales
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m24s
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m24s
Cancelled orders: - Moving an order out of STORNIERT is possible again, but only after a warning is confirmed in the status form (OrderStatusForm); without the confirmation the server changes nothing. - Reopening books the counter-entry to the cancellation: goods that the cancellation restocked are deducted again (atomically, only if enough stock is left, otherwise the order stays cancelled). A later second cancellation therefore restocks +1 again instead of leaving +2. - The warning states that no new payment is collected. Dashboard figures: - One definition of a counted sale (lib/sales.ts): not unpaid (NEU), not cancelled, no refunded return. Used for revenue today/month, sold glasses, average cart, top products/categories, revenue trend and repeat customers. - "Verkaufte Gläser" previously summed every order item, including cancelled orders and refunded returns. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
dc1523aafd
commit
e0b47faf45
11 files changed
+243
-82
No files matched your search
@@ -4,6 +4,7 @@ import { BackLink } from "@/components/back-link";
|
||||
import { formatPrice, formatDate } from "@/lib/format";
|
||||
import { OrderStatusBadge } from "@/components/account-ui";
|
||||
import { updateOrderStatusAction } from "../actions";
|
||||
import { OrderStatusForm } from "@/components/order-status-form";
|
||||
import type { OrderStatus } from "@/generated/prisma/client";
|
||||
import { insensitiveContains } from "@/lib/prisma-search";
|
||||
import { getLocale } from "@/i18n/locale";
|
||||
@@ -139,30 +140,16 @@ export default async function AdminOrdersPage(props: PageProps<"/admin/bestellun
|
||||
<td className="p-4 whitespace-nowrap tabular-nums">{formatPrice(order.totalCents, order.currency)}</td>
|
||||
<td className="p-4">
|
||||
<OrderStatusBadge status={order.status} label={statusLabels[order.status] ?? order.status} />
|
||||
{/* Storniert ist ein Endzustand (isOrderStatusChangeAllowed) — keine Auswahl anbieten, die der Server ablehnt. */}
|
||||
{order.status !== "STORNIERT" ? (
|
||||
<form action={updateOrderStatusAction} className="mt-2 flex items-center gap-2">
|
||||
<input type="hidden" name="orderId" value={order.id} />
|
||||
<select
|
||||
name="status"
|
||||
defaultValue={order.status}
|
||||
aria-label={t.adminOrdersPage.statusLabel}
|
||||
className="border border-ink/20 bg-background px-2 py-1.5 text-xs"
|
||||
>
|
||||
{Object.entries(statusLabels).map(([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
type="submit"
|
||||
className="border border-heading px-3 py-1.5 text-xs text-heading transition-colors duration-300 hover:bg-forest hover:text-cream"
|
||||
>
|
||||
{t.adminOrdersPage.save}
|
||||
</button>
|
||||
</form>
|
||||
) : null}
|
||||
<OrderStatusForm
|
||||
action={updateOrderStatusAction}
|
||||
orderId={order.id}
|
||||
currentStatus={order.status}
|
||||
labels={statusLabels}
|
||||
saveLabel={t.adminOrdersPage.save}
|
||||
statusAriaLabel={t.adminOrdersPage.statusLabel}
|
||||
reopenConfirmMessage={t.adminOrdersPage.reopenConfirm}
|
||||
compact
|
||||
/>
|
||||
</td>
|
||||
<td className="p-4">
|
||||
{order.fulfillmentMethod === "VERSAND" ? t.adminOrdersPage.shipping : t.adminOrdersPage.pickup}
|
||||
|
||||
Reference in new issue
Block a user