Some items (grilled shashlik) can't have a flat price since the cooked
weight varies. Product gains pricing_mode ("fixed"/"per_100g") and
reference_weight_g; Product.effective_unit_price computes an estimated
per-unit charge (price-per-100g * reference_weight_g / 100) that every
cart/order call site now reads instead of raw Product.price, so the
customer sees a ready "≈" estimate everywhere (menu, cart, checkout) with
zero changes to the actual cart/checkout math.
Also adds a general per-line price override to the admin order create/edit
forms (unit_price), so staff can correct the charged amount after weighing
an item — for any product, not just weight-priced ones. This incidentally
fixes an existing bug where editing an order for an unrelated reason (e.g.
a phone number) silently re-snapshotted every line item to today's catalog
price instead of preserving what was actually charged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
128 lines
2.6 KiB
TypeScript
128 lines
2.6 KiB
TypeScript
export interface Category {
|
|
id: number;
|
|
name: string;
|
|
slug: string;
|
|
sort_order: number;
|
|
}
|
|
|
|
export interface Allergen {
|
|
code: string;
|
|
label: string;
|
|
emoji: string;
|
|
}
|
|
|
|
export interface Product {
|
|
id: number;
|
|
category_id: number;
|
|
name: string;
|
|
description: string;
|
|
weight: string;
|
|
price: number;
|
|
is_estimated_price: boolean;
|
|
image_url: string;
|
|
allergens: Allergen[];
|
|
}
|
|
|
|
export interface CartLine {
|
|
product_id: number;
|
|
name: string;
|
|
weight: string;
|
|
image_url: string;
|
|
price: number;
|
|
is_estimated_price: boolean;
|
|
quantity: number;
|
|
line_total: number;
|
|
}
|
|
|
|
export interface Cart {
|
|
items: CartLine[];
|
|
items_total: number;
|
|
count: number;
|
|
}
|
|
|
|
export interface OrderItemDto {
|
|
name: string;
|
|
quantity: number;
|
|
price: number;
|
|
}
|
|
|
|
export interface OrderDto {
|
|
id: number;
|
|
number: string;
|
|
order_type: "delivery" | "pickup" | "dine_in";
|
|
status: string;
|
|
status_label: string;
|
|
payment_method: "cash" | "card" | null;
|
|
payment_status: "unpaid" | "paid";
|
|
items_total: number;
|
|
delivery_fee: number;
|
|
discount_amount: number;
|
|
promo_code: string | null;
|
|
promo_discount_amount: number;
|
|
total: number;
|
|
created_at: string | null;
|
|
pickup_time: string | null;
|
|
address_street: string | null;
|
|
address_house: string | null;
|
|
table_number: string | null;
|
|
can_cancel: boolean;
|
|
can_pay: boolean;
|
|
items: OrderItemDto[];
|
|
}
|
|
|
|
export interface CheckoutPrefill {
|
|
has_orders: boolean;
|
|
name: string | null;
|
|
phone: string | null;
|
|
delivery: {
|
|
address_city: string | null;
|
|
address_street: string | null;
|
|
address_house: string | null;
|
|
address_apartment: string | null;
|
|
} | null;
|
|
}
|
|
|
|
export interface CheckoutResult {
|
|
order_id: number;
|
|
order_number: string;
|
|
total: number;
|
|
payment_method: "cash" | "card";
|
|
pay_url: string | null;
|
|
card_unavailable_message: string | null;
|
|
track_url: string | null;
|
|
}
|
|
|
|
export interface PromoValidateResult {
|
|
valid: boolean;
|
|
message?: string;
|
|
code?: string;
|
|
discount_percent?: number;
|
|
discount_amount?: number;
|
|
}
|
|
|
|
export interface PublicSettings {
|
|
cafe_name: string;
|
|
logo_url: string;
|
|
phone: string;
|
|
address: string;
|
|
work_hours_from: string;
|
|
work_hours_to: string;
|
|
is_open: boolean;
|
|
instagram_url: string;
|
|
maps_url: string;
|
|
delivery_fee: number;
|
|
free_delivery_threshold: number;
|
|
min_delivery_order_amount: number;
|
|
pickup_discount_percent: number;
|
|
force_closed: boolean;
|
|
force_closed_message: string;
|
|
announcement_enabled: boolean;
|
|
announcement_message: string;
|
|
has_active_promos: boolean;
|
|
theme: {
|
|
accent: string;
|
|
accent_2: string;
|
|
accent_grad: string;
|
|
accent_ink: string;
|
|
};
|
|
}
|