mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal.git
synced 2026-08-19 04:24:12 +00:00
customer report
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Reports;
|
||||
|
||||
use App\Classes\ValueObjects\Constants\HttpStatus;
|
||||
use App\Classes\ValueObjects\Response\ApiResponseObject;
|
||||
use App\Models\Container;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MonthlyReportController
|
||||
{
|
||||
public function report(Request $request): JsonResponse {
|
||||
$containers = Container::whereMonth('loading_date', '=', '09')->whereYear('loading_date', '=', '2021')->orderBy('loading_date')->get();
|
||||
|
||||
$total = 0;
|
||||
$customers = collect();
|
||||
|
||||
foreach ($containers as $container){
|
||||
foreach($container->packingLists as $packingList){
|
||||
$cbm = $packingList->packages->sum(function ($package){
|
||||
return (( (float) $package->width / 100) * ( (float) $package->length / 100) * ( (float) $package->height / 100)) * $package->quantity;
|
||||
});
|
||||
|
||||
$total += $cbm;
|
||||
$marking = $packingList->owner->companyModule->inviters()->withPivot('invitee_reference')->first()->pivot->invitee_reference;
|
||||
if(!$customers->has($marking)){
|
||||
$customers->put($marking, collect([
|
||||
'cbm' => $cbm,
|
||||
'orders' => 1,
|
||||
'packages' => $packingList->packages->sum('quantity')
|
||||
]));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$customers[$marking]['cbm'] += $cbm;
|
||||
$customers[$marking]['orders'] += 1;
|
||||
$customers[$marking]['packages'] += $packingList->packages->sum('quantity');
|
||||
}
|
||||
}
|
||||
|
||||
$activeOrders = $customers->sum(function($customer){
|
||||
return $customer['orders'];
|
||||
});
|
||||
return (new ApiResponseObject('fetch monthly report Successful',
|
||||
'',
|
||||
HttpStatus::OK_WITH_MESSAGE, ['data' => [
|
||||
'containers' => count($containers),
|
||||
'activated_orders' => $activeOrders,
|
||||
'active_customers' => count($customers),
|
||||
'total_cbm' => $customers->sum(function($customer){
|
||||
return $customer['cbm'];
|
||||
}),
|
||||
'packages' => $customers->sum(function($customer){
|
||||
return $customer['packages'];
|
||||
}),
|
||||
'total_orders' => Order::whereMonth('created_at', 9)->whereHas('packingLists')->count()
|
||||
]]))->handler();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="row" >
|
||||
<div class="col">
|
||||
<loading-component v-if="isLoading"></loading-component>
|
||||
<div class="row" v-if="report">
|
||||
<div class="col">
|
||||
<div class="card no-border bg-master-lightest widget-loader-bar m-b-10">
|
||||
<div class="container-xs-height full-height">
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="card-header top-left top-right">
|
||||
<div class="card-title">
|
||||
<span class="font-montserrat fs-11 all-caps">Total CBM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="p-l-20 p-t-50 p-b-40 p-r-20">
|
||||
<h3 class="no-margin p-b-5">{{(Math.ceil(report.total_cbm * 1000) / 1000).toFixed(3)}} CBM</h3>
|
||||
<span class="small hint-text pull-left">{{ Math.ceil((((report.total_cbm / 700) * 100) * 100) / 100).toFixed(2)}}%</span>
|
||||
<span class="pull-right small text-primary">Goal: 700 CBM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-bottom">
|
||||
<div class="progress progress-small m-b-0">
|
||||
<div class="progress-bar progress-bar-primary" :style="{'width': Math.ceil((((report.total_cbm / 700) * 100) * 100) / 100).toFixed(2)+'%'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card no-border bg-master-lightest widget-loader-bar m-b-10">
|
||||
<div class="container-xs-height full-height">
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="card-header top-left top-right">
|
||||
<div class="card-title">
|
||||
<span class="font-montserrat fs-11 all-caps">Activated Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="p-l-20 p-t-50 p-b-40 p-r-20">
|
||||
<h3 class="no-margin p-b-5">{{report.activated_orders}}</h3>
|
||||
<span class="small hint-text pull-left">{{ Math.ceil((((report.activated_orders / report.total_orders) * 100) * 100) / 100).toFixed(2)}}%</span>
|
||||
<span class="pull-right small text-primary">Total: {{report.total_orders}} Orders</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-bottom">
|
||||
<div class="progress progress-small m-b-0">
|
||||
<div class="progress-bar progress-bar-primary" :style="{'width': Math.ceil((((report.activated_orders / report.total_orders) * 100) * 100) / 100).toFixed(2)+'%'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card no-border bg-master-lightest widget-loader-bar m-b-10">
|
||||
<div class="container-xs-height full-height">
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="card-header top-left top-right">
|
||||
<div class="card-title">
|
||||
<span class="font-montserrat fs-11 all-caps">Active Customers</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="p-l-20 p-t-50 p-b-40 p-r-20">
|
||||
<h3 class="no-margin p-b-5">{{report.active_customers}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-bottom">
|
||||
<div class="progress progress-small m-b-0">
|
||||
<div class="progress-bar progress-bar-primary" style="width: 0"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card no-border bg-master-lightest widget-loader-bar m-b-10">
|
||||
<div class="container-xs-height full-height">
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="card-header top-left top-right">
|
||||
<div class="card-title">
|
||||
<span class="font-montserrat fs-11 all-caps">Containers</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-top">
|
||||
<div class="p-l-20 p-t-50 p-b-40 p-r-20">
|
||||
<h3 class="no-margin p-b-5">{{report.containers}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-xs-height">
|
||||
<div class="col-xs-height col-bottom">
|
||||
<div class="progress progress-small m-b-0">
|
||||
<div class="progress-bar progress-bar-primary" style="width: 0"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import LoadingComponent from "../../general/elements/LoadingComponent";
|
||||
export default {
|
||||
components: {LoadingComponent},
|
||||
data(){
|
||||
return {
|
||||
section: 'monthlyReportSection',
|
||||
isLoading: true,
|
||||
report: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pendingQueue () {
|
||||
return this.$store.getters.isInCompleteQueue(this.section);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
pendingQueue(inComplete){
|
||||
if(inComplete){
|
||||
this.fetchReport();
|
||||
}
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.$store.dispatch('updateListQueue', {'name': this.section});
|
||||
},
|
||||
methods: {
|
||||
fetchReport(){
|
||||
this.isLoading = true;
|
||||
this.submit(route('api.report'), 'get', this.section, false, false)
|
||||
},
|
||||
successHandler(response){
|
||||
this.$store.dispatch('completeList', {'name': this.section, 'data': []});
|
||||
this.isLoading = false;
|
||||
this.report = response.payload.data;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,25 @@
|
||||
@extends('layouts.base_portal')
|
||||
@section('inner_content')
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row d-none" :class="[{'d-flex': $store.getters.isAdmin}]" v-if="$store.getters.isAdmin">
|
||||
<div class="col-8">
|
||||
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
|
||||
<div class="col no-padding">
|
||||
<h6>Customers List</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-l-0 m-r-0">
|
||||
<div class="col">
|
||||
<list-component key="2" section="customerListSection" :endpoint="route('api.company.list')" :options="{'has_business_module_type': 1}">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<company-component :data="data"></company-component>
|
||||
</template>
|
||||
</list-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -1,47 +1,42 @@
|
||||
<div class="row d-none" :class="[{'d-flex': $store.getters.isAdmin}]" v-if="$store.getters.isAdmin">
|
||||
<div class="col-8">
|
||||
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
|
||||
<div class="col no-padding">
|
||||
<h6>Customers List</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-l-0 m-r-0">
|
||||
<div class="col">
|
||||
<div class="row m-b-50">
|
||||
<div class="col">
|
||||
<list-component key="2" section="customerListSection" :endpoint="route('api.company.list')" :options="{'has_business_module_type': 1}">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<company-component :data="data"></company-component>
|
||||
</template>
|
||||
</list-component>
|
||||
<monthly-report-section-component></monthly-report-section-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
|
||||
<div class="col no-padding">
|
||||
<h6>Identification Verification</h6>
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
|
||||
<div class="col no-padding">
|
||||
<h6>Identification Verification</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-margin">
|
||||
<div class="col">
|
||||
<list-component key="2" section="identificationVerificationSection" :options="{'per_page': 10, 'document_type_in': ['SSM_REGISTRATION', 'IDENTITY_CARD'], 'status': 1, 'with_company': true}" :endpoint="route('api.document.list')">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<identification-verification-component section="identificationVerificationSection" :data="data"></identification-verification-component>
|
||||
</template>
|
||||
</list-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-margin">
|
||||
<div class="col">
|
||||
<list-component key="2" section="identificationVerificationSection" :options="{'per_page': 5, 'document_type_in': ['SSM_REGISTRATION', 'IDENTITY_CARD'], 'status': 1, 'with_company': true}" :endpoint="route('api.document.list')">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<identification-verification-component section="identificationVerificationSection" :data="data"></identification-verification-component>
|
||||
</template>
|
||||
</list-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
|
||||
<div class="col no-padding">
|
||||
<h6>Address Change Verification</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-margin">
|
||||
<div class="col">
|
||||
<list-component key="2" section="addressVerificationSection" :options="{'per_page': 5, 'status': 1, 'with_owner': true}" :endpoint="route('api.address.list')">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<address-verification-component section="addressVerificationSection" :data="data"></address-verification-component>
|
||||
</template>
|
||||
</list-component>
|
||||
<div class="col-4">
|
||||
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
|
||||
<div class="col no-padding">
|
||||
<h6>Address Change Verification</h6>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-margin">
|
||||
<div class="col">
|
||||
<list-component key="2" section="addressVerificationSection" :options="{'per_page': 10, 'status': 1, 'with_owner': true}" :endpoint="route('api.address.list')">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<address-verification-component section="addressVerificationSection" :data="data"></address-verification-component>
|
||||
</template>
|
||||
</list-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -50,5 +50,7 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function
|
||||
|
||||
require __DIR__ . '/schedule.php';
|
||||
|
||||
Route::get('/report', 'Reports\MonthlyReportController@report')->name('report');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Classes\Jobs\FetchPackingListFromVTPortalJob;
|
||||
use App\Classes\Jobs\FetchWarehouseReceiveListFromVTPortalJob;
|
||||
use App\Classes\Modules\Orders\Services\FetchesDataFromVTPortal;
|
||||
use App\Models\CompanyConnection;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use App\Models\Container;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@@ -53,6 +54,10 @@ Route::get('/dashboard', function () {
|
||||
return view('pages.dashboard');
|
||||
})->name('dashboard');
|
||||
|
||||
Route::get('/customers', function () {
|
||||
return view('pages.admin.customers');
|
||||
})->name('customers');
|
||||
|
||||
Route::get('/orders', function () {
|
||||
return view('pages.orders.index');
|
||||
})->name('orders');
|
||||
@@ -178,3 +183,4 @@ Route::get('/report', function(){
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user