537 lines
25 KiB
PHP
537 lines
25 KiB
PHP
@extends('layouts.layout-2')
|
|
|
|
@section('content')
|
|
|
|
@if (session('success'))
|
|
<div class="alert alert-success alert-dismissible fade show">
|
|
{{ session('success') }}
|
|
<button type="button" class="close" data-dismiss="alert"><span>×</span></button>
|
|
</div>
|
|
@endif
|
|
@if (session('error'))
|
|
<div class="alert alert-danger alert-dismissible fade show">
|
|
{{ session('error') }}
|
|
<button type="button" class="close" data-dismiss="alert"><span>×</span></button>
|
|
</div>
|
|
@endif
|
|
@if (session('info'))
|
|
<div class="alert alert-info alert-dismissible fade show">
|
|
{{ session('info') }}
|
|
<button type="button" class="close" data-dismiss="alert"><span>×</span></button>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="card">
|
|
<h5 class="card-header">
|
|
<i class="fas fa-file-invoice"></i> DATEV Export - Steuerberater
|
|
</h5>
|
|
<div class="card-body">
|
|
|
|
<div class="form-row align-items-center px-0 pb-2 pt-0">
|
|
<div class="col-6 col-sm-3 col-md-3 col-lg-2 mb-1">
|
|
<label class="small mb-1">Monat</label>
|
|
<select class="custom-select" name="payment_taxadvisor_filter_month" id="filter_month">
|
|
@foreach ($filter_months as $key => $value)
|
|
<option value="{{ $key }}" @if (session('payment_taxadvisor_filter_month') == $key) selected @endif>
|
|
{{ $value }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="col-6 col-sm-3 col-md-3 col-lg-2 mb-1">
|
|
<label class="small mb-1">Jahr</label>
|
|
<select class="custom-select" name="payment_taxadvisor_filter_year" id="filter_year">
|
|
@foreach ($filter_years as $key => $value)
|
|
<option value="{{ $value }}" @if (session('payment_taxadvisor_filter_year') == $value) selected @endif>
|
|
{{ $value }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="col-12 col-sm-6 col-md-6 col-lg-4 mb-1 d-flex align-items-end">
|
|
<button type="button" class="btn btn-info mr-2" id="btn-preview">
|
|
<i class="fas fa-search"></i> Vorschau laden
|
|
</button>
|
|
<button type="button" class="btn btn-primary" id="btn-generate"
|
|
@if ($current_export && $current_export->isLocked()) disabled @endif>
|
|
<i class="fas fa-cogs"></i> Export generieren
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
@if ($current_export)
|
|
<div class="alert alert-{{ $current_export->isLocked() ? 'secondary' : 'info' }} mt-3">
|
|
<div class="d-flex justify-content-between align-items-center flex-wrap">
|
|
<div class="mb-1">
|
|
<strong>Aktueller Export:</strong>
|
|
{!! $current_export->status_badge !!}
|
|
— {{ $current_export->total_lines }} Zeilen
|
|
({{ $current_export->invoice_count }} RE,
|
|
{{ $current_export->credit_count }} GS,
|
|
{{ $current_export->cancellation_count }} ST)
|
|
— {{ $current_export->created_at->format('d.m.Y H:i') }}
|
|
@if ($current_export->warning_count > 0)
|
|
<span class="badge badge-warning ml-1">{{ $current_export->warning_count }}
|
|
Warnungen</span>
|
|
@endif
|
|
</div>
|
|
<div class="mb-1">
|
|
<a href="{{ route('admin_payments_taxadvisor_download', $current_export->id) }}"
|
|
class="btn btn-sm btn-success">
|
|
<i class="fas fa-download"></i> CSV
|
|
</a>
|
|
@if (!$current_export->isLocked())
|
|
<form action="{{ route('admin_payments_taxadvisor_lock', $current_export->id) }}"
|
|
method="POST" class="d-inline" onsubmit="return confirm('Export wirklich sperren?')">
|
|
@csrf
|
|
<button type="submit" class="btn btn-sm btn-dark">
|
|
<i class="fas fa-lock"></i> Sperren
|
|
</button>
|
|
</form>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div id="preview-section" style="display:none;">
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-sm-6 col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body py-3">
|
|
<div class="text-muted small">Rechnungen</div>
|
|
<h4 class="mb-0" id="stat-invoices">0</h4>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body py-3">
|
|
<div class="text-muted small">Gutschriften</div>
|
|
<h4 class="mb-0" id="stat-credits">0</h4>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body py-3">
|
|
<div class="text-muted small">Stornos</div>
|
|
<h4 class="mb-0" id="stat-cancellations">0</h4>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-md-3">
|
|
<div class="card text-center">
|
|
<div class="card-body py-3">
|
|
<div class="text-muted small">Gesamt Zeilen</div>
|
|
<h4 class="mb-0" id="stat-total">0</h4>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-sm-6 col-md-4">
|
|
<div class="card">
|
|
<div class="card-body py-3">
|
|
<div class="text-muted small">Umsatz (Haben)</div>
|
|
<h5 class="mb-0 text-success" id="stat-revenue">0,00 EUR</h5>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-md-4">
|
|
<div class="card">
|
|
<div class="card-body py-3">
|
|
<div class="text-muted small">Provisionen (Soll)</div>
|
|
<h5 class="mb-0 text-warning" id="stat-commissions">0,00 EUR</h5>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-md-4">
|
|
<div class="card">
|
|
<div class="card-body py-3">
|
|
<div class="text-muted small">Stornos (Soll)</div>
|
|
<h5 class="mb-0 text-danger" id="stat-cancellation-amount">0,00 EUR</h5>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="validation-section" style="display:none;">
|
|
<div id="validation-errors" class="alert alert-danger" style="display:none;">
|
|
<strong><i class="fas fa-exclamation-triangle"></i> Fehler:</strong>
|
|
<ul id="validation-errors-list" class="mb-0 mt-1"></ul>
|
|
</div>
|
|
<div id="validation-warnings" class="alert alert-warning" style="display:none;">
|
|
<strong><i class="fas fa-exclamation-circle"></i> Warnungen:</strong>
|
|
<ul id="validation-warnings-list" class="mb-0 mt-1"></ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h5 class="card-header">Vorschau: Konten-Zusammenfassung</h5>
|
|
<div class="card-body p-0">
|
|
<table class="table table-striped table-bordered mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Konto</th>
|
|
<th>BU-Schl.</th>
|
|
<th>S/H</th>
|
|
<th>Anzahl</th>
|
|
<th class="text-right">Summe</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="preview-table-body">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if ($current_export)
|
|
<div class="card mt-3">
|
|
<h5 class="card-header">
|
|
Buchungszeilen (Export {{ $current_export->period_label }})
|
|
</h5>
|
|
<div class="card-body">
|
|
<div class="card-datatable table-responsive">
|
|
<table class="table table-striped table-bordered" id="datatable-datev-lines">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Typ</th>
|
|
<th>Umsatz</th>
|
|
<th>S/H</th>
|
|
<th>Konto</th>
|
|
<th>Gegenkonto</th>
|
|
<th>BU</th>
|
|
<th>Belegdatum</th>
|
|
<th>Belegfeld 1</th>
|
|
<th>Buchungstext</th>
|
|
<th>USt-ID</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
@if ($recent_exports->count() > 0)
|
|
<div class="card mt-3">
|
|
<h5 class="card-header">
|
|
<i class="fas fa-history"></i> Export-Historie
|
|
</h5>
|
|
<div class="card-body p-0">
|
|
<table class="table table-striped table-bordered mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Periode</th>
|
|
<th>Status</th>
|
|
<th>Zeilen</th>
|
|
<th class="text-right">Umsatz</th>
|
|
<th class="text-right">Provisionen</th>
|
|
<th>Erstellt</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($recent_exports as $exp)
|
|
<tr>
|
|
<td>{{ $exp->period_label }}</td>
|
|
<td>{!! $exp->status_badge !!}</td>
|
|
<td>{{ $exp->total_lines }}</td>
|
|
<td class="text-right">{{ number_format($exp->total_revenue, 2, ',', '.') }} EUR</td>
|
|
<td class="text-right">{{ number_format($exp->total_commissions, 2, ',', '.') }} EUR</td>
|
|
<td>{{ $exp->created_at->format('d.m.Y H:i') }}</td>
|
|
<td>
|
|
<a href="{{ route('admin_payments_taxadvisor_download', $exp->id) }}"
|
|
class="btn btn-xs btn-success" title="Download">
|
|
<i class="fas fa-download"></i>
|
|
</a>
|
|
@if (!$exp->isLocked())
|
|
<form action="{{ route('admin_payments_taxadvisor_lock', $exp->id) }}"
|
|
method="POST" class="d-inline" onsubmit="return confirm('Export sperren?')">
|
|
@csrf
|
|
<button type="submit" class="btn btn-xs btn-dark" title="Sperren">
|
|
<i class="fas fa-lock"></i>
|
|
</button>
|
|
</form>
|
|
<form action="{{ route('admin_payments_taxadvisor_destroy', $exp->id) }}"
|
|
method="POST" class="d-inline"
|
|
onsubmit="return confirm('Export wirklich loeschen?')">
|
|
@csrf
|
|
@method('DELETE')
|
|
<button type="submit" class="btn btn-xs btn-danger" title="Loeschen">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</form>
|
|
@else
|
|
<span class="badge badge-dark"><i class="fas fa-lock"></i></span>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="modal fade" id="generateModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">DATEV Export generieren</h5>
|
|
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p>Soll der DATEV-Export fuer <strong id="modal-period"></strong> generiert werden?</p>
|
|
<p class="text-muted small">Ein vorhandener frueherer Export wird ersetzt.</p>
|
|
<div id="modal-preview-stats" class="mb-2"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
|
|
<form id="form-generate" method="POST" action="{{ route('admin_payments_taxadvisor_generate') }}">
|
|
@csrf
|
|
<input type="hidden" name="month" id="generate-month">
|
|
<input type="hidden" name="year" id="generate-year">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-cogs"></i> Jetzt generieren
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
|
|
function fmtNum(num) {
|
|
return new Intl.NumberFormat('de-DE', {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2
|
|
}).format(num);
|
|
}
|
|
|
|
function renderValidationEntry(entry) {
|
|
if (typeof entry === 'string') {
|
|
return entry;
|
|
}
|
|
var html = '<span>' + entry.message + '</span>';
|
|
var links = [];
|
|
if (entry.source_id && entry.belegfeld1) {
|
|
links.push('<a href="{{ url('/admin/payments/invoice') }}?search=' + entry.belegfeld1 +
|
|
'" target="_blank" title="Rechnung anzeigen"><i class="fas fa-file-invoice"></i> ' +
|
|
entry.belegfeld1 + '</a>');
|
|
}
|
|
if (entry.user_id) {
|
|
links.push('<a href="{{ url('/admin/sales/users/detail') }}/' + entry.user_id +
|
|
'" target="_blank" title="Berater anzeigen"><i class="fas fa-user"></i> User #' + entry
|
|
.user_id + '</a>');
|
|
}
|
|
if (links.length > 0) {
|
|
html += ' <span class="ml-2">' + links.join(' · ') + '</span>';
|
|
}
|
|
return html;
|
|
}
|
|
|
|
$('#btn-preview').on('click', function() {
|
|
var btn = $(this);
|
|
var month = $('#filter_month').val();
|
|
var year = $('#filter_year').val();
|
|
btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Laden...');
|
|
|
|
$.ajax({
|
|
url: '{!! route('admin_payments_taxadvisor_preview') !!}',
|
|
method: 'POST',
|
|
data: {
|
|
_token: '{{ csrf_token() }}',
|
|
month: month,
|
|
year: year
|
|
},
|
|
success: function(res) {
|
|
if (res.success) {
|
|
var s = res.data.summary;
|
|
$('#stat-invoices').text(s.invoice_count);
|
|
$('#stat-credits').text(s.credit_count);
|
|
$('#stat-cancellations').text(s.cancellation_count);
|
|
$('#stat-total').text(s.total_lines);
|
|
$('#stat-revenue').text(fmtNum(s.total_revenue) + ' EUR');
|
|
$('#stat-commissions').text(fmtNum(s.total_commissions) + ' EUR');
|
|
$('#stat-cancellation-amount').text(fmtNum(s.total_cancellations) +
|
|
' EUR');
|
|
|
|
var tbody = $('#preview-table-body');
|
|
tbody.empty();
|
|
var grouped = res.data.grouped;
|
|
if (grouped && grouped.length > 0) {
|
|
for (var i = 0; i < grouped.length; i++) {
|
|
var r = grouped[i];
|
|
var cls = r.soll_haben === 'H' ? 'badge-success' :
|
|
'badge-warning';
|
|
tbody.append('<tr><td>' + r.konto + '</td><td>' + r
|
|
.bu_schluessel + '</td><td><span class="badge ' +
|
|
cls + '">' + r.soll_haben + '</span></td><td>' + r
|
|
.count + '</td><td class="text-right">' + fmtNum(r
|
|
.total) + ' EUR</td></tr>');
|
|
}
|
|
} else {
|
|
tbody.append(
|
|
'<tr><td colspan="5" class="text-center text-muted">Keine Daten</td></tr>'
|
|
);
|
|
}
|
|
|
|
var v = res.data.validation;
|
|
$('#validation-section').show();
|
|
if (v.errors && v.errors.length > 0) {
|
|
var el = $('#validation-errors-list');
|
|
el.empty();
|
|
for (var e = 0; e < v.errors.length; e++) {
|
|
el.append('<li>' + renderValidationEntry(v.errors[e]) +
|
|
'</li>');
|
|
}
|
|
$('#validation-errors').show();
|
|
} else {
|
|
$('#validation-errors').hide();
|
|
}
|
|
|
|
if (v.warnings && v.warnings.length > 0) {
|
|
var wl = $('#validation-warnings-list');
|
|
wl.empty();
|
|
var max = Math.min(v.warnings.length, 20);
|
|
for (var w = 0; w < max; w++) {
|
|
wl.append('<li>' + renderValidationEntry(v.warnings[w]) +
|
|
'</li>');
|
|
}
|
|
if (v.warnings.length > 20) {
|
|
wl.append('<li><em>... und ' + (v.warnings.length - 20) +
|
|
' weitere</em></li>');
|
|
}
|
|
$('#validation-warnings').show();
|
|
} else {
|
|
$('#validation-warnings').hide();
|
|
}
|
|
|
|
$('#preview-section').slideDown();
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
alert('Fehler: ' + (xhr.responseJSON ? xhr.responseJSON.message :
|
|
'Unbekannt'));
|
|
},
|
|
complete: function() {
|
|
btn.prop('disabled', false).html(
|
|
'<i class="fas fa-search"></i> Vorschau laden');
|
|
}
|
|
});
|
|
});
|
|
|
|
$('#btn-generate').on('click', function() {
|
|
var month = $('#filter_month').val();
|
|
var year = $('#filter_year').val();
|
|
var monthText = $('#filter_month option:selected').text();
|
|
$('#modal-period').text(monthText + ' ' + year);
|
|
$('#generate-month').val(month);
|
|
$('#generate-year').val(year);
|
|
var stats = '';
|
|
var inv = $('#stat-invoices').text();
|
|
if (inv !== '0') {
|
|
stats = '<div class="small text-muted">' + inv + ' Rechnungen, ' + $('#stat-credits')
|
|
.text() + ' Gutschriften, ' + $('#stat-cancellations').text() + ' Stornos</div>';
|
|
}
|
|
$('#modal-preview-stats').html(stats);
|
|
$('#generateModal').modal('show');
|
|
});
|
|
|
|
$('#filter_month, #filter_year').on('change', function() {
|
|
var month = $('#filter_month').val();
|
|
var year = $('#filter_year').val();
|
|
window.location.href = '{!! route('admin_payments_taxadvisor') !!}?payment_taxadvisor_filter_month=' + month +
|
|
'&payment_taxadvisor_filter_year=' + year;
|
|
});
|
|
|
|
@if ($current_export)
|
|
$('#datatable-datev-lines').DataTable({
|
|
processing: true,
|
|
serverSide: true,
|
|
stateSave: true,
|
|
ajax: {
|
|
url: '{!! route('admin_payments_taxadvisor_datatable') !!}',
|
|
data: function(d) {
|
|
d.export_id = {{ $current_export->id }};
|
|
}
|
|
},
|
|
order: [
|
|
[0, 'asc']
|
|
],
|
|
columns: [{
|
|
data: 'line_number',
|
|
orderable: true,
|
|
searchable: false
|
|
},
|
|
{
|
|
data: 'source_type_label',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
{
|
|
data: 'amount_display',
|
|
orderable: false,
|
|
searchable: false
|
|
},
|
|
{
|
|
data: 'soll_haben',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
{
|
|
data: 'konto',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
{
|
|
data: 'gegenkonto',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
{
|
|
data: 'bu_schluessel',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
{
|
|
data: 'belegdatum_display',
|
|
orderable: false,
|
|
searchable: false
|
|
},
|
|
{
|
|
data: 'belegfeld1',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
{
|
|
data: 'buchungstext',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
{
|
|
data: 'eu_ustid',
|
|
orderable: false,
|
|
searchable: true
|
|
},
|
|
],
|
|
bLengthChange: false,
|
|
iDisplayLength: 50,
|
|
language: {
|
|
url: '/js/datatables-{{ \App::getLocale() }}.json'
|
|
}
|
|
});
|
|
@endif
|
|
});
|
|
</script>
|
|
|
|
@endsection
|