Skip to content

Commit

Permalink
Merge pull request #68 from tsh11na/issue-62
Browse files Browse the repository at this point in the history
管理画面のアップデート
  • Loading branch information
tsh11na authored Feb 10, 2024
2 parents 4aff5a6 + 05750fd commit 9228170
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 50 deletions.
92 changes: 90 additions & 2 deletions tsuke/admin.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,103 @@
from django.contrib import admin
from django.db.models import Sum

from .models import ItemCategory, Tsuke
from .models import ItemCategory, Tsuke, TsukeTotal


class PaidFilter(admin.SimpleListFilter):
"""清算済みフィルタ"""
title = "清算"
parameter_name = "is_paid"

def lookups(self, request, model_admin):
return (
("paid", "済"),
("unpaid", "未"),
)

def queryset(self, request, queryset):
if self.value() == "paid":
return queryset.filter(is_paid=True)
if self.value() == "unpaid":
return queryset.filter(is_paid=False)


class ItemCategoryAdmin(admin.ModelAdmin):
list_display = ["category"]
change_list_template = "admin/itemcategory_change_list.html"


class TsukeAdmin(admin.ModelAdmin):
list_display = ["purchase_date", "amount", "user", "note", "payment_date"]
list_display = ["purchase_date", "amount", "user", "note", "is_paid", "payment_date"]
readonly_fields = [field.name for field in Tsuke._meta.fields]
list_filter = ["user", "category", PaidFilter]
change_list_template = "admin/tsuke_change_list.html" # 一覧画面
change_form_template = "admin/tsuke_change_form.html" # 詳細画面

def has_add_permission(self, request, obj=None) -> bool:
"""ツケは登録ページからしか追加できない"""
return False

def has_view_permission(self, request, obj=None) -> bool:
if request.user.is_staff:
return True # スタッフは閲覧可能
return False

def has_delete_permission(self, request, obj=None) -> bool:
if request.user.is_superuser:
return True # スーパーユーザのみ削除可能
return False

def changelist_view(self, request, extra_context=None):
response = super().changelist_view(request, extra_context)

# try:
# qs = response.context_data["cl"].get_queryset(request)
# except (AttributeError, KeyError):
# return response

# フィルタ条件を取得
filters = {
param: request.GET.getlist(param) for param in request.GET
if not param.startswith("_")
}

response.context_data['filters'] = filters
return response


class TsukeTotalAdmin(admin.ModelAdmin):
list_display = ["user"]
change_list_template = "admin/tsuketotal_change_list.html"

def has_add_permission(self, *args, **kwargs):
"""ツケの合計は変更できない"""
return False

def changelist_view(self, request, extra_context=None):
response = super().changelist_view(request, extra_context)
try:
qs = response.context_data["cl"].queryset
except (AttributeError, KeyError):
return response
metrics = {
"total_amount": Sum("amount"),
}

# ユーザ一覧を取得
user_list = [tsuke.user for tsuke in qs]
# ユーザごとの合計金額を取得
total_amount = {
user: qs.filter(user=user, is_paid=False).aggregate(**metrics)["total_amount"] or 0
for user in user_list
}

response.context_data["summary"] = total_amount
return response

admin.site.site_header = "Tsukepp 管理画面"
admin.site.index_title = "メニュー"

admin.site.register(ItemCategory, ItemCategoryAdmin)
admin.site.register(Tsuke, TsukeAdmin)
admin.site.register(TsukeTotal, TsukeTotalAdmin)
24 changes: 24 additions & 0 deletions tsuke/migrations/0008_tsuketotal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.6 on 2024-02-10 02:23

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("tsuke", "0007_alter_tsuke_payment_date"),
]

operations = [
migrations.CreateModel(
name="TsukeTotal",
fields=[],
options={
"verbose_name": "ツケ合計",
"verbose_name_plural": "ツケ合計",
"proxy": True,
"indexes": [],
"constraints": [],
},
bases=("tsuke.tsuke",),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 4.2.6 on 2024-02-10 09:08

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("tsuke", "0008_tsuketotal"),
]

operations = [
migrations.AlterModelOptions(
name="tsuketotal",
options={"verbose_name": "集計", "verbose_name_plural": "集計"},
),
migrations.AlterField(
model_name="tsuke",
name="is_paid",
field=models.BooleanField(default=False, verbose_name="清算"),
),
]
12 changes: 11 additions & 1 deletion tsuke/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Meta:
def __str__(self):
return str(self.category)


class Tsuke(models.Model):
"""1回のツケ"""

Expand All @@ -28,7 +29,7 @@ class Tsuke(models.Model):
amount = models.PositiveSmallIntegerField(
verbose_name="金額",
validators=[positive_validator])
is_paid = models.BooleanField(verbose_name="清算済", default=False)
is_paid = models.BooleanField(verbose_name="清算", default=False)
category = models.ForeignKey(ItemCategory, verbose_name="品目", on_delete=models.SET_NULL, null=True)
payment_date = models.DateTimeField(verbose_name="清算日時", null=True)
note = models.CharField(verbose_name="メモ", max_length=50, blank=True)
Expand All @@ -40,3 +41,12 @@ class Meta:

def __str__(self):
return f"{self.amount}円({self.category})"


class TsukeTotal(Tsuke):
"""各ユーザのツケの合計額"""

class Meta:
proxy = True
verbose_name = "集計"
verbose_name_plural = "集計"
7 changes: 7 additions & 0 deletions tsuke/templates/admin/itemcategory_change_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% extends "admin/change_list.html" %}

{% block content_title %}
<h1>品目一覧</h1>
{% endblock %}

<!-- タイトル以外は変更しない -->
7 changes: 7 additions & 0 deletions tsuke/templates/admin/tsuke_change_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% extends "admin/change_form.html" %}

{% block content_title %}
<h1>ツケ詳細</h1>
{% endblock %}

<!-- タイトル以外は変更しない -->
28 changes: 28 additions & 0 deletions tsuke/templates/admin/tsuke_change_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{% extends "admin/change_list.html" %}

{% block content_title %}
<h1>
ツケ一覧
<!-- {% for x in cl.filter_specs %}
{% endfor %} -->
{% for filter in cl.filter_specs %}
{% if filter.lookup_choices and filter.lookup_val %}
| {{ filter.lookup_title }} =
{% for choice in filter.lookup_choices %}
{% if choice.0|stringformat:"s" == filter.lookup_val %}
{{ choice.1 }}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% if "is_paid__exact" in filters %}
{% if filters.is_paid__exact.0 == "0" %}
| 未清算
{% elif filters.is_paid__exact.0 == "1" %}
| 清算済
{% endif %}
{% endif %}
</h1>
{% endblock %}

<!-- タイトル以外は変更しない -->
24 changes: 24 additions & 0 deletions tsuke/templates/admin/tsuketotal_change_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{% extends "admin/change_list.html" %}

{% block content_title %}
<h1>未清算のツケの合計額(ユーザごと)</h1>
{% endblock %}

{% block result_list %}
<table>
<thead>
<tr>
<th>ユーザ</th>
<th>未精算額合計</th>
</tr>
</thead>
<tbody>
{% for user, amount in summary.items %}
<tr>
<td><a href="{% url 'admin:tsuke_tsuke_changelist' %}?is_paid__exact=0&user__id__exact={{user.id}}">{{ user }}</a></td>
<td>{{ amount }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
5 changes: 5 additions & 0 deletions tsuke/templates/tsuke/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ <h1 class="logo"><a href={% url "tsuke:index" %}>Tsukepp</a></h1>
<li>
<a class="nav-link scrollto" href={% url "tsuke:history" %}>履歴を見る</a>
</li>
{% if user.is_staff %}
<li>
<a class="nav-link scrollto" href={% url "admin:index" %}>管理画面</a>
</li>
{% endif %}
{% if user.id %}
<li class="dropdown">
<a href="#"><span>アカウント</span> <i class="bi bi-chevron-right"></i></a>
Expand Down
69 changes: 69 additions & 0 deletions tsuke/tests/test_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""テスト用のアカウントやログイン状態を定義するモジュール"""

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse_lazy


class LoggedInTestCase(TestCase):
"""
ログイン状態を定義するテストケース
(「動かして学ぶ!Python Django開発入門」p.286より)
"""
def setUp(self):
"""テストメソッド実行前の事前設定"""
self.username = "testa"
self.password = 'xyab2023'
self.SUBMIT_TOKEN = "test_token"

self.test_user = get_user_model().objects.create_user(
username=self.username,
password=self.password,
)

self.client.login(
username=self.username,
password=self.password
)

def set_pseudo_token(self):
"""疑似submit tokenをセッションに格納する"""
sess = self.client.session
sess["submit_token"] = self.SUBMIT_TOKEN
sess.save()


class SuperuserLoggedInTestCase(LoggedInTestCase):
"""管理サイトのログイン状態を定義するクラス"""
def setUp(self):
"""テストメソッド実行前の事前設定"""
self.username = "admin"
self.password = 'admin2023'
self.SUBMIT_TOKEN = "test_token"

self.test_user = get_user_model().objects.create_superuser(
username=self.username,
email="[email protected]",
password=self.password
)

self.client.login(
username=self.username,
password=self.password
)

def set_pseudo_token(self):
"""疑似submit tokenをセッションに格納する"""
sess = self.client.session
sess["submit_token"] = self.SUBMIT_TOKEN
sess.save()


class TestAccount(LoggedInTestCase):
"""アカウント機能のテストクラス"""
def test_login_ok(self):
"""すでにログイン状態であることの確認"""
response = self.client.get(
'/accounts/login/'
)
self.assertRedirects(response, reverse_lazy('tsuke:index'))
46 changes: 46 additions & 0 deletions tsuke/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.urls import reverse_lazy

from ..models import ItemCategory, Tsuke
from .test_account import SuperuserLoggedInTestCase


class TestAdminTsukeTotal(SuperuserLoggedInTestCase):
"""管理サイトのツケ合計表示用のテストクラス"""

@classmethod
def setUpTestData(cls):
cls.category1 = ItemCategory.objects.create(category="飲み物")
cls.category2 = ItemCategory.objects.create(category="お菓子")

def test_admin_tsuke_total(self):
"""管理サイトのツケ合計が正しく表示されることを確認"""
url = reverse_lazy("admin:tsuke_tsuketotal_changelist")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)

# 疑似データを登録
Tsuke.objects.create(
amount=123,
category=self.category1,
note="お茶",
is_paid=False,
user=self.test_user,
)
Tsuke.objects.create(
amount=456,
category=self.category2,
note="グミ",
is_paid=False,
user=self.test_user,
)
Tsuke.objects.create(
amount=789,
category=self.category1,
note="コーヒー",
is_paid=True, # 清算済
user=self.test_user,
)

# 未清算のツケのみの合計額が表示されることを確認
response = self.client.get(url)
self.assertContains(response, "579") # 123 + 456
7 changes: 0 additions & 7 deletions tsuke/tests/test_sample.py

This file was deleted.

Loading

0 comments on commit 9228170

Please sign in to comment.