mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
118 lines
4.3 KiB
Python
118 lines
4.3 KiB
Python
import logging
|
||
import requests
|
||
from typing import Optional, List
|
||
from pyzbar.pyzbar import decode
|
||
from PIL import Image
|
||
import numpy as np
|
||
|
||
# Импортируем модель из parser.py
|
||
from parser import ParsedItem
|
||
|
||
API_TOKEN = "36590.yqtiephCvvkYUKM2W"
|
||
API_URL = "https://proverkacheka.com/api/v1/check/get"
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
def is_valid_fiscal_qr(qr_string: str) -> bool:
|
||
"""
|
||
Проверяет, соответствует ли строка формату фискального чека ФНС.
|
||
Ожидаемый формат: t=...&s=...&fn=...&i=...&fp=...&n=...
|
||
Мы проверяем наличие хотя бы 3-х ключевых параметров.
|
||
"""
|
||
if not qr_string:
|
||
return False
|
||
|
||
# Ключевые параметры, которые обязаны быть в строке чека
|
||
required_keys = ["t=", "s=", "fn="]
|
||
|
||
# Проверяем, что все ключевые параметры присутствуют
|
||
# (порядок может отличаться, поэтому проверяем вхождение каждого)
|
||
matches = [key in qr_string for key in required_keys]
|
||
|
||
return all(matches)
|
||
|
||
def detect_and_decode_qr(image: np.ndarray) -> Optional[str]:
|
||
"""
|
||
Ищет ВСЕ QR-коды на изображении и возвращает только тот,
|
||
который похож на фискальный чек.
|
||
"""
|
||
try:
|
||
pil_img = Image.fromarray(image)
|
||
|
||
# Декодируем все коды на картинке
|
||
decoded_objects = decode(pil_img)
|
||
|
||
if not decoded_objects:
|
||
logger.info("No QR codes detected on the image.")
|
||
return None
|
||
|
||
logger.info(f"Detected {len(decoded_objects)} code(s). Scanning for fiscal data...")
|
||
|
||
for obj in decoded_objects:
|
||
if obj.type == 'QRCODE':
|
||
qr_data = obj.data.decode("utf-8")
|
||
|
||
# Логируем найденное (для отладки, если вдруг формат хитрый)
|
||
# Обрезаем длинные строки, чтобы не засорять лог
|
||
log_preview = (qr_data[:75] + '..') if len(qr_data) > 75 else qr_data
|
||
logger.info(f"Checking QR content: {log_preview}")
|
||
|
||
if is_valid_fiscal_qr(qr_data):
|
||
logger.info("Valid fiscal QR found!")
|
||
return qr_data
|
||
else:
|
||
logger.info("QR skipped (not a fiscal receipt pattern).")
|
||
|
||
logger.warning("QR codes were found, but none matched the fiscal receipt format.")
|
||
return None
|
||
|
||
except Exception as e:
|
||
logger.error(f"Error during QR detection: {e}")
|
||
return None
|
||
|
||
def fetch_data_from_api(qr_raw: str) -> List[ParsedItem]:
|
||
"""
|
||
Отправляет данные QR-кода в API proverkacheka.com и парсит JSON-ответ.
|
||
"""
|
||
try:
|
||
payload = {
|
||
'qrraw': qr_raw,
|
||
'token': API_TOKEN
|
||
}
|
||
|
||
logger.info("Sending request to Check API...")
|
||
response = requests.post(API_URL, data=payload, timeout=10)
|
||
|
||
if response.status_code != 200:
|
||
logger.error(f"API Error: Status {response.status_code}")
|
||
return []
|
||
|
||
data = response.json()
|
||
|
||
if data.get('code') != 1:
|
||
logger.warning(f"API returned non-success code: {data.get('code')}")
|
||
return []
|
||
|
||
json_data = data.get('data', {}).get('json', {})
|
||
items_data = json_data.get('items', [])
|
||
|
||
parsed_items = []
|
||
|
||
for item in items_data:
|
||
price = float(item.get('price', 0)) / 100.0
|
||
total_sum = float(item.get('sum', 0)) / 100.0
|
||
quantity = float(item.get('quantity', 0))
|
||
name = item.get('name', 'Unknown')
|
||
|
||
parsed_items.append(ParsedItem(
|
||
raw_name=name,
|
||
amount=quantity,
|
||
price=price,
|
||
sum=total_sum
|
||
))
|
||
|
||
return parsed_items
|
||
|
||
except Exception as e:
|
||
logger.error(f"Error fetching/parsing API data: {e}")
|
||
return [] |