first upload all files

This commit is contained in:
NW
2023-06-11 13:14:03 +01:00
parent f14dbc52b5
commit c08b36d1b6
1705 changed files with 106852 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
<?php
namespace Modules\Report;
use Modules\Brand\Entities\Brand;
use Illuminate\Database\Eloquent\Builder;
class BrandedProductsReport extends Report
{
protected $filters = [];
protected function view()
{
return 'report::admin.reports.branded_products_report.index';
}
protected function query()
{
return Brand::select('id')
->when(request()->has('brand'), function (Builder $query) {
$query->whereTranslationLike('name', request('brand') . '%');
})
->withCount('products')
->orderByDesc('products_count');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Modules\Report;
use Modules\Category\Entities\Category;
use Illuminate\Database\Eloquent\Builder;
class CategorizedProductsReport extends Report
{
protected $filters = [];
protected function view()
{
return 'report::admin.reports.categorized_products_report.index';
}
protected function query()
{
return Category::withoutGlobalScope('active')
->select('id')
->when(request()->has('category'), function (Builder $query) {
$query->whereTranslationLike('name', request('category') . '%');
})
->withCount('products')
->orderByDesc('products_count');
}
}

View File

@@ -0,0 +1,22 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Define which assets will be available through the asset manager
|--------------------------------------------------------------------------
| These assets are registered on the asset manager
*/
'all_assets' => [
'admin.report.css' => ['module' => 'report:admin/css/report.css'],
'admin.report.js' => ['module' => 'report:admin/js/report.js'],
],
/*
|--------------------------------------------------------------------------
| Define which default assets will always be included in your pages
| through the asset pipeline
|--------------------------------------------------------------------------
*/
'required_assets' => [],
];

View File

@@ -0,0 +1,7 @@
<?php
return [
'admin.reports' => [
'index' => 'report::permissions.index',
],
];

View File

@@ -0,0 +1,30 @@
<?php
namespace Modules\Report;
use Modules\Coupon\Entities\Coupon;
class CouponsReport extends Report
{
protected $date = 'orders.created_at';
protected function view()
{
return 'report::admin.reports.coupons_report.index';
}
protected function query()
{
return Coupon::withoutGlobalScope('active')
->select('coupons.id', 'code')
->join('orders', 'coupons.id', '=', 'orders.coupon_id')
->selectRaw('MIN(orders.created_at) as start_date')
->selectRaw('MAX(orders.created_at) as end_date')
->selectRaw('COUNT(*) as total_orders')
->selectRaw('SUM(orders.discount) as total')
->when(request()->has('coupon_code'), function ($query) {
$query->where('code', request('coupon_code'));
})
->groupBy(['coupons.id', 'coupons.code']);
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Modules\Report;
use Modules\Order\Entities\Order;
class CustomersOrderReport extends Report
{
protected function view()
{
return 'report::admin.reports.customers_order_report.index';
}
protected function query()
{
return Order::select('customer_id', 'customer_first_name', 'customer_last_name', 'customer_email')
->selectRaw('MIN(orders.created_at) as start_date')
->selectRaw('MAX(orders.created_at) as end_date')
->selectRaw('COUNT(*) as total_orders')
->join('order_products', 'orders.id', '=', 'order_products.order_id')
->selectRaw('SUM(order_products.qty) as total_products')
->selectRaw('SUM(orders.total) as total')
->when(request()->has('customer_name'), function ($query) {
$query->where('customer_first_name', 'like', request('customer_name') . '%')
->orWhere('customer_last_name', 'like', request('customer_name') . '%');
})
->when(request()->has('customer_email'), function ($query) {
$query->where('customer_email', request('customer_email'));
})
->groupBy([
'orders.id',
'customer_id',
'customer_first_name',
'customer_last_name',
'customer_email',
]);
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Modules\Report\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Modules\Report\TaxReport;
use Modules\Report\SalesReport;
use Modules\Report\SearchReport;
use Modules\Report\CouponsReport;
use Modules\Report\ShippingReport;
use Modules\Report\ProductsViewReport;
use Modules\Report\ProductsStockReport;
use Modules\Report\TaxedProductsReport;
use Modules\Report\CustomersOrderReport;
use Modules\Report\TaggedProductsReport;
use Modules\Report\BrandedProductsReport;
use Modules\Report\ProductsPurchaseReport;
use Modules\Report\CategorizedProductsReport;
class ReportController
{
/**
* Array of available reports.
*
* @var array
*/
private $reports = [
'coupons_report' => CouponsReport::class,
'customers_order_report' => CustomersOrderReport::class,
'products_purchase_report' => ProductsPurchaseReport::class,
'products_stock_report' => ProductsStockReport::class,
'products_view_report' => ProductsViewReport::class,
'branded_products_report' => BrandedProductsReport::class,
'categorized_products_report' => CategorizedProductsReport::class,
'taxed_products_report' => TaxedProductsReport::class,
'tagged_products_report' => TaggedProductsReport::class,
'sales_report' => SalesReport::class,
'search_report' => SearchReport::class,
'shipping_report' => ShippingReport::class,
'tax_report' => TaxReport::class,
];
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$type = $request->query('type');
if (! $this->reportTypeExists($type)) {
return redirect()->route('admin.reports.index', ['type' => 'coupons_report']);
}
return $this->report($type)->render($request);
}
/**
* Determine if the report type exists.
*
* @param string $type
* @return bool
*/
private function reportTypeExists($type)
{
return array_key_exists($type, $this->reports);
}
/**
* Returns a new instance of the given type of report.
*
* @param string $type
* @return mixed
*/
private function report($type)
{
return new $this->reports[$type];
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\Report;
use Modules\Product\Entities\Product;
class ProductsPurchaseReport extends Report
{
protected $date = 'orders.created_at';
protected function view()
{
return 'report::admin.reports.products_purchase_report.index';
}
protected function query()
{
return Product::withoutGlobalScope('active')
->select('products.id')
->join('order_products', 'products.id', '=', 'order_products.product_id')
->selectRaw('SUM(order_products.qty) as qty')
->selectRaw('SUM(order_products.line_total) as total')
->join('orders', 'order_products.order_id', '=', 'orders.id')
->selectRaw('MIN(orders.created_at) as start_date')
->selectRaw('MAX(orders.created_at) as end_date')
->when(request()->has('product'), function ($query) {
$query->whereTranslationLike('name', request('product') . '%');
})
->when(request()->has('sku'), function ($query) {
$query->where('sku', request('sku'));
})
->groupBy('products.id');
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\Report;
use Modules\Product\Entities\Product;
class ProductsStockReport extends Report
{
protected function view()
{
return 'report::admin.reports.products_stock_report.index';
}
protected function query()
{
return Product::select('id', 'qty', 'in_stock')
->withName()
->when(request()->has('quantity_above'), function ($query) {
$query->where('manage_stock', true)
->where('qty', '>', request('quantity_above'));
})
->when(request()->has('quantity_below'), function ($query) {
$query->where('manage_stock', true)
->where('qty', '<', request('quantity_below'));
})
->when(request('stock_availability') === 'in_stock', function ($query) {
$query->where('in_stock', true);
})
->when(request('stock_availability') === 'out_of_stock', function ($query) {
$query->where('in_stock', false);
})
->orderByDesc('qty');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Modules\Report;
use Modules\Product\Entities\Product;
class ProductsViewReport extends Report
{
protected $filters = [];
protected function view()
{
return 'report::admin.reports.products_view_report.index';
}
protected function query()
{
return Product::withoutGlobalScope('active')
->select('id', 'viewed')
->when(request()->has('product'), function ($query) {
$query->whereTranslationLike('name', request('product') . '%');
})
->when(request()->has('sku'), function ($query) {
$query->where('sku', request('sku'));
})
->orderByDesc('viewed');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Modules\Report\Providers;
use Illuminate\Support\Facades\View;
use Modules\Support\Traits\AddsAsset;
use Illuminate\Support\ServiceProvider;
class ReportServiceProvider extends ServiceProvider
{
use AddsAsset;
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
View::composer('report::admin.reports.*', function ($view) {
$view->with('request', $this->app['request']);
});
$this->addAdminAssets('admin.reports.index', ['admin.report.css', 'admin.report.js']);
}
}

107
Modules/Report/Report.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
namespace Modules\Report;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
abstract class Report
{
protected $filters = ['from', 'to', 'status', 'group'];
protected $groups = ['years', 'months', 'weeks', 'days'];
protected $date = 'created_at';
abstract protected function view();
abstract protected function query();
protected function data()
{
return [];
}
public function render($request)
{
$report = $this->report($request)
->simplePaginate(20)
->appends($request->query());
return view($this->view())
->with(array_merge(compact('report'), $this->data()));
}
public function report($request)
{
$this->query = $this->query();
foreach ($this->filters($request) as $name => $value) {
$this->{$name}($value);
}
return $this->query;
}
private function filters($request)
{
return array_filter($request->query(), function ($value, $name) {
return ! is_null($value) && in_array($name, $this->filters);
}, ARRAY_FILTER_USE_BOTH);
}
private function from($date)
{
$this->query->whereDate($this->date, '>=', Carbon::parse($date));
}
private function to($date)
{
$this->query->whereDate($this->date, '<=', Carbon::parse($date));
}
private function status($status)
{
$this->query->where('orders.status', $status);
}
private function group($group)
{
if (in_array($group, $this->groups)) {
$this->{"groupBy{$group}"}();
}
}
private function groupByYears()
{
$this->groupAndOrderBy('YEAR');
}
private function groupByMonths()
{
$this->groupAndOrderBy('YEAR')
->groupAndOrderBy('MONTH');
}
private function groupByWeeks()
{
$this->groupAndOrderBy('YEAR')
->groupAndOrderBy('MONTH')
->groupAndOrderBy('WEEK');
}
private function groupByDays()
{
$this->groupAndOrderBy('YEAR')
->groupAndOrderBy('MONTH')
->groupAndOrderBy('WEEK')
->groupAndOrderBy('DAY');
}
private function groupAndOrderBy($part)
{
$this->query->selectRaw("EXTRACT({$part} FROM {$this->date}) as {$part}")
->groupBy(DB::raw("EXTRACT({$part} FROM {$this->date})"))
->orderbyDesc($part);
return $this;
}
}

View File

@@ -0,0 +1,9 @@
$('form').on('submit', (e) => {
$(e.currentTarget).find(':input').filter((i, el) => {
return ! el.value;
}).attr('disabled', 'disabled');
});
$('#report-type').on('change', (e) => {
window.location = route('admin.reports.index', { type: e.currentTarget.value });
});

View File

@@ -0,0 +1,59 @@
.report-wrapper {
background: transparent;
box-shadow: none;
.box-body {
margin-left: -15px;
margin-right: -15px;
}
}
.report-result {
padding: 15px 15px 10px;
background: #ffffff;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
.table-responsive {
margin-bottom: 0;
}
.table {
margin-top: 15px;
margin-bottom: 0;
td {
padding: 15px;
border: none;
&.empty {
text-align: center;
}
a {
color: #333;
}
}
}
}
.filter-report {
padding: 15px;
background: #ffffff;
border-radius: 3px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
form {
margin-top: 15px;
}
.form-group select {
width: 100%;
}
}
@media screen and (max-width: 991px) {
.filter-report {
margin-top: 30px;
}
}

View File

@@ -0,0 +1,112 @@
<?php
return [
'reports' => 'Reports',
'no_data' => 'No data available!',
'filter' => 'Filter',
'filters' => [
'report_type' => 'Report Type',
'report_types' => [
'coupons_report' => 'Coupons Report',
'customers_order_report' => 'Customers Order Report',
'products_purchase_report' => 'Products Purchase Report',
'products_stock_report' => 'Products Stock Report',
'products_view_report' => 'Products View Report',
'branded_products_report' => 'Branded Products Report',
'categorized_products_report' => 'Categorized Products Report',
'taxed_products_report' => 'Taxed Products Report',
'tagged_products_report' => 'Tagged Products Report',
'sales_report' => 'Sales Report',
'search_report' => 'Search Report',
'shipping_report' => 'Shipping Report',
'tax_report' => 'Tax Report',
],
'date_start' => 'Date Start',
'date_end' => 'Date End',
'group_by' => 'Group By',
'groups' => [
'days' => 'Days',
'weeks' => 'Weeks',
'months' => 'Months',
'years' => 'Years',
],
'please_select' => 'Please Select',
'status' => 'Order Status',
'coupon_code' => 'Coupon Code',
'customer_name' => 'Customer Name',
'customer_email' => 'Customer Email',
'product' => 'Product',
'sku' => 'SKU',
'brand' => 'Brand',
'category' => 'Category',
'tax_class' => 'Tax Class',
'tag' => 'Tag',
'keyword' => 'Keyword',
'quantity_below' => 'Quantity Below',
'quantity_above' => 'Quantity Above',
'stock_availability' => 'Stock Availability',
'stock_availability_states' => [
'in_stock' => 'In Stock',
'out_of_stock' => 'Out of Stock',
],
'shipping_method' => 'Shipping Method',
'tax_name' => 'Tax Name',
],
'table' => [
'date' => 'Date',
'orders' => 'Orders',
'products' => 'Products',
'product' => 'Product',
'products_count' => 'Products Count',
'total' => 'Total',
// coupons_report
'coupon_name' => 'Coupon Name',
'coupon_code' => 'Coupon Code',
// customer orders report
'customer_name' => 'Customer Email',
'customer_email' => 'Customer Email',
'customer_group' => 'Customer Group',
'guest' => 'Guest',
'registered' => 'Registered',
// products purchase report
'qty' => 'Qty',
// products stock report
'stock_availability' => 'Stock Availability',
// products view report
'views' => 'Views',
// branded products report
'brand' => 'Brand',
// category products report
'category' => 'Category',
// taxed products report
'tax_class' => 'Tax Class',
// tagged products report
'tag' => 'Tag',
// sales report
'subtotal' => 'Subtotal',
'shipping' => 'Shipping',
'discount' => 'Discount',
'tax' => 'Tax',
// search report
'keyword' => 'Keyword',
'results' => 'Results',
'hits' => 'Hits',
// shipping report
'shipping_method' => 'Shipping Method',
// tax report
'tax_name' => 'Tax Name',
],
];

View File

@@ -0,0 +1,5 @@
<?php
return [
'index' => 'Index Reports',
];

View File

@@ -0,0 +1,5 @@
<?php
return [
'reports' => 'Reports',
];

View File

@@ -0,0 +1,48 @@
@extends('report::admin.reports.layout')
@section('filters')
<div class="form-group">
<label for="brand">{{ trans('report::admin.filters.brand') }}</label>
<input type="text" name="brand" class="form-control" id="brand" value="{{ $request->brand }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.branded_products_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.brand') }}</th>
<th>{{ trans('report::admin.table.products_count') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $brand)
<tr>
<td>
{{ $brand->name }}
</td>
<td>
{{ $brand->products_count }}
</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,48 @@
@extends('report::admin.reports.layout')
@section('filters')
<div class="form-group">
<label for="category">{{ trans('report::admin.filters.category') }}</label>
<input type="text" name="category" class="form-control" id="category" value="{{ $request->category }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.categorized_products_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.category') }}</th>
<th>{{ trans('report::admin.table.products_count') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $category)
<tr>
<td>
{{ $category->name }}
</td>
<td>
{{ $category->products_count }}
</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,53 @@
@extends('report::admin.reports.layout')
@section('filters')
@include('report::admin.reports.filters.from')
@include('report::admin.reports.filters.to')
@include('report::admin.reports.filters.status')
@include('report::admin.reports.filters.group')
<div class="form-group">
<label for="coupon-code">{{ trans('report::admin.filters.coupon_code') }}</label>
<input type="text" name="coupon_code" class="form-control" id="coupon-code" value="{{ $request->coupon_code }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.coupons_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.date') }}</th>
<th>{{ trans('report::admin.table.coupon_name') }}</th>
<th>{{ trans('report::admin.table.coupon_code') }}</th>
<th>{{ trans('report::admin.table.orders') }}</th>
<th>{{ trans('report::admin.table.total') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $data)
<tr>
<td>{{ $data->start_date->toFormattedDateString() }} - {{ $data->end_date->toFormattedDateString() }}</td>
<td>{{ $data->name }}</td>
<td>{{ $data->code }}</td>
<td>{{ $data->total_orders }}</td>
<td>{{ $data->total->format() }}</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,62 @@
@extends('report::admin.reports.layout')
@section('filters')
@include('report::admin.reports.filters.from')
@include('report::admin.reports.filters.to')
@include('report::admin.reports.filters.status')
@include('report::admin.reports.filters.group')
<div class="form-group">
<label for="customer-name">{{ trans('report::admin.filters.customer_name') }}</label>
<input type="text" name="customer_name" class="form-control" id="customer-name" value="{{ $request->customer_name }}">
</div>
<div class="form-group">
<label for="customer-email">{{ trans('report::admin.filters.customer_email') }}</label>
<input type="text" name="customer_email" class="form-control" id="customer-email" value="{{ $request->customer_email }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.customers_order_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.date') }}</th>
<th>{{ trans('report::admin.table.customer_name') }}</th>
<th>{{ trans('report::admin.table.customer_email') }}</th>
<th>{{ trans('report::admin.table.customer_group') }}</th>
<th>{{ trans('report::admin.table.orders') }}</th>
<th>{{ trans('report::admin.table.products') }}</th>
<th>{{ trans('report::admin.table.total') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $data)
<tr>
<td>{{ $data->start_date->toFormattedDateString() }} - {{ $data->end_date->toFormattedDateString() }}</td>
<td>{{ $data->customer_full_name }}</td>
<td>{{ $data->customer_email }}</td>
<td>{{ is_null($data->customer_id) ? trans('report::admin.table.guest') : trans('report::admin.table.registered') }}</td>
<td>{{ $data->total_orders }}</td>
<td>{{ $data->total_products }}</td>
<td>{{ $data->total->format() }}</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,4 @@
<div class="form-group">
<label for="from">{{ trans('report::admin.filters.date_start') }}</label>
<input type="text" name="from" class="form-control datetime-picker" id="from" data-default-date="{{ $request->from }}">
</div>

View File

@@ -0,0 +1,13 @@
<div class="form-group">
<label for="group">{{ trans('report::admin.filters.group_by') }}</label>
<select name="group" id="group" class="custom-select-black">
<option value="">{{ trans('report::admin.filters.please_select') }}</option>
@foreach (trans('report::admin.filters.groups') as $group => $label)
<option value="{{ $group }}" {{ $request->group === $group ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>

View File

@@ -0,0 +1,13 @@
<div class="form-group">
<label for="status">{{ trans('report::admin.filters.status') }}</label>
<select name="status" id="status" class="custom-select-black">
<option value="">{{ trans('report::admin.filters.please_select') }}</option>
@foreach (trans('order::statuses') as $name => $label)
<option value="{{ $name }}" {{ $request->status === $name ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>

View File

@@ -0,0 +1,4 @@
<div class="form-group">
<label for="to">{{ trans('report::admin.filters.date_end') }}</label>
<input type="text" name="to" class="form-control datetime-picker" id="to" data-default-date="{{ $request->to }}">
</div>

View File

@@ -0,0 +1,47 @@
@extends('admin::layout')
@component('admin::components.page.header')
@slot('title', trans('report::admin.reports'))
<li class="active">{{ trans('report::admin.reports') }}</li>
@endcomponent
@section('content')
<div class="box box-primary report-wrapper">
<div class="box-body">
<div class="row">
<div class="col-lg-9 col-md-8">
<div class="report-result">
@yield('report_result')
</div>
</div>
<div class="col-lg-3 col-md-4">
<div class="filter-report clearfix">
<h3 class="tab-content-title">{{ trans('report::admin.filter') }}</h3>
<form method="GET" action="{{ route('admin.reports.index') }}">
<div class="form-group">
<label for="report-type">{{ trans('report::admin.filters.report_type') }}</label>
<select name="type" id="report-type" class="custom-select-black">
@foreach (trans('report::admin.filters.report_types') as $type => $label)
<option value="{{ $type }}" {{ $request->type === $type ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>
@yield('filters')
<button type="submit" class="btn btn-default" data-loading>
{{ trans('report::admin.filter') }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View File

@@ -0,0 +1,64 @@
@extends('report::admin.reports.layout')
@section('filters')
@include('report::admin.reports.filters.from')
@include('report::admin.reports.filters.to')
@include('report::admin.reports.filters.status')
@include('report::admin.reports.filters.group')
<div class="form-group">
<label for="product">{{ trans('report::admin.filters.product') }}</label>
<input type="text" name="product" class="form-control" id="product" value="{{ $request->product }}">
</div>
<div class="form-group">
<label for="sku">{{ trans('report::admin.filters.sku') }}</label>
<input type="text" name="sku" class="form-control" id="sku" value="{{ $request->sku }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.products_purchase_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.date') }}</th>
<th>{{ trans('report::admin.table.product') }}</th>
<th>{{ trans('report::admin.table.qty') }}</th>
<th>{{ trans('report::admin.table.total') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $product)
<tr>
<td>{{ $product->start_date->toFormattedDateString() }} - {{ $product->end_date->toFormattedDateString() }}</td>
<td>
@if ($product->trashed())
{{ $product->name }}
@else
<a href="{{ route('admin.products.edit', $product) }}">{{ $product->name }}</a>
@endif
</td>
<td>{{ $product->qty }}</td>
<td>{{ $product->total->format() }}</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,81 @@
@extends('report::admin.reports.layout')
@section('filters')
<div class="form-group">
<label for="quantity-above">{{ trans('report::admin.filters.quantity_above') }}</label>
<input type="number" name="quantity_above" class="form-control" id="quantity-above" value="{{ $request->quantity_above }}">
</div>
<div class="form-group">
<label for="quantity-below">{{ trans('report::admin.filters.quantity_below') }}</label>
<input type="number" name="quantity_below" class="form-control" id="quantity-below" value="{{ $request->quantity_below }}">
</div>
<div class="form-group">
<label for="stock-availability">{{ trans('report::admin.filters.stock_availability') }}</label>
<select name="stock_availability" class="form-control custom-select-black" id="stock-availability">
<option value="">{{ trans('report::admin.filters.please_select') }}</option>
<option value="in_stock" {{ request('stock_availability') === 'in_stock' ? 'selected' : '' }}>
{{ trans('report::admin.filters.stock_availability_states.in_stock') }}
</option>
<option value="out_of_stock" {{ request('stock_availability') === 'out_of_stock' ? 'selected' : '' }}>
{{ trans('report::admin.filters.stock_availability_states.out_of_stock') }}
</option>
</select>
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.products_stock_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.product') }}</th>
<th>{{ trans('report::admin.table.qty') }}</th>
<th>{{ trans('report::admin.table.stock_availability') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $product)
<tr>
<td>
@if ($product->trashed())
{{ $product->name }}
@else
<a href="{{ route('admin.products.edit', $product) }}">{{ $product->name }}</a>
@endif
</td>
<td>
{!! $product->qty ?: '&mdash;' !!}
</td>
<td>
@if ($product->isInStock())
{{ trans('report::admin.filters.stock_availability_states.in_stock') }}
@else
{{ trans('report::admin.filters.stock_availability_states.out_of_stock') }}
@endif
</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,56 @@
@extends('report::admin.reports.layout')
@section('filters')
<div class="form-group">
<label for="product">{{ trans('report::admin.filters.product') }}</label>
<input type="text" name="product" class="form-control" id="product" value="{{ $request->product }}">
</div>
<div class="form-group">
<label for="sku">{{ trans('report::admin.filters.sku') }}</label>
<input type="text" name="sku" class="form-control" id="sku" value="{{ $request->sku }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.products_view_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.product') }}</th>
<th>{{ trans('report::admin.table.views') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $product)
<tr>
<td>
@if ($product->trashed())
{{ $product->name }}
@else
<a href="{{ route('admin.products.edit', $product) }}">{{ $product->name }}</a>
@endif
</td>
<td>
{{ $product->viewed }}
</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,54 @@
@extends('report::admin.reports.layout')
@section('filters')
@include('report::admin.reports.filters.from')
@include('report::admin.reports.filters.to')
@include('report::admin.reports.filters.status')
@include('report::admin.reports.filters.group')
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.sales_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.date') }}</th>
<th>{{ trans('report::admin.table.orders') }}</th>
<th>{{ trans('report::admin.table.products') }}</th>
<th>{{ trans('report::admin.table.subtotal') }}</th>
<th>{{ trans('report::admin.table.shipping') }}</th>
<th>{{ trans('report::admin.table.discount') }}</th>
<th>{{ trans('report::admin.table.tax') }}</th>
<th>{{ trans('report::admin.table.total') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $data)
<tr>
<td>{{ $data->start_date->toFormattedDateString() }} - {{ $data->end_date->toFormattedDateString() }}</td>
<td>{{ $data->total_orders }}</td>
<td>{{ $data->total_products }}</td>
<td>{{ $data->sub_total->format() }}</td>
<td>{{ $data->shipping_cost->format() }}</td>
<td>{{ $data->discount->format() }}</td>
<td>{{ $data->tax->format() }}</td>
<td>{{ $data->total->format() }}</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,44 @@
@extends('report::admin.reports.layout')
@section('filters')
<div class="form-group">
<label for="keyword">{{ trans('report::admin.filters.keyword') }}</label>
<input type="text" name="keyword" class="form-control" id="keyword" value="{{ $request->keyword }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.search_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.keyword') }}</th>
<th>{{ trans('report::admin.table.results') }}</th>
<th>{{ trans('report::admin.table.hits') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $data)
<tr>
<td>{{ $data->term }}</td>
<td>{{ $data->results }}</td>
<td>{{ $data->hits }}</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,60 @@
@extends('report::admin.reports.layout')
@section('filters')
@include('report::admin.reports.filters.from')
@include('report::admin.reports.filters.to')
@include('report::admin.reports.filters.status')
@include('report::admin.reports.filters.group')
<div class="form-group">
<label for="shipping-method">{{ trans('report::admin.filters.shipping_method') }}</label>
<select name="shipping_method" id="shipping-method" class="custom-select-black">
<option value="">{{ trans('report::admin.filters.please_select') }}</option>
@foreach ($shippingMethods as $name => $shippingMethod)
<option value="{{ $name }}" {{ $request->shipping_method === $name ? 'selected' : '' }}>
{{ $shippingMethod->label }}
</option>
@endforeach
</select>
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.shipping_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.date') }}</th>
<th>{{ trans('report::admin.table.shipping_method') }}</th>
<th>{{ trans('report::admin.table.orders') }}</th>
<th>{{ trans('report::admin.table.total') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $data)
<tr>
<td>{{ $data->start_date->toFormattedDateString() }} - {{ $data->end_date->toFormattedDateString() }}</td>
<td>{{ $data->shipping_method }}</td>
<td>{{ $data->total_orders }}</td>
<td>{{ $data->total->format() }}</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,48 @@
@extends('report::admin.reports.layout')
@section('filters')
<div class="form-group">
<label for="tag">{{ trans('report::admin.filters.tag') }}</label>
<input type="text" name="tag" class="form-control" id="tag" value="{{ $request->tag }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.tagged_products_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.tag') }}</th>
<th>{{ trans('report::admin.table.products_count') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $tag)
<tr>
<td>
{{ $tag->name }}
</td>
<td>
{{ $tag->products_count }}
</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,51 @@
@extends('report::admin.reports.layout')
@section('filters')
@include('report::admin.reports.filters.from')
@include('report::admin.reports.filters.to')
@include('report::admin.reports.filters.status')
@include('report::admin.reports.filters.group')
<div class="form-group">
<label for="tax-name">{{ trans('report::admin.filters.tax_name') }}</label>
<input type="text" name="tax_name" class="form-control" id="tax-name" value="{{ $request->tax_name }}">
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.tax_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.date') }}</th>
<th>{{ trans('report::admin.table.tax_name') }}</th>
<th>{{ trans('report::admin.table.orders') }}</th>
<th>{{ trans('report::admin.table.total') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $data)
<tr>
<td>{{ $data->start_date->toFormattedDateString() }} - {{ $data->end_date->toFormattedDateString() }}</td>
<td>{{ $data->name }}</td>
<td>{{ $data->total_orders }}</td>
<td>{{ $data->total->format() }}</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,56 @@
@extends('report::admin.reports.layout')
@section('filters')
<div class="form-group">
<label for="tax-class">{{ trans('report::admin.filters.tax_class') }}</label>
<select name="tax_class" id="tax-class" class="form-control">
<option value="">{{ trans('report::admin.filters.please_select') }}</option>
@foreach ($taxClasses as $id => $label)
<option value="{{ $id }}" {{ request('tax_class') == $id ? 'selected' : '' }}>
{{ $label }}
</option>
@endforeach
</select>
</div>
@endsection
@section('report_result')
<h3 class="tab-content-title">
{{ trans('report::admin.filters.report_types.taxed_products_report') }}
</h3>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ trans('report::admin.table.tax_class') }}</th>
<th>{{ trans('report::admin.table.products_count') }}</th>
</tr>
</thead>
<tbody>
@forelse ($report as $taxClass)
<tr>
<td>
{{ $taxClass->label }}
</td>
<td>
{{ $taxClass->products_count }}
</td>
</tr>
@empty
<tr>
<td class="empty" colspan="8">{{ trans('report::admin.no_data') }}</td>
</tr>
@endforelse
</tbody>
</table>
<div class="pull-right">
{!! $report->links() !!}
</div>
</div>
@endsection

View File

@@ -0,0 +1,9 @@
<?php
use Illuminate\Support\Facades\Route;
Route::get('reports', [
'as' => 'admin.reports.index',
'uses' => 'ReportController@index',
'middleware' => 'can:admin.reports.index',
]);

View File

@@ -0,0 +1,35 @@
<?php
namespace Modules\Report;
use Modules\Order\Entities\Order;
use Illuminate\Support\Facades\DB;
class SalesReport extends Report
{
protected function view()
{
return 'report::admin.reports.sales_report.index';
}
protected function query()
{
return Order::selectRaw('orders.id')
->selectRaw('MIN(created_at) as start_date')
->selectRaw('MAX(created_at) as end_date')
->selectRaw('COUNT(*) as total_orders')
->join(DB::raw('(SELECT order_id, sum(qty) qty FROM order_products GROUP BY order_id) op'), function ($join) {
$join->on('orders.id', '=', 'op.order_id');
})
->selectRaw('SUM(op.qty) as total_products')
->selectRaw('SUM(sub_total) as sub_total')
->selectRaw('SUM(shipping_cost) as shipping_cost')
->selectRaw('SUM(discount) as discount')
->leftJoin(DB::raw('(SELECT order_id, sum(amount) amount FROM order_taxes GROUP BY order_id) ot'), function ($join) {
$join->on('orders.id', '=', 'ot.order_id');
})
->selectRaw('SUM(ot.amount) as tax')
->selectRaw('SUM(orders.total) as total')
->groupBy('orders.id');
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Modules\Report;
use Modules\Product\Entities\SearchTerm;
class SearchReport extends Report
{
protected $filters = [];
protected function view()
{
return 'report::admin.reports.search_report.index';
}
public function query()
{
return SearchTerm::orderByDesc('hits')
->when(request()->has('keyword'), function ($query) {
$query->where('term', 'LIKE', request('keyword') . '%');
});
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Modules\Report;
use Modules\Order\Entities\Order;
use Modules\Shipping\Facades\ShippingMethod;
class ShippingReport extends Report
{
protected function view()
{
return 'report::admin.reports.shipping_report.index';
}
protected function data()
{
return [
'shippingMethods' => ShippingMethod::all(),
];
}
public function query()
{
return Order::select('shipping_method')
->selectRaw('MIN(created_at) as start_date')
->selectRaw('MAX(created_at) as end_date')
->selectRaw('COUNT(*) as total_orders')
->selectRaw('SUM(shipping_cost) as total')
->when(request()->has('shipping_method'), function ($query) {
$query->where('shipping_method', request('shipping_method'));
})
->groupBy('shipping_method');
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Modules\Report\Sidebar;
use Maatwebsite\Sidebar\Item;
use Maatwebsite\Sidebar\Menu;
use Maatwebsite\Sidebar\Group;
use Modules\Admin\Sidebar\BaseSidebarExtender;
class SidebarExtender extends BaseSidebarExtender
{
public function extend(Menu $menu)
{
$menu->group(trans('admin::sidebar.system'), function (Group $group) {
$group->item(trans('report::sidebar.reports'), function (Item $item) {
$item->icon('fa fa-bar-chart');
$item->weight(20);
$item->route('admin.reports.index');
$item->authorize(
$this->auth->hasAccess('admin.reports.index')
);
});
});
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Modules\Report;
use Modules\Tag\Entities\Tag;
use Illuminate\Database\Eloquent\Builder;
class TaggedProductsReport extends Report
{
protected $filters = [];
protected function view()
{
return 'report::admin.reports.tagged_products_report.index';
}
protected function query()
{
return Tag::select('id')
->when(request()->has('tag'), function (Builder $query) {
$query->whereTranslationLike('name', request('tag') . '%');
})
->withCount('products')
->orderByDesc('products_count');
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Modules\Report;
use Modules\Tax\Entities\TaxRate;
class TaxReport extends Report
{
protected $date = 'orders.created_at';
protected function view()
{
return 'report::admin.reports.tax_report.index';
}
public function query()
{
return TaxRate::select('tax_rates.id')
->join('order_taxes', 'tax_rates.id', '=', 'order_taxes.tax_rate_id')
->selectRaw('SUM(order_taxes.amount) as total')
->join('orders', 'order_taxes.order_id', '=', 'orders.id')
->selectRaw('MIN(orders.created_at) as start_date')
->selectRaw('MAX(orders.created_at) as end_date')
->selectRaw('COUNT(*) as total_orders')
->when(request()->has('tax_name'), function ($query) {
$query->whereTranslationLike('name', request('tax_name') . '%');
})
->groupBy('tax_rates.id');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Modules\Report;
use Modules\Tax\Entities\TaxClass;
use Illuminate\Database\Eloquent\Builder;
class TaxedProductsReport extends Report
{
protected $filters = [];
protected function view()
{
return 'report::admin.reports.taxed_products_report.index';
}
protected function data()
{
return ['taxClasses' => TaxClass::list()];
}
protected function query()
{
return TaxClass::select('id')
->when(request()->has('tax_class'), function (Builder $query) {
$query->where('id', request('tax_class'));
})
->withCount('products')
->orderByDesc('products_count');
}
}

View File

@@ -0,0 +1,27 @@
{
"name": "fleetcart/report",
"description": "The FleetCart Report Module.",
"authors": [
{
"name": "Envay Soft",
"email": "envaysoft@gmail.com"
}
],
"require": {
"php": "^8.0.2"
},
"autoload": {
"psr-4": {
"Modules\\Report\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,9 @@
{
"name": "Report",
"alias": "report",
"description": "The FleetCart Report Module.",
"priority": 100,
"providers": [
"Modules\\Report\\Providers\\ReportServiceProvider"
]
}

View File

@@ -0,0 +1,8 @@
let mix = require('laravel-mix');
let execSync = require('child_process').execSync;
mix.js(`${__dirname}/Resources/assets/admin/js/main.js`, `${__dirname}/Assets/admin/js/report.js`)
.sass(`${__dirname}/Resources/assets/admin/scss/main.scss`, `${__dirname}/Assets/admin/css/report.css`)
.then(() => {
execSync(`npm run rtlcss ${__dirname}/Assets/admin/css/report.css ${__dirname}/Assets/admin/css/report.rtl.css`);
});