データベースルールにowner_typeとis_canceledを追加し、is_businessを廃止。Expenseモデルと関連するマイグレーションを実施。明細編集UIをowner_type選択に更新し、取り消し済みの経費を一覧から除外。作業ログをdiary.mdに追記。
This commit is contained in:
56
expenses/migrations/0002_owner_type_cancel.py
Normal file
56
expenses/migrations/0002_owner_type_cancel.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Generated by Django 4.2.27 on 2025-12-20 00:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def map_is_business_to_owner_type(apps, schema_editor):
|
||||
Expense = apps.get_model('expenses', 'Expense')
|
||||
for expense in Expense.objects.all():
|
||||
if expense.is_business is True:
|
||||
expense.owner_type = 'company'
|
||||
elif expense.is_business is False:
|
||||
expense.owner_type = 'personal'
|
||||
else:
|
||||
expense.owner_type = 'pending'
|
||||
expense.save(update_fields=['owner_type'])
|
||||
|
||||
|
||||
def reverse_owner_type_to_is_business(apps, schema_editor):
|
||||
Expense = apps.get_model('expenses', 'Expense')
|
||||
for expense in Expense.objects.all():
|
||||
if expense.owner_type == 'company':
|
||||
expense.is_business = True
|
||||
elif expense.owner_type == 'personal':
|
||||
expense.is_business = False
|
||||
else:
|
||||
expense.is_business = None
|
||||
expense.save(update_fields=['is_business'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('expenses', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
name='owner_type',
|
||||
field=models.CharField(
|
||||
choices=[('company', 'Company'), ('personal', 'Personal'), ('pending', 'Pending')],
|
||||
default='pending',
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='expense',
|
||||
name='is_canceled',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.RunPython(map_is_business_to_owner_type, reverse_owner_type_to_is_business),
|
||||
migrations.RemoveField(
|
||||
model_name='expense',
|
||||
name='is_business',
|
||||
),
|
||||
]
|
||||
@@ -21,6 +21,11 @@ class Expense(models.Model):
|
||||
SOURCE_CHOICES = [
|
||||
('idemitsu', 'Idemitsu'),
|
||||
]
|
||||
OWNER_TYPE_CHOICES = [
|
||||
('company', 'Company'),
|
||||
('personal', 'Personal'),
|
||||
('pending', 'Pending'),
|
||||
]
|
||||
|
||||
use_date = models.DateField()
|
||||
description = models.CharField(max_length=255)
|
||||
@@ -29,12 +34,13 @@ class Expense(models.Model):
|
||||
expense_category = models.ForeignKey(
|
||||
ExpenseCategory, null=True, blank=True, on_delete=models.SET_NULL
|
||||
)
|
||||
is_business = models.BooleanField(null=True, blank=True)
|
||||
owner_type = models.CharField(max_length=20, choices=OWNER_TYPE_CHOICES, default='pending')
|
||||
note = models.TextField(blank=True)
|
||||
source = models.CharField(max_length=50, choices=SOURCE_CHOICES)
|
||||
source_hash = models.CharField(max_length=64)
|
||||
ai_score = models.FloatField(null=True, blank=True)
|
||||
human_confirmed = models.BooleanField(default=False)
|
||||
is_canceled = models.BooleanField(default=False)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ def csv_upload(request):
|
||||
|
||||
|
||||
def expense_list(request):
|
||||
expenses = Expense.objects.select_related('store', 'expense_category').order_by('-use_date', '-id')[:200]
|
||||
expenses = (
|
||||
Expense.objects.filter(is_canceled=False)
|
||||
.select_related('store', 'expense_category')
|
||||
.order_by('-use_date', '-id')[:200]
|
||||
)
|
||||
stores = Store.objects.filter(is_active=True).order_by('name')
|
||||
categories = ExpenseCategory.objects.filter(is_active=True).order_by('name')
|
||||
return render(
|
||||
@@ -57,11 +61,14 @@ def expense_update(request, expense_id: int):
|
||||
return JsonResponse({'status': 'error', 'message': 'JSONはオブジェクトで送信してください。'}, status=400)
|
||||
expense = get_object_or_404(Expense, pk=expense_id)
|
||||
fields = {}
|
||||
for key in ('store_id', 'expense_category_id', 'is_business', 'note'):
|
||||
for key in ('store_id', 'expense_category_id', 'owner_type', 'note'):
|
||||
if key in payload:
|
||||
value = payload[key]
|
||||
if value in ('', None):
|
||||
fields[key] = None
|
||||
if key == 'owner_type':
|
||||
fields[key] = 'pending'
|
||||
else:
|
||||
fields[key] = None
|
||||
else:
|
||||
fields[key] = value
|
||||
if 'store_id' in fields and fields['store_id'] is not None:
|
||||
@@ -78,9 +85,11 @@ def expense_update(request, expense_id: int):
|
||||
return JsonResponse(
|
||||
{'status': 'error', 'message': 'expense_category_idが不正です。'}, status=400
|
||||
)
|
||||
if 'is_business' in fields and fields['is_business'] is not None:
|
||||
if not isinstance(fields['is_business'], bool):
|
||||
return JsonResponse({'status': 'error', 'message': 'is_businessの型が不正です。'}, status=400)
|
||||
if 'owner_type' in fields and fields['owner_type'] is not None:
|
||||
if not isinstance(fields['owner_type'], str):
|
||||
return JsonResponse({'status': 'error', 'message': 'owner_typeの型が不正です。'}, status=400)
|
||||
if fields['owner_type'] not in {'company', 'personal', 'pending'}:
|
||||
return JsonResponse({'status': 'error', 'message': 'owner_typeが不正です。'}, status=400)
|
||||
if 'note' in fields and fields['note'] is not None:
|
||||
if not isinstance(fields['note'], str):
|
||||
return JsonResponse({'status': 'error', 'message': 'noteの型が不正です。'}, status=400)
|
||||
|
||||
Reference in New Issue
Block a user