Initial commit
This commit is contained in:
commit
983cee0320
322 changed files with 57174 additions and 0 deletions
304
backup/first -fina app/app/templates/bank_import_review.html
Normal file
304
backup/first -fina app/app/templates/bank_import_review.html
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ _('bank.review_title') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1><i class="fas fa-check-circle"></i> {{ _('bank.review_title') }}</h1>
|
||||
<p class="text-muted">{{ _('bank.review_subtitle') }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Summary Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-md-3">
|
||||
<h3 class="text-primary">{{ total_found }}</h3>
|
||||
<p class="text-muted">{{ _('bank.transactions_found') }}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h3 class="text-info">{{ bank_format }}</h3>
|
||||
<p class="text-muted">{{ _('bank.detected_format') }}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h3 class="text-success" id="selectedCount">0</h3>
|
||||
<p class="text-muted">{{ _('bank.selected') }}</p>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h3 class="text-warning" id="unmappedCount">{{ total_found }}</h3>
|
||||
<p class="text-muted">{{ _('bank.unmapped') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parse Errors (if any) -->
|
||||
{% if parse_errors %}
|
||||
<div class="alert alert-warning">
|
||||
<strong><i class="fas fa-exclamation-triangle"></i> {{ _('bank.parse_warnings') }}:</strong>
|
||||
<ul class="mb-0">
|
||||
{% for error in parse_errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Bank Format Warning -->
|
||||
{% if bank_format in ['generic', 'unknown', 'Generic'] %}
|
||||
<div class="alert alert-warning">
|
||||
<strong><i class="fas fa-exclamation-triangle"></i> {{ _('bank.format_not_recognized') }}</strong><br>
|
||||
{{ _('bank.format_not_recognized_hint') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Import Form -->
|
||||
<form method="POST" action="{{ url_for('main.bank_import_confirm') }}" id="importForm">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<!-- Bulk Actions -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check">
|
||||
<input type="checkbox"
|
||||
class="form-check-input"
|
||||
id="selectAll"
|
||||
onchange="toggleAll(this)">
|
||||
<label class="form-check-label" for="selectAll">
|
||||
{{ _('bank.select_all') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-secondary"
|
||||
onclick="selectExpenses()">
|
||||
<i class="fas fa-arrow-down"></i> {{ _('bank.select_expenses') }}
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-secondary"
|
||||
onclick="selectIncome()">
|
||||
<i class="fas fa-arrow-up"></i> {{ _('bank.select_income') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transactions Table -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>{{ _('bank.transactions_to_import') }}</h3>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover mb-0">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th width="5%">
|
||||
<input type="checkbox" id="selectAllTable" onchange="toggleAll(this)">
|
||||
</th>
|
||||
<th width="10%">{{ _('bank.date') }}</th>
|
||||
<th width="35%">{{ _('bank.description') }}</th>
|
||||
<th width="15%">{{ _('bank.amount') }}</th>
|
||||
<th width="10%">{{ _('bank.type') }}</th>
|
||||
<th width="25%">{{ _('bank.category') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for trans in transactions %}
|
||||
<tr class="transaction-row {% if trans.amount < 0 %}expense-row{% else %}income-row{% endif %}">
|
||||
<td>
|
||||
<input type="checkbox"
|
||||
name="selected_transactions"
|
||||
value="{{ loop.index0 }}"
|
||||
class="transaction-checkbox"
|
||||
onchange="updateCounts()">
|
||||
</td>
|
||||
<td>{{ trans.date.strftime('%Y-%m-%d') }}</td>
|
||||
<td>
|
||||
<small>{{ trans.description }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="{% if trans.amount < 0 %}text-danger{% else %}text-success{% endif %}">
|
||||
{% if trans.amount < 0 %}-{% else %}+{% endif %}
|
||||
{{ "%.2f"|format(trans.amount|abs) }} {{ _('expense.currency') }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{% if trans.amount < 0 %}
|
||||
<span class="badge badge-danger">{{ _('bank.expense') }}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-success">{{ _('bank.income') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<select name="category_{{ loop.index0 }}"
|
||||
class="form-control form-control-sm category-select"
|
||||
onchange="updateCounts()">
|
||||
<option value="">{{ _('bank.select_category') }}</option>
|
||||
{% for category in categories %}
|
||||
<option value="{{ category.id }}">{{ category.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="mt-4">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="{{ url_for('main.bank_import') }}" class="btn btn-secondary btn-block">
|
||||
<i class="fas fa-arrow-left"></i> {{ _('bank.back') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<button type="submit" class="btn btn-primary btn-block" id="submitBtn" disabled>
|
||||
<i class="fas fa-check"></i> {{ _('bank.import_selected') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.transaction-row {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.transaction-row:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.category-select {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.expense-row {
|
||||
border-left: 3px solid #dc3545;
|
||||
}
|
||||
|
||||
.income-row {
|
||||
border-left: 3px solid #28a745;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.table {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Toggle all checkboxes
|
||||
function toggleAll(checkbox) {
|
||||
const checkboxes = document.querySelectorAll('.transaction-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = checkbox.checked;
|
||||
});
|
||||
updateCounts();
|
||||
}
|
||||
|
||||
// Select expenses only
|
||||
function selectExpenses() {
|
||||
const checkboxes = document.querySelectorAll('.expense-row .transaction-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = true;
|
||||
});
|
||||
updateCounts();
|
||||
}
|
||||
|
||||
// Select income only
|
||||
function selectIncome() {
|
||||
const checkboxes = document.querySelectorAll('.income-row .transaction-checkbox');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = true;
|
||||
});
|
||||
updateCounts();
|
||||
}
|
||||
|
||||
// Update counts and enable/disable submit button
|
||||
function updateCounts() {
|
||||
const checkboxes = document.querySelectorAll('.transaction-checkbox:checked');
|
||||
const selectedCount = checkboxes.length;
|
||||
|
||||
// Count how many selected transactions have a category
|
||||
let mappedCount = 0;
|
||||
checkboxes.forEach(cb => {
|
||||
const row = cb.closest('tr');
|
||||
const categorySelect = row.querySelector('.category-select');
|
||||
if (categorySelect && categorySelect.value) {
|
||||
mappedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
const unmappedCount = selectedCount - mappedCount;
|
||||
|
||||
// Update UI
|
||||
document.getElementById('selectedCount').textContent = selectedCount;
|
||||
document.getElementById('unmappedCount').textContent = unmappedCount;
|
||||
|
||||
// Enable submit button only if at least one transaction is selected with a category
|
||||
document.getElementById('submitBtn').disabled = (mappedCount === 0);
|
||||
}
|
||||
|
||||
// Auto-select checkbox when category is selected
|
||||
document.querySelectorAll('.category-select').forEach(select => {
|
||||
select.addEventListener('change', function() {
|
||||
if (this.value) {
|
||||
const row = this.closest('tr');
|
||||
const checkbox = row.querySelector('.transaction-checkbox');
|
||||
checkbox.checked = true;
|
||||
}
|
||||
updateCounts();
|
||||
});
|
||||
});
|
||||
|
||||
// Confirm before submit
|
||||
document.getElementById('importForm').addEventListener('submit', function(e) {
|
||||
const selectedCount = document.querySelectorAll('.transaction-checkbox:checked').length;
|
||||
if (selectedCount === 0) {
|
||||
e.preventDefault();
|
||||
alert('{{ _('bank.no_transactions_selected') }}');
|
||||
return false;
|
||||
}
|
||||
|
||||
const unmappedCount = parseInt(document.getElementById('unmappedCount').textContent);
|
||||
if (unmappedCount > 0) {
|
||||
const msg = '{{ _('bank.confirm_unmapped') }}'.replace('{count}', unmappedCount);
|
||||
if (!confirm(msg)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
document.getElementById('submitBtn').disabled = true;
|
||||
document.getElementById('submitBtn').innerHTML = '<i class="fas fa-spinner fa-spin"></i> {{ _('bank.importing') }}...';
|
||||
});
|
||||
|
||||
// Initial update
|
||||
updateCounts();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue