Initial commit

This commit is contained in:
Елизавета Данилова 2026-04-04 00:27:11 +03:00
commit f4d7e204d7
9 changed files with 465 additions and 0 deletions

10
.idea/InventoryManager.iml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13 (InventoryManager)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (InventoryManager)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/InventoryManager.iml" filepath="$PROJECT_DIR$/.idea/InventoryManager.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

41
.idea/workspace.xml generated Normal file
View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="b32e6acd-5da1-46f8-a48d-12dc1410b7d6" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 3
}]]></component>
<component name="ProjectId" id="3BrXroiobVyuVOwKhaCCNo1Tl0n" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true"
}
}]]></component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-python-sdk-02acdf704d18-cbfc7b97e6b2-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-252.28539.27" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="b32e6acd-5da1-46f8-a48d-12dc1410b7d6" name="Changes" comment="" />
<created>1775248363717</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1775248363717</updated>
</task>
<servers />
</component>
</project>

9
data/products.csv Normal file
View File

@ -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,КанцТрейд
1 product_id name category quantity price supplier
2 1 Ноутбук Lenovo ThinkPad Электроника 15 85000 ООО Компьютерные технологии
3 2 Мышь Logitech MX Master Электроника 45 12000 ООО Компьютерные технологии
4 3 Стул офисный Ergohuman Мебель 8 35000 МебельПро
5 4 Монитор Dell UltraSharp Электроника 12 45000 ООО Компьютерные технологии
6 5 Клавиатура Mechanical Электроника 0 8000 Аксессуары Плюс
7 6 Стол письменный Мебель 5 12000 МебельПро
8 7 Блокнот A5 Канцелярия 120 250 КанцТрейд
9 8 Ручка шариковая Канцелярия 500 50 КанцТрейд

366
inventory_manager.py Normal file
View File

@ -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()

View File

@ -0,0 +1,15 @@
ИНВЕНТАРИЗАЦИОННЫЙ ОТЧЁТ
==================================================
Общее количество товаров: 8
Общая стоимость склада: 2,750,000.00 руб.
Топ-3 категории по количеству товаров:
1. Электроника: 4 товаров
2. Мебель: 2 товаров
3. Канцелярия: 2 товаров
Товары с остатком менее 10 единиц:
- Стул офисный Ergohuman: 8 шт.
- Клавиатура Mechanical: 0 шт.
- Стол письменный: 5 шт.