April 2026 waren Wirtschaft Feedback

This commit is contained in:
Kevin Adametz 2026-04-10 17:14:38 +02:00
parent 02f2a4c23e
commit 9ce711d6b2
167 changed files with 25278 additions and 8518 deletions

View file

@ -32,11 +32,31 @@
</div>
</div>
<div class="form-row">
<div class="form-group col-sm-4">
<label class="form-label" for="material_quality_id">{{ __('Rohstoffqualität') }}</label>
<select name="material_quality_id" id="material_quality_id" class="form-control">
<option value="">{{ __('— keine Angabe —') }}</option>
@foreach(\App\Models\MaterialQuality::orderBy('pos')->orderBy('name')->get() as $quality)
<option value="{{ $quality->id }}" @selected((string)old('material_quality_id', $model->material_quality_id) === (string)$quality->id)>{{ $quality->name }}</option>
@endforeach
</select>
</div>
</div>
<div class="form-row">
<div class="form-group col-sm-2">
<label class="form-label" for="pos">{{ __('pos') }}</label>
{{ Form::text('pos', $model->pos, array('placeholder'=>__('pos'), 'class'=>'form-control', 'id'=>'pos')) }}
</div>
<div class="form-group col-sm-3">
<label class="form-label" for="default_factor">{{ __('Standard-Schwundfaktor') }}</label>
{{ Form::text('default_factor', $model->default_factor !== null ? formatNumber($model->default_factor) : formatNumber(1.10), array('placeholder'=>'1,10', 'class'=>'form-control', 'id'=>'default_factor')) }}
</div>
<div class="form-group col-sm-3">
<label class="form-label" for="min_stock_alert">{{ __('Meldebestand (g)') }}</label>
{{ Form::text('min_stock_alert', $model->min_stock_alert !== null ? formatNumber($model->min_stock_alert) : '', array('placeholder'=>__('optional'), 'class'=>'form-control', 'id'=>'min_stock_alert')) }}
</div>
</div>
</div>

View file

@ -0,0 +1,35 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header">{{ $model->exists ? __('Lagerort bearbeiten') : __('Lagerort anlegen') }}</h6>
<div class="card-body">
<form method="post" action="{{ $model->exists ? route('admin.inventory.locations.update', $model) : route('admin.inventory.locations.store') }}">
@csrf
@if($model->exists)
@method('PUT')
@endif
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
<input type="text" name="name" id="name" class="form-control @error('name') is-invalid @enderror"
value="{{ old('name', $model->name) }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" name="active" value="1" class="custom-control-input"
@checked(old('active', $model->active))>
<span class="custom-control-label">{{ __('Aktiv') }}</span>
</label>
</div>
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.locations.index') }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,60 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Lagerorte') }}</span>
<a href="{{ route('admin.inventory.locations.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Status') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($values as $value)
<tr>
<td>
<a href="{{ route('admin.inventory.locations.edit', $value) }}" class="btn icon-btn btn-sm btn-primary">
<span class="far fa-edit"></span>
</a>
</td>
<td>{{ $value->name }}</td>
<td data-sort="{{ $value->active ? 1 : 0 }}">
@if($value->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
@endif
</td>
<td>
<form action="{{ route('admin.inventory.locations.destroy', $value) }}" method="post" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0" title="{{ __('Delete') }}"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[1, "asc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -0,0 +1,36 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header">{{ $model->exists ? __('Rohstoffqualität bearbeiten') : __('Rohstoffqualität anlegen') }}</h6>
<div class="card-body">
<form method="post" action="{{ $model->exists ? route('admin.inventory.material-qualities.update', $model) : route('admin.inventory.material-qualities.store') }}">
@csrf
@if($model->exists)
@method('PUT')
@endif
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
<input type="text" name="name" id="name" class="form-control @error('name') is-invalid @enderror"
value="{{ old('name', $model->name) }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="pos">{{ __('Pos') }}</label>
<input type="number" name="pos" id="pos" class="form-control @error('pos') is-invalid @enderror"
value="{{ old('pos', $model->pos) }}" min="0" max="255">
@error('pos')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.material-qualities.index') }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,53 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Rohstoffqualität') }}</span>
<a href="{{ route('admin.inventory.material-qualities.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
<th>{{ __('Pos') }}</th>
<th>{{ __('Name') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($values as $value)
<tr>
<td>
<a href="{{ route('admin.inventory.material-qualities.edit', $value) }}" class="btn icon-btn btn-sm btn-primary">
<span class="far fa-edit"></span>
</a>
</td>
<td>{{ $value->pos }}</td>
<td>{{ $value->name }}</td>
<td>
<form action="{{ route('admin.inventory.material-qualities.destroy', $value) }}" method="post" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[1, "asc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -0,0 +1,108 @@
@php
$isShipping = ($category ?? 'packaging') === 'shipping';
$pageLabel = $isShipping ? __('Versandverpackung') : __('Produktverpackung');
@endphp
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header">{{ $model->exists ? $pageLabel . ' ' . __('bearbeiten') : $pageLabel . ' ' . __('anlegen') }}</h6>
<div class="card-body">
<form method="post" action="{{ $model->exists ? route('admin.inventory.packaging-items.update', $model) : route('admin.inventory.packaging-items.store') }}">
@csrf
@if($model->exists)
@method('PUT')
@endif
<input type="hidden" name="category" value="{{ old('category', $model->category) }}">
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
<input type="text" name="name" id="name" class="form-control @error('name') is-invalid @enderror"
value="{{ old('name', $model->name) }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="packaging_material_id">{{ __('Verpackungsmaterial') }}</label>
<div class="light-style">
<select name="packaging_material_id" id="packaging_material_id" class="w-100 @error('packaging_material_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($packagingMaterials as $pm)
<option value="{{ $pm->id }}" @selected((string)old('packaging_material_id', $model->packaging_material_id) === (string)$pm->id)>{{ $pm->name }}</option>
@endforeach
</select>
</div>
@error('packaging_material_id')
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="weight_grams">{{ __('Gewicht (g)') }}</label>
<input type="text" name="weight_grams" id="weight_grams" class="form-control @error('weight_grams') is-invalid @enderror"
value="{{ old('weight_grams', $model->weight_grams) }}">
@error('weight_grams')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-md-6">
<label for="min_stock_alert">{{ __('Mindestbestand Warnung') }}</label>
<input type="number" name="min_stock_alert" id="min_stock_alert" class="form-control @error('min_stock_alert') is-invalid @enderror"
value="{{ old('min_stock_alert', $model->min_stock_alert) }}" min="0">
@error('min_stock_alert')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="form-group">
<label for="supplier_id">{{ __('Lieferant') }}</label>
<div class="light-style">
<select name="supplier_id" id="supplier_id" class="w-100 @error('supplier_id') is-invalid @enderror">
<option value="">{{ __('—') }}</option>
@foreach($suppliers as $sup)
<option value="{{ $sup->id }}" @selected((string)old('supplier_id', $model->supplier_id) === (string)$sup->id)>{{ $sup->name }}</option>
@endforeach
</select>
</div>
@error('supplier_id')
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="url">{{ __('URL (Onlineshop)') }}</label>
<input type="url" name="url" id="url" class="form-control @error('url') is-invalid @enderror"
value="{{ old('url', $model->url) }}" placeholder="https://">
@error('url')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" name="active" value="1" class="custom-control-input"
@checked(old('active', $model->active))>
<span class="custom-control-label">{{ __('Aktiv') }}</span>
</label>
</div>
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.packaging-items.index', ['category' => $category ?? 'packaging']) }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
$(document).ready(function () {
$('#supplier_id, #packaging_material_id').select2({theme: 'default', width: '100%'});
});
</script>
@endsection

View file

@ -0,0 +1,73 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ $pageTitle }}</span>
<a href="{{ route('admin.inventory.packaging-items.create', ['category' => $category]) }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Material') }}</th>
<th>{{ __('Gewicht (g)') }}</th>
<th>{{ __('Lieferant') }}</th>
<th>{{ __('URL') }}</th>
<th>{{ __('Status') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($values as $value)
<tr>
<td>
<a href="{{ route('admin.inventory.packaging-items.edit', $value) }}" class="btn icon-btn btn-sm btn-primary">
<span class="far fa-edit"></span>
</a>
</td>
<td>{{ $value->name }}</td>
<td>{{ $value->packagingMaterial?->name ?? '—' }}</td>
<td>{{ $value->weight_grams }}</td>
<td>{{ $value->supplier?->name ?? '—' }}</td>
<td>
@if($value->url)
<a href="{{ $value->url }}" target="_blank" title="{{ $value->url }}"><i class="fa fa-external-link-alt"></i></a>
@else
@endif
</td>
<td data-sort="{{ $value->active ? 1 : 0 }}">
@if($value->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
@endif
</td>
<td>
<form action="{{ route('admin.inventory.packaging-items.destroy', $value) }}" method="post" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[1, "asc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -0,0 +1,36 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header">{{ $model->exists ? __('Verpackungsmaterial bearbeiten') : __('Verpackungsmaterial anlegen') }}</h6>
<div class="card-body">
<form method="post" action="{{ $model->exists ? route('admin.inventory.packaging-materials.update', $model) : route('admin.inventory.packaging-materials.store') }}">
@csrf
@if($model->exists)
@method('PUT')
@endif
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
<input type="text" name="name" id="name" class="form-control @error('name') is-invalid @enderror"
value="{{ old('name', $model->name) }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="pos">{{ __('Pos') }}</label>
<input type="number" name="pos" id="pos" class="form-control @error('pos') is-invalid @enderror"
value="{{ old('pos', $model->pos) }}" min="0" max="255">
@error('pos')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.packaging-materials.index') }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,53 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Verpackungsmaterial') }}</span>
<a href="{{ route('admin.inventory.packaging-materials.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
<th>{{ __('Pos') }}</th>
<th>{{ __('Name') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($values as $value)
<tr>
<td>
<a href="{{ route('admin.inventory.packaging-materials.edit', $value) }}" class="btn icon-btn btn-sm btn-primary">
<span class="far fa-edit"></span>
</a>
</td>
<td>{{ $value->pos }}</td>
<td>{{ $value->name }}</td>
<td>
<form action="{{ route('admin.inventory.packaging-materials.destroy', $value) }}" method="post" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[1, "asc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -0,0 +1,191 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Neue Produktion') }}</h4>
<div class="card">
<div class="card-body">
<form method="post" action="{{ route('admin.inventory.productions.store') }}" id="form-production">
@csrf
<div class="form-row">
<div class="form-group col-md-6">
<label for="product_id">{{ __('Produkt') }}</label>
<select name="product_id" id="product_id" class="form-control @error('product_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($products as $p)
<option value="{{ $p->id }}" @selected(old('product_id', $model?->product_id) == $p->id)>{{ $p->name }}</option>
@endforeach
</select>
@error('product_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-md-6">
<label for="location_id">{{ __('Lagerort / Produktionsstandort') }}</label>
<select name="location_id" id="location_id" class="form-control @error('location_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($locations as $loc)
<option value="{{ $loc->id }}" @selected(old('location_id', $model?->location_id ?? $defaultLocationId) == $loc->id)>{{ $loc->name }}</option>
@endforeach
</select>
@error('location_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="produced_at">{{ __('Produktionsdatum') }}</label>
<input type="date" name="produced_at" id="produced_at" class="form-control @error('produced_at') is-invalid @enderror"
value="{{ old('produced_at', now()->toDateString()) }}" required>
@error('produced_at')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-md-4">
<label for="quantity">{{ __('Produzierte Stückzahl') }}</label>
<input type="number" name="quantity" id="quantity" min="1" step="1" class="form-control @error('quantity') is-invalid @enderror"
value="{{ old('quantity', $model?->quantity ?? 1) }}" required>
@error('quantity')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div id="recipe-area" class="mb-3" style="display:none;">
<hr>
<h6>{{ __('Chargen zuordnen') }}</h6>
<p class="text-muted small" id="recipe-hint"></p>
<div id="recipe-ingredient-lines"></div>
@error('ingredient_lines')
<div class="text-danger small">{{ $message }}</div>
@enderror
<h6 class="mt-3">{{ __('Verpackung (Vorschau)') }}</h6>
<div class="table-responsive border rounded">
<table class="table table-sm mb-0">
<thead><tr><th>{{ __('Artikel') }}</th><th>{{ __('Stück gesamt') }}</th></tr></thead>
<tbody id="recipe-packaging-preview"></tbody>
</table>
</div>
</div>
<div class="form-group">
<label for="notes">{{ __('Notizen') }}</label>
<textarea name="notes" id="notes" class="form-control" rows="2">{{ old('notes') }}</textarea>
</div>
<button type="submit" class="btn btn-primary" id="btn-submit-production">{{ __('Produktion speichern') }}</button>
<a href="{{ route('admin.inventory.productions.index') }}" class="btn btn-outline-secondary">{{ __('Abbrechen') }}</a>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
(function ($) {
var recipeBase = @json(url('/admin/inventory/api/products'));
var lineIndex = 0;
function recipeUrl() {
var pid = $('#product_id').val();
var lid = $('#location_id').val();
var qty = $('#quantity').val() || 1;
if (!pid || !lid) return null;
return recipeBase + '/' + pid + '/recipe?location_id=' + encodeURIComponent(lid) + '&quantity=' + encodeURIComponent(qty);
}
function addIngredientRow($tbody, ing, stockEntries) {
var idx = lineIndex;
lineIndex++;
var tr = $('<tr></tr>');
var select = $('<select class="form-control form-control-sm" name="ingredient_lines[' + idx + '][stock_entry_id]" required></select>');
select.append('<option value="">{{ __('Charge wählen') }}</option>');
(stockEntries || []).forEach(function (se) {
var label = '#' + se.id;
if (se.batch_number) label += ' — ' + se.batch_number;
if (se.best_before) label += ' (MHD ' + se.best_before + ')';
select.append($('<option></option>').attr('value', se.id).text(label));
});
var td1 = $('<td></td>');
td1.append($('<input type="hidden" name="ingredient_lines[' + idx + '][ingredient_id]" value="' + ing.id + '">'));
td1.append(select);
tr.append(td1);
tr.append($('<td></td>').append(
$('<input type="text" class="form-control form-control-sm" required name="ingredient_lines[' + idx + '][quantity_used]" placeholder="0">')
));
$tbody.append(tr);
}
function renderRecipe(data) {
$('#recipe-ingredient-lines').empty();
lineIndex = 0;
var hintParts = [];
var hasMissingGram = false;
(data.ingredients || []).forEach(function (ing) {
if (ing.required_grams_total === null) {
hasMissingGram = true;
hintParts.push(ing.name + ': {{ __('Gramm in der Rezeptur fehlt') }}');
return;
}
var wrap = $('<div class="border rounded p-2 mb-2" data-ingredient-id="' + ing.id + '"></div>');
wrap.data('recipe-ing', ing);
var soll = '<strong>' + $('<div/>').text(ing.name).html() + '</strong> — {{ __('Soll') }}: ' +
ing.required_grams_total.toLocaleString('de-DE', {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' g';
wrap.append(soll);
var tbody = $('<tbody></tbody>');
var tbl = $('<table class="table table-sm table-bordered mb-1"><thead><tr><th>{{ __('Charge') }}</th><th>{{ __('Menge (g)') }}</th></tr></thead></table>');
tbl.append(tbody);
wrap.append(tbl);
wrap.append('<button type="button" class="btn btn-sm btn-outline-secondary btn-add-split">{{ __('Weitere Charge') }}</button>');
$('#recipe-ingredient-lines').append(wrap);
addIngredientRow(tbody, ing, ing.stock_entries || []);
});
if (hasMissingGram) {
$('#recipe-hint').text(hintParts.join(' · ')).removeClass('text-muted').addClass('text-danger');
} else {
$('#recipe-hint').text('{{ __('Pro Charge die entnommene Menge in Gramm eintragen. Summe je Inhaltsstoff muss dem Soll entsprechen.') }}').removeClass('text-danger').addClass('text-muted');
}
var $pk = $('#recipe-packaging-preview').empty();
(data.packagings || []).forEach(function (pk) {
$pk.append('<tr><td>' + $('<div/>').text(pk.name).html() + '</td><td>' + pk.total_pieces + '</td></tr>');
});
}
$(document).on('click', '.btn-add-split', function () {
var $wrap = $(this).closest('[data-ingredient-id]');
var ing = $wrap.data('recipe-ing');
if (!ing) return;
var $tbody = $wrap.find('tbody').first();
addIngredientRow($tbody, ing, ing.stock_entries || []);
});
function loadRecipe() {
var url = recipeUrl();
if (!url) {
$('#recipe-area').hide();
return;
}
$('#recipe-hint').text('{{ __('Lade ') }}');
$.getJSON(url)
.done(function (data) {
$('#recipe-area').show();
renderRecipe(data);
})
.fail(function () {
$('#recipe-area').show();
$('#recipe-hint').text('{{ __('Rezept konnte nicht geladen werden.') }}').addClass('text-danger');
});
}
$('#product_id, #location_id, #quantity').on('change', loadRecipe);
$(document).ready(function () {
if ($('#product_id').val() && $('#location_id').val()) {
loadRecipe();
}
});
})(jQuery);
</script>
@endsection

View file

@ -0,0 +1,203 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Produktion bearbeiten') }} #{{ $model->id }}</h4>
<div class="card">
<div class="card-body">
<form method="post" action="{{ route('admin.inventory.productions.update', $model) }}" id="form-production">
@csrf
@method('PUT')
<div class="form-row">
<div class="form-group col-md-6">
<label for="product_id">{{ __('Produkt') }}</label>
<select name="product_id" id="product_id" class="form-control @error('product_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($products as $p)
<option value="{{ $p->id }}" @selected(old('product_id', $model->product_id) == $p->id)>{{ $p->name }}</option>
@endforeach
</select>
@error('product_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-md-6">
<label for="location_id">{{ __('Lagerort / Produktionsstandort') }}</label>
<select name="location_id" id="location_id" class="form-control @error('location_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($locations as $loc)
<option value="{{ $loc->id }}" @selected(old('location_id', $model->location_id) == $loc->id)>{{ $loc->name }}</option>
@endforeach
</select>
@error('location_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label for="produced_at">{{ __('Produktionsdatum') }}</label>
<input type="date" name="produced_at" id="produced_at" class="form-control @error('produced_at') is-invalid @enderror"
value="{{ old('produced_at', $model->produced_at?->toDateString()) }}" required>
@error('produced_at')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-md-4">
<label for="quantity">{{ __('Produzierte Stückzahl') }}</label>
<input type="number" name="quantity" id="quantity" min="1" step="1" class="form-control @error('quantity') is-invalid @enderror"
value="{{ old('quantity', $model->quantity) }}" required>
@error('quantity')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div id="recipe-area" class="mb-3" style="display:none;">
<hr>
<h6>{{ __('Chargen zuordnen') }}</h6>
<p class="text-muted small" id="recipe-hint"></p>
<div id="recipe-ingredient-lines"></div>
@error('ingredient_lines')
<div class="text-danger small">{{ $message }}</div>
@enderror
<h6 class="mt-3">{{ __('Verpackung (Vorschau)') }}</h6>
<div class="table-responsive border rounded">
<table class="table table-sm mb-0">
<thead><tr><th>{{ __('Artikel') }}</th><th>{{ __('Stück gesamt') }}</th></tr></thead>
<tbody id="recipe-packaging-preview"></tbody>
</table>
</div>
</div>
<div class="form-group">
<label for="notes">{{ __('Notizen') }}</label>
<textarea name="notes" id="notes" class="form-control" rows="2">{{ old('notes', $model->notes) }}</textarea>
</div>
<button type="submit" class="btn btn-primary" id="btn-submit-production">{{ __('Produktion speichern') }}</button>
<a href="{{ route('admin.inventory.productions.show', $model) }}" class="btn btn-outline-secondary">{{ __('Abbrechen') }}</a>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
(function ($) {
var recipeBase = @json(url('/admin/inventory/api/products'));
var lineIndex = 0;
function recipeUrl() {
var pid = $('#product_id').val();
var lid = $('#location_id').val();
var qty = $('#quantity').val() || 1;
if (!pid || !lid) return null;
return recipeBase + '/' + pid + '/recipe?location_id=' + encodeURIComponent(lid) + '&quantity=' + encodeURIComponent(qty);
}
function addIngredientRow($tbody, ing, stockEntries, preselectedEntryId, preselectedQty) {
var idx = lineIndex;
lineIndex++;
var tr = $('<tr></tr>');
var select = $('<select class="form-control form-control-sm" name="ingredient_lines[' + idx + '][stock_entry_id]" required></select>');
select.append('<option value="">{{ __('Charge wählen') }}</option>');
(stockEntries || []).forEach(function (se) {
var label = '#' + se.id;
if (se.batch_number) label += ' — ' + se.batch_number;
if (se.best_before) label += ' (MHD ' + se.best_before + ')';
var opt = $('<option></option>').attr('value', se.id).text(label);
if (preselectedEntryId && String(se.id) === String(preselectedEntryId)) opt.prop('selected', true);
select.append(opt);
});
var td1 = $('<td></td>');
td1.append($('<input type="hidden" name="ingredient_lines[' + idx + '][ingredient_id]" value="' + ing.id + '">'));
td1.append(select);
tr.append(td1);
tr.append($('<td></td>').append(
$('<input type="text" class="form-control form-control-sm" required name="ingredient_lines[' + idx + '][quantity_used]" placeholder="0">').val(preselectedQty || '')
));
$tbody.append(tr);
}
function renderRecipe(data) {
$('#recipe-ingredient-lines').empty();
lineIndex = 0;
var existingLines = @json($model->productionIngredients->groupBy('ingredient_id')->map(function($lines) {
return $lines->map(function($l) {
return ['stock_entry_id' => $l->stock_entry_id, 'quantity_used' => (float) $l->quantity_used];
})->values();
}));
var hintParts = [];
var hasMissingGram = false;
(data.ingredients || []).forEach(function (ing) {
if (ing.required_grams_total === null) {
hasMissingGram = true;
hintParts.push(ing.name + ': {{ __('Gramm in der Rezeptur fehlt') }}');
return;
}
var wrap = $('<div class="border rounded p-2 mb-2" data-ingredient-id="' + ing.id + '"></div>');
wrap.data('recipe-ing', ing);
var soll = '<strong>' + $('<div/>').text(ing.name).html() + '</strong> — {{ __('Soll') }}: ' +
ing.required_grams_total.toLocaleString('de-DE', {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' g';
wrap.append(soll);
var tbody = $('<tbody></tbody>');
var tbl = $('<table class="table table-sm table-bordered mb-1"><thead><tr><th>{{ __('Charge') }}</th><th>{{ __('Menge (g)') }}</th></tr></thead></table>');
tbl.append(tbody);
wrap.append(tbl);
wrap.append('<button type="button" class="btn btn-sm btn-outline-secondary btn-add-split">{{ __('Weitere Charge') }}</button>');
$('#recipe-ingredient-lines').append(wrap);
var existing = existingLines[String(ing.id)] || [];
if (existing.length > 0) {
existing.forEach(function (ex) {
addIngredientRow(tbody, ing, ing.stock_entries || [], ex.stock_entry_id, ex.quantity_used);
});
} else {
addIngredientRow(tbody, ing, ing.stock_entries || []);
}
});
if (hasMissingGram) {
$('#recipe-hint').text(hintParts.join(' · ')).removeClass('text-muted').addClass('text-danger');
} else {
$('#recipe-hint').text('{{ __('Pro Charge die entnommene Menge in Gramm eintragen.') }}').removeClass('text-danger').addClass('text-muted');
}
var $pk = $('#recipe-packaging-preview').empty();
(data.packagings || []).forEach(function (pk) {
$pk.append('<tr><td>' + $('<div/>').text(pk.name).html() + '</td><td>' + pk.total_pieces + '</td></tr>');
});
}
$(document).on('click', '.btn-add-split', function () {
var $wrap = $(this).closest('[data-ingredient-id]');
var ing = $wrap.data('recipe-ing');
if (!ing) return;
var $tbody = $wrap.find('tbody').first();
addIngredientRow($tbody, ing, ing.stock_entries || []);
});
function loadRecipe() {
var url = recipeUrl();
if (!url) { $('#recipe-area').hide(); return; }
$('#recipe-hint').text('{{ __('Lade ') }}');
$.getJSON(url)
.done(function (data) { $('#recipe-area').show(); renderRecipe(data); })
.fail(function () {
$('#recipe-area').show();
$('#recipe-hint').text('{{ __('Rezept konnte nicht geladen werden.') }}').addClass('text-danger');
});
}
$('#product_id, #location_id, #quantity').on('change', loadRecipe);
$(document).ready(function () {
if ($('#product_id').val() && $('#location_id').val()) {
loadRecipe();
}
});
})(jQuery);
</script>
@endsection

View file

@ -0,0 +1,62 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Produktionen') }}</span>
<a href="{{ route('admin.inventory.productions.create') }}" class="btn btn-sm btn-primary">{{ __('Neue Produktion') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th>{{ __('Datum') }}</th>
<th>{{ __('Produkt') }}</th>
<th>{{ __('Stück') }}</th>
<th>{{ __('Standort') }}</th>
<th>{{ __('MHD-Hinweis') }}</th>
<th style="max-width: 80px;"></th>
</tr>
</thead>
<tbody>
@foreach($values as $row)
<tr>
<td data-sort="{{ $row->produced_at?->timestamp ?? 0 }}">{{ $row->produced_at?->format('d.m.Y') }}</td>
<td>{{ $row->product?->name ?? '—' }}</td>
<td>{{ $row->quantity }}</td>
<td>{{ $row->location?->name ?? '—' }}</td>
<td>
@if($row->mhd_warning)
<span class="badge badge-warning">{{ __('Ja') }}</span>
@else
<span class="text-muted"></span>
@endif
</td>
<td class="text-nowrap">
<a href="{{ route('admin.inventory.productions.show', $row) }}" class="btn icon-btn btn-sm btn-primary" title="{{ __('Details') }}">
<span class="far fa-eye"></span>
</a>
<a href="{{ route('admin.inventory.productions.edit', $row) }}" class="btn icon-btn btn-sm btn-outline-primary" title="{{ __('Bearbeiten') }}">
<span class="far fa-edit"></span>
</a>
<a href="{{ route('admin.inventory.productions.copy', $row) }}" class="btn icon-btn btn-sm btn-outline-secondary" title="{{ __('Kopieren') }}">
<span class="far fa-copy"></span>
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[0, "desc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -0,0 +1,104 @@
@php
/** @var \App\Models\Production $model */
@endphp
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Produktion') }} #{{ $model->id }}</h4>
<div class="card mb-3">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Übersicht') }}</span>
<div>
<a href="{{ route('admin.inventory.productions.edit', $model) }}" class="btn btn-sm btn-primary">{{ __('Bearbeiten') }}</a>
<a href="{{ route('admin.inventory.productions.copy', $model) }}" class="btn btn-sm btn-outline-primary">{{ __('Kopieren') }}</a>
<a href="{{ route('admin.inventory.productions.index') }}" class="btn btn-sm btn-outline-secondary">{{ __('Zurück') }}</a>
</div>
</h6>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">{{ __('Produkt') }}</dt>
<dd class="col-sm-9">{{ $model->product?->name ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('Produktionsdatum') }}</dt>
<dd class="col-sm-9">{{ $model->produced_at?->format('d.m.Y') }}</dd>
<dt class="col-sm-3">{{ __('Stückzahl') }}</dt>
<dd class="col-sm-9">{{ $model->quantity }}</dd>
<dt class="col-sm-3">{{ __('Standort') }}</dt>
<dd class="col-sm-9">{{ $model->location?->name ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('Erfasst von') }}</dt>
<dd class="col-sm-9">{{ $model->producedByUser?->getFullName(false) ?: $model->producedByUser?->email ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('MHD-Hinweis') }}</dt>
<dd class="col-sm-9">
@if($model->mhd_warning)
<span class="badge badge-warning">{{ __('Rohstoff-MHD kürzer als Produkt-MHD') }}</span>
@else
@endif
</dd>
@if($model->notes)
<dt class="col-sm-3">{{ __('Notizen') }}</dt>
<dd class="col-sm-9">{{ $model->notes }}</dd>
@endif
</dl>
</div>
</div>
<div class="card mb-3">
<h6 class="card-header">{{ __('Rohstoff-Verbrauch (Chargen)') }}</h6>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead>
<tr>
<th>{{ __('Inhaltsstoff') }}</th>
<th>{{ __('Charge / Wareneingang') }}</th>
<th>{{ __('Menge (g)') }}</th>
</tr>
</thead>
<tbody>
@foreach($model->productionIngredients as $line)
<tr>
<td>{{ $line->ingredient?->name ?? '—' }}</td>
<td>
#{{ $line->stock_entry_id }}
@if($line->stockEntry?->batch_number)
{{ $line->stockEntry->batch_number }}
@endif
@if($line->stockEntry?->best_before)
(MHD {{ $line->stockEntry->best_before->format('d.m.Y') }})
@endif
</td>
<td>{{ \App\Services\Util::formatNumber($line->quantity_used) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<div class="card">
<h6 class="card-header">{{ __('Verpackung (Snapshot)') }}</h6>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-striped mb-0">
<thead>
<tr>
<th>{{ __('Artikel') }}</th>
<th>{{ __('Verbrauch (Stk.)') }}</th>
</tr>
</thead>
<tbody>
@foreach($model->productionPackagings as $line)
<tr>
<td>{{ $line->packagingItem?->name ?? '—' }}</td>
<td>{{ $line->quantity_used }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,111 @@
@php
/** @var \App\Models\StockEntry $model */
$isEdit = $model->exists;
@endphp
<div class="form-group">
<label for="entry_type">{{ __('Art') }}</label>
<select name="entry_type" id="entry_type" class="form-control @error('entry_type') is-invalid @enderror" required>
@foreach($entryTypeLabels as $value => $label)
<option value="{{ $value }}" @selected(old('entry_type', $model->entry_type ?? 'ingredient') === $value)>{{ $label }}</option>
@endforeach
</select>
@error('entry_type')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div id="stock-entry-ingredient-block" class="form-group" style="display:none;">
<label for="ingredient_id">{{ __('Inhaltsstoff') }}</label>
<div class="light-style">
<select name="ingredient_id" id="ingredient_id" class="w-100" data-search-url="{{ route('admin.inventory.api.ingredients.search') }}">
@if($model->ingredient_id && $model->ingredient)
<option value="{{ $model->ingredient_id }}" selected>{{ $model->ingredient->name }}@if($model->ingredient->inci) ({{ $model->ingredient->inci }})@endif</option>
@elseif(old('ingredient_id'))
<option value="{{ old('ingredient_id') }}" selected>{{ old('ingredient_id') }}</option>
@endif
</select>
</div>
@error('ingredient_id')
<div class="text-danger small">{{ $message }}</div>
@enderror
</div>
<div id="stock-entry-packaging-block" class="form-group" style="display:none;">
<label for="packaging_item_id">{{ __('Verpackungsartikel') }}</label>
<div class="light-style">
<select name="packaging_item_id" id="packaging_item_id" class="w-100" data-search-url="{{ route('admin.inventory.api.packaging-items.search') }}">
@if($model->packaging_item_id && $model->packagingItem)
<option value="{{ $model->packaging_item_id }}" selected>{{ $model->packagingItem->name }}</option>
@elseif(old('packaging_item_id'))
<option value="{{ old('packaging_item_id') }}" selected>{{ old('packaging_item_id') }}</option>
@endif
</select>
</div>
@error('packaging_item_id')
<div class="text-danger small">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="supplier_id">{{ __('Lieferant') }}</label>
<select name="supplier_id" id="supplier_id" class="form-control @error('supplier_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($suppliers as $sup)
<option value="{{ $sup->id }}" @selected((string)old('supplier_id', $model->supplier_id) === (string)$sup->id)>{{ $sup->name }}</option>
@endforeach
</select>
@error('supplier_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="location_id">{{ __('Lagerort') }}</label>
<select name="location_id" id="location_id" class="form-control @error('location_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($locations as $loc)
<option value="{{ $loc->id }}" @selected((string)old('location_id', $model->location_id) === (string)$loc->id)>{{ $loc->name }}</option>
@endforeach
</select>
@error('location_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="ordered_at">{{ __('Bestelldatum') }}</label>
<input type="date" name="ordered_at" id="ordered_at" class="form-control @error('ordered_at') is-invalid @enderror"
value="{{ old('ordered_at', $model->ordered_at ? $model->ordered_at->format('Y-m-d') : '') }}" required>
@error('ordered_at')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="ordered_quantity">{{ __('Bestellte Menge') }}</label>
<input type="text" name="ordered_quantity" id="ordered_quantity" class="form-control @error('ordered_quantity') is-invalid @enderror"
value="{{ old('ordered_quantity', $model->ordered_quantity !== null ? \App\Services\Util::formatNumber($model->ordered_quantity) : '') }}" required>
<small class="text-muted">{{ __('Bei Rohstoff in Gramm, bei Verpackung in Stück.') }}</small>
@error('ordered_quantity')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div id="price-per-kg-block" class="form-group" style="display:none;">
<label for="price_per_kg">{{ __('Netto-Preis pro kg') }}</label>
<input type="text" name="price_per_kg" id="price_per_kg" class="form-control @error('price_per_kg') is-invalid @enderror"
value="{{ old('price_per_kg', $model->price_per_kg !== null ? \App\Services\Util::formatNumber($model->price_per_kg) : '') }}">
@error('price_per_kg')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div id="price-total-block" class="form-group" style="display:none;">
<label for="price_total">{{ __('Gesamtpreis netto') }}</label>
<input type="text" name="price_total" id="price_total" class="form-control @error('price_total') is-invalid @enderror"
value="{{ old('price_total', $model->price_total !== null ? \App\Services\Util::formatNumber($model->price_total) : '') }}">
@error('price_total')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>

View file

@ -0,0 +1,79 @@
<script>
(function ($) {
function toggleBlocks() {
var t = $('#entry_type').val();
var isIng = t === 'ingredient';
$('#stock-entry-ingredient-block').toggle(isIng);
$('#stock-entry-packaging-block').toggle(!isIng);
$('#price-per-kg-block').toggle(isIng);
$('#price-total-block').toggle(!isIng);
}
function initIngredientSelect2() {
var $el = $('#ingredient_id');
if ($el.data('select2')) {
$el.select2('destroy');
}
$el.select2({
theme: 'default',
width: '100%',
placeholder: '{{ __('Inhaltsstoff suchen…') }}',
allowClear: true,
ajax: {
url: $el.data('search-url'),
dataType: 'json',
delay: 250,
data: function (params) {
return {q: params.term || ''};
},
processResults: function (data) {
return {results: data.results || []};
},
cache: true
},
minimumInputLength: 1
});
}
function initPackagingSelect2() {
var $el = $('#packaging_item_id');
if ($el.data('select2')) {
$el.select2('destroy');
}
$el.select2({
theme: 'default',
width: '100%',
placeholder: '{{ __('Verpackungsartikel suchen…') }}',
allowClear: true,
ajax: {
url: $el.data('search-url'),
dataType: 'json',
delay: 250,
data: function (params) {
return {
q: params.term || '',
entry_type: $('#entry_type').val()
};
},
processResults: function (data) {
return {results: data.results || []};
},
cache: true
},
minimumInputLength: 0
});
}
$(document).ready(function () {
toggleBlocks();
initIngredientSelect2();
initPackagingSelect2();
$('#entry_type').on('change', function () {
toggleBlocks();
$('#packaging_item_id').val(null).trigger('change');
initPackagingSelect2();
});
});
})(jQuery);
</script>

View file

@ -0,0 +1,19 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Neuer Einkauf (Stufe 1)') }}</h4>
<div class="card">
<div class="card-body">
<form method="post" action="{{ route('admin.inventory.stock-entries.store') }}">
@csrf
@include('admin.inventory.stock-entries._form')
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.stock-entries.index') }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection
@section('scripts')
@include('admin.inventory.stock-entries._scripts')
@endsection

View file

@ -0,0 +1,20 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Bestellung bearbeiten') }}</h4>
<div class="card">
<div class="card-body">
<form method="post" action="{{ route('admin.inventory.stock-entries.update', $model) }}">
@csrf
@method('PUT')
@include('admin.inventory.stock-entries._form')
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.stock-entries.index') }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection
@section('scripts')
@include('admin.inventory.stock-entries._scripts')
@endsection

View file

@ -0,0 +1,85 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Wareneingang') }}</span>
@if(Auth::user()->isAdmin())
<a href="{{ route('admin.inventory.stock-entries.create') }}" class="btn btn-sm btn-primary">{{ __('Neuer Einkauf') }}</a>
@endif
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th>{{ __('Status') }}</th>
<th>{{ __('Bestellt') }}</th>
<th>{{ __('Art') }}</th>
<th>{{ __('Artikel') }}</th>
<th>{{ __('Lieferant') }}</th>
<th>{{ __('Menge') }}</th>
<th style="max-width: 80px;"></th>
</tr>
</thead>
<tbody>
@foreach($values as $row)
<tr>
<td data-sort="{{ $row->status === 'pending' ? 0 : 1 }}">
@if($row->status === 'pending')
<span class="badge badge-warning">{{ __('Offen') }}</span>
@else
<span class="badge badge-success">{{ __('Eingegangen') }}</span>
@endif
</td>
<td data-sort="{{ $row->ordered_at?->timestamp ?? 0 }}">{{ $row->ordered_at?->format('d.m.Y') }}</td>
<td>{{ $entryTypeLabels[$row->entry_type] ?? $row->entry_type }}</td>
<td>
@if($row->entry_type === 'ingredient' && $row->ingredient)
{{ $row->ingredient->name }}
@elseif($row->packagingItem)
{{ $row->packagingItem->name }}
@else
@endif
</td>
<td>{{ $row->supplier?->name ?? '—' }}</td>
<td>
@if($row->unit === 'gram')
{{ \App\Services\Util::formatNumber($row->ordered_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($row->ordered_quantity, 0) }} {{ __('Stk.') }}
@endif
</td>
<td>
<a href="{{ route('admin.inventory.stock-entries.show', $row) }}" class="btn icon-btn btn-sm btn-primary" title="{{ __('Details') }}">
<span class="far fa-eye"></span>
</a>
@if(Auth::user()->isAdmin() && $row->status === 'pending')
<a href="{{ route('admin.inventory.stock-entries.edit', $row) }}" class="btn icon-btn btn-sm btn-secondary">
<span class="far fa-edit"></span>
</a>
<form action="{{ route('admin.inventory.stock-entries.destroy', $row) }}" method="post" class="d-inline"
onsubmit="return confirm(@json(__('Eintrag wirklich löschen?')));">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0" title="{{ __('Delete') }}"><i class="far fa-trash-alt"></i></button>
</form>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[0, "asc"], [1, "desc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -0,0 +1,191 @@
@php
/** @var \App\Models\StockEntry $model */
@endphp
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">{{ __('Wareneingang') }}</h4>
<div class="card mb-3">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>
@if($model->status === 'pending')
<span class="badge badge-warning">{{ __('Offen') }}</span>
@else
<span class="badge badge-success">{{ __('Eingegangen') }}</span>
@endif
</span>
<span>
<a href="{{ route('admin.inventory.stock-entries.index') }}" class="btn btn-sm btn-outline-secondary">{{ __('Zurück zur Liste') }}</a>
@if(Auth::user()->isAdmin() && $model->isPending())
<a href="{{ route('admin.inventory.stock-entries.edit', $model) }}" class="btn btn-sm btn-primary">{{ __('Bearbeiten') }}</a>
@endif
</span>
</h6>
<div class="card-body">
<dl class="row mb-0">
<dt class="col-sm-3">{{ __('Art') }}</dt>
<dd class="col-sm-9">{{ $entryTypeLabels[$model->entry_type] ?? $model->entry_type }}</dd>
<dt class="col-sm-3">{{ __('Artikel') }}</dt>
<dd class="col-sm-9">
@if($model->entry_type === 'ingredient' && $model->ingredient)
{{ $model->ingredient->name }}@if($model->ingredient->inci) ({{ $model->ingredient->inci }})@endif
@elseif($model->packagingItem)
{{ $model->packagingItem->name }}
@if($model->packagingItem->packagingMaterial)
<span class="text-muted"> {{ $model->packagingItem->packagingMaterial->name }}</span>
@endif
@else
@endif
</dd>
<dt class="col-sm-3">{{ __('Lieferant') }}</dt>
<dd class="col-sm-9">{{ $model->supplier?->name ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('Lagerort') }}</dt>
<dd class="col-sm-9">{{ $model->location?->name ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('Bestelldatum') }}</dt>
<dd class="col-sm-9">{{ $model->ordered_at?->format('d.m.Y') }}</dd>
<dt class="col-sm-3">{{ __('Bestellte Menge') }}</dt>
<dd class="col-sm-9">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->ordered_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->ordered_quantity, 0) }} {{ __('Stk.') }}
@endif
</dd>
@if(Auth::user()->isAdmin())
<dt class="col-sm-3">{{ __('Preise (Stufe 1)') }}</dt>
<dd class="col-sm-9">
@if($model->entry_type === 'ingredient')
@if($model->price_per_kg !== null)
{{ \App\Services\Util::formatNumber($model->price_per_kg) }} / kg
@else
@endif
@else
@if($model->price_total !== null)
{{ \App\Services\Util::formatNumber($model->price_total) }} {{ __('netto') }}
@else
@endif
@endif
</dd>
@endif
<dt class="col-sm-3">{{ __('Bestellt von') }}</dt>
<dd class="col-sm-9">{{ $model->orderedByUser?->getFullName(false) ?: $model->orderedByUser?->email ?? '—' }}</dd>
@if($model->isReceived())
<dt class="col-sm-3">{{ __('Eingangsdatum') }}</dt>
<dd class="col-sm-9">{{ $model->received_at?->format('d.m.Y') }}</dd>
<dt class="col-sm-3">{{ __('Eingegangene Menge') }}</dt>
<dd class="col-sm-9">
@if($model->unit === 'gram')
{{ \App\Services\Util::formatNumber($model->received_quantity) }} g
@else
{{ \App\Services\Util::formatNumber($model->received_quantity, 0) }} {{ __('Stk.') }}
@endif
</dd>
@if($model->entry_type === 'ingredient')
<dt class="col-sm-3">{{ __('Charge') }}</dt>
<dd class="col-sm-9">{{ $model->batch_number ?: '—' }}</dd>
<dt class="col-sm-3">{{ __('Mindesthaltbarkeit') }}</dt>
<dd class="col-sm-9">{{ $model->best_before?->format('d.m.Y') ?? '—' }}</dd>
<dt class="col-sm-3">{{ __('Materialqualität') }}</dt>
<dd class="col-sm-9">{{ $model->quality?->name ?? '—' }}</dd>
@endif
<dt class="col-sm-3">{{ __('Eingebucht von') }}</dt>
<dd class="col-sm-9">{{ $model->receivedByUser?->getFullName(false) ?: $model->receivedByUser?->email ?? '—' }}</dd>
@endif
</dl>
</div>
</div>
@if($model->isPending())
<div class="card">
<h6 class="card-header">{{ __('Wareneingang buchen (Stufe 2)') }}</h6>
<div class="card-body">
<form method="post" action="{{ route('admin.inventory.stock-entries.receive', $model) }}">
@csrf
@method('PUT')
<div class="form-group">
<label for="received_at">{{ __('Eingangsdatum') }}</label>
<input type="date" name="received_at" id="received_at" required
class="form-control @error('received_at') is-invalid @enderror"
value="{{ old('received_at', now()->toDateString()) }}">
@error('received_at')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="received_quantity">{{ __('Eingegangene Menge') }}</label>
<input type="text" name="received_quantity" id="received_quantity" required
class="form-control @error('received_quantity') is-invalid @enderror"
value="{{ old('received_quantity', \App\Services\Util::formatNumber($model->ordered_quantity)) }}">
<small class="text-muted">
@if($model->unit === 'gram')
{{ __('Angabe in Gramm') }}
@else
{{ __('Angabe in Stück') }}
@endif
</small>
@error('received_quantity')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
@if($model->entry_type === 'ingredient')
<div class="form-group">
<label for="batch_number">{{ __('Chargennummer') }}</label>
<input type="text" name="batch_number" id="batch_number" maxlength="100"
class="form-control @error('batch_number') is-invalid @enderror"
value="{{ old('batch_number') }}">
@error('batch_number')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="best_before">{{ __('Mindesthaltbarkeit') }}</label>
<input type="date" name="best_before" id="best_before"
class="form-control @error('best_before') is-invalid @enderror"
value="{{ old('best_before') }}">
@error('best_before')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="quality_id">{{ __('Materialqualität') }}</label>
<select name="quality_id" id="quality_id" class="form-control @error('quality_id') is-invalid @enderror">
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($materialQualities as $mq)
<option value="{{ $mq->id }}" @selected((string)old('quality_id') === (string)$mq->id)>{{ $mq->name }}</option>
@endforeach
</select>
@error('quality_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
@endif
<button type="submit" class="btn btn-primary">{{ __('Als eingegangen buchen') }}</button>
</form>
</div>
</div>
@endif
@endsection

View file

@ -0,0 +1,36 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header">{{ $model->exists ? __('Lieferanten-Kategorie bearbeiten') : __('Lieferanten-Kategorie anlegen') }}</h6>
<div class="card-body">
<form method="post" action="{{ $model->exists ? route('admin.inventory.supplier-categories.update', $model) : route('admin.inventory.supplier-categories.store') }}">
@csrf
@if($model->exists)
@method('PUT')
@endif
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
<input type="text" name="name" id="name" class="form-control @error('name') is-invalid @enderror"
value="{{ old('name', $model->name) }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="pos">{{ __('Pos') }}</label>
<input type="number" name="pos" id="pos" class="form-control @error('pos') is-invalid @enderror"
value="{{ old('pos', $model->pos) }}" min="0" max="255">
@error('pos')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.supplier-categories.index') }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection

View file

@ -0,0 +1,53 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Lieferanten-Kategorien') }}</span>
<a href="{{ route('admin.inventory.supplier-categories.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
<th>{{ __('Pos') }}</th>
<th>{{ __('Name') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($values as $value)
<tr>
<td>
<a href="{{ route('admin.inventory.supplier-categories.edit', $value) }}" class="btn icon-btn btn-sm btn-primary">
<span class="far fa-edit"></span>
</a>
</td>
<td>{{ $value->pos }}</td>
<td>{{ $value->name }}</td>
<td>
<form action="{{ route('admin.inventory.supplier-categories.destroy', $value) }}" method="post" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[1, "asc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -0,0 +1,125 @@
@extends('layouts.layout-2')
@section('content')
@php
$selectedCategoryIds = old('supplier_category_ids', $model->exists ? $model->supplierCategories->pluck('id')->all() : []);
@endphp
<div class="card">
<h6 class="card-header">{{ $model->exists ? __('Lieferant bearbeiten') : __('Lieferant anlegen') }}</h6>
<div class="card-body">
<form method="post" action="{{ $model->exists ? route('admin.inventory.suppliers.update', $model) : route('admin.inventory.suppliers.store') }}">
@csrf
@if($model->exists)
@method('PUT')
@endif
<div class="form-group">
<label for="name">{{ __('Name') }}</label>
<input type="text" name="name" id="name" class="form-control @error('name') is-invalid @enderror"
value="{{ old('name', $model->name) }}" required>
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="supplier_category_ids">{{ __('Kategorien') }}</label>
<div class="light-style">
<select name="supplier_category_ids[]" id="supplier_category_ids" class="w-100" multiple="multiple" data-placeholder="{{ __('Kategorien wählen') }}">
@foreach($supplierCategories as $cat)
<option value="{{ $cat->id }}" @selected(in_array($cat->id, $selectedCategoryIds, true))>{{ $cat->name }}</option>
@endforeach
</select>
</div>
@error('supplier_category_ids')
<div class="text-danger small">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="country_id">{{ __('Land') }}</label>
<select name="country_id" id="country_id" class="form-control @error('country_id') is-invalid @enderror" required>
<option value="">{{ __('Bitte wählen') }}</option>
@foreach($countries as $country)
<option value="{{ $country->id }}" @selected((string)old('country_id', $model->country_id) === (string)$country->id)>
{{ $country->de }} ({{ $country->code }})
</option>
@endforeach
</select>
@error('country_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="url">{{ __('Webseite') }}</label>
<input type="url" name="url" id="url" class="form-control @error('url') is-invalid @enderror"
value="{{ old('url', $model->url) }}">
@error('url')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="contact_person">{{ __('Ansprechpartner') }}</label>
<input type="text" name="contact_person" id="contact_person" class="form-control @error('contact_person') is-invalid @enderror"
value="{{ old('contact_person', $model->contact_person) }}">
@error('contact_person')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-md-6">
<label for="email">{{ __('E-Mail') }}</label>
<input type="email" name="email" id="email" class="form-control @error('email') is-invalid @enderror"
value="{{ old('email', $model->email) }}">
@error('email')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
<div class="form-group">
<label for="phone">{{ __('Telefon') }}</label>
<input type="text" name="phone" id="phone" class="form-control @error('phone') is-invalid @enderror"
value="{{ old('phone', $model->phone) }}">
@error('phone')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label for="notes">{{ __('Notizen') }}</label>
<textarea name="notes" id="notes" rows="3" class="form-control @error('notes') is-invalid @enderror">{{ old('notes', $model->notes) }}</textarea>
@error('notes')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label class="custom-control custom-checkbox">
<input type="checkbox" name="active" value="1" class="custom-control-input"
@checked(old('active', $model->active))>
<span class="custom-control-label">{{ __('Aktiv') }}</span>
</label>
</div>
<button type="submit" class="btn btn-primary">{{ __('Speichern') }}</button>
<a href="{{ route('admin.inventory.suppliers.index') }}" class="btn btn-outline-secondary">{{ __('Zurück') }}</a>
</form>
</div>
</div>
@endsection
@section('scripts')
<script>
$(document).ready(function () {
$('#supplier_category_ids').select2({
theme: 'default',
placeholder: '{{ __('Kategorien wählen') }}',
width: '100%',
closeOnSelect: false
});
});
</script>
@endsection

View file

@ -0,0 +1,67 @@
@extends('layouts.layout-2')
@section('content')
<div class="card">
<h6 class="card-header d-flex justify-content-between align-items-center">
<span>{{ __('Lieferanten') }}</span>
<a href="{{ route('admin.inventory.suppliers.create') }}" class="btn btn-sm btn-primary">{{ __('Neu anlegen') }}</a>
</h6>
<div class="card-datatable table-responsive">
<table class="datatables-style table table-striped table-bordered">
<thead>
<tr>
<th style="max-width: 60px;">&nbsp;</th>
<th>{{ __('Name') }}</th>
<th>{{ __('Land') }}</th>
<th>{{ __('Kategorien') }}</th>
<th>{{ __('Status') }}</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($values as $value)
<tr>
<td>
<a href="{{ route('admin.inventory.suppliers.edit', $value) }}" class="btn icon-btn btn-sm btn-primary">
<span class="far fa-edit"></span>
</a>
</td>
<td>{{ $value->name }}</td>
<td>{{ $value->country?->de ?? '—' }}</td>
<td>
@foreach($value->supplierCategories as $cat)
<span class="badge badge-secondary">{{ $cat->name }}</span>
@endforeach
</td>
<td data-sort="{{ $value->active ? 1 : 0 }}">
@if($value->active)
<span class="badge badge-pill badge-success"><i class="fa fa-check"></i></span>
@else
<span class="badge badge-pill badge-danger"><i class="fa fa-times"></i></span>
@endif
</td>
<td>
<form action="{{ route('admin.inventory.suppliers.destroy', $value) }}" method="post" class="d-inline"
onsubmit="return confirm('{{ __('Really delete entry?') }}');">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-link text-danger p-0"><i class="far fa-trash-alt"></i></button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
<script>
$(document).ready(function () {
$('.datatables-style').dataTable({
"bLengthChange": false,
"iDisplayLength": 100,
"order": [[1, "asc"]],
"language": {"url": "/js/German.json"}
});
});
</script>
@endsection

View file

@ -2,275 +2,284 @@
@section('content')
<style>
/* Optimierte Reminder Badge Styles */
.reminder-badge {
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
<style>
/* Optimierte Reminder Badge Styles */
.reminder-badge {
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.reminder-badge:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}
.reminder-badge:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.reminder-badge.badge-warning {
background: linear-gradient(45deg, #ffc107, #ffb300);
border: 1px solid #ffa000;
}
.reminder-badge.badge-warning {
background: linear-gradient(45deg, #ffc107, #ffb300);
border: 1px solid #ffa000;
}
.reminder-badge.badge-danger {
background: linear-gradient(45deg, #dc3545, #c82333);
border: 1px solid #bd2130;
}
.reminder-badge.badge-danger {
background: linear-gradient(45deg, #dc3545, #c82333);
border: 1px solid #bd2130;
}
.reminder-badge.badge-dark {
background: linear-gradient(45deg, #343a40, #23272b);
border: 1px solid #1d2124;
}
.reminder-badge.badge-dark {
background: linear-gradient(45deg, #343a40, #23272b);
border: 1px solid #1d2124;
}
.reminder-badge.badge-light {
background: linear-gradient(45deg, #f8f9fa, #e9ecef);
border: 1px solid #dee2e6;
color: #6c757d !important;
}
.reminder-badge.badge-light {
background: linear-gradient(45deg, #f8f9fa, #e9ecef);
border: 1px solid #dee2e6;
color: #6c757d !important;
}
/* Tooltip Verbesserungen */
.tooltip-inner {
background-color: #343a40;
color: white;
border-radius: 6px;
padding: 8px 12px;
font-size: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
/* Tooltip Verbesserungen */
.tooltip-inner {
background-color: #343a40;
color: white;
border-radius: 6px;
padding: 8px 12px;
font-size: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.tooltip.bs-tooltip-top .arrow::before {
border-top-color: #343a40;
}
.tooltip.bs-tooltip-top .arrow::before {
border-top-color: #343a40;
}
.tooltip.bs-tooltip-bottom .arrow::before {
border-bottom-color: #343a40;
}
.tooltip.bs-tooltip-bottom .arrow::before {
border-bottom-color: #343a40;
}
/* Reminder Datum Styling */
.reminder-date {
font-size: 11px;
opacity: 0.8;
transition: opacity 0.3s ease;
}
/* Reminder Datum Styling */
.reminder-date {
font-size: 11px;
opacity: 0.8;
transition: opacity 0.3s ease;
}
.reminder-date:hover {
opacity: 1;
}
.reminder-date:hover {
opacity: 1;
}
/* Countdown Styling */
.countdown-info {
font-size: 10px;
font-weight: 500;
padding: 2px 6px;
border-radius: 12px;
background: rgba(0,0,0,0.05);
transition: all 0.3s ease;
}
/* Countdown Styling */
.countdown-info {
font-size: 10px;
font-weight: 500;
padding: 2px 6px;
border-radius: 12px;
background: rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.countdown-info:hover {
background: rgba(0,0,0,0.1);
transform: scale(1.05);
}
.countdown-info:hover {
background: rgba(0, 0, 0, 0.1);
transform: scale(1.05);
}
.countdown-info.text-info {
background: rgba(23, 162, 184, 0.1);
border: 1px solid rgba(23, 162, 184, 0.2);
}
.countdown-info.text-info {
background: rgba(23, 162, 184, 0.1);
border: 1px solid rgba(23, 162, 184, 0.2);
}
.countdown-info.text-danger {
background: rgba(220, 53, 69, 0.1);
border: 1px solid rgba(220, 53, 69, 0.2);
animation: pulse 2s infinite;
}
.countdown-info.text-danger {
background: rgba(220, 53, 69, 0.1);
border: 1px solid rgba(220, 53, 69, 0.2);
animation: pulse 2s infinite;
}
.countdown-info.text-success {
background: rgba(40, 167, 69, 0.1);
border: 1px solid rgba(40, 167, 69, 0.2);
}
.countdown-info.text-success {
background: rgba(40, 167, 69, 0.1);
border: 1px solid rgba(40, 167, 69, 0.2);
}
.countdown-days {
font-weight: bold;
color: #17a2b8;
}
.countdown-days {
font-weight: bold;
color: #17a2b8;
}
/* Pulse Animation für fällige Erinnerungen */
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(220, 53, 69, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
}
}
/* Pulse Animation für fällige Erinnerungen */
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
}
/* Responsive Anpassungen */
@media (max-width: 768px) {
.reminder-badge {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
}
.reminder-date {
font-size: 10px;
}
.countdown-info {
font-size: 9px;
padding: 1px 4px;
}
.countdown-days {
font-size: 9px;
}
}
70% {
box-shadow: 0 0 0 10px rgba(220, 53, 69, 0);
}
/* Info Box Styling */
.info-box {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
padding: 20px;
margin-bottom: 30px;
color: white;
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
}
100% {
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
}
}
.info-box h5 {
color: white;
margin-bottom: 15px;
font-weight: 600;
}
/* Responsive Anpassungen */
@media (max-width: 768px) {
.reminder-badge {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
}
.info-box .feature-list {
list-style: none;
padding: 0;
margin: 0;
}
.reminder-date {
font-size: 10px;
}
.info-box .feature-list li {
padding: 8px 0;
border-bottom: 1px solid rgba(255,255,255,0.1);
display: flex;
align-items: center;
}
.countdown-info {
font-size: 9px;
padding: 1px 4px;
}
.info-box .feature-list li:last-child {
border-bottom: none;
}
.countdown-days {
font-size: 9px;
}
}
.info-box .feature-list li i {
margin-right: 10px;
width: 20px;
text-align: center;
}
/* Info Box Styling */
.info-box {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
padding: 20px;
margin-bottom: 30px;
color: white;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.info-box .workflow-steps {
background: rgba(255,255,255,0.1);
border-radius: 8px;
padding: 15px;
margin-top: 15px;
}
.info-box h5 {
color: white;
margin-bottom: 15px;
font-weight: 600;
}
.info-box .workflow-steps h6 {
color: white;
margin-bottom: 10px;
font-weight: 600;
}
.info-box .feature-list {
list-style: none;
padding: 0;
margin: 0;
}
.info-box .step {
display: flex;
align-items: center;
margin-bottom: 8px;
font-size: 14px;
}
.info-box .feature-list li {
padding: 8px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
}
.info-box .step-number {
background: rgba(255,255,255,0.2);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
font-weight: bold;
font-size: 12px;
}
.info-box .feature-list li:last-child {
border-bottom: none;
}
.info-box .cron-info {
background: rgba(255,255,255,0.1);
border-radius: 8px;
padding: 15px;
margin-top: 15px;
font-family: 'Courier New', monospace;
font-size: 13px;
}
.info-box .feature-list li i {
margin-right: 10px;
width: 20px;
text-align: center;
}
.info-box .cron-info code {
background: rgba(0,0,0,0.3);
padding: 2px 6px;
border-radius: 4px;
color: #ffd700;
}
</style>
.info-box .workflow-steps {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 15px;
margin-top: 15px;
}
.info-box .workflow-steps h6 {
color: white;
margin-bottom: 10px;
font-weight: 600;
}
.info-box .step {
display: flex;
align-items: center;
margin-bottom: 8px;
font-size: 14px;
}
.info-box .step-number {
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
font-weight: bold;
font-size: 12px;
}
.info-box .cron-info {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 15px;
margin-top: 15px;
font-family: 'Courier New', monospace;
font-size: 13px;
}
.info-box .cron-info code {
background: rgba(0, 0, 0, 0.3);
padding: 2px 6px;
border-radius: 4px;
color: #ffd700;
}
</style>
<div class="card">
<div class="card-header">
<h4 class="card-title">
<i class="fas fa-envelope"></i> Erinnerungen
</h4>
<div class="float-right">
<a href="{{ route('admin_payments_reminder_create') }}" class="btn btn-sm btn-primary">
<span class="far fa-plus-circle"></span> Erinnerung hinzufügen
</a>
</div>
</div>
<div class="card-datatable table-responsive pt-2">
<table class="datatables-style table table-striped table-bordered" id="datatable-pay-credit">
<thead>
<tr>
<th>#</th>
<th>{{__('Titel')}}</th>
<th>{{__('Interval') }}</th>
<th>{{__('Typ') }}</th>
<th>{{__('Action') }}</th>
<th>{{__('Aktiv') }}</th>
</tr>
</thead>
<tbody>
@foreach($reminders as $reminder)
<tr>
<td>
<a href="{{ route('admin_payments_reminder_edit', $reminder->id) }}" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>
</td>
<td>{{ $reminder->title }}</td>
<td>{{ $reminder->interval }}</td>
<td>{{ $reminder->getClearingtype() }}</td>
<td>{{ $reminder->action }}</td>
<td>{!! get_active_badge($reminder->active) !!}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="card">
<div class="card-header">
<h4 class="card-title">
<i class="fas fa-envelope"></i> Erinnerungen
</h4>
<div class="float-right">
<a href="{{ route('admin_payments_reminder_create') }}" class="btn btn-sm btn-primary">
<span class="far fa-plus-circle"></span> Erinnerung hinzufügen
</a>
</div>
</div>
<div class="card-datatable table-responsive pt-2">
<table class="datatables-style table table-striped table-bordered" id="datatable-pay-credit">
<thead>
<tr>
<th>#</th>
<th>{{ __('Titel') }}</th>
<th>{{ __('Interval') }}</th>
<th>{{ __('Typ') }}</th>
<th>{{ __('Action') }}</th>
<th>{{ __('Aktiv') }}</th>
</tr>
</thead>
<tbody>
@foreach ($reminders as $reminder)
<tr>
<td>
<a href="{{ route('admin_payments_reminder_edit', $reminder->id) }}"
class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>
</td>
<td>{{ $reminder->title }}</td>
<td>{{ $reminder->interval }}</td>
<td>{{ $reminder->getClearingtype() }}</td>
<td>{{ $reminder->action }}</td>
<td>{!! get_active_badge($reminder->active) !!}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class=" px-3" <p>Das Intervall bezieht sich immer auf das Datum der Bestellung, bzw. wann wurde die letzen
Erinnerung gesendet und
die Differenz zum nächtem Intervall.<br>
<i>(Beispiel: 2. Zahlungserinnerung am 01.10, 2.Zahlsintervall 30 - 3.Zahlsintervall 50 = 20 Tage, nächste
Erinnerung am 20.10 in 19 Tagen)</i>
</p>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
@ -282,7 +291,8 @@
</h4>
<div class="card-tools">
<span class="badge badge-primary">{{ $totalPayments }} offene Zahlungen</span>
<span class="badge badge-warning">{{ number_format($totalAmount, 2, ',', '.') }} Gesamtbetrag</span>
<span class="badge badge-warning">{{ number_format($totalAmount, 2, ',', '.') }}
Gesamtbetrag</span>
</div>
<div class="float-right">
<a href="{{ route('admin_payments_reminder_logs') }}" class="btn btn-sm btn-info mr-2">
@ -291,8 +301,8 @@
</div>
</div>
<div class="card-body">
<!-- Detaillierte Tabelle -->
<div class="table-responsive">
<table class="table table-bordered table-striped" id="payment-reminders-table">
@ -312,127 +322,129 @@
</thead>
<tbody>
@forelse($detailedData as $payment)
<tr>
<td data-order="{{ $payment['order_id'] }}">
<a class="btn btn-sm btn-outline-primary" href="{{ route('admin_sales_detail', $payment['order_id']) }}" target="_blank">
<i class="fa fa-eye"></i> {{ $payment['order_id'] }}
</a>
</td>
<td>
<span class="text-danger font-weight-bold">
{{ number_format($payment['amount']/100, 2, ',', '.') }}
</span>
</td>
<td>{{ $payment['created_at'] }}</td>
<td>
@if($payment['days_old'] > 30)
<span class="badge badge-danger">{{ $payment['days_old'] }} Tage</span>
@elseif($payment['days_old'] > 14)
<span class="badge badge-warning">{{ $payment['days_old'] }} Tage</span>
@else
<span class="badge badge-info">{{ $payment['days_old'] }} Tage</span>
@endif
</td>
<td>{{ $payment['name'] }}</td>
<td>{{ $payment['email'] }}</td>
<td>
@if($payment['clearingtype'] == 'fnc')
<span class="badge badge-primary">{{ $payment['clearingtype_name'] }}</span>
@else
<span class="badge badge-warning">{{ $payment['clearingtype_name'] }}</span>
@endif
</td>
<td>{!! $payment['shipped'] !!}</td>
<td>
@if($payment['reminder'] > 0)
<div class="d-flex flex-column align-items-start">
<!-- Reminder Badge mit verbesserter Darstellung -->
<span class="badge badge-pill reminder-badge badge-{{ $payment['reminder'] == 1 ? 'warning' : ($payment['reminder'] == 2 ? 'danger' : 'dark') }} mb-1"
data-toggle="tooltip"
data-placement="top"
title="{{ $payment['reminder'] }}. Zahlungserinnerung gesendet">
<i class="fas fa-envelope-open-text mr-1"></i>
<strong>{{ $payment['reminder'] }}.</strong>
@if($payment['reminder'] == 1)
<i class="fas fa-exclamation-triangle ml-1"></i>
@elseif($payment['reminder'] >= 2)
<i class="fas fa-exclamation-circle ml-1"></i>
@endif
</span>
<!-- Datum mit verbesserter Formatierung -->
@if($payment['reminder_date'])
<small class="text-muted reminder-date"
data-toggle="tooltip"
data-placement="bottom"
title="Letzte Erinnerung gesendet">
<i class="far fa-clock mr-1"></i>
{{ \Carbon\Carbon::parse($payment['reminder_date'])->diffForHumans() }}
</small>
@endif
<!-- Countdown für nächste Erinnerung -->
@if(isset($payment['countdown']) && $payment['countdown'])
@if($payment['countdown']['type'] == 'countdown')
<small class="text-info countdown-info mt-1"
data-toggle="tooltip"
data-placement="bottom"
title="Nächste Erinnerung am {{ \Carbon\Carbon::parse($payment['countdown']['next_reminder_date'])->format('d.m.Y') }}">
<i class="fas fa-hourglass-half mr-1"></i>
<span class="countdown-days">{{ $payment['countdown']['days_left'] }}</span> Tage
</small>
@elseif($payment['countdown']['type'] == 'overdue')
<small class="text-danger countdown-info mt-1"
data-toggle="tooltip"
data-placement="bottom"
title="Nächste Erinnerung war fällig am {{ \Carbon\Carbon::parse($payment['countdown']['next_reminder_date'])->format('d.m.Y') }}">
<i class="fas fa-exclamation-triangle mr-1"></i>
Fällig!
</small>
@elseif($payment['countdown']['type'] == 'completed')
<small class="text-success countdown-info mt-1"
data-toggle="tooltip"
data-placement="bottom"
title="Alle verfügbaren Erinnerungen wurden gesendet">
<i class="fas fa-check-circle mr-1"></i>
Alle gesendet
</small>
@endif
@endif
</div>
@else
<span class="badge badge-pill reminder-badge badge-light text-muted"
data-toggle="tooltip"
data-placement="top"
title="Noch keine Erinnerung gesendet">
<i class="fas fa-envelope mr-1"></i>
0
<tr>
<td data-order="{{ $payment['order_id'] }}">
<a class="btn btn-sm btn-outline-primary"
href="{{ route('admin_sales_detail', $payment['order_id']) }}"
target="_blank">
<i class="fa fa-eye"></i> {{ $payment['order_id'] }}
</a>
</td>
<td>
<span class="text-danger font-weight-bold">
{{ number_format($payment['amount'] / 100, 2, ',', '.') }}
</span>
@endif
</td>
<td>
<div class="btn-group">
<a href="{{ route('admin_payments_reminder_action', ['action' => 'send_reminder', 'id' => $payment['payment_id']]) }}" class="btn btn-sm btn-outline-primary"
</td>
<td>{{ $payment['created_at'] }}</td>
<td>
@if ($payment['days_old'] > 30)
<span class="badge badge-danger">{{ $payment['days_old'] }} Tage</span>
@elseif($payment['days_old'] > 14)
<span class="badge badge-warning">{{ $payment['days_old'] }} Tage</span>
@else
<span class="badge badge-info">{{ $payment['days_old'] }} Tage</span>
@endif
</td>
<td>{{ $payment['name'] }}</td>
<td>{{ $payment['email'] }}</td>
<td>
@if ($payment['clearingtype'] == 'fnc')
<span
class="badge badge-primary">{{ $payment['clearingtype_name'] }}</span>
@else
<span
class="badge badge-warning">{{ $payment['clearingtype_name'] }}</span>
@endif
</td>
<td>{!! $payment['shipped'] !!}</td>
<td>
@if ($payment['reminder'] > 0)
<div class="d-flex flex-column align-items-start">
<!-- Reminder Badge mit verbesserter Darstellung -->
<span
class="badge badge-pill reminder-badge badge-{{ $payment['reminder'] == 1 ? 'warning' : ($payment['reminder'] == 2 ? 'danger' : 'dark') }} mb-1"
data-toggle="tooltip" data-placement="top"
title="{{ $payment['reminder'] }}. Zahlungserinnerung gesendet">
<i class="fas fa-envelope-open-text mr-1"></i>
<strong>{{ $payment['reminder'] }}.</strong>
@if ($payment['reminder'] == 1)
<i class="fas fa-exclamation-triangle ml-1"></i>
@elseif($payment['reminder'] >= 2)
<i class="fas fa-exclamation-circle ml-1"></i>
@endif
</span>
<!-- Datum mit verbesserter Formatierung -->
@if ($payment['reminder_date'])
<small class="text-muted reminder-date" data-toggle="tooltip"
data-placement="bottom" title="Letzte Erinnerung gesendet">
<i class="far fa-clock mr-1"></i>
{{ \Carbon\Carbon::parse($payment['reminder_date'])->diffForHumans() }}
</small>
@endif
<!-- Countdown für nächste Erinnerung -->
@if (isset($payment['countdown']) && $payment['countdown'])
@if ($payment['countdown']['type'] == 'countdown')
<small class="text-info countdown-info mt-1"
data-toggle="tooltip" data-placement="bottom"
title="Nächste Erinnerung am {{ \Carbon\Carbon::parse($payment['countdown']['next_reminder_date'])->format('d.m.Y') }}">
<i class="fas fa-hourglass-half mr-1"></i>
<span
class="countdown-days">{{ $payment['countdown']['days_left'] }}</span>
Tage
</small>
@elseif($payment['countdown']['type'] == 'overdue')
<small class="text-danger countdown-info mt-1"
data-toggle="tooltip" data-placement="bottom"
title="Nächste Erinnerung war fällig am {{ \Carbon\Carbon::parse($payment['countdown']['next_reminder_date'])->format('d.m.Y') }}">
<i class="fas fa-exclamation-triangle mr-1"></i>
Fällig!
</small>
@elseif($payment['countdown']['type'] == 'completed')
<small class="text-success countdown-info mt-1"
data-toggle="tooltip" data-placement="bottom"
title="Alle verfügbaren Erinnerungen wurden gesendet">
<i class="fas fa-check-circle mr-1"></i>
Alle gesendet
</small>
@endif
@endif
</div>
@else
<span class="badge badge-pill reminder-badge badge-light text-muted"
data-toggle="tooltip" data-placement="top"
title="Noch keine Erinnerung gesendet">
<i class="fas fa-envelope mr-1"></i>
0
</span>
@endif
</td>
<td>
<div class="btn-group">
<a href="{{ route('admin_payments_reminder_action', ['action' => 'send_reminder', 'id' => $payment['payment_id']]) }}"
class="btn btn-sm btn-outline-primary"
onclick="return confirm('Möchten Sie wirklich eine Zahlungserinnerung senden?')">
<i class="fas fa-envelope"></i> Erinnerung senden
</a>
<a href="{{ route('admin_payments_reminder_action', ['action' => 'no_payment', 'id' => $payment['payment_id']]) }}" class="btn btn-sm btn-outline-danger"
<i class="fas fa-envelope"></i> Erinnerung senden
</a>
<a href="{{ route('admin_payments_reminder_action', ['action' => 'no_payment', 'id' => $payment['payment_id']]) }}"
class="btn btn-sm btn-outline-danger"
onclick="return confirm('Soll die Zahlung als nicht bezahlt markiert werden?')">
<i class="fas fa-trash"></i> keine Zahlung
</a>
</div>
</td>
</tr>
<i class="fas fa-trash"></i> keine Zahlung
</a>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="9" class="text-center text-muted">
<i class="fas fa-check-circle"></i> Keine offenen Zahlungen gefunden
</td>
</tr>
<tr>
<td colspan="9" class="text-center text-muted">
<i class="fas fa-check-circle"></i> Keine offenen Zahlungen gefunden
</td>
</tr>
@endforelse
</tbody>
</table>
@ -445,7 +457,7 @@
{{-- <div class="info-box mt-4">
{{-- <div class="info-box mt-4">
<h5><i class="fas fa-info-circle mr-2"></i>Zahlungserinnerungen System - Übersicht & Funktionsweise</h5>
<div class="row">
@ -527,56 +539,54 @@
</div>
</div>
</div> --}}
</div>
<script>
$(document).ready(function() {
// Tooltips initialisieren
$('[data-toggle="tooltip"]').tooltip();
// DataTable für Payment Reminder Einstellungen
$('#payment-reminders-settings-table').DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.10.24/i18n/German.json"
},
"pageLength": 25,
"responsive": true
});
</div>
<script>
$(document).ready(function() {
// Tooltips initialisieren
$('[data-toggle="tooltip"]').tooltip();
// DataTable für offene Zahlungen
$('#payment-reminders-table').DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.10.24/i18n/German.json"
},
"order": [[3, "desc"]], // Sortiere nach "Tage alt" absteigend
"pageLength": 50,
"responsive": true,
"columnDefs": [
{
"targets": [8], // Reminder Spalte
"orderable": true,
"searchable": false
// DataTable für Payment Reminder Einstellungen
$('#payment-reminders-settings-table').DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.10.24/i18n/German.json"
},
"pageLength": 25,
"responsive": true
});
// DataTable für offene Zahlungen
$('#payment-reminders-table').DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.10.24/i18n/German.json"
},
"order": [
[3, "desc"]
], // Sortiere nach "Tage alt" absteigend
"pageLength": 50,
"responsive": true,
"columnDefs": [{
"targets": [8], // Reminder Spalte
"orderable": true,
"searchable": false
}]
});
// Tooltips nach DataTable-Initialisierung neu initialisieren
$('#payment-reminders-table').on('draw.dt', function() {
$('[data-toggle="tooltip"]').tooltip();
});
});
function sendReminder(paymentId) {
if (confirm('Möchten Sie wirklich eine Zahlungserinnerung senden?')) {
// TODO: Implementiere Erinnerung senden
alert('Erinnerung für Payment ID ' + paymentId + ' würde gesendet werden');
}
]
});
// Tooltips nach DataTable-Initialisierung neu initialisieren
$('#payment-reminders-table').on('draw.dt', function() {
$('[data-toggle="tooltip"]').tooltip();
});
});
}
function sendReminder(paymentId) {
if (confirm('Möchten Sie wirklich eine Zahlungserinnerung senden?')) {
// TODO: Implementiere Erinnerung senden
alert('Erinnerung für Payment ID ' + paymentId + ' würde gesendet werden');
}
}
function viewDetails(paymentId) {
// TODO: Implementiere Detail-Ansicht
alert('Details für Payment ID ' + paymentId + ' würden angezeigt werden');
}
</script>
function viewDetails(paymentId) {
// TODO: Implementiere Detail-Ansicht
alert('Details für Payment ID ' + paymentId + ' würden angezeigt werden');
}
</script>
@endsection

View file

@ -42,3 +42,414 @@
@include('admin.product.upload_whitelabel')
@endsection
@section('scripts')
@php
$ingredient_catalog_for_js = $ingredient_catalog->keyBy('id')->map(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
'inci' => $item->inci,
'effect' => $item->effect,
'default_factor' => $item->default_factor,
'quality_name' => $item->materialQuality?->name ?? '',
];
});
$packaging_catalog_for_js = $packaging_catalog->keyBy('id')->map(function ($item) {
return [
'name' => $item->name,
'weight_grams' => $item->weight_grams,
'material_name' => $item->packagingMaterial?->name ?? '',
];
});
@endphp
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"></script>
<script>
(function ($) {
var catalog = @json($ingredient_catalog_for_js);
var packagingCatalog = @json($packaging_catalog_for_js);
function parseDeNumber(val) {
if (val === undefined || val === null || val === '') return NaN;
var s = String(val).replace(/\s/g, '').replace(',', '.');
var n = parseFloat(s);
return isNaN(n) ? NaN : n;
}
function updateEffective($row) {
var g = parseDeNumber($row.find('.pi-gram').val());
var f = parseDeNumber($row.find('.pi-factor').val());
var $out = $row.find('.pi-effective');
if (!isNaN(g) && !isNaN(f)) {
var eff = g * f;
$out.text(eff.toLocaleString('de-DE', { minimumFractionDigits: 3, maximumFractionDigits: 3 }));
} else {
$out.text('—');
}
}
function updateRecipeTotal() {
var total = 0;
var hasValue = false;
$('#ingredient-sortable-rows tr').each(function () {
var v = parseDeNumber($(this).find('.pi-gram').val());
if (!isNaN(v)) { total += v; hasValue = true; }
});
var $cell = $('#recipe-total-percent');
if (!hasValue) {
$cell.text('—').removeClass('text-danger text-success');
return;
}
var formatted = total.toLocaleString('de-DE', { minimumFractionDigits: 3, maximumFractionDigits: 3 }) + ' %';
$cell.text(formatted);
var diff = Math.abs(total - 100);
if (diff < 0.001) {
$cell.removeClass('text-danger').addClass('text-success');
$cell.attr('title', '');
} else {
$cell.removeClass('text-success').addClass('text-danger');
$cell.attr('title', 'Die Gesamtrezeptur ergibt nicht 100 %!');
}
}
function refreshAllEffectives() {
$('#ingredient-sortable-rows tr').each(function () { updateEffective($(this)); });
updateRecipeTotal();
}
$(document).ready(function () {
var $tbody = document.getElementById('ingredient-sortable-rows');
if ($tbody && typeof Sortable !== 'undefined') {
new Sortable($tbody, {
handle: '.ingredient-drag-handle',
animation: 150,
});
}
function appendIngredientRow(id) {
id = String(id);
var ing = catalog[id];
if (!ing) return;
var factor = ing.default_factor != null ? String(ing.default_factor).replace('.', ',') : '1,10';
var row = $('<tr data-ingredient-id="' + id + '">' +
'<td class="text-muted align-middle ingredient-drag-handle" style="cursor:grab">&#9776;</td>' +
'<td class="align-middle"></td><td class="align-middle small text-muted"></td><td class="align-middle"></td>' +
'<td><input type="hidden" name="pi_ingredient_id[]" value="' + id + '">' +
'<input type="text" name="pi_gram[]" class="form-control form-control-sm pi-gram" value="" autocomplete="off" step="0.001"></td>' +
'<td><input type="text" name="pi_factor[]" class="form-control form-control-sm pi-factor" value="' + factor + '" autocomplete="off"></td>' +
'<td class="align-middle pi-effective text-right small text-muted">—</td>' +
'<td class="align-middle"><a class="text-danger ingredient-row-remove" href="#" title="Entfernen"><i class="far fa-trash-alt"></i></a></td></tr>');
row.find('td').eq(1).text(ing.name || '');
row.find('td').eq(2).text(ing.quality_name || '—');
row.find('td').eq(3).text(ing.inci || '');
$('#ingredient-sortable-rows').append(row);
updateEffective(row);
updateRecipeTotal();
}
function syncIngredientModalRows() {
var usedIds = {};
$('#ingredient-sortable-rows tr').each(function () {
var id = $(this).data('ingredient-id');
if (id) usedIds[String(id)] = true;
});
$('#modal-ingredients-pick .ingredient-modal-row').each(function () {
var id = String($(this).data('ingredient-id'));
var inUse = !!usedIds[id];
var $cb = $(this).find('.js-ingredient-modal-cb');
$cb.prop('disabled', inUse);
$cb.attr('title', inUse ? '{{ __('Bereits im Produkt') }}' : '');
if (inUse) {
$cb.prop('checked', false);
}
$(this).toggleClass('text-muted', inUse);
$(this).css('opacity', inUse ? 0.65 : 1);
$(this).find('.js-ing-modal-name').toggleClass('text-muted', inUse);
$(this).find('.js-ing-modal-inci').toggleClass('text-muted', inUse);
});
}
function filterIngredientModalRows() {
var q = $('#ingredient-modal-search').val().trim().toLowerCase();
var visible = 0;
$('#ingredient-modal-tbody .ingredient-modal-row').each(function () {
var hay = $(this).attr('data-ingredient-search') || '';
var match = !q || hay.indexOf(q) !== -1;
$(this).toggle(match);
if (match) {
visible++;
}
});
$('#ingredient-modal-no-results').toggleClass('d-none', !(q.length > 0 && visible === 0));
}
$('#ingredient-modal-search').on('input', filterIngredientModalRows);
$('#modal-ingredients-pick').on('show.bs.modal', function () {
$('#ingredient-modal-search').val('');
$('#ingredient-modal-tbody .ingredient-modal-row').show();
$('#ingredient-modal-no-results').addClass('d-none');
syncIngredientModalRows();
});
$('#btn-ingredients-modal-add').on('click', function () {
var added = 0;
$('#modal-ingredients-pick .js-ingredient-modal-cb:checked:not(:disabled)').each(function () {
var id = String($(this).val());
appendIngredientRow(id);
$(this).prop('checked', false);
added++;
});
if (added > 0) {
syncIngredientModalRows();
$('#modal-ingredients-pick').modal('hide');
}
});
$(document).on('click', '.ingredient-row-remove', function (ev) {
ev.preventDefault();
var $tr = $(this).closest('tr');
$tr.remove();
syncIngredientModalRows();
updateRecipeTotal();
});
$(document).on('input change', '.pi-gram, .pi-factor', function () {
updateEffective($(this).closest('tr'));
updateRecipeTotal();
});
refreshAllEffectives();
var $packTbody = document.getElementById('packaging-sortable-rows');
if ($packTbody && typeof Sortable !== 'undefined') {
new Sortable($packTbody, {
handle: '.packaging-drag-handle',
animation: 150,
});
}
function appendPackagingRow(id) {
id = String(id);
var pk = packagingCatalog[id];
if (!pk) {
return;
}
var wCell = '—';
if (pk.weight_grams != null && pk.weight_grams !== '') {
wCell = String(pk.weight_grams).replace('.', ',');
}
var row = $('<tr data-packaging-item-id="' + id + '">' +
'<td class="text-muted align-middle packaging-drag-handle" style="cursor:grab">&#9776;</td>' +
'<td class="align-middle"></td><td class="align-middle"></td><td class="align-middle text-right"></td>' +
'<td><input type="hidden" name="pp_packaging_item_id[]" value="' + id + '">' +
'<input type="text" name="pp_quantity[]" class="form-control form-control-sm pp-quantity" value="1" autocomplete="off"></td>' +
'<td class="align-middle"><a class="text-danger packaging-row-remove" href="#" title="{{ __('Entfernen') }}"><i class="far fa-trash-alt"></i></a></td></tr>');
row.find('td').eq(1).text(pk.name || '');
row.find('td').eq(2).text(pk.material_name || '—');
row.find('td').eq(3).text(wCell);
$('#packaging-sortable-rows').append(row);
}
function syncPackagingModalRows() {
var usedIds = {};
$('#packaging-sortable-rows tr').each(function () {
var pid = $(this).data('packaging-item-id');
if (pid) {
usedIds[String(pid)] = true;
}
});
$('#modal-packaging-pick .packaging-modal-row').each(function () {
var pid = String($(this).data('packaging-item-id'));
var inUse = !!usedIds[pid];
var $cb = $(this).find('.js-packaging-modal-cb');
$cb.prop('disabled', inUse);
$cb.attr('title', inUse ? '{{ __('Bereits im Produkt') }}' : '');
if (inUse) {
$cb.prop('checked', false);
}
$(this).toggleClass('text-muted', inUse);
$(this).css('opacity', inUse ? 0.65 : 1);
$(this).find('.js-pk-modal-name').toggleClass('text-muted', inUse);
$(this).find('.js-pk-modal-mat').toggleClass('text-muted', inUse);
});
}
function filterPackagingModalRows() {
var q = $('#packaging-modal-search').val().trim().toLowerCase();
var visible = 0;
$('#packaging-modal-tbody .packaging-modal-row').each(function () {
var hay = $(this).attr('data-packaging-search') || '';
var match = !q || hay.indexOf(q) !== -1;
$(this).toggle(match);
if (match) {
visible++;
}
});
$('#packaging-modal-no-results').toggleClass('d-none', !(q.length > 0 && visible === 0));
}
$('#packaging-modal-search').on('input', filterPackagingModalRows);
$('#modal-packaging-pick').on('show.bs.modal', function () {
$('#packaging-modal-search').val('');
$('#packaging-modal-tbody .packaging-modal-row').show();
$('#packaging-modal-no-results').addClass('d-none');
syncPackagingModalRows();
});
$('#btn-packaging-modal-add').on('click', function () {
var added = 0;
$('#modal-packaging-pick .js-packaging-modal-cb:checked:not(:disabled)').each(function () {
var id = String($(this).val());
appendPackagingRow(id);
$(this).prop('checked', false);
added++;
});
if (added > 0) {
syncPackagingModalRows();
$('#modal-packaging-pick').modal('hide');
}
});
$(document).on('click', '.packaging-row-remove', function (ev) {
ev.preventDefault();
$(this).closest('tr').remove();
syncPackagingModalRows();
});
// === Hersteller-Rezeptur (Manufacturer) ===
var $mfgTbody = document.getElementById('mfg-ingredient-sortable-rows');
if ($mfgTbody && typeof Sortable !== 'undefined') {
new Sortable($mfgTbody, {
handle: '.mfg-ingredient-drag-handle',
animation: 150,
});
}
function updateMfgEffective($row) {
var g = parseDeNumber($row.find('.mfg-gram').val());
var f = parseDeNumber($row.find('.mfg-factor').val());
var $out = $row.find('.mfg-effective');
if (!isNaN(g) && !isNaN(f)) {
$out.text((g * f).toLocaleString('de-DE', { minimumFractionDigits: 3, maximumFractionDigits: 3 }));
} else {
$out.text('—');
}
}
function updateMfgRecipeTotal() {
var total = 0, hasValue = false;
$('#mfg-ingredient-sortable-rows tr').each(function () {
var v = parseDeNumber($(this).find('.mfg-gram').val());
if (!isNaN(v)) { total += v; hasValue = true; }
});
var $cell = $('#mfg-recipe-total-percent');
if (!hasValue) { $cell.text('—').removeClass('text-danger text-success'); return; }
$cell.text(total.toLocaleString('de-DE', { minimumFractionDigits: 3, maximumFractionDigits: 3 }) + ' %');
if (Math.abs(total - 100) < 0.001) {
$cell.removeClass('text-danger').addClass('text-success').attr('title', '');
} else {
$cell.removeClass('text-success').addClass('text-danger').attr('title', 'Die Gesamtrezeptur ergibt nicht 100 %!');
}
}
function refreshAllMfgEffectives() {
$('#mfg-ingredient-sortable-rows tr').each(function () { updateMfgEffective($(this)); });
updateMfgRecipeTotal();
}
function appendMfgIngredientRow(id) {
id = String(id);
var ing = catalog[id];
if (!ing) return;
var factor = ing.default_factor != null ? String(ing.default_factor).replace('.', ',') : '1,10';
var row = $('<tr data-ingredient-id="' + id + '">' +
'<td class="text-muted align-middle mfg-ingredient-drag-handle" style="cursor:grab">&#9776;</td>' +
'<td class="align-middle"></td><td class="align-middle small text-muted"></td><td class="align-middle"></td>' +
'<td><input type="hidden" name="mfg_ingredient_id[]" value="' + id + '">' +
'<input type="text" name="mfg_gram[]" class="form-control form-control-sm mfg-gram" value="" autocomplete="off" step="0.001"></td>' +
'<td><input type="text" name="mfg_factor[]" class="form-control form-control-sm mfg-factor" value="' + factor + '" autocomplete="off"></td>' +
'<td class="align-middle mfg-effective text-right small text-muted">—</td>' +
'<td class="align-middle"><a class="text-danger mfg-ingredient-row-remove" href="#" title="Entfernen"><i class="far fa-trash-alt"></i></a></td></tr>');
row.find('td').eq(1).text(ing.name || '');
row.find('td').eq(2).text(ing.quality_name || '—');
row.find('td').eq(3).text(ing.inci || '');
$('#mfg-ingredient-sortable-rows').append(row);
updateMfgEffective(row);
updateMfgRecipeTotal();
}
function syncMfgIngredientModalRows() {
var usedIds = {};
$('#mfg-ingredient-sortable-rows tr').each(function () {
var id = $(this).data('ingredient-id');
if (id) usedIds[String(id)] = true;
});
$('#modal-mfg-ingredients-pick .mfg-ingredient-modal-row').each(function () {
var id = String($(this).data('ingredient-id'));
var inUse = !!usedIds[id];
var $cb = $(this).find('.js-mfg-ingredient-modal-cb');
$cb.prop('disabled', inUse);
if (inUse) $cb.prop('checked', false);
$(this).css('opacity', inUse ? 0.65 : 1);
});
}
$('#mfg-ingredient-modal-search').on('input', function () {
var q = $(this).val().trim().toLowerCase();
var visible = 0;
$('#mfg-ingredient-modal-tbody .mfg-ingredient-modal-row').each(function () {
var hay = $(this).attr('data-ingredient-search') || '';
var match = !q || hay.indexOf(q) !== -1;
$(this).toggle(match);
if (match) visible++;
});
$('#mfg-ingredient-modal-no-results').toggleClass('d-none', !(q.length > 0 && visible === 0));
});
$('#modal-mfg-ingredients-pick').on('show.bs.modal', function () {
$('#mfg-ingredient-modal-search').val('');
$('#mfg-ingredient-modal-tbody .mfg-ingredient-modal-row').show();
$('#mfg-ingredient-modal-no-results').addClass('d-none');
syncMfgIngredientModalRows();
});
$('#btn-mfg-ingredients-modal-add').on('click', function () {
var added = 0;
$('#modal-mfg-ingredients-pick .js-mfg-ingredient-modal-cb:checked:not(:disabled)').each(function () {
appendMfgIngredientRow(String($(this).val()));
$(this).prop('checked', false);
added++;
});
if (added > 0) { syncMfgIngredientModalRows(); $('#modal-mfg-ingredients-pick').modal('hide'); }
});
$(document).on('click', '.mfg-ingredient-row-remove', function (ev) {
ev.preventDefault();
$(this).closest('tr').remove();
syncMfgIngredientModalRows();
updateMfgRecipeTotal();
});
$(document).on('input change', '.mfg-gram, .mfg-factor', function () {
updateMfgEffective($(this).closest('tr'));
updateMfgRecipeTotal();
});
refreshAllMfgEffectives();
function toggleShelfMonths() {
var v = $('.js-shelf-life-type:checked').val();
if (v === 'fixed') {
$('#shelf-life-months-wrap').show();
} else {
$('#shelf-life-months-wrap').hide();
}
}
$(document).on('change', '.js-shelf-life-type', toggleShelfMonths);
toggleShelfMonths();
});
})(jQuery);
</script>
@endsection

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,218 +1,392 @@
@if (Auth::check())
@if(Auth::check())
<div id="layout-sidenav" class="{{ isset($layout_sidenav_horizontal) ? 'layout-sidenav-horizontal sidenav-horizontal container-p-x flex-grow-0' : 'layout-sidenav sidenav-vertical' }} sidenav bg-sidenav-theme">
<div id="layout-sidenav"
class="{{ isset($layout_sidenav_horizontal) ? 'layout-sidenav-horizontal sidenav-horizontal container-p-x flex-grow-0' : 'layout-sidenav sidenav-vertical' }} sidenav bg-sidenav-theme">
<!-- Inner -->
<ul class="sidenav-inner{{ empty($layout_sidenav_horizontal) ? ' py-1' : '' }}">
<li class="sidenav-item{{ Request::is('home') ? ' active' : '' }}">
<a href="{{ route('home') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-home"></i><div>{{__('navigation.home')}}</div></a>
<a href="{{ route('home') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-home"></i>
<div>{{ __('navigation.home') }}</div>
</a>
</li>
@if(Auth::user()->showSideNav())
<li class="sidenav-item @if(Request::is('user/edit', 'user/membership')) open @endif">
@if (Auth::user()->showSideNav())
<li class="sidenav-item @if (Request::is('user/edit', 'user/membership')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-ios-contact"></i>
<div>{{ __('navigation.my_account') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item Request::is('user/payment/paycredit') ? ' active' : '' }} {{ Request::is('user/payment/paycredit') ? ' active' : '' }}">
<a href="{{ route('user_payment_paycredit') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-cash"></i><div>{{ __('navigation.paycredit') }}</div></a>
<li class="sidenav-item{{ Request::is('user/payment/paycredit') ? ' active' : '' }}">
<a href="{{ route('user_payment_paycredit') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-cash"></i>
<div>{{ __('navigation.paycredit') }}</div>
</a>
</li>
<li class="sidenav-item Request::is('user/payment/credit') ? ' active' : '' }} {{ Request::is('user/payment/credit') ? ' active' : '' }}">
<a href="{{ route('user_payment_credit') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-cash"></i><div>{{ __('navigation.credit') }}</div></a>
<li class="sidenav-item{{ Request::is('user/payment/credit') ? ' active' : '' }}">
<a href="{{ route('user_payment_credit') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-cash"></i>
<div>{{ __('navigation.credit') }}</div>
</a>
</li>
<li class="sidenav-item Request::is('user/payment/revenue') ? ' active' : '' }} {{ Request::is('user/payment/revenue') ? ' active' : '' }}">
<a href="{{ route('user_payment_revenue') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-trending-up"></i><div>{{ __('navigation.revenue') }}</div></a>
<li class="sidenav-item{{ Request::is('user/payment/revenue') ? ' active' : '' }}">
<a href="{{ route('user_payment_revenue') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-trending-up"></i>
<div>{{ __('navigation.revenue') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('user/edit') ? ' active' : '' }}">
<a href="{{ route('user_edit') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-create"></i><div>{{ __('navigation.my_data') }}</div></a>
<a href="{{ route('user_edit') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-create"></i>
<div>{{ __('navigation.my_data') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('user/profile') ? ' active' : '' }}">
<a href="{{ route('user_profile') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-contact"></i><div>{{ __('navigation.my_profile') }}</div></a>
<a href="{{ route('user_profile') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-contact"></i>
<div>{{ __('navigation.my_profile') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('user/membership') ? ' active' : '' }}">
<a href="{{ route('user_membership') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-people"></i><div>{{ __('navigation.membership') }}</div></a>
<a href="{{ route('user_membership') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-people"></i>
<div>{{ __('navigation.membership') }}</div>
</a>
</li>
</ul>
</li>
@if(Auth::user()->isActiveAccount())
<li class="sidenav-item{{ Request::is('user/shop') ? ' active' : '' }}">
<a href="{{ route('user_shop') }}" class="sidenav-link">
<i class="sidenav-icon ion ion-ios-basket"></i>
<div>{{ __('navigation.my_shop') }}</div>
<div class="pl-1 ml-auto">
</div>
</a>
</li>
@if (Auth::user()->isActiveAccount())
<li class="sidenav-item{{ Request::is('user/shop') ? ' active' : '' }}">
<a href="{{ route('user_shop') }}" class="sidenav-link">
<i class="sidenav-icon ion ion-ios-basket"></i>
<div>{{ __('navigation.my_shop') }}</div>
<div class="pl-1 ml-auto">
</div>
</a>
</li>
@endif
@if(Auth::user()->isActiveAccount())
<li class="sidenav-item @if(Request::is('user/promotion', '/user/promotion/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-rocket"></i>
<div>{{ __('navigation.my_promotions') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('user/promotion') ? ' active' : '' }}">
<a href="{{ route('user_promotion') }}" class="sidenav-link"><i class="sidenav-icon ion-md-rocket"></i><div>{{ __('navigation.overview') }}</div></a>
</li>
<li class="sidenav-item">
<a href="{{ route('user_order_my_delivery', ['cr']) }}" class="sidenav-link"><i class="sidenav-icon ion-md-cash"></i><div>{{ __('navigation.recharge_credit') }}</div></a>
</li>
</ul>
</li>
@if (Auth::user()->isActiveAccount())
<li class="sidenav-item @if (Request::is('user/promotion', '/user/promotion/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-rocket"></i>
<div>{{ __('navigation.my_promotions') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('user/promotion') ? ' active' : '' }}">
<a href="{{ route('user_promotion') }}" class="sidenav-link"><i
class="sidenav-icon ion-md-rocket"></i>
<div>{{ __('navigation.overview') }}</div>
</a>
</li>
<li class="sidenav-item">
<a href="{{ route('user_order_my_delivery', ['cr']) }}" class="sidenav-link"><i
class="sidenav-icon ion-md-cash"></i>
<div>{{ __('navigation.recharge_credit') }}</div>
</a>
</li>
</ul>
</li>
@endif
@if(Auth::user()->isActiveAccount())
<li class="sidenav-item @if(Request::is('user/team/*')) open @endif">
@if (Auth::user()->isActiveAccount())
<li class="sidenav-item @if (Request::is('user/team/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-ios-people"></i>
<div>{{ __('navigation.member') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('user/team/members') ? ' active' : '' }}">
<a href="{{ route('user_team_members') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-person-add"></i><div>{{ __('navigation.new_register') }}</div></a>
<a href="{{ route('user_team_members') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-person-add"></i>
<div>{{ __('navigation.new_register') }}</div>
</a>
</li>
</ul>
</li>
@endif
@if(Auth::user()->isActiveAccount())
<li class="sidenav-item @if(Request::is('user/customers', 'user/customer/*')) open @endif">
@if (Auth::user()->isActiveAccount())
<li class="sidenav-item @if (Request::is('user/customers', 'user/customer/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-contacts"></i>
<div>{{ __('navigation.my_clients') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('user/customers', 'user/customer/detail/*', 'user/sales/orders') ? ' active' : '' }}">
<a href="{{ route('user_customers') }}" class="sidenav-link"><i class="sidenav-icon ion ion ion-md-contacts"></i><div>{{ __('navigation.overview') }}</div></a>
<li
class="sidenav-item{{ Request::is('user/customers', 'user/customer/detail/*', 'user/sales/orders') ? ' active' : '' }}">
<a href="{{ route('user_customers') }}" class="sidenav-link"><i
class="sidenav-icon ion ion ion-md-contacts"></i>
<div>{{ __('navigation.overview') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('user/customer/add/*') ? ' active' : '' }}">
<a href="{{ route('user_customer_add', ['new']) }}" class="sidenav-link"><i class="sidenav-icon ion ion ion-md-person-add"></i><div>{{ __('navigation.add') }}</div></a>
<a href="{{ route('user_customer_add', ['new']) }}" class="sidenav-link"><i
class="sidenav-icon ion ion ion-md-person-add"></i>
<div>{{ __('navigation.add') }}</div>
</a>
</li>
</ul>
</li>
@endif
@if(Auth::user()->isActiveAccount())
<li class="sidenav-item{{ Request::is('user/order/my/*') ? ' active' : '' }}">
<a href="{{ route('user_order_my_delivery', ['me']) }}" class="sidenav-link"><i class="sidenav-icon ion ion ion-md-basket"></i><div>{{ __('navigation.do_order') }}</div></a>
</li>
@if (Auth::user()->isActiveAccount())
<li class="sidenav-item{{ Request::is('user/order/my/*') ? ' active' : '' }}">
<a href="{{ route('user_order_my_delivery', ['me']) }}" class="sidenav-link"><i
class="sidenav-icon ion ion ion-md-basket"></i>
<div>{{ __('navigation.do_order') }}</div>
</a>
</li>
@endif
@if(Auth::user()->isActiveAccount())
<li class="sidenav-item @if(Request::is('user/myorders', 'user/myorder/*', 'user/sales', 'user/sales/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-ios-cart"></i>
<div>{{ __('navigation.orders') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('user/myorders', 'user/myorder/detail/*') ? ' active' : '' }}">
<a href="{{ route('user_myorders') }}" class="sidenav-link"><i class="sidenav-icon ion ion ion-ios-contact"></i><div>{{ __('navigation.my') }}</div></a>
</li>
<li class="sidenav-item{{ Request::is('user/sales', 'user/sales/*') ? ' active' : '' }}">
<a href="{{ route('user_sales') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-contacts"></i><div>{{ __('navigation.clients') }}</div></a>
</li>
</ul>
</li>
@if (Auth::user()->isActiveAccount())
<li class="sidenav-item @if (Request::is('user/myorders', 'user/myorder/*', 'user/sales', 'user/sales/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-ios-cart"></i>
<div>{{ __('navigation.orders') }}</div>
</a>
<ul class="sidenav-menu">
<li
class="sidenav-item{{ Request::is('user/myorders', 'user/myorder/detail/*') ? ' active' : '' }}">
<a href="{{ route('user_myorders') }}" class="sidenav-link"><i
class="sidenav-icon ion ion ion-ios-contact"></i>
<div>{{ __('navigation.my') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('user/sales', 'user/sales/*') ? ' active' : '' }}">
<a href="{{ route('user_sales') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-contacts"></i>
<div>{{ __('navigation.clients') }}</div>
</a>
</li>
</ul>
</li>
@endif
@endif
@if(Auth::user()->isCopyReader())
@if (Auth::user()->isCopyReader())
<li class="sidenav-divider mb-1"></li>
<li class="sidenav-header small font-weight-semibold">REDAKTEUR</li>
<li class="sidenav-item @if(Request::is('admin/product/*')) open @endif">
<li class="sidenav-item @if (Request::is('admin/product/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-cube"></i>
<div>{{ __('navigation.products') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('admin/product/show') ? ' active' : '' }}">
<a href="{{ route('admin_product_show') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-cube"></i><div>{{ __('navigation.overview') }}</div></a>
<a href="{{ route('admin_product_show') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-cube"></i>
<div>{{ __('navigation.overview') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/product/categories') ? ' active' : '' }}">
<a href="{{ route('admin_product_categories') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-list"></i><div>{{ __('navigation.categories') }}</div></a>
<a href="{{ route('admin_product_categories') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-list"></i>
<div>{{ __('navigation.categories') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/product/ingredients') ? ' active' : '' }}">
<a href="{{ route('admin_product_ingredients') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-ice-cream"></i><div>{{ __('navigation.ingredients') }}</div></a>
<a href="{{ route('admin_product_ingredients') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-ice-cream"></i>
<div>{{ __('navigation.ingredients') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/product/attributes') ? ' active' : '' }}">
<a href="{{ route('admin_product_attributes') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-apps"></i><div>{{ __('navigation.attribute') }}</div></a>
<li class="sidenav-item{{ Request::is('admin/product/attributes') ? ' active' : '' }}">
<a href="{{ route('admin_product_attributes') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-apps"></i>
<div>{{ __('navigation.attribute') }}</div>
</a>
</li>
</ul>
</li>
@endif
@if(Auth::user()->isAdmin())
@if (Auth::user()->isSuperAdmin() || Auth::user()->isAdmin() || Auth::user()->isCopyReader())
<li class="sidenav-divider mb-1"></li>
<li class="sidenav-header small font-weight-semibold">WARENWIRTSCHAFT</li>
@if (Auth::user()->isCopyReader())
<li class="sidenav-item{{ Request::is('admin/inventory/stock-entries*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.stock-entries.index') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-download"></i>
<div>{{ __('Wareneingang') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/inventory/productions*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.productions.index') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-construct"></i>
<div>{{ __('Produktion') }}</div>
</a>
</li>
@endif
@if (Auth::user()->isAdmin())
<li class="sidenav-item @if (Request::is(
'admin/inventory/supplier-categories*',
'admin/inventory/suppliers*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-bus"></i>
<div>Lieferanten</div>
</a>
<ul class="sidenav-menu">
<li
class="sidenav-item{{ Request::is('admin/inventory/supplier-categories*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.supplier-categories.index') }}"
class="sidenav-link"><i class="sidenav-icon ion ion-md-pricetags"></i>
<div>{{ __('Kategorien') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/inventory/suppliers*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.suppliers.index') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-list"></i>
<div>{{ __('Übersicht') }}</div>
</a>
</li>
</ul>
</li>
<li class="sidenav-item{{ Request::is('admin/inventory/packaging-items*') && request('category', 'packaging') === 'packaging' ? ' active' : '' }}">
<a href="{{ route('admin.inventory.packaging-items.index', ['category' => 'packaging']) }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-basket"></i>
<div>{{ __('Produktverpackung') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/inventory/packaging-items*') && request('category') === 'shipping' ? ' active' : '' }}">
<a href="{{ route('admin.inventory.packaging-items.index', ['category' => 'shipping']) }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-mail"></i>
<div>{{ __('Versandverpackung') }}</div>
</a>
</li>
@endif
@if (Auth::user()->isSuperAdmin())
<li class="sidenav-item @if (Request::is(
'admin/inventory/locations*',
'admin/inventory/material-qualities*',
'admin/inventory/packaging-materials*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-cog"></i>
<div>Einstellungen</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('admin/inventory/locations*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.locations.index') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-pin"></i>
<div>{{ __('Lagerorte') }}</div>
</a>
</li>
<li
class="sidenav-item{{ Request::is('admin/inventory/material-qualities*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.material-qualities.index') }}"
class="sidenav-link"><i class="sidenav-icon ion ion-md-flask"></i>
<div>{{ __('Rohstoffqualität') }}</div>
</a>
</li>
<li
class="sidenav-item{{ Request::is('admin/inventory/packaging-materials*') ? ' active' : '' }}">
<a href="{{ route('admin.inventory.packaging-materials.index') }}"
class="sidenav-link"><i class="sidenav-icon ion ion-md-cube"></i>
<div>{{ __('Verpackungsmaterial') }}</div>
</a>
</li>
</ul>
</li>
@endif
@endif
@if (Auth::user()->isAdmin())
<li class="sidenav-divider mb-1"></li>
<li class="sidenav-header small font-weight-semibold">ADMIN</li>
<li class="sidenav-item{{ Request::is('admin/leads') ? ' active' : '' }} {{ Request::is('admin/lead/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_leads') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-contacts"></i><div>{{ __('navigation.member') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/leads') ? ' active' : '' }} {{ Request::is('admin/lead/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_leads') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-contacts"></i>
<div>{{ __('navigation.member') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/customers') ? ' active' : '' }} {{ Request::is('admin/customer/*') ? ' active' : '' }}">
<a href="{{ route('admin_customers') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-contacts"></i><div>{{ __('navigation.clients') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/customers') ? ' active' : '' }} {{ Request::is('admin/customer/*') ? ' active' : '' }}">
<a href="{{ route('admin_customers') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-contacts"></i>
<div>{{ __('navigation.clients') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/sales') ? ' active' : '' }} {{ Request::is('admin/sales/*') ? ' active' : '' }}">
<a href="{{ route('admin_sales') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-cart"></i><div>{{ __('navigation.orders') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/sales') ? ' active' : '' }} {{ Request::is('admin/sales/*') ? ' active' : '' }}">
<a href="{{ route('admin_sales') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-cart"></i>
<div>{{ __('navigation.orders') }}</div>
</a>
</li>
<li class="sidenav-item @if(Request::is('admin/payments/*')) open @endif">
<li class="sidenav-item @if (Request::is('admin/payments/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-cash"></i>
<div>{{ __('navigation.payments') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('admin/payments/credit') ? ' active' : '' }}">
<a href="{{ route('admin_payments_credit') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-arrow-dropright-circle"></i><div>{{ __('navigation.credit') }}</div></a>
<a href="{{ route('admin_payments_credit') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-arrow-dropright-circle"></i>
<div>{{ __('navigation.credit') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/payments/paycredit') ? ' active' : '' }}">
<a href="{{ route('admin_payments_paycredit') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-arrow-dropup-circle"></i><div>{{ __('navigation.paycredit') }}</div></a>
<a href="{{ route('admin_payments_paycredit') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-arrow-dropup-circle"></i>
<div>{{ __('navigation.paycredit') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/payments/invoice') ? ' active' : '' }}">
<a href="{{ route('admin_payments_invoice') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-arrow-dropleft-circle"></i><div>{{ __('navigation.invoice') }}</div></a>
<a href="{{ route('admin_payments_invoice') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-arrow-dropleft-circle"></i>
<div>{{ __('navigation.invoice') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/payments/reminder') ? ' active' : '' }} {{ Request::is('admin/payments/reminder/*') ? ' active' : '' }}">
<a href="{{ route('admin_payments_reminder') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-alarm"></i><div>{{ __('navigation.reminder') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/payments/reminder') ? ' active' : '' }} {{ Request::is('admin/payments/reminder/*') ? ' active' : '' }}">
<a href="{{ route('admin_payments_reminder') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-alarm"></i>
<div>{{ __('navigation.reminder') }}</div>
</a>
</li>
</ul>
</li>
<li class="sidenav-item @if(Request::is('admin/promotions', '/admin/promotion/*')) open @endif">
<li class="sidenav-item @if (Request::is('admin/promotions', '/admin/promotion/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-ios-rocket"></i>
<div>{{ __('navigation.promotion') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('admin/promotions') ? ' active' : '' }}">
<a href="{{ route('admin_promotions') }}" class="sidenav-link"><i class="sidenav-icon ion-ios-rocket"></i><div>{{ __('navigation.overview') }}</div></a>
<a href="{{ route('admin_promotions') }}" class="sidenav-link"><i
class="sidenav-icon ion-ios-rocket"></i>
<div>{{ __('navigation.overview') }}</div>
</a>
</li>
</ul>
</li>
<li class="sidenav-item @if(Request::is('admin/stats/*')) open @endif">
<li class="sidenav-item @if (Request::is('admin/stats/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-analytics"></i>
<div>{{ __('navigation.evaluation') }}</div>
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('admin/stats/sales/volumes') ? ' active' : '' }}">
<a href="{{ route('admin_stats_sales_volumes') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-cart"></i><div>{{ __('navigation.sales_volumes') }}</div></a>
<a href="{{ route('admin_stats_sales_volumes') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-cart"></i>
<div>{{ __('navigation.sales_volumes') }}</div>
</a>
</li>
</ul>
</li>
{{--
<li class="sidenav-item @if(Request::is('admin/sites/*')) open @endif">
{{--
<li class="sidenav-item @if (Request::is('admin/sites/*')) open @endif">
<a href="javascript:void(0)" class="sidenav-link sidenav-toggle">
<i class="sidenav-icon ion ion-md-albums"></i>
<div>{{ __('navigation.contents') }}</div>
@ -243,33 +417,58 @@
--}}
@endif
@if(Auth::user()->isSuperAdmin())
@if (Auth::user()->isSuperAdmin())
<li class="sidenav-divider mb-1"></li>
<li class="sidenav-header small font-weight-semibold">SUPERADMIN</li>
<li class="sidenav-item{{ Request::is('admin/users', 'admin/user/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_users') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-ribbon"></i><div>{{ __('navigation.user_roles') }}</div></a>
<a href="{{ route('admin_users') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-ribbon"></i>
<div>{{ __('navigation.user_roles') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/shippings', 'admin/shipping/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_shippings') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-gift"></i><div>{{ __('navigation.shipping_costs') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/shippings', 'admin/shipping/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_shippings') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-gift"></i>
<div>{{ __('navigation.shipping_costs') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/payment_methods') ? ' active' : '' }}">
<a href="{{ route('admin_payment_methods') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-cash"></i><div>{{ __('navigation.payment_methods') }}</div></a>
<a href="{{ route('admin_payment_methods') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-cash"></i>
<div>{{ __('navigation.payment_methods') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/levels') ? ' active' : '' }} {{ Request::is('admin/level/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_levels') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-trending-up"></i><div>{{ __('navigation.user_level') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/levels') ? ' active' : '' }} {{ Request::is('admin/level/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_levels') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-trending-up"></i>
<div>{{ __('navigation.user_level') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/lead_types') ? ' active' : '' }}">
<a href="{{ route('admin_lead_types') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-contacts"></i><div>Arten {{ __('navigation.member') }}</div></a>
<a href="{{ route('admin_lead_types') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-contacts"></i>
<div>Arten {{ __('navigation.member') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/countries') ? ' active' : '' }} {{ Request::is('admin/country/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_countries') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-globe"></i><div>{{ __('navigation.countries') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/countries') ? ' active' : '' }} {{ Request::is('admin/country/edit/*') ? ' active' : '' }}">
<a href="{{ route('admin_countries') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-globe"></i>
<div>{{ __('navigation.countries') }}</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('admin/settings') ? ' active' : '' }} {{ Request::is('admin/setting/*') ? ' active' : '' }}">
<a href="{{ route('admin_settings') }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-cog"></i><div>{{ __('navigation.settings') }}</div></a>
<li
class="sidenav-item{{ Request::is('admin/settings') ? ' active' : '' }} {{ Request::is('admin/setting/*') ? ' active' : '' }}">
<a href="{{ route('admin_settings') }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-cog"></i>
<div>{{ __('navigation.settings') }}</div>
</a>
</li>
@endif
@if(Auth::user()->isSySAdmin())
@if (Auth::user()->isSySAdmin())
<li class="sidenav-divider mb-1"></li>
<li class="sidenav-header small font-weight-semibold">SYSADMIN</li>
@ -280,10 +479,16 @@
</a>
<ul class="sidenav-menu">
<li class="sidenav-item{{ Request::is('sysadmin/tools/pay_credits') ? ' active' : '' }}">
<a href="{{ route('sysadmin_tools', ['export_vp']) }}" class="sidenav-link"><i class="sidenav-icon ion ion-md-download"></i><div>Export VP</div></a>
<a href="{{ route('sysadmin_tools', ['export_vp']) }}" class="sidenav-link"><i
class="sidenav-icon ion ion-md-download"></i>
<div>Export VP</div>
</a>
</li>
<li class="sidenav-item{{ Request::is('sysadmin/tools/dbaction') ? ' active' : '' }}">
<a href="{{ route('sysadmin_tools', ['dbaction']) }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-warning"></i><div>DB Action</div></a>
<a href="{{ route('sysadmin_tools', ['dbaction']) }}" class="sidenav-link"><i
class="sidenav-icon ion ion-ios-warning"></i>
<div>DB Action</div>
</a>
</li>
</ul>
{{-- <ul class="sidenav-menu">
@ -305,14 +510,16 @@
</ul>
--}}
</li>
{{--<li class="sidenav-item{{ Request::is('sysadmin/settings') ? ' active' : '' }}">
{{-- <li class="sidenav-item{{ Request::is('sysadmin/settings') ? ' active' : '' }}">
<a href="{{ route('sysadmin_settings') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-cog"></i><div>{{ __('navigation.system_settings') }}</div></a>
</li>--}}
</li> --}}
@endif
<li class="sidenav-divider mb-1"></li>
<li class="sidenav-item">
<a href="{{ route('logout') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-power"></i><div>{{ __('navigation.logout') }}</div></a>
<a href="{{ route('logout') }}" class="sidenav-link"><i class="sidenav-icon ion ion-ios-power"></i>
<div>{{ __('navigation.logout') }}</div>
</a>
</li>
</ul>
</div>

View file

@ -38,6 +38,18 @@
</script>
@endif
@if(Session::has('alert-warning'))
<script>
$( document ).ready(function() {
$.growl({
title: "{{ __('Hinweis') }}",
message: '{{ Session::get('alert-warning') }}',
location: 'tr'
});
});
</script>
@endif
<!-- Layout wrapper -->
<div class="layout-wrapper layout-2">
<div class="layout-inner">
@ -56,6 +68,7 @@
<!-- Content -->
<div class="container-fluid flex-grow-1 container-p-y">
@yield('content')
@yield('scripts')
</div>
<!-- / Content -->

View file

@ -0,0 +1,524 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>partner.gruene-seele.bio - Stornorechnung</title>
<style>
/* roboto-300 - latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
src: url('fonts/roboto-v20-latin-300.ttf') format('truetype'),
/* Safari, Android, iOS */
}
/* roboto-regular - latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url('fonts/roboto-v20-latin-regular.ttf') format('truetype'),
/* Safari, Android, iOS */
}
/* roboto-500 - latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
src: url('fonts/roboto-v20-latin-500.ttf') format('truetype'),
/* Safari, Android, iOS */
}
/* roboto-700 - latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: url('fonts/roboto-v20-latin-700.ttf') format('truetype'),
/* Safari, Android, iOS */
}
html {
width: 100%;
height: 100%;
}
body {
position: relative;
width: 100%;
height: 100%;
margin: 0 auto;
padding: 0;
color: #000;
background: #fff;
font-family: 'Roboto', sans-serif;
font-size: 8pt;
font-weight: 400;
}
table {
border: none;
}
strong {
font-weight: bold !important;
}
@page {
margin: 0px;
}
@page {
margin-top: 0px;
}
@page {
margin: 75mm 0 40mm 0;
}
.font-weight-bold {
font-weight: bold !important;
}
#address_box {
position: absolute;
top: -30mm;
left: 15mm;
width: 100mm;
height: 45mm;
z-index: 1;
font-size: 8pt;
line-height: 9pt;
letter-spacing: 0.05em;
}
#address_box_top {
font-size: 7pt;
color: #858585;
}
#title_box {
position: absolute;
top: 0mm;
left: 15mm;
width: 180mm;
height: 10mm;
z-index: 2;
text-align: center;
}
#title_box .title {
font-size: 15pt;
line-height: 12pt;
color: #d9534f;
}
#title_box .subtitle {
font-size: 9pt;
line-height: 9pt;
}
#detail_box_left {
position: absolute;
top: 8mm;
left: 15mm;
width: 90mm;
height: 15mm;
z-index: 3;
font-size: 8pt;
}
#detail_box_right {
position: absolute;
top: 8mm;
left: 105mm;
width: 90mm;
height: 15mm;
z-index: 4;
font-size: 8pt;
}
#detail_box_left table,
#detail_box_right table {
width: 100%;
line-height: 9pt;
}
#detail_box_left table td {
text-align: left;
}
#detail_box_right table td {
text-align: right;
}
#invoice_box {
position: relative;
padding-top: 20mm;
margin-left: 15mm;
width: 180mm;
font-size: 8pt;
line-height: 8pt;
}
#invoice_box table {
width: 100%;
border-collapse: collapse;
}
#invoice_box table tfoot tr {
page-break-after: avoid;
}
#invoice_box table tr td {
vertical-align: top;
padding-top: 2mm;
padding-bottom: 2mm;
padding-left: 1.5mm;
padding-right: 1.5mm;
border-top: 0.5pt dotted #1a1a18;
}
#invoice_box table tfoot tr td {
border-top: none;
}
#invoice_box table tfoot tr.fullline td {
border-top: 0.3pt solid #575755;
}
#invoice_box table tfoot tr.fullline td.no-border {
border-top: none;
}
#invoice_box table tfoot tr td {
padding-top: 1.2mm;
padding-bottom: 1.2mm;
}
#invoice_box table td.small {
width: 1%;
white-space: nowrap;
}
#invoice_box table tr th {
line-height: 12pt;
padding-bottom: 1mm;
padding-left: 1mm;
padding-right: 1mm;
background-color: rgb(212, 212, 212);
}
#invoice_box table td .title {
font-size: 10pt;
}
#invoice_box table td .description {
padding-top: 1mm;
font-size: 9pt;
}
#invoice_box table td .price_net {
padding-top: 1mm;
font-size: 10pt;
padding-bottom: 1mm;
}
#invoice_box table td .price_tax {
padding-top: 0.7mm;
padding-bottom: 0.7mm;
}
#invoice_box table td .price_total {
padding-top: 2mm;
font-size: 11pt;
}
.singel-line-top {
border-top: 1pt solid #1a1a18;
}
.double-line {
border-bottom: 2.5pt double #1a1a18;
}
.dotted-line {
border-bottom: 0.8pt dotted #1a1a18;
}
#footer_box {
position: relative;
top: 0mm;
left: 15mm;
width: 180mm;
height: 20mm;
z-index: 6;
font-size: 8pt;
line-height: 8pt;
}
.text-right {
text-align: right;
}
.text-left {
text-align: left;
}
.text-center {
text-align: center;
}
.text-red {
color: #d9534f;
}
</style>
</head>
<body>
<div id="address_box">
<div id="address_box_top">GRÜNE SEELE GmbH Hauptstr. 174 51143 Köln</div>
@if ($shopping_order->shopping_user->billing_company)
{{ $shopping_order->shopping_user->billing_company }}<br>
@else
@if ($shopping_order->payment_for !== 7)
Firma <br>
@endif
@endif
{{ \App\Services\HTMLHelper::getSalutationLang($shopping_order->shopping_user->billing_salutation) }}
{{ $shopping_order->shopping_user->billing_firstname }}
{{ $shopping_order->shopping_user->billing_lastname }}<br>
{{ $shopping_order->shopping_user->billing_address }}<br>
@if ($shopping_order->shopping_user->billing_address_2)
{{ $shopping_order->shopping_user->billing_address_2 }}<br>
@endif
{{ $shopping_order->shopping_user->billing_zipcode }} {{ $shopping_order->shopping_user->billing_city }}<br>
@if ($shopping_order->shopping_user->billing_country)
{{ $shopping_order->shopping_user->billing_country->getLocated() }}
@endif
</div>
<div id="title_box">
<div class="title">STORNORECHNUNG</div>
<div class="subtitle">zur Rechnung Nr. {{ $original_invoice_number }}</div>
</div>
<div id="detail_box_left">
<table>
<tr>
<td>
{{ __('Stornonummer') }}: {{ $cancellation_number }}
</td>
</tr>
<tr>
<td>
{{ __('Ursprüngliche Rechnung') }}: {{ $original_invoice_number }}
</td>
</tr>
<tr>
<td>
{{ __('Bestellnummer') }}: {{ $shopping_order->getLastShoppingPayment('reference') }}
</td>
</tr>
</table>
</div>
<div id="detail_box_right">
<table>
<tr>
<td>
{{ __('Stornodatum') }}: {{ $cancellation_date }}
</td>
</tr>
<tr>
<td>
{{ __('Urspr. Rechnungsdatum') }}: {{ $original_invoice_date }}
</td>
</tr>
<tr>
<td>
{{ __(' Umsatzsteuer-ID') }}: DE296797691
</td>
</tr>
</table>
</div>
<div id="invoice_box">
<p style="margin-bottom: 5mm; font-size: 9pt;">
<strong>Hiermit stornieren wir die Rechnung Nr. {{ $original_invoice_number }} vom
{{ $original_invoice_date }}.</strong><br>
Die folgenden Beträge werden mit negativem Vorzeichen ausgewiesen:
</p>
<table>
<thead>
<tr>
<th class="text-left" style="width: 50%">Bezeichnung</th>
<th class="text-center" style="width: 15%">Anzahl</th>
<th class="text-center" style="width: 20%">
@if ($shopping_order->shopping_user->is_from === 'user_order')
Netto-Preis
@else
Preis
@endif
</th>
<th class="text-right" style="width: 15%">Summe</th>
</tr>
</thead>
<tbody>
@foreach ($shopping_order->shopping_order_items as $shopping_order_item)
<tr class="item">
<td class="small text-left">
{{ $shopping_order_item->product->name }}
</td>
<td class="text-center small text-red">
-{{ $shopping_order_item->qty }}
</td>
<td class="small text-center text-red">
@if ($shopping_order->shopping_user->is_from === 'user_order')
-{{ $shopping_order_item->getFormattedPriceNet() }} &euro;
@else
-{{ $shopping_order_item->getFormattedPrice() }} &euro;
@endif
</td>
<td class="text-right small text-red">
@if ($shopping_order->shopping_user->is_from === 'user_order')
-{{ $shopping_order_item->getFormattedTotalPriceNet() }} &euro;
@else
-{{ $shopping_order_item->getFormattedTotalPrice() }} &euro;
@endif
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@if ($shopping_order->shopping_user->is_from === 'user_order')
<tr class="fullline">
<td class="text-right" colspan="3">
Zwischensumme
</td>
<td class="text-right text-red">
-{{ $shopping_order->getFormattedSubtotalFull() }} &euro;*
</td>
</tr>
@if ($shopping_order->shopping_user->is_for !== 'cr')
<tr class="">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
Handelspanne
</td>
<td class="text-right text-red">
{{ $shopping_order->getFormattedDiscount() }} &euro;*
</td>
</tr>
<tr class="">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
<strong>Summe (Netto)</strong>
</td>
<td class="text-right text-red">
<strong>-{{ $shopping_order->getFormattedSubtotal() }} &euro;*</strong>
</td>
</tr>
@endif
@endif
<tr class="fullline">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
{{ __('email.checkout_mail_shipping') }}
</td>
<td class="text-right text-red">
@if ($shopping_order->shopping_user->is_from === 'user_order')
-{{ $shopping_order->getFormattedShippingNet() }} &euro;*
@else
-{{ $shopping_order->getFormattedShipping() }} &euro;
@endif
</td>
</tr>
<tr class="fullline">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
{{ __('email.checkout_mail_subtotal_ws') }}
</td>
<td class="text-right text-red">
-{{ $shopping_order->getFormattedSubtotalShipping() }} &euro;*
</td>
</tr>
<tr class="">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
@if ($shopping_order->isTax())
{{ __('email.checkout_mail_tax_19') }}
@else
{{ __('email.checkout_mail_tax') }}
@endif
</td>
<td class="text-right text-red">
-{{ $shopping_order->getFormattedTax() }} &euro;
</td>
</tr>
@if ($shopping_order->shopping_user->is_from === 'user_order' && $shopping_order->payment_credit > 0)
<tr class="fullline">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
<b> Summe (Brutto):</b>
</td>
<td class="text-right text-red">
<b> -{{ $shopping_order->getFormattedTotalWithoutCredit() }} &euro;</b>
</td>
</tr>
<tr class="border-bottom">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
aus Guthaben (Brutto):
</td>
<td class="text-right text-red">
-{{ $shopping_order->getFormattedPaymentCredit() }} &euro;
</td>
</tr>
@endif
<tr class="fullline">
<td class="text-right no-border">&nbsp;</td>
<td class="text-right" colspan="2">
<b>Stornobetrag (Brutto):</b>
</td>
<td class="text-right text-red">
<b>-{{ $shopping_order->getFormattedTotalShipping() }} &euro;</b>
<br>
<span style="font-size: 0.9em"><em>* Nettobeträge</em></span>
</td>
</tr>
</tfoot>
</table>
<div style="margin-top: 10mm; font-size: 9pt;">
<p>
<strong>Hinweis:</strong> Mit dieser Stornorechnung wird die ursprüngliche Rechnung Nr.
{{ $original_invoice_number }} vollständig storniert.
Der Rechnungsbetrag von {{ $shopping_order->getFormattedTotalShipping() }} wird Ihnen gutgeschrieben
bzw. erstattet.
</p>
</div>
</div>
<div id="footer_box">
<p style="font-size: 7pt; text-align: center; margin-top: 5mm;">
GRÜNE SEELE GmbH Hauptstr. 174 51143 Köln<br>
Tel: +49 (0) 2203 / 29 57 57 0 E-Mail: info@gruene-seele.bio Web: www.gruene-seele.bio
</p>
</div>
</body>
</html>