Skip to content

Repository files navigation

stock_api

стек

  • python
  • fastapi
  • pydantic
  • uvicorn
  • postgresql
  • alembic
  • sqlalchemy
  • pytest

что внутри

  • jwt логин по email/password
  • профиль текущего пользователя через GET /api/auth/users/me
  • список товаров
  • создание резерва
  • просмотр своих резервов
  • отмена своего активного резерва
  • создание товаров админом
  • просмотр всех резервов админом
  • подтверждение отгрузки админом
  • фильтр резервов по статусу
  • пагинация списка резервов
  • внешний reservation_key
  • структурированные ошибки для конфликтов
  • uuid вместо числовых id
  • время в ответах и моделях в часовом поясе Europe/Moscow

структура

app/
  api/
  core/
  db/
  schemas/
  services/
alembic/
tests/

переменные окружения

создай .env на основе .env.example

DATABASE_URL=postgresql+psycopg://postgres:postgres@db:5432/warehouse
SECRET_KEY=supersecretkey
ACCESS_TOKEN_EXPIRE_MINUTES=60

быстрый старт через docker compose

cp .env.example .env
make up

сервис будет доступен на http://localhost:8000

swagger будет доступен на http://localhost:8000/docs

локальный запуск без docker

нужны python 3.12 и postgresql

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
alembic upgrade head
uvicorn app.main:app --reload

make команды

  • make up-d — поднять postgres и приложение
  • make down — остановить и удалить контейнеры и volume
  • make build — пересобрать контейнер приложения
  • make logs — смотреть логи приложения
  • make test — запустить pytest
  • make db — запустить только postgres

демо данные

после make up и автоприменения миграций будут доступны

  • admin: admin@example.com / admin123
  • user: user@example.com / user123
  • товары: ноутбук, сканер

основные ручки

auth

  • POST /api/auth/jwt/login
  • GET /api/auth/users/me

пример логина

curl -X POST http://localhost:8000/api/auth/jwt/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"admin@example.com","password":"admin123"}'

products

  • POST /api/products — только admin
  • GET /api/products — любой авторизованный пользователь

пример создания товара

curl -X POST http://localhost:8000/api/products \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <access_token>' \
  -d '{"name":"терминал сбора данных","total_quantity":12}'

reservations

  • POST /api/reservations
  • GET /api/reservations/my?status=active&limit=20&offset=0
  • POST /api/reservations/{reservation_uuid}/cancel
  • GET /api/reservations?status=active&limit=20&offset=0 — только admin
  • POST /api/reservations/{reservation_uuid}/ship — только admin

пример создания резерва

curl -X POST http://localhost:8000/api/reservations \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer <access_token>' \
  -d '{"product_uuid":"<product_uuid>","quantity":2}'

как считаются остатки

  • у товара есть total_quantity и available_quantity
  • при создании активного резерва available_quantity уменьшается на quantity
  • при отмене активного резерва available_quantity увеличивается обратно
  • при отгрузке резерв переводится в shipped, доступный остаток не меняется, потому что товар уже был снят с доступного остатка в момент резерва
  • резерв можно закрыть только один раз

кто что может делать

обычный пользователь

  • войти и получить access token
  • смотреть свой профиль
  • смотреть список товаров
  • создать резерв
  • смотреть только свои резервы
  • отменять только свои активные резервы

admin

  • все действия обычного пользователя
  • создавать товары
  • смотреть все резервы
  • подтверждать отгрузку любого активного резерва

ошибки

для конфликтов используется структурированный формат

{
  "detail": {
    "code": "insufficient_stock",
    "message": "requested quantity exceeds available stock",
    "context": {
      "product_uuid": "...",
      "requested_quantity": 11,
      "available_quantity": 10
    }
  }
}

pytest

make test

покрыты ключевые сценарии

  • успешный логин и me
  • неверный пароль
  • невалидный bearer token
  • создание товара админом и запрет для обычного пользователя
  • создание и отмена своего резерва
  • конфликт при нехватке остатка
  • запрет на работу с чужим резервом
  • отгрузка админом и запрет повторного закрытия
  • фильтр по статусу и пагинация
  • ошибка для несуществующего товара
  • ошибка для несуществующего резерва
  • валидация невалидного количества

сценарии ручной проверки

  1. залогиниться под admin@example.com, создать товар и убедиться что available_quantity == total_quantity
  2. залогиниться под user@example.com, получить список товаров и создать резерв на доступное количество
  3. проверить GET /api/reservations/my и убедиться что у резерва есть reservation_key
  4. отменить свой активный резерв и убедиться что доступный остаток у товара восстановился
  5. попробовать создать резерв больше остатка и получить 409 insufficient_stock
  6. попробовать отменить чужой резерв и получить 403
  7. под админом отгрузить активный резерв, затем повторить отгрузку и получить 409 reservation_closed

About

api складских резервов и отгрузки / probation stuff pt.8

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages