реализованы все функции и основной сценарий
This commit is contained in:
parent
f16fe5c1c4
commit
ada10f5c01
6
.idea/ALEXSim.iml
generated
6
.idea/ALEXSim.iml
generated
@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module type="PYTHON_MODULE" version="4">
|
<module type="PYTHON_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$" />
|
<content url="file://$MODULE_DIR$">
|
||||||
<orderEntry type="inheritedJdk" />
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.14 (ALEXSim)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
7
.idea/misc.xml
generated
7
.idea/misc.xml
generated
@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Black">
|
|
||||||
<option name="sdkName" value="Python 3.14 (PythonProject1)" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.14 (PythonProject1)" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
291
cart_processor.py
Normal file
291
cart_processor.py
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
"""
|
||||||
|
Модуль обработки корзины интернет-магазина
|
||||||
|
"""
|
||||||
|
|
||||||
|
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()
|
||||||
7
data/cart.txt
Normal file
7
data/cart.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Ноутбук|55000|1|Электроника
|
||||||
|
Мышь|1200|2|Электроника
|
||||||
|
Книга|800|3|Книги
|
||||||
|
Чехол для телефона|900|1|Аксессуары
|
||||||
|
Клавиатура|2500|1|Электроника
|
||||||
|
Наушники|3500|1|Электроника
|
||||||
|
Блокнот|150|4|Канцелярия
|
||||||
Loading…
Reference in New Issue
Block a user