From f4d7e204d704ea00aff59bc372bdd90320571a75 Mon Sep 17 00:00:00 2001 From: stud203791 Date: Sat, 4 Apr 2026 00:27:11 +0300 Subject: [PATCH] Initial commit --- .idea/InventoryManager.iml | 10 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 4 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .idea/workspace.xml | 41 ++ data/products.csv | 9 + inventory_manager.py | 366 ++++++++++++++++++ reports/inventory_report.txt | 15 + 9 files changed, 465 insertions(+) create mode 100644 .idea/InventoryManager.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 data/products.csv create mode 100644 inventory_manager.py create mode 100644 reports/inventory_report.txt diff --git a/.idea/InventoryManager.iml b/.idea/InventoryManager.iml new file mode 100644 index 0000000..3d53442 --- /dev/null +++ b/.idea/InventoryManager.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..7a68218 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..5ca585a --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..94d17da --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + 1775248363717 + + + + \ No newline at end of file diff --git a/data/products.csv b/data/products.csv new file mode 100644 index 0000000..309a119 --- /dev/null +++ b/data/products.csv @@ -0,0 +1,9 @@ +product_id,name,category,quantity,price,supplier +1,Ноутбук Lenovo ThinkPad,Электроника,15,85000,ООО Компьютерные технологии +2,Мышь Logitech MX Master,Электроника,45,12000,ООО Компьютерные технологии +3,Стул офисный Ergohuman,Мебель,8,35000,МебельПро +4,Монитор Dell UltraSharp,Электроника,12,45000,ООО Компьютерные технологии +5,Клавиатура Mechanical,Электроника,0,8000,Аксессуары Плюс +6,Стол письменный,Мебель,5,12000,МебельПро +7,Блокнот A5,Канцелярия,120,250,КанцТрейд +8,Ручка шариковая,Канцелярия,500,50,КанцТрейд diff --git a/inventory_manager.py b/inventory_manager.py new file mode 100644 index 0000000..b9e51db --- /dev/null +++ b/inventory_manager.py @@ -0,0 +1,366 @@ +""" +Модуль управления складскими запасами интернет-магазина +Содержит функции для работы с товарами, фильтрации, анализа и отчётности +""" + +import csv +import os +from typing import List, Dict, Optional +from collections import Counter + + +def load_products(filepath: str) -> List[Dict]: + """ + Загружает данные о товарах из CSV-файла. + + Args: + filepath: Путь к CSV-файлу + + Returns: + Список словарей с данными о товарах. При отсутствии файла возвращает пустой список + """ + products = [] + + try: + with open(filepath, 'r', encoding='utf-8') as file: + reader = csv.DictReader(file) + for row in reader: + product = { + 'product_id': int(row['product_id']), + 'name': row['name'], + 'category': row['category'], + 'quantity': int(row['quantity']), + 'price': float(row['price']), + 'supplier': row['supplier'] + } + products.append(product) + except FileNotFoundError: + print(f"Ошибка: Файл {filepath} не найден") + return [] + except Exception as e: + print(f"Ошибка при чтении файла: {e}") + return [] + + return products + + +def validate_product(product: Dict) -> bool: + """ + Проверяет корректность данных товара. + + Args: + product: Словарь с данными товара + + Returns: + True, если все поля присутствуют и корректны, иначе False + """ + required_fields = ['product_id', 'name', 'category', 'quantity', 'price', 'supplier'] + + for field in required_fields: + if field not in product: + return False + + try: + if not isinstance(product['product_id'], int) or product['product_id'] <= 0: + return False + + if not isinstance(product['quantity'], int) or product['quantity'] < 0: + return False + + if not isinstance(product['price'], (int, float)) or product['price'] <= 0: + return False + + if not isinstance(product['name'], str) or not product['name'].strip(): + return False + + if not isinstance(product['category'], str) or not product['category'].strip(): + return False + + if not isinstance(product['supplier'], str) or not product['supplier'].strip(): + return False + except (TypeError, ValueError): + return False + + return True + + +def filter_by_category(products: List[Dict], category: str) -> List[Dict]: + """ + Фильтрует товары по категории (регистронезависимо). + + Args: + products: Список товаров + category: Название категории для фильтрации + + Returns: + Новый список товаров указанной категории + """ + return [product for product in products + if product['category'].lower() == category.lower()] + + +def filter_by_quantity(products: List[Dict], min_quantity: int) -> List[Dict]: + """ + Фильтрует товары с остатком не ниже заданного. + + Args: + products: Список товаров + min_quantity: Минимальное количество + + Returns: + Отфильтрованный список товаров + """ + return [product for product in products + if product['quantity'] >= min_quantity] + + +def calculate_total_value(products: List[Dict]) -> float: + """ + Вычисляет общую стоимость всех товаров на складе. + + Args: + products: Список товаров + + Returns: + Сумма произведений quantity * price для каждого товара + """ + return sum(product['quantity'] * product['price'] for product in products) + + +def find_low_stock(products: List[Dict], threshold: int) -> List[Dict]: + """ + Находит товары с критически низким остатком. + + Args: + products: Список товаров + threshold: Пороговое значение + + Returns: + Список товаров с quantity <= threshold, отсортированный по возрастанию остатка + """ + low_stock = [product for product in products if product['quantity'] <= threshold] + return sorted(low_stock, key=lambda x: x['quantity']) + + +def group_by_supplier(products: List[Dict]) -> Dict[str, Dict]: + """ + Группирует товары по поставщикам. + + Args: + products: Список товаров + + Returns: + Словарь, где ключ — поставщик, значение — словарь со статистикой + """ + suppliers = {} + + for product in products: + supplier = product['supplier'] + if supplier not in suppliers: + suppliers[supplier] = { + 'total_products': 0, + 'total_value': 0.0 + } + + suppliers[supplier]['total_products'] += 1 + suppliers[supplier]['total_value'] += product['quantity'] * product['price'] + + return suppliers + + +def get_most_expensive_product(products: List[Dict]) -> Optional[Dict]: + """ + Находит самый дорогой товар. + + Args: + products: Список товаров + + Returns: + Словарь товара с максимальной ценой, при пустом списке возвращает None + """ + if not products: + return None + + return max(products, key=lambda x: x['price']) + + +def search_by_name(products: List[Dict], keyword: str) -> List[Dict]: + """ + Ищет товары по ключевому слову в названии (регистронезависимо). + + Args: + products: Список товаров + keyword: Ключевое слово для поиска + + Returns: + Список товаров, содержащих ключевое слово в названии + """ + keyword_lower = keyword.lower() + return [product for product in products + if keyword_lower in product['name'].lower()] + + +def save_inventory_report(products: List[Dict], filepath: str) -> bool: + """ + Сохраняет инвентаризационный отчёт в текстовый файл. + + Args: + products: Список товаров + filepath: Путь для сохранения отчёта + + Returns: + True при успешной записи, False при ошибке + """ + try: + os.makedirs(os.path.dirname(filepath), exist_ok=True) + + with open(filepath, 'w', encoding='utf-8') as file: + total_products = len(products) + total_value = calculate_total_value(products) + + file.write("ИНВЕНТАРИЗАЦИОННЫЙ ОТЧЁТ\n") + file.write("=" * 50 + "\n\n") + file.write(f"Общее количество товаров: {total_products}\n") + file.write(f"Общая стоимость склада: {total_value:,.2f} руб.\n\n") + + categories = Counter([p['category'] for p in products]) + top_categories = categories.most_common(3) + + file.write("Топ-3 категории по количеству товаров:\n") + for i, (category, count) in enumerate(top_categories, 1): + file.write(f" {i}. {category}: {count} товаров\n") + + low_stock_items = [p for p in products if p['quantity'] < 10] + if low_stock_items: + file.write(f"\nТовары с остатком менее 10 единиц:\n") + for item in low_stock_items: + file.write(f" - {item['name']}: {item['quantity']} шт.\n") + else: + file.write(f"\nТоваров с остатком менее 10 единиц нет\n") + + return True + except Exception as e: + print(f"Ошибка при сохранении отчёта: {e}") + return False + + +def create_products_csv(): + """ + Создаёт файл data/products.csv с тестовыми данными + """ + os.makedirs('data', exist_ok=True) + + data = [ + ['product_id', 'name', 'category', 'quantity', 'price', 'supplier'], + [1, 'Ноутбук Lenovo ThinkPad', 'Электроника', 15, 85000, 'ООО Компьютерные технологии'], + [2, 'Мышь Logitech MX Master', 'Электроника', 45, 12000, 'ООО Компьютерные технологии'], + [3, 'Стул офисный Ergohuman', 'Мебель', 8, 35000, 'МебельПро'], + [4, 'Монитор Dell UltraSharp', 'Электроника', 12, 45000, 'ООО Компьютерные технологии'], + [5, 'Клавиатура Mechanical', 'Электроника', 0, 8000, 'Аксессуары Плюс'], + [6, 'Стол письменный', 'Мебель', 5, 12000, 'МебельПро'], + [7, 'Блокнот A5', 'Канцелярия', 120, 250, 'КанцТрейд'], + [8, 'Ручка шариковая', 'Канцелярия', 500, 50, 'КанцТрейд'] + ] + + with open('data/products.csv', 'w', encoding='utf-8', newline='') as file: + writer = csv.writer(file) + writer.writerows(data) + + print("✓ Файл data/products.csv успешно создан!") + + +def main(): + """ + Главная функция программы, выполняющая все шаги по анализу складских запасов. + """ + print("=" * 60) + print("УПРАВЛЕНИЕ СКЛАДСКИМИ ЗАПАСАМИ ИНТЕРНЕТ-МАГАЗИНА") + print("=" * 60) + + print("\n[Подготовка] Создание файла с данными...") + create_products_csv() + + print("\n1. Загрузка данных...") + products = load_products('data/products.csv') + + if not products: + print("Не удалось загрузить данные. Программа завершена.") + return + + print(f"Загружено товаров: {len(products)}") + + print("\n2. Проверка корректности данных...") + valid_products = [] + invalid_count = 0 + + for product in products: + if validate_product(product): + valid_products.append(product) + else: + invalid_count += 1 + print(f" Некорректный товар: {product.get('name', 'Неизвестно')}") + + print(f"Корректных товаров: {len(valid_products)}") + print(f"Некорректных записей пропущено: {invalid_count}") + + if not valid_products: + print("Нет корректных товаров для анализа. Программа завершена.") + return + + print("\n3. Общая статистика склада:") + print(f" Общее количество товаров на складе: {len(valid_products)}") + total_value = calculate_total_value(valid_products) + print(f" Общая стоимость склада: {total_value:,.2f} руб.") + + print("\n4. Товары с низким остатком (порог 10 ед.):") + low_stock_items = find_low_stock(valid_products, 10) + if low_stock_items: + for item in low_stock_items: + print(f" - {item['name']}: {item['quantity']} шт.") + else: + print(" Товаров с низким остатком нет") + + print("\n5. Анализ категории 'Электроника':") + electronics = filter_by_category(valid_products, "Электроника") + print(f" Количество товаров в категории: {len(electronics)}") + + most_expensive = get_most_expensive_product(electronics) + if most_expensive: + print(f" Самый дорогой товар: {most_expensive['name']} " + f"({most_expensive['price']:,.2f} руб.)") + else: + print(" Товаров в категории нет") + + print("\n6. Товары с остатком более 50 единиц:") + high_stock = filter_by_quantity(valid_products, 51) + print(f" Количество таких товаров: {len(high_stock)}") + + print("\n7. Поиск товаров по ключевому слову 'ноутбук':") + search_results = search_by_name(valid_products, "ноутбук") + if search_results: + for product in search_results: + print(f" - {product['name']}") + else: + print(" Товары не найдены") + + print("\n8. Статистика по поставщикам:") + suppliers_stats = group_by_supplier(valid_products) + for supplier, stats in suppliers_stats.items(): + print(f" {supplier}:") + print(f" - Количество товаров: {stats['total_products']}") + print(f" - Общая стоимость: {stats['total_value']:,.2f} руб.") + + print("\n9. Сохранение инвентаризационного отчёта...") + report_path = 'reports/inventory_report.txt' + if save_inventory_report(valid_products, report_path): + print(f" Отчёт успешно сохранён в файл: {report_path}") + else: + print(" Ошибка при сохранении отчёта") + + print("\n" + "=" * 60) + print("ПРОГРАММА УСПЕШНО ЗАВЕРШЕНА") + print("=" * 60) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/reports/inventory_report.txt b/reports/inventory_report.txt new file mode 100644 index 0000000..e048a8f --- /dev/null +++ b/reports/inventory_report.txt @@ -0,0 +1,15 @@ +ИНВЕНТАРИЗАЦИОННЫЙ ОТЧЁТ +================================================== + +Общее количество товаров: 8 +Общая стоимость склада: 2,750,000.00 руб. + +Топ-3 категории по количеству товаров: + 1. Электроника: 4 товаров + 2. Мебель: 2 товаров + 3. Канцелярия: 2 товаров + +Товары с остатком менее 10 единиц: + - Стул офисный Ergohuman: 8 шт. + - Клавиатура Mechanical: 0 шт. + - Стол письменный: 5 шт.