From f1a068d9d09ad18812169b0ec739ba24fffdb595 Mon Sep 17 00:00:00 2001 From: ALEXSim Date: Fri, 24 Apr 2026 23:07:00 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D1=8B=20=D0=B2=D1=81=D0=B5=20=D1=84=D1=83?= =?UTF-8?q?=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20=D0=B8=20=D0=BE=D1=81=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D0=BE=D0=B9=20=D1=81=D1=86=D0=B5=D0=BD=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cart_processor.py | 284 ++++++++++++++++++++++++++++++++++++++++++++++ data/cart.txt | 7 ++ data/report.txt | 19 ++++ 3 files changed, 310 insertions(+) create mode 100644 cart_processor.py create mode 100644 data/cart.txt create mode 100644 data/report.txt diff --git a/cart_processor.py b/cart_processor.py new file mode 100644 index 0000000..14cebdd --- /dev/null +++ b/cart_processor.py @@ -0,0 +1,284 @@ +""" +Модуль обработки корзины интернет-магазина +""" + +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/report.txt") # ← исправлено на report.txt + + # 14. Финальное сообщение + print("\n" + "=" * 60) + print("✅ Программа успешно завершена!") + print("=" * 60) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/data/cart.txt b/data/cart.txt new file mode 100644 index 0000000..89f4de5 --- /dev/null +++ b/data/cart.txt @@ -0,0 +1,7 @@ +Ноутбук|55000|1|Электроника +Мышь|1200|2|Электроника +Книга|800|3|Книги +Чехол для телефона|900|1|Аксессуары +Клавиатура|2500|1|Электроника +Наушники|3500|1|Электроника +Блокнот|150|4|Канцелярия \ No newline at end of file diff --git a/data/report.txt b/data/report.txt new file mode 100644 index 0000000..80444ea --- /dev/null +++ b/data/report.txt @@ -0,0 +1,19 @@ +================================================== + ЧЕК ПОКУПАТЕЛЯ +================================================== +Товар Кол-во Цена Итого +-------------------------------------------------- +Ноутбук 1 46750.00 46750.00 +Мышь 2 1020.00 2040.00 +Книга 3 800.00 2400.00 +Чехол для телефона 1 900.00 900.00 +Клавиатура 1 2125.00 2125.00 +Наушники 1 2975.00 2975.00 +Блокнот 4 150.00 600.00 +-------------------------------------------------- +Подытог: 57790.00 +Доставка: 0.00 +-------------------------------------------------- +ИТОГО К ОПЛАТЕ: 52011.00 +================================================== + Спасибо за покупку! \ No newline at end of file