commit 08-2025

This commit is contained in:
Kevin Adametz 2025-08-12 15:51:04 +02:00
parent 9b54eb0512
commit 02f2a4c23e
184 changed files with 31653 additions and 22327 deletions

18
.env
View file

@ -12,12 +12,12 @@ APP_PROMO_DOMAIN=testemich.test
APP_SHOP_URL=https://grueneseele.test
APP_SHOP_DOMAIN=grueneseele.test
APP_CHECKOUT_MAIL=register@adametz.media
APP_CHECKOUT_MAIL=kevin.adametz@me.com
APP_CHECKOUT_TEST_MAIL=register@adametz.media
APP_INFO_MAIL=register@adametz.media
APP_INFO_MAIL=kevin.adametz@me.com
APP_INFO_TEST_MAIL=register@adametz.media
EXCEPTION_MAIL=exception@adametz.media
LOGISTIC_MAIL=kevin.adametz@me.com
APP_MAIN_TAX = 1.19
APP_MAIN_TAX_RATE = 19
@ -54,11 +54,13 @@ REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=w017f6e4.kasserver.com
MAIL_HOST=w017e534.kasserver.com
MAIL_PORT=587
MAIL_USERNAME=m04a9fbc
MAIL_PASSWORD=3tQ72oCHZgncCTpK
MAIL_USERNAME=m0496c96
MAIL_PASSWORD=mZtVp7WQcs6DC3hf
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=dev@adametz.media
MAIL_FROM_NAME="DEV Grüne Seele"
#MAIL_MAILER=smtp
#MAIL_HOST=s182.goserver.host
@ -66,8 +68,8 @@ MAIL_ENCRYPTION=null
#MAIL_USERNAME=web28p3
#MAIL_PASSWORD=WeE2bmI9GjB7pDgi
#MAIL_ENCRYPTION=""
MAIL_FROM_ADDRESS=partner@gruene-seele.bio
MAIL_FROM_NAME="GRÜNE SEELE Naturkosmetik"
#MAIL_FROM_ADDRESS=partner@gruene-seele.bio
#MAIL_FROM_NAME="GRÜNE SEELE Naturkosmetik"
RECAPTCHA_SITE_KEY="6LcGr_kqAAAAAOnz-L6IIBC_fTzJ7siTheZgFVMY"
RECAPTCHA_SECRET_KEY="6LcGr_kqAAAAAKBZVoy37ski0Gl54jenWOlrbc9z"

File diff suppressed because it is too large Load diff

249
PAYMENT_REMINDER_CRON.md Normal file
View file

@ -0,0 +1,249 @@
# Payment Reminder Cron-Job Einrichtung
## Übersicht
Der `PaymentsReminders` Command automatisiert das Senden von Zahlungserinnerungen basierend auf den konfigurierten Intervallen in der Datenbank.
## Command ausführen
```bash
php artisan payments:reminders
```
## Cron-Job Konfiguration
### 1. Crontab öffnen
```bash
crontab -e
```
### 2. Cron-Job hinzufügen
**Täglich um 9:00 Uhr:**
```bash
0 9 * * * cd /path/to/your/project && php artisan payments:reminders >> /dev/null 2>&1
```
**Stündlich:**
```bash
0 * * * * cd /path/to/your/project && php artisan payments:reminders >> /dev/null 2>&1
```
**Alle 6 Stunden:**
```bash
0 */6 * * * cd /path/to/your/project && php artisan payments:reminders >> /dev/null 2>&1
```
### 3. Cron-Job testen
```bash
# Teste den Command manuell
php artisan payments:reminders
# Prüfe die Logs
tail -f storage/logs/laravel.log
```
## Funktionsweise
### 1. Intervall-basierte Verarbeitung
- Der Command holt alle aktiven `PaymentReminder` aus der Datenbank
- Gruppiert sie nach `clearingtype` (z.B. 'invoice', 'prepayment')
- Verwendet das kleinste Intervall pro `clearingtype`
### 2. Zahlungsprüfung
- Sucht offene Zahlungen, die älter als das konfigurierte Intervall sind
- Berücksichtigt nur die neueste Zahlung pro Bestellung
- Prüft nur Live-Zahlungen (nicht Test)
### 3. Erinnerungslogik
- **Erste Erinnerung**: Nach X Tagen ab Bestelldatum
- **Weitere Erinnerungen**: Nach Y Tagen ab letzter Erinnerung
- Stoppt wenn alle konfigurierten Erinnerungen gesendet wurden
### 4. Automatische Aktionen
- E-Mail-Versand mit Platzhalter-Ersetzung
- Optional: Bestellung auf "Storniert" setzen
- Optional: Payment auf "non" Status setzen
- Logging aller Aktivitäten
## Logging
### Command-Logs
```bash
# Live-Logs während der Ausführung
php artisan payments:reminders
# Beispiel-Output:
RUN Command Payments Reminders: 15.12.2024 09:00
=== STARTE PAYMENT REMINDERS ===
Gefundene aktive PaymentReminder: 3
Gefundene clearingtypes mit kleinsten Intervallen:
- invoice: 7 Tage
- prepayment: 3 Tage
--- Verarbeite clearingtype: invoice mit Intervall: 7 Tage ---
Suche Zahlungen vor: 08.12.2024 09:00:00
Gefundene offene Zahlungen für invoice: 5
Verarbeite Order ID: 12345, Created: 05.12.2024 10:30:00, Amount: 5000, Reminder: 0
📧 Sende Erinnerung...
✅ Erinnerung erfolgreich gesendet
=== PAYMENT REMINDERS ABGESCHLOSSEN ===
Ausführungszeit: 2.34 Sekunden
Statistiken:
- Gesamt verarbeitet: 5
- Erinnerungen gesendet: 3
- Fehler: 0
- Übersprungen: 2
```
### Laravel-Logs
```bash
# Logs in storage/logs/laravel.log
tail -f storage/logs/laravel.log | grep "Payment reminder"
```
## Konfiguration
### PaymentReminder Einstellungen
```sql
-- Beispiel-Konfiguration
INSERT INTO payment_reminders (clearingtype, interval, subject, message, action, active) VALUES
('invoice', 7, 'Zahlungserinnerung - Bestellung {order_number}', 'Sehr geehrte/r {billing_first_name}...', NULL, 1),
('invoice', 14, '2. Zahlungserinnerung - Bestellung {order_number}', 'Sehr geehrte/r {billing_first_name}...', NULL, 1),
('invoice', 21, 'Letzte Zahlungserinnerung - Bestellung {order_number}', 'Sehr geehrte/r {billing_first_name}...', 'set_order_status_cancelled', 1);
```
### Platzhalter
- `{billing_first_name}` - Vorname
- `{billing_last_name}` - Nachname
- `{order_number}` - Bestellnummer
- `{order_date}` - Bestelldatum
- `{order_total}` - Bestellsumme
## Monitoring
### 1. Log-Statistiken
- Admin-Bereich: `/admin/payments/reminder/logs`
- Zeigt Statistiken der letzten 7, 30 und 90 Tage
- Filter nach Order ID, Aktion und Datum
### 2. Erfolgsrate
- E-Mails gesendet vs. Fehler
- Aktionen ausgeführt
- Übersprungene Erinnerungen
### 3. Performance
- Ausführungszeit pro Lauf
- Anzahl verarbeiteter Zahlungen
- Speicherverbrauch
## Troubleshooting
### Häufige Probleme
**1. Command läuft nicht**
```bash
# Prüfe PHP-Pfad
which php
# Prüfe Projekt-Pfad
pwd
# Teste Command manuell
php artisan payments:reminders
```
**2. Keine E-Mails werden gesendet**
```bash
# Prüfe Mail-Konfiguration
php artisan config:cache
# Prüfe Logs
tail -f storage/logs/laravel.log
```
**3. Falsche Intervalle**
```bash
# Prüfe PaymentReminder-Konfiguration
php artisan tinker
>>> App\Models\PaymentReminder::where('active', true)->get()
```
**4. Cron-Job läuft nicht**
```bash
# Prüfe Crontab
crontab -l
# Prüfe Cron-Logs
sudo tail -f /var/log/cron
# Teste mit absoluten Pfaden
0 9 * * * /usr/bin/php /path/to/project/artisan payments:reminders
```
## Sicherheit
### 1. Berechtigungen
```bash
# Stelle sicher, dass der Webserver-Benutzer Schreibrechte hat
chown -R www-data:www-data storage/logs
chmod -R 755 storage/logs
```
### 2. Log-Rotation
```bash
# Konfiguriere Log-Rotation in /etc/logrotate.d/laravel
/path/to/project/storage/logs/*.log {
daily
missingok
rotate 52
compress
notifempty
create 644 www-data www-data
}
```
### 3. Backup
```bash
# Backup der PaymentReminder-Konfiguration
mysqldump -u username -p database payment_reminders > payment_reminders_backup.sql
```
## Performance-Optimierung
### 1. Batch-Verarbeitung
- Der Command verarbeitet Zahlungen in Batches
- Verwendet Datenbank-Indizes für bessere Performance
### 2. Memory-Management
- Garbage Collection nach jeder Zahlung
- Begrenzte Anzahl von Logs
### 3. Timeout-Handling
- Lange Ausführungen werden abgebrochen
- Fehler werden geloggt und übersprungen

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,252 @@
<?php
namespace App\Console\Commands;
use App\Models\PaymentReminder;
use App\Models\ShoppingPayment;
use App\Services\PaymentReminderService;
use Carbon\Carbon;
use Illuminate\Console\Command;
/**
* Command für automatische Zahlungserinnerungen
*
* Dieser Command wird als Cron-Job ausgeführt und sendet automatisch
* Zahlungserinnerungen basierend auf den konfigurierten Intervallen.
*
* Verwendung:
* php artisan payments:reminders
*
* Cron-Job Konfiguration (täglich um 9:00 Uhr):
* 0 9 * * * cd /path/to/project && php artisan payments:reminders >> /dev/null 2>&1
*
* Oder für stündliche Ausführung:
* 0 * * * * cd /path/to/project && php artisan payments:reminders >> /dev/null 2>&1
*
* Logs werden automatisch in storage/logs/laravel.log geschrieben
*/
class PaymentsReminders extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'payments:reminders';
protected $description = 'Run Payments Reminders';
private $timeStart;
private $dev = false;
private $paymentReminderService;
private $stats = [
'total_processed' => 0,
'reminders_sent' => 0,
'errors' => 0,
'skipped' => 0
];
public function __construct(PaymentReminderService $paymentReminderService)
{
parent::__construct();
$this->paymentReminderService = $paymentReminderService;
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
\Log::info('Starting PaymentsReminders Command', ['timestamp' => now()]);
$this->info('RUN Command Payments Reminders: '.date('d.m.Y H:i'));
$this->timeStart = microtime(true);
try {
$this->functionReminder();
$executionTime = round(microtime(true) - $this->timeStart, 2);
$this->info("\n=== PAYMENT REMINDERS ABGESCHLOSSEN ===");
$this->info("Ausführungszeit: {$executionTime} Sekunden");
$this->info("Statistiken:");
$this->info(" - Gesamt verarbeitet: {$this->stats['total_processed']}");
$this->info(" - Erinnerungen gesendet: {$this->stats['reminders_sent']}");
$this->info(" - Fehler: {$this->stats['errors']}");
$this->info(" - Übersprungen: {$this->stats['skipped']}");
\Log::info('PaymentsReminders Command completed successfully', [
'execution_time' => $executionTime,
'stats' => $this->stats
]);
return 0;
} catch (\Exception $e) {
\Log::error('PaymentsReminders Command failed', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
$this->error('Command failed: ' . $e->getMessage());
return 1;
}
}
/**
* Hauptfunktion für die Verarbeitung der Zahlungserinnerungen
*/
private function functionReminder()
{
$this->info('=== STARTE PAYMENT REMINDERS ===');
// Hole alle aktiven PaymentReminder und gruppiere sie nach clearingtype
$payment_reminders = PaymentReminder::where('active', true)->get();
$this->info("Gefundene aktive PaymentReminder: " . $payment_reminders->count());
if ($payment_reminders->isEmpty()) {
$this->warn("Keine aktiven PaymentReminder gefunden!");
return;
}
// Finde für jeden clearingtype das kleinste Intervall (in Tagen)
$intervals = $this->paymentReminderService->getActiveIntervals();
$this->info("Gefundene clearingtypes mit kleinsten Intervallen:");
foreach($intervals as $clearingtype => $interval){
$this->line(" - {$clearingtype}: {$interval} Tage");
}
// Verarbeite jeden clearingtype mit seinem kleinsten Intervall
foreach($intervals as $clearingtype => $interval){
$this->info("\n--- Verarbeite clearingtype: {$clearingtype} mit Intervall: {$interval} Tage ---");
$date = Carbon::now()->subDays($interval);
$this->line("Suche Zahlungen vor: " . $date->format('d.m.Y H:i:s'));
// Hole nur die neueste ShoppingPayment pro shopping_order_id
$shopping_payments = $this->paymentReminderService->getOpenPaymentsForClearingType($clearingtype, $interval);
$this->info("Gefundene offene Zahlungen für {$clearingtype}: " . $shopping_payments->count());
if ($shopping_payments->isEmpty()) {
$this->line(" Keine Zahlungen für {$clearingtype} gefunden.");
continue;
}
// Verarbeite jede Zahlung
foreach($shopping_payments as $shopping_payment){
$this->processPayment($shopping_payment, $clearingtype);
}
}
}
/**
* Verarbeitet eine einzelne Zahlung und sendet ggf. eine Erinnerung
*/
private function processPayment($shopping_payment, $clearingtype)
{
$this->stats['total_processed']++;
try {
$this->line(" Verarbeite Order ID: {$shopping_payment->shopping_order_id}, Created: {$shopping_payment->created_at->format('d.m.Y H:i:s')}, Amount: {$shopping_payment->amount}, Reminder: {$shopping_payment->reminder}");
// Prüfe ob eine Erinnerung gesendet werden soll
if ($this->shouldSendReminder($shopping_payment, $clearingtype)) {
$this->sendReminderForPayment($shopping_payment);
} else {
$this->line(" ⏭️ Übersprungen - Keine Erinnerung fällig");
$this->stats['skipped']++;
}
} catch (\Exception $e) {
$this->error(" ❌ Fehler bei Order ID {$shopping_payment->shopping_order_id}: " . $e->getMessage());
$this->stats['errors']++;
\Log::error('Error processing payment reminder', [
'order_id' => $shopping_payment->shopping_order_id,
'payment_id' => $shopping_payment->id,
'error' => $e->getMessage()
]);
}
}
/**
* Prüft ob eine Erinnerung für diese Zahlung gesendet werden soll
*
* Logik:
* - Erste Erinnerung: Nach X Tagen ab Bestelldatum
* - Weitere Erinnerungen: Nach Y Tagen ab letzter Erinnerung
*/
private function shouldSendReminder($shopping_payment, $clearingtype)
{
// Hole alle aktiven Erinnerungen für diesen Clearingtype
$payment_reminders = PaymentReminder::where('active', true)
->where('clearingtype', $clearingtype)
->orderBy('interval', 'asc')
->get();
if ($payment_reminders->isEmpty()) {
return false;
}
// Wenn alle Erinnerungen bereits gesendet wurden
if ($shopping_payment->reminder >= $payment_reminders->count()) {
return false;
}
// Hole die nächste Erinnerung
$next_reminder = $payment_reminders[$shopping_payment->reminder];
// Wenn noch keine Erinnerung gesendet wurde, prüfe das erste Intervall
if ($shopping_payment->reminder == 0) {
$daysSinceOrder = $shopping_payment->created_at->diffInDays(now());
return $daysSinceOrder >= $next_reminder->interval;
}
// Wenn bereits Erinnerungen gesendet wurden, prüfe das nächste Intervall
if ($shopping_payment->reminder_date) {
$current_reminder = $payment_reminders[$shopping_payment->reminder - 1];
$interval_difference = $next_reminder->interval - $current_reminder->interval;
$next_reminder_date = Carbon::parse($shopping_payment->reminder_date)->addDays($interval_difference);
return now()->gte($next_reminder_date);
}
return false;
}
/**
* Sendet eine Erinnerung für eine spezifische Zahlung
*/
private function sendReminderForPayment($shopping_payment)
{
try {
$this->line(" 📧 Sende Erinnerung...");
$result = $this->paymentReminderService->sendReminder($shopping_payment);
if ($result) {
$this->line(" ✅ Erinnerung erfolgreich gesendet");
$this->stats['reminders_sent']++;
// Log für Cron-Job
\Log::info('Payment reminder sent via cron', [
'order_id' => $shopping_payment->shopping_order_id,
'payment_id' => $shopping_payment->id,
'reminder_count' => $shopping_payment->reminder + 1,
'email' => $shopping_payment->shopping_order->shopping_user->billing_email ?? 'N/A'
]);
} else {
$this->line(" ⚠️ Keine Erinnerung gesendet (keine weitere Erinnerung verfügbar)");
$this->stats['skipped']++;
}
} catch (\Exception $e) {
$this->error(" ❌ Fehler beim Senden der Erinnerung: " . $e->getMessage());
$this->stats['errors']++;
\Log::error('Error sending payment reminder', [
'order_id' => $shopping_payment->shopping_order_id,
'payment_id' => $shopping_payment->id,
'error' => $e->getMessage()
]);
}
}
}

View file

@ -24,13 +24,22 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('payments:accounts')
/*$schedule->command('payments:accounts')
->sendOutputTo(storage_path('logs/cron.log'))
->appendOutputTo(storage_path('logs/cron-history.log'))
->emailOutputOnFailure(config('app.exception_mail'))
->onFailure(function () {
\Log::error('Payments:accounts command failed');
});
*/
$schedule->command('payments:reminders')
->sendOutputTo(storage_path('logs/cron.log'))
->appendOutputTo(storage_path('logs/cron-reminders.log'))
->emailOutputOnFailure(config('app.exception_mail'))
->onFailure(function () {
\Log::error('Payments:reminders command failed');
});
}
/**

View file

@ -4,6 +4,9 @@ namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
use Illuminate\Support\Facades\Mail;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
class Handler extends ExceptionHandler
{
@ -36,6 +39,9 @@ class Handler extends ExceptionHandler
*/
public function report(Throwable $exception)
{
if ($this->shouldReport($exception)) {
$this->sendEmail($exception);
}
parent::report($exception);
}
@ -52,4 +58,35 @@ class Handler extends ExceptionHandler
{
return parent::render($request, $exception);
}
public function sendEmail(Throwable $exception)
{
try {
$e = FlattenException::create($exception);
$handler = new HtmlErrorRenderer(true); // boolean, true raises debug flag...
$css = $handler->getStylesheet();
$content = $handler->getBody($e);
//Mail::to(config('app.exception_mail'))->send(new MailContact($contact));
// Verwende normale Mail-Klasse statt Facade, um Probleme bei der Initialisierung zu vermeiden
$to = config('app.exception_mail');
$subject = 'gruene-seele Exception: ' . \Request::fullUrl();
if ($to) {
\Mail::send('emails.exception', compact('css', 'content'), function ($message) use ($to, $subject) {
$message
->to($to)
->subject($subject)
;
});
}
} catch (Throwable $ex) {
// Einfache Fehlerprotokollierung ohne Facade
file_put_contents(
storage_path('logs/laravel-' . date('Y-m-d') . '.log'),
'[' . date('Y-m-d H:i:s') . '] exception-handler-error: ' . $ex->getMessage() . "\n",
FILE_APPEND
);
}
}
}

View file

@ -1,33 +0,0 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Excel;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithHeadings;
class UserTeamExport implements FromCollection, WithHeadings
{
protected $collection;
protected $headings;
use Exportable;
public function __construct($data,$header)
{
$this->collection = $data;
$this->headings = $header;
}
public function collection()
{
return collect($this->collection);
}
public function headings(): array
{
return [$this->headings];
}
}

View file

@ -1,33 +0,0 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Excel;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\WithHeadings;
class XLSExport implements FromCollection, WithHeadings
{
protected $collection;
protected $headings;
use Exportable;
public function __construct($data,$header)
{
$this->collection = $data;
$this->headings = $header;
}
public function collection()
{
return collect($this->collection);
}
public function headings(): array
{
return [$this->headings];
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Services\PaymentReminderService;
use Illuminate\Http\Request;
class PaymentReminderController extends Controller
{
/* not used at the moment */
private $paymentReminderService;
public function __construct(PaymentReminderService $paymentReminderService)
{
$this->paymentReminderService = $paymentReminderService;
}
/**
* Zeige die Payment Reminders Übersicht
*/
public function index()
{
$detailedData = $this->paymentReminderService->getDetailedPaymentsData();
$summaryData = $this->paymentReminderService->getAllOpenPayments();
// Statistiken für die Übersicht
$totalPayments = collect($detailedData)->count();
$totalAmount = collect($detailedData)->sum('amount');
$clearingTypes = collect($detailedData)->groupBy('clearingtype')->map->count();
return view('admin.payment.reminder.index', compact(
'detailedData',
'summaryData',
'totalPayments',
'totalAmount',
'clearingTypes'
));
}
}

View file

@ -1,200 +0,0 @@
<?php
namespace App\Http\Controllers\Evo;
use Auth;
use Request;
use App\User;
use Carbon\Carbon;
use App\Services\HTMLHelper;
use App\Models\ShoppingOrder;
use App\Exports\UserTeamExport;
use App\Models\ShoppingOrderItem;
use App\Http\Controllers\Controller;
use Maatwebsite\Excel\Facades\Excel;
use App\Services\BusinessPlan\ExportBot;
use Illuminate\Database\Eloquent\Collection;
class SalesController extends Controller
{
public function __construct()
{
$this->middleware('admin');
}
public function index()
{
$this->setFilterVars();
$data = [
'filter_months' => HTMLHelper::getTransMonths(),
'filter_years' => HTMLHelper::getYearRange(2022),
];
return view('admin.evaluation.salesvolume', $data);
}
public function download(){
if(Request::get('action') === "export"){
$objects = $this->initSearch(false);
$columns = [];
$filename = "gs-absatzmengen-".session('product_sales_vol_filter_month').'_'.session('product_sales_vol_filter_year')."-export";
$headers = array(
'#',
'Produkt',
'Artikelnummer',
'Menge',
);
if($objects){
foreach ($objects as $key => $obj){
$columns[] = array(
'id' => $key,
'name' => $obj['name'],
'number' => $obj['number'],
'value' => $obj['value'],
);
}
}
return Excel::download(new UserTeamExport($columns, $headers), $filename.'.xls');
}
}
private function setFilterVars(){
if(!session('product_sales_vol_filter_month')){
session(['product_sales_vol_filter_month' => intval(date('m'))]);
}
if(!session('product_sales_vol_filter_year')){
session(['product_sales_vol_filter_year' => intval(date('Y'))]);
}
if(Request::get('product_sales_vol_filter_month')){
session(['product_sales_vol_filter_month' => Request::get('product_sales_vol_filter_month')]);
}
if(Request::get('product_sales_vol_filter_year')){
session(['product_sales_vol_filter_year' => Request::get('product_sales_vol_filter_year')]);
}
}
private function initSearch($returnColl = true)
{
$this->setFilterVars();
$date_start = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->format('Y-m-d H:i:s');
$date_end = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d H:i:s');
$ShoppingOrders = ShoppingOrder::where('paid', 1)->where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
$objects = [];
foreach($ShoppingOrders as $ShoppingOrder){
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product){
if(isset($objects[$shopping_order_item->product->id])){
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
$objects[$shopping_order_item->product->id]['value'] = $value;
}else{
$objects[$shopping_order_item->product->id] = [
'name' => $shopping_order_item->product->name,
'number' => $shopping_order_item->product->number,
'value' => $shopping_order_item->qty
];
}
}
}
}
if($returnColl){
$collection = collect();
foreach($objects as $key => $obj){
$collection->push([
'id' => $key,
'name' => $obj['name'],
'number' => $obj['number'],
'value' => $obj['value'],
]);
}
return $collection;
}
return $objects;
}
public function datatable(){
$collection = $this->initSearch(true);
$collect = collect([
['id' => 1, 'name' => 'John', 'number'=>92012, 'value'=>123],
['id' => 2, 'name' => 'Jane', 'number'=>92012, 'value'=>123],
['id' => 3, 'name' => 'James', 'number'=>92012, 'value'=>123],
]);
return \DataTables::of($collection)->toJson();
}
/*private function testCheckFunction(){
//$date_start = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->format('Y-m-d');
//$date_end = Carbon::parse('01.'.session('product_sales_vol_filter_month').'.'.session('product_sales_vol_filter_year'))->endOfMonth()->format('Y-m-d');
$date_start = Carbon::parse('01.01.2024')->format('Y-m-d H:i:s');
$date_end = Carbon::parse('01.01.2024')->endOfMonth()->format('Y-m-d H:i:s');
dump($date_start);
dump($date_end);
$ShoppingOrders = ShoppingOrder::where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
$objects = [];
$counter = 0;
foreach($ShoppingOrders as $ShoppingOrder){
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product){
if($shopping_order_item->product->id === 122){
//dump($shopping_order_item->qty);
//$counter += $shopping_order_item->qty;
if(isset($objects[$shopping_order_item->product->id])){
$value = intval($objects[$shopping_order_item->product->id]['value'] + $shopping_order_item->qty);
$objects[$shopping_order_item->product->id]['value'] = $value;
}else{
$objects[$shopping_order_item->product->id] = [
'name' => $shopping_order_item->product->name,
'number' => $shopping_order_item->product->number,
'value' => $shopping_order_item->qty
];
}
}
}
}
}
$ShoppingOrderItems = ShoppingOrderItem::whereProductId(122)->whereBetween('created_at', [$date_start, $date_end])->get();
$counter = 0;
foreach($ShoppingOrderItems as $ShoppingOrderItem){
$counter += $ShoppingOrderItem->qty;
dump($ShoppingOrderItem->id);
}
// dump($objects);
dump($counter);
dd("OKAY");
}*/
}

View file

@ -27,7 +27,7 @@ class ModalController extends Controller
if(Request::ajax()){
if($data['action'] === 'shopping-order-change-member'){
$value = ShoppingOrder::find($data['id']);
$route = route('admin_sales_customers_detail', [$value->id]);
$route = route('admin_sales_detail', [$value->id]);
$ret = view("admin.modal.member", compact('value', 'data', 'route'))->render();
}
if($data['action'] === 'shopping-user-change-member'){

View file

@ -62,7 +62,7 @@ class PaymentInvoiceController extends Controller
return \DataTables::eloquent($query)
->addColumn('id', function (ShoppingOrder $ShoppingOrder) {
return '<a href="' . route('admin_sales_customers_detail', [$ShoppingOrder->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
return '<a href="' . route('admin_sales_detail', [$ShoppingOrder->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('total_shipping', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->getFormattedTotalShipping()."";

View file

@ -0,0 +1,147 @@
<?php
namespace App\Http\Controllers;
use Carbon;
use Request;
use App\Models\PaymentReminder;
use App\Models\ShoppingPayment;
use App\Services\PaymentReminderService;
class PaymentReminderController extends Controller
{
private $filter_user_status;
private $paymentReminderService;
public function __construct(PaymentReminderService $paymentReminderService)
{
$this->middleware('auth');
$this->paymentReminderService = $paymentReminderService;
}
public function index()
{
// Hole die detaillierten Daten für die Tabellen-Ansicht
$detailedData = $this->paymentReminderService->getDetailedPaymentsData();
// $summaryData = $this->paymentReminderService->getAllOpenPayments();
// Statistiken für die Übersicht
$totalPayments = collect($detailedData)->count();
$totalAmount = collect($detailedData)->sum('amount')/100;
$clearingTypes = collect($detailedData)->groupBy('clearingtype')->map->count();
$data = [
'reminders' => PaymentReminder::all(),
'detailedData' => $detailedData,
//'summaryData' => $summaryData,
'totalPayments' => $totalPayments,
'totalAmount' => $totalAmount,
'clearingTypes' => $clearingTypes,
];
return view('admin.payment.reminder.index', $data);
}
public function create()
{
$reminder = new PaymentReminder();
$reminder->active = true;
$data = [
'reminder' => $reminder,
];
return view('admin.payment.reminder.edit', $data);
}
public function edit($id)
{
$data = [
'reminder' => PaymentReminder::find($id),
];
return view('admin.payment.reminder.edit', $data);
}
public function store()
{
$data = Request::all();
$data['active'] = isset($data['active']) ? true : false;
$data['action'] = isset($data['action']) ? $data['action'] : NULL;
if ($data['id'] === 'new') {
PaymentReminder::create($data);
} else {
$reminder = PaymentReminder::find($data['id']);
$reminder->update($data);
}
return redirect()->route('admin_payments_reminder')->with('success', 'Erinnerung gespeichert');
}
public function action($action, $id)
{
$payment = ShoppingPayment::find($id);
if($action == 'send_reminder'){
$bool = $this->paymentReminderService->sendReminder($payment);
if($bool){
\Session()->flash('alert-success', "Zahlungserinnerung gesendet");
}else{
\Session()->flash('alert-error', "Keine Zahlungserinnerung gesendet");
}
}
if($action == 'no_payment'){
$this->paymentReminderService->setNoNPayment($payment);
\Session()->flash('alert-success', "Zahlung als nicht bezahlt markiert");
}
return redirect()->route('admin_payments_reminder');
}
public function delete($id)
{
$reminder = PaymentReminder::find($id);
$reminder->delete();
return redirect()->route('admin_payments_reminder');
}
public function logs()
{
// Hole die Log-Statistiken für verschiedene Zeiträume
$stats7Days = $this->paymentReminderService->getLogStatistics(7);
$stats30Days = $this->paymentReminderService->getLogStatistics(30);
$stats90Days = $this->paymentReminderService->getLogStatistics(90);
// Hole die neuesten Logs
$recentLogs = $this->paymentReminderService->getPaymentReminderLogs(50);
// Filter-Parameter
$orderId = Request::get('order_id');
$action = Request::get('action');
$startDate = Request::get('start_date');
$endDate = Request::get('end_date');
// Gefilterte Logs
$filteredLogs = null;
if ($orderId || $action || $startDate || $endDate) {
if ($startDate && $endDate) {
$filteredLogs = $this->paymentReminderService->getLogsForDateRange($startDate, $endDate);
} elseif ($orderId) {
$filteredLogs = $this->paymentReminderService->getLogsForPayment($orderId);
} elseif ($action) {
$filteredLogs = $this->paymentReminderService->getPaymentReminderLogs(100, null, $action);
}
}
$data = [
'stats7Days' => $stats7Days,
'stats30Days' => $stats30Days,
'stats90Days' => $stats90Days,
'recentLogs' => $recentLogs,
'filteredLogs' => $filteredLogs,
'orderId' => $orderId,
'action' => $action,
'startDate' => $startDate,
'endDate' => $endDate,
];
return view('admin.payment.reminder.logs', $data);
}
}

View file

@ -4,7 +4,6 @@ namespace App\Http\Controllers;
use Request;
use App\Models\Setting;
use App\Models\UserShop;
use App\Services\Payment;
use App\Models\ShoppingUser;
use App\Models\ShoppingOrder;
@ -13,6 +12,7 @@ use App\Models\ShoppingPayment;
use App\Models\PaymentTransaction;
use App\Services\CustomerPriority;
use App\Repositories\InvoiceRepository;
use App\Services\PaymentService;
class SalesController extends Controller
{
@ -21,7 +21,167 @@ class SalesController extends Controller
$this->middleware('admin');
}
public function users(){
public function index(){
if(Request::get('reset') === 'filter'){
set_user_attr('filter_txaction', null);
set_user_attr('filter_member_id', null);
set_user_attr('filter_art', null);
set_user_attr('filter_shipped', null);
return redirect(route('admin_sales'));
}
//set Filter!
$filter_members = ShoppingOrder::join('users', 'member_id', '=', 'users.id')->groupBy('member_id')->join('user_accounts', 'account_id', '=', 'user_accounts.id')->select('users.id', 'users.email', 'user_accounts.first_name', 'user_accounts.last_name')->get();
$data = [
'filter_members' => $filter_members,
];
return view('admin.sales.index', $data);
}
public function detail($id){
$ShoppingOrder = ShoppingOrder::find($id);
if($ShoppingOrder->shipped == 0){
$ShoppingOrder->shipped = 1;
$ShoppingOrder->save();
}
$data = [
'shopping_order' => $ShoppingOrder,
'isAdmin' => true,
'isView' => $ShoppingOrder->auth_user_id ? 'sales_user' : 'sales_customer',
];
return view('admin.sales.detail', $data);
}
public function detailStore($id){
$data = Request::all();
$change_member_error = false;
if($data['action']==='shopping-order-change-member'){
if(!isset($data['change_member_key']) || $data['change_member_key'] !== config('main.edit_data_pass')){
$change_member_error = "Das Passwort ist falsch.";
}else{
//change
$shopping_order = ShoppingOrder::findOrFail($data['id']);
CustomerPriority::newMemberForOrder($shopping_order, $data['change_member_id'], $data['customer_set_member_for']);
\Session()->flash('alert-save', true);
return redirect(route('admin_sales_detail', [$shopping_order->id]));
}
}
if($data['action']==='shopping-user-is-like-member'){
if(!isset($data['change_member_key']) || $data['change_member_key'] !== config('main.edit_data_pass')){
\Session()->flash('alert-error', 'Das Passwort ist falsch.');
return redirect($data['back']);
}else{
if(!isset($data['is_like_shopping_user_id'])){
\Session()->flash('alert-error', 'Keine Änderung ausgewählt');
return redirect($data['back']);
}
$shopping_user = ShoppingUser::findOrFail($data['id']);
$set_like_shopping_user = ShoppingUser::findOrFail($data['is_like_shopping_user_id']);
$send_member_mail = isset($data['send_member_mail']) ? true : false;
$change_shopping_user = isset($data['change_shopping_user']) ? true : false;
//Mail send in setIsLike
CustomerPriority::setIsLike($shopping_user, $set_like_shopping_user, $send_member_mail, $change_shopping_user);
\Session()->flash('alert-save', true);
return redirect($data['back']);
}
}
$ShoppingOrder = ShoppingOrder::find($id);
$data = [
'change_member_error' => $change_member_error,
'shopping_order' => $ShoppingOrder,
'isAdmin' => true,
'isView' => $ShoppingOrder->auth_user_id ? 'sales_user' : 'sales_customer',
];
return view('admin.sales.detail', $data);
}
public function datatable(){
$query = ShoppingOrder::with('shopping_user', 'shopping_payments')->select('shopping_orders.*');
set_user_attr('filter_txaction', Request::get('filter_txaction'));
if(Request::get('filter_txaction') != ""){
if(Request::get('filter_txaction') === 'NULL'){
$query->where('txaction', '=', NULL);
}else{
$query->where('txaction', '=', Request::get('filter_txaction'));
}
}
set_user_attr('filter_member_id', Request::get('filter_member_id'));
if(Request::get('filter_member_id') != ""){
$query->where('member_id', '=', Request::get('filter_member_id'));
}
set_user_attr('filter_art', Request::get('filter_art'));
if(Request::get('filter_art') != ""){
if(Request::get('filter_art') === 'user_order'){
$query->where('shopping_orders.auth_user_id', '!=', NULL)->where('payment_for', '!=', 6);
}elseif(Request::get('filter_art') === 'customer_order'){
$query->where('shopping_orders.auth_user_id', NULL);
}elseif(Request::get('filter_art') === 'user_for_customer'){
$query->where('shopping_user_id', '!=', NULL)->where('payment_for', '=', 6);
}
// $query->where('payment_for', '=', Request::get('filter_art'));
}
set_user_attr('filter_shipped', Request::get('filter_shipped'));
if(Request::get('filter_shipped') != ""){
$query->where('shipped', '=', Request::get('filter_shipped'));
}
return \DataTables::eloquent($query)
->addColumn('id', function (ShoppingOrder $ShoppingOrder) {
return '<a href="' . route('admin_sales_detail', [$ShoppingOrder->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('created_at', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->created_at->format("d.m.Y");
})
->addColumn('txaction', function (ShoppingOrder $ShoppingOrder) {
return Payment::getShoppingOrderBadge($ShoppingOrder);
})
->addColumn('total_shipping', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->getFormattedTotalShipping()."";
})
->addColumn('payment', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->getLastShoppingPayment('getPaymentType');
})
->addColumn('shipped', function (ShoppingOrder $ShoppingOrder) {
return '<span class="badge badge-pill badge-'.$ShoppingOrder->getShippedColor().'">'.$ShoppingOrder->getShippedType().'</span>';
})
->addColumn('payment_for', function (ShoppingOrder $ShoppingOrder) {
return Payment::getPaymentForTypeBadge($ShoppingOrder);
})
->addColumn('reference', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->getLastShoppingPayment('reference');
})
->addColumn('member_id', function (ShoppingOrder $ShoppingOrder) {
if($ShoppingOrder->member_id) {
return $ShoppingOrder->member_id ? '<a href="' . route('admin_lead_edit', [$ShoppingOrder->member_id]) . '">' . $ShoppingOrder->member->getFullName() . '</a>' : '';
}
if($ShoppingOrder->shopping_user && $ShoppingOrder->shopping_user->is_like){
return '<button type="button" class="btn btn-xs btn-outline-info" data-toggle="modal" data-target="#modals-load-content"
data-id="'.$ShoppingOrder->shopping_user->id.'"
data-action="shopping-user-is-like-member"
data-back="'.route('admin_sales').'"
data-modal="modal-xl"
data-route="'.route('modal_load').'"><span class="fa fa-edit"></span> Vertriebspartner zuordnen</button>';
}
return '';
})
->orderColumn('id', 'id $1')
->orderColumn('txaction', 'txaction $1')
->orderColumn('payment_for', 'payment_for $1')
->orderColumn('member_id', 'member_id $1')
->orderColumn('shipped', 'shipped $1')
->orderColumn('total_shipping', 'total_shipping $1')
->rawColumns(['id', 'member_id', 'txaction', 'user_shop_id', 'payment_for', 'shipped'])
->make(true);
}
/*public function users(){
if(Request::get('reset') === 'filter'){
return redirect(route('admin_sales_users'));
@ -105,7 +265,7 @@ class SalesController extends Controller
if(Request::get('reset') === 'filter'){
set_user_attr('filter_txaction', null);
set_user_attr('filter_member_id', null);
return redirect(route('admin_sales_customers'));
return redirect(route('admin_sales'));
}
$filter_members = ShoppingOrder::join('users', 'member_id', '=', 'users.id')->groupBy('member_id')->join('user_accounts', 'account_id', '=', 'user_accounts.id')->select('users.id', 'users.email', 'user_accounts.first_name', 'user_accounts.last_name')->get(); //->pluck('email', 'id')->unique()->toArray();
$data = [
@ -141,7 +301,7 @@ class SalesController extends Controller
$shopping_order = ShoppingOrder::findOrFail($data['id']);
CustomerPriority::newMemberForOrder($shopping_order, $data['change_member_id'], $data['customer_set_member_for']);
\Session()->flash('alert-save', true);
return redirect(route('admin_sales_customers_detail', [$shopping_order->id]));
return redirect(route('admin_sales_detail', [$shopping_order->id]));
}
}
if($data['action']==='shopping-user-is-like-member'){
@ -169,7 +329,7 @@ class SalesController extends Controller
'isAdmin' => true,
'isView' => 'sales_customer',
];
return view('admin.sales.customer_detail', $data);
return view('admin.sales._detail', $data);
}
public function customersDatatable(){
@ -192,7 +352,7 @@ class SalesController extends Controller
return \DataTables::eloquent($query)
->addColumn('id', function (ShoppingOrder $ShoppingOrder) {
return '<a href="' . route('admin_sales_customers_detail', [$ShoppingOrder->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
return '<a href="' . route('admin_sales_detail', [$ShoppingOrder->id]) . '" class="btn icon-btn btn-sm btn-primary"><span class="fa fa-edit"></span></a>';
})
->addColumn('created_at', function (ShoppingOrder $ShoppingOrder) {
return $ShoppingOrder->created_at->format("d.m.Y");
@ -223,7 +383,7 @@ class SalesController extends Controller
return '<button type="button" class="btn btn-xs btn-outline-info" data-toggle="modal" data-target="#modals-load-content"
data-id="'.$ShoppingOrder->shopping_user->id.'"
data-action="shopping-user-is-like-member"
data-back="'.route('admin_sales_customers').'"
data-back="'.route('admin_sales').'"
data-modal="modal-xl"
data-route="'.route('modal_load').'"><span class="fa fa-edit"></span> Vertriebspartner zuordnen</button>';
}
@ -238,7 +398,7 @@ class SalesController extends Controller
->orderColumn('total_shipping', 'total_shipping $1')
->rawColumns(['id', 'member_id', 'txaction', 'user_shop_id', 'payment_for', 'shipped'])
->make(true);
}
}*/
public function store(){
$data = Request::all();
@ -306,67 +466,11 @@ class SalesController extends Controller
}
/* txaction ändern
änderung der txaction von der Bestellung, Status Zahlung, offen, bezahlt, keine zahlung */
if($data['action'] === 'store_txaction' && isset($data['txaction']) && isset($data['payment_id'])){
$shopping_order = ShoppingOrder::findOrFail($data['id']);
$shopping_payment = ShoppingPayment::findOrFail($data['payment_id']);
if($shopping_payment->txaction === $data['txaction']){
return back();
}
//shopping_order_margin Bestellung im partner Center
if($shopping_order->shopping_order_margin && $shopping_order->shopping_order_margin->from_payment_credit > 0){
$last_UserPayCredit = UserPayCredit::where('shopping_order_id', $shopping_order->id)->whereIn('status', [2, 4])->orderBy('id', 'DESC')->first();
//Status Keine Zahlung, Guthaben zurückführen, wenn status 2 / deduction from payment
if($last_UserPayCredit && $data['txaction'] === 'non' && $last_UserPayCredit->status === 2){
Payment::handelUserPayCredits($shopping_order, 'return');
}
//Status Zahlung, voher gab es eine Storno, Guthaben abziehen wenn status 4 / return from order
if($last_UserPayCredit && $last_UserPayCredit->status === 4 && ($data['txaction'] === 'open' || $data['txaction'] === 'paid')){
Payment::handelUserPayCredits($shopping_order, 'deduction');
}
}
$payt = PaymentTransaction::create([
'shopping_payment_id' => $shopping_payment->id,
'request' => 'transaction',
'txid' => 0,
'userid' => 0,
'status' => $shopping_payment->clearingtype,
'transmitted_data' => NULL,
'txaction' => $data['txaction'],
'mode' => $shopping_payment->mode,
]);
$shopping_order->txaction = $data['txaction'];
$shopping_order->paid = $payt->txaction === 'paid' ? true : false;
$shopping_order->save();
$shopping_payment->txaction = $data['txaction'];
$shopping_payment->save();
if($payt->status === 'vor' && $payt->txaction === 'paid'){
$send_link = Payment::paymentStatusPaidAction($shopping_order, true);
}
//handel credit loading by change when by $shopping_order_item->handl
if($shopping_order->shopping_user->is_for === 'cr'){
$last_UserPayCredit = UserPayCredit::where('shopping_order_id', $shopping_order->id)->whereIn('status', [7, 8])->orderBy('id', 'DESC')->first();
//Status Keine Zahlung, Guthaben abziehen, wenn status 7 <- wurde aufgeladen
if($data['txaction'] === 'non' && $last_UserPayCredit && $last_UserPayCredit->status === 7){
Payment::handelUserPayChargingCredits($shopping_order, 'remove');
}
//Status Zahlung, voher gab es eine Storno, Guthaben wieder aufladen, wenn bezahlt wenn status 8
if($last_UserPayCredit && $last_UserPayCredit->status === 8 && $data['txaction'] === 'paid'){
Payment::handelUserPayChargingCredits($shopping_order, 'add');
}
}
$edata = [
'mode' => $payt->mode,
'txaction' => $payt->txaction,
'send_link' => false,
];
//TODO can send MAIL
Payment::paymentStatusSendMail($shopping_order, $shopping_payment, $edata);
PaymentService::updateTransactionStatus($data['id'], $data['txaction'], $data['payment_id']);
}
}
@ -391,9 +495,21 @@ class SalesController extends Controller
}else{
$user_invoice = $invoice_repo->create($data);
}
return redirect(route('admin_sales_users_detail', [$shopping_order->id]));
return redirect(route('admin_sales_detail', [$shopping_order->id]));
}
}
}
public function sendLogisticMail($id){
$shopping_order = ShoppingOrder::findOrFail($id);
if(\App\Services\Invoice::isInvoice($shopping_order)){
\App\Services\Invoice::sendLogisticMail($shopping_order);
\Session()->flash('alert-success', "Rechnung / Lieferschein wurde an den Versand gesendet.");
}else{
\Session()->flash('alert-error', "Keine Rechnung vorhanden.");
}
return redirect(route('admin_sales_detail', [$shopping_order->id]));
}
}

View file

@ -43,7 +43,6 @@ class SalesController extends Controller
public function download()
{
$this->setFilterVars();
if (Request::get('action') === "filter") {

View file

@ -61,7 +61,7 @@ class PromotionController extends Controller
if(isset($data['action']) && $data['action'] === 'save-user-promotion'){
$rules = array(
'name' => 'required',
'user_promotion_url' => ' required|alpha_dash|profanity|'.'unique:promotion_users,url,'.$id.',id'.'|min:4|max:20|full_word_check',
'user_promotion_url' => ' required|alpha_dash|'.'unique:promotion_users,url,'.$id.',id'.'|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
$profanity = \App\Models\Setting::getContentBySlug('promotion_user_url_profanity');
@ -107,7 +107,7 @@ class PromotionController extends Controller
$unique .= ','.$data['puid'].',id';
}
$rules = array(
'user_promotion_url' => ' required|alpha_dash|profanity|'.$unique.'|min:4|max:20|full_word_check',
'user_promotion_url' => ' required|alpha_dash|'.$unique.'|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
$profanity = \App\Models\Setting::getContentBySlug('promotion_user_url_profanity');

View file

@ -1,53 +0,0 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\User;
use Request;
use Carbon;
use App\Models\ShoppingOrder;
class RevenueController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
$start = 2021;
$end = date('Y');
$years = range($start, $end);
if(Request::get('filter_sales_year')){
$active_year = Request::get('filter_sales_year');
}else{
$active_year = $end;
}
$date1 = Carbon::parse('01.01.'.$active_year." 00:00:00")->format('Y-m-d H:i:s');
$date2 = Carbon::parse('31.12.'.$active_year." 23:59:59")->toDateString();
$values = ShoppingOrder::where('shopping_orders.auth_user_id', '!=', NULL) //::with('shopping_user', )->select('shopping_orders.*')
->where('mode', '=', 'live')
->where('paid', '=', 1)
->whereHas('shopping_order_items', function($q) {
$q->where('product_id', 34)->OrWhere('product_id', 35)->OrWhere('product_id', 36)->OrWhere('product_id', 67)->OrWhere('product_id', 69);
})
->whereBetween('created_at', [$date1, $date2])
->get();
$data = [
'years' => $years,
'active_year' => $active_year,
'values' => $values,
];
return view('user.revenue.index', $data);
}
}

View file

@ -54,7 +54,7 @@ class ShopController extends Controller
if(isset($data['action']) && $data['action'] === 'save-user-shop'){
$rules = array(
'name' => 'required',
'user_shop_url' => ' required|alpha_dash|profanity|'.'unique:user_shops,url,'.$user->shop->id.',id'.'|min:4|max:20|full_word_check',
'user_shop_url' => ' required|alpha_dash|'.'unique:user_shops,url,'.$user->shop->id.',id'.'|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
$profanity = \App\Models\Setting::getContentBySlug('promotion_user_url_profanity');
@ -84,7 +84,7 @@ class ShopController extends Controller
$unique .= ','.$data['usid'].',id';
}
$rules = array(
'user_shop_url' => ' required|alpha_dash|profanity|'.$unique.'|min:4|max:20|full_word_check',
'user_shop_url' => ' required|alpha_dash|'.$unique.'|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
$profanity = \App\Models\Setting::getContentBySlug('promotion_user_url_profanity');

View file

@ -270,7 +270,7 @@ class UserShopController extends Controller
if(Request::get('shop_submit') == 'check'){
$rules = array(
'user_shop_name' => ' required|alpha_dash|profanity|unique:user_shops,name|min:4|max:20|full_word_check',
'user_shop_name' => ' required|alpha_dash|unique:user_shops,name|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
if(in_array($value, config('profanity.full_word_check'))){
@ -291,7 +291,7 @@ class UserShopController extends Controller
if(Request::get('shop_submit') == 'action') {
$rules = array(
'user_shop_name' => ' required|alpha_dash|profanity|unique:user_shops,name|min:4|max:20|full_word_check',
'user_shop_name' => ' required|alpha_dash|unique:user_shops,name|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
if(in_array($value, config('profanity.full_word_check'))){
@ -381,7 +381,7 @@ class UserShopController extends Controller
public function checkUserShopName(){
$rules = array(
'user_shop_name' => ' required|alpha_dash|profanity|unique:user_shops,name|min:4|max:20|full_word_check',
'user_shop_name' => ' required|alpha_dash|unique:user_shops,name|min:4|max:20|full_word_check',
);
Validator::extend('full_word_check', function ($attribute, $value, $parameters, $validator) {
if(in_array($value, config('profanity.full_word_check'))){

View file

@ -3,14 +3,15 @@
namespace App\Http\Controllers;
use App\Mail\MailActivateUser;
use App\User;
use Auth;
use Validator;
use Request;
use Carbon\Carbon;
use Illuminate\Database\Connection;
use App\Mail\MailActivateUser;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use Request;
use Validator;
class UserUpdateEmailController extends Controller
{
@ -152,7 +153,7 @@ class UserUpdateEmailController extends Controller
protected function getToken()
{
return hash_hmac('sha256', str_random(40), config('app.key'));
return hash_hmac('sha256', Str::random(40), config('app.key'));
}
public function createActivation($user, array $data)

View file

@ -15,7 +15,7 @@ class Kernel extends HttpKernel
*/
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,

View file

@ -2,15 +2,15 @@
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array|string
* @var array|string|null
*/
protected $proxies;
@ -19,5 +19,10 @@ class TrustProxies extends Middleware
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_ALL;
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View file

@ -0,0 +1,188 @@
<?php
namespace App\Libraries;
//use FPDI in myMerge v2
//use FPDF;
//use FPDI;
class MyPDFMerger
{
private $_files; //['form.pdf'] ["1,2,4, 5-19"]
private $_fpdi;
public function __construct()
{
/* if(!class_exists("FPDF")) {
require_once(__DIR__.'/fpdf/fpdf.php');
}
if(!class_exists("FPDI")) {
require_once(__DIR__.'/fpdi/fpdi.php');
}*/
}
public function addPDF($filepath, $pages = 'all')
{
if (file_exists($filepath)) {
if (strtolower($pages) != 'all') {
$pages = $this->_rewritepages($pages);
}
$this->_files[] = array($filepath, $pages);
} else {
throw new \exception("Could not locate PDF on '$filepath'");
}
return $this;
}
public function myMerge($outputmode = 'browser', $outputpath = 'newfile.pdf', $theme = false)
{
if (!isset($this->_files) || !is_array($this->_files)): throw new \exception("No PDFs to merge."); endif;
$fpdi = new \setasign\Fpdi\Fpdi();
$first = 1;
//
//merger operations
foreach ($this->_files as $file) {
$filename = $file[0];
$filepages = $file[1];
$count = $fpdi->setSourceFile($filename);
//add the pages
if ($filepages == 'all') {
for ($i = 1; $i <= $count; $i++) {
$count = $fpdi->setSourceFile($filename);
$template = $fpdi->importPage($i);
$size = $fpdi->getTemplateSize($template);
$orientation = ($size['height'] > $size['width']) ? 'P' : 'L';
$fpdi->AddPage($orientation, array($size['width'], $size['height']));
if($theme){
$fpdi->setSourceFile(__DIR__ . '/../../public/pdf/'.$theme.'-'.$first.'.pdf');
if($first == 1){
$first = 2;
}
$backId = $fpdi->importPage(1);
$fpdi->useTemplate($backId);
}
$fpdi->useTemplate($template);
}
} else {
foreach ($filepages as $page) {
$count = $fpdi->setSourceFile($filename);
if (!$template = $fpdi->importPage($page)): throw new \exception("Could not load page '$page' in PDF '$filename'. Check that the page exists."); endif;
$size = $fpdi->getTemplateSize($template);
$orientation = ($size['h'] > $size['w']) ? 'P' : 'L';
$fpdi->AddPage($orientation, array($size['w'], $size['h']));
if($theme){
$fpdi->setSourceFile(__DIR__ . '/../../public/pdf/'.$theme.'-'.$first.'.pdf');
if($first == 1){
$first = 2;
}
$backId = $fpdi->importPage(1);
$fpdi->useTemplate($backId);
}
$fpdi->useTemplate($template);
}
}
//after first file (invoice) on bpaper
$slug = false;
}
//output operations
$mode = $this->_switchmode($outputmode);
if ($mode == 'S') {
return $fpdi->Output($outputpath, 'S');
} else {
if ($fpdi->Output($outputpath, $mode) == '') {
return true;
} else {
throw new \exception("Error outputting PDF to '$outputmode'.");
return false;
}
}
}
/**
* FPDI uses single characters for specifying the output location. Change our more descriptive string into proper format.
* @param $mode
* @return Character
*/
private function _switchmode($mode)
{
switch (strtolower($mode)) {
case 'download':
return 'D';
break;
case 'browser':
return 'I';
break;
case 'file':
return 'F';
break;
case 'string':
return 'S';
break;
default:
return 'I';
break;
}
}
/**
* Takes our provided pages in the form of 1,3,4,16-50 and creates an array of all pages
* @param $pages
* @return array
* @throws exception
*/
private function _rewritepages($pages)
{
$pages = str_replace(' ', '', $pages);
$part = explode(',', $pages);
//parse hyphens
foreach ($part as $i) {
$ind = explode('-', $i);
if (count($ind) == 2) {
$x = $ind[0]; //start page
$y = $ind[1]; //end page
if ($x > $y): throw new \exception("Starting page, '$x' is greater than ending page '$y'.");
return false; endif;
//add middle pages
while ($x <= $y): $newpages[] = (int)$x;
$x++; endwhile;
} else {
$newpages[] = (int)$ind[0];
}
}
return $newpages;
}
}
/*
$pdf = new PDFMerger;
$pdf->addPDF('samplepdfs/one.pdf', '1, 3, 4')
->addPDF('samplepdfs/two.pdf', '1-2')
->addPDF('samplepdfs/three.pdf', 'all')
->merge('file', 'samplepdfs/TEST2.pdf');
//REPLACE 'file' WITH 'browser', 'download', 'string', or 'file' for output options
//You do not need to give a file path for browser, string, or download - just the name.
*/

87
app/Mail/MailLogistic.php Normal file
View file

@ -0,0 +1,87 @@
<?php
namespace App\Mail;
use App\User;
use App\Services\Invoice;
use App\Models\ShoppingOrder;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Storage;
use Illuminate\Contracts\Queue\ShouldQueue;
class MailLogistic extends Mailable
{
use Queueable, SerializesModels;
protected $shopping_order;
public $subject;
public function __construct(ShoppingOrder $shopping_order)
{
$this->shopping_order = $shopping_order;
$name = $shopping_order->shopping_user->billing_firstname . ' ' . $shopping_order->shopping_user->billing_lastname;
$company = $shopping_order->shopping_user->billing_company ?? '';
$this->subject = 'Partner';
if($shopping_order->user_white_label){
//Bei allen, die ein eigenes Logo haben
$this->subject .= ' (mit Logo)';
}else{
//Bei allen, die kein Tattoostudio sind
$this->subject = ' - ';
}
if($shopping_order->shopping_user->same_as_billing){
//Rechnungsadresse und Lieferadresse sind gleich
$this->subject = '';
}else{
//hat eine andere Lieferadresse
$this->subject = ' Lieferadresse';
}
$this->subject .= ' '.$company.' (' . $name . ')';
}
public function build()
{
$title = false;
$copy1line = false;
$filename = Invoice::getFilename($this->shopping_order);
$path = Invoice::getDownloadPath($this->shopping_order);
if (!Storage::disk('public')->exists($path)) {
return;
}
$file = Storage::disk('public')->path($path);
$mime = Storage::disk('public')->mimeType($path);
$mail = $this->view('emails.logistic')->with([
'title' => $title,
'copy1line' => $copy1line,
])->attach($file,[
'as' => $filename,
'mime' => $mime,
]);
//Wenn das Logo gesetzt ist und die Rechnungsadresse und Lieferadresse unterschiedlich sind, dann wird der Lieferschein angehängt
if($this->shopping_order->user_white_label && !$this->shopping_order->shopping_user->same_as_billing){
$filenameDelivery = Invoice::getDeliveryFilename($this->shopping_order);
$pathDelivery = Invoice::getDownloadPathDelivery($this->shopping_order);
if (!Storage::disk('public')->exists($pathDelivery)) {
return;
}
$fileDelivery = Storage::disk('public')->path($pathDelivery);
$mimeDelivery = Storage::disk('public')->mimeType($pathDelivery);
$mail->attach($fileDelivery,[
'as' => $filenameDelivery,
'mime' => $mimeDelivery,
]); // attach file;
}
return $mail;
}
}

59
app/Mail/MailLogitic.php Normal file
View file

@ -0,0 +1,59 @@
<?php
namespace App\Mail;
use App\User;
use App\Services\Invoice;
use App\Models\ShoppingOrder;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Storage;
use Illuminate\Contracts\Queue\ShouldQueue;
class MailLogistic extends Mailable
{
use Queueable, SerializesModels;
protected $shopping_order;
public $subject;
public function __construct(ShoppingOrder $shopping_order)
{
$this->shopping_order = $shopping_order;
if($shopping_order->user_white_label){
//Bei allen, die ein eigenes Logo haben
$this->subject = 'Partner (mit Logo) - Firma (Vorname Nachname)';
}else{
//Bei allen, die kein Tattoostudio sind
$this->subject = 'Partner - Firma (Vorname Nachname)';
}
}
public function build()
{
$title = false;
$copy1line = false;
$filename = Invoice::getFilename($this->shopping_order);
$path = Invoice::getDownloadPath($this->shopping_order);
if (!Storage::disk('public')->exists($path)) {
return;
}
$file = Storage::disk('public')->path($path);
$mime = Storage::disk('public')->mimeType($path);
return $this->view('emails.blank')->with([
'title' => $title,
'copy1line' => $copy1line,
])->attach($file,[
'as' => $filename,
'mime' => $mime,
]); // attach file;
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Services\Util;
class PaymentReminderEmail extends Mailable
{
use Queueable, SerializesModels;
protected $emailSubject;
protected $message;
protected $order;
public $data;
/**
* Erstellt eine neue PaymentReminderEmail Instanz
*
* @param string $subject
* @param string $message
* @param object|null $user
* @param object|null $order
* @param float|null $paymentAmount
* @param string|null $dueDate
* @param string|null $paymentUrl
*/
public function __construct($subject, $message, $order = null)
{
$this->emailSubject = $subject;
$this->message = $message;
$this->order = $order;
}
/**
* Baut die E-Mail
*
* @return $this
*/
public function build()
{
$paymentUrl = route('user_myorder_detail', ['id' => $this->order->id]);
$buttonText = __('email.my_orders') ?: 'Meine Bestellungen';
return $this->subject($this->emailSubject)
->view('emails.payment_reminder')
->with([
'content' => $this->message,
'url' => $paymentUrl,
'button' => $buttonText,
]);
}
}

View file

@ -0,0 +1,57 @@
<?php
/**
* Created by Reliese Model.
*/
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
/**
* Class PaymentReminder
*
* @property int $id
* @property string|null $title
* @property int|null $interval
* @property string|null $message
* @property string|null $action
* @property bool $active
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
*
* @package App\Models
*/
class PaymentReminder extends Model
{
protected $table = 'payment_reminders';
protected $casts = [
'interval' => 'int',
'active' => 'bool'
];
protected $fillable = [
'title',
'subject',
'interval',
'message',
'action',
'clearingtype',
'active'
];
protected static $clearingtypes = [
'fnc' => 'Rechnung',
'vor' => 'Vorkasse',
];
public function getClearingtype(){
return isset(self::$clearingtypes[$this->clearingtype]) ? self::$clearingtypes[$this->clearingtype] : 'Kein Typ';
}
public static function returnClearingtypes(){
return self::$clearingtypes;
}
}

View file

@ -105,6 +105,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Eloquent\Builder|ShoppingOrder whereShippingOption($value)
* @property array|null $delivery
* @method static \Illuminate\Database\Eloquent\Builder|ShoppingOrder whereDelivery($value)
* @property bool $user_white_label
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShoppingOrder whereUserWhiteLabel($value)
* @mixin \Eloquent
*/
class ShoppingOrder extends Model
@ -138,6 +140,7 @@ class ShoppingOrder extends Model
'paid',
'invoice',
'delivery',
'user_white_label',
'invoice_number',
'txaction',
'wp_invoice_path',
@ -146,7 +149,7 @@ class ShoppingOrder extends Model
'shipped',
'shipped_at',
'shipping_option',
'tracking'
'tracking',
];
protected $casts = [
@ -154,6 +157,7 @@ class ShoppingOrder extends Model
'invoice' => 'array',
'delivery' => 'array',
'shipped_at' => 'datetime',
'user_white_label' => 'boolean',
];
public static $shippedTypes = [
@ -161,6 +165,7 @@ class ShoppingOrder extends Model
1 => 'in Bearbeitung',
2 => 'versendet',
3 => 'abgeschlossen',
5 => 'Wartestellung',
4 => 'Abholung',
10 => 'storniert'
];
@ -213,6 +218,7 @@ class ShoppingOrder extends Model
2 => 'success',
3 => 'secondary',
4 => 'success',
5 => 'warning-dark',
10 => 'danger',
];

View file

@ -58,6 +58,14 @@ use Illuminate\Database\Eloquent\Model;
* @method static \Illuminate\Database\Eloquent\Builder|ShoppingOrderMargin wherePartnerCommissionPendingTo($value)
* @property int|null $user_credit_id
* @method static \Illuminate\Database\Eloquent\Builder|ShoppingOrderMargin whereUserCreditId($value)
* @property bool $order_paid
* @property bool $out_paid
* @property \Illuminate\Support\Carbon|null $margin_pending_to
* @property bool|null $margin_paid
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShoppingOrderMargin whereMarginPaid($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShoppingOrderMargin whereMarginPendingTo($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShoppingOrderMargin whereOrderPaid($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|ShoppingOrderMargin whereOutPaid($value)
* @mixin \Eloquent
*/
class ShoppingOrderMargin extends Model
@ -113,7 +121,12 @@ class ShoppingOrderMargin extends Model
public static $statusTypes = [
0 => 'user order',
1 => '',
1 => 'Registrierung',
2 => 'Mitgliedschaft',
3 => 'Guthabenaufladung',
4 => 'VP.Bestellung Abholung',
5 => 'VP.Bestellung Lieferung',
6 => 'VP.Kundenbestellung',
7 => 'from promotion',
8 => 'from shop',
9 => 'storniert'

View file

@ -54,9 +54,18 @@ class ShoppingPayment extends Model
'reference',
'amount',
'currency',
'status',
'reminder',
'reminder_date',
'txaction',
'mode'
];
protected $casts = [
'reminder' => 'integer',
'reminder_date' => 'datetime',
];
public function shopping_order()
{
@ -91,6 +100,7 @@ class ShoppingPayment extends Model
if($this->clearingtype === 'non') {
return 'keine';
}
return 'keine';
}
public function getPaymentAmount(){

72
app/Policies/ModelPolicy.php Executable file
View file

@ -0,0 +1,72 @@
<?php
namespace App\Policies;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;
abstract class ModelPolicy
{
use HandlesAuthorization;
abstract protected function getModelClass(): string;
public function viewAny(User $user)
{
return $user->can('view-any-' . $this->getModelClass());
}
public function view(User $user, Model $model)
{
if ($user->can('view-' . $this->getModelClass())) {
return true;
}
if ($user->can('view-self-' . $this->getModelClass())) {
return $this->isOwner($user, $model);
}
return false;
}
public function create(User $user)
{
return $user->can('create-' . $this->getModelClass());
}
public function update(User $user, Model $model)
{
if ($user->can('update-' . $this->getModelClass())) {
return true;
}
if ($user->can('update-self-' . $this->getModelClass())) {
return $this->isOwner($user, $model);
}
return false;
}
public function delete(User $user, Model $model)
{
if ($user->can('delete-' . $this->getModelClass())) {
return true;
}
if ($user->can('delete-self-' . $this->getModelClass())) {
return $this->isOwner($user, $model);
}
return false;
}
private function isOwner(User $user, Model $model): bool
{
if (!empty($user) && method_exists($model, 'user')) {
return $user->getKey() === $model->getRelation('user')->getKey();
}
return false;
}
}

View file

@ -2,6 +2,7 @@
namespace App\Providers;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -13,7 +14,7 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
//
Schema::defaultStringLength(191);
}
/**
@ -23,6 +24,10 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
//
if ($this->app->environment() !== 'production') {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
// ...
}
}

View file

@ -6,7 +6,6 @@ use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
/**
@ -15,7 +14,7 @@ class AuthServiceProvider extends ServiceProvider
* @var array
*/
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
@ -26,7 +25,12 @@ class AuthServiceProvider extends ServiceProvider
public function boot()
{
$this->registerPolicies();
Passport::routes();
// Die neuere Passport-Konfiguration verwendet separate Methoden
// anstelle von Passport::routes()
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
//
}

View file

@ -133,7 +133,7 @@ class ContractPDFRepository extends BaseRepository {
$filename = "Vertriebspartnervertrag.pdf";
Storage::disk($this->disk)->put($this->dir.$filename, $pdf->Output('S'));
$size = Storage::disk($this->disk)->size($this->dir.$filename);
$mine = Storage::disk($this->disk)->getMimeType($this->dir.$filename);
$mine = Storage::disk($this->disk)->mimeType($this->dir.$filename);
File::create([
'user_id' => $this->model->id,

View file

@ -43,7 +43,7 @@ class CreditRepository extends BaseRepository {
if(!Storage::disk('public')->exists( $dir )){
Storage::disk('public')->makeDirectory($dir); //creates directory
}
$path = Storage::disk('public')->getAdapter()->getPathPrefix();
$path = Storage::disk('public')->path('');
$filename = Credit::makeCreditFilename($credit_number);
$pdf->save($path.$dir.$filename);
@ -87,7 +87,7 @@ class CreditRepository extends BaseRepository {
private function finishUserCredit($user_credit){
//next number
Credit::makeNextCreditNumber();
//Partner Provision
//Partner Provision shopping order margin
$ShoppingOrderMargins = UserMarign::getPartnerCommissionItems($this->model->id, false);
foreach($ShoppingOrderMargins as $ShoppingOrderMargin){
$ShoppingOrderMargin->partner_commission_paid = true;
@ -96,7 +96,7 @@ class CreditRepository extends BaseRepository {
$ShoppingOrderMargin->save();
}
//Shop Provision
//Shop Provision shopping order margin
$ShoppingOrderMargins = UserMarign::getShopCommissionItems($this->model->id, false);
foreach($ShoppingOrderMargins as $ShoppingOrderMargin){
$ShoppingOrderMargin->margin_paid = true;
@ -105,7 +105,7 @@ class CreditRepository extends BaseRepository {
$ShoppingOrderMargin->save();
}
//Hinzugefügte Provision
//Hinzugefügte Provision user credit margin
$UserCreditMargins = UserMarign::getUserCreditMarginByUserID($this->model->id);
foreach($UserCreditMargins as $UserCreditMargin){
$UserCreditMargin->paid = true; //wurde ausgezahlt

View file

@ -22,6 +22,7 @@ class InvoiceRepository extends BaseRepository {
private $delivery_dir;
private $delivery_filename;
public function __construct(ShoppingOrder $model)
{
$this->model = $model;
@ -130,7 +131,7 @@ class InvoiceRepository extends BaseRepository {
if(!Storage::disk('public')->exists( $this->delivery_dir )){
Storage::disk('public')->makeDirectory($this->delivery_dir); //creates directory
}
$path = Storage::disk('public')->getAdapter()->getPathPrefix();
$path = Storage::disk('public')->path('');
$pdf_file = new InvoicePDF('pdf.invoice');
$pdf_file->create($data, $this->filename, 'save', $path.$this->dir);
@ -176,6 +177,10 @@ class InvoiceRepository extends BaseRepository {
foreach($whitelabel_image->attributes as $attribute){
if(in_array($attribute, $label->attributes)){
//found and overwrite
if(!$this->model->user_white_label){
$this->model->user_white_label = true;
$this->model->save();
}
$labels[$key] = $whitelabel_image;
}
}
@ -184,7 +189,6 @@ class InvoiceRepository extends BaseRepository {
}
}
}
foreach($labels as $key=>$label){
//label hat attribue
$varinats = [];

View file

@ -2,6 +2,7 @@
namespace App\Services;
use App\Mail\MailInvoice;
use App\Mail\MailLogistic;
use App\Services\Util;
use App\Models\Setting;
use App\Models\ShoppingOrder;
@ -118,4 +119,9 @@ class Invoice
}
Mail::to($billing_email)->bcc($bcc)->send(new MailInvoice($shopping_order));
}
public static function sendLogisticMail(ShoppingOrder $shopping_order){
$to = [config('app.logistic_mail')]; //['versand@aloe-vera.bio'];
Mail::to($to)->send(new MailLogistic($shopping_order));
}
}

View file

@ -51,7 +51,7 @@ class PDFMerger {
* Construct and initialize a new instance
* @param Filesystem $oFilesystem
*/
public function __construct(Filesystem $oFilesystem = null){
public function __construct($oFilesystem = null){
$this->oFilesystem = $oFilesystem;
$this->oFPDI = new FPDI();
$this->tmpFiles = collect([]);

View file

@ -4,7 +4,6 @@ namespace App\Services;
use App\User;
use App\Models\Product;
use App\Models\Setting;
use App\Mail\MailCheckout;
use App\Models\ProductBuy;
use App\Models\ShoppingOrder;
@ -19,15 +18,16 @@ class Payment
public static $txaction_text = [
'paid' => "bezahlt",
'appointed' => "offen",
'open' => "offen",
'appointed' => "offen (appointed)",
'failed' => "abbruch",
'extern' => "extern",
'open' => "offen",
'invoice_open' => "Re. offen",
'invoice_paid' => "Re. bezahlt",
'invoice_non' => "Re. keine Zahlung",
'non' => "keine Zahlung",
'non' => "keine Zahlung (non)",
'NULL' => 'keine Zahlung',
'prev' => "keine Zahlung (prev)",
];
public static $txaction_invoice = [
@ -56,7 +56,7 @@ class Payment
'invoice_open' => "warning",
'invoice_paid' => "success",
'invoice_non' => "danger",
'prev' => "warning",
];
@ -360,26 +360,60 @@ class Payment
}
public static function paymentStatusSendMail(ShoppingOrder $shopping_order, $shopping_payment, $data){
$bcc = [];
$billing_email = $shopping_order->shopping_user->billing_email;
if(!$billing_email){
if($data['mode'] === 'test'){
$billing_email = config('app.checkout_test_mail');
}else{
$billing_email = config('app.checkout_mail');
public static function paymentStatusSendMail(ShoppingOrder $shopping_order, $shopping_payment, $data)
{
$billing_email = self::determineBillingEmail($shopping_order, $data);
$bcc = self::determineBccRecipients($shopping_order, $data);
try {
Mail::to($billing_email)
->bcc($bcc)
->send(new MailCheckout(
$data['txaction'],
$shopping_order,
$shopping_payment,
$data['send_link'],
$data['mode']
));
} catch (\Exception $e) {
\Log::error('Fehler beim E-Mail-Versand: ' . $e->getMessage());
}
}
if($data['mode'] === 'test'){
$bcc[] = config('app.checkout_test_mail');
}else{
$bcc[] = config('app.checkout_mail');
}
if(!$shopping_order->shopping_user->is_like && $shopping_order->shopping_user->member){
private static function determineBillingEmail($shopping_order, $data)
{
if (Util::isTestSystem()) {
return config('app.checkout_test_mail');
}
$billing_email = $shopping_order->shopping_user->billing_email;
if (!$billing_email) {
return $data['mode'] === 'test'
? config('app.checkout_test_mail')
: config('app.checkout_mail');
}
return $billing_email;
}
private static function determineBccRecipients($shopping_order, $data)
{
$bcc = [];
// Add checkout email to BCC
$bcc[] = $data['mode'] === 'test'
? config('app.checkout_test_mail')
: config('app.checkout_mail');
// Add member email to BCC if applicable
if ($data['mode'] !== 'test'
&& !Util::isTestSystem()
&& !$shopping_order->shopping_user->is_like
&& $shopping_order->shopping_user->member
) {
$bcc[] = $shopping_order->shopping_user->member->email;
}
Mail::to($billing_email)->bcc($bcc)->send(new MailCheckout($data['txaction'], $shopping_order, $shopping_payment, $data['send_link'], $data['mode']));
return $bcc;
}
}

View file

@ -1,269 +0,0 @@
<?php
namespace App\Services\Stats;
use Carbon\Carbon;
use App\Services\Util;
use App\Models\ShoppingOrder;
class Sales
{
private $month;
private $year;
private $products;
private $objects;
public function __construct()
{
$this->month = 0;
$this->year = 0;
$this->products = [];
$this->objects = [];
}
public function setFilterVars($month = null, $year = null, $products = null){
$this->month = $month ? $month : intval(date('m'));
$this->year = $year ? $year : intval(date('Y'));
$this->products = $products;
}
public function setFilterProducts(){
$ShoppingOrders = $this->getShoppingOrdersBy($this->month, $this->year);
$products = [];
foreach($ShoppingOrders as $ShoppingOrder){
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product && !$shopping_order_item->product->exclude_stats_sales && !isset($products[$shopping_order_item->product->id])){
$products[$shopping_order_item->product->id] = $shopping_order_item->product->name.' # '.
($shopping_order_item->product->single_commission ? $shopping_order_item->product->value_commission.' / '.$shopping_order_item->product->partner_commission : 'Staffelrabatt');
}
}
}
return $products;
}
private function getShoppingOrdersBy($month, $year){
if($month == '13'){ //all the year
$date_start = Carbon::parse('01.01.'.$year)->format('Y-m-d H:i:s');
$date_end = Carbon::parse('31.12.'.$year)->endOfMonth()->format('Y-m-d H:i:s');
}else{
$date_start = Carbon::parse('01.'.$month.'.'.$year)->format('Y-m-d H:i:s');
$date_end = Carbon::parse('01.'.$month.'.'.$year)->endOfMonth()->format('Y-m-d H:i:s');
}
return ShoppingOrder::where('paid', 1)->where('mode', 'live')->whereBetween('created_at', [$date_start, $date_end])->get();
}
public function getCollection(){
$this->getObjects();
$collection = collect();
foreach($this->objects as $key => $obj){
$collection->push([
'id' => $key,
'name' => $obj['name'],
'number' => $obj['number'],
'qty' => $obj['qty'],
'total' => $obj['total'],
'pre_qty' => $obj['pre_qty'],
'pre_total' => $obj['pre_total'],
'single_commission' => $obj['single_commission'],
'value_commission' => $obj['value_commission'],
'partner_commission' => $obj['partner_commission'],
]);
}
return $collection;
}
public function getObjects(){
$this->readObjects();
$this->readObjectsPreview();
return $this->objects;
}
private function readObjects()
{
$shoppingOrders = $this->getShoppingOrdersBy($this->month, $this->year);
$this->objects = [];
$subtotal_full = 0; // gesamtumsatz
$subtotal = 0; // gesamtumsatz ohne rabatte
$discount = 0; // gesamtrabatte
$subtotal_hide = 0; // ausgeschlossene Produkte
foreach($shoppingOrders as $ShoppingOrder){
$subtotal_full += $ShoppingOrder->subtotal_full;
$subtotal += $ShoppingOrder->subtotal;
$discount += $ShoppingOrder->discount;
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product){
if(!in_array($shopping_order_item->product->id, $this->products) && !$shopping_order_item->product->exclude_stats_sales){ //ausschließen der Produkte über filter und exclude_stats_sales
if(isset($this->objects[$shopping_order_item->product->id])){
$qty = intval($this->objects[$shopping_order_item->product->id]['qty'] + $shopping_order_item->qty);
$total = round($this->objects[$shopping_order_item->product->id]['total'] + ($shopping_order_item->price_net * $shopping_order_item->qty), 3);
$this->objects[$shopping_order_item->product->id]['qty'] = $qty;
$this->objects[$shopping_order_item->product->id]['total'] = $total;
}else{
$this->objects[$shopping_order_item->product->id] = [
'name' => $shopping_order_item->product->name,
'number' => $shopping_order_item->product->number,
'qty' => $shopping_order_item->qty,
'total' => round($shopping_order_item->price_net * $shopping_order_item->qty, 3),
'pre_qty' => 0,
'pre_total' => 0,
'single_commission' => $shopping_order_item->product->single_commission ? 'Ja' : 'Nein',
'value_commission' => $shopping_order_item->product->single_commission ? $shopping_order_item->product->value_commission : '',
'partner_commission' => $shopping_order_item->product->single_commission ? $shopping_order_item->product->partner_commission : '',
];
}
}else{
$subtotal_hide += $shopping_order_item->price_net * $shopping_order_item->qty;
}
}
}
}
$this->objects[9990] = [
'name' => 'Angezeigter Umsatz netto €',
'number' => '',
'qty' => '',
'total' => round($subtotal_full - $subtotal_hide, 2),
'pre_qty' => 0,
'pre_total' => 0,
'single_commission' => '',
'value_commission' => '',
'partner_commission' => '',
];
$this->objects[9991] = [
'name' => 'Ausgeblendeter Umsatz netto €',
'number' => '',
'qty' => '',
'total' => $subtotal_hide,
'pre_qty' => 0,
'pre_total' => 0,
'single_commission' => '',
'value_commission' => '',
'partner_commission' => '',
];
$this->objects[9992] = [
'name' => 'Gesamter Umsatz netto € (alle Verkäufe)',
'number' => '',
'qty' => '',
'total' => $subtotal_full,
'pre_qty' => 0,
'pre_total' => 0,
'single_commission' => '',
'value_commission' => '',
'partner_commission' => '',
];
$this->objects[9998] = [
'name' => 'Gesamte Rabatte netto € (alle Verkäufe)',
'number' => '',
'qty' => '',
'total' => ($discount),
'pre_qty' => 0,
'pre_total' => 0,
'single_commission' => '',
'value_commission' => '',
'partner_commission' => '',
];
$this->objects[9999] = [
'name' => 'Gesamt netto € (alle Verkäufe)',
'number' => '',
'qty' => '',
'total' => ($subtotal),
'pre_qty' => 0,
'pre_total' => 0,
'single_commission' => '',
'value_commission' => '',
'partner_commission' => '',
];
//format total
foreach($this->objects as $key => $obj){
$this->objects[$key]['total'] = formatNumber($obj['total']);
}
}
private function readObjectsPreview(){
$shoppingOrders = $this->getShoppingOrdersBy($this->month, $this->year-1);
$subtotal_full = 0; // gesamtumsatz
$subtotal = 0; // gesamtumsatz ohne rabatte
$discount = 0; // gesamtrabatte
$subtotal_hide = 0; // ausgeschlossene Produkte
foreach($shoppingOrders as $ShoppingOrder){
$subtotal_full += $ShoppingOrder->subtotal_full;
$subtotal += $ShoppingOrder->subtotal;
$discount += $ShoppingOrder->discount;
foreach($ShoppingOrder->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product){
if(!in_array($shopping_order_item->product->id, $this->products) && !$shopping_order_item->product->exclude_stats_sales){ //ausschließen der Produkte über filter und exclude_stats_sales
if(isset($this->objects[$shopping_order_item->product->id])){ //einsetzen der Zahlen, wenn vorhanden
$qty = intval($this->objects[$shopping_order_item->product->id]['pre_qty'] + $shopping_order_item->qty);
$total = round($this->objects[$shopping_order_item->product->id]['pre_total'] + ($shopping_order_item->price_net * $shopping_order_item->qty), 3);
$this->objects[$shopping_order_item->product->id]['pre_qty'] = $qty;
$this->objects[$shopping_order_item->product->id]['pre_total'] = $total;
}else{ // nicht vorhanden, anlegen
$this->objects[$shopping_order_item->product->id] = [
'name' => $shopping_order_item->product->name,
'number' => $shopping_order_item->product->number,
'qty' => 0,
'total' => 0,
'pre_qty' => $shopping_order_item->qty,
'pre_total' => round($shopping_order_item->price_net * $shopping_order_item->qty, 3),
'single_commission' => $shopping_order_item->product->single_commission ? 'Ja' : 'Nein',
'value_commission' => $shopping_order_item->product->single_commission ? $shopping_order_item->product->value_commission : '',
'partner_commission' => $shopping_order_item->product->single_commission ? $shopping_order_item->product->partner_commission : '',
];
}
}else{
//ausgeschlossene Produkte
$subtotal_hide += $shopping_order_item->price_net * $shopping_order_item->qty;
}
}
}
}
$this->objects[9990]['pre_total'] = round($subtotal_full - $subtotal_hide, 2);
$this->objects[9991]['pre_total'] = $subtotal_hide;
$this->objects[9992]['pre_total'] = $subtotal_full;
$this->objects[9998]['pre_total'] = ($discount);
$this->objects[9999]['pre_total'] = ($subtotal);
//format total
foreach($this->objects as $key => $obj){
$this->objects[$key]['pre_total'] = formatNumber($obj['pre_total']);
}
}
}

View file

@ -30,6 +30,7 @@ class UserBot
//user die manuelle Gutschriften haben
$usersWithCreditMargin = $this->getUsersWithCreditMargin();
//user die Shop Provisionen haben
$usersWithShopCommission = $this->getUsersWithShopCommission(false);
@ -181,9 +182,9 @@ class UserBot
$entry->badge = \App\Services\Payment::getPaymentForTypeBadge($shoppingOrderMargin->shopping_order);
if ($shoppingOrderMargin->shopping_order->payment_for === 7 || $shoppingOrderMargin->shopping_order->payment_for === 8) {
$entry->link = route('admin_sales_customers_detail', [$shoppingOrderMargin->shopping_order->id]);
$entry->link = route('admin_sales_detail', [$shoppingOrderMargin->shopping_order->id]);
} else {
$entry->link = route('admin_sales_users_detail', [$shoppingOrderMargin->shopping_order->id]);
$entry->link = route('admin_sales_detail', [$shoppingOrderMargin->shopping_order->id]);
}
$entry->name = $shoppingOrderMargin->shopping_order->shopping_user->billing_firstname . " " .
@ -258,7 +259,8 @@ class UserBot
->whereOutPaid(false)
->whereCancellation(false)
->whereMarginPaid(false)
->whereNotNull('margin_pending_to');
->whereNotNull('margin_pending_to')
->whereIn('status', [7,8]);
if ($isPending) {
$query->where('margin_pending_to', '>=', Carbon::now());

View file

@ -0,0 +1,13 @@
<?php
namespace App\Services;
class PaymentHelper
{
public static $txaction_art = [
'user_order' => "Vertriebspartner",
'customer_order' => "Kundenbestellung",
'user_for_customer' => "VP.Kundenbestellung",
];
}

View file

@ -0,0 +1,453 @@
<?php
namespace App\Services;
use App\Models\Logger;
use App\Models\PaymentReminder;
use App\Models\ShoppingPayment;
use App\Mail\PaymentReminderEmail;
use App\Services\Util;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
class PaymentReminderService
{
protected $clearingtypes = [];
public function __construct()
{
$this->clearingtypes = PaymentReminder::returnClearingtypes();
}
/**
* Erstellt einen Log-Eintrag für Payment Reminder Aktivitäten
*/
private function createLog($action, $message, $model = null, $modelId = null, $level = 2)
{
return Logger::create([
'user_id' => null, // System-Aktion
'model_id' => $modelId,
'model' => $model,
'action' => $action,
'channel' => 'payment_reminder',
'message' => $message,
'level' => $level
]);
}
/**
* Hole alle aktiven Intervalle für Zahlungserinnerungen
*/
public function getActiveIntervals()
{
$intervals = [];
$payment_reminders = PaymentReminder::where('active', true)->get();
foreach($payment_reminders as $reminder) {
$intervals[$reminder->clearingtype] = $reminder->interval;
}
return $intervals;
}
/**
* Hole alle offenen Zahlungen für einen bestimmten clearingtype
*/
public function getOpenPaymentsForClearingType($clearingtype, $interval)
{
$date = Carbon::now()->subDays($interval);
$payments = ShoppingPayment::join('shopping_orders', 'shopping_payments.shopping_order_id', '=', 'shopping_orders.id')
->where('shopping_payments.clearingtype', '=', $clearingtype)
->where('shopping_payments.txaction', '=', 'open')
->where('shopping_payments.mode', '=', 'live')
->where('shopping_payments.created_at', '<', $date)
->where('shopping_payments.amount', '>', 0)
->whereNull('shopping_orders.deleted_at')
->whereIn('shopping_payments.id', function($query) use ($clearingtype, $date) {
$query->selectRaw('MAX(shopping_payments.id)')
->from('shopping_payments')
->join('shopping_orders', 'shopping_payments.shopping_order_id', '=', 'shopping_orders.id')
->where('shopping_payments.clearingtype', '=', $clearingtype)
->where('shopping_payments.txaction', '=', 'open')
->where('shopping_payments.mode', '=', 'live')
->where('shopping_payments.created_at', '<', $date)
->where('shopping_payments.amount', '>', 0)
->whereNull('shopping_orders.deleted_at')
->groupBy('shopping_payments.shopping_order_id');
})
->select('shopping_payments.*')
->get();
return $payments;
}
/**
* Hole alle offenen Zahlungen für alle clearingtypes
*/
public function getAllOpenPayments()
{
$intervals = $this->getActiveIntervals();
$results = [];
foreach($intervals as $clearingtype => $interval){
$date = Carbon::now()->subDays($interval);
$payments = $this->getOpenPaymentsForClearingType($clearingtype, $interval);
$results[$clearingtype] = [
'interval' => $interval,
'date_limit' => $date,
'payments' => $payments,
'count' => $payments->count()
];
}
return $results;
}
/**
* Hole detaillierte Daten für Tabellen-Ansicht
*/
public function getDetailedPaymentsData()
{
$intervals = $this->getActiveIntervals();
$detailedData = [];
foreach($intervals as $clearingtype => $interval){
$date = Carbon::now()->subDays($interval);
$payments = $this->getOpenPaymentsForClearingType($clearingtype, $interval);
foreach($payments as $payment){
$name = !isset($payment->shopping_order->shopping_user) ? 'Kein Name' : $payment->shopping_order->shopping_user->billing_firstname.' '.$payment->shopping_order->shopping_user->billing_lastname;
$email = !isset($payment->shopping_order->shopping_user) ? 'Keine Email' : $payment->shopping_order->shopping_user->billing_email;
$shipped = '<span class="badge badge-pill badge-'.$payment->shopping_order->getShippedColor().'">'.$payment->shopping_order->getShippedType().'</span>';
// Countdown für nächste Erinnerung berechnen
$countdown = $this->getNextReminderCountdown($payment);
$detailedData[] = [
'clearingtype' => $clearingtype,
'clearingtype_name' => $this->getClearingtype($clearingtype),
'interval_days' => $interval,
'date_limit' => $date->format('d.m.Y H:i:s'),
'order_id' => $payment->shopping_order_id,
'payment_id' => $payment->id,
'amount' => $payment->amount,
'created_at' => $payment->created_at->format('d.m.Y H:i:s'),
'days_old' => $payment->created_at->diffInDays(now()),
'payment' => $payment, // Vollständiges Payment-Objekt für weitere Verarbeitung
'name' => $name,
'email' => $email,
'shipped' => $shipped,
'reminder' => $payment->reminder,
'reminder_date' => $payment->reminder_date ? $payment->reminder_date->format('d.m.Y H:i:s') : null,
'countdown' => $countdown,
];
}
}
return $detailedData;
}
/**
* Sende die nächste Zahlungserinnerung
* noch kein reminder gesendet = 1. Zahlungserinnerung
* reminder > 0 die nächste zahlungserinnerung aus der liste holen
*/
public function sendReminder($payment)
{
//holen der nächsten zahlungserinnerung
$payment_reminder = $this->getReminder((int) $payment->reminder, $payment->clearingtype);
if(!$payment_reminder){
return false;
}
//zahlungserinnerung Platzhalter ersetzen.
$payment_reminder = $this->replacePlaceholder($payment, $payment_reminder);
//zahlungserinnerung senden
$emailSent = $this->sendReminderEmail($payment, $payment_reminder);
if ($emailSent) {
$this->createLog(
'email_sent',
"Zahlungserinnerung E-Mail gesendet an: {$payment->shopping_order->shopping_user->billing_email}, Subject: {$payment_reminder->subject}",
'ShoppingOrder',
$payment->shopping_order_id,
3
);
}
//action ausführen
if($payment_reminder->action === 'set_order_status_cancelled'){
$this->setNoNPayment($payment);
$payment->shopping_order->shipped = 10;
$payment->shopping_order->save();
$this->createLog(
'action_completed',
"Action abgeschlossen: Bestellung auf 'Storniert' gesetzt, Payment auf 'non' gesetzt",
'ShoppingOrder',
$payment->shopping_order_id,
3
);
}
//reminder setzen +1
$payment->reminder = (int) $payment->reminder + 1;
$payment->reminder_date = Carbon::now();
$payment->save();
$this->createLog(
'reminder_completed',
"Zahlungserinnerung für Payment ID: {$payment->id}, Order ID: {$payment->shopping_order_id}",
'ShoppingOrder',
$payment->shopping_order_id,
4
);
return true;
}
public function setNoNPayment($payment)
{
$this->createLog(
'set_non_payment',
"Setze Payment ID: {$payment->id} auf 'non' Status",
'ShoppingOrder',
$payment->shopping_order_id,
4
);
PaymentService::updateTransactionStatus($payment->shopping_order_id, 'non', $payment->id);
}
public function getClearingtype($clearingtype)
{
return isset($this->clearingtypes[$clearingtype]) ? $this->clearingtypes[$clearingtype] : $clearingtype;
}
public function getReminder($reminder, $clearingtype)
{
$payment_reminders = PaymentReminder::where('active', true)
->where('clearingtype', $clearingtype)
->orderBy('interval', 'asc')
->get();
if($payment_reminders->isEmpty()) {
return false;
}
// Wenn reminder größer ist als Anzahl der Erinnerungen
if($reminder >= $payment_reminders->count()) {
return false;
}
// Hole die Erinnerung an Position $reminder (0,1,2,3...)
return $payment_reminders[$reminder];
}
public function replacePlaceholder($payment, $payment_reminder)
{
$shopping_order = $payment->shopping_order;
$shopping_user = $shopping_order->shopping_user;
$replacements = [
'{billing_first_name}' => $shopping_user->billing_firstname,
'{billing_last_name}' => $shopping_user->billing_lastname,
'{order_number}' => '<b>'.$shopping_order->getLastShoppingPayment('reference').'</b>',
'{order_date}' => '<b>'.$shopping_order->created_at->format('d.m.Y').'</b>',
'{order_total}' => '<b>'.$shopping_order->getFormattedTotalShipping().'</b>'
];
$payment_reminder->subject = str_replace(
array_keys($replacements),
array_values($replacements),
$payment_reminder->subject
);
$payment_reminder->message = str_replace(
array_keys($replacements),
array_values($replacements),
$payment_reminder->message
);
return $payment_reminder;
}
public function sendReminderEmail($payment, $payment_reminder)
{
try {
$email = $payment->shopping_order->shopping_user->billing_email;
$subject = $payment_reminder->subject;
$message = $payment_reminder->message;
if(Util::isTestSystem()){
$email = config('app.checkout_test_mail');
}
if($payment->shopping_order->mode === 'test' || Util::isTestSystem()){
$bcc[] = config('app.checkout_test_mail');
}else{
$bcc[] = config('app.checkout_mail');
}
Mail::to($email)->bcc($bcc)->send(new PaymentReminderEmail($subject, $message, $payment->shopping_order));
return true;
} catch (\Exception $e) {
\Log::error('Fehler beim E-Mail-Versand: ' . $e->getMessage());
$this->createLog(
'email_exception',
"E-Mail Exception: " . $e->getMessage() . " für Payment ID: {$payment->id}",
'ShoppingOrder',
$payment->shopping_order_id,
5
);
return false;
}
}
/**
* Berechnet den Countdown bis zur nächsten Zahlungserinnerung
*/
public function getNextReminderCountdown($payment)
{
// Wenn noch keine Erinnerung gesendet wurde
if ($payment->reminder == 0) {
return null;
}
// Hole alle aktiven Erinnerungen für diesen Clearingtype
$payment_reminders = PaymentReminder::where('active', true)
->where('clearingtype', $payment->clearingtype)
->orderBy('interval', 'asc')
->get();
if ($payment_reminders->isEmpty()) {
return null;
}
// Wenn alle Erinnerungen bereits gesendet wurden
if ($payment->reminder >= $payment_reminders->count()) {
return [
'type' => 'completed',
'message' => 'Alle Erinnerungen gesendet',
'days_left' => 0
];
}
// Hole die nächste Erinnerung
$next_reminder = $payment_reminders[$payment->reminder];
// Berechne die Differenz zwischen aktuellem und nächstem Reminder
$current_reminder = $payment_reminders[$payment->reminder - 1];
$interval_difference = $next_reminder->interval - $current_reminder->interval;
// Berechne das Datum der nächsten Erinnerung
$next_reminder_date = Carbon::parse($payment->reminder_date)->addDays($interval_difference);
// Berechne die verbleibenden Tage
$days_left = Carbon::now()->diffInDays($next_reminder_date, false);
// Wenn die nächste Erinnerung bereits fällig ist
if ($days_left <= 0) {
return [
'type' => 'overdue',
'message' => 'Nächste Erinnerung fällig',
'days_left' => 0,
'next_reminder_date' => $next_reminder_date
];
}
return [
'type' => 'countdown',
'message' => 'Nächste Erinnerung in ' . $days_left . ' Tagen',
'days_left' => $days_left,
'next_reminder_date' => $next_reminder_date,
'next_reminder_interval' => $interval_difference
];
}
/**
* Hole alle Logs für Payment Reminder
*/
public function getPaymentReminderLogs($limit = 100, $paymentId = null, $action = null)
{
$query = Logger::where('channel', 'payment_reminder')
->orderBy('created_at', 'desc');
if ($paymentId) {
$query->where('model_id', $paymentId);
}
if ($action) {
$query->where('action', $action);
}
return $query->limit($limit)->get();
}
/**
* Hole Logs für einen spezifischen Payment
*/
public function getLogsForPayment($orderId)
{
return Logger::where('channel', 'payment_reminder')
->where('model_id', $orderId)
->orderBy('created_at', 'desc')
->get();
}
/**
* Hole Logs für einen spezifischen Zeitraum
*/
public function getLogsForDateRange($startDate, $endDate)
{
return Logger::where('channel', 'payment_reminder')
->whereBetween('created_at', [$startDate, $endDate])
->orderBy('created_at', 'desc')
->get();
}
/**
* Hole Statistiken für Payment Reminder Logs
*/
public function getLogStatistics($days = 30)
{
$startDate = Carbon::now()->subDays($days);
$stats = Logger::where('channel', 'payment_reminder')
->where('created_at', '>=', $startDate)
->selectRaw('action, level, COUNT(*) as count')
->groupBy('action', 'level')
->get();
$summary = [
'total_logs' => Logger::where('channel', 'payment_reminder')
->where('created_at', '>=', $startDate)
->count(),
'emails_sent' => Logger::where('channel', 'payment_reminder')
->where('action', 'email_sent')
->where('created_at', '>=', $startDate)
->count(),
'emails_failed' => Logger::where('channel', 'payment_reminder')
->where('action', 'email_exception')
->where('created_at', '>=', $startDate)
->count(),
'reminders_completed' => Logger::where('channel', 'payment_reminder')
->where('action', 'reminder_completed')
->where('created_at', '>=', $startDate)
->count(),
'actions_executed' => Logger::where('channel', 'payment_reminder')
->where('action', 'action_completed')
->where('created_at', '>=', $startDate)
->count(),
];
return [
'summary' => $summary,
'detailed_stats' => $stats
];
}
}

View file

@ -0,0 +1,201 @@
<?php
namespace App\Services;
use App\Models\ShoppingOrder;
use App\Models\ShoppingPayment;
use App\Models\UserPayCredit;
use App\Models\PaymentTransaction;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class PaymentService
{
public static $txaction_art = [
'user_order' => "Vertriebspartner",
'customer_order' => "Kundenbestellung",
'user_for_customer' => "VP.Kundenbestellung",
];
// Konstanten für bessere Wartbarkeit
const CREDIT_STATUS_DEDUCTION = 2;
const CREDIT_STATUS_RETURN = 4;
const CREDIT_STATUS_CHARGED = 7;
const CREDIT_STATUS_REMOVED = 8;
const TRANSACTION_REQUEST = 'transaction';
const DEFAULT_TXID = 0;
const DEFAULT_USERID = 0;
/**
* Aktualisiert den Transaktionsstatus einer Bestellung
*
* @param int $orderId
* @param string $txaction
* @param int $paymentId
* @return bool
* @throws \Exception
*/
public static function updateTransactionStatus($orderId, $txaction, $paymentId)
{
// Validierung der Eingabeparameter
if (empty($orderId) || empty($txaction) || empty($paymentId)) {
throw new \InvalidArgumentException('Alle Parameter müssen angegeben werden');
}
return DB::transaction(function () use ($orderId, $txaction, $paymentId) {
$shopping_order = ShoppingOrder::findOrFail($orderId);
$shopping_payment = ShoppingPayment::findOrFail($paymentId);
// Prüfen ob sich der Status tatsächlich geändert hat
if ($shopping_payment->txaction === $txaction) {
return false;
}
// Guthaben-Logik für Partner-Center Bestellungen
self::handlePartnerCenterCredits($shopping_order, $txaction);
// PaymentTransaction erstellen
$paymentTransaction = self::createPaymentTransaction($shopping_payment, $txaction);
// Bestellung und Payment aktualisieren
self::updateOrderAndPayment($shopping_order, $shopping_payment, $txaction, $paymentTransaction);
// Paid-Action ausführen falls nötig
self::handlePaidAction($paymentTransaction, $shopping_order);
// Credit-Loading Logik
self::handleCreditLoading($shopping_order, $txaction);
// E-Mail versenden
self::sendStatusEmail($shopping_order, $shopping_payment, $paymentTransaction);
return true;
});
}
/**
* Behandelt Guthaben-Logik für Partner-Center Bestellungen
*/
private static function handlePartnerCenterCredits(ShoppingOrder $shoppingOrder, string $txaction): void
{
if (!$shoppingOrder->shopping_order_margin || $shoppingOrder->shopping_order_margin->from_payment_credit <= 0) {
return;
}
$lastUserPayCredit = self::getLastUserPayCredit($shoppingOrder->id, [self::CREDIT_STATUS_DEDUCTION, self::CREDIT_STATUS_RETURN]);
if (!$lastUserPayCredit) {
return;
}
// Status Keine Zahlung, Guthaben zurückführen
if ($txaction === 'non' && $lastUserPayCredit->status === self::CREDIT_STATUS_DEDUCTION) {
Payment::handelUserPayCredits($shoppingOrder, 'return');
}
// Status Zahlung, vorher gab es eine Storno, Guthaben abziehen
if ($lastUserPayCredit->status === self::CREDIT_STATUS_RETURN && in_array($txaction, ['open', 'paid'])) {
Payment::handelUserPayCredits($shoppingOrder, 'deduction');
}
}
/**
* Erstellt eine neue PaymentTransaction
*/
private static function createPaymentTransaction(ShoppingPayment $shoppingPayment, string $txaction): PaymentTransaction
{
return PaymentTransaction::create([
'shopping_payment_id' => $shoppingPayment->id,
'request' => self::TRANSACTION_REQUEST,
'txid' => self::DEFAULT_TXID,
'userid' => self::DEFAULT_USERID,
'status' => $shoppingPayment->clearingtype,
'transmitted_data' => null,
'txaction' => $txaction,
'mode' => $shoppingPayment->mode,
]);
}
/**
* Aktualisiert Bestellung und Payment
*/
private static function updateOrderAndPayment(ShoppingOrder $shoppingOrder, ShoppingPayment $shoppingPayment, string $txaction, PaymentTransaction $paymentTransaction): void
{
$shoppingOrder->txaction = $txaction;
$shoppingOrder->paid = $paymentTransaction->txaction === 'paid';
$shoppingOrder->save();
$shoppingPayment->txaction = $txaction;
$shoppingPayment->save();
}
/**
* Führt Paid-Action aus falls nötig
*/
private static function handlePaidAction(PaymentTransaction $paymentTransaction, ShoppingOrder $shoppingOrder): void
{
if ($paymentTransaction->status === 'vor' && $paymentTransaction->txaction === 'paid') {
Payment::paymentStatusPaidAction($shoppingOrder, true);
}
}
/**
* Behandelt Credit-Loading Logik
*/
private static function handleCreditLoading(ShoppingOrder $shoppingOrder, string $txaction): void
{
if (!$shoppingOrder->shopping_user || $shoppingOrder->shopping_user->is_for !== 'cr') {
return;
}
$lastUserPayCredit = self::getLastUserPayCredit($shoppingOrder->id, [self::CREDIT_STATUS_CHARGED, self::CREDIT_STATUS_REMOVED]);
if (!$lastUserPayCredit) {
return;
}
// Status Keine Zahlung, Guthaben abziehen
if ($txaction === 'non' && $lastUserPayCredit->status === self::CREDIT_STATUS_CHARGED) {
Payment::handelUserPayChargingCredits($shoppingOrder, 'remove');
}
// Status Zahlung, vorher gab es eine Storno, Guthaben wieder aufladen
if ($lastUserPayCredit->status === self::CREDIT_STATUS_REMOVED && $txaction === 'paid') {
Payment::handelUserPayChargingCredits($shoppingOrder, 'add');
}
}
/**
* Sendet Status-E-Mail
*/
private static function sendStatusEmail(ShoppingOrder $shoppingOrder, ShoppingPayment $shoppingPayment, PaymentTransaction $paymentTransaction): void
{
$emailData = [
'mode' => $paymentTransaction->mode,
'txaction' => $paymentTransaction->txaction,
'send_link' => false,
];
try {
Payment::paymentStatusSendMail($shoppingOrder, $shoppingPayment, $emailData);
} catch (\Exception $e) {
Log::error('Fehler beim Senden der Status-E-Mail', [
'order_id' => $shoppingOrder->id,
'payment_id' => $shoppingPayment->id,
'error' => $e->getMessage()
]);
}
}
/**
* Holt den letzten UserPayCredit Eintrag
*/
private static function getLastUserPayCredit(int $orderId, array $statuses): ?UserPayCredit
{
return UserPayCredit::where('shopping_order_id', $orderId)
->whereIn('status', $statuses)
->orderBy('id', 'DESC')
->first();
}
}

View file

@ -44,7 +44,11 @@ class UserMarign
return $sum_net_amount;
}
public static function getMontlyAmount(User $user, $date = null, $format = false){
public static function getMontlyAmount($user, $date = null, $format = false){
if(!$user instanceof User){
return 0;
}
$now = $date ? Carbon::parse($date) : Carbon::now();
$startDay = $now->startOfMonth()->toDateString();
@ -202,7 +206,8 @@ class UserMarign
->whereOutPaid(false)
->whereCancellation(false)
->whereMarginPaid(false)
->whereNotNull('margin_pending_to');
->whereNotNull('margin_pending_to')
->whereIn('status', [7,8]);
if ($isPending) {
$query->where('margin_pending_to', '>=', Carbon::now());

View file

@ -3,6 +3,7 @@ namespace App\Services;
use Yard;
use App\User;
use Illuminate\Support\Str;
use App\Models\PromotionUser;
use App\Models\ShippingCountry;
@ -18,7 +19,7 @@ class UserService
public static function createConfirmationCode() {
$unique = false;
do{
$confirmation_code = str_random(30);
$confirmation_code = Str::random(30);
if(User::where('confirmation_code', '=', $confirmation_code)->count() == 0){
$unique = true;
}

View file

@ -13,7 +13,7 @@ class Util
public static function getToken()
{
return hash_hmac('sha256', str_random(40), config('app.key'));
return hash_hmac('sha256', Str::random(40), config('app.key'));
}
public static function uuidToken()
@ -127,7 +127,7 @@ class Util
return false;
}
public static function setUserHistoryValue($values = [], $identifier){
public static function setUserHistoryValue($values, $identifier){
if($user_history = self::getUserHistory($identifier)){
foreach ($values as $key=>$val){
$user_history->{$key} = $val;
@ -171,6 +171,13 @@ class Util
return false;
}
public static function isTestSystem(){
if(config('app.debug')){
return true;
}
return false;
}
public static function isPromotionUrl($debug = false){
if($debug && config('app.debug')){
return false;

View file

@ -8,49 +8,51 @@
],
"license": "MIT",
"require": {
"php": "^7.4|^8.0",
"cocur/slugify": "^4.6.0",
"askedio/laravel5-profanity-filter": "*",
"barryvdh/laravel-dompdf": "*",
"cviebrock/eloquent-sluggable": "*",
"doctrine/dbal": "*",
"fideloper/proxy": "^4.4",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"intervention/image": "^2.7",
"jenssegers/date": "*",
"laracasts/flash": "*",
"laravel/framework": "^8.12",
"laravel/helpers": "*",
"laravel/passport": "*",
"laravel/tinker": "^2.5",
"laravel/ui": "^3.4",
"laravelcollective/html": "*",
"php": "^8.2",
"alban/laravel-collective-spatie-html-parser": "^1.1.9",
"barryvdh/laravel-dompdf": "^2.2",
"cocur/slugify": "^4.5",
"cviebrock/eloquent-sluggable": "^11.0",
"doctrine/dbal": "^3.6.0|^4.0",
"guzzlehttp/guzzle": "^7.4",
"intervention/image": "^3",
"jenssegers/date": "^4.0",
"joedixon/laravel-translation": "2.x-dev",
"laracasts/flash": "^3.2",
"laravel/framework": "^11.0",
"laravel/passport": "^12.0",
"laravel/prompts": "^0.1.14",
"laravel/tinker": "^2.9",
"laravel/ui": "^4.2",
"maatwebsite/excel": "^3.1",
"reliese/laravel": "*",
"rguedes/pdfmerger": "^1.0",
"setasign/fpdf": "*",
"setasign/fpdi": "*",
"srmklive/paypal": "~3.0",
"yajra/laravel-datatables-oracle": "*"
"setasign/fpdf": "^1.8.6",
"setasign/fpdi": "^2.6",
"wearepixel/laravel-google-shopping-feed": "^4.0",
"yajra/laravel-datatables-oracle": "^11.0",
"srmklive/paypal": "~3.0"
},
"require-dev": {
"facade/ignition": "^2.5",
"fakerphp/faker": "^1.9.1",
"laravel/sail": "^1.0.1",
"mockery/mockery": "^1.4.2",
"nunomaduro/collision": "^5.0",
"phpunit/phpunit": "^9.3.3",
"barryvdh/laravel-debugbar": "*",
"barryvdh/laravel-ide-helper": "*"
},
"conflict": {
"cocur/slugify": "4.5.0"
"spatie/laravel-ignition": "^2.0",
"nunomaduro/collision": "^8.1",
"fakerphp/faker": "^1.23",
"laravel/pint": "^1.0",
"laravel/sail": "^1.26",
"mockery/mockery": "^1.6.2",
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-laravel": "^2.0",
"barryvdh/laravel-debugbar": "^3.13",
"barryvdh/laravel-ide-helper": "^3.0"
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"composer/installers": true
}
},
"extra": {
"laravel": {
@ -73,25 +75,35 @@
"Tests\\": "tests/"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi",
"@php artisan vendor:publish --force --tag=livewire:assets --ansi"
],
"post-update-cmd": [
"php artisan clear-compiled",
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
"php artisan ide-helper:generate",
"php artisan ide-helper:meta",
"php artisan ide-helper:models"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
"@php artisan key:generate --ansi",
"@php artisan storage:link --ansi"
],
"format": [
"./vendor/bin/pint"
]
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/bjhijmans/laravel-translation.git"
}
],
"minimum-stability": "dev",
"prefer-stable": true
}

5560
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,385 +0,0 @@
<?php
namespace App\Services;
use App\User;
use App\Models\Product;
use App\Models\Setting;
use App\Mail\MailCheckout;
use App\Models\ProductBuy;
use App\Models\ShoppingOrder;
use App\Models\UserPayCredit;
use App\Models\ShoppingPayment;
use App\Models\UserCreditMargin;
use App\Models\PromotionUserOrder;
use Illuminate\Support\Facades\Mail;
class Payment
{
public static $txaction_text = [
'paid' => "bezahlt",
'appointed' => "offen",
'failed' => "abbruch",
'extern' => "extern",
'open' => "offen",
'invoice_open' => "Re. offen",
'invoice_paid' => "Re. bezahlt",
'invoice_non' => "Re. keine Zahlung",
'non' => "keine Zahlung",
'NULL' => 'keine Zahlung',
];
public static $txaction_invoice = [
'open' => "offen",
'paid' => "bezahlt",
'non' => "keine Zahlung",
/*'open_vor' => "Vorkasse offen",
'paid_vor' => "Vorkasse bezahlt",
'non_vor' => "Vorkasse keine Zahlung",
'invoice_open' => "Rechung offen",
'invoice_paid' => "Rechung bezahlt",
'invoice_non' => 'Rechung keine Zahlung',*/
];
public static $txaction_color = [
'appointed' => "warning",
'failed' => "danger",
'extern' => "success",
'open' => "warning",
'paid' => "success",
'non' => "danger",
'open_vor' => "warning",
'paid_vor' => "success",
'non_vor' => "danger",
'invoice_open' => "warning",
'invoice_paid' => "success",
'invoice_non' => "danger",
];
public static function getFormattedTxaction($txaction){
if($txaction && isset(self::$txaction_text[$txaction])){
return self::$txaction_text[$txaction];
}
return self::$txaction_text['NULL'];
}
public static function getFormattedTxactionColor($txaction){
if($txaction && isset(self::$txaction_color[$txaction])){
return self::$txaction_color[$txaction];
}
return "warning";
}
public static function generateNextInvoiceNumber(){
$invoice_number = \App\Models\Setting::getContentBySlug('invoice-number');
return $invoice_number;
}
public static function getShoppingOrderBadge(ShoppingOrder $shopping_order){
if($shopping_order->mode === 'test'){
return '<span class="badge badge-pill badge-default">'.strtoupper($shopping_order->mode).' - '.self::getFormattedTxaction($shopping_order->txaction).'</span>';
}
if($shopping_order->mode === 'dev'){
return '<span class="badge badge-pill badge-info">'.strtoupper($shopping_order->mode).' - '.self::getFormattedTxaction($shopping_order->txaction).'</span>';
}
return '<span class="badge badge-pill badge-'.self::getFormattedTxactionColor($shopping_order->txaction).'">'.self::getFormattedTxaction($shopping_order->txaction).'</span>';
}
public static function getPaymentForTypeBadge(ShoppingOrder $shopping_order){
return '<span class="badge badge-pill badge-'.$shopping_order->getPaymentForColor().'">'.$shopping_order->getPaymentForType().'</span>';
}
public static function getShoppingPaymentBadge(ShoppingPayment $shopping_payment){
if($shopping_payment->mode === 'test'){
return '<span class="badge badge-pill badge-default">'.strtoupper($shopping_payment->mode).' - '.self::getFormattedTxaction($shopping_payment->txaction).'</span>';
}
return '<span class="badge badge-pill badge-'.self::getFormattedTxactionColor($shopping_payment->txaction).'">'.self::getFormattedTxaction($shopping_payment->txaction).'</span>';
}
public static function addUserPayCredits(User $user, $credit, $status, $message, $shopping_order_id = null){
UserPayCredit::create([
'user_id' => $user->id,
'credit' => $credit,
'old_credit_total' => $user->payment_credit,
'new_credit_total' => $user->payment_credit + $credit,
'message' => $message,
'status' => $status,
'shopping_order_id' => $shopping_order_id,
]);
$user->payment_credit = $user->payment_credit + $credit;
$user->save();
}
public static function addUserCreditMargin(User $user, $credit, $status, $message){
UserCreditMargin::create([
'user_id' => $user->id,
'credit' => $credit,
'message' => $message,
'status' => $status,
]);
}
public static function addProductBuy(User $user, Product $product){
if($product->max_buy && $product->max_buy_num > 0){
$ProductBuy = ProductBuy::where('auth_user_id', $user->id)->where('product_id', $product->id)->first();
if(!$ProductBuy){
ProductBuy::create([
'auth_user_id' => $user->id,
'product_id' => $product->id,
'num' => 1,
]);
}else{
$ProductBuy->num = $ProductBuy->num+1;
$ProductBuy->save();
}
}
}
/**/
public static function paymentStatusPaidAction(ShoppingOrder $shopping_order, $paid){
$send_link = false;
$shopping_order->setUserHistoryValue(['status' => 8]);
Shop::userOrders();
$shopping_order->paid = $paid;
$shopping_order->save();
//if product has actions
if($shopping_order->shopping_order_items && $shopping_order->auth_user_id){
foreach($shopping_order->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product){
//add product when buy
$user = User::findOrFail($shopping_order->auth_user_id);
if($shopping_order_item->product->max_buy && $shopping_order_item->product->max_buy_num > 0){
self::addProductBuy($user, $shopping_order_item->product);
}
//product action
if($shopping_order_item->product->action){
$user->save();
$send_link = true;
//new date
$date = \Carbon::now()->modify('1 year');
if($user->payment_account && $user->daysActiveAccount()>0){
$date = \Carbon::parse($user->payment_account)->modify('1 year');
}
foreach ($shopping_order_item->product->action as $do){
if($shopping_order_item->product->getActionName($do) === 'payment_for_account' && !$shopping_order_item->handle){
// $user->payment_order_id = $shopping_order_item->product->id; //34
$user->payment_account = $date;
$user->wizard = 100;
$user->save();
self::addUserPayCredits($user, $shopping_order_item->product->price, 1, 'payment_for_account', $shopping_order->id);
$shopping_order_item->handle = true;
$shopping_order_item->save();
$shopping_order->setUserHistoryValue(['status' => 9]);
}
if($shopping_order_item->product->getActionName($do) === 'charging_credits' && !$shopping_order_item->handle){
self::addUserPayCredits($user, ($shopping_order_item->product->price * $shopping_order_item->qty), 7, 'charging_credits_add', $shopping_order->id);
$shopping_order_item->handle = true;
$shopping_order_item->save();
$shopping_order->setUserHistoryValue(['status' => 9]);
}
/*if($shopping_order_item->product->getActionName($do) === 'payment_for_shop'){
$user->payment_order_id = $shopping_order_item->product->id; //35
$user->payment_shop = $date;
$user->wizard = 100;
$shopping_order->setUserHistoryValue(['status' => 9]);
}
if($shopping_order_item->product->getActionName($do) === 'payment_for_shop_upgrade'){
if($shopping_order_item->product->upgrade_to_id){
$user->payment_order_id = $shopping_order_item->product->upgrade_to_id;
}
$user->payment_shop = $user->payment_account; //same Date, is upgrade
$shopping_order->setUserHistoryValue(['status' => 9]);
}
if($shopping_order_item->product->getActionName($do) === 'payment_for_lead_upgrade'){
if($shopping_order_item->product->upgrade_to_id){
$user->m_level = $shopping_order_item->product->upgrade_to_id;
}
}*/
//$user->save();
}
}
}
}
}
//if the order has action
if(($shopping_order->shopping_user->is_from === 'user_order' || $shopping_order->shopping_user->is_from === 'shopping') && $shopping_order->shopping_order_margin){
//is margin -> set paid
$shopping_order->shopping_order_margin->order_paid = true;
$shopping_order->shopping_order_margin->save();
}
return $send_link;
}
public static function handelPromotionProduct(ShoppingOrder $shopping_order){
//add the Promotion Product to Order
$shopping_order = ShoppingOrder::find($shopping_order->id);
foreach($shopping_order->shopping_order_items as $shopping_order_item){
if($shopping_order_item->isFreeProduct()){
if($promotion_user_product = $shopping_order_item->promotion_user_product){
$promotion_admin_product = $promotion_user_product->promotion_admin_product;
$PromotionUserOrder = PromotionUserOrder::create([
'promotion_admin_id' => $promotion_user_product->promotion_admin_id,
'promotion_user_id' => $shopping_order->promotion_user_id,
'promotion_user_product_id' => $promotion_user_product->id,
'product_id' => $promotion_user_product->product_id,
'shopping_order_item_id' => $shopping_order_item->id,
'shopping_order_id' => $shopping_order->id,
'shopping_user_id' => $shopping_order->shopping_user_id,
'qty' => $shopping_order_item->qty,
'price' => $promotion_admin_product->getPriceWith(false),
'price_net' => $promotion_admin_product->getPriceWith(true),
'tax_rate' => $promotion_admin_product->product->tax,
'status' => 0,
'pick_up' => $shopping_order->isPickUp()
]);
$promotion_user_product->open_items -= $PromotionUserOrder->qty;
$promotion_user_product->sell_items += $PromotionUserOrder->qty;
$promotion_user_product->used_budget_total += $PromotionUserOrder->price;
$promotion_user_product->save();
//Guthaben abziehen wenn nicht abholung
if(!$shopping_order->isPickUp()){
self::addUserPayCredits($promotion_user_product->promotion_user->user, ($PromotionUserOrder->price*-1), 5, 'promotion_order_deduction', $shopping_order->id);
}
}
}
}
}
//remove or add form credit, is from Charging credits, handle is true (by paymentStatusPaidAction first action by paid)
public static function handelUserPayChargingCredits(ShoppingOrder $shopping_order, $action){
//only from cr <- credit Charging
if($shopping_order->shopping_user->is_for !== 'cr'){
return;
}
if($shopping_order->shopping_order_items && $shopping_order->auth_user_id){
foreach($shopping_order->shopping_order_items as $shopping_order_item){
if($shopping_order_item->product){
$user = User::findOrFail($shopping_order->auth_user_id);
//product action
if($shopping_order_item->product->action){
foreach ($shopping_order_item->product->action as $do){
if($shopping_order_item->product->getActionName($do) === 'charging_credits' && $shopping_order_item->handle){
if($action === 'remove'){
self::addUserPayCredits($user, ($shopping_order_item->product->price*-1), 8, 'charging_credits_remove', $shopping_order->id);
}
if($action === 'add'){
self::addUserPayCredits($user, $shopping_order_item->product->price, 7, 'charging_credits_add', $shopping_order->id);
}
}
}
}
}
}
}
}
//remove form credit, every sale fnc / vor / etc from CheckoutController
//when done, put it back SalesController
public static function handelUserPayCredits(ShoppingOrder $shopping_order, $do){
//is payment credit, deduction or return
if(!$shopping_order->shopping_order_margin){
return;
}
if($do === 'deduction'){
if($shopping_order->shopping_order_margin->from_payment_credit > 0){
$credit = $shopping_order->shopping_order_margin->from_payment_credit * -1;
self::addUserPayCredits($shopping_order->auth_user, $credit, 2, 'user_order_deduction', $shopping_order->id);
}
}
if($do === 'return'){
if($shopping_order->shopping_order_margin->from_payment_credit > 0){
$credit = $shopping_order->shopping_order_margin->from_payment_credit;
self::addUserPayCredits($shopping_order->auth_user, $credit, 4, 'user_order_return', $shopping_order->id);
}
}
}
public static function handelUserPromotionOrder(ShoppingOrder $shopping_order){
//no user promotion
if($shopping_order->payment_for !== 7 || !$shopping_order->promotion_user){
return;
}
if($shopping_order->promotion_user->promotion_user_orders){
foreach($shopping_order->promotion_user->getPromotionUserOrders($shopping_order->id) as $promotion_user_order){
$promotion_user_order->setStatusShipped($shopping_order->getAPIShippedType());
if(!$promotion_user_order->pick_up){ // keine abholung handel credit
$last_UserPayCredit = UserPayCredit::where('shopping_order_id', $shopping_order->id)->whereIn('status', [5, 6])->orderBy('id', 'DESC')->first();
if($last_UserPayCredit && $promotion_user_order->status === 10 && $last_UserPayCredit->status === 5){
Payment::handelUserPayCreditsPromotion($promotion_user_order, 'return');
}
//Status Zahlung, voher gab es eine Storno, Guthaben abziehen wenn status 6 / return from order
if($last_UserPayCredit && $promotion_user_order->status === 0 && $last_UserPayCredit->status === 6){
Payment::handelUserPayCreditsPromotion($promotion_user_order, 'deduction');
}
}
}
}
}
public static function handelUserShopOrder(ShoppingOrder $shopping_order){
//no user shop
if($shopping_order->payment_for !== 8 || !$shopping_order->user_shop){
return;
}
// need something to do?
}
//add or remove form credit,
//when done, put it back SalesController
public static function handelUserPayCreditsPromotion(PromotionUserOrder $promotion_user_order, $do){
//is promotion pick up, dont
if($promotion_user_order->pick_up){
return;
}
if($do === 'deduction'){
if($promotion_user_order->price > 0){
$credit = $promotion_user_order->price * -1;
self::addUserPayCredits($promotion_user_order->promotion_user->user, $credit, 5, 'promotion_order_deduction', $promotion_user_order->shopping_order->id);
}
}
if($do === 'return'){
if($promotion_user_order->price > 0){
$credit = $promotion_user_order->price;
self::addUserPayCredits($promotion_user_order->promotion_user->user, $credit, 6, 'promotion_order_return', $promotion_user_order->shopping_order->id);
}
}
}
public static function paymentStatusSendMail(ShoppingOrder $shopping_order, $shopping_payment, $data){
$bcc = [];
$billing_email = $shopping_order->shopping_user->billing_email;
if(!$billing_email){
if($data['mode'] === 'test'){
$billing_email = config('app.checkout_test_mail');
}else{
$billing_email = config('app.checkout_mail');
}
}
if($data['mode'] === 'test'){
$bcc[] = config('app.checkout_test_mail');
}else{
$bcc[] = config('app.checkout_mail');
}
if(!$shopping_order->shopping_user->is_like && $shopping_order->shopping_user->member){
$bcc[] = $shopping_order->shopping_user->member->email;
}
Mail::to($billing_email)->bcc($bcc)->send(new MailCheckout($data['txaction'], $shopping_order, $shopping_payment, $data['send_link'], $data['mode']));
}
}

View file

@ -75,6 +75,7 @@ return [
'main_user_id' => env('APP_MAIN_USER_ID', 1),
'exception_mail' => env('EXCEPTION_MAIL', 'exception@adametz.media'),
'logistic_mail' => env('LOGISTIC_MAIL', 'kevin.adametz@me.com'),
/*
|--------------------------------------------------------------------------
@ -185,6 +186,8 @@ return [
/*
* Package Service Providers...
*/
Laravel\Tinker\TinkerServiceProvider::class,
/*
* Application Service Providers...
@ -194,14 +197,11 @@ return [
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
Jenssegers\Date\DateServiceProvider::class,
Collective\Html\HtmlServiceProvider::class,
Intervention\Image\ImageServiceProvider::class,
// Maatwebsite\Excel\ExcelServiceProvider::class,
Maatwebsite\Excel\ExcelServiceProvider::class,
Yajra\DataTables\DataTablesServiceProvider::class,
App\Providers\YardServiceProvider::class
App\Providers\YardServiceProvider::class,
],
@ -253,7 +253,6 @@ return [
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'Input' => Illuminate\Support\Facades\Input::class,
'Form' => Collective\Html\FormFacade::class,
'HTML' => Collective\Html\HtmlFacade::class,
'Image' => Intervention\Image\Facades\Image::class,
@ -261,7 +260,7 @@ return [
'Date' => Jenssegers\Date\Date::class,
'HTMLHelper' => App\Services\HTMLHelper::class,
'Util' => App\Services\Util::class,
//'Excel' => Maatwebsite\Excel\Facades\Excel::class,
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
'DataTables' => Yajra\DataTables\Facades\DataTables::class,
'Yard' => App\Services\Facade\Yard::class,
],

View file

@ -47,8 +47,86 @@ return [
],
/* Replace these words no matter what language. */
'defaults' => [
'full_word_check' => [
'aloe',
'aloevera',
'mivita',
'shop',
'onlineshop',
'aloeveramallorca',
'aloemallorca',
'aloeverademallorca',
'babydaloe',
'fuck',
'shit',
'anal',
'anus',
'arse',
'ass',
'ballsack',
'balls',
'bastard',
'bitch',
'biatch',
'bloody',
'blowjob',
'bollock',
'bollok',
'boner',
'boob',
'bugger',
'bum',
'butt',
'buttplug',
'clitoris',
'cock',
'coon',
'crap',
'cunt',
'damn',
'dick',
'dildo',
'dyke',
'fag',
'feck',
'fellate',
'fellatio',
'felching',
'fuck',
'fudgepacker',
'flange',
'goddamn',
'hell',
'homo',
'jizz',
'knobend',
'labia',
'muff',
'nigger',
'nigga',
'penis',
'piss',
'poop',
'prick',
'pube',
'pussy',
'queer',
'scrotum',
'sex',
'shit',
'sh1t',
'slut',
'smegma',
'spunk',
'suck',
'tit',
'tosser',
'turd',
'twat',
'vagina',
'wank',
'whore',
'wtf',
],
];

View file

@ -1 +0,0 @@
RUN Command Payments Account: 14.12.2022 13:19

View file

@ -48,10 +48,10 @@ class CreateShoppingOrdersTable extends Migration
$table->unsignedInteger('weight')->nullable();
$table->boolean('paid')->default(false);
$table->string('invoice_number', 255)->nullable();
$table->text('invoice')->nullable();
$table->text('delivery')->nullable();
$table->boolean('user_white_label')->default(false);
$table->string('wp_invoice_path', 255)->nullable();
$table->text('wp_notice')->nullable();

View file

@ -29,6 +29,9 @@ class CreateShoppingPaymentsTable extends Migration
$table->string('status', 10)->nullable()->index();
$table->string('txaction', 20)->nullable()->index();
$table->unsignedSmallInteger('reminder')->nullable();
$table->dateTime('reminder_date')->nullable();
$table->char('mode', 4)->nullable();
$table->timestamps();

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('payment_reminders', function (Blueprint $table) {
$table->increments('id');
$table->string('title')->nullable();
$table->string('subject')->nullable();
$table->unsignedSmallInteger('interval')->nullable();
$table->text('message')->nullable();
$table->string('action')->nullable();
$table->string('clearingtype', 3)->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('payment_reminders');
}
};

View file

@ -15,7 +15,6 @@ a {
color: #46564d;
}
a:hover {
color: #5f7567;
}
@ -35,12 +34,13 @@ a:hover {
padding: 0.6rem 1.5rem;
}
#accordion a.card-header:hover, #accordion a.card-header:focus {
#accordion a.card-header:hover,
#accordion a.card-header:focus {
background: #fff;
}
.text-dark[href]:hover {
color: #696f5b !important
color: #696f5b !important;
}
#accordion .card-body {
@ -50,32 +50,31 @@ a:hover {
width: 220px;
}
.table th, .table td {
.table th,
.table td {
border-top: 1px solid #d6d6de;
}
.btn {
font-weight: 500;
}
.footer.bg-white .footer-link:hover, .footer.bg-white .footer-link:focus {
.footer.bg-white .footer-link:hover,
.footer.bg-white .footer-link:focus {
color: rgba(0, 0, 0, 0.9);
text-decoration: underline;
}
.footer.bg-white .footer-link {
color: rgba(0, 0, 0, 1);
text-decoration: none;
}
.footer p {
color:#000;
color: #000;
}
.footer.bg-white {
background-color: #fff !important;
}
.footer a {
color:#000;
color: #000;
}
.footer a:hover {
text-decoration: underline;
@ -100,3 +99,8 @@ a:hover {
[data-toggle="collapse"]:not(.collapsed) .if-collapsed {
display: none;
}
.badge-warning-dark {
background-color: #f98000;
color: #fff;
}

View file

@ -200,5 +200,8 @@
"open your shop": "Er\u00f6ffne Deinen eigenen Shop",
"settings your shop": "Deine Shop-Einstellungen",
"taxable_sales_1": "umsatzsteuerpflichtig (Ich mache eine UST-Voranmeldung pro Monat \/ Quartal \/ Jahr ans Finanzamt)",
"taxable_sales_2": "nicht umsatzsteuerpflichtig (Kleinunternehmer im Sinne von \u00a7 19)"
"taxable_sales_2": "nicht umsatzsteuerpflichtig (Kleinunternehmer im Sinne von \u00a7 19)",
"Order": "Bestellung",
"Orders": "Bestellungen"
}

View file

@ -92,4 +92,5 @@
'checkout_mail_pay_invoice_open' => 'Deine Zahlung per Rechnung wurde genehmigt.',
'checkout_mail_pay_non' => 'Deine Bestellung wurde ausgeführt.',
'checkout_mail_system_status' => 'SystemStatus:',
'my_orders' => 'Meine Bestellungen',
);

View file

@ -43,6 +43,7 @@ return [
'payments' => 'Zahlungen',
'credit' => 'Gutschriften',
'invoice' => 'Rechnungen',
'reminder' => 'Erinnerungen',
'revenue' => 'Umsätze',
'paycredit' => 'Einkaufsguthaben',
'commissions' => 'Provisionen',

View file

@ -200,7 +200,7 @@
@if(Yard::instance('shopping')->content()->count())
<!-- CART -->
{!! Form::open(['url' => url(Util::getPostRoute().'card/update'), 'class' => 'cartContent clearfix', 'id'=>'']) !!}
{!! Form::open(['action' => url(Util::getPostRoute().'card/update'), 'class' => 'cartContent clearfix', 'id'=>'']) !!}
<!-- cart content -->
<div id="cartContent">

View file

@ -147,7 +147,7 @@
<div class="m-checkout">
{!! Form::open(['url' => route('checkout.checkout_card_final'), 'class' => 'row clearfix', 'id'=>'checkout_card_final']) !!}
{!! Form::open(['action' => route('checkout.checkout_card_final'), 'class' => 'row clearfix', 'id'=>'checkout_card_final']) !!}
{!! Form::hidden('selected_country', '') !!}
{!! Form::hidden('is_for', $shopping_user->is_for) !!}

View file

@ -86,7 +86,7 @@
<p>Du interessierst Dich für unser Geschäft oder hast Fragen zu den Produkten? Dann freuen wir uns auf eine Nachricht von Dir. Wir werden uns im Anschluss sobald wie möglich bei Dir zurückmelden.</p>
<div class="box-static box-transparent box-bordered padding-20">
{!! Form::open(['url' => '/kontakt']) !!}
{!! Form::open(['action' => '/kontakt']) !!}
<div class="text-right" style="margin-bottom: 8px; margin-top: -16px;">
<em class="small">* {{trans('register.required_fields')}}</em>
</div>

View file

@ -109,7 +109,7 @@
</div>
<hr>
<div class="shop-item-price text-right" style="line-height: 1.3em">
{!! Form::open(['url' => url(Util::getPostRoute().'card/add/'.$product->id), 'class' => 'mb-0', 'id'=>'']) !!}
{!! Form::open(['action' => url(Util::getPostRoute().'card/add/'.$product->id), 'class' => 'mb-0', 'id'=>'']) !!}
<div class="qty float-left">
<input type="number" value="1" name="quantity" maxlength="3" max="999" min="1"><br>
</div>

View file

@ -87,7 +87,7 @@
<div class="box-static box-transparent box-bordered padding-20">
{!! Form::open(['url' => '/registrierung']) !!}
{!! Form::open(['action' => '/registrierung']) !!}
@if(isset($from_member_id))
{!! Form::hidden('from_member_id', $from_member_id) !!}
@endif

View file

@ -20,7 +20,7 @@
{{ __('Create/Edit Kategorien') }}
</h4>
{!! Form::open(['url' => route('admin_product_category_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
{!! Form::open(['action' => route('admin_product_category_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
<input type="hidden" name="id" id="id" value="@if($category->id>0){{$category->id}}@else new @endif">
<input type="hidden" name="action" value="save-form">

View file

@ -13,7 +13,7 @@
</div>
</div>
{!! Form::open(['url' => route('admin_lead_change_mail', [$user->id]) ]) !!}
{!! Form::open(['action' => route('admin_lead_change_mail', [$user->id]) ]) !!}
<div class="form-group row">
<label class="col-form-label col-sm-2 text-sm-right">{{__('New E-Mail Address')}}*</label>

View file

@ -20,7 +20,7 @@
{{ __('Create/Edit Land') }}
</h4>
{!! Form::open(['url' => route('admin_country_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
{!! Form::open(['action' => route('admin_country_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
<input type="hidden" name="id" id="id" value="@if($country->id>0){{$country->id}}@else new @endif">

View file

@ -5,7 +5,7 @@
<a href="{{route('admin_customer_detail', [$shopping_user->id])}}" class="btn btn-sm btn-default float-right">zurück</a>
{{ __('Kunden Details') }} bearbeiten
</h4>
{!! Form::open(['url' => route('admin_customer_edit', [$shopping_user->id]), 'class' => 'form-horizontal', 'id'=>'lead-form-validation']) !!}
{!! Form::open(['action' => route('admin_customer_edit', [$shopping_user->id]), 'class' => 'form-horizontal', 'id'=>'lead-form-validation']) !!}
@include('admin.customer._edit')
<div class="text-left mt-3">
<button type="submit" class="btn btn-secondary" name="action" value="shopping-user-store">{{ __('save changes') }}</button>&nbsp;

View file

@ -1,114 +0,0 @@
@extends('layouts.layout-2')
@section('content')
@if ($errors->any())
<div class="row">
<div class="col-sm-12">
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<div class="card">
<h5 class="card-header">
{{ __('navigation.products') }} {{ __('navigation.sales_volumes') }}
</h5>
<div class="card-body">
{!! Form::open(['url' => route('admin_evaluation_sales_volumes_download'), 'class' => '']) !!}
{!! Form::hidden('key', 'value') !!}
<button type="submit" name="action" value="export" class="btn btn-md btn-primary mb-2"><i class="ion ion-md-download"></i> &nbsp;Export als xls</button>
<hr>
<div class="form-row align-items-center px-0 pb-2 pt-0">
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_select_filter" name="product_sales_vol_filter_month">
@foreach($filter_months as $key=>$value)
<option value="{{$key}}" @if(session('product_sales_vol_filter_month') == $key) selected @endif>{{$value}}</option>
@endforeach
</select>
</div>
<div class="col-6 col-sm-4 col-md-4 col-lg-4 mb-1">
<select class="custom-select on_change_select_filter" name="product_sales_vol_filter_year">
@foreach($filter_years as $key=>$value)
<option value="{{$value}}" @if(session('product_sales_vol_filter_year') == $value) selected @endif>{{$value}}</option>
@endforeach
</select>
</div>
</div>
{!! Form::close() !!}
<div class="card">
<div class="card-datatable table-responsive">
<table class="table table-striped table-bordered" id="datatable-sales-volume">
<thead>
<tr>
<th>{{__('#') }}</th>
<th>{{__('tables.product')}}</th>
<th>{{__('tables.article_no')}}</th>
<th>{{__('tables.quantity')}}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
$( document ).ready(function() {
var oTable = $('#datatable-sales-volume').DataTable({
"processing": true,
"serverSide": true,
"stateSave": true,
"searching": false,
ajax: {
url: '{!! route('admin_evaluation_sales_volumes_datatable') !!}',
data: function(d) {
d.product_sales_vol_filter_month = $('select[name=product_sales_vol_filter_month]').val();
d.product_sales_vol_filter_year = $('select[name=product_sales_vol_filter_year]').val();
}
},
"order": [[0, "asc" ]],
"columns": [
{ data: 'id', orderable: true, searchable: false },
{ data: 'name', name: 'name', orderable: true, searchable: false },
{ data: 'number', name: 'number', orderable: true, searchable: false },
{ data: 'value', name: 'value', orderable: true, searchable: false },
],
"bLengthChange": false,
"iDisplayLength": 100,
"language": {
"url": "/js/datatables-{{ \App::getLocale() }}.json"
}
});
$('select.on_change_select_filter').on('change', function(){
oTable.draw();
});
$('input.on_keyup_input_filter').on('keyup', function(){
oTable.draw();
});
});
</script>
@endsection

View file

@ -20,7 +20,7 @@
{{ __('Create/Edit Inhaltsstoff') }}
</h4>
{!! Form::open(['url' => route('admin_product_ingredient_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
{!! Form::open(['action' => route('admin_product_ingredient_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
<input type="hidden" name="id" id="id" value="@if($model->id>0){{$model->id}}@else new @endif">

View file

@ -24,7 +24,7 @@
@endif
</h4>
{!! Form::open(['url' => route('admin_lead_store')."?show=".$show, 'class' => 'form-horizontal', 'id'=>'lead-form-validation']) !!}
{!! Form::open(['action' => route('admin_lead_store')."?show=".$show, 'class' => 'form-horizontal', 'id'=>'lead-form-validation']) !!}
<div class="text-left mt-0 mb-2">
<button type="submit" class="btn btn-submit btn-primary">{{ __('save') }}</button>&nbsp;
@ -56,7 +56,7 @@
<!-- Modal template -->
<div class="modal fade" id="modal-user-vat-validation">
<div class="modal-dialog">
{!! Form::open(['url' => route('admin_lead_store'), 'class' => 'modal-content']) !!}
{!! Form::open(['action' => route('admin_lead_store'), 'class' => 'modal-content']) !!}
<input type="hidden" name="user_id" id="user_id" value="@if($user->id>0){{$user->id}}@else new @endif">
<div class="modal-header">

View file

@ -7,7 +7,7 @@
<div class="card">
<div class="card-header">
{!! Form::open(['url' => route('admin_lead_download'), 'class' => '']) !!}
{!! Form::open(['action' => route('admin_lead_download'), 'class' => '']) !!}
<div class="form-row align-items-center">
<div class="col-sm-5 col-md-5">
<label class="form-label" for="filter_user_shop_id">Art Vertriebspartner</label>

View file

@ -21,7 +21,7 @@
</div>
</h5>
<div class="card-body">
{!! Form::open(['url' => route('admin_lead_update')."?show=".$show, 'class' => 'form-horizontal', 'id'=>'white-label-form']) !!}
{!! Form::open(['action' => route('admin_lead_update')."?show=".$show, 'class' => 'form-horizontal', 'id'=>'white-label-form']) !!}
<input type="hidden" name="user_id" value="{{$user->id}}">
<div class="form-row">
<div class="form-group col-md-12">

View file

@ -1,58 +0,0 @@
@if($m_data_error)
<div class="row">
<div class="col-sm-12">
<div class="alert alert-danger">
<ul>
<li>{{ $m_data_error }}</li>
</ul>
</div>
</div>
</div>
@endif
<div class="card-body m-0 p-0">
<div class="table-responsive">
<table class="table card-table m-0">
<tbody>
<tr>
<th style="width: 33%">{{ __('Name') }}</th>
<th style="width: 66%">{{ __('Account ID') }}</th>
</tr>
<tr>
<td>{{ \App\Services\HTMLHelper::getSalutationLang($user->account->m_salutation) }} {{ $user->account->m_first_name }} {{ $user->account->m_last_name }}</td>
<td>{{ $user->account->m_account }}</td>
</tr>
</tbody>
</table>
<table class="table card-table m-0">
<tbody>
<tr>
<th style="width: 33%">{{ __('Art Vertriebspartner') }}</th>
<th style="width: 33%">{{ __('Rolle') }}</th>
<th style="width: 33%">{{ __('Sponsor') }}</th>
</tr>
<tr>
<td>@if($user->lead_type) {{ $user->lead_type->name }} @else Standard @endif</td>
<td>@if($user->user_level){{ $user->user_level->name }}@endif</td>
<td>@if($user->m_sponsor){{ $user->getMUserSponsor() }}@endif</td>
</tr>
</tbody>
</table>
@if($user->account->m_notes)
<table class="table card-table m-0">
<tbody>
<tr>
<th>{{ __('weitere Daten') }}</th>
</tr>
<tr>
<td>{{$user->account->m_notes}}</td>
</tr>
</tbody>
</table>
@endif
</div>
</div>

View file

@ -25,7 +25,7 @@
</div>
<div class="card mb-2">
{!! Form::open(['url' => route('admin_level_store',), 'class' => 'form-horizontal', 'id'=>'']) !!}
{!! Form::open(['action' => route('admin_level_store',), 'class' => 'form-horizontal', 'id'=>'']) !!}
<input type="hidden" name="id" value="@if($value->id>0){{$value->id}}@else new @endif">
<h5 class="card-header">
{{ __('Rolle') }}

View file

@ -1,4 +1,4 @@
{!! Form::open(['url' => route('admin_payments_credit'), 'class' => 'modal-content form-prevent-multiple-submits', 'enctype' => 'multipart/form-data']) !!}
{!! Form::open(['action' => route('admin_payments_credit'), 'class' => 'modal-content form-prevent-multiple-submits', 'enctype' => 'multipart/form-data']) !!}
<div class="modal-header">
<h5 class="modal-title">
{{ __('Gutschrift') }}

View file

@ -1,4 +1,4 @@
{!! Form::open(['url' => route('admin_payments_paycredit'), 'class' => 'modal-content form-prevent-multiple-submits', 'enctype' => 'multipart/form-data']) !!}
{!! Form::open(['action' => route('admin_payments_paycredit'), 'class' => 'modal-content form-prevent-multiple-submits', 'enctype' => 'multipart/form-data']) !!}
<div class="modal-header">
<h5 class="modal-title">
{{ __('Einkaufsguthaben') }}

View file

@ -1,4 +1,4 @@
{!! Form::open(['url' => route('admin_sales_customers_detail', [$current->id]), 'class' => 'modal-content', 'enctype' => 'multipart/form-data']) !!}
{!! Form::open(['action' => route('admin_sales_detail', [$current->id]), 'class' => 'modal-content', 'enctype' => 'multipart/form-data']) !!}
<input type="hidden" name="action" value="{{$data['action']}}">
<input type="hidden" name="id" value="{{$data['id']}}">

View file

@ -1,4 +1,4 @@
{!! Form::open(['url' => $route, 'class' => 'modal-content', 'enctype' => 'multipart/form-data']) !!}
{!! Form::open(['action' => $route, 'class' => 'modal-content', 'enctype' => 'multipart/form-data']) !!}
<div class="modal-header">
<h5 class="modal-title">
{{ __('Vertriebspartner') }}

View file

@ -1,4 +1,4 @@
{!! Form::open(['url' => route('admin_promotion_detail', [$data['promotion_id']]), 'class' => 'modal-content form-prevent-multiple-submits', 'enctype' => 'multipart/form-data']) !!}
{!! Form::open(['action' => route('admin_promotion_detail', [$data['promotion_id']]), 'class' => 'modal-content form-prevent-multiple-submits', 'enctype' => 'multipart/form-data']) !!}
<div class="modal-header">
<h5 class="modal-title">
{{ __('Produkt') }} <span class="font-weight-light">hinzufügen / bearbeiten</span>

View file

@ -1,45 +0,0 @@
{!! Form::open(['url' => route('admin_payments_credit'), 'class' => 'modal-content form-prevent-multiple-submits', 'enctype' => 'multipart/form-data']) !!}
<div class="modal-header">
<h5 class="modal-title">
{{ __('Gutschrift') }}
<span class="font-weight-light">hinzufügen</span>
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">×</button>
</div>
<div class="modal-body">
<input type="hidden" name="action" value="{{$data['action']}}">
<input type="hidden" name="id" value="{{$data['id']}}">
<div class="form-row">
<div class="form-group col-12">
<label for="member_id" class="form-label">{{ __('Vertriebspartner auswählen') }}*</label>
<select class="selectpicker" name="member_id" data-style="btn-light" data-live-search="true" required>
{!! HTMLHelper::getMembersOptions(0, true) !!}
</select>
</div>
<div class="form-group col-12">
<label class="form-label" for="credit">{{ __('Betrag') }} netto*</label>
{{ Form::text('credit', '', array('placeholder'=>__('in Euro'), 'class'=>'form-control', 'required'=>true)) }}
</div>
<div class="form-group col-12">
<label class="form-label" for="message">{{ __('Mitteilung') }}*</label>
{{ Form::textarea('message', '' , array('placeholder'=>__('Mitteilung'), 'class'=>'form-control', 'rows'=>4, 'required'=>true)) }}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">schließen</button>
<button type="submit" class="btn btn-primary button-prevent-multiple-submits">{{__('Gutschrift hinzufügen')}}</button>
</div>
{!! Form::close() !!}
<script type="text/javascript">
$( document ).ready(function() {
});
</script>

View file

@ -1,4 +1,4 @@
{!! Form::open(['url' => route('admin_payments_credit_create'), 'class' => 'modal-content', 'enctype' => 'multipart/form-data']) !!}
{!! Form::open(['action' => route('admin_payments_credit_create'), 'class' => 'modal-content', 'enctype' => 'multipart/form-data']) !!}

View file

@ -43,7 +43,7 @@
<td><div class="no-line-break">{{ formatDate($user_pay_credit->created_at) }}</div></td>
@if($user_pay_credit->shopping_order_id)
<td><a class="btn btn-secondary btn-xs" href="{{ route('admin_sales_users_detail', [$user_pay_credit->shopping_order_id]) }}"><i class="ion ion-md-eye"></i></a></td>
<td><a class="btn btn-secondary btn-xs" href="{{ route('admin_sales_detail', [$user_pay_credit->shopping_order_id]) }}"><i class="ion ion-md-eye"></i></a></td>
@else
@if($user_pay_credit->status === 3 && $deleteTime = $user_pay_credit->deleteTime())
<td>

View file

@ -1,37 +0,0 @@
<div class="td-entry-table-margin">
{!! $entry->badge !!}
@if($entry->link)
<a href="{{ $entry->link }}">
@else
<span>
@endif
{!! $entry->name !!} /
@if($entry->reference)
{!! $entry->reference !!} /
@endif
@if($entry->total)
({!! $entry->total !!})
@endif
@if($entry->date)
{!! $entry->date !!} /
@endif
@if(isset($entry->price_formatted))
<strong>{!! $entry->price_formatted !!} </strong>
@endif
@if($entry->link)
</a>
@else
</span>
@endif
@if(isset($entry->delete))
&nbsp; {!! $entry->delete !!}
@endif
</div>

View file

@ -1,37 +0,0 @@
<div class="td-entry-table-margin">
{!! $entry->badge !!}
@if($entry->link)
<a href="{{ $entry->link }}">
@else
<span>
@endif
{!! $entry->name !!} /
@if($entry->reference)
{!! $entry->reference !!} /
@endif
@if($entry->total)
({!! $entry->total !!})
@endif
@if($entry->date)
{!! $entry->date !!} /
@endif
@if(isset($entry->price_formatted))
<strong>{!! $entry->price_formatted !!} </strong>
@endif
@if($entry->link)
</a>
@else
</span>
@endif
@if(isset($entry->delete))
&nbsp; {!! $entry->delete !!}
@endif
</div>

View file

@ -117,7 +117,7 @@
Zahlungen / erstellte Gutschriften
</h6>
<div class="col-sm-6 mb-0 mt-2">
{!! Form::open(['url' => route('admin_payments_credit'), 'class' => 'form-horizontal', 'id'=>'form_filter_sales_year']) !!}
{!! Form::open(['action' => route('admin_payments_credit'), 'class' => 'form-horizontal', 'id'=>'form_filter_sales_year']) !!}
<label class="form-label" for="filter_sales_year">Filter Jahr</label>
<select class="custom-select" name="filter_sales_year" id="filter_sales_year">

View file

@ -257,7 +257,7 @@
Zahlungen / erstellte Gutschriften
</h6>
<div class="col-sm-6 mb-0 mt-2">
{!! Form::open(['url' => route('admin_payments_credit'), 'class' => 'form-horizontal', 'id'=>'form_filter_sales_year']) !!}
{!! Form::open(['action' => route('admin_payments_credit'), 'class' => 'form-horizontal', 'id'=>'form_filter_sales_year']) !!}
<label class="form-label" for="filter_sales_year">Filter Jahr</label>
<select class="custom-select" name="filter_sales_year" id="filter_sales_year">

View file

@ -6,7 +6,7 @@
Zahlungen / Rechnungen
</h6>
<div class="col-sm-6 mb-0 mt-2">
{!! Form::open(['url' => route('admin_payments_invoice'), 'class' => 'form-horizontal', 'id'=>'form_filter_sales_year']) !!}
{!! Form::open(['action' => route('admin_payments_invoice'), 'class' => 'form-horizontal', 'id'=>'form_filter_sales_year']) !!}
<label class="form-label" for="filter_sales_year">Filter Jahr</label>
<select class="custom-select" name="filter_sales_year" id="filter_sales_year">

View file

@ -15,7 +15,7 @@
</div>
</h6>
{{-- <div class="col-sm-6 mb-0 mt-2">
{!! Form::open(['url' => route('admin_payments_paycredit'), 'class' => 'form-horizontal', 'id'=>'form_filter_user_status']) !!}
{!! Form::open(['action' => route('admin_payments_paycredit'), 'class' => 'form-horizontal', 'id'=>'form_filter_user_status']) !!}
<label class="form-label" for="filter_user_status">Filter User</label>
<select class="custom-select" name="filter_user_status" id="filter_user_status">
<option value="all" @if($filter_user_status === 'all') selected @endif>alle</option>

View file

@ -0,0 +1,107 @@
@extends('layouts.layout-2')
@section('content')
@if ($errors->any())
<div class="row">
<div class="col-sm-12">
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<h4 class="font-weight-bold py-2 mb-2">
{{ __('Erinnerung') }} {{ $reminder->id > 0 ? 'bearbeiten' : 'erstellen' }}
</h4>
<div class="text-left mt-0 mb-2">
<a href="{{ route('admin_payments_reminder') }}" class="btn btn-default">{{ __('back') }}</a>
</div>
<div class="card mb-2">
{!! Form::open(['action' => route('admin_payments_reminder_store',), 'class' => 'form-horizontal', 'id'=>'']) !!}
<input type="hidden" name="id" value="@if($reminder->id>0){{$reminder->id}}@else new @endif">
<div class="card-body">
<div class="form-row">
<div class="form-group col">
<label for="title" class="form-label">{{__('Titel')}}*</label>
{{ Form::text('title', $reminder->title, array('placeholder'=>__('Titel'), 'class'=>'form-control', 'required')) }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
<label for="subject" class="form-label">{{__('Betreff')}}*</label>
{{ Form::text('subject', $reminder->subject, array('placeholder'=>__('Betreff'), 'class'=>'form-control', 'required')) }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
<label for="message" class="form-label">{{__('Nachricht')}}</label>
{{ Form::textarea('message', $reminder->message, array('placeholder'=>__('Nachricht'), 'class'=>'form-control summernote-small')) }}
<p> <i>Platzhalter: {billing_first_name}, {billing_last_name}, {order_number}, {order_date}, {order_total}</i></p>
</div>
</div>
<div class="form-row">
<div class="form-group col">
<label for="interval" class="form-label">{{__('Interval (in Tagen nach der Bestellung)')}}*</label>
{{ Form::number('interval', $reminder->interval, array('placeholder'=>__('Interval'), 'class'=>'form-control', 'required')) }}
</div>
</div>
<div class="form-row">
<div class="form-group col">
<label for="clearingtype" class="form-label">{{__('Typ')}}*</label>
{{ Form::select('clearingtype', \App\Models\PaymentReminder::returnClearingtypes(), $reminder->clearingtype, array('placeholder'=>__('Typ'), 'class'=>'form-control custom-select', 'required')) }}
</div>
</div>
<div class="form-row">
<div class="form-group col-12">
<label class="custom-control custom-checkbox m-2">
{!! Form::checkbox('action', 'set_order_status_cancelled', $reminder->action == 'set_order_status_cancelled', ['class'=>'custom-control-input']) !!}
<span class="custom-control-label">Action: Bestellung auf "storniert" setzen</span>
</label>
</div>
<div class="form-group col-12">
<label class="custom-control custom-checkbox m-2">
{!! Form::checkbox('active', 1, $reminder->active, ['class'=>'custom-control-input']) !!}
<span class="custom-control-label">{{__('active')}}</span>
</label>
</div>
</div>
@if($reminder->id > 0)
<div class="float-right">
<a href="{{ route('admin_payments_reminder_delete', $reminder->id) }}" class="btn btn-danger btn-sm" onclick="return confirm('{{ __('Are you sure you want to delete this reminder?') }}')"><i class="fa fa-trash"></i> {{ __('delete') }}</a>
</div>
@endif
<div class="text-left mt-0 mb-2">
<button type="submit" class="btn btn-secondary">{{ __('save') }}</button>&nbsp;
</div>
</div>
{!! Form::close() !!}
</div>
<div class="text-left mt-2 mb-2">
<a href="{{ route('admin_payments_reminder') }}" class="btn btn-default">{{ __('back') }}</a>
</div>
@endsection

View file

@ -0,0 +1,582 @@
@extends('layouts.layout-2')
@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);
}
.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-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-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.bs-tooltip-top .arrow::before {
border-top-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-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-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-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-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);
}
}
/* 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;
}
}
/* 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 h5 {
color: white;
margin-bottom: 15px;
font-weight: 600;
}
.info-box .feature-list {
list-style: none;
padding: 0;
margin: 0;
}
.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 .feature-list li:last-child {
border-bottom: none;
}
.info-box .feature-list li i {
margin-right: 10px;
width: 20px;
text-align: center;
}
.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>
<div class="row mt-4">
<div class="col-12">
<!-- Offene Zahlungen Übersicht -->
<div class="card">
<div class="card-header">
<h4 class="card-title">
<i class="fas fa-clock"></i> Offene Zahlungen - Übersicht
</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>
</div>
<div class="float-right">
<a href="{{ route('admin_payments_reminder_logs') }}" class="btn btn-sm btn-info mr-2">
<i class="fas fa-chart-line"></i> Logs & Statistiken
</a>
</div>
</div>
<div class="card-body">
<!-- Detaillierte Tabelle -->
<div class="table-responsive">
<table class="table table-bordered table-striped" id="payment-reminders-table">
<thead>
<tr>
<th>Bestell-ID</th>
<th>Betrag</th>
<th>Erstellt am</th>
<th>Tage alt</th>
<th>Name</th>
<th>Email</th>
<th>Zahlungsart</th>
<th>Versand</th>
<th>Erinnerung</th>
<th>Aktionen</th>
</tr>
</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
</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"
onclick="return confirm('Soll die Zahlung als nicht bezahlt markiert werden?')">
<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>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{{-- <div class="info-box mt-4">
<h5><i class="fas fa-info-circle mr-2"></i>Zahlungserinnerungen System - Übersicht & Funktionsweise</h5>
<div class="row">
<div class="col-md-6">
<h6><i class="fas fa-cogs mr-2"></i>System-Features:</h6>
<ul class="feature-list">
<li><i class="fas fa-check text-success"></i>Dynamisch konfigurierbare Erinnerungsintervalle</li>
<li><i class="fas fa-check text-success"></i>Automatische E-Mail-Versendung via Cron-Job</li>
<li><i class="fas fa-check text-success"></i>Intelligente Fälligkeitsberechnung</li>
<li><i class="fas fa-check text-success"></i>Detailliertes Logging & Statistiken</li>
<li><i class="fas fa-check text-success"></i>Manuelle & automatische Aktionen</li>
<li><i class="fas fa-check text-success"></i>Responsive Admin-Interface</li>
</ul>
</div>
<div class="col-md-6">
<h6><i class="fas fa-clock mr-2"></i>Workflow:</h6>
<div class="workflow-steps">
<div class="step">
<span class="step-number">1</span>
<span>Offene Zahlung wird erkannt</span>
</div>
<div class="step">
<span class="step-number">2</span>
<span>Intervall-basierte Fälligkeitsprüfung</span>
</div>
<div class="step">
<span class="step-number">3</span>
<span>Automatische E-Mail-Versendung</span>
</div>
<div class="step">
<span class="step-number">4</span>
<span>Logging & Status-Update</span>
</div>
<div class="step">
<span class="step-number">5</span>
<span>Nächste Erinnerung planen</span>
</div>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<h6><i class="fas fa-calendar-alt mr-2"></i>Reminder-Erstellung:</h6>
<p style="font-size: 14px; line-height: 1.5;">
Erstellen Sie neue Erinnerungen über den "Erinnerung hinzufügen" Button.
Jeder Reminder kann individuell konfiguriert werden mit:
</p>
<ul class="feature-list" style="font-size: 13px;">
<li><i class="fas fa-arrow-right text-info"></i>Titel & Beschreibung</li>
<li><i class="fas fa-arrow-right text-info"></i>Intervall in Tagen (z.B. 7, 14, 30)</li>
<li><i class="fas fa-arrow-right text-info"></i>Zahlungsart-Filter (FNC, PayPal, etc.)</li>
<li><i class="fas fa-arrow-right text-info"></i>E-Mail-Template & Betreff</li>
<li><i class="fas fa-arrow-right text-info"></i>Automatische Aktionen</li>
</ul>
</div>
<div class="col-md-6">
<h6><i class="fas fa-robot mr-2"></i>Automatisierung:</h6>
<div class="cron-info">
<strong>Cron-Job Konfiguration:</strong><br>
<code>0 9 * * *</code> php /path/to/artisan payments:reminders<br><br>
<strong>Ausführung:</strong> Täglich um 9:00 Uhr<br>
<strong>Funktion:</strong> Prüft alle aktiven Reminder und sendet fällige E-Mails
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<h6><i class="fas fa-chart-line mr-2"></i>Monitoring & Statistiken:</h6>
<p style="font-size: 14px; line-height: 1.5;">
Überwachen Sie die Performance Ihres Reminder-Systems über die "Logs & Statistiken" Seite.
Dort finden Sie detaillierte Auswertungen zu Versandraten, Erfolgsquoten,
durchschnittlichen Reaktionszeiten und mehr. Das System loggt jeden Schritt
automatisch für vollständige Transparenz.
</p>
</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
});
// 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');
}
}
function viewDetails(paymentId) {
// TODO: Implementiere Detail-Ansicht
alert('Details für Payment ID ' + paymentId + ' würden angezeigt werden');
}
</script>
@endsection

View file

@ -0,0 +1,333 @@
@extends('layouts.layout-2')
@section('content')
<style>
/* Statistik Cards */
.stats-card {
transition: all 0.3s ease;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.stats-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
}
.stats-number {
font-size: 2rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.stats-label {
font-size: 0.9rem;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Log Tabelle */
.log-table {
font-size: 0.85rem;
}
.log-level {
padding: 2px 8px;
border-radius: 12px;
font-size: 0.75rem;
font-weight: 500;
}
.log-level.info { background: rgba(23, 162, 184, 0.1); color: #17a2b8; }
.log-level.warning { background: rgba(255, 193, 7, 0.1); color: #ffc107; }
.log-level.error { background: rgba(220, 53, 69, 0.1); color: #dc3545; }
.log-level.notice { background: rgba(40, 167, 69, 0.1); color: #28a745; }
/* Filter Form */
.filter-form {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
/* Responsive */
@media (max-width: 768px) {
.stats-number {
font-size: 1.5rem;
}
.log-table {
font-size: 0.8rem;
}
}
</style>
<div class="container-fluid">
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">
<i class="fas fa-chart-line"></i> Payment Reminder Logs & Statistiken
</h1>
<a href="{{ route('admin_payments_reminder') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Zurück zur Übersicht
</a>
</div>
<!-- Statistiken Übersicht -->
{{-- <div class="row mb-4">
<!-- 7 Tage -->
<div class="col-md-4 mb-3">
<div class="card stats-card bg-primary text-white">
<div class="card-body text-center">
<div class="stats-number">{{ $stats7Days['summary']['total_logs'] }}</div>
<div class="stats-label">Logs (7 Tage)</div>
<div class="mt-2">
<small>
<i class="fas fa-envelope"></i> {{ $stats7Days['summary']['emails_sent'] }} gesendet |
<i class="fas fa-exclamation-triangle"></i> {{ $stats7Days['summary']['emails_failed'] }} fehlgeschlagen
</small>
</div>
</div>
</div>
</div>
<!-- 30 Tage -->
<div class="col-md-4 mb-3">
<div class="card stats-card bg-success text-white">
<div class="card-body text-center">
<div class="stats-number">{{ $stats30Days['summary']['total_logs'] }}</div>
<div class="stats-label">Logs (30 Tage)</div>
<div class="mt-2">
<small>
<i class="fas fa-envelope"></i> {{ $stats30Days['summary']['emails_sent'] }} gesendet |
<i class="fas fa-exclamation-triangle"></i> {{ $stats30Days['summary']['emails_failed'] }} fehlgeschlagen
</small>
</div>
</div>
</div>
</div>
<!-- 90 Tage -->
<div class="col-md-4 mb-3">
<div class="card stats-card bg-info text-white">
<div class="card-body text-center">
<div class="stats-number">{{ $stats90Days['summary']['total_logs'] }}</div>
<div class="stats-label">Logs (90 Tage)</div>
<div class="mt-2">
<small>
<i class="fas fa-envelope"></i> {{ $stats90Days['summary']['emails_sent'] }} gesendet |
<i class="fas fa-exclamation-triangle"></i> {{ $stats90Days['summary']['emails_failed'] }} fehlgeschlagen
</small>
</div>
</div>
</div>
</div>
</div>
--}}
<!-- Filter -->
<div class="card filter-form">
<div class="card-body">
<h5 class="card-title mb-3">
<i class="fas fa-filter"></i> Filter
</h5>
<form method="GET" action="{{ route('admin_payments_reminder_logs') }}" class="row">
<div class="col-md-3 mb-2">
<label for="order_id" class="form-label">Order ID</label>
<input type="number" class="form-control" id="order_id" name="order_id" value="{{ $orderId }}" placeholder="Order ID">
</div>
<div class="col-md-3 mb-2">
<label for="action" class="form-label">Aktion</label>
<select class="form-control custom-select" id="action" name="action">
<option value="">Alle Aktionen</option>
<option value="email_sent" {{ $action == 'email_sent' ? 'selected' : '' }}>E-Mail gesendet</option>
<option value="email_exception" {{ $action == 'email_exception' ? 'selected' : '' }}>E-Mail Fehler</option>
<option value="reminder_completed" {{ $action == 'reminder_completed' ? 'selected' : '' }}>Erinnerung abgeschlossen</option>
<option value="action_completed" {{ $action == 'action_completed' ? 'selected' : '' }}>Aktion abgeschlossen</option>
</select>
</div>
<div class="col-md-2 mb-2">
<label for="start_date" class="form-label">Von</label>
<input type="date" class="form-control" id="start_date" name="start_date" value="{{ $startDate }}">
</div>
<div class="col-md-2 mb-2">
<label for="end_date" class="form-label">Bis</label>
<input type="date" class="form-control" id="end_date" name="end_date" value="{{ $endDate }}">
</div>
<div class="col-md-2 mb-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary me-2">
<i class="fas fa-search"></i> Filtern
</button>
<a href="{{ route('admin_payments_reminder_logs') }}" class="btn btn-outline-secondary">
<i class="fas fa-times"></i> Reset
</a>
</div>
</form>
</div>
</div>
<!-- Logs Tabelle -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-list"></i>
@if($filteredLogs)
Gefilterte Logs ({{ $filteredLogs->count() }} Einträge)
@else
Neueste Logs ({{ $recentLogs->count() }} Einträge)
@endif
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped log-table" id="logs-table">
<thead>
<tr>
<th>Datum</th>
<th>Aktion</th>
<th>Level</th>
<th>Model</th>
<th>Model ID</th>
<th>Nachricht</th>
</tr>
</thead>
<tbody>
@forelse(($filteredLogs ?: $recentLogs) as $log)
<tr>
<td>
<small>{{ $log->created_at->format('d.m.Y H:i:s') }}</small>
</td>
<td>
<span class="badge badge-secondary">{{ $log->action }}</span>
</td>
<td>
<span class="log-level {{ $log->getLevelType() }}">
{{ ucfirst($log->getLevelType()) }}
</span>
</td>
<td>
<small>{{ $log->model ?: '-' }}</small>
</td>
<td>
@if($log->model_id)
<a href="{{ route('admin_sales_detail', $log->model_id) }}" target="_blank" class="btn btn-sm btn-outline-primary">
{{ $log->model_id }}
</a>
@else
<small>-</small>
@endif
</td>
<td>
<small>{{ Str::limit($log->message, 100) }}</small>
@if(strlen($log->message) > 100)
<button type="button" class="btn btn-sm btn-link" data-toggle="tooltip" data-placement="top" title="{{ $log->message }}">
<i class="fas fa-eye"></i>
</button>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center text-muted">
<i class="fas fa-inbox"></i> Keine Logs gefunden
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
<!-- Detaillierte Statistiken -->
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-chart-pie"></i> Aktionen (30 Tage)
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Aktion</th>
<th>Level</th>
<th>Anzahl</th>
</tr>
</thead>
<tbody>
@foreach($stats30Days['detailed_stats'] as $stat)
<tr>
<td><small>{{ $stat->action }}</small></td>
<td>
<span class="log-level {{ $stat->level == 2 ? 'info' : ($stat->level == 5 ? 'error' : 'notice') }}">
{{ $stat->level == 2 ? 'Info' : ($stat->level == 5 ? 'Error' : 'Notice') }}
</span>
</td>
<td><strong>{{ $stat->count }}</strong></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-chart-bar"></i> Erfolgsrate (30 Tage)
</h5>
</div>
<div class="card-body">
@php
$totalEmails = $stats30Days['summary']['emails_sent'] + $stats30Days['summary']['emails_failed'];
$successRate = $totalEmails > 0 ? round(($stats30Days['summary']['emails_sent'] / $totalEmails) * 100, 1) : 0;
@endphp
<div class="text-center">
<div class="display-4 text-success">{{ $successRate }}%</div>
<div class="text-muted">Erfolgsrate</div>
<div class="mt-3">
<div class="progress">
<div class="progress-bar bg-success" style="width: {{ $successRate }}%"></div>
</div>
</div>
<div class="mt-2">
<small class="text-muted">
{{ $stats30Days['summary']['emails_sent'] }} erfolgreich / {{ $totalEmails }} gesamt
</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
// DataTable für Logs
$('#logs-table').DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.10.24/i18n/German.json"
},
"order": [[0, "desc"]], // Sortiere nach Datum absteigend
"pageLength": 50,
"responsive": true
});
// Tooltips initialisieren
$('[data-toggle="tooltip"]').tooltip();
});
</script>
@endsection

View file

@ -0,0 +1,176 @@
@extends('layouts.layout-2')
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">
<i class="fas fa-clock"></i> Payment Reminders
</h3>
<div class="card-tools">
<span class="badge badge-primary">{{ $totalPayments }} offene Zahlungen</span>
<span class="badge badge-warning">{{ number_format($totalAmount, 2, ',', '.') }} Gesamtbetrag</span>
</div>
</div>
<div class="card-body">
<!-- Statistiken -->
<div class="row mb-4">
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-info"><i class="fas fa-list"></i></span>
<div class="info-box-content">
<span class="info-box-text">Offene Zahlungen</span>
<span class="info-box-number">{{ $totalPayments }}</span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-warning"><i class="fas fa-euro-sign"></i></span>
<div class="info-box-content">
<span class="info-box-text">Gesamtbetrag</span>
<span class="info-box-number">{{ number_format($totalAmount, 2, ',', '.') }} </span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-success"><i class="fas fa-credit-card"></i></span>
<div class="info-box-content">
<span class="info-box-text">Zahlungsarten</span>
<span class="info-box-number">{{ count($clearingTypes) }}</span>
</div>
</div>
</div>
<div class="col-md-3">
<div class="info-box">
<span class="info-box-icon bg-danger"><i class="fas fa-calendar"></i></span>
<div class="info-box-content">
<span class="info-box-text">Letzte Aktualisierung</span>
<span class="info-box-number">{{ now()->format('d.m.Y H:i') }}</span>
</div>
</div>
</div>
</div>
<!-- Übersicht nach Zahlungsarten -->
<div class="row mb-4">
@foreach($summaryData as $clearingtype => $data)
<div class="col-md-4">
<div class="small-box bg-info">
<div class="inner">
<h3>{{ $data['count'] }}</h3>
<p>{{ strtoupper($clearingtype) }} - {{ $data['interval'] }} Tage</p>
</div>
<div class="icon">
<i class="fas fa-credit-card"></i>
</div>
</div>
</div>
@endforeach
</div>
<!-- Detaillierte Tabelle -->
<div class="table-responsive">
<table class="table table-bordered table-striped" id="payment-reminders-table">
<thead>
<tr>
<th>Zahlungsart</th>
<th>Intervall (Tage)</th>
<th>Bestell-ID</th>
<th>Zahlungs-ID</th>
<th>Betrag</th>
<th>Erstellt am</th>
<th>Tage alt</th>
<th>Grenzdatum</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
@forelse($detailedData as $payment)
<tr>
<td>
<span class="badge badge-primary">{{ strtoupper($payment['clearingtype']) }}</span>
</td>
<td>{{ $payment['interval_days'] }}</td>
<td>
<a href="{{ route('admin_sales_detail', $payment['order_id']) }}" target="_blank">
#{{ $payment['order_id'] }}
</a>
</td>
<td>{{ $payment['payment_id'] }}</td>
<td>
<span class="text-danger font-weight-bold">
{{ number_format($payment['amount'], 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['date_limit'] }}</td>
<td>
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-primary"
onclick="sendReminder({{ $payment['payment_id'] }})">
<i class="fas fa-envelope"></i> Erinnerung senden
</button>
<button type="button" class="btn btn-sm btn-outline-info"
onclick="viewDetails({{ $payment['payment_id'] }})">
<i class="fas fa-eye"></i> Details
</button>
</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>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
$('#payment-reminders-table').DataTable({
"language": {
"url": "//cdn.datatables.net/plug-ins/1.10.24/i18n/German.json"
},
"order": [[6, "desc"]], // Sortiere nach "Tage alt" absteigend
"pageLength": 25,
"responsive": true
});
});
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>
@endsection

View file

@ -20,7 +20,7 @@
{{ __('Create/Edit Produkt') }}
</h4>
{!! Form::open(['url' => route('admin_product_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
{!! Form::open(['action' => route('admin_product_store'), 'class' => 'form-horizontal', 'id'=>'']) !!}
<input type="hidden" name="id" id="id" value="@if($product->id>0){{$product->id}}@else new @endif">
<div class="text-left mt-0 mb-2">

View file

@ -20,7 +20,7 @@
{{ __('Create/Edit Promotion') }}
</h4>
{!! Form::open(['url' => route('admin_promotion_detail', $promotion->id), 'class' => 'form-horizontal']) !!}
{!! Form::open(['action' => route('admin_promotion_detail', $promotion->id), 'class' => 'form-horizontal']) !!}
<input type="hidden" name="id" id="id" value="@if($promotion->id>0){{$promotion->id}}@else new @endif">
<div class="text-left mt-0 mb-2">

View file

@ -3,14 +3,14 @@
<!-- Status -->
<div class="card-body">
<div class="row">
<div class="col-md-3 mb-1">
<div class="col-sm-6 col-md-4 col-lg-3 mb-1">
<strong class="mr-2">Status:</strong>
<span class="text-big">
{!! \App\Services\Payment::getPaymentForTypeBadge($shopping_order) !!}
{!! \App\Services\Payment::getShoppingOrderBadge($shopping_order) !!}
</span>
</div>
<div class="col-md-3 mb-1">
<div class="col-sm-6 col-md-4 col-lg-3 mb-1">
<strong class="mr-2">Versand:</strong>
@if($isAdmin)
<button type="button" class="btn btn-sm btn-{{$shopping_order->getShippedColor()}}" data-toggle="modal" data-target="#modals-shipped"
@ -26,7 +26,7 @@
</span>
@endif
</div>
<div class="col-md-3 mb-1">
<div class="col-sm-6 col-md-4 col-lg-3 mb-1">
<strong class="mr-2">Rechnung:</strong>
@if($isAdmin)
@if(App\Services\Invoice::isInvoice($shopping_order))
@ -59,7 +59,7 @@
@endif
</div>
<div class="col-md-3 mb-1">
<div class="col-sm-6 col-md-4 col-lg-3 mb-1">
@if ($isAdmin)
<strong class="mr-2">Lieferschein:</strong>
@if (App\Services\Invoice::isDelivery($shopping_order))
@ -70,10 +70,14 @@
<a href="{{ route('storage_file', [$shopping_order->id, 'invoice_delivery', 'download']) }}"
class="btn btn-dark btn-sm"><i class="fa fa-file-download"></i><i class="fa fa-file-download"></i></a>
<a href="{{ route('admin_sales_send_logistic_mail', [$shopping_order->id]) }}"
class="btn btn-info btn-sm"><i class="fa fa-envelope"></i></a>
@endif
@endif
</div>
</div>
</div>
@ -96,9 +100,8 @@
{{$shopping_order->getFormattedTotalShipping()}} &euro;
</div>
<div class="col-md-3 mb-3">
{{-- <div class="text-muted small">Points gesamt</div>
{{ $shopping_order->points }}
--}}
<div class="text-muted small">White Label</div>
{{ $shopping_order->user_white_label ? 'Ja' : 'Nein' }}
</div>
</div>
</div>
@ -134,7 +137,7 @@
<button type="button" class="btn btn-xs btn-outline-info" data-toggle="modal" data-target="#modals-load-content"
data-id="{{$shopping_order->shopping_user->id}}"
data-action="shopping-user-is-like-member"
data-back="{{route('admin_sales_customers_detail', [$shopping_order->id])}}"
data-back="{{route('admin_sales_detail', [$shopping_order->id])}}"
data-modal="modal-xl"
data-route="{{route('modal_load')}}"><span class="fa fa-edit"></span> Vertriebspartner zuordnen</button>
@else

View file

@ -0,0 +1,15 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
<a href="{{route('admin_sales')}}" class="btn btn-sm btn-default float-right">zurück</a>
@if($isView === 'sales_customer')
{{ __('Bestellung Kunde') }} <span class="text-muted">#{{$shopping_order->id}}</span>
@elseif($isView === 'sales_user')
{{ __('Bestellung Vertriebspartner') }} <span class="text-muted">#{{$shopping_order->id}}</span>
@endif
</h4>
@include('admin.sales._detail')
<a href="{{route('admin_sales')}}" class="btn btn-sm btn-default mt-2 float-right">zurück</a>
@endsection

View file

@ -0,0 +1,128 @@
@extends('layouts.layout-2')
@section('content')
<h4 class="font-weight-bold py-2 mb-2">
{{ __('Bestellungen Kunden') }}
</h4>
<div class="card">
<div class="card-header">
<div class="form-row align-items-center">
<div class="col-sm-6 col-md-4 col-lg-3 mb-2">
<label class="form-label" for="filter_member_id">Filter zugewiesener Vertriebspartner</label>
<select class="custom-select" name="filter_member_id" id="filter_member_id">
<option value="">Filter aus</option>
@foreach($filter_members as $member)
<option value="{{$member->id}}" @if(get_user_attr('filter_member_id') == $member->id) selected @endif>{{$member->first_name}} {{$member->last_name}} |{{$member->email}}</option>
@endforeach
</select>
</div>
<div class="col-sm-6 col-md-4 col-lg-3 mb-2">
<label class="form-label" for="filter_txaction">Filter Status</label>
<select class="custom-select" name="filter_txaction" id="filter_txaction">
<option value="">Filter aus</option>
@foreach(\App\Services\Payment::$txaction_text as $id=>$name)
<option value="{{$id}}" @if(get_user_attr('filter_txaction') == $id) selected @endif>{{$name}}</option>
@endforeach
</select>
</div>
<div class="col-sm-6 col-md-4 col-lg-3 mb-2">
<label class="form-label" for="filter_shipped">Filter Versand</label>
<select class="custom-select" name="filter_shipped" id="filter_shipped">
<option value="">Filter aus</option>
@foreach(\App\Models\ShoppingOrder::$shippedTypes as $id=>$name)
<option value="{{$id}}" @if(get_user_attr('filter_shipped') === $id) selected @endif>{{$name}}</option>
@endforeach
</select>
</div>
<div class="col-sm-6 col-md-4 col-lg-2 mb-2">
<label class="form-label" for="filter_art">Filter Art</label>
<select class="custom-select" name="filter_art" id="filter_art">
<option value="">Filter aus</option>
@foreach(\App\Services\PaymentService::$txaction_art as $id=>$name)
<option value="{{$id}}" @if(get_user_attr('filter_art') == $id) selected @endif>{{$name}}</option>
@endforeach
</select>
</div>
<div class="col-sm-1 col-md-1 col-lg-1 mb-2 mt-4">
<a href="{{ route('admin_sales') }}?reset=filter" data-toggle="tooltip" data-placement="top" title="Reset Filter" class="btn icon-btn btn-sm btn-outline-dark float-right">
<span class="fa fa-sync"></span>
</a>
</div>
</div>
</div>
<div class="card-datatable table-responsive">
<table class="datatable-customers table table-striped table-bordered" id="datatable-customers">
<thead>
<tr>
<th>#</th>
<th>{{__('Datum')}}</th>
<th>{{__('Betrag')}}</th>
<th>{{__('Zahlung')}}</th>
<th>{{__('Status')}}</th>
<th>{{__('Versand')}}</th>
<th>{{__('Art')}}</th>
<th>{{__('First name')}}</th>
<th>{{__('Last name')}}</th>
<th>{{__('E-Mail')}}</th>
<th>{{__('zugewiesener Vertriebspartner')}}</th>
<th>{{__('Rf-Nr.')}}</th>
</tr>
</thead>
</table>
</div>
</div>
<script>
$( document ).ready(function() {
var oTable = $('#datatable-customers').DataTable({
"processing": true,
"serverSide": true,
ajax: {
url: '{!! route( 'admin_sales_datatable') !!}',
data: function(d) {
d.filter_member_id = $('select[name=filter_member_id]').val();
d.filter_txaction = $('select[name=filter_txaction]').val();
d.filter_art = $('select[name=filter_art]').val();
d.filter_shipped = $('select[name=filter_shipped]').val();
}
},
"order": [[0, "desc" ]],
"columns": [
{ data: 'id', searchable: false },
{ data: 'created_at', name: 'shopping_orders.created_at' },
{ data: 'total_shipping', name: 'total_shipping' },
{ data: 'payment', name: 'payment', orderable: false },
{ data: 'txaction', name: 'txaction' },
{ data: 'shipped', name: 'shipped' },
{ data: 'payment_for', name: 'payment_for' },
{ data: 'shopping_user.billing_firstname', name: 'shopping_user.billing_firstname' },
{ data: 'shopping_user.billing_lastname', name: 'shopping_user.billing_lastname' },
{ data: 'shopping_user.billing_email', name: 'shopping_user.billing_email' },
{ data: 'member_id', name: 'member_id', searchable: false, orderable: false },
{ data: 'reference', name: 'reference' },
],
"bLengthChange": false,
"iDisplayLength": 100,
"language": {
"url": "/js/German.json"
}
});
$('#filter_member_id').on('change', function(){
oTable.draw();
});
$('#filter_txaction').on('change', function(){
oTable.draw();
});
$('#filter_art').on('change', function(){
oTable.draw();
});
$('#filter_shipped').on('change', function(){
oTable.draw();
});
});
</script>
@endsection

Some files were not shown because too many files have changed in this diff Show more