Stage 06.1 - journal management UI, export and system menu redesign
This commit is contained in:
@@ -7,11 +7,6 @@ from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
|
||||
from src.telegram.ui.exchange_error import (
|
||||
show_callback_exchange_error,
|
||||
show_message_exchange_error,
|
||||
)
|
||||
|
||||
from src.integrations.exchange.exceptions import ExchangeError
|
||||
from src.telegram.handlers.trade.new_order_core import router
|
||||
from src.telegram.handlers.trade.new_order_ui import (
|
||||
@@ -42,6 +37,12 @@ from src.telegram.handlers.trade.new_order_ui import (
|
||||
mode_line,
|
||||
show_recent_drafts,
|
||||
)
|
||||
from src.telegram.ui.exchange_error import (
|
||||
classify_exchange_error,
|
||||
show_callback_exchange_error,
|
||||
show_message_exchange_error,
|
||||
)
|
||||
from src.trading.journal.service import JournalService
|
||||
from src.trading.orders.service import OrderDraftsService
|
||||
from src.trading.orders.states import NewOrderDraftStates
|
||||
|
||||
@@ -61,6 +62,24 @@ MAIN_MENU_BUTTONS = {
|
||||
}
|
||||
|
||||
|
||||
def _user_id_from_message(message: Message) -> int | None:
|
||||
return message.from_user.id if message.from_user else None
|
||||
|
||||
|
||||
def _chat_id_from_message(message: Message) -> int | None:
|
||||
return message.chat.id if message.chat else None
|
||||
|
||||
|
||||
def _user_id_from_callback(callback: CallbackQuery) -> int | None:
|
||||
return callback.from_user.id if callback.from_user else None
|
||||
|
||||
|
||||
def _chat_id_from_callback(callback: CallbackQuery) -> int | None:
|
||||
if callback.message and callback.message.chat:
|
||||
return callback.message.chat.id
|
||||
return None
|
||||
|
||||
|
||||
@router.callback_query(F.data == "drafts:noop")
|
||||
async def drafts_noop(callback: CallbackQuery) -> None:
|
||||
await callback.answer()
|
||||
@@ -75,7 +94,17 @@ async def paginate_drafts(callback: CallbackQuery) -> None:
|
||||
|
||||
page = int(value)
|
||||
await callback.answer()
|
||||
|
||||
if callback.message is not None:
|
||||
JournalService().log_ui_info(
|
||||
event_type="trade_drafts_paginate",
|
||||
message="Открыта страница черновиков.",
|
||||
screen="trade",
|
||||
action="drafts_paginate",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={"page": page},
|
||||
)
|
||||
await show_recent_drafts(callback.message, edit_mode=True, page=page)
|
||||
|
||||
|
||||
@@ -87,9 +116,28 @@ async def open_draft(callback: CallbackQuery) -> None:
|
||||
|
||||
draft = service.get_draft_by_id(draft_id)
|
||||
if not draft:
|
||||
JournalService().log_ui_warning(
|
||||
event_type="trade_draft_open_not_found",
|
||||
message="Черновик не найден.",
|
||||
screen="trade",
|
||||
action="draft_open",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={"draft_id": draft_id, "page": page},
|
||||
)
|
||||
await callback.answer("Черновик не найден", show_alert=True)
|
||||
return
|
||||
|
||||
JournalService().log_ui_info(
|
||||
event_type="trade_draft_open_success",
|
||||
message="Черновик открыт.",
|
||||
screen="trade",
|
||||
action="draft_open",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={"draft_id": draft_id, "page": page},
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
_render_draft_detail(draft),
|
||||
reply_markup=_draft_detail_keyboard(draft_id, page),
|
||||
@@ -100,11 +148,22 @@ async def open_draft(callback: CallbackQuery) -> None:
|
||||
@router.callback_query(F.data.startswith("draft_edit:"))
|
||||
async def edit_draft(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
|
||||
_, draft_id, page_raw = callback.data.split(":", 2)
|
||||
page = int(page_raw)
|
||||
|
||||
draft = service.get_draft_by_id(draft_id)
|
||||
if not draft:
|
||||
journal.log_ui_warning(
|
||||
event_type="trade_draft_edit_not_found",
|
||||
message="Черновик не найден.",
|
||||
screen="trade",
|
||||
action="draft_edit",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={"draft_id": draft_id, "page": page},
|
||||
)
|
||||
await callback.answer("Черновик не найден", show_alert=True)
|
||||
return
|
||||
|
||||
@@ -150,8 +209,34 @@ async def edit_draft(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
drafts_page=page,
|
||||
),
|
||||
)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_draft_edit_requested",
|
||||
message="Запрошено редактирование черновика.",
|
||||
screen="trade",
|
||||
action="draft_edit",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={
|
||||
"draft_id": draft_id,
|
||||
"page": page,
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
},
|
||||
)
|
||||
await callback.answer()
|
||||
except (ExchangeError, ValueError) as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_draft_edit_error",
|
||||
message="Не удалось открыть редактирование черновика.",
|
||||
screen="trade",
|
||||
action="draft_edit",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
error_type=classify_exchange_error(exc) if isinstance(exc, ExchangeError) else "generic",
|
||||
raw_error=str(exc),
|
||||
payload={"draft_id": draft_id, "page": page},
|
||||
)
|
||||
await show_callback_exchange_error(
|
||||
callback,
|
||||
title=_screen_title(is_edit_mode=True),
|
||||
@@ -160,17 +245,35 @@ async def edit_draft(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
back_callback_data=f"draft_open:{draft_id}:{page}",
|
||||
drafts_page=page,
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("draft_delete:"))
|
||||
async def delete_draft_stub(callback: CallbackQuery) -> None:
|
||||
JournalService().log_ui_info(
|
||||
event_type="trade_draft_delete_requested",
|
||||
message="Запрошено удаление черновика.",
|
||||
screen="trade",
|
||||
action="draft_delete",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={"callback_data": callback.data},
|
||||
)
|
||||
await callback.answer("Удаление скоро появится")
|
||||
|
||||
|
||||
@router.message(Command("cancel_order"))
|
||||
async def cancel_order_builder(message: Message, state: FSMContext) -> None:
|
||||
await state.clear()
|
||||
|
||||
JournalService().log_ui_info(
|
||||
event_type="trade_order_create_cancelled",
|
||||
message="Создание черновика ордера отменено.",
|
||||
screen="trade",
|
||||
action="order_cancel",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
"<b>📊 Торговля — Новый ордер</b>\n"
|
||||
f"{mode_line()}"
|
||||
@@ -189,6 +292,7 @@ async def start_new_order_draft(
|
||||
await state.set_state(NewOrderDraftStates.waiting_side)
|
||||
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
|
||||
try:
|
||||
context = service.get_entry_context(side="BUY", order_type="MARKET")
|
||||
@@ -200,11 +304,31 @@ async def start_new_order_draft(
|
||||
"Шаг 1/4. Выбери сторону"
|
||||
)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_create_requested",
|
||||
message="Запрошено создание черновика ордера.",
|
||||
screen="trade",
|
||||
action="order_create",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
payload={"symbol": context.symbol},
|
||||
)
|
||||
|
||||
if edit_mode:
|
||||
await message.edit_text(text, reply_markup=_side_keyboard())
|
||||
else:
|
||||
await message.answer(text, reply_markup=_side_keyboard())
|
||||
except ExchangeError as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_create_error",
|
||||
message="Не удалось открыть создание черновика ордера.",
|
||||
screen="trade",
|
||||
action="order_create",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
)
|
||||
await show_message_exchange_error(
|
||||
message,
|
||||
title="<b>📊 Торговля — Новый ордер</b>",
|
||||
@@ -227,6 +351,7 @@ async def process_order_side_callback(
|
||||
|
||||
path = _render_order_path(side=side)
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
|
||||
try:
|
||||
context = service.get_entry_context(side=side, order_type="MARKET")
|
||||
@@ -239,9 +364,30 @@ async def process_order_side_callback(
|
||||
"Шаг 2/4. Выбери тип ордера"
|
||||
)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_side_selected",
|
||||
message="Выбрана сторона ордера.",
|
||||
screen="trade",
|
||||
action="order_side",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={"side": side, "symbol": context.symbol},
|
||||
)
|
||||
|
||||
await callback.message.edit_text(text, reply_markup=_type_keyboard())
|
||||
await callback.answer()
|
||||
except ExchangeError as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_side_error",
|
||||
message="Не удалось обработать выбор стороны ордера.",
|
||||
screen="trade",
|
||||
action="order_side",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
payload={"side": side},
|
||||
)
|
||||
await show_callback_exchange_error(
|
||||
callback,
|
||||
title="<b>📊 Торговля — Новый ордер</b>",
|
||||
@@ -270,6 +416,7 @@ async def process_order_type_callback(
|
||||
state: FSMContext,
|
||||
) -> None:
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
order_type = callback.data.split(":", 1)[1]
|
||||
|
||||
data = await state.get_data()
|
||||
@@ -290,6 +437,21 @@ async def process_order_type_callback(
|
||||
base_currency=context.base_currency,
|
||||
)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_type_selected",
|
||||
message="Пользователь выбрал тип ордера.",
|
||||
screen="trade",
|
||||
action="order_select_type",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
"symbol": context.symbol,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
_render_quantity_step_screen(
|
||||
title=_screen_title(is_edit_mode),
|
||||
@@ -307,6 +469,21 @@ async def process_order_type_callback(
|
||||
)
|
||||
await callback.answer()
|
||||
except ExchangeError as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_type_select_error",
|
||||
message="Не удалось обработать выбор типа ордера.",
|
||||
screen="trade",
|
||||
action="order_select_type",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
payload={
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
await show_callback_exchange_error(
|
||||
callback,
|
||||
title=_screen_title(is_edit_mode),
|
||||
@@ -327,6 +504,10 @@ async def process_order_type_text(message: Message) -> None:
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(
|
||||
NewOrderDraftStates.waiting_quantity,
|
||||
F.data.startswith("order_qty:"),
|
||||
)
|
||||
@router.callback_query(
|
||||
NewOrderDraftStates.waiting_quantity,
|
||||
F.data.startswith("order_qty:"),
|
||||
@@ -336,6 +517,7 @@ async def process_quantity_callback(
|
||||
state: FSMContext,
|
||||
) -> None:
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
value = callback.data.split(":", 1)[1]
|
||||
|
||||
data = await state.get_data()
|
||||
@@ -360,6 +542,20 @@ async def process_quantity_callback(
|
||||
base_currency=context.base_currency,
|
||||
)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_quantity_manual_open",
|
||||
message="Открыт ручной ввод количества.",
|
||||
screen="trade",
|
||||
action="order_quantity_manual",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
_render_manual_quantity_screen(
|
||||
title=title,
|
||||
@@ -388,6 +584,21 @@ async def process_quantity_callback(
|
||||
|
||||
await state.update_data(quantity=quantity)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_quantity_selected",
|
||||
message="Выбрано количество ордера.",
|
||||
screen="trade",
|
||||
action="order_quantity",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
"quantity": quantity,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
|
||||
if order_type == "LIMIT":
|
||||
path = _render_order_path(
|
||||
side=side,
|
||||
@@ -458,6 +669,22 @@ async def process_quantity_callback(
|
||||
)
|
||||
await callback.answer()
|
||||
except ExchangeError as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_quantity_error",
|
||||
message="Не удалось обработать выбор количества ордера.",
|
||||
screen="trade",
|
||||
action="order_quantity",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
payload={
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
"value": value,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
await show_callback_exchange_error(
|
||||
callback,
|
||||
title=title,
|
||||
@@ -473,6 +700,7 @@ async def process_quantity_callback(
|
||||
)
|
||||
async def process_order_quantity(message: Message, state: FSMContext) -> None:
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
raw_quantity = message.text or ""
|
||||
|
||||
data = await state.get_data()
|
||||
@@ -504,17 +732,15 @@ async def process_order_quantity(message: Message, state: FSMContext) -> None:
|
||||
)
|
||||
|
||||
if quantity is None:
|
||||
path = _render_order_path(
|
||||
side=side,
|
||||
order_type=order_type,
|
||||
base_currency=context.base_currency,
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
_render_quantity_inline_error(
|
||||
title=title,
|
||||
symbol=context.symbol,
|
||||
order_path=path,
|
||||
order_path=_render_order_path(
|
||||
side=side,
|
||||
order_type=order_type,
|
||||
base_currency=context.base_currency,
|
||||
),
|
||||
errors=["Количество должно быть числом больше нуля."],
|
||||
help_text=help_text,
|
||||
),
|
||||
@@ -529,17 +755,15 @@ async def process_order_quantity(message: Message, state: FSMContext) -> None:
|
||||
price=None,
|
||||
)
|
||||
if quantity_errors:
|
||||
path = _render_order_path(
|
||||
side=side,
|
||||
order_type=order_type,
|
||||
base_currency=context.base_currency,
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
_render_quantity_inline_error(
|
||||
title=title,
|
||||
symbol=context.symbol,
|
||||
order_path=path,
|
||||
order_path=_render_order_path(
|
||||
side=side,
|
||||
order_type=order_type,
|
||||
base_currency=context.base_currency,
|
||||
),
|
||||
errors=quantity_errors,
|
||||
help_text=help_text,
|
||||
),
|
||||
@@ -549,6 +773,21 @@ async def process_order_quantity(message: Message, state: FSMContext) -> None:
|
||||
|
||||
await state.update_data(quantity=quantity)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_quantity_manual_success",
|
||||
message="Количество ордера введено вручную.",
|
||||
screen="trade",
|
||||
action="order_quantity_manual",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
payload={
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
"quantity": quantity,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
|
||||
if order_type == "LIMIT":
|
||||
path = _render_order_path(
|
||||
side=side,
|
||||
@@ -615,6 +854,22 @@ async def process_order_quantity(message: Message, state: FSMContext) -> None:
|
||||
reply_markup=_confirm_keyboard(drafts_page=drafts_page),
|
||||
)
|
||||
except ExchangeError as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_quantity_manual_error",
|
||||
message="Не удалось обработать ручной ввод количества.",
|
||||
screen="trade",
|
||||
action="order_quantity_manual",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
payload={
|
||||
"side": side,
|
||||
"order_type": order_type,
|
||||
"raw_quantity": raw_quantity,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
await show_message_exchange_error(
|
||||
message,
|
||||
title=title,
|
||||
@@ -632,6 +887,7 @@ async def process_price_callback(
|
||||
state: FSMContext,
|
||||
) -> None:
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
value = callback.data.split(":", 1)[1]
|
||||
|
||||
data = await state.get_data()
|
||||
@@ -657,6 +913,16 @@ async def process_price_callback(
|
||||
base_currency=context.base_currency,
|
||||
)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_price_manual_open",
|
||||
message="Открыт ручной ввод цены.",
|
||||
screen="trade",
|
||||
action="order_price_manual",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={"is_edit_mode": is_edit_mode},
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
_render_manual_price_screen(
|
||||
title=title,
|
||||
@@ -700,6 +966,19 @@ async def process_price_callback(
|
||||
|
||||
await state.set_state(NewOrderDraftStates.waiting_confirm)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_price_selected",
|
||||
message="Выбрана цена ордера.",
|
||||
screen="trade",
|
||||
action="order_price",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={
|
||||
"price": price,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
|
||||
await callback.message.edit_text(
|
||||
_render_confirm(
|
||||
symbol=draft.symbol,
|
||||
@@ -716,6 +995,20 @@ async def process_price_callback(
|
||||
)
|
||||
await callback.answer()
|
||||
except ExchangeError as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_price_error",
|
||||
message="Не удалось обработать выбор цены ордера.",
|
||||
screen="trade",
|
||||
action="order_price",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
payload={
|
||||
"value": value,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
await show_callback_exchange_error(
|
||||
callback,
|
||||
title=title,
|
||||
@@ -731,6 +1024,7 @@ async def process_price_callback(
|
||||
)
|
||||
async def process_order_price(message: Message, state: FSMContext) -> None:
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
raw_price = message.text or ""
|
||||
price = service.normalize_price(raw_price)
|
||||
|
||||
@@ -754,18 +1048,16 @@ async def process_order_price(message: Message, state: FSMContext) -> None:
|
||||
)
|
||||
|
||||
if price is None:
|
||||
path = _render_order_path(
|
||||
side=data.get("side"),
|
||||
order_type=data.get("order_type"),
|
||||
quantity=data.get("quantity"),
|
||||
base_currency=context.base_currency,
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
_render_price_inline_error(
|
||||
title=title,
|
||||
symbol=context.symbol,
|
||||
order_path=path,
|
||||
order_path=_render_order_path(
|
||||
side=data.get("side"),
|
||||
order_type=data.get("order_type"),
|
||||
quantity=data.get("quantity"),
|
||||
base_currency=context.base_currency,
|
||||
),
|
||||
errors=["Цена должна быть числом больше нуля."],
|
||||
help_text=help_text,
|
||||
),
|
||||
@@ -782,18 +1074,16 @@ async def process_order_price(message: Message, state: FSMContext) -> None:
|
||||
|
||||
validation = service.validate_draft(draft)
|
||||
if not validation.is_valid:
|
||||
path = _render_order_path(
|
||||
side=draft.side,
|
||||
order_type=draft.order_type,
|
||||
quantity=draft.quantity,
|
||||
base_currency=context.base_currency,
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
_render_price_inline_error(
|
||||
title=title,
|
||||
symbol=context.symbol,
|
||||
order_path=path,
|
||||
order_path=_render_order_path(
|
||||
side=draft.side,
|
||||
order_type=draft.order_type,
|
||||
quantity=draft.quantity,
|
||||
base_currency=context.base_currency,
|
||||
),
|
||||
errors=validation.errors,
|
||||
help_text=help_text,
|
||||
),
|
||||
@@ -817,6 +1107,19 @@ async def process_order_price(message: Message, state: FSMContext) -> None:
|
||||
)
|
||||
await state.set_state(NewOrderDraftStates.waiting_confirm)
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_price_manual_success",
|
||||
message="Цена ордера введена вручную.",
|
||||
screen="trade",
|
||||
action="order_price_manual",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
payload={
|
||||
"price": price,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
|
||||
await message.answer(
|
||||
_render_confirm(
|
||||
symbol=draft.symbol,
|
||||
@@ -832,6 +1135,20 @@ async def process_order_price(message: Message, state: FSMContext) -> None:
|
||||
reply_markup=_confirm_keyboard(drafts_page=drafts_page),
|
||||
)
|
||||
except ExchangeError as exc:
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_price_manual_error",
|
||||
message="Не удалось обработать ручной ввод цены.",
|
||||
screen="trade",
|
||||
action="order_price_manual",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
payload={
|
||||
"raw_price": raw_price,
|
||||
"is_edit_mode": is_edit_mode,
|
||||
},
|
||||
)
|
||||
await show_message_exchange_error(
|
||||
message,
|
||||
title=title,
|
||||
@@ -842,17 +1159,34 @@ async def process_order_price(message: Message, state: FSMContext) -> None:
|
||||
|
||||
@router.message(Command("drafts"))
|
||||
async def drafts_command(message: Message) -> None:
|
||||
JournalService().log_ui_info(
|
||||
event_type="trade_drafts_open_requested",
|
||||
message="Запрошено открытие списка черновиков.",
|
||||
screen="trade",
|
||||
action="drafts_open",
|
||||
user_id=_user_id_from_message(message),
|
||||
chat_id=_chat_id_from_message(message),
|
||||
)
|
||||
await show_recent_drafts(message, edit_mode=False, page=1)
|
||||
|
||||
|
||||
@router.callback_query(NewOrderDraftStates.waiting_confirm, F.data == "order_confirm")
|
||||
async def confirm_order(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
service = OrderDraftsService()
|
||||
journal = JournalService()
|
||||
data = await state.get_data()
|
||||
|
||||
raw = data.get("confirm_draft")
|
||||
if not raw:
|
||||
await state.clear()
|
||||
journal.log_ui_warning(
|
||||
event_type="trade_order_confirm_state_error",
|
||||
message="Состояние подтверждения черновика не найдено.",
|
||||
screen="trade",
|
||||
action="order_confirm",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
)
|
||||
await callback.answer("Ошибка состояния", show_alert=True)
|
||||
return
|
||||
|
||||
@@ -874,6 +1208,21 @@ async def confirm_order(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
edit_page = data.get("draft_edit_page")
|
||||
await state.clear()
|
||||
errors = [item.strip() for item in str(exc).split(";") if item.strip()]
|
||||
|
||||
journal.log_ui_warning(
|
||||
event_type="trade_order_confirm_validation_error",
|
||||
message="Черновик не прошёл проверку при сохранении.",
|
||||
screen="trade",
|
||||
action="order_confirm",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
raw_error=str(exc),
|
||||
payload={
|
||||
"errors": errors,
|
||||
"edit_page": edit_page,
|
||||
},
|
||||
)
|
||||
|
||||
reply_markup = (
|
||||
_drafts_back_keyboard(int(edit_page))
|
||||
if edit_page
|
||||
@@ -887,6 +1236,19 @@ async def confirm_order(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
return
|
||||
except ExchangeError as exc:
|
||||
await state.clear()
|
||||
|
||||
journal.log_ui_error(
|
||||
event_type="trade_order_confirm_error",
|
||||
message="Не удалось сохранить черновик ордера.",
|
||||
screen="trade",
|
||||
action="order_confirm",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
error_type=classify_exchange_error(exc),
|
||||
raw_error=str(exc),
|
||||
payload={"draft_edit_page": data.get("draft_edit_page")},
|
||||
)
|
||||
|
||||
await show_callback_exchange_error(
|
||||
callback,
|
||||
title="<b>📊 Торговля — Подтверждение черновика</b>",
|
||||
@@ -899,6 +1261,23 @@ async def confirm_order(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
edit_page = data.get("draft_edit_page")
|
||||
await state.clear()
|
||||
|
||||
journal.log_ui_info(
|
||||
event_type="trade_order_confirm_success",
|
||||
message="Черновик ордера сохранён.",
|
||||
screen="trade",
|
||||
action="order_confirm",
|
||||
user_id=_user_id_from_callback(callback),
|
||||
chat_id=_chat_id_from_callback(callback),
|
||||
payload={
|
||||
"symbol": draft.symbol,
|
||||
"side": draft.side,
|
||||
"order_type": draft.order_type,
|
||||
"quantity": draft.quantity,
|
||||
"price": draft.price,
|
||||
"is_edit_mode": bool(edit_page),
|
||||
},
|
||||
)
|
||||
|
||||
reply_markup = (
|
||||
_drafts_back_keyboard(int(edit_page))
|
||||
if edit_page
|
||||
|
||||
Reference in New Issue
Block a user