Added: Sales Report

This commit is contained in:
Fahim 2021-09-08 14:39:22 +06:00
parent 13f64c9a2b
commit f9d4295656
36 changed files with 768 additions and 194 deletions

View File

@ -2,7 +2,7 @@ APP_NAME="Triangle POS"
APP_ENV=local APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=http://localhost APP_URL=http://127.0.0.1:8000
DEBUGBAR_ENABLED=false DEBUGBAR_ENABLED=false
@ -12,8 +12,8 @@ LOG_LEVEL=debug
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=127.0.0.1 DB_HOST=127.0.0.1
DB_PORT=3306 DB_PORT=3306
DB_DATABASE=laravel DB_DATABASE=triangle_pos
DB_USERNAME=root DB_USERNAME=
DB_PASSWORD= DB_PASSWORD=
BROADCAST_DRIVER=log BROADCAST_DRIVER=log

View File

View File

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

View File

View File

@ -0,0 +1,21 @@
<?php
namespace Modules\Reports\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class ReportsDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
// $this->call("OthersTableSeeder");
}
}

View File

View File

@ -0,0 +1,14 @@
<?php
namespace Modules\Reports\Http\Controllers;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class ReportsController extends Controller
{
public function salesReport() {
return view('reports::sales.index');
}
}

View File

View File

View File

View File

@ -0,0 +1,112 @@
<?php
namespace Modules\Reports\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Eloquent\Factory;
class ReportsServiceProvider extends ServiceProvider
{
/**
* @var string $moduleName
*/
protected $moduleName = 'Reports';
/**
* @var string $moduleNameLower
*/
protected $moduleNameLower = 'reports';
/**
* Boot the application events.
*
* @return void
*/
public function boot()
{
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->register(RouteServiceProvider::class);
}
/**
* Register config.
*
* @return void
*/
protected function registerConfig()
{
$this->publishes([
module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'),
], 'config');
$this->mergeConfigFrom(
module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower
);
}
/**
* Register views.
*
* @return void
*/
public function registerViews()
{
$viewPath = resource_path('views/modules/' . $this->moduleNameLower);
$sourcePath = module_path($this->moduleName, 'Resources/views');
$this->publishes([
$sourcePath => $viewPath
], ['views', $this->moduleNameLower . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
}
/**
* Register translations.
*
* @return void
*/
public function registerTranslations()
{
$langPath = resource_path('lang/modules/' . $this->moduleNameLower);
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
} else {
$this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower);
}
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [];
}
private function getPublishableViewPaths(): array
{
$paths = [];
foreach (\Config::get('view.paths') as $path) {
if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
$paths[] = $path . '/modules/' . $this->moduleNameLower;
}
}
return $paths;
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace Modules\Reports\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* The module namespace to assume when generating URLs to actions.
*
* @var string
*/
protected $moduleNamespace = 'Modules\Reports\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->moduleNamespace)
->group(module_path('Reports', '/Routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->moduleNamespace)
->group(module_path('Reports', '/Routes/api.php'));
}
}

View File

View File

View File

View File

View File

@ -0,0 +1,16 @@
@extends('layouts.app')
@section('title', 'Sales Report')
@section('breadcrumb')
<ol class="breadcrumb border-0 m-0">
<li class="breadcrumb-item"><a href="{{ route('home') }}">Home</a></li>
<li class="breadcrumb-item active">Sales Report</li>
</ol>
@endsection
@section('content')
<div class="container-fluid">
<livewire:reports.sales-report :customers="\Modules\People\Entities\Customer::all()"/>
</div>
@endsection

View File

View File

@ -0,0 +1,18 @@
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/reports', function (Request $request) {
return $request->user();
});

View File

@ -0,0 +1,18 @@
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::group(['middleware' => 'auth'], function () {
//Sales Report
Route::get('/sales-report', 'ReportsController@salesReport')
->name('sales-report.index');
});

View File

View File

View File

@ -0,0 +1,23 @@
{
"name": "nwidart/reports",
"description": "",
"authors": [
{
"name": "Nicolas Widart",
"email": "n.widart@gmail.com"
}
],
"extra": {
"laravel": {
"providers": [],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Reports\\": ""
}
}
}

View File

@ -0,0 +1,13 @@
{
"name": "Reports",
"alias": "reports",
"description": "",
"keywords": [],
"priority": 0,
"providers": [
"Modules\\Reports\\Providers\\ReportsServiceProvider"
],
"aliases": {},
"files": [],
"requires": []
}

View File

@ -0,0 +1,17 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"cross-env": "^7.0",
"laravel-mix": "^5.0.1",
"laravel-mix-merge-manifest": "^0.1.2"
}
}

14
Modules/Reports/webpack.mix.js vendored Normal file
View File

@ -0,0 +1,14 @@
const dotenvExpand = require('dotenv-expand');
dotenvExpand(require('dotenv').config({ path: '../../.env'/*, debug: true*/}));
const mix = require('laravel-mix');
require('laravel-mix-merge-manifest');
mix.setPublicPath('../../public').mergeManifest();
mix.js(__dirname + '/Resources/assets/js/app.js', 'js/reports.js')
.sass( __dirname + '/Resources/assets/sass/app.scss', 'css/reports.css');
if (mix.inProduction()) {
mix.version();
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Livewire\Reports;
use Livewire\Component;
use Livewire\WithPagination;
use Modules\Sale\Entities\Sale;
class SalesReport extends Component
{
use WithPagination;
protected $paginationTheme = 'bootstrap';
public $customers;
public $start_date;
public $end_date;
public $customer_id;
public $sale_status;
public $payment_status;
protected $rules = [
'start_date' => 'required|date|before:end_date',
'end_date' => 'required|date|after:start_date',
];
public function mount($customers) {
$this->customers = $customers;
$this->start_date = today()->subDays(30)->format('Y-m-d');
$this->end_date = today()->format('Y-m-d');
$this->customer_id = '';
$this->sale_status = '';
$this->payment_status = '';
}
public function render() {
$sales = Sale::whereDate('date', '>=', $this->start_date)
->whereDate('date', '<=', $this->end_date)
->when($this->customer_id, function ($query) {
return $query->where('customer_id', $this->customer_id);
})
->when($this->sale_status, function ($query) {
return $query->where('status', $this->sale_status);
})
->when($this->payment_status, function ($query) {
return $query->where('payment_status', $this->payment_status);
})
->orderBy('date', 'desc')->paginate(10);
return view('livewire.reports.sales-report', [
'sales' => $sales
]);
}
public function generateReport() {
$this->validate();
$this->render();
}
}

378
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,5 +12,6 @@
"SaleReturn": true, "SaleReturn": true,
"SalesReturn": true, "SalesReturn": true,
"PurchasesReturn": true, "PurchasesReturn": true,
"Quotation": true "Quotation": true,
"Reports": true
} }

View File

@ -133,7 +133,7 @@
@endcan @endcan
@can('access_sales') @can('access_sales')
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('sales*') || request()->routeIs('sale-payments*') ? 'c-show' : '' }}"> <li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('sales.*') || request()->routeIs('sale-payments*') ? 'c-show' : '' }}">
<a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#"> <a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#">
<i class="c-sidebar-nav-icon bi bi-receipt" style="line-height: 1;"></i> Sales <i class="c-sidebar-nav-icon bi bi-receipt" style="line-height: 1;"></i> Sales
</a> </a>
@ -233,6 +233,23 @@
</li> </li>
@endcan @endcan
@can('access_sales_report')
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('sales-report.index') ? 'c-show' : '' }}">
<a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#">
<i class="c-sidebar-nav-icon bi bi-graph-up" style="line-height: 1;"></i> Reports
</a>
<ul class="c-sidebar-nav-dropdown-items">
@can('access_sales_report')
<li class="c-sidebar-nav-item">
<a class="c-sidebar-nav-link {{ request()->routeIs('sales-report.index') ? 'c-active' : '' }}" href="{{ route('sales-report.index') }}">
<i class="c-sidebar-nav-icon bi bi-clipboard-data" style="line-height: 1;"></i> Sales Report
</a>
</li>
@endcan
</ul>
</li>
@endcan
@can('access_user_management') @can('access_user_management')
<li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('roles*') ? 'c-show' : '' }}"> <li class="c-sidebar-nav-item c-sidebar-nav-dropdown {{ request()->routeIs('roles*') ? 'c-show' : '' }}">
<a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#"> <a class="c-sidebar-nav-link c-sidebar-nav-dropdown-toggle" href="#">

View File

@ -0,0 +1,156 @@
<div>
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body">
<form wire:submit.prevent="generateReport">
<div class="form-row">
<div class="col-lg-4">
<div class="form-group">
<label>Start Date <span class="text-danger">*</span></label>
<input wire:model.defer="start_date" type="date" class="form-control" name="start_date">
@error('start_date')
<span class="text-danger mb-1">{{ $message }}</span>
@enderror
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label>End Date <span class="text-danger">*</span></label>
<input wire:model.defer="end_date" type="date" class="form-control" name="end_date">
@error('end_date')
<span class="text-danger mb-1">{{ $message }}</span>
@enderror
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label>Customer</label>
<select wire:model.defer="customer_id" class="form-control" name="customer_id">
<option value="" disabled>Select Customer</option>
@foreach($customers as $customer)
<option value="{{ $customer->id }}">{{ $customer->customer_name }}</option>
@endforeach
</select>
</div>
</div>
</div>
<div class="form-row">
<div class="col-lg-6">
<div class="form-group">
<label>Status</label>
<select wire:model.defer="sale_status" class="form-control" name="sale_status">
<option value="" disabled>Select Status</option>
<option value="Pending">Pending</option>
<option value="Shipped">Shipped</option>
<option value="Completed">Completed</option>
</select>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label>Payment Status</label>
<select wire:model.defer="sale_status" class="form-control" name="payment_status">
<option value="" disabled>Select Payment Status</option>
<option value="Paid">Paid</option>
<option value="Unpaid">Unpaid</option>
<option value="Partial">Partial</option>
</select>
</div>
</div>
</div>
<div class="form-group mb-0">
<button type="submit" class="btn btn-primary">
<span wire:target="generateReport" wire:loading class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
<i wire:target="generateReport" wire:loading.remove class="bi bi-shuffle"></i>
Filter Report
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body">
<table class="table table-bordered table-striped text-center mb-0">
<div wire:loading.flex class="col-12 position-absolute justify-content-center align-items-center" style="top:0;right:0;left:0;bottom:0;background-color: rgba(255,255,255,0.5);z-index: 99;">
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
<thead>
<tr>
<th>Date</th>
<th>Reference</th>
<th>Customer</th>
<th>Status</th>
<th>Total</th>
<th>Paid</th>
<th>Due</th>
<th>Payment Status</th>
</tr>
</thead>
<tbody>
@forelse($sales as $sale)
<tr>
<td>{{ \Carbon\Carbon::parse($sale->date)->format('d M, Y') }}</td>
<td>{{ $sale->reference }}</td>
<td>{{ $sale->customer_name }}</td>
<td>
@if ($sale->status == 'Pending')
<span class="badge badge-info">
{{ $sale->status }}
</span>
@elseif ($sale->status == 'Shipped')
<span class="badge badge-primary">
{{ $sale->status }}
</span>
@else
<span class="badge badge-success">
{{ $sale->status }}
</span>
@endif
</td>
<td>{{ format_currency($sale->total_amount) }}</td>
<td>{{ format_currency($sale->paid_amount) }}</td>
<td>{{ format_currency($sale->due_amount) }}</td>
<td>
@if ($sale->payment_status == 'Partial')
<span class="badge badge-warning">
{{ $sale->payment_status }}
</span>
@elseif ($sale->payment_status == 'Paid')
<span class="badge badge-success">
{{ $sale->payment_status }}
</span>
@else
<span class="badge badge-danger">
{{ $sale->payment_status }}
</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="8">
<span class="text-danger">No Sales Data Available!</span>
</td>
</tr>
@endforelse
</tbody>
</table>
@if($sales->links())
<div class="mt-3">
{{ $sales->links() }}
</div>
@endif
</div>
</div>
</div>
</div>
</div>