Task Description
Кейс:
Имеется 3 поставщика, каждый из поставщиков может поставлять 2 вида груш и 2 вида яблок. Поставщики заранее сообщают свои цены на виды продукции на определенный период поставок.
Задача:
- Создать интерфейс приёмки поставок от поставщиков. В одной поставке от поставщика может быть несколько видов продукции.
- Создать отчёт. За выбранный период показать поступление видов продукции по поставщикам с итогами по весу и стоимости.
Требования:
- Данные приложения должны сохранятся в БД путем формирования таблиц из объектов Backend (СУБД PostgreSQL).
- Backend – Java 17+, Spring Boot 3, Spring Data JPA (Hibernate). Требования задачи реализуются на уровне API, фронтенд необязателен.
- Нам важно получить исходные коды программы + работающее мини приложение. Выложить все файлы в GitHub или GitLab.
- Поставщики и продукты: вместо жёстко зашитых 3 поставщиков и 2×2 типов продуктов сделаны полноценные CRUD‑эндпоинты для:
- поставщиков (
/api/v1/supplier) - продуктов (
/api/v1/product)
- поставщиков (
- Имя поставщика по ИНН: если при создании поставщика поле
supplierNameпустое/пробельное, сервис:- запрашивает данные во внешнем API ФНС по ИНН (
taxIdNumber) - берёт полное наименование компании из ответа и сохраняет его как
supplierName
- запрашивает данные во внешнем API ФНС по ИНН (
- Отчёт:
- доступен response (
GET /api/v1/report) - и как CSV‑файл (
GET /api/v1/report/export?fileType=csv)
- доступен response (
- Supplier: поставщик (id, имя, ИНН, уникальный ИНН).
- Product: продукт (id, название, тип: груша/яблоко и т.п.).
- PriceOffer: ценовое предложение поставщика на продукт с периодом действия.
- Delivery: поставка от поставщика в дату, содержит набор DeliveryItem.
- DeliveryItem: строка поставки (продукт, вес в кг, фактическая цена за кг на дату поставки).
- SupplierService: CRUD по поставщикам, интеграция с ФНС при пустом имени.
- ProductService: CRUD по продуктам.
- PriceOfferService: создание ценовых предложений, поиск действующей цены по дате.
- DeliveryService: создание поставки:
- подтягивает действующую цену из
PriceOffer - считает хэш поставки, чтобы не допускать дублей.
- подтягивает действующую цену из
- ReportService: строит агрегированный отчёт по периодам и экспортирует его через стратегии (
FileExportStrategy).
Все эндпоинты начинаются с префикса /api/v1.
- POST
/api/v1/supplier- Вход:
SupplierRequestDTO { supplierName, taxIdNumber } - Если
supplierNameпустой → имя берётся из внешнего API ФНС поtaxIdNumber. - Выход:
SupplierDTO(id, имя, ИНН).
- Вход:
- GET
/api/v1/supplier/{supplierId}– получить поставщика по id. - PATCH
/api/v1/supplier/{supplierId}– частичное обновление имени/ИНН. - DELETE
/api/v1/supplier/{supplierId}– удалить поставщика.
- POST
/api/v1/product– создать продукт (ProductRequestDTO { productName, productType }). - GET
/api/v1/product/{productId}– получить продукт. - PATCH
/api/v1/product/{productId}– частичное обновление. - DELETE
/api/v1/product/{productId}– удалить.
- POST
/api/v1/price-offer- Вход:
PriceOfferRequestDTO { supplierId, productId, validPricePerKg, validFrom, validTo } - Проверяется пересечение периодов; при пересечении выбрасывается ошибка.
- Выход:
PriceOfferDTOс сохранённым предложением.
- Вход:
- GET
/api/v1/price-offer/{priceOfferId}– получить ценовое предложение по id.
- POST
/api/v1/delivery- Вход:
DeliveryRequestDTO { supplierId, deliveryDate, deliveryItems[] }, гдеdeliveryItemsсодержит{ productId, weightInKg }. - Для каждой строки подбирается действующая цена из
PriceOfferна датуdeliveryDate. - Выход:
DeliveryDTOс рассчитанными позициями.
- Вход:
- GET
/api/v1/delivery/{deliveryId}– получить поставку по id.
- GET
/api/v1/report?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD- Выход:
DeliveryReportDTO:- список продуктов с суммарным весом/стоимостью по каждому
- разбивка по поставщикам внутри продукта
- общие итоги по весу и сумме.
- Выход:
- GET
/api/v1/report/export?startDate=...&endDate=...&fileType=csv- Выход:
Content-Disposition: attachment; filename="...", тело – CSV‑файл с тем же содержимым, что и JSON‑отчёт.
- Выход:
- Используется клиент
FnsApiClient(OpenFeign). - Запрос происходит только при создании поставщика, если имя не задано.
- При отсутствии данных по ИНН возвращается ошибка уровня домена (поставщик с таким ИНН не найден, предложено добавить вручную).
- Важно: публичный API
api-fns.ruможет ограничивать число запросов с одного IP-адреса. При необходимости вы можете завести собственный аккаунт наhttps://api-fns.ru/.
- В проекте есть
Dockerfileиdocker-compose.yaml. - Шаги:
- Установить Docker и Docker Compose.
- В корне проекта выполнить:
docker compose up --build
- Будут подняты:
- контейнер с PostgreSQL
- контейнер с приложением Spring Boot.
- По умолчанию приложение доступно на порту 8080 (
http://localhost:8080). - Все миграции/схемы формируются автоматически через JPA по сущностям.
Если будет время до дедлайна;
- Реализовать постраничную навигацию на основе курсора с использованием JPA specifications.
- Покрыть ПО тестами (unit и интеграционные тесты).
Validation.