Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

Commit 7911460

Browse files
authored
Merge pull request #134 from u-hossy/feature/payment-fetch-db
Feature/payment fetch db
2 parents 696c860 + 754b18a commit 7911460

File tree

5 files changed

+109
-35
lines changed

5 files changed

+109
-35
lines changed

backend/app/views.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,34 @@ def get_queryset(self):
119119
if event_code:
120120
qs = qs.filter(event_id=event_code)
121121
return qs
122+
123+
@action(detail=False, methods=["delete"])
124+
def delete_by_key(self, request):
125+
event_id = request.query_params.get("event_id")
126+
payment_id = request.query_params.get("payment_id")
127+
128+
deleted, _ = models.Payments.objects.filter(
129+
event_id=event_id,
130+
payment_id=payment_id
131+
).delete()
132+
133+
return Response({"status": "deleted"}, status=200)
134+
135+
@action(detail=False, methods=["patch"])
136+
def patch_by_key(self, request):
137+
event_id = request.data.get("event_id")
138+
payment_id = request.data.get("payment_id")
139+
140+
try:
141+
obj = models.Payments.objects.get(event_id=event_id, payment_id=payment_id)
142+
except models.Payments.DoesNotExist:
143+
return Response({"error": "payment not found"}, status=404)
144+
145+
serializer = self.get_serializer(obj, data=request.data, partial=True)
146+
serializer.is_valid(raise_exception=True)
147+
serializer.save()
148+
149+
return Response(serializer.data, status=200)
122150

123151

124152
class ResultsViewSet(viewsets.ModelViewSet):

frontend/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ function App() {
7171
<BillingPage
7272
members={members}
7373
payments={payments}
74+
setMembers={setMembers}
7475
setPayments={setPayments}
7576
/>
7677
}

frontend/src/components/BillingDetailCard.tsx

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect, useState } from "react";
2+
import { useParams } from "react-router-dom";
23
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
34
import { Input } from "@/components/ui/input";
45
import {
@@ -28,6 +29,7 @@ export default function BillingDetailCard({
2829
const [details, setDetails] = useState<
2930
Array<{ id: number; paidFor: number; amount: number | "" }>
3031
>([{ id: -1, paidFor: -1, amount: "" }]);
32+
const { eventId } = useParams();
3133

3234
// payerに関連するpaymentsをdetailsに反映
3335
useEffect(() => {
@@ -45,7 +47,7 @@ export default function BillingDetailCard({
4547
setDetails(newDetails);
4648
}, [payments, paidBy.id]);
4749

48-
const handleReceiverChange = (index: number, value: string) => {
50+
const handleReceiverChange = async (index: number, value: string) => {
4951
const newValue = value === "none" ? -1 : Number(value);
5052
setDetails((prev) => {
5153
const updated = [...prev];
@@ -67,6 +69,19 @@ export default function BillingDetailCard({
6769
};
6870
setPayments((prev) => [...prev, newPayment]);
6971

72+
await fetch("http://127.0.0.1:8000/api/v1/payments/", {
73+
method: "POST",
74+
headers: { "Content-Type": "application/json" },
75+
body: JSON.stringify({
76+
event_id: eventId,
77+
payment_id: newPayment.id,
78+
paid_by: newPayment.paidBy,
79+
paid_for: newPayment.paidFor,
80+
amount: 0,
81+
note: "",
82+
}),
83+
});
84+
7085
// detailsのidも更新
7186
setDetails((prev) => {
7287
const updated = [...prev];
@@ -86,17 +101,6 @@ export default function BillingDetailCard({
86101
const updated = [...prev];
87102
updated[index].amount = v;
88103

89-
const detail = updated[index];
90-
91-
// 支払い情報が既存なら即時反映
92-
if (detail.id !== -1 && v !== "") {
93-
setPayments((prevPayments) =>
94-
prevPayments.map((p) =>
95-
p.id === detail.id ? { ...p, amount: Number(v) } : p,
96-
),
97-
);
98-
}
99-
100104
const last = updated[updated.length - 1];
101105
if (last.amount !== "" && last.paidFor !== -1) {
102106
updated.push({ id: -1, paidFor: -1, amount: "" });
@@ -106,28 +110,10 @@ export default function BillingDetailCard({
106110
});
107111
};
108112

109-
const handleBlur = (index: number) => {
113+
const handleBlur = async (index: number) => {
110114
const detail = details[index];
111115

112-
if (detail.id === -1 && detail.paidFor !== -1 && detail.amount !== "") {
113-
// 新規作成
114-
const newId =
115-
payments.length > 0 ? Math.max(...payments.map((p) => p.id)) + 1 : 0;
116-
const newPayment: Payment = {
117-
id: newId,
118-
paidBy: paidBy.id,
119-
paidFor: detail.paidFor,
120-
amount: Number(detail.amount),
121-
};
122-
setPayments((prev) => [...prev, newPayment]);
123-
124-
// detailsのidも更新
125-
setDetails((prev) => {
126-
const updated = [...prev];
127-
updated[index].id = newId;
128-
return updated;
129-
});
130-
} else if (detail.id !== -1) {
116+
if (detail.id !== -1) {
131117
// 既存を更新
132118
setPayments((prev) =>
133119
prev.map((p) =>
@@ -136,6 +122,17 @@ export default function BillingDetailCard({
136122
);
137123
}
138124

125+
await fetch(`http://127.0.0.1:8000/api/v1/payments/patch_by_key/`, {
126+
method: "PATCH",
127+
headers: { "Content-Type": "application/json" },
128+
body: JSON.stringify({
129+
event_id: eventId,
130+
payment_id: detail.id,
131+
amount: detail.amount,
132+
note: "",
133+
}),
134+
});
135+
139136
if (
140137
index === details.length - 1 &&
141138
(detail.paidFor !== -1 || detail.amount !== "")
@@ -144,13 +141,20 @@ export default function BillingDetailCard({
144141
}
145142
};
146143

147-
const handleDeleteBilling = (index: number) => {
144+
const handleDeleteBilling = async (index: number) => {
148145
const target = details[index];
149146

150147
if (!confirm("本当に削除しますか?")) return;
151148

152149
if (target.id !== -1) {
153150
setPayments((prev) => prev.filter((p) => p.id !== target.id));
151+
152+
await fetch(
153+
`http://127.0.0.1:8000/api/v1/payments/delete_by_key/?event_id=${eventId}&payment_id=${target.id}`,
154+
{
155+
method: "DELETE",
156+
},
157+
);
154158
}
155159

156160
setDetails((prev) => prev.filter((_, i) => i !== index));

frontend/src/components/MemberList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ interface MemberListProps {
1212
setPayments: React.Dispatch<React.SetStateAction<Payment[]>>;
1313
}
1414

15-
type MemberResponse = {
15+
interface MemberResponse {
1616
member_id: number;
1717
name: string;
1818
id: number;
19-
};
19+
}
2020

2121
export default function MemberList({
2222
members,

frontend/src/pages/BillingPage.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useEffect } from "react";
12
import { useNavigate, useParams } from "react-router-dom";
23
import BillingTabList from "../components/BillingTabList";
34
import CardWrapper from "../components/CardWrapper";
@@ -8,17 +9,57 @@ import type { Payment } from "../types/payment";
89
interface BillingPageProps {
910
members: Member[];
1011
payments: Payment[];
12+
setMembers: React.Dispatch<React.SetStateAction<Member[]>>;
1113
setPayments: React.Dispatch<React.SetStateAction<Payment[]>>;
1214
}
1315

16+
interface MemberResponse {
17+
member_id: number;
18+
name: string;
19+
}
20+
21+
interface PaymentResponse {
22+
payment_id: number;
23+
paid_by: number;
24+
paid_for: number;
25+
amount: number;
26+
}
27+
1428
export default function BillingPage({
1529
members,
1630
payments,
31+
setMembers,
1732
setPayments,
1833
}: BillingPageProps) {
1934
const navigate = useNavigate();
2035
const { eventId } = useParams();
2136

37+
useEffect(() => {
38+
fetch(`http://127.0.0.1:8000/api/v1/payments/?event_id=${eventId}`)
39+
.then((res) => res.json())
40+
.then((data) =>
41+
setPayments(
42+
(data as PaymentResponse[]).map((p) => ({
43+
id: p.payment_id,
44+
paidBy: p.paid_by,
45+
paidFor: p.paid_for,
46+
amount: p.amount,
47+
})),
48+
),
49+
);
50+
51+
fetch(`http://127.0.0.1:8000/api/v1/members/?event_id=${eventId}`)
52+
.then((res) => res.json())
53+
.then((data) =>
54+
setMembers(
55+
(data as MemberResponse[]).map((p) => ({
56+
id: p.member_id,
57+
name: p.name,
58+
})),
59+
),
60+
);
61+
}, [eventId, setMembers, setPayments]);
62+
2263
return (
2364
<div className="mx-auto w-full max-w-3xl p-6">
2465
<CardWrapper

0 commit comments

Comments
 (0)