123 lines
3.9 KiB
HTML
123 lines
3.9 KiB
HTML
{% extends 'base.html' %}
|
|
|
|
{% block title %}明細編集{% endblock %}
|
|
|
|
{% block content %}
|
|
<section>
|
|
<h2>明細編集</h2>
|
|
{% if imported %}
|
|
<p>取込件数: {{ imported }} / 重複件数: {{ duplicated }} / 文字コード: {{ encoding }}</p>
|
|
{% endif %}
|
|
<form>
|
|
{% csrf_token %}
|
|
</form>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>利用日</th>
|
|
<th>利用先</th>
|
|
<th>金額</th>
|
|
<th>店舗区分</th>
|
|
<th>経費区分</th>
|
|
<th>区分</th>
|
|
<th>備考</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for expense in expenses %}
|
|
<tr data-expense-id="{{ expense.id }}">
|
|
<td>{{ expense.use_date }}</td>
|
|
<td>{{ expense.description }}</td>
|
|
<td>{{ expense.amount }}</td>
|
|
<td>
|
|
<select class="js-expense-field" data-field="store_id">
|
|
<option value="">未設定</option>
|
|
{% for store in stores %}
|
|
<option value="{{ store.id }}" {% if expense.store_id == store.id %}selected{% endif %}>
|
|
{{ store.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select class="js-expense-field" data-field="expense_category_id">
|
|
<option value="">未設定</option>
|
|
{% for category in categories %}
|
|
<option value="{{ category.id }}" {% if expense.expense_category_id == category.id %}selected{% endif %}>
|
|
{{ category.name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<select class="js-expense-field" data-field="owner_type">
|
|
<option value="pending" {% if expense.owner_type == "pending" %}selected{% endif %}>
|
|
未設定
|
|
</option>
|
|
<option value="company" {% if expense.owner_type == "company" %}selected{% endif %}>
|
|
会社
|
|
</option>
|
|
<option value="personal" {% if expense.owner_type == "personal" %}selected{% endif %}>
|
|
家計
|
|
</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<input
|
|
class="js-expense-field"
|
|
data-field="note"
|
|
type="text"
|
|
value="{{ expense.note }}"
|
|
>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="7">データがありません。</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
<script>
|
|
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]')?.value;
|
|
|
|
async function saveExpense(expenseId, payload) {
|
|
const response = await fetch(`/expenses/${expenseId}/update/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': csrfToken,
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error('保存に失敗しました');
|
|
}
|
|
return response.json();
|
|
}
|
|
|
|
function coerceValue(field, value) {
|
|
return value;
|
|
}
|
|
|
|
document.querySelectorAll('.js-expense-field').forEach((element) => {
|
|
const eventName = element.tagName === 'INPUT' ? 'change' : 'change';
|
|
element.addEventListener(eventName, async (event) => {
|
|
const row = event.target.closest('tr');
|
|
if (!row) {
|
|
return;
|
|
}
|
|
const expenseId = row.dataset.expenseId;
|
|
const field = event.target.dataset.field;
|
|
const value = coerceValue(field, event.target.value);
|
|
try {
|
|
await saveExpense(expenseId, { [field]: value });
|
|
} catch (error) {
|
|
alert(error.message);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
</section>
|
|
{% endblock %}
|