From 9eebcf670e3d3e8d79755926d958dd93d6ce9363 Mon Sep 17 00:00:00 2001 From: Amagasu Date: Wed, 18 Jun 2025 11:05:50 +0900 Subject: [PATCH 1/6] api/tobuy/addByRecipe can select stock-differ option --- .../todoapp/controller/ToBuysController.java | 7 +++--- .../example/todoapp/dto/AddByRecipeDTO.java | 25 +++++++++++++++++++ .../todoapp/service/ToBuysService.java | 19 +++++++++++--- 3 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/com/example/todoapp/dto/AddByRecipeDTO.java diff --git a/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java index e263330..35844f7 100644 --- a/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java +++ b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java @@ -7,6 +7,7 @@ package com.example.todoapp.controller; +import com.example.todoapp.dto.AddByRecipeDTO; import com.example.todoapp.dto.BuyRequestDTO; import com.example.todoapp.dto.DeleteToBuyRequestDTO; import com.example.todoapp.dto.ToBuyResponseDTO; @@ -181,12 +182,10 @@ public class ToBuysController { */ @PostMapping("/addByRecipe") public ResponseEntity> addByRecipe( - @RequestBody Map payload, + @RequestBody AddByRecipeDTO addByRecipeDTO, Authentication authentication) { - Long recipeId = payload.get("recipeId"); - Long servings = payload.get("servings"); - List responseList = toBuysService.addByRecipeId(recipeId, servings,authentication); + List responseList = toBuysService.addByRecipeId(addByRecipeDTO.getRecipeId(), addByRecipeDTO.getServings(), addByRecipeDTO.getDifference(), authentication); //shopのフィールドを削除 List> filteredList = responseList.stream() diff --git a/backend/src/main/java/com/example/todoapp/dto/AddByRecipeDTO.java b/backend/src/main/java/com/example/todoapp/dto/AddByRecipeDTO.java new file mode 100644 index 0000000..5fef152 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/dto/AddByRecipeDTO.java @@ -0,0 +1,25 @@ +//-------------------------------- +// AddByRecipeDTO.java +// +// +// 更新履歴:2025/06/18 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- +package com.example.todoapp.dto; + +import lombok.Data; + +/** + * 料理からの買うものリスト追加データ転送オブジェクト(DTO) + *

+ * このクラスはクライアントとサーバー間で料理情報をやり取りするために使用されます。 + * エンティティとは異なり、必要な情報のみを含み、関連エンティティへの参照ではなくIDのみを保持します。 + *

+ */ + +@Data +public class AddByRecipeDTO { + private Long recipeId; + private Long servings; + private Boolean difference; +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java index 2c0cc2a..d88d629 100644 --- a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java +++ b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java @@ -63,6 +63,9 @@ public class ToBuysService { @Autowired private RecipeStuffsRepository RecipeStuffsRepository; + @Autowired + private StocksService stocksService; + /** * 購入リストに新しいアイテムを追加する * @@ -222,7 +225,7 @@ public class ToBuysService { * @param authentication 認証情報 * @return 追加された「買うもの」のリスト */ - public List addByRecipeId(Long recipeId, Long servings,Authentication authentication) { + public List addByRecipeId(Long recipeId, Long servings, Boolean difference, Authentication authentication) { // ユーザー情報を取得 String username = authentication.getName(); User user = userRepository.findByUsername(username) @@ -236,13 +239,23 @@ public class ToBuysService { for (RecipeStuffs rs : recipeStuffsList) { Stuffs stuff = rs.getStuff(); - // 材料の数量をサービング数に基づいて計算 + // 材料の数量を在庫数とサービング数に基づいて計算 + int stockAmount = stocksService.calcAmountByStuffId(user.getId(), stuff.getStuffId()); int requiredAmount = rs.getAmount() * (servings != null ? servings.intValue() : 1); + if (difference) { + // 差分を計算 + requiredAmount = requiredAmount - stockAmount; + if (requiredAmount <= 0) { + continue; // 在庫が十分なので追加しない + } + } else { + // 差分を考慮しない場合はrequiredAmountをそのまま使用 + } Optional existingToBuyOpt = toBuysRepository.findByUserAndStuff(user, stuff); ToBuys toBuy; - if (existingToBuyOpt.isPresent()) { + if (existingToBuyOpt.isPresent()) { // 既存の「買うもの」がある場合 toBuy = existingToBuyOpt.get(); toBuy.setAmount(toBuy.getAmount() + requiredAmount); // 既存の数量を更新 } else { From 1fb9ffcbf959c39686eb34a40c5e716db0db511f Mon Sep 17 00:00:00 2001 From: Amagasu Date: Wed, 18 Jun 2025 11:14:47 +0900 Subject: [PATCH 2/6] update api.ts --- frontend/src/services/api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index e6f0442..8aacbc6 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -132,11 +132,11 @@ export const toBuyApi = { }, - addByRecipe: async (recipeId: number, servings: number): Promise => { + addByRecipe: async (recipeId: number, servings: number, difference: boolean): Promise => { const response = await fetch(`${API_BASE_URL}/api/tobuy/addByRecipe`, { method: 'POST', headers: getHeaders(), - body: JSON.stringify({ recipeId, servings }), + body: JSON.stringify({ recipeId, servings, difference }), }) if (!response.ok) { throw new Error(TOBUY_ERRORS.CREATE_FAILED); From 90e2fffb17379cbe8f12865f692551140b67f069 Mon Sep 17 00:00:00 2001 From: "masato.fujita" Date: Wed, 18 Jun 2025 11:22:46 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E5=9C=A8=E5=BA=AB=E5=88=86=E6=B8=9B?= =?UTF-8?q?=E3=82=89=E3=81=99=E6=A9=9F=E8=83=BD(=E6=9C=AA=E5=AE=8C?= =?UTF-8?q?=E6=88=90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 75678cf..600ab79 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -18,6 +18,8 @@ import { DialogActions, TextField, Button, + Checkbox, + FormControlLabel, Box, } from '@mui/material'; import { @@ -27,7 +29,7 @@ import { import AddStuffAmountDialog from '../components/AddStuffAmountDialog'; import { StuffAndCategoryAndAmount } from '../types/types'; import EditAmountDialog from '../components/EditAmountDialog'; -import { recipeApi, toBuyApi } from '../services/api'; +import { recipeApi, toBuyApi, stockApi } from '../services/api'; import { useNavigate, useParams } from 'react-router-dom'; import MessageAlert from '../components/MessageAlert'; import { useMessage } from '../components/MessageContext'; @@ -60,6 +62,8 @@ const AddRecipe: React.FC = () => { const [editingItemIdx, setEditingItemIdx] = useState(0); //削除確認ダイアログの表示状態 const [openDeleteDialog, setOpenDeleteDialog] = useState(false); + // チェックボックスが押されたかどうか + const [checked, setChecked] = useState(false); // エラーメッセージ表示 const { showErrorMessage, showSuccessMessage } = useMessage(); @@ -150,7 +154,25 @@ const AddRecipe: React.FC = () => { console.log("yes2"); return false; } - await toBuyApi.addByRecipe(recipeId, numOfPeaple); + await toBuyApi.addByRecipe(recipeId, numOfPeaple, checked); + // const recipeStuffInfo = (await recipeApi.getById(recipeId)).stuffAndAmountArray; + // const recipeStuffId = recipeStuffInfo.map(item => item.stuffId); + // console.log(recipeStuffId); + // const stockStuff = await stockApi.getStocks(); + // const stockedStuffId = stockStuff.filter(stock => recipeStuff.stuffAndAmountArray).map(item => item.id); + // const stockedStuffAmountRecipe = recipeStuff.filter(recipe => (stockStuff.some(stuff => recipe.stuffId === stuff.stuffId))); + // console.log(stockedStuffAmountRecipe) + // const stockedStuffAmountStock = stockStuff.filter(stock => (stockedStuffAmountRecipe.some(stocked => stock.stuffId === stocked.stuffId))); + + // recipeStuff.map(item => { + // await Promise.all(stockStuff.map(async stock => { + // if (item.stuffId == stock.stuffId) + // await toBuyApi.updateToBuy({stuffName: item.stuffName, + // amount: item.amount - stock.amount, + // tobuyId: item.tobuyId, + // stuffId: item.stuffId}); + // })) + // }); showSuccessMessage('レシピが保存されて買うものリストに追加されました!'); navigate('/tasks'); } @@ -337,6 +359,10 @@ const AddRecipe: React.FC = () => { /> + setChecked(e.target.checked)} />} + label={足りない食材のみ登録} + /> From 3e16bc22d6048c375bfab2f4305bbcf86f49f833 Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Wed, 18 Jun 2025 11:47:25 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=E8=B3=BC=E5=85=A5=E5=87=A6=E7=90=86?= =?UTF-8?q?=E3=81=A7=E8=B3=BC=E5=85=A5=E6=95=B0=E9=87=8F=E3=81=8C=E4=BA=88?= =?UTF-8?q?=E5=AE=9A=E6=95=B0=E9=87=8F=E3=81=AB=E6=BA=80=E3=81=9F=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=A0=B4=E5=90=88=EF=BC=8C=E6=AE=8B=E3=82=8A=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E3=82=92=E8=B2=B7=E3=81=86=E3=82=82=E3=81=AE=E3=83=AA?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=81=AB=E6=AE=8B=E3=81=99=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../todoapp/repository/ToBuysRepository.java | 11 ++++++++++ .../todoapp/service/ToBuysService.java | 22 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java b/backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java index 0778f5f..24d464f 100644 --- a/backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java +++ b/backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java @@ -47,6 +47,17 @@ public interface ToBuysRepository extends JpaRepository { */ @Query("SELECT t FROM ToBuys t WHERE t.user.id = :userId ORDER BY t.tobuyId ASC") List findByUserIdOrderByTobuyIdAsc(@Param("userId") Long userId); + + /** + * 指定された「買うもの」IDに基づいて「買うもの」での数量を更新 + * + * @param tobuyId 「買うもの」ID + * @param amount 「買うもの」数量 + * @return 更新された行数 + */ + @Modifying + @Query("UPDATE ToBuys t SET t.amount = :amount WHERE t.tobuyId = :tobuyId") + int updateAmountByTobuyId(Long tobuyId, int amount); /** * 指定された「買うもの」IDに基づいて「買うもの」リストを削除 diff --git a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java index 2c0cc2a..323a447 100644 --- a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java +++ b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java @@ -171,6 +171,16 @@ public class ToBuysService { return toBuysRepository.findByUserIdOrderByTobuyIdAsc(user.getId()); } + /** + * 指定された購入リストIDに基づいて「数量」を変更する + * + * @param tobuyId 購入リストID + */ + @Transactional + public int updateToBuysAmountByTobuyId(Long tobuyId, int amount) { + return toBuysRepository.updateAmountByTobuyId(tobuyId, amount); + } + /** * 指定された購入リストIDに基づいて「買うもの」を削除する * @@ -207,9 +217,17 @@ public class ToBuysService { stock.setBuyDate(dto.getBuyDate()); stock.setExpDate(dto.getExpDate()); - // 買うものリストから削除 + // まだ買うべき数量を計算 + int remainAmount = Math.max(tobuy.getAmount() - dto.getAmount(), 0); + System.out.println("remainAmount=" + remainAmount); + + // 買うものリストから削除または数量変更 System.out.println("tobuy.getTobuyId()=" + tobuy.getTobuyId()); - deleteToBuysByTobuyId(tobuy.getTobuyId()); + if (remainAmount > 0) { + updateToBuysAmountByTobuyId(tobuy.getTobuyId(), remainAmount); + } else { + deleteToBuysByTobuyId(tobuy.getTobuyId()); + } // データベースに保存 return stocksRepository.save(stock); From 1a62ee732865a6646593737b388b7a829a977187 Mon Sep 17 00:00:00 2001 From: "masato.fujita" Date: Wed, 18 Jun 2025 13:08:03 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=E5=9C=A8=E5=BA=AB=E5=88=86=E7=99=BB?= =?UTF-8?q?=E9=8C=B2=E3=81=99=E3=82=8B=E9=87=8F=E3=82=92=E6=B8=9B=E3=82=89?= =?UTF-8?q?=E3=81=99=E6=A9=9F=E8=83=BD=E3=81=AE=E5=AE=9F=E8=A3=85=E3=80=81?= =?UTF-8?q?=E3=81=99=E3=81=A7=E3=81=AB=E5=9C=A8=E5=BA=AB=E3=81=AB=E7=99=BB?= =?UTF-8?q?=E9=8C=B2=E5=88=86=E3=81=82=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=9D?= =?UTF-8?q?=E3=82=8C=E3=82=92=E3=83=A6=E3=83=BC=E3=82=B6=E3=81=AB=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E3=81=97=E3=81=A6=E3=83=AA=E3=82=B9=E3=83=88=E3=81=AB?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 600ab79..ecff456 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -154,7 +154,8 @@ const AddRecipe: React.FC = () => { console.log("yes2"); return false; } - await toBuyApi.addByRecipe(recipeId, numOfPeaple, checked); + const finalAddResult = await toBuyApi.addByRecipe(recipeId, numOfPeaple, checked); + console.log(finalAddResult) // const recipeStuffInfo = (await recipeApi.getById(recipeId)).stuffAndAmountArray; // const recipeStuffId = recipeStuffInfo.map(item => item.stuffId); // console.log(recipeStuffId); @@ -173,7 +174,12 @@ const AddRecipe: React.FC = () => { // stuffId: item.stuffId}); // })) // }); - showSuccessMessage('レシピが保存されて買うものリストに追加されました!'); + if (finalAddResult.data.length === 0) { + showSuccessMessage('必要な食材が在庫にあったのでリストには追加されませんでした!'); + } + else { + showSuccessMessage('レシピが保存されて買うものリストに追加されました!'); + } navigate('/tasks'); } From b708eb0612c09f91aa65d83105fd859ea55430a2 Mon Sep 17 00:00:00 2001 From: "Haru.Kusano" Date: Wed, 18 Jun 2025 13:30:24 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=AF=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Layout.tsx | 12 ++++++++---- frontend/src/pages/RecipeList.tsx | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index fe8bff1..7e1c73f 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -41,15 +41,19 @@ const Layout: React.FC = () => { const getTabIndex = (pathname: string) => { + pathname = pathname.split("/")[1]; + console.log(pathname); switch (pathname) { - case '/stock': + case 'stock': return 0; - case '/tasks': + case 'tasks': return 1; - case '/recipeList': + case 'recipeList': + return 2; + case 'addRecipe': return 2; default: - return 0; + return -1; } }; diff --git a/frontend/src/pages/RecipeList.tsx b/frontend/src/pages/RecipeList.tsx index f5f6f11..bcdce5a 100644 --- a/frontend/src/pages/RecipeList.tsx +++ b/frontend/src/pages/RecipeList.tsx @@ -102,7 +102,7 @@ const RecipeList: React.FC = () => { fontSize: "32px", left: "50%", transform: 'translateX(-50%)' }} color="primary" - onClick={() => navigate('/AddRecipe')} + onClick={() => navigate('/addRecipe')} > 新しい料理を追加