no message
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||||
|
from ...exceptions import TaskExceptionWithEmailNotify
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -13,12 +14,12 @@ class DeliveryPeriodFromConfig(AbstractTask):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
@pass_if_error
|
#@pass_if_error
|
||||||
def do(self, attachment) -> None:
|
def do(self, attachment) -> None:
|
||||||
try:
|
try:
|
||||||
delivery_period = self.config.get("delivery_period")
|
delivery_period = self.config.get("delivery_period")
|
||||||
attachment["delivery_period"] = delivery_period
|
attachment["delivery_period"] = delivery_period
|
||||||
logger.warning(f"Срок доставки установлен из конфига - {delivery_period} (ч.)")
|
logger.warning(f"Срок доставки установлен из конфига - {delivery_period} (ч.)")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"Ошибка при установке срока доставки из конфига. Детали ошибки: {e}")
|
raise TaskExceptionWithEmailNotify(f"Ошибка при установке срока доставки из конфига. Детали ошибки: {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||||
|
from ...exceptions import TaskExceptionWithEmailNotify
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
@@ -48,7 +49,7 @@ class DeliveryPeriodFromSubject(AbstractTask):
|
|||||||
logger.debug(f"Срок доставки для файла {attachment["name"]} установлен как {delivery_time}")
|
logger.debug(f"Срок доставки для файла {attachment["name"]} установлен как {delivery_time}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
raise TaskExceptionWithEmailNotify(f"Ошибка при установке срока доставки из темы письма. Детали ошибки: {e}")
|
||||||
|
|
||||||
|
|
||||||
def _parse_delivery_period(self, subject: str) -> int:
|
def _parse_delivery_period(self, subject: str) -> int:
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ class DeliveryPeriodLocalStore(AbstractTask):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def do(self) -> None:
|
def do(self, attachment) -> None:
|
||||||
attachments = self.context.data["attachments"]
|
attachment["delivery_period"] = 0
|
||||||
for attachment in attachments:
|
logger.info(f"Срок доставки для файла {attachment["name"]} - только из наличия")
|
||||||
attachment["delivery_period"] = 0
|
|
||||||
logger.info(f"Срок доставки для файла {attachment["name"]} - только из наличия")
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from email import encoders
|
|||||||
|
|
||||||
|
|
||||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||||
|
from ...exceptions import TaskExceptionWithEmailNotify
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -19,40 +20,41 @@ class EmailReplyTask(AbstractTask):
|
|||||||
"""Формирует ответ на входящее письмо с запросом на заказ°"""
|
"""Формирует ответ на входящее письмо с запросом на заказ°"""
|
||||||
EMAIl = "zosimovaa@yandex.ru" #"noreply@zapchastiya.ru"
|
EMAIl = "zosimovaa@yandex.ru" #"noreply@zapchastiya.ru"
|
||||||
|
|
||||||
@pass_if_error
|
#@pass_if_error
|
||||||
#@handle_errors
|
#@handle_errors
|
||||||
def do(self, attachment):
|
def do(self, attachment):
|
||||||
|
try:
|
||||||
|
email = self.context.data.get("email")
|
||||||
|
|
||||||
email = self.context.data.get("email")
|
if not email:
|
||||||
|
raise ValueError("В контексте нет входящего сообщения")
|
||||||
|
|
||||||
if not email:
|
email_from = self.context.data.get("email_from")
|
||||||
raise ValueError("В контексте нет входящего сообщения")
|
if not email_from:
|
||||||
|
raise ValueError("В контексте не определен адрес отправителя")
|
||||||
email_from = self.context.data.get("email_from")
|
|
||||||
if not email_from:
|
|
||||||
raise ValueError("В контексте не определен адрес отправителя")
|
|
||||||
|
|
||||||
|
|
||||||
reply_message = MIMEMultipart()
|
reply_message = MIMEMultipart()
|
||||||
|
|
||||||
email_subj = self.context.data.get("email_subj")
|
email_subj = self.context.data.get("email_subj")
|
||||||
|
|
||||||
reply_message["From"] = os.environ.get("EMAIL_USER")
|
reply_message["From"] = os.environ.get("EMAIL_USER")
|
||||||
reply_message["To"] = email_from
|
reply_message["To"] = email_from
|
||||||
#reply_message["Cc"] = self.config.get("reply_to", "")
|
#reply_message["Cc"] = self.config.get("reply_to", "")
|
||||||
reply_message["Subject"] = f"Re: {email_subj}"
|
reply_message["Subject"] = f"Re: {email_subj}"
|
||||||
reply_message["Date"] = formatdate(localtime=True)
|
reply_message["Date"] = formatdate(localtime=True)
|
||||||
|
|
||||||
body = "Автоматический ответ на создание заказа"
|
body = "Автоматический ответ на создание заказа"
|
||||||
reply_message.attach(MIMEText(body, "plain", "utf-8"))
|
reply_message.attach(MIMEText(body, "plain", "utf-8"))
|
||||||
|
|
||||||
|
|
||||||
self._attach_file(reply_message, attachment)
|
self._attach_file(reply_message, attachment)
|
||||||
|
|
||||||
self.context.email_client.send_email(reply_message)
|
self.context.email_client.send_email(reply_message)
|
||||||
|
|
||||||
logger.warning(f"Сформирован ответ на заказ на email")
|
|
||||||
|
|
||||||
|
logger.warning(f"Сформирован ответ на заказ на email")
|
||||||
|
except Exception as e:
|
||||||
|
raise TaskExceptionWithEmailNotify("Произошла ошибка при отправке уведомления клиенту об успешном заказе")
|
||||||
|
|
||||||
def _attach_file(self, reply_message, attachment):
|
def _attach_file(self, reply_message, attachment):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import logging
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from mail_order_bot.task_processor.abstract_task import AbstractTask, pass_if_error, handle_errors
|
from mail_order_bot.task_processor.abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||||
from mail_order_bot.task_processor.handlers.excel_parcers.order_extractor import ExcelFileParcer
|
from mail_order_bot.task_processor.handlers.excel_parcers.order_extractor import ExcelFileParcer
|
||||||
|
from ...exceptions import TaskExceptionWithEmailNotify
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -15,10 +16,13 @@ class ExcelExtractor(AbstractTask):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.excel_config = self.config.get("excel", {})
|
self.excel_config = self.config.get("excel", {})
|
||||||
|
|
||||||
@pass_if_error
|
#@pass_if_error
|
||||||
#@handle_errors
|
#@handle_errors
|
||||||
def do(self, attachment) -> None:
|
def do(self, attachment) -> None:
|
||||||
file_bytes = BytesIO(attachment['bytes'])
|
try:
|
||||||
excel_file = ExcelFileParcer(file_bytes, self.excel_config)
|
file_bytes = BytesIO(attachment['bytes'])
|
||||||
attachment["excel"] = excel_file
|
excel_file = ExcelFileParcer(file_bytes, self.excel_config)
|
||||||
logger.warning(f"Произведен успешный парсинг файла {attachment.get('name', 'неизвестный файл')}")
|
attachment["excel"] = excel_file
|
||||||
|
logger.warning(f"Произведен успешный парсинг файла {attachment.get('name', 'неизвестный файл')}")
|
||||||
|
except Exception as e:
|
||||||
|
raise TaskExceptionWithEmailNotify("Произошла ошибка при парсинге эксель файла. Детали ошибки: {e}")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import pandas as pd
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from mail_order_bot.parsers.order_parcer import OrderParser
|
from mail_order_bot.parsers.order_parcer import OrderParser
|
||||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||||
|
from ...exceptions import TaskExceptionWithEmailNotify
|
||||||
|
|
||||||
from mail_order_bot.parsers.excel_parcer import ExcelFileParcer
|
from mail_order_bot.parsers.excel_parcer import ExcelFileParcer
|
||||||
|
|
||||||
@@ -19,23 +20,26 @@ class OrderExtractor(AbstractTask):
|
|||||||
self.excel_config = self.config.get("excel", {})
|
self.excel_config = self.config.get("excel", {})
|
||||||
|
|
||||||
@pass_if_error
|
@pass_if_error
|
||||||
#@handle_errors
|
#@handle_errors("Произошла ошибка при парсинге заказа")
|
||||||
def do(self, attachment) -> None:
|
def do(self, attachment) -> None:
|
||||||
# todo сделать проверку на наличие файла и его тип
|
try:
|
||||||
delivery_period = attachment.get("delivery_period", 0)
|
# todo сделать проверку на наличие файла и его тип
|
||||||
mapping = self.excel_config.get("mapping")
|
delivery_period = attachment.get("delivery_period", 0)
|
||||||
|
mapping = self.excel_config.get("mapping")
|
||||||
|
|
||||||
excel_file = attachment.get("excel")
|
excel_file = attachment.get("excel")
|
||||||
client_id = self.config.get("client_id")
|
client_id = self.config.get("client_id")
|
||||||
|
|
||||||
order_parcer = OrderParser(mapping, delivery_period, client_id)
|
order_parcer = OrderParser(mapping, delivery_period, client_id)
|
||||||
|
|
||||||
order_dataframe = excel_file.get_order_rows()
|
order_dataframe = excel_file.get_order_rows()
|
||||||
order = order_parcer.parse(order_dataframe)
|
order = order_parcer.parse(order_dataframe)
|
||||||
|
|
||||||
attachment["order"] = order
|
attachment["order"] = order
|
||||||
|
|
||||||
logger.warning(f"Файл заказа обработан успешно, извлечено {len(order.positions)} позиций")
|
logger.warning(f"Файл заказа обработан успешно, извлечено {len(order.positions)} позиций")
|
||||||
|
except Exception as e:
|
||||||
|
raise TaskExceptionWithEmailNotify(f"Ошибка при парсинге заказа. Детали ошибки: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||||
|
from ...exceptions import TaskExceptionWithEmailNotify
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -15,31 +15,34 @@ class UpdateExcelFile(AbstractTask):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.excel_config = self.config.get("excel", {})
|
self.excel_config = self.config.get("excel", {})
|
||||||
|
|
||||||
@pass_if_error
|
#@pass_if_error
|
||||||
#@handle_errors
|
#@handle_errors
|
||||||
def do(self, attachment) -> None:
|
def do(self, attachment) -> None:
|
||||||
# todo сделать проверку на наличие файла и его тип
|
try:
|
||||||
excel_file = attachment.get("excel")
|
# todo сделать проверку на наличие файла и его тип
|
||||||
order = attachment.get("order")
|
excel_file = attachment.get("excel")
|
||||||
config = self.context.data.get("config", {})
|
order = attachment.get("order")
|
||||||
excel_config = config.get("excel", {})
|
config = self.context.data.get("config", {})
|
||||||
updatable_fields = excel_config.get("updatable_fields", {})
|
excel_config = config.get("excel", {})
|
||||||
|
updatable_fields = excel_config.get("updatable_fields", {})
|
||||||
|
|
||||||
for position in order.positions:
|
for position in order.positions:
|
||||||
|
|
||||||
sku = position.sku
|
sku = position.sku
|
||||||
manufacturer = position.manufacturer
|
manufacturer = position.manufacturer
|
||||||
|
|
||||||
for key, value in updatable_fields.items():
|
for key, value in updatable_fields.items():
|
||||||
|
|
||||||
if key == "ordered_quantity":
|
if key == "ordered_quantity":
|
||||||
column = value
|
column = value
|
||||||
value = position.order_quantity
|
value = position.order_quantity
|
||||||
excel_file.set_value(sku, manufacturer, column, value)
|
excel_file.set_value(sku, manufacturer, column, value)
|
||||||
|
|
||||||
if key == "ordered_price":
|
if key == "ordered_price":
|
||||||
column = value
|
column = value
|
||||||
value = position.order_price
|
value = position.order_price
|
||||||
excel_file.set_value(sku, manufacturer, column, value)
|
excel_file.set_value(sku, manufacturer, column, value)
|
||||||
|
|
||||||
logger.warning(f"Файла {attachment.get('name', 'неизвестный файл')} отредактирован")
|
logger.warning(f"Файла {attachment.get('name', 'неизвестный файл')} отредактирован")
|
||||||
|
except Exception as e:
|
||||||
|
raise TaskExceptionWithEmailNotify(f"Не удалось отредактировать исходный файл с заказом. Детали ошибки: {e}")
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import logging
|
|||||||
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
from ...abstract_task import AbstractTask, pass_if_error, handle_errors
|
||||||
from mail_order_bot.abcp_api.abcp_provider import AbcpProvider
|
from mail_order_bot.abcp_api.abcp_provider import AbcpProvider
|
||||||
from mail_order_bot.credential_provider import CredentialProvider
|
from mail_order_bot.credential_provider import CredentialProvider
|
||||||
|
from ...exceptions import TaskExceptionSilent
|
||||||
|
|
||||||
from mail_order_bot.telegram.client import TelegramClient
|
from mail_order_bot.telegram.client import TelegramClient
|
||||||
|
|
||||||
@@ -19,23 +20,26 @@ class TelegramNotifier(AbstractTask):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
@pass_if_error
|
#@pass_if_error
|
||||||
# @handle_errors
|
# @handle_errors
|
||||||
def do(self, attachment) -> None:
|
def do(self, attachment) -> None:
|
||||||
message = self.build_message(attachment)
|
try:
|
||||||
|
message = self.build_message(attachment)
|
||||||
|
|
||||||
client = TelegramClient()
|
client = TelegramClient()
|
||||||
result = client.send_message(message)
|
result = client.send_message(message)
|
||||||
|
|
||||||
# Отправка экселя в телеграм
|
# Отправка экселя в телеграм
|
||||||
excel = attachment["excel"]
|
excel = attachment["excel"]
|
||||||
file = excel.get_file_bytes()
|
file = excel.get_file_bytes()
|
||||||
client.send_document(
|
client.send_document(
|
||||||
document=file,
|
document=file,
|
||||||
filename=attachment.get("name", "document.xlsx")
|
filename=attachment.get("name", "document.xlsx")
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.warning("Инфо по заказу отправлено в телеграм")
|
logger.warning("Инфо по заказу отправлено в телеграм")
|
||||||
|
except Exception as e:
|
||||||
|
raise TaskExceptionSilent(f"Ошибка при отправке в телеграм. Детали ошибки: {e}")
|
||||||
|
|
||||||
def build_message(self, attachment):
|
def build_message(self, attachment):
|
||||||
order = attachment["order"]
|
order = attachment["order"]
|
||||||
|
|||||||
@@ -15,17 +15,15 @@ from mail_order_bot.abcp_api.abcp_provider import AbcpProvider
|
|||||||
from mail_order_bot.credential_provider import CredentialProvider
|
from mail_order_bot.credential_provider import CredentialProvider
|
||||||
from mail_order_bot.order.auto_part_order import OrderStatus
|
from mail_order_bot.order.auto_part_order import OrderStatus
|
||||||
|
|
||||||
from ...exceptions import TaskException
|
from ...exceptions import TaskExceptionWithEmailNotify
|
||||||
|
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class StockSelectorException(TaskException):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class RefusalLevelExceededException(TaskException):
|
class RefusalLevelExceededException(TaskExceptionWithEmailNotify):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -46,39 +44,45 @@ class StockSelector(AbstractTask):
|
|||||||
#@handle_errors
|
#@handle_errors
|
||||||
def do(self, attachment) -> None:
|
def do(self, attachment) -> None:
|
||||||
# todo сделать проверку на наличие файла и его тип
|
# todo сделать проверку на наличие файла и его тип
|
||||||
order = attachment.get("order", None)
|
try:
|
||||||
delivery_period = attachment.get("delivery_period")
|
order = attachment.get("order", None)
|
||||||
for position in order.positions:
|
delivery_period = attachment.get("delivery_period")
|
||||||
|
for position in order.positions:
|
||||||
|
|
||||||
#1. Получаем остатки со складов
|
#1. Получаем остатки со складов
|
||||||
stock_data = self.client_provider.get_stock(position.sku, position.manufacturer)
|
stock_data = self.client_provider.get_stock(position.sku, position.manufacturer)
|
||||||
|
|
||||||
#2. Из данных остатков выбираем оптимальное значение по стратегии
|
#2. Из данных остатков выбираем оптимальное значение по стратегии
|
||||||
if stock_data["success"]:
|
if stock_data["success"]:
|
||||||
stock_list = stock_data.get("data", [])
|
stock_list = stock_data.get("data", [])
|
||||||
asking_price = position.asking_price
|
asking_price = position.asking_price
|
||||||
asking_quantity = position.asking_quantity
|
asking_quantity = position.asking_quantity
|
||||||
|
|
||||||
|
|
||||||
optimal_stock_positions = self.get_optimal_stock(stock_list, asking_price, asking_quantity, delivery_period)
|
optimal_stock_positions = self.get_optimal_stock(stock_list, asking_price, asking_quantity, delivery_period)
|
||||||
|
|
||||||
# 3. Устанавливаем выбранное значение в позицию
|
# 3. Устанавливаем выбранное значение в позицию
|
||||||
if len(optimal_stock_positions):
|
if len(optimal_stock_positions):
|
||||||
position.set_order_item(optimal_stock_positions[0])
|
position.set_order_item(optimal_stock_positions[0])
|
||||||
|
else:
|
||||||
|
position.status = PositionStatus.NO_AVAILABLE_STOCK
|
||||||
|
# Мне не очень нравится управление статусами в этом месте, кажется что лучше это делать внутри AutoPartPosition
|
||||||
else:
|
else:
|
||||||
position.status = PositionStatus.NO_AVAILABLE_STOCK
|
position.status = PositionStatus.STOCK_FAILED
|
||||||
# Мне не очень нравится управление статусами в этом месте, кажется что лучше это делать внутри AutoPartPosition
|
|
||||||
else:
|
|
||||||
position.status = PositionStatus.STOCK_FAILED
|
|
||||||
|
|
||||||
refusal_threshold = self.config.get("refusal_threshold", 1)
|
refusal_threshold = self.config.get("refusal_threshold", 1)
|
||||||
refusal_level = order.get_refusal_level()
|
refusal_level = order.get_refusal_level()
|
||||||
|
|
||||||
if refusal_level > refusal_threshold:
|
if refusal_level > refusal_threshold:
|
||||||
raise RefusalLevelExceededException(f"Превышен лимит по отказам, необходима ручная обработка. "
|
raise RefusalLevelExceededException(f"Превышен лимит по отказам, необходима ручная обработка. "
|
||||||
f"Уровень отказов: {refusal_level:.2%}, допустимый лимит: {refusal_threshold:.2%}")
|
f"Уровень отказов: {refusal_level:.2%}, допустимый лимит: {refusal_threshold:.2%}")
|
||||||
|
|
||||||
logger.warning("Определены оптимальные позиции со складов")
|
logger.warning("Определены оптимальные позиции со складов")
|
||||||
|
except RefusalLevelExceededException:
|
||||||
|
raise RefusalLevelExceededException
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise TaskExceptionWithEmailNotify(f"Произошла ошибка при выборе позиций со складов. Детали ошибки: {e}")
|
||||||
|
|
||||||
def get_optimal_stock(self, stock_list, asking_price, asking_quantity, delivery_period):
|
def get_optimal_stock(self, stock_list, asking_price, asking_quantity, delivery_period):
|
||||||
"""Выбирает позицию для заказа"""
|
"""Выбирает позицию для заказа"""
|
||||||
|
|||||||
Reference in New Issue
Block a user