""" Модуль обработки корзины интернет-магазина """ 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()