ALEXSim/cart_processor.py

291 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Модуль обработки корзины интернет-магазина
"""
import os
from typing import List, Dict, Tuple
def load_cart(filepath: str) -> List[Dict]:
"""
Загружает корзину из файла.
Формат строки: название|цена|количество|категория
"""
cart = []
try:
with open(filepath, 'r', encoding='utf-8') as file:
for line_num, line in enumerate(file, 1):
line = line.strip()
if not line:
continue
parts = line.split('|')
if len(parts) != 4:
print(f"Предупреждение: строка {line_num} имеет неверный формат, пропущена")
continue
try:
name = parts[0].strip()
price = float(parts[1].strip())
quantity = int(parts[2].strip())
category = parts[3].strip()
if price < 0 or quantity <= 0:
print(f"Предупреждение: строка {line_num} содержит недопустимые значения, пропущена")
continue
cart.append({
'name': name,
'price': price,
'quantity': quantity,
'category': category
})
except ValueError:
print(f"Предупреждение: строка {line_num} содержит нечисловые значения, пропущена")
continue
except FileNotFoundError:
print(f"Ошибка: файл {filepath} не найден. Создайте файл с данными.")
return []
return cart
def calculate_item_total(item: Dict) -> float:
"""Вычисляет общую стоимость одного товара (цена × количество)"""
return item['price'] * item['quantity']
def apply_category_discount(cart: List[Dict], category: str, discount_percent: float) -> List[Dict]:
"""
Применяет скидку к товарам указанной категории.
Возвращает новый список (не изменяет исходный).
"""
if discount_percent < 0 or discount_percent > 100:
raise ValueError("Скидка должна быть в диапазоне 0-100%")
new_cart = []
discount_factor = 1 - discount_percent / 100
for item in cart:
new_item = item.copy()
if item['category'].lower() == category.lower():
new_price = item['price'] * discount_factor
new_item['price'] = max(0, round(new_price, 2)) # цена не может быть отрицательной
new_cart.append(new_item)
return new_cart
def calculate_subtotal(cart: List[Dict]) -> float:
"""Вычисляет общую сумму всех товаров в корзине"""
total = sum(calculate_item_total(item) for item in cart)
return round(total, 2)
def apply_bulk_discount(subtotal: float, total_quantity: int, threshold: int, discount_percent: float) -> float:
"""
Применяет скидку на общую сумму, если количество товаров превышает порог.
"""
if threshold <= 0 or discount_percent < 0 or discount_percent > 100:
raise ValueError("Недопустимые параметры скидки")
if total_quantity >= threshold:
discount_amount = subtotal * (discount_percent / 100)
return round(subtotal - discount_amount, 2)
return subtotal
def calculate_delivery_cost(subtotal: float, free_threshold: float, base_cost: float = 5.0) -> float:
"""
Рассчитывает стоимость доставки.
Бесплатно, если сумма заказа >= порога.
"""
if free_threshold < 0 or base_cost < 0:
raise ValueError("Стоимость не может быть отрицательной")
if subtotal >= free_threshold:
return 0.0
return base_cost
def find_most_expensive_item(cart: List[Dict]) -> Dict:
"""Возвращает товар с максимальной итоговой стоимостью"""
if not cart:
return {}
most_expensive = max(cart, key=calculate_item_total)
return most_expensive
def group_by_category(cart: List[Dict]) -> Dict[str, List[Dict]]:
"""Группирует товары по категориям"""
groups = {}
for item in cart:
category = item['category']
if category not in groups:
groups[category] = []
groups[category].append(item)
return groups
def detect_duplicates(cart: List[Dict]) -> List[str]:
"""Находит дубликаты товаров (по названию без учёта регистра)"""
name_counts = {}
for item in cart:
name_lower = item['name'].lower()
name_counts[name_lower] = name_counts.get(name_lower, 0) + 1
duplicates = [item['name'] for item in cart
if name_counts[item['name'].lower()] > 1]
# Убираем дубликаты в возвращаемом списке
return list(set(duplicates))
def generate_receipt(cart: List[Dict], delivery_cost: float, final_total: float) -> str:
"""
Формирует текстовый чек
"""
if not cart:
return "Корзина пуста"
# Заголовок чека
receipt = []
receipt.append("=" * 50)
receipt.append("ЧЕК ПОКУПАТЕЛЯ".center(50))
receipt.append("=" * 50)
receipt.append(f"{'Товар':<20} {'Кол-во':>6} {'Цена':>10} {'Итого':>10}")
receipt.append("-" * 50)
# Строки товаров
for item in cart:
name = item['name'][:18] # Обрезаем длинные названия
quantity = item['quantity']
price = item['price']
total = calculate_item_total(item)
receipt.append(f"{name:<20} {quantity:>6} {price:>10.2f} {total:>10.2f}")
receipt.append("-" * 50)
# Информация о доставке и итоге
subtotal = calculate_subtotal(cart)
receipt.append(f"{'Подытог:':<40} {subtotal:>10.2f}")
receipt.append(f"{'Доставка:':<40} {delivery_cost:>10.2f}")
receipt.append("-" * 50)
receipt.append(f"{'ИТОГО К ОПЛАТЕ:':<40} {final_total:>10.2f}")
receipt.append("=" * 50)
receipt.append("Спасибо за покупку!".center(50))
return "\n".join(receipt)
def save_to_file(content: str, filepath: str) -> None:
"""Сохраняет содержимое в файл (создаёт директорию при необходимости)"""
# Создаём директорию, если её нет
directory = os.path.dirname(filepath)
if directory and not os.path.exists(directory):
os.makedirs(directory)
with open(filepath, 'w', encoding='utf-8') as file:
file.write(content)
print(f"Чек сохранён в файл: {filepath}")
def print_cart(cart: List[Dict]) -> None:
"""Выводит содержимое корзины в читаемом виде"""
print("\nСодержимое корзины:")
print("-" * 60)
for item in cart:
total = calculate_item_total(item)
print(
f" {item['name']:<20} | {item['price']:>8.2f} x {item['quantity']} = {total:>10.2f} | Кат: {item['category']}")
print("-" * 60)
def main():
"""Главная функция программы"""
print("=" * 60)
print("СИСТЕМА УПРАВЛЕНИЯ ЗАКАЗАМИ ИНТЕРНЕТ-МАГАЗИНА")
print("=" * 60)
# 1. Загрузка корзины из файла
cart = load_cart("data/cart.txt")
if not cart:
print("Нет данных для обработки. Программа завершена.")
return
# 2. Вывод содержимого корзины
print_cart(cart)
# 3. Расчёт начальной суммы
subtotal = calculate_subtotal(cart)
print(f"\n1. Промежуточная сумма (без скидок): {subtotal:.2f} руб.")
# 4. Применение скидки на категорию "Электроника"
print("\n2. Применяем скидку 15% на категорию 'Электроника'...")
discounted_cart = apply_category_discount(cart, "Электроника", 15)
subtotal_after_cat_discount = calculate_subtotal(discounted_cart)
print(f" Сумма после категорийной скидки: {subtotal_after_cat_discount:.2f} руб.")
# 5. Подсчёт общего количества товаров
total_quantity = sum(item['quantity'] for item in discounted_cart)
print(f"\n3. Общее количество товаров в корзине: {total_quantity} шт.")
# 6. Применение bulk-скидки (если количество >= 5)
subtotal_after_bulk = subtotal_after_cat_discount
if total_quantity >= 5:
subtotal_after_bulk = apply_bulk_discount(subtotal_after_cat_discount, total_quantity, 5, 10)
print(f" Применена bulk-скидка 10% (порог 5 шт.)")
print(f" Сумма после bulk-скидки: {subtotal_after_bulk:.2f} руб.")
else:
print(f" Bulk-скидка не применена (нужно {5} товаров, имеется {total_quantity})")
# 7. Расчёт стоимости доставки
delivery_cost = calculate_delivery_cost(subtotal_after_bulk, 2000.0, 300.0)
if delivery_cost == 0:
print(f"\n4. Доставка: БЕСПЛАТНО (сумма заказа >= 2000 руб.)")
else:
print(f"\n4. Стоимость доставки: {delivery_cost:.2f} руб.")
# 8. Поиск самого дорогого товара
most_expensive = find_most_expensive_item(discounted_cart)
if most_expensive:
expensive_total = calculate_item_total(most_expensive)
print(f"\n5. Самый дорогой товар: {most_expensive['name']} ({expensive_total:.2f} руб.)")
# 9. Группировка по категориям
print("\n6. Группировка товаров по категориям:")
grouped = group_by_category(discounted_cart)
for category, items in grouped.items():
category_total = calculate_subtotal(items)
items_count = sum(item['quantity'] for item in items)
print(f"{category}: {items_count} шт., сумма {category_total:.2f} руб.")
# 10. Поиск дубликатов
duplicates = detect_duplicates(discounted_cart)
if duplicates:
print(f"\n7. ВНИМАНИЕ: Найдены дубликаты товаров: {', '.join(duplicates)}")
else:
print("\n7. Дубликаты товаров не обнаружены")
# 11. Формирование итоговой суммы к оплате
final_total = subtotal_after_bulk + delivery_cost
# 12. Генерация чека
receipt = generate_receipt(discounted_cart, delivery_cost, final_total)
print("\n" + receipt)
# 13. Сохранение чека в файл
save_to_file(receipt, "data/receipt.txt")
# 14. Финальное сообщение
print("\n" + "=" * 60)
print("✅ Программа успешно завершена!")
print("=" * 60)
if __name__ == "__main__":
main()