月次レポート機能を実装し、経費の取り消し・復帰機能を追加。CSV取込画面にドラッグ&ドロップエリアを実装し、エラーメッセージ表示を追加。金額のカンマ区切り表示を全体に適用。faviconとapple-touch-iconを追加し、404エラーを回避。作業ログをdiary.mdに追記。
This commit is contained in:
@@ -22,3 +22,12 @@
|
|||||||
- 0002_owner_type_cancel マイグレーションを追加し既存データを移行
|
- 0002_owner_type_cancel マイグレーションを追加し既存データを移行
|
||||||
- 仕様書のDB項目を owner_type / is_canceled に更新
|
- 仕様書のDB項目を owner_type / is_canceled に更新
|
||||||
- リモートDBに対してマイグレーションを実行
|
- リモートDBに対してマイグレーションを実行
|
||||||
|
- 取消/復帰APIと一覧の取消ボタン・表示切替トグルを追加
|
||||||
|
- 取消済み行の打ち消し線表示を追加
|
||||||
|
- CSV取込のD&Dエリアを実装しファイル名表示を追加
|
||||||
|
- CSVブランド判定失敗時のエラーメッセージ表示を追加
|
||||||
|
- CSVパーサの行結合で落ちる不具合を修正(ExpenseRowのfrozen解除)
|
||||||
|
- 月次レポートの集計ロジックと画面表示を実装(店舗/経費区分/区分/未分類)
|
||||||
|
- 月次レポートに明細行一覧を追加(各集計の下に日付/利用先/金額/備考)
|
||||||
|
- 金額のカンマ区切りと右寄せを全体に適用(humanize + amountクラス)
|
||||||
|
- favicon/apple-touch-iconの404回避と簡易アイコンを追加
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.humanize',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
|||||||
@@ -15,9 +15,17 @@ Including another URLconf
|
|||||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.http import HttpResponse
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
|
|
||||||
|
def empty_icon(request):
|
||||||
|
return HttpResponse(status=204)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('favicon.ico', empty_icon),
|
||||||
|
path('apple-touch-icon.png', empty_icon),
|
||||||
|
path('apple-touch-icon-precomposed.png', empty_icon),
|
||||||
path('', include('expenses.urls')),
|
path('', include('expenses.urls')),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -33,3 +33,5 @@ root@x85-131-243-202:~# sudo -u postgres psql -c "\du"
|
|||||||
|
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
DB_NAME=accounting DB_USER=account_user DB_PASSWORD=account_Hideyukey-1234 DB_HOST=labo.sunamura-llc.com DB_PORT=5432 .venv/bin/python manage.py runserver
|
DB_NAME=accounting DB_USER=account_user DB_PASSWORD=account_Hideyukey-1234 DB_HOST=labo.sunamura-llc.com DB_PORT=5432 .venv/bin/python manage.py runserver
|
||||||
|
|
||||||
|
DJANGO_DEBUG=true DB_NAME=accounting DB_USER=account_user DB_PASSWORD=account_Hideyukey-1234 DB_HOST=labo.sunamura-llc.com DB_PORT=5432 .venv/bin/python manage.py runserver
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from datetime import date
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class ExpenseRow:
|
class ExpenseRow:
|
||||||
use_date: date
|
use_date: date
|
||||||
description: str
|
description: str
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import date
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
from django.db.models import Count, Sum
|
||||||
|
|
||||||
from .csv_parsers import get_parser
|
from .csv_parsers import get_parser
|
||||||
from .models import Expense
|
from .models import Expense
|
||||||
|
|
||||||
@@ -34,3 +39,74 @@ def import_csv_lines(lines: Iterable[str]) -> CSVImportResult:
|
|||||||
else:
|
else:
|
||||||
duplicated += 1
|
duplicated += 1
|
||||||
return CSVImportResult(imported=imported, duplicated=duplicated)
|
return CSVImportResult(imported=imported, duplicated=duplicated)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MonthlyReportResult:
|
||||||
|
target_month: date
|
||||||
|
total_amount: int
|
||||||
|
store_totals: list[dict]
|
||||||
|
category_totals: list[dict]
|
||||||
|
owner_totals: list[dict]
|
||||||
|
unclassified_counts: dict
|
||||||
|
|
||||||
|
|
||||||
|
def build_monthly_report(start_date: date, end_date: date) -> MonthlyReportResult:
|
||||||
|
queryset = Expense.objects.filter(
|
||||||
|
is_canceled=False,
|
||||||
|
use_date__gte=start_date,
|
||||||
|
use_date__lt=end_date,
|
||||||
|
)
|
||||||
|
detail_queryset = (
|
||||||
|
queryset.select_related('store', 'expense_category')
|
||||||
|
.order_by('use_date', 'id')
|
||||||
|
)
|
||||||
|
store_details = defaultdict(list)
|
||||||
|
category_details = defaultdict(list)
|
||||||
|
owner_details = defaultdict(list)
|
||||||
|
for expense in detail_queryset:
|
||||||
|
detail = {
|
||||||
|
'use_date': expense.use_date,
|
||||||
|
'description': expense.description,
|
||||||
|
'amount': expense.amount,
|
||||||
|
'note': expense.note,
|
||||||
|
}
|
||||||
|
store_details[expense.store_id].append(detail)
|
||||||
|
category_details[expense.expense_category_id].append(detail)
|
||||||
|
owner_details[expense.owner_type].append(detail)
|
||||||
|
total_amount = queryset.aggregate(total=Sum('amount'))['total'] or 0
|
||||||
|
store_totals = list(
|
||||||
|
queryset.values('store_id', 'store__name')
|
||||||
|
.annotate(total=Sum('amount'), count=Count('id'))
|
||||||
|
.order_by('store__name', 'store_id')
|
||||||
|
)
|
||||||
|
for row in store_totals:
|
||||||
|
row['details'] = store_details.get(row['store_id'], [])
|
||||||
|
category_totals = list(
|
||||||
|
queryset.values('expense_category_id', 'expense_category__name')
|
||||||
|
.annotate(total=Sum('amount'), count=Count('id'))
|
||||||
|
.order_by('expense_category__name', 'expense_category_id')
|
||||||
|
)
|
||||||
|
for row in category_totals:
|
||||||
|
row['details'] = category_details.get(row['expense_category_id'], [])
|
||||||
|
owner_totals = list(
|
||||||
|
queryset.values('owner_type')
|
||||||
|
.annotate(total=Sum('amount'), count=Count('id'))
|
||||||
|
.order_by('owner_type')
|
||||||
|
)
|
||||||
|
for row in owner_totals:
|
||||||
|
row['details'] = owner_details.get(row['owner_type'], [])
|
||||||
|
unclassified_counts = {
|
||||||
|
'store_missing': queryset.filter(store__isnull=True).count(),
|
||||||
|
'category_missing': queryset.filter(expense_category__isnull=True).count(),
|
||||||
|
'owner_pending': queryset.filter(owner_type='pending').count(),
|
||||||
|
'total': queryset.count(),
|
||||||
|
}
|
||||||
|
return MonthlyReportResult(
|
||||||
|
target_month=start_date,
|
||||||
|
total_amount=total_amount,
|
||||||
|
store_totals=store_totals,
|
||||||
|
category_totals=category_totals,
|
||||||
|
owner_totals=owner_totals,
|
||||||
|
unclassified_counts=unclassified_counts,
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ urlpatterns = [
|
|||||||
path('', views.csv_upload, name='csv_upload'),
|
path('', views.csv_upload, name='csv_upload'),
|
||||||
path('expenses/', views.expense_list, name='expense_list'),
|
path('expenses/', views.expense_list, name='expense_list'),
|
||||||
path('expenses/<int:expense_id>/update/', views.expense_update, name='expense_update'),
|
path('expenses/<int:expense_id>/update/', views.expense_update, name='expense_update'),
|
||||||
|
path('expenses/<int:expense_id>/cancel/', views.expense_cancel, name='expense_cancel'),
|
||||||
path('reports/monthly/', views.monthly_report, name='monthly_report'),
|
path('reports/monthly/', views.monthly_report, name='monthly_report'),
|
||||||
path('reports/monthly/pdf/', views.monthly_report_pdf, name='monthly_report_pdf'),
|
path('reports/monthly/pdf/', views.monthly_report_pdf, name='monthly_report_pdf'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
|
from datetime import date, datetime
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
import chardet
|
import chardet
|
||||||
@@ -8,7 +9,7 @@ from django.shortcuts import redirect
|
|||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
from .services import import_csv_lines
|
from .services import build_monthly_report, import_csv_lines
|
||||||
from .models import Expense, ExpenseCategory, Store
|
from .models import Expense, ExpenseCategory, Store
|
||||||
|
|
||||||
|
|
||||||
@@ -21,7 +22,11 @@ def csv_upload(request):
|
|||||||
encoding = detected.get('encoding') or 'utf-8'
|
encoding = detected.get('encoding') or 'utf-8'
|
||||||
text = raw.decode(encoding, errors='ignore')
|
text = raw.decode(encoding, errors='ignore')
|
||||||
lines = io.StringIO(text)
|
lines = io.StringIO(text)
|
||||||
|
try:
|
||||||
result = import_csv_lines(lines)
|
result = import_csv_lines(lines)
|
||||||
|
except ValueError as exc:
|
||||||
|
context['error_message'] = str(exc)
|
||||||
|
return render(request, 'expenses/csv_upload.html', context)
|
||||||
query = urlencode(
|
query = urlencode(
|
||||||
{'imported': result.imported, 'duplicated': result.duplicated, 'encoding': encoding}
|
{'imported': result.imported, 'duplicated': result.duplicated, 'encoding': encoding}
|
||||||
)
|
)
|
||||||
@@ -30,11 +35,11 @@ def csv_upload(request):
|
|||||||
|
|
||||||
|
|
||||||
def expense_list(request):
|
def expense_list(request):
|
||||||
expenses = (
|
include_canceled = request.GET.get('include_canceled') == '1'
|
||||||
Expense.objects.filter(is_canceled=False)
|
queryset = Expense.objects.select_related('store', 'expense_category')
|
||||||
.select_related('store', 'expense_category')
|
if not include_canceled:
|
||||||
.order_by('-use_date', '-id')[:200]
|
queryset = queryset.filter(is_canceled=False)
|
||||||
)
|
expenses = queryset.order_by('-use_date', '-id')[:200]
|
||||||
stores = Store.objects.filter(is_active=True).order_by('name')
|
stores = Store.objects.filter(is_active=True).order_by('name')
|
||||||
categories = ExpenseCategory.objects.filter(is_active=True).order_by('name')
|
categories = ExpenseCategory.objects.filter(is_active=True).order_by('name')
|
||||||
return render(
|
return render(
|
||||||
@@ -47,6 +52,7 @@ def expense_list(request):
|
|||||||
'imported': request.GET.get('imported'),
|
'imported': request.GET.get('imported'),
|
||||||
'duplicated': request.GET.get('duplicated'),
|
'duplicated': request.GET.get('duplicated'),
|
||||||
'encoding': request.GET.get('encoding'),
|
'encoding': request.GET.get('encoding'),
|
||||||
|
'include_canceled': include_canceled,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -104,8 +110,70 @@ def expense_update(request, expense_id: int):
|
|||||||
|
|
||||||
|
|
||||||
def monthly_report(request):
|
def monthly_report(request):
|
||||||
return render(request, 'expenses/monthly_report.html')
|
error_message = None
|
||||||
|
target_month = request.GET.get('target_month')
|
||||||
|
if target_month:
|
||||||
|
try:
|
||||||
|
start_date = datetime.strptime(target_month, '%Y-%m').date().replace(day=1)
|
||||||
|
except ValueError:
|
||||||
|
error_message = '年月の形式が不正です。'
|
||||||
|
start_date = date.today().replace(day=1)
|
||||||
|
else:
|
||||||
|
start_date = date.today().replace(day=1)
|
||||||
|
if start_date.month == 12:
|
||||||
|
end_date = start_date.replace(year=start_date.year + 1, month=1)
|
||||||
|
else:
|
||||||
|
end_date = start_date.replace(month=start_date.month + 1)
|
||||||
|
report = build_monthly_report(start_date, end_date)
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'expenses/monthly_report.html',
|
||||||
|
{
|
||||||
|
'report': report,
|
||||||
|
'target_month': start_date.strftime('%Y-%m'),
|
||||||
|
'error_message': error_message,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def monthly_report_pdf(request):
|
def monthly_report_pdf(request):
|
||||||
return render(request, 'expenses/monthly_report_pdf.html')
|
target_month = request.GET.get('target_month')
|
||||||
|
if target_month:
|
||||||
|
try:
|
||||||
|
start_date = datetime.strptime(target_month, '%Y-%m').date().replace(day=1)
|
||||||
|
except ValueError:
|
||||||
|
start_date = date.today().replace(day=1)
|
||||||
|
else:
|
||||||
|
start_date = date.today().replace(day=1)
|
||||||
|
if start_date.month == 12:
|
||||||
|
end_date = start_date.replace(year=start_date.year + 1, month=1)
|
||||||
|
else:
|
||||||
|
end_date = start_date.replace(month=start_date.month + 1)
|
||||||
|
report = build_monthly_report(start_date, end_date)
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'expenses/monthly_report_pdf.html',
|
||||||
|
{
|
||||||
|
'report': report,
|
||||||
|
'target_month': start_date.strftime('%Y-%m'),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@require_POST
|
||||||
|
def expense_cancel(request, expense_id: int):
|
||||||
|
try:
|
||||||
|
payload = json.loads(request.body.decode('utf-8'))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return JsonResponse({'status': 'error', 'message': 'JSON形式が不正です。'}, status=400)
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
return JsonResponse({'status': 'error', 'message': 'JSONはオブジェクトで送信してください。'}, status=400)
|
||||||
|
if 'is_canceled' not in payload:
|
||||||
|
return JsonResponse({'status': 'error', 'message': 'is_canceledが必要です。'}, status=400)
|
||||||
|
if not isinstance(payload['is_canceled'], bool):
|
||||||
|
return JsonResponse({'status': 'error', 'message': 'is_canceledの型が不正です。'}, status=400)
|
||||||
|
expense = get_object_or_404(Expense, pk=expense_id)
|
||||||
|
expense.is_canceled = payload['is_canceled']
|
||||||
|
expense.human_confirmed = True
|
||||||
|
expense.save(update_fields=['is_canceled', 'human_confirmed', 'updated_at'])
|
||||||
|
return JsonResponse({'status': 'ok'})
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{% block title %}Card Data Sorting{% endblock %}</title>
|
<title>{% block title %}Card Data Sorting{% endblock %}</title>
|
||||||
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='10' fill='%231a73e8'/%3E%3Ctext x='32' y='42' font-size='28' text-anchor='middle' fill='white' font-family='sans-serif'%3ECS%3C/text%3E%3C/svg%3E">
|
||||||
|
<link rel="apple-touch-icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 180 180'%3E%3Crect width='180' height='180' rx='28' fill='%231a73e8'/%3E%3Ctext x='90' y='118' font-size='72' text-anchor='middle' fill='white' font-family='sans-serif'%3ECS%3C/text%3E%3C/svg%3E">
|
||||||
|
<style>
|
||||||
|
.amount {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
|
|||||||
@@ -5,13 +5,76 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<section>
|
<section>
|
||||||
<h2>CSV取込</h2>
|
<h2>CSV取込</h2>
|
||||||
|
{% if error_message %}
|
||||||
|
<p style="color: #b00020;">{{ error_message }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<style>
|
||||||
|
.drop-zone {
|
||||||
|
border: 2px dashed #888;
|
||||||
|
padding: 24px;
|
||||||
|
text-align: center;
|
||||||
|
background: #fafafa;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
.drop-zone.is-dragover {
|
||||||
|
background: #e9f2ff;
|
||||||
|
}
|
||||||
|
.drop-zone input[type="file"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.drop-zone .file-name {
|
||||||
|
margin-top: 8px;
|
||||||
|
color: #555;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<div>
|
<div>
|
||||||
<p>ここにDrag & Dropエリアを配置予定。</p>
|
<form method="post" enctype="multipart/form-data" id="js-csv-form">
|
||||||
<form method="post" enctype="multipart/form-data">
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="file" name="csv_file">
|
<label class="drop-zone" id="js-drop-zone">
|
||||||
|
<strong>ここにCSVをドラッグ&ドロップ</strong>
|
||||||
|
<div>またはクリックして選択</div>
|
||||||
|
<input type="file" name="csv_file" id="js-csv-input" accept=".csv">
|
||||||
|
<div class="file-name" id="js-file-name">ファイル未選択</div>
|
||||||
|
</label>
|
||||||
<button type="submit">アップロード</button>
|
<button type="submit">アップロード</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
const dropZone = document.querySelector('#js-drop-zone');
|
||||||
|
const fileInput = document.querySelector('#js-csv-input');
|
||||||
|
const fileName = document.querySelector('#js-file-name');
|
||||||
|
|
||||||
|
function updateFileName(files) {
|
||||||
|
if (!files || files.length === 0) {
|
||||||
|
fileName.textContent = 'ファイル未選択';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileName.textContent = files[0].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
dropZone.addEventListener('dragover', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
dropZone.classList.add('is-dragover');
|
||||||
|
});
|
||||||
|
|
||||||
|
dropZone.addEventListener('dragleave', () => {
|
||||||
|
dropZone.classList.remove('is-dragover');
|
||||||
|
});
|
||||||
|
|
||||||
|
dropZone.addEventListener('drop', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
dropZone.classList.remove('is-dragover');
|
||||||
|
if (!event.dataTransfer?.files?.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileInput.files = event.dataTransfer.files;
|
||||||
|
updateFileName(fileInput.files);
|
||||||
|
});
|
||||||
|
|
||||||
|
fileInput.addEventListener('change', (event) => {
|
||||||
|
updateFileName(event.target.files);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,15 +1,26 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
{% block title %}明細編集{% endblock %}
|
{% block title %}明細編集{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section>
|
<section>
|
||||||
<h2>明細編集</h2>
|
<h2>明細編集</h2>
|
||||||
|
<style>
|
||||||
|
.is-canceled {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% if imported %}
|
{% if imported %}
|
||||||
<p>取込件数: {{ imported }} / 重複件数: {{ duplicated }} / 文字コード: {{ encoding }}</p>
|
<p>取込件数: {{ imported }} / 重複件数: {{ duplicated }} / 文字コード: {{ encoding }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form>
|
<form>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="js-include-canceled" {% if include_canceled %}checked{% endif %}>
|
||||||
|
取り消し済みを表示
|
||||||
|
</label>
|
||||||
</form>
|
</form>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -17,6 +28,7 @@
|
|||||||
<th>利用日</th>
|
<th>利用日</th>
|
||||||
<th>利用先</th>
|
<th>利用先</th>
|
||||||
<th>金額</th>
|
<th>金額</th>
|
||||||
|
<th>取消</th>
|
||||||
<th>店舗区分</th>
|
<th>店舗区分</th>
|
||||||
<th>経費区分</th>
|
<th>経費区分</th>
|
||||||
<th>区分</th>
|
<th>区分</th>
|
||||||
@@ -25,10 +37,15 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for expense in expenses %}
|
{% for expense in expenses %}
|
||||||
<tr data-expense-id="{{ expense.id }}">
|
<tr data-expense-id="{{ expense.id }}" class="{% if expense.is_canceled %}is-canceled{% endif %}">
|
||||||
<td>{{ expense.use_date }}</td>
|
<td>{{ expense.use_date }}</td>
|
||||||
<td>{{ expense.description }}</td>
|
<td>{{ expense.description }}</td>
|
||||||
<td>{{ expense.amount }}</td>
|
<td class="amount">{{ expense.amount|intcomma }}</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="js-expense-cancel">
|
||||||
|
{% if expense.is_canceled %}復帰{% else %}取消{% endif %}
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select class="js-expense-field" data-field="store_id">
|
<select class="js-expense-field" data-field="store_id">
|
||||||
<option value="">未設定</option>
|
<option value="">未設定</option>
|
||||||
@@ -73,7 +90,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7">データがありません。</td>
|
<td colspan="8">データがありません。</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -96,6 +113,21 @@
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setCanceled(expenseId, isCanceled) {
|
||||||
|
const response = await fetch(`/expenses/${expenseId}/cancel/`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': csrfToken,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ is_canceled: isCanceled }),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('取消更新に失敗しました');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
function coerceValue(field, value) {
|
function coerceValue(field, value) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -117,6 +149,41 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.js-expense-cancel').forEach((button) => {
|
||||||
|
button.addEventListener('click', async (event) => {
|
||||||
|
const row = event.target.closest('tr');
|
||||||
|
if (!row) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const expenseId = row.dataset.expenseId;
|
||||||
|
const isCanceled = event.target.textContent.trim() === '取消';
|
||||||
|
try {
|
||||||
|
await setCanceled(expenseId, isCanceled);
|
||||||
|
if (isCanceled && !document.querySelector('#js-include-canceled')?.checked) {
|
||||||
|
row.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
row.classList.toggle('is-canceled', isCanceled);
|
||||||
|
event.target.textContent = isCanceled ? '復帰' : '取消';
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const includeToggle = document.querySelector('#js-include-canceled');
|
||||||
|
if (includeToggle) {
|
||||||
|
includeToggle.addEventListener('change', (event) => {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
if (event.target.checked) {
|
||||||
|
url.searchParams.set('include_canceled', '1');
|
||||||
|
} else {
|
||||||
|
url.searchParams.delete('include_canceled');
|
||||||
|
}
|
||||||
|
window.location.href = url.toString();
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
{% load humanize %}
|
||||||
|
|
||||||
{% block title %}月次レポート{% endblock %}
|
{% block title %}月次レポート{% endblock %}
|
||||||
|
|
||||||
@@ -8,20 +9,181 @@
|
|||||||
<form method="get">
|
<form method="get">
|
||||||
<label>
|
<label>
|
||||||
年月
|
年月
|
||||||
<input type="month" name="target_month">
|
<input type="month" name="target_month" value="{{ target_month }}">
|
||||||
</label>
|
</label>
|
||||||
<button type="submit">表示</button>
|
<button type="submit">表示</button>
|
||||||
</form>
|
</form>
|
||||||
|
{% if error_message %}
|
||||||
|
<p style="color: #b00020;">{{ error_message }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<div>
|
||||||
|
<h3>合計</h3>
|
||||||
|
<p class="amount">{{ report.total_amount|intcomma }} 円</p>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>店舗別合計</h3>
|
<h3>店舗別合計</h3>
|
||||||
<p>集計結果をここに表示。</p>
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>店舗</th>
|
||||||
|
<th>件数</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in report.store_totals %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.store__name|default:"未設定" }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
<td class="amount">{{ row.total|intcomma }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% for row in report.store_totals %}
|
||||||
|
<h4>{{ row.store__name|default:"未設定" }} の明細</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>日付</th>
|
||||||
|
<th>利用先</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
<th>備考</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for detail in row.details %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ detail.use_date }}</td>
|
||||||
|
<td>{{ detail.description }}</td>
|
||||||
|
<td class="amount">{{ detail.amount|intcomma }}</td>
|
||||||
|
<td>{{ detail.note }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3>経費区分別合計</h3>
|
<h3>経費区分別合計</h3>
|
||||||
<p>集計結果をここに表示。</p>
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>経費区分</th>
|
||||||
|
<th>件数</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in report.category_totals %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.expense_category__name|default:"未設定" }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
<td class="amount">{{ row.total|intcomma }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% for row in report.category_totals %}
|
||||||
|
<h4>{{ row.expense_category__name|default:"未設定" }} の明細</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>日付</th>
|
||||||
|
<th>利用先</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
<th>備考</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for detail in row.details %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ detail.use_date }}</td>
|
||||||
|
<td>{{ detail.description }}</td>
|
||||||
|
<td class="amount">{{ detail.amount|intcomma }}</td>
|
||||||
|
<td>{{ detail.note }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>未分類件数の警告をここに表示。</p>
|
<h3>区分別合計</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>区分</th>
|
||||||
|
<th>件数</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in report.owner_totals %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.owner_type }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
<td class="amount">{{ row.total|intcomma }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% for row in report.owner_totals %}
|
||||||
|
<h4>{{ row.owner_type }} の明細</h4>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>日付</th>
|
||||||
|
<th>利用先</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
<th>備考</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for detail in row.details %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ detail.use_date }}</td>
|
||||||
|
<td>{{ detail.description }}</td>
|
||||||
|
<td class="amount">{{ detail.amount|intcomma }}</td>
|
||||||
|
<td>{{ detail.note }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>未分類件数</h3>
|
||||||
|
<ul>
|
||||||
|
<li>店舗未設定: {{ report.unclassified_counts.store_missing }}</li>
|
||||||
|
<li>経費区分未設定: {{ report.unclassified_counts.category_missing }}</li>
|
||||||
|
<li>区分未設定: {{ report.unclassified_counts.owner_pending }}</li>
|
||||||
|
<li>対象総件数: {{ report.unclassified_counts.total }}</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{% load humanize %}
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
@@ -8,10 +9,103 @@
|
|||||||
h1, h2 { margin: 0 0 8px; }
|
h1, h2 { margin: 0 0 8px; }
|
||||||
table { width: 100%; border-collapse: collapse; }
|
table { width: 100%; border-collapse: collapse; }
|
||||||
th, td { border: 1px solid #333; padding: 6px; }
|
th, td { border: 1px solid #333; padding: 6px; }
|
||||||
|
.amount { text-align: right; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>月次レポート</h1>
|
<h1>月次レポート</h1>
|
||||||
<p>PDF出力用テンプレートです。</p>
|
<p>対象年月: {{ target_month }}</p>
|
||||||
|
<h2>合計</h2>
|
||||||
|
<p class="amount">{{ report.total_amount|intcomma }} 円</p>
|
||||||
|
<h2>店舗別合計</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>店舗</th>
|
||||||
|
<th>件数</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in report.store_totals %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.store__name|default:"未設定" }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
<td class="amount">{{ row.total|intcomma }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h2>経費区分別合計</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>経費区分</th>
|
||||||
|
<th>件数</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in report.category_totals %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.expense_category__name|default:"未設定" }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
<td class="amount">{{ row.total|intcomma }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h2>区分別合計</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>区分</th>
|
||||||
|
<th>件数</th>
|
||||||
|
<th class="amount">金額</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in report.owner_totals %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.owner_type }}</td>
|
||||||
|
<td>{{ row.count }}</td>
|
||||||
|
<td class="amount">{{ row.total|intcomma }}</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">対象データがありません。</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h2>未分類件数</h2>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>店舗未設定</th>
|
||||||
|
<td>{{ report.unclassified_counts.store_missing }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>経費区分未設定</th>
|
||||||
|
<td>{{ report.unclassified_counts.category_missing }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>区分未設定</th>
|
||||||
|
<td>{{ report.unclassified_counts.owner_pending }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>対象総件数</th>
|
||||||
|
<td>{{ report.unclassified_counts.total }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
24
testdata/IDEMITSU_2510.csv
vendored
Normal file
24
testdata/IDEMITSU_2510.csv
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<EFBFBD>J<EFBFBD>[<5B>h<EFBFBD><68><EFBFBD><EFBFBD>,<EFBFBD>o<EFBFBD><EFBFBD><EFBFBD>S<EFBFBD>[<5B><><EFBFBD>h<EFBFBD>J<EFBFBD>[<5B>h
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>x<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,2025/10/07
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>z,0000056846
|
||||||
|
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD>X<EFBFBD><EFBFBD><EFBFBD>y<EFBFBD>я<EFBFBD><EFBFBD>i<EFBFBD><EFBFBD>,<EFBFBD>{<7B>l<EFBFBD>E<EFBFBD>Ƒ<EFBFBD><C691>敪,<EFBFBD>x<EFBFBD><EFBFBD><EFBFBD>敪<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>敪,<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>z,<EFBFBD><EFBFBD><EFBFBD>l
|
||||||
|
2025/08/09,<EFBFBD>d<EFBFBD>s<EFBFBD>b<EFBFBD>Ҍ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>߁@<40>i<EFBFBD>O<EFBFBD>}<7D><><EFBFBD>ވ<EFBFBD><DE88>]<5D><><EFBFBD><EFBFBD><EFBFBD>j,,<EFBFBD>P<EFBFBD><EFBFBD>,,890,
|
||||||
|
2025/08/10,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,150,
|
||||||
|
2025/08/12,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,480,
|
||||||
|
2025/08/14,<EFBFBD>d<EFBFBD>s<EFBFBD>b<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>{<7B>@<40>@<40>i<EFBFBD>ވ<EFBFBD><DE88>]<5D><><EFBFBD>쁨<EFBFBD>]<5D>ʓ<EFBFBD><CA93>j,,<EFBFBD>P<EFBFBD><EFBFBD>,,1530,
|
||||||
|
2025/08/20,<EFBFBD>v<EFBFBD><EFBFBD><EFBFBD>m<EFBFBD>@<40>^<5E>L<EFBFBD>J<EFBFBD><4A><EFBFBD>e<EFBFBD><65>,,<EFBFBD>P<EFBFBD><EFBFBD>,,4927,
|
||||||
|
2025/08/21,<EFBFBD>c<EFBFBD>b<EFBFBD>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,2199,
|
||||||
|
2025/08/22,<EFBFBD>d<EFBFBD>s<EFBFBD>b<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>{<7B>@<40>@<40>i<EFBFBD><69><EFBFBD><EFBFBD><EFBFBD>r<EFBFBD>`<60>X<EFBFBD><58><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>鐲<EFBFBD>j,,<EFBFBD>P<EFBFBD><EFBFBD>,,1780,
|
||||||
|
2025/08/24,<EFBFBD>`<60>a<EFBFBD>d<EFBFBD>l<EFBFBD>`<60>v<EFBFBD><76><EFBFBD>~<7E>A<EFBFBD><41>,,<EFBFBD>P<EFBFBD><EFBFBD>,,1080,
|
||||||
|
2025/08/26,<EFBFBD>n<EFBFBD>o<EFBFBD>d<EFBFBD>m<EFBFBD>`<60>h<EFBFBD>@<40><><EFBFBD>b<EFBFBD>g<EFBFBD>`<60>s<EFBFBD>f<EFBFBD>o<EFBFBD>s<EFBFBD>@<40>r<EFBFBD>t<EFBFBD>a<EFBFBD>r<EFBFBD>b<EFBFBD>q,,<EFBFBD>P<EFBFBD><EFBFBD>,,3369,<EFBFBD><EFBFBD><EFBFBD>n<EFBFBD>ʉ݊z<EFBFBD>F<EFBFBD>Q<EFBFBD>Q<EFBFBD>D<EFBFBD>O<EFBFBD>O<EFBFBD>@<40>t<EFBFBD>r<EFBFBD>c
|
||||||
|
,<EFBFBD>i<EFBFBD>r<EFBFBD>`<60>m<EFBFBD>@<40>e<EFBFBD>q<EFBFBD>`<60>m<EFBFBD>b<EFBFBD>h<EFBFBD>r<EFBFBD>b<EFBFBD>n<EFBFBD>j,,,,,<EFBFBD>~<7E><><EFBFBD>Z<EFBFBD><5A><EFBFBD>[<5B>g<EFBFBD>F<EFBFBD>W<EFBFBD>^<5E>Q<EFBFBD>V<EFBFBD>@<40>P<EFBFBD>T<EFBFBD>R<EFBFBD>D<EFBFBD>P<EFBFBD>R<EFBFBD>U<EFBFBD>S
|
||||||
|
2025/08/28,<EFBFBD>P<EFBFBD>|<7C>Y<EFBFBD>f<EFBFBD><66><EFBFBD>L,,<EFBFBD>P<EFBFBD><EFBFBD>,,19950,
|
||||||
|
2025/08/31,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,1652,
|
||||||
|
2025/08/31,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,1144,
|
||||||
|
2025/09/01,<EFBFBD>G<EFBFBD>b<EFBFBD>N<EFBFBD>X<EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|<7C>^<5E>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,2200,
|
||||||
|
2025/09/01,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40>h<EFBFBD><68><EFBFBD>C<EFBFBD><43><EFBFBD>T<EFBFBD>[<5B>r<EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,270,
|
||||||
|
2025/09/01,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,1200,
|
||||||
|
2025/09/05,<EFBFBD>R<EFBFBD>|<7C>N<EFBFBD>@<40>I<EFBFBD><49><EFBFBD>@<40>y<EFBFBD>C,,<EFBFBD>P<EFBFBD><EFBFBD>,,100,
|
||||||
|
2025/09/08,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>^<5E><><EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|,,<EFBFBD>P<EFBFBD><EFBFBD>,,2612,
|
||||||
|
2025/09/10,<EFBFBD>A<EFBFBD>b<EFBFBD>g<EFBFBD>E<EFBFBD>j<EFBFBD>t<EFBFBD>e<EFBFBD>B,,<EFBFBD>P<EFBFBD><EFBFBD>,,11313,
|
||||||
|
27
testdata/IDEMITSU_2511.csv
vendored
Normal file
27
testdata/IDEMITSU_2511.csv
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<EFBFBD>J<EFBFBD>[<5B>h<EFBFBD><68><EFBFBD><EFBFBD>,<EFBFBD>o<EFBFBD><EFBFBD><EFBFBD>S<EFBFBD>[<5B><><EFBFBD>h<EFBFBD>J<EFBFBD>[<5B>h
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>x<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,2025/11/07
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>z,0000061542
|
||||||
|
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD>X<EFBFBD><EFBFBD><EFBFBD>y<EFBFBD>я<EFBFBD><EFBFBD>i<EFBFBD><EFBFBD>,<EFBFBD>{<7B>l<EFBFBD>E<EFBFBD>Ƒ<EFBFBD><C691>敪,<EFBFBD>x<EFBFBD><EFBFBD><EFBFBD>敪<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>敪,<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>z,<EFBFBD><EFBFBD><EFBFBD>l
|
||||||
|
2025/09/10,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,150,
|
||||||
|
2025/09/10,<EFBFBD>c<EFBFBD><EFBFBD><EFBFBD>n,,<EFBFBD>P<EFBFBD><EFBFBD>,,1423,
|
||||||
|
2025/09/12,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,480,
|
||||||
|
2025/09/14,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,2490,
|
||||||
|
2025/09/16,<EFBFBD>c<EFBFBD>b<EFBFBD>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,3058,
|
||||||
|
2025/09/17,<EFBFBD>`<60>l<EFBFBD>`<60>y<EFBFBD>n<EFBFBD>m<EFBFBD>D<EFBFBD>b<EFBFBD>n<EFBFBD>D<EFBFBD>i<EFBFBD>o,,<EFBFBD>P<EFBFBD><EFBFBD>,,3992,
|
||||||
|
2025/09/18,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,2476,
|
||||||
|
2025/09/21,<EFBFBD>c<EFBFBD>b<EFBFBD>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,5026,
|
||||||
|
2025/09/24,<EFBFBD>`<60>a<EFBFBD>d<EFBFBD>l<EFBFBD>`<60>v<EFBFBD><76><EFBFBD>~<7E>A<EFBFBD><41>,,<EFBFBD>P<EFBFBD><EFBFBD>,,1080,
|
||||||
|
2025/09/26,<EFBFBD>n<EFBFBD>o<EFBFBD>d<EFBFBD>m<EFBFBD>`<60>h<EFBFBD>@<40><><EFBFBD>b<EFBFBD>g<EFBFBD>`<60>s<EFBFBD>f<EFBFBD>o<EFBFBD>s<EFBFBD>@<40>r<EFBFBD>t<EFBFBD>a<EFBFBD>r<EFBFBD>b<EFBFBD>q,,<EFBFBD>P<EFBFBD><EFBFBD>,,3419,<EFBFBD><EFBFBD><EFBFBD>n<EFBFBD>ʉ݊z<EFBFBD>F<EFBFBD>Q<EFBFBD>Q<EFBFBD>D<EFBFBD>O<EFBFBD>O<EFBFBD>@<40>t<EFBFBD>r<EFBFBD>c
|
||||||
|
,<EFBFBD>i<EFBFBD>r<EFBFBD>`<60>m<EFBFBD>@<40>e<EFBFBD>q<EFBFBD>`<60>m<EFBFBD>b<EFBFBD>h<EFBFBD>r<EFBFBD>b<EFBFBD>n<EFBFBD>j,,,,,<EFBFBD>~<7E><><EFBFBD>Z<EFBFBD><5A><EFBFBD>[<5B>g<EFBFBD>F<EFBFBD>X<EFBFBD>^<5E>Q<EFBFBD>V<EFBFBD>@<40>P<EFBFBD>T<EFBFBD>T<EFBFBD>D<EFBFBD>S<EFBFBD>O<EFBFBD>X<EFBFBD>P
|
||||||
|
2025/09/27,<EFBFBD>v<EFBFBD><EFBFBD><EFBFBD>m<EFBFBD>@<40>^<5E>L<EFBFBD>J<EFBFBD><4A><EFBFBD>e<EFBFBD><65>,,<EFBFBD>P<EFBFBD><EFBFBD>,,2133,
|
||||||
|
2025/09/29,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,2050,
|
||||||
|
2025/09/30,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,5440,
|
||||||
|
2025/10/01,<EFBFBD>G<EFBFBD>b<EFBFBD>N<EFBFBD>X<EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|<7C>^<5E>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,2200,
|
||||||
|
2025/10/01,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40>h<EFBFBD><68><EFBFBD>C<EFBFBD><43><EFBFBD>T<EFBFBD>[<5B>r<EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,270,
|
||||||
|
2025/10/01,<EFBFBD>v<EFBFBD><EFBFBD><EFBFBD>m<EFBFBD>@<40>^<5E>L<EFBFBD>J<EFBFBD><4A><EFBFBD>e<EFBFBD><65>,,<EFBFBD>P<EFBFBD><EFBFBD>,,2485,
|
||||||
|
2025/10/01,<EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>X<EFBFBD>g<EFBFBD>A<EFBFBD>i<EFBFBD>a<EFBFBD><61><EFBFBD><EFBFBD><EFBFBD>a<EFBFBD>j,,<EFBFBD>P<EFBFBD><EFBFBD>,,660,
|
||||||
|
2025/10/01,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,1200,
|
||||||
|
2025/10/03,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,6160,
|
||||||
|
2025/10/06,<EFBFBD>c<EFBFBD>b<EFBFBD>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,12738,
|
||||||
|
2025/10/08,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>^<5E><><EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|,,<EFBFBD>P<EFBFBD><EFBFBD>,,2612,
|
||||||
|
27
testdata/IDEMITSU_2512.csv
vendored
Normal file
27
testdata/IDEMITSU_2512.csv
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<EFBFBD>J<EFBFBD>[<5B>h<EFBFBD><68><EFBFBD><EFBFBD>,<EFBFBD>o<EFBFBD><EFBFBD><EFBFBD>S<EFBFBD>[<5B><><EFBFBD>h<EFBFBD>J<EFBFBD>[<5B>h
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>x<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,2025/12/08
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>z,0000109353
|
||||||
|
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD>X<EFBFBD><EFBFBD><EFBFBD>y<EFBFBD>я<EFBFBD><EFBFBD>i<EFBFBD><EFBFBD>,<EFBFBD>{<7B>l<EFBFBD>E<EFBFBD>Ƒ<EFBFBD><C691>敪,<EFBFBD>x<EFBFBD><EFBFBD><EFBFBD>敪<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>敪,<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>z,<EFBFBD><EFBFBD><EFBFBD>l
|
||||||
|
2025/10/28,<EFBFBD>L<EFBFBD><EFBFBD><EFBFBD>@<40>@<40>@<40>@<40>@<40>@<40>@<40>@<40>@<40>@<40><><EFBFBD><EFBFBD>,,<EFBFBD>P<EFBFBD><EFBFBD>,,24794,<EFBFBD><EFBFBD><EFBFBD>ʁF<EFBFBD>Q<EFBFBD>O<EFBFBD>O
|
||||||
|
2025/10/10,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,150,
|
||||||
|
2025/10/12,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,3530,
|
||||||
|
2025/10/12,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,480,
|
||||||
|
2025/10/14,<EFBFBD>G<EFBFBD>b<EFBFBD>N<EFBFBD>X<EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|<7C>^<5E>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,1190,
|
||||||
|
2025/10/20,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,4174,
|
||||||
|
2025/10/20,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,1680,
|
||||||
|
2025/10/21,<EFBFBD>b<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40>i<EFBFBD><69><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40>r<EFBFBD><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,,<EFBFBD>P<EFBFBD><EFBFBD>,,38456,
|
||||||
|
2025/10/23,<EFBFBD>R<EFBFBD>|<7C>N<EFBFBD>@<40>I<EFBFBD><49><EFBFBD>@<40>y<EFBFBD>C,,<EFBFBD>P<EFBFBD><EFBFBD>,,100,
|
||||||
|
2025/10/24,<EFBFBD>`<60>a<EFBFBD>d<EFBFBD>l<EFBFBD>`<60>v<EFBFBD><76><EFBFBD>~<7E>A<EFBFBD><41>,,<EFBFBD>P<EFBFBD><EFBFBD>,,1080,
|
||||||
|
2025/10/26,<EFBFBD>n<EFBFBD>o<EFBFBD>d<EFBFBD>m<EFBFBD>`<60>h<EFBFBD>@<40><><EFBFBD>b<EFBFBD>g<EFBFBD>`<60>s<EFBFBD>f<EFBFBD>o<EFBFBD>s<EFBFBD>@<40>r<EFBFBD>t<EFBFBD>a<EFBFBD>r<EFBFBD>b<EFBFBD>q,,<EFBFBD>P<EFBFBD><EFBFBD>,,3488,<EFBFBD><EFBFBD><EFBFBD>n<EFBFBD>ʉ݊z<EFBFBD>F<EFBFBD>Q<EFBFBD>Q<EFBFBD>D<EFBFBD>O<EFBFBD>O<EFBFBD>@<40>t<EFBFBD>r<EFBFBD>c
|
||||||
|
,<EFBFBD>i<EFBFBD>r<EFBFBD>`<60>m<EFBFBD>@<40>e<EFBFBD>q<EFBFBD>`<60>m<EFBFBD>b<EFBFBD>h<EFBFBD>r<EFBFBD>b<EFBFBD>n<EFBFBD>j,,,,,<EFBFBD>~<7E><><EFBFBD>Z<EFBFBD><5A><EFBFBD>[<5B>g<EFBFBD>F<EFBFBD>P<EFBFBD>O<EFBFBD>^<5E>Q<EFBFBD>V<EFBFBD>@<40>P<EFBFBD>T<EFBFBD>W<EFBFBD>D<EFBFBD>T<EFBFBD>S<EFBFBD>T<EFBFBD>T
|
||||||
|
2025/10/28,<EFBFBD>c<EFBFBD>b<EFBFBD>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,4158,
|
||||||
|
2025/10/29,<EFBFBD>c<EFBFBD><EFBFBD><EFBFBD>n,,<EFBFBD>P<EFBFBD><EFBFBD>,,2154,
|
||||||
|
2025/11/01,<EFBFBD>G<EFBFBD>b<EFBFBD>N<EFBFBD>X<EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|<7C>^<5E>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,2200,
|
||||||
|
2025/11/01,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40>h<EFBFBD><68><EFBFBD>C<EFBFBD><43><EFBFBD>T<EFBFBD>[<5B>r<EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,270,
|
||||||
|
2025/11/01,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,1200,
|
||||||
|
2025/11/04,<EFBFBD>R<EFBFBD>|<7C>N<EFBFBD>@<40>I<EFBFBD><49><EFBFBD>@<40>y<EFBFBD>C,,<EFBFBD>P<EFBFBD><EFBFBD>,,130,
|
||||||
|
2025/11/06,<EFBFBD>c<EFBFBD>b<EFBFBD>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,5236,
|
||||||
|
2025/11/07,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>^<5E><><EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|,,<EFBFBD>P<EFBFBD><EFBFBD>,,2612,
|
||||||
|
2025/11/09,<EFBFBD>R<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>[<5B><><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,1480,
|
||||||
|
2025/11/10,<EFBFBD>A<EFBFBD>b<EFBFBD>g<EFBFBD>E<EFBFBD>j<EFBFBD>t<EFBFBD>e<EFBFBD>B,,<EFBFBD>P<EFBFBD><EFBFBD>,,10791,
|
||||||
|
25
testdata/IDEMITSU_2601.csv
vendored
Normal file
25
testdata/IDEMITSU_2601.csv
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<EFBFBD>J<EFBFBD>[<5B>h<EFBFBD><68><EFBFBD><EFBFBD>,<EFBFBD>o<EFBFBD><EFBFBD><EFBFBD>S<EFBFBD>[<5B><><EFBFBD>h<EFBFBD>J<EFBFBD>[<5B>h
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>x<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,2026/01/07
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>z,0000132408
|
||||||
|
|
||||||
|
<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p<EFBFBD>X<EFBFBD><EFBFBD><EFBFBD>y<EFBFBD>я<EFBFBD><EFBFBD>i<EFBFBD><EFBFBD>,<EFBFBD>{<7B>l<EFBFBD>E<EFBFBD>Ƒ<EFBFBD><C691>敪,<EFBFBD>x<EFBFBD><EFBFBD><EFBFBD>敪<EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>敪,<EFBFBD><EFBFBD><EFBFBD>p<EFBFBD><EFBFBD><EFBFBD>z,<EFBFBD><EFBFBD><EFBFBD>l
|
||||||
|
2025/11/26,<EFBFBD>L<EFBFBD><EFBFBD><EFBFBD>@<40>@<40>@<40>@<40>@<40>@<40>@<40>@<40>@<40>@<40><><EFBFBD><EFBFBD>,,<EFBFBD>P<EFBFBD><EFBFBD>,,23926,<EFBFBD><EFBFBD><EFBFBD>ʁF<EFBFBD>P<EFBFBD>X<EFBFBD>R
|
||||||
|
2025/11/10,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,150,
|
||||||
|
2025/11/12,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,480,
|
||||||
|
2025/11/13,<EFBFBD>v<EFBFBD><EFBFBD><EFBFBD>m<EFBFBD>@<40>^<5E>L<EFBFBD>J<EFBFBD><4A><EFBFBD>e<EFBFBD><65>,,<EFBFBD>P<EFBFBD><EFBFBD>,,18128,
|
||||||
|
2025/11/15,<EFBFBD><EFBFBD><EFBFBD>K<EFBFBD>l<EFBFBD>嗤<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>P<EFBFBD>C<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>w<EFBFBD>O<EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,57000,
|
||||||
|
2025/11/24,<EFBFBD>`<60>a<EFBFBD>d<EFBFBD>l<EFBFBD>`<60>v<EFBFBD><76><EFBFBD>~<7E>A<EFBFBD><41>,,<EFBFBD>P<EFBFBD><EFBFBD>,,1080,
|
||||||
|
2025/11/26,<EFBFBD>n<EFBFBD>o<EFBFBD>d<EFBFBD>m<EFBFBD>`<60>h<EFBFBD>@<40><><EFBFBD>b<EFBFBD>g<EFBFBD>`<60>s<EFBFBD>f<EFBFBD>o<EFBFBD>s<EFBFBD>@<40>r<EFBFBD>t<EFBFBD>a<EFBFBD>r<EFBFBD>b<EFBFBD>q,,<EFBFBD>P<EFBFBD><EFBFBD>,,3568,<EFBFBD><EFBFBD><EFBFBD>n<EFBFBD>ʉ݊z<EFBFBD>F<EFBFBD>Q<EFBFBD>Q<EFBFBD>D<EFBFBD>O<EFBFBD>O<EFBFBD>@<40>t<EFBFBD>r<EFBFBD>c
|
||||||
|
,<EFBFBD>i<EFBFBD>r<EFBFBD>`<60>m<EFBFBD>@<40>e<EFBFBD>q<EFBFBD>`<60>m<EFBFBD>b<EFBFBD>h<EFBFBD>r<EFBFBD>b<EFBFBD>n<EFBFBD>j,,,,,<EFBFBD>~<7E><><EFBFBD>Z<EFBFBD><5A><EFBFBD>[<5B>g<EFBFBD>F<EFBFBD>P<EFBFBD>P<EFBFBD>^<5E>Q<EFBFBD>V<EFBFBD>@<40>P<EFBFBD>U<EFBFBD>Q<EFBFBD>D<EFBFBD>P<EFBFBD>W<EFBFBD>P<EFBFBD>X
|
||||||
|
2025/11/26,<EFBFBD>c<EFBFBD><EFBFBD><EFBFBD>n,,<EFBFBD>P<EFBFBD><EFBFBD>,,2774,
|
||||||
|
2025/11/28,<EFBFBD>`<60><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>v<EFBFBD><76><EFBFBD>C<EFBFBD><43><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,,<EFBFBD>P<EFBFBD><EFBFBD>,,2950,
|
||||||
|
2025/11/28,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,1350,
|
||||||
|
2025/11/28,<EFBFBD>z<EFBFBD>N<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>V<EFBFBD><EFBFBD><EFBFBD>b<EFBFBD>v<EFBFBD>e<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>e<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>V<EFBFBD><EFBFBD><EFBFBD>Ȃ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,3065,
|
||||||
|
2025/11/30,<EFBFBD>`<60>l<EFBFBD>`<60>y<EFBFBD>n<EFBFBD>m<EFBFBD>D<EFBFBD>b<EFBFBD>n<EFBFBD>D<EFBFBD>i<EFBFBD>o,,<EFBFBD>P<EFBFBD><EFBFBD>,,1577,
|
||||||
|
2025/11/30,<EFBFBD>`<60>l<EFBFBD>`<60>y<EFBFBD>n<EFBFBD>m<EFBFBD>D<EFBFBD>b<EFBFBD>n<EFBFBD>D<EFBFBD>i<EFBFBD>o,,<EFBFBD>P<EFBFBD><EFBFBD>,,4971,
|
||||||
|
2025/12/01,<EFBFBD>G<EFBFBD>b<EFBFBD>N<EFBFBD>X<EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|<7C>^<5E>l,,<EFBFBD>P<EFBFBD><EFBFBD>,,2200,
|
||||||
|
2025/12/01,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40>h<EFBFBD><68><EFBFBD>C<EFBFBD><43><EFBFBD>T<EFBFBD>[<5B>r<EFBFBD>X,,<EFBFBD>P<EFBFBD><EFBFBD>,,272,
|
||||||
|
2025/12/01,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,1200,
|
||||||
|
2025/12/03,<EFBFBD>`<60>o<EFBFBD>o<EFBFBD>k<EFBFBD>d<EFBFBD>@<40>b<EFBFBD>n<EFBFBD>l<EFBFBD>@<40>a<EFBFBD>h<EFBFBD>k<EFBFBD>k,,<EFBFBD>P<EFBFBD><EFBFBD>,,480,
|
||||||
|
2025/12/08,<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD>D<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>^<5E><><EFBFBD>T<EFBFBD>|<7C>o<EFBFBD>|,,<EFBFBD>P<EFBFBD><EFBFBD>,,2612,
|
||||||
|
2025/12/10,<EFBFBD>A<EFBFBD>b<EFBFBD>g<EFBFBD>E<EFBFBD>j<EFBFBD>t<EFBFBD>e<EFBFBD>B,,<EFBFBD>P<EFBFBD><EFBFBD>,,4625,
|
||||||
|
Reference in New Issue
Block a user