Abo Einmalprodukte und Bestätigung abschließen

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin 2026-06-05 15:28:08 +00:00
parent 2bdc9ada3c
commit 2269ce031f
57 changed files with 3647 additions and 371 deletions

View file

@ -0,0 +1,56 @@
<div class="modal fade" id="modal-confirm-add" tabindex="-1" role="dialog" aria-labelledby="modal-confirm-add-label"
aria-hidden="true" data-title-add="{{ __('abo.confirm_add_title_normal') }}"
data-title-increase="{{ __('abo.confirm_increase_title') }}"
data-info-add="{{ __('abo.confirm_add_info') }}"
data-info-increase="{{ __('abo.confirm_increase_info') }}"
data-per-delivery="{{ __('abo.confirm_per_delivery') }}"
data-current-total="{{ $user_abo->getFormattedAmount() }}"
data-next-billing-date="{{ $user_abo->next_date ?: __('abo.confirm_next_delivery_unknown') }}">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-confirm-add-label"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="alert alert-info mb-3">
<span id="confirm-add-info-text"></span>
</div>
<div class="font-weight-bold mb-2">{{ __('abo.confirm_article_details') }}</div>
<table class="table table-sm mb-0">
<tr>
<td class="font-weight-bold">{{ __('order.article') }}:</td>
<td id="confirm-add-product-name"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('tables.quantity') }}:</td>
<td id="confirm-add-qty-info"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('abo.confirm_additional_costs') }}:</td>
<td id="confirm-add-product-price"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('abo.confirm_new_total') }}:</td>
<td id="confirm-add-new-total"></td>
</tr>
</table>
<p class="small text-muted mb-0 mt-3">
{!! __('abo.confirm_legal_notice', [
'nextBillingDate' => '<span id="confirm-add-next-billing-date"></span>',
'agb' => '<a href="'.url('/agb').'" target="_blank">'.__('abo.confirm_terms_link').'</a>',
'withdrawal' => '<a href="'.asset('download/mivita_widerruf_formular.pdf').'" target="_blank">'.__('abo.confirm_withdrawal_link').'</a>',
]) !!}
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">{{ __('abo.confirm_add_cancel') }}</button>
<button type="button" class="btn btn-primary"
id="confirm-add-btn">{{ __('abo.confirm_add_ok') }}</button>
</div>
</div>
</div>
</div>

View file

@ -134,8 +134,10 @@
@php
$only_show_products = isset($only_show_products) ? $only_show_products : false;
$add_only_mode = isset($add_only_mode) ? $add_only_mode : false;
$split_mode = isset($split_mode) ? $split_mode : false;
$summary = isset($summary) ? $summary : null;
@endphp
@include('admin.abo._order_abo_show', ['only_show_products' => $only_show_products, 'add_only_mode' => $add_only_mode])
@include('admin.abo._order_abo_show', ['only_show_products' => $only_show_products, 'add_only_mode' => $add_only_mode, 'split_mode' => $split_mode, 'summary' => $summary])
</div>
</div>

View file

@ -1,3 +1,4 @@
@php($cartInstance = \App\Services\AboOrderCart::INSTANCE)
@if(isset($error_message) && $error_message)
<div class="alert alert-danger mt-2" id="insert_show_error_message">{{ $error_message }}</div>
@endif
@ -65,37 +66,49 @@
<tr>
<td colspan="4"><hr></td>
</tr>
@if(isset($split_mode) && $split_mode)
<tr>
<td colspan="3" class="text-right small"><strong>{{ __('abo.abo_subtotal') }}:</strong></td>
<td class="text-right small">{{ formatNumber($summary['abo']['gross'] ?? 0) }} </td>
</tr>
<tr>
<td colspan="3" class="text-right small no-border-top"><strong>{{ __('abo.abo_next_delivery_total') }}:</strong></td>
<td class="text-right small no-border-top">{{ formatNumber($summary['total_with_shipping'] ?? 0) }} </td>
</tr>
@else
@php($taxFree = Yard::instance($cartInstance)->getUserTaxFree())
<tr>
<td colspan="3" class="text-right small"><strong>{{ __('order.subtotal') }}:</strong></td>
<td class="text-right small">{{ Yard::instance('shopping')->subtotal() }} </td>
<td class="text-right small">{{ Yard::instance($cartInstance)->subtotal() }} </td>
</tr>
<tr>
<td colspan="3" class="text-right small no-border-top"><strong>{{ __('Delivery country') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance('shopping')->getShippingCountryName() }}</td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->getShippingCountryName() }}</td>
</tr>
<tr>
<td colspan="3" class="text-right small no-border-top"><strong>{{ __('order.shipping_costs') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance('shopping')->shipping() }} </td>
<td class="text-right small no-border-top">{{ $taxFree ? Yard::instance($cartInstance)->shippingNet() : Yard::instance($cartInstance)->shipping() }} </td>
</tr>
@if(Yard::instance('shopping')->getUserTaxFree())
@if($taxFree)
<tr>
<td colspan="3" class="text-right small no-border-top"><strong>{{ __('order.sum_net') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance('shopping')->subtotalWithShipping() }} </td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->subtotalWithShipping() }} </td>
</tr>
@else
<tr>
<td colspan="3" class="text-right small no-border-top"><strong>{{ __('order.total_without_VAT') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance('shopping')->subtotalWithShipping() }} </td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->subtotalWithShipping() }} </td>
</tr>
<tr>
<td colspan="3" class="text-right small no-border-top"><strong>{{ __('order.plus_VAT') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance('shopping')->taxWithShipping() }} </td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->taxWithShipping() }} </td>
</tr>
@endif
<tr>
<td colspan="3" class="text-right"><strong>{{ __('order.total_sum') }}:</strong></td>
<td class="text-right">{{ Yard::instance('shopping')->totalWithShipping() }} </td>
</tr>
<tr>
<td colspan="3" class="text-right"><strong>{{ __('order.total_sum') }}:</strong></td>
<td class="text-right">{{ Yard::instance($cartInstance)->totalWithShipping() }} </td>
</tr>
@endif
</tfoot>
</table>
</div>

View file

@ -0,0 +1,50 @@
@php
$cartInstance = \App\Services\AboOrderCart::INSTANCE;
$taxFree = Yard::instance($cartInstance)->getUserTaxFree();
@endphp
<div class="card-header bg-primary text-white">
<h5 class="font-weight-semibold mb-0">{{ __('abo.combined_summary_hl') }}</h5>
</div>
<div class="card-body bg-light">
<table class="table table-product m-0">
<tbody>
@if(($summary['one_time']['gross'] ?? 0) > 0)
<tr>
<td class="small"><strong>{{ __('abo.onetime_subtotal') }}:</strong></td>
<td class="text-right small">{{ formatNumber($summary['one_time']['gross']) }} </td>
</tr>
@endif
<tr>
<td class="small"><strong>{{ __('abo.abo_subtotal') }}:</strong></td>
<td class="text-right small">{{ formatNumber($summary['abo']['gross'] ?? 0) }} </td>
</tr>
<tr>
<td class="small no-border-top"><strong>{{ __('Delivery country') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->getShippingCountryName() }}</td>
</tr>
<tr>
<td class="small no-border-top"><strong>{{ __('order.shipping_costs') }}:</strong></td>
<td class="text-right small no-border-top">{{ $taxFree ? Yard::instance($cartInstance)->shippingNet() : Yard::instance($cartInstance)->shipping() }} </td>
</tr>
@if($taxFree)
<tr>
<td class="small no-border-top"><strong>{{ __('order.sum_net') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->subtotalWithShipping() }} </td>
</tr>
@else
<tr>
<td class="small no-border-top"><strong>{{ __('order.total_without_VAT') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->subtotalWithShipping() }} </td>
</tr>
<tr>
<td class="small no-border-top"><strong>{{ __('order.plus_VAT') }}:</strong></td>
<td class="text-right small no-border-top">{{ Yard::instance($cartInstance)->taxWithShipping() }} </td>
</tr>
@endif
<tr class="bg-white">
<td class="pt-2 pb-2"><strong>{{ __('order.total_sum') }}:</strong></td>
<td class="text-right font-weight-bold pt-2 pb-2">{{ Yard::instance($cartInstance)->totalWithShipping() }} </td>
</tr>
</tbody>
</table>
</div>

View file

@ -0,0 +1,23 @@
<div class="card-body">
<h5 class="font-weight-semibold mb-2">
<i class="fa fa-bolt text-warning"></i> {{ __('abo.onetime_card_hl') }}
</h5>
<div class="alert alert-info">{!! __('abo.onetime_card_info') !!}</div>
<div class="alert alert-warning small mb-3">
<strong>{{ __('abo.onetime_important_hl') }}</strong>
<ul class="mb-0 pl-3">
<li>{{ __('abo.onetime_reset_hint') }}</li>
<li>{{ __('abo.onetime_coverage_hint') }}</li>
</ul>
</div>
<button type="button" class="btn btn-sm btn-warning btn-block mt-2"
data-toggle="modal" data-target="#modals-load-content"
data-id="{{ $user_abo->id }}"
data-action="abo-add-onetime"
data-route="{{ route('modal_load') }}"><i class="fa fa-plus-circle"></i> {{ __('abo.add_onetime_product') }}</button>
<div id="insert_show_onetime_order" data-onetime-abo-id="{{ $user_abo->id }}" class="mt-2">
@include('admin.abo._order_onetime_show', ['user_abo' => $user_abo, 'summary' => $summary])
</div>
</div>

View file

@ -0,0 +1,105 @@
@php
$one_time_items = $user_abo->one_time_items()->with('product')->get();
$hasOneTimeChanges = \App\Services\AboOneTimeService::hasUnconfirmedChanges($user_abo);
$hasConfirmedOneTimeItems = \App\Services\AboOneTimeService::hasConfirmedItems($user_abo);
$oneTimeGross = $summary['one_time']['gross'] ?? 0;
$nextDeliveryTotal = $summary['total_with_shipping'] ?? 0;
$oneTimeConfirmationState = $hasOneTimeChanges ? 'changed' : ($hasConfirmedOneTimeItems ? 'confirmed' : 'empty');
@endphp
@if(isset($error_message) && $error_message)
<div class="alert alert-danger mt-2" id="insert_onetime_error_message">{{ $error_message }}</div>
@endif
<div class="table-responsive">
<table class="table table-product m-0" style="min-width:550px;">
<thead>
<tr>
<th>#</th>
<th>{{ __('order.article') }}</th>
<th>{{ __('tables.quantity') }}</th>
<th class="text-right">{{ __('tables.price') }}</th>
</tr>
</thead>
<tbody>
@forelse($one_time_items as $one_time_item)
<tr>
<td>
@if($one_time_item->product && count($one_time_item->product->images))
<img class="img-fluid img-extra" alt="" src="{{ route('product_image', [$one_time_item->product->images->first()->slug]) }}">
@endif
</td>
<td class="min-width-80">
<strong>{{ $one_time_item->product?->getLang('name') }}</strong>
<span class="badge badge-pill badge-warning"><i class="fa fa-bolt"></i> {{ __('abo.onetime_badge') }}</span>
<div class="text-body">
<div>{{ __('order.content') }}: {{ $one_time_item->product?->contents }}</div>
<div>{{ __('order.art_no') }}: {{ $one_time_item->product?->number }}</div>
</div>
<div class="options">
<a href="#" class="auto-delete-product remove_onetime_from_cart product-tooltip" data-onetime-item-id="{{ $one_time_item->id }}"><i class="fa fa-times"></i> {{ __('order.article_remove') }}</a>
</div>
</td>
<td>
<div class="no-line-break input-group-min-w">
<div class="input-group d-inline-flex w-auto">
<span class="input-group-prepend">
<button type="button" class="btn btn-secondary icon-btn md-btn-extra onetime-remove-from-basket" data-onetime-item-id="{{ $one_time_item->id }}">-</button>
</span>
<input type="text" class="form-control text-center input-extra onetime-input-onchange" name="onetime_qty_{{ $one_time_item->id }}" data-onetime-item-id="{{ $one_time_item->id }}" value="{{ $one_time_item->qty }}">
<span class="input-group-append">
<button type="button" class="btn btn-secondary icon-btn md-btn-extra onetime-add-from-basket" data-onetime-item-id="{{ $one_time_item->id }}">+</button>
</span>
</div>
</div>
</td>
<td class="text-right font-weight-semibold align-top px-3 py-2" style="width: 100px;">
<div class="no-line-break">{{ $one_time_item->getFormattedTotalPrice() }} &euro;</div>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="text-muted small py-3 text-center">{{ __('abo.onetime_empty') }}</td>
</tr>
@endforelse
</tbody>
<tfoot>
<tr>
<td colspan="4"><hr></td>
</tr>
<tr>
<td colspan="3" class="text-right small"><strong>{{ __('abo.onetime_subtotal') }}:</strong></td>
<td class="text-right small">{{ formatNumber($summary['one_time']['gross'] ?? 0) }} </td>
</tr>
@if($oneTimeGross > 0 || $hasOneTimeChanges || $hasConfirmedOneTimeItems)
<tr>
<td colspan="3" class="text-right small no-border-top"><strong>{{ __('abo.onetime_next_delivery_total') }}:</strong></td>
<td class="text-right small no-border-top">{{ formatNumber($nextDeliveryTotal) }} </td>
</tr>
@endif
</tfoot>
</table>
</div>
@if($oneTimeGross > 0 || $hasOneTimeChanges || $hasConfirmedOneTimeItems)
<div class="mt-3" id="onetime_confirmation_block" data-confirmation-state="{{ $oneTimeConfirmationState }}" aria-live="polite">
<p class="small text-muted mb-2">
{!! __('abo.onetime_legal_notice', [
'nextBillingDate' => $user_abo->next_date ?: __('abo.confirm_next_delivery_unknown'),
'agb' => '<a href="'.url('/agb').'" target="_blank">'.__('abo.confirm_terms_link').'</a>',
'withdrawal' => '<a href="'.asset('download/mivita_widerruf_formular.pdf').'" target="_blank">'.__('abo.confirm_withdrawal_link').'</a>',
]) !!}
</p>
@if($hasOneTimeChanges)
<div class="d-flex flex-wrap justify-content-end">
<button type="button" class="btn btn-default mr-2 mb-2 onetime-discard-changes">
{{ __('abo.onetime_discard_changes') }}
</button>
<button type="button" class="btn btn-primary mb-2 onetime-confirm-changes">
{{ __('abo.onetime_confirm_paid') }}
</button>
</div>
@elseif($hasConfirmedOneTimeItems)
<div class="alert alert-success small mb-0">
<span aria-hidden="true">&check;</span> {{ __('abo.onetime_confirm_success') }}
</div>
@endif
</div>
@endif

View file

@ -51,13 +51,14 @@
'data-add-only-mode' => '0',
]) !!}
<input type="hidden" name="is_for" value="{{ $user_abo->is_for }}">
@php($cartInstance = \App\Services\AboOrderCart::INSTANCE)
<div class="card mt-3">
@include('admin.abo._order_abo', ['add_only_mode' => false])
</div>
@if ($comp_products && Yard::instance('shopping')->getNumComp() > 0)
@if ($comp_products && Yard::instance($cartInstance)->getNumComp() > 0)
<div id="holder_html_view_comp_product">
@include('user.order.comp_product')
@include('user.order.comp_product', ['cart_instance' => $cartInstance])
</div>
@endif
@ -78,47 +79,7 @@
<a href="{{ route('admin_abos') }}" class="btn btn-sm btn-default mt-2 float-right">{{ __('back') }}</a>
<div class="modal fade" id="modal-confirm-add" tabindex="-1" role="dialog" aria-labelledby="modal-confirm-add-label"
aria-hidden="true" data-title-add-only="{{ __('abo.confirm_add_title') }}"
data-title-normal="{{ __('abo.confirm_add_title_normal') }}"
data-warning-add-only="{{ __('abo.confirm_add_warning') }}"
data-warning-normal="{{ __('abo.confirm_add_warning_normal') }}">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-confirm-add-label"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="alert alert-warning mb-3">
<i class="fa fa-exclamation-triangle"></i> <span id="confirm-add-warning-text"></span>
</div>
<table class="table table-sm mb-0">
<tr>
<td class="font-weight-bold">{{ __('order.article') }}:</td>
<td id="confirm-add-product-name"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('tables.price') }}:</td>
<td id="confirm-add-product-price"></td>
</tr>
<tr>
<td class="font-weight-bold">{{ __('tables.quantity') }}:</td>
<td id="confirm-add-qty-info"></td>
</tr>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default"
data-dismiss="modal">{{ __('abo.confirm_add_cancel') }}</button>
<button type="button" class="btn btn-primary"
id="confirm-add-btn">{{ __('abo.confirm_add_ok') }}</button>
</div>
</div>
</div>
</div>
@include('admin.abo._confirm_add_modal')
@if ($user_abo->status === 3 && $user_abo->active)
<div class="modal fade" id="modal-retry-abo-payment" tabindex="-1" role="dialog" aria-labelledby="modal-retry-abo-payment-label" aria-hidden="true">