added invoice controller

added order relations
fixed ticket bug
master
A1Gard 2 weeks ago
parent e9402cac80
commit 63b96c1674

@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Controllers\XController;
use App\Http\Requests\InvoiceSaveRequest;
use App\Models\Access;
use App\Models\Credit;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\Order;
use Illuminate\Http\Request;
use App\Helper;
use function App\Helpers\hasCreateRoute;
class InvoiceController extends XController
{
// protected $_MODEL_ = Invoice::class;
// protected $SAVE_REQUEST = InvoiceSaveRequest::class;
protected $cols = ['hash', 'customer_id', 'count', 'total_price', 'status'];
protected $extra_cols = ['id'];
protected $searchable = ['desc'];
protected $listView = 'admin.invoices.invoice-list';
protected $formView = 'admin.invoices.invoice-form';
protected $buttons = [
'edit' =>
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
'show' =>
['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
'destroy' =>
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
];
public function __construct()
{
parent::__construct(Invoice::class, InvoiceSaveRequest::class);
}
/**
* @param $invoice Invoice
* @param $request InvoiceSaveRequest
* @return Invoice
*/
public function save($invoice, $request)
{
$invoice->transport_id = $request->input('transport_id', null);
$invoice->address_id = $request->input('address_id', null);
$invoice->tracking_code = $request->tracking_code;
$invoice->status = $request->status;
$invoice->save();
return $invoice;
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
return view($this->formView);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Invoice $item)
{
//
return view($this->formView, compact('item'));
}
public function bulk(Request $request)
{
// dd($request->all());
$data = explode('.', $request->input('action'));
$action = $data[0];
$ids = $request->input('id');
switch ($action) {
case 'delete':
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
$this->_MODEL_::destroy($ids);
break;
/**restore*/
case 'restore':
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
foreach ($ids as $id) {
$this->_MODEL_::withTrashed()->find($id)->restore();
}
break;
/*restore**/
default:
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
}
return $this->do_bulk($msg, $action, $ids);
}
public function destroy(Invoice $item)
{
return parent::delete($item);
}
public function update(Request $request, Invoice $item)
{
return $this->bringUp($request, $item);
}
/**restore*/
public function restore($item)
{
return parent::restoreing(Invoice::withTrashed()->where('id', $item)->first());
}
public function removeOrder(Order $order)
{
$customer = Customer::whereId($order->invoice->customer_id)->first();
if ($order->price_total > 0) {
$diff = $order->price_total;
$customer->credit += $diff;
$customer->save();
$cr = new Credit();
$cr->customer_id = $customer->id;
$cr->amount = $diff;
$cr->data = json_encode([
'user_id' => auth()->user()->id,
'message' => __("Increase by Admin removed:") . ' ' . $order->product->name . __("Invoice") . ' : ' . $order->invoice->hash,
]);
$cr->save();
$order->delete();
}
return redirect()->back()->with('message', __('Order removed successfully'));
}
/*restore**/
}

@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Contracts\Payment;
use App\Http\Requests\ContactSubmitRequest;
use App\Models\Attachment;
use App\Models\Category;
@ -11,6 +12,7 @@ use App\Models\Contact;
use App\Models\Customer;
use App\Models\Gallery;
use App\Models\Group;
use App\Models\Invoice;
use App\Models\Post;
use App\Models\Product;
use App\Models\Quantity;
@ -620,4 +622,42 @@ class ClientController extends Controller
{
return $this->welcome();
}
public function pay($hash){
$invoice = Invoice::where('hash', $hash)->first();
// dd($invoice->created_at->timestamp , (time() - 3600));
if (!in_array($invoice->status, ['PENDING', 'CANCELED', 'FAILED'] ) || $invoice->created_at->timestamp < (time() - 3600) ){
return redirect()->back()->withErrors(__('This payment method is not available.'));
}
$activeGateway = config('xshop.payment.active_gateway');
/** @var Payment $gateway */
$gateway = app($activeGateway . '-gateway');
logger()->info('pay controller', ["active_gateway" => $activeGateway, "invoice" => $invoice->toArray(),]);
if ($invoice->isCompleted()) {
return redirect()->back()->with('message', __('Invoice payed.'));
}
$callbackUrl = route('pay.check', ['invoice_hash' => $invoice->hash, 'gateway' => $gateway->getName()]);
$payment = null;
try {
$response = $gateway->request((($invoice->total_price - $invoice->credit_price) * config('app.currency.factor')), $callbackUrl);
$payment = $invoice->storePaymentRequest($response['order_id'], (($invoice->total_price - $invoice->credit_price) * config('app.currency.factor')), $response['token'] ?? null, null, $gateway->getName());
session(["payment_id" => $payment->id]);
\Session::save();
return $gateway->goToBank();
} catch (\Throwable $exception) {
$invoice->status = 'FAILED';
$invoice->save();
\Log::error("Payment REQUEST exception: " . $exception->getMessage());
\Log::warning($exception->getTraceAsString());
$result = false;
$message = __('error in payment. contact admin.');
return redirect()->back()->withErrors($message);
}
}
}

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class InvoiceSaveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'transport_id' => ['nullable', 'integer', 'exists:transports,id'],
'address_id' => ['nullable', 'integer', 'exists:addresses,id'],
'tracking_code' => ['nullable', 'string'],
'status' => ['required', 'string'],
];
}
}

@ -6,10 +6,11 @@ use App\Events\InvoiceFailed;
use App\Events\InvoiceSucceed;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Invoice extends Model
{
use HasFactory;
use HasFactory,SoftDeletes;
const PENDING = 'PENDING';
const PROCESSING = 'PROCESSING';
@ -112,7 +113,7 @@ class Invoice extends Model
$payment->status = "SUCCESS";
$payment->save();
/** @var \App\Models\Invoice $this */
$this->status = "COMPLETED";
$this->status = "PAID";
$this->save();
try {
event(new InvoiceSucceed($this, $payment));

@ -8,4 +8,16 @@ use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
public function product(){
return $this->belongsTo(Product::class);
}
public function quantity(){
return $this->belongsTo(Quantity::class);
}
public function invoice(){
return $this->belongsTo(Invoice::class);
}
}

@ -8,7 +8,7 @@ use Illuminate\Database\Eloquent\Model;
class Ticket extends Model
{
// use HasFactory;
public static $ticket_statuses = [['PENDING','ANSWERED','CLOSED']];
public static $ticket_statuses = ['PENDING','ANSWERED','CLOSED'];
public function customer(){

@ -97,16 +97,23 @@ a.btn,a.action-btn,a.circle-btn{
margin: auto;
border-radius: 3px;
}
.status-0,.status-CLOSED {
.status-0,.status-CLOSED,.status-FAILED,.status-PROCESSING {
background: red;
}
.status-1,.status-ANSWERED{
.status-1,.status-ANSWERED,.status-COMPLETED{
background: lime;
}
.status-PENDING{
background: gold;
}
.status-CANCELED{
background: orange;
}
.status-PAID{
background: white;
}
.image-x64{
height: 64px;

@ -0,0 +1,229 @@
@extends('admin.templates.panel-form-template')
@section('title')
@if(isset($item))
{{__("Edit invoice")}} [{{$item->id}}]
@else
{{__("Add new invoice")}}
@endif -
@endsection
@section('form')
<div class="row">
<div class="col-lg-3">
@include('components.err')
<div class="item-list mb-3">
<h3 class="p-3">
<i class="ri-message-3-line"></i>
{{__("Tips")}}
</h3>
<ul>
<li>
{{__("If you cancel this, You must increase credit yourself.")}}
</li>
<li>
{{__("If you change transport method you must think about think about the price diffrance")}}
</li>
<li>
{{__("If you removed order from invoice, system adding amount to customer's credit automatically")}}
</li>
</ul>
</div>
<div class="item-list mb-3">
<h3 class="p-3">
<i class="ri-user-line"></i>
{{__("Customer")}}
</h3>
<ul>
<li class="mb-2">
<a href="{{route('admin.customer.show',$item->customer->id)}}">
{{__("Name")}}: {{$item->customer->name}}
</a>
</li>
<li class="mb-2">
<a href="{{route('admin.customer.show',$item->customer->id)}}">
{{__("Mobile")}}: {{$item->customer->mobile}}
</a>
</li>
<li class="mb-2">
<a href="{{route('admin.customer.show',$item->customer->id)}}">
{{__("Successfully Invoices")}}
: {{number_format($item->customer->invoices()->whereIn('status',[ 'PAID', 'PROCESSING', 'COMPLETED'])->count())}}
</a>
</li>
<li class="mb-2">
<a href="{{route('admin.customer.show',$item->customer->id)}}">
{{__("Failed Invoices")}}
: {{number_format($item->customer->invoices()->whereIn('status',[ 'PENDING', 'CANCELED', 'FAILED'])->count())}}
</a>
</li>
</ul>
</div>
@if( $item->desc != null && trim($item->desc) != '')
<div class="item-list mb-3">
<h3 class="p-3">
<i class="ri-message-line"></i>
{{__("Description")}}
</h3>
<p class="px-4">
{{$item->desc}}
</p>
</div>
@endif
</div>
<div class="col-lg-9 ps-xl-1 ps-xxl-1">
<div class="general-form ">
<h1>
@if(isset($item))
{{__("Edit invoice")}} [{{$item->id}}]
@else
{{__("Add new invoice")}}
@endif
</h1>
<div class="row">
<div class="col-md-6 mt-3">
<div class="form-group">
<label for="tracking_code">
{{__('Tracking code')}}
</label>
<input name="tracking_code" type="text"
class="form-control @error('tracking_code') is-invalid @enderror" id="tracking_code"
placeholder="{{__('Tracking code')}}" value="{{old('tracking_code',$item->tracking_code??null)}}"/>
</div>
</div>
<div class="col-md-6 mt-3">
<div class="form-group">
<label for="status">
{{__('Status')}}
</label>
<searchable-select
:items='{{arrayNormolizeVueCompatible(\App\Models\Invoice::$invoiceStatus, true)}}'
title-field="name"
value-field="name"
xname="status"
@error('status') :err="true" @enderror
xvalue='{{old('status',$item->status??null)}}'
:close-on-Select="true"></searchable-select>
</div>
</div>
<div class="col-md-12 mt-3">
<h5>
{{__("Address")}}
</h5>
<ul class="list-group">
@foreach($item->customer->addresses as $adr)
<li class="list-group-item">
<label>
<input type="radio" name="address_id" value="{{$adr->id}}"
@if($adr->id == $item->address_id) checked @endif/>
{{$adr->address}}
</label>
</li>
@endforeach
</ul>
</div>
<div class="col-md-12 mt-3">
<h5>
{{__("Address")}}
</h5>
<ul class="list-group">
@foreach(\App\Models\Transport::all() as $t)
<li class="list-group-item">
<label>
<input type="radio" name="transport_id" value="{{$t->id}}"
@if($t->id == $item->transport_id) checked @endif/>
{{$t->title}} ({{number_format($t->price)}})
</label>
</li>
@endforeach
</ul>
</div>
<div class="col-md-12">
<label> &nbsp;</label>
<input name="" type="submit" class="btn btn-primary mt-2" value="{{__('Save')}}"/>
</div>
</div>
</div>
</div>
<div class="mt-4">
<table class="table table-striped align-middle">
<tr>
<th>
#
</th>
<th>
{{__("Product")}}
</th>
<th>
{{__("Count")}}
</th>
<th>
{{__("Quantity")}}
</th>
<th>
{{__("Price")}}
</th>
<th>
-
</th>
</tr>
@foreach($item->orders as $k => $order)
<tr>
<td>
{{$k + 1}}
</td>
<td>
{{$order->product->name}}
</td>
<td>
{{number_format($order->count)}}
</td>
<td>
@if( ($order->quantity->meta??null) == null)
-
@else
@foreach($order->quantity->meta as $m)
<span>
{{$m->human_value}}
</span>
@endforeach
@endif
</td>
<td>
{{number_format($order->price_total)}}
</td>
<td>
<a href="{{route('admin.invoice.remove-order',$order->id)}}" class="btn btn-danger delete-confirm">
<i class="ri-close-circle-line"></i>
</a>
</td>
</tr>
@endforeach
<tr>
<td>
-
</td>
<td>
{{__("Transport")}}
{{number_format($item->transport_price)}}
</td>
<td colspan="2">
{{__("Total price")}}
{{number_format($item->total_price)}}
</td>
<td colspan="2">
{{__("Orders count")}}: ({{number_format($item->count)}})
</td>
</tr>
</table>
</div>
</div>
@endsection

@ -0,0 +1,25 @@
@extends('admin.templates.panel-list-template')
@section('list-title')
<i class="ri-user-3-line"></i>
{{__("Invoices list")}}
@endsection
@section('title')
{{__("Invoices list")}} -
@endsection
@section('filter')
<h2>
<i class="ri-shield-check-line"></i>
{{__("Status")}}:
</h2>
<searchable-multi-select
:items='{{arrayNormolizeVueCompatible(\App\Models\Invoice::$invoiceStatus, true)}}'
title-field="name"
value-field="name"
xname="filter[status]"
:xvalue='{{request()->input('filter.status','[]')}}'
:close-on-Select="true"></searchable-multi-select>
@endsection
@section('bulk')
{{-- <option value="-"> - </option> --}}
@endsection

@ -178,7 +178,10 @@
{{ $item->parent?->{$cols[0]}??'-' }}
@break
@case('status')
<div class="model-status status-{{$item->status}} float-start"></div>
<div class="model-status status-{{$item->status}} float-start" data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-custom-class="custom-tooltip"
data-bs-title="{{$item->status}}"></div>
@break
@case('user_id')
<a href="{{route('admin.user.edit',$item->user?->email)}}">

@ -8,7 +8,17 @@
{{__("Tickets list")}} -
@endsection
@section('filter')
{{-- Other filters --}}
<h2>
<i class="ri-shield-check-line"></i>
{{__("Status")}}:
</h2>
<searchable-multi-select
:items='{{arrayNormolizeVueCompatible(\App\Models\Ticket::$ticket_statuses, true)}}'
title-field="name"
value-field="name"
xname="filter[status]"
:xvalue='{{request()->input('filter.status','[]')}}'
:close-on-Select="true"></searchable-multi-select>
@endsection
@section('bulk')
<option value="close"> {{__("Close")}} </option>

@ -13,6 +13,14 @@
<i class="ri-shopping-cart-line"></i>
</a>
<ul id="card">
@if( auth()->user()->hasAnyAccess( 'invoice' ))
<li>
<a href="{{ route('admin.invoice.index') }}">
<i class="ri-file-list-3-fill"></i>
{{__('Invoices')}}
</a>
</li>
@endif
@if( auth()->user()->hasAnyAccess( 'customer' ))
<li>
<a href="{{route('admin.customer.index')}}">
@ -22,14 +30,6 @@
</a>
</li>
@endif
@if( auth()->user()->hasAnyAccess( 'invoice' ))
<li>
<a>
<i class="ri-file-list-3-fill"></i>
{{__('Invoices')}}
</a>
</li>
@endif
@if( auth()->user()->hasAnyAccess( 'discount' ))
<li>
<a href="{{route('admin.discount.index')}}">

@ -139,6 +139,20 @@ Route::prefix(config('app.panel.prefix'))->name('admin.')->group(
Route::post('sort/save', [\App\Http\Controllers\Admin\CategoryController::class, 'sortSave'])->name('sort-save');
Route::get('sort', [\App\Http\Controllers\Admin\CategoryController::class, 'sort'])->name('sort');
});
Route::prefix('invoices')->name('invoice.')->group(
function () {
Route::get('', [\App\Http\Controllers\Admin\InvoiceController::class, 'index'])->name('index');
// Route::get('create', [\App\Http\Controllers\Admin\InvoiceController::class, 'create'])->name('create');
// Route::post('store', [\App\Http\Controllers\Admin\InvoiceController::class, 'store'])->name('store');
Route::get('edit/{item}', [\App\Http\Controllers\Admin\InvoiceController::class, 'edit'])->name('edit');
Route::get('show/{item}', [\App\Http\Controllers\Admin\InvoiceController::class, 'show'])->name('show');
Route::post('update/{item}', [\App\Http\Controllers\Admin\InvoiceController::class, 'update'])->name('update');
Route::get('delete/{item}', [\App\Http\Controllers\Admin\InvoiceController::class, 'destroy'])->name('destroy');
Route::get('restore/{item}', [\App\Http\Controllers\Admin\InvoiceController::class, 'restore'])->name('restore');
Route::get('remove/ordere/{order}', [\App\Http\Controllers\Admin\InvoiceController::class, 'removeOrder'])->name('remove-order');
Route::post('bulk', [\App\Http\Controllers\Admin\InvoiceController::class, "bulk"])->name('bulk');
Route::get('trashed', [\App\Http\Controllers\Admin\InvoiceController::class, "trashed"])->name('trashed');
});
Route::prefix('posts')->name('post.')->group(
function () {
@ -401,7 +415,8 @@ Route::middleware([\App\Http\Middleware\VisitorCounter::class])
Route::get('/gallery/{gallery}', [ClientController::class, 'gallery'])->name('gallery');
Route::get('/search', [ClientController::class, 'search'])->name('search');
Route::get('attach/download/{attachment}', [ClientController::class, 'attachDl'])->name('attach-dl');
Route::get('/post/{post}', [ClientController::class, 'post'])->name('post');
Route::get('/post/{post}', [ClientController::class, 'post'])->name('post');
Route::get('pay/{invoice}', [ClientController::class, 'pay'])->name('pay');
Route::get('product/fav/toggle/{product}', [\App\Http\Controllers\CustomerController::class, 'ProductFavToggle'])->name('product-fav-toggle');
Route::get('product/compare/toggle/{product}', [\App\Http\Controllers\CardController::class, 'productCompareToggle'])->name('product-compare-toggle');

Loading…
Cancel
Save