diff options
author | mrfoxygmfr <mrfoxygmfr@sch9.ru> | 2025-04-28 00:45:56 +0300 |
---|---|---|
committer | mrfoxygmfr <mrfoxygmfr@sch9.ru> | 2025-04-28 00:45:56 +0300 |
commit | e9cef81f89584110776c84ee511e26097eb8323c (patch) | |
tree | dabd0daf3bde7d61509d3e496dd50f9a5b4602cf /src | |
parent | facd0103437ffb50108113567d5fbb24487ffc81 (diff) |
feat: implement products controller + pages
Diffstat (limited to 'src')
4 files changed, 346 insertions, 0 deletions
diff --git a/src/main/java/ru/mrfoxygmfr/warehouse_accounting/http/controllers/ProductsController.java b/src/main/java/ru/mrfoxygmfr/warehouse_accounting/http/controllers/ProductsController.java new file mode 100644 index 0000000..b17ec47 --- /dev/null +++ b/src/main/java/ru/mrfoxygmfr/warehouse_accounting/http/controllers/ProductsController.java @@ -0,0 +1,132 @@ +package ru.mrfoxygmfr.warehouse_accounting.http.controllers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import ru.mrfoxygmfr.warehouse_accounting.db.dao.*; +import ru.mrfoxygmfr.warehouse_accounting.db.dao.specs.PartnerSpecs; +import ru.mrfoxygmfr.warehouse_accounting.db.dao.specs.ProductSlotSpecs; +import ru.mrfoxygmfr.warehouse_accounting.db.dao.specs.ProductSpecs; +import ru.mrfoxygmfr.warehouse_accounting.db.models.*; + +import java.time.Duration; +import java.util.List; + +@Controller +public class ProductsController { + @Autowired + private ProductDAO productDAO; + @Autowired + private ProductSlotDAO productSlotDAO; + + @GetMapping("products") + public String products(@RequestParam(name = "productName", required = false) String name, + @RequestParam(name = "productHeightLess", required = false) Integer heightLess, + @RequestParam(name = "productHeightGreater", required = false) Integer heightGreater, + @RequestParam(name = "productWidthLess", required = false) Integer widthLess, + @RequestParam(name = "productWidthGreater", required = false) Integer widthGreater, + @RequestParam(name = "productDepthLess", required = false) Integer depthLess, + @RequestParam(name = "productDepthGreater", required = false) Integer depthGreater, + Model model) { + Specification<Product> spec = Specification.where(null); + if (name != null && !name.isEmpty()) { + spec = spec.and(ProductSpecs.productNameLike(name)); + model.addAttribute("productNameFilter", name); + } + if (heightLess != null) { + spec = spec.and(ProductSpecs.productHeightLess(heightLess)); + model.addAttribute("productHeightLessFilter", heightLess); + } + if (heightGreater != null) { + spec = spec.and(ProductSpecs.productHeightGreater(heightGreater)); + model.addAttribute("productHeightGreaterFilter", heightGreater); + } + if (widthLess != null) { + spec = spec.and(ProductSpecs.productWidthLess(widthLess)); + model.addAttribute("productWidthLessFilter", widthLess); + } + if (widthGreater != null) { + spec = spec.and(ProductSpecs.productWidthGreater(widthGreater)); + model.addAttribute("productWidthGreaterFilter", widthGreater); + } + if (depthLess != null) { + spec = spec.and(ProductSpecs.productDepthLess(depthLess)); + model.addAttribute("productDepthLessFilter", depthLess); + } + if (depthGreater != null) { + spec = spec.and(ProductSpecs.productDepthGreater(depthGreater)); + model.addAttribute("productDepthGreaterFilter", depthGreater); + } + + List<Product> products = productDAO.findAll(spec); + model.addAttribute("products", products); + return "products"; + } + + @GetMapping("product") + public String product(@RequestParam(name = "id") Integer id, Model model) { + Product product = productDAO.findById(id).orElseThrow(); + model.addAttribute("product", product); + return "productEdit"; + } + + + @PostMapping("product") + public String product(@RequestParam(name = "productId") Integer id, + @RequestParam(name = "productName") String name, + @RequestParam(name = "productHeight") Integer height, + @RequestParam(name = "productWidth") Integer width, + @RequestParam(name = "productDepth") Integer depth, + @RequestParam(name = "productMaxStorageDuration", required = false) Integer maxStorageDurationInteger) { + Product product; + Duration maxStorageDuration = null; + if (maxStorageDurationInteger != null && maxStorageDurationInteger != 0) { + maxStorageDuration = Duration.ofDays(maxStorageDurationInteger); + } + if (id == -1) { + product = new Product(name, ProductType.UNKNOWN, height, width, depth); + product.setMaxStorageDuration(maxStorageDuration); + } else { + product = productDAO.findById(id).orElseThrow(); + product.setName(name); + product.setHeight(height); + product.setWidth(width); + product.setDepth(depth); + product.setMaxStorageDuration(maxStorageDuration); + } + productDAO.save(product); + return "redirect:/products"; + } + + @GetMapping("storage") + public String productsStorage(@RequestParam(name = "storageName", required = false) String name, + @RequestParam(name = "storageStatus", required = false) ProductStorageStatus storageStatus, + Model model) { + Specification<ProductSlot> spec = Specification.where(null); + if (name != null && !name.isEmpty()) { + spec = spec.and(ProductSlotSpecs.productSlotContainsLike(name)); + model.addAttribute("storageNameFilter", name); + } + if (storageStatus != null) { + spec = spec.and(ProductSlotSpecs.productStorageStatusEqual(storageStatus)); + model.addAttribute("storageStatusFilter", storageStatus.toString()); + } + + List<ProductSlot> storage = productSlotDAO.findAll(spec); + model.addAttribute("storage", storage); + return "storage"; + } + + @GetMapping("newProduct") + public String newProduct(Model model) { + Product product = new Product(); + product.setId(-1); + model.addAttribute("product", product); + model.addAttribute("newItem", true); + return "productEdit"; + } +} diff --git a/src/main/resources/templates/productEdit.html b/src/main/resources/templates/productEdit.html new file mode 100644 index 0000000..dac8ba6 --- /dev/null +++ b/src/main/resources/templates/productEdit.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org" lang="en"> +<div th:replace="~{common :: head}"></div> + +<body> +<div th:replace="~{common :: page-header}"></div> + +<div class="indent"> + <div id="updateToggleSelector"> + <button id="updateBtn" class="btn btn-primary" onclick="toggleDisabled()">Изменить</button> <br><br> + <a th:href="@{/operations(operationProductName=${product.getName()})}"> + <button class="btn btn-primary">Операции с товаром</button> + </a><br><br> + <a th:href="@{/storage(storageName=${product.getName()})}"> + <button class="btn btn-primary">К хранению товара</button> + </a><br><br> + </div> + + <form method="post" action="/product"> + <input disabled hidden id="productId" name="productId" th:value="${product.getId()}"> + + <label for="productName">Название:</label> + <input disabled type="text" id="productName" name="productName" required th:value="${product.getName()}"><br><br> + + <label for="productHeight">Высота:</label> + <input disabled type="text" id="productHeight" name="productHeight" required th:value="${product.getHeight()}"><br><br> + + <label for="productWidth">Ширина:</label> + <input disabled type="text" id="productWidth" name="productWidth" required th:value="${product.getWidth()}"><br><br> + + <label for="productDepth">Глубина:</label> + <input disabled type="text" id="productDepth" name="productDepth" required th:value="${product.getDepth()}"><br><br> + + <label for="productMaxStorageDuration">Срок хранения (дни):</label> + <input disabled type="text" id="productMaxStorageDuration" name="productMaxStorageDuration" th:value="${product.getMaxStorageDuration() == null ? '' : product.getMaxStorageDuration().toDays()}"><br><br> + + <input id="saveBtn" type="submit" value="Сохранить" class="btn btn-primary" hidden> + + </form> +</div> + + +<div th:replace="~{common :: site-footer}"></div> +<div th:replace="~{common :: site-script}"></div> +<div th:replace="~{common :: editFieldsToggle}"></div> + +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/products.html b/src/main/resources/templates/products.html new file mode 100644 index 0000000..4db7f21 --- /dev/null +++ b/src/main/resources/templates/products.html @@ -0,0 +1,84 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org" lang="en"> +<div th:replace="~{common :: head}"></div> + +<body> +<div th:replace="~{common :: page-header}"></div> + +<div class="indent"> + <form method="get" action="/newProduct"> + <button id="newOperationBtn" type="submit" class="btn btn-primary">Создать новый продукт</button> + </form> + <br> + + <form method="get" action="/products"> + <table class="table"> + <thead class="theme-dark"> + <tr> + <th colspan="6">Фильтры</th> + </tr> + </thead> + <tbody> + <tr> + <td>Название</td> + <td> + <input type="text" id="productNameFilter" name="productName" th:value="${productNameFilter}"> + </td> + </tr> + <tr> + <td>Высота</td> + <td> + от <input type="text" id="productHeightGreaterFilter" name="productHeightGreater" th:value="${productHeightGreaterFilter}"> + до <input type="text" id="productHeightLessFilter" name="productHeightLess" th:value="${productHeightLessFilter}"> + </td> + </tr> + <tr> + <td>Ширина</td> + <td> + от <input type="text" id="productWidthGreaterFilter" name="productWidthGreater" th:value="${productWidthGreaterFilter}"> + до <input type="text" id="productWidthLessFilter" name="productWidthLess" th:value="${productWidthLessFilter}"> + </td> + </tr> + <tr> + <td>Глубина</td> + <td> + от <input type="text" id="productDepthGreaterFilter" name="productDepthGreater" th:value="${productDepthGreaterFilter}"> + до <input type="text" id="productDepthLessFilter" name="productDepthLess" th:value="${productDepthLessFilter}"> + </td> + </tr> + <tr> + <td colspan="6"><input id="saveBtn" type="submit" value="Применить" class="btn btn-primary"></td> + </tr> + </tbody> + </table> + </form> + + <table class="table table-bordered table-warning"> + <thead class="thead-dark"> + <tr> + <th scope="col">Название</th> + <th scope="col">Габариты (В*Ш*Г)</th> + </tr> + </thead> + <tbody> + <tr th:if="${products.isEmpty()}"> + <td colspan="6">Данному фильтру не удовлетворяет ни одного продукта.</td> + </tr> + <tr th:each="product : ${products}"> + <td> + <a th:href="'/product?id=' + ${product.getId()}"> + <span th:text="${product.getName()}"></span> + </a> + </td> + <td> + <span th:text="${product.getHeight()} + ' * ' + ${product.getWidth()} + ' * ' + ${product.getDepth()}"></span> + </td> + </tr> + </tbody> + </table> +</div> + +<div th:replace="~{common :: site-footer}"></div> +<div th:replace="~{common :: site-script}"></div> +</body> +</html>
\ No newline at end of file diff --git a/src/main/resources/templates/storage.html b/src/main/resources/templates/storage.html new file mode 100644 index 0000000..975af0f --- /dev/null +++ b/src/main/resources/templates/storage.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML> +<html xmlns:th="http://www.thymeleaf.org" lang="en"> +<div th:replace="~{common :: head}"></div> + +<body> +<div th:replace="~{common :: page-header}"></div> + +<div class="indent"> + <form method="get" action="/storage"> + <table class="table"> + <thead class="theme-dark"> + <tr> + <th colspan="6">Фильтры</th> + </tr> + </thead> + <tbody> + <tr> + <td>Название</td> + <td> + <input type="text" id="storageNameFilter" name="storageName" th:value="${storageNameFilter}"> + </td> + </tr> + <tr> + <td>Статус</td> + <td> + <select id="storageStatusFilter" name="storageStatus"> + <option value="">Любой</option> + <option th:value="'RESERVED_FOR_SUPPLY'" th:text="RESERVED_FOR_SUPPLY" th:selected="${storageStatusFilter == 'RESERVED_FOR_SUPPLY'}"></option> + <option th:value="'PLACED'" th:text="PLACED" th:selected="${storageStatusFilter == 'PLACED'}"></option> + <option th:value="'RESERVED_FOR_ISSUE'" th:text="RESERVED_FOR_ISSUE" th:selected="${storageStatusFilter == 'RESERVED_FOR_ISSUE'}"></option> + <option th:value="'PROHIBITED'" th:text="PROHIBITED" th:selected="${storageStatusFilter == 'PROHIBITED'}"></option> + </select> + </td> + </tr> + <tr> + <td colspan="6"><input id="saveBtn" type="submit" value="Применить" class="btn btn-primary"></td> + </tr> + </tbody> + </table> + </form> + + <table class="table table-bordered table-warning"> + <thead class="thead-dark"> + <tr> + <th scope="col">Название</th> + <th scope="col">Локация</th> + <th scope="col">Количество</th> + <th scope="col">Время размещения</th> + <th scope="col">Статус хранения</th> + </tr> + </thead> + <tbody> + <tr th:if="${storage.isEmpty()}"> + <td colspan="6">Данному фильтру не удовлетворяет ни одного хранящегося продукта.</td> + </tr> + <tr th:each="productStorage : ${storage}"> + <td> + <a th:href="'/product?id=' + ${productStorage.getProduct().getId()}"> + <span th:text="${productStorage.getProduct().getName()}"></span> + </a> + </td> + <td> + <span th:text="${productStorage.getSlot().getLocation()}"></span> + </td> + <td> + <span th:text="${productStorage.getAmount()}"></span> + </td> + <td> + <span th:text="${productStorage.getPlacementTime()}"></span> + </td> + <td> + <span th:text="${productStorage.getStatus()}"></span> + </td> + </tr> + </tbody> + </table> +</div> + +<div th:replace="~{common :: site-footer}"></div> +<div th:replace="~{common :: site-script}"></div> +</body> +</html>
\ No newline at end of file |