From aede82735aba98cd825f1ce39b75d60753c83af8 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Fri, 29 Mar 2024 11:20:52 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E9=94=80=E5=94=AE=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Finance/SalesStatisticController.php | 89 +++++++++++++++++++ app/Admin/Filters/LedgerItemFilter.php | 37 ++++++++ app/Admin/Filters/StoreFilter.php | 16 ++++ app/Admin/routes.php | 3 + app/Models/LedgerItem.php | 6 ++ bootstrap/helpers.php | 25 ++++++ composer.json | 5 +- database/seeders/AdminPermissionSeeder.php | 9 ++ 8 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 app/Admin/Controllers/Finance/SalesStatisticController.php create mode 100644 app/Admin/Filters/LedgerItemFilter.php create mode 100644 bootstrap/helpers.php diff --git a/app/Admin/Controllers/Finance/SalesStatisticController.php b/app/Admin/Controllers/Finance/SalesStatisticController.php new file mode 100644 index 0000000..a66fc42 --- /dev/null +++ b/app/Admin/Controllers/Finance/SalesStatisticController.php @@ -0,0 +1,89 @@ +actionOfGetData()) { + return $this->response()->success([ + 'items' => $this->getLotteryTypeStatistics(request()), + ]); + } + + return $this->response()->success( + $this->baseList( + $this->baseCRUD() + ->headerToolbar([ + amis('filter-toggler')->align('right'), + ]) + ->footerToolbar([]) + ->bulkActions([]) + ->filter($this->baseFilter()->body([ + amis()->GroupControl()->mode('horizontal')->body([ + amis()->DateRangeControl('date_range', '日期') + ->valueFormat('YYYY-MM-DD') + ->columnRatio(6), + amis()->InputCityControl('region', '区域') + ->inputClassName('w-40') + ->allowDistrict(false) + ->extractValue(false), + amis()->SelectControl('store_id', __('finance.ledger.store')) + ->source(admin_url('api/stores?region=${region}')) + ->labelField('title') + ->valueField('id') + ->clearable(), + ]), + ])) + ->columns([ + amis()->TableColumn('name', '彩种'), + amis()->TableColumn('sales', '销量'), + amis()->TableColumn('expenditure', '兑奖'), + ]) + ->affixRow([ + ['type' => 'text', 'text' => '合计'], + ['type' => 'tpl', 'text' => '${items|pick:sales|sum}'], + ['type' => 'tpl', 'text' => '${items|pick:expenditure|sum}'], + ]) + ) + ); + } + + protected function getLotteryTypeStatistics(Request $request): array + { + /** @var \Illuminate\Database\Eloquent\Collection */ + $lotteryTypes = Keyword::where('parent_key', 'lottery_type')->get(); + + /** @var \Illuminate\Database\Eloquent\Collection */ + $statistics = LedgerItem::select([ + 'ledger_item_type_id', + DB::raw('SUM(sales) as sales'), + DB::raw('SUM(expenditure) as expenditure'), + ]) + ->filter($request->input(), LedgerItemFilter::class) + ->whereIn('ledger_item_type_id', $lotteryTypes->pluck('value')) + ->groupBy('ledger_item_type_id') + ->get() + ->keyBy('ledger_item_type_id'); + + return $lotteryTypes->map(function ($lotteryType) use ($statistics) { + $statistic = $statistics->get($lotteryType->value); + return [ + 'name' => $lotteryType->name, + 'sales' => trim_zeros($statistic->sales ?? '0.00'), + 'expenditure' => trim_zeros($statistic->expenditure ?? '0.00'), + ]; + })->all(); + } +} diff --git a/app/Admin/Filters/LedgerItemFilter.php b/app/Admin/Filters/LedgerItemFilter.php new file mode 100644 index 0000000..a8d2b9e --- /dev/null +++ b/app/Admin/Filters/LedgerItemFilter.php @@ -0,0 +1,37 @@ +whereBetween('date', explode(',', $dateRange)); + } + + public function region($region) + { + if ($this->input('store_id') !== null || ! is_array($region)) { + return; + } + + $provinceCode = Arr::get($region, 'provinceCode'); + $cityCode = Arr::get($region, 'cityCode'); + if (empty($provinceCode) && empty($cityCode)) { + return; + } + + $this->related('store', function($query) use ($provinceCode, $cityCode) { + $query->when($provinceCode, fn ($query) => $query->where('region->provinceCode', $provinceCode)) + ->when($cityCode, fn ($query) => $query->where('region->cityCode', $cityCode)); + }); + } + + public function store($id) + { + $this->where('store_id', $id); + } +} diff --git a/app/Admin/Filters/StoreFilter.php b/app/Admin/Filters/StoreFilter.php index 8e2e943..989b67a 100644 --- a/app/Admin/Filters/StoreFilter.php +++ b/app/Admin/Filters/StoreFilter.php @@ -3,6 +3,7 @@ namespace App\Admin\Filters; use EloquentFilter\ModelFilter; +use Illuminate\Support\Arr; class StoreFilter extends ModelFilter { @@ -30,4 +31,19 @@ class StoreFilter extends ModelFilter { $this->where('business_status', $key); } + + public function region($region) + { + if (! is_array($region)) { + return; + } + + if ($provinceCode = Arr::get($region, 'provinceCode')) { + $this->where('region->provinceCode', $provinceCode); + } + + if ($cityCode = Arr::get($region, 'cityCode')) { + $this->where('region->cityCode', $cityCode); + } + } } diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 816eb40..c59dd6b 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -2,6 +2,7 @@ use App\Admin\Controllers\BaseKeywordController; use App\Admin\Controllers\Finance\LedgerController; +use App\Admin\Controllers\Finance\SalesStatisticController; use App\Admin\Controllers\Hr\EmployeeController; use App\Admin\Controllers\Hr\RestController; use App\Admin\Controllers\Hr\SignController; @@ -85,6 +86,8 @@ Route::group([ // 上报数据 $router->resource('ledgers', LedgerController::class); $router->post('ledgers/{ledger}/approval', [LedgerController::class, 'approval'])->name('ledgers.approval'); + // 销售统计 + $router->get('sales-statistics', [SalesStatisticController::class, 'index'])->name('sales_statistics.index'); }); /* diff --git a/app/Models/LedgerItem.php b/app/Models/LedgerItem.php index 4e44503..7cc1f1f 100644 --- a/app/Models/LedgerItem.php +++ b/app/Models/LedgerItem.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Traits\HasDateTimeFormatter; use EloquentFilter\Filterable; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -36,4 +37,9 @@ class LedgerItem extends Model { return $this->belongsTo(Keyword::class, 'ledger_item_type_id', 'key'); } + + public function store(): BelongsTo + { + return $this->belongsTo(Store::class); + } } diff --git a/bootstrap/helpers.php b/bootstrap/helpers.php new file mode 100644 index 0000000..7bdf723 --- /dev/null +++ b/bootstrap/helpers.php @@ -0,0 +1,25 @@ + ['list', 'update', 'view'], 'children' => [], ], + 'sales_statistics' => [ + 'name' => '销售统计', + 'icon' => 'ri:bar-chart-2-line', + 'uri' => '/finance/sales-statistics', + 'resource' => false, + 'children' => [ + 'index' => '销售统计', + ], + ], ], ], From 18e8b386f2c2d302296cccdab10b85c8278d2299 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Fri, 29 Mar 2024 12:39:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E9=97=A8=E5=BA=97=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Finance/StoreStatisticController.php | 86 +++++++++++++++++++ app/Admin/Filters/LedgerFilter.php | 26 +++++- app/Admin/routes.php | 3 + database/seeders/AdminPermissionSeeder.php | 9 ++ 4 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 app/Admin/Controllers/Finance/StoreStatisticController.php diff --git a/app/Admin/Controllers/Finance/StoreStatisticController.php b/app/Admin/Controllers/Finance/StoreStatisticController.php new file mode 100644 index 0000000..9861b0e --- /dev/null +++ b/app/Admin/Controllers/Finance/StoreStatisticController.php @@ -0,0 +1,86 @@ +actionOfGetData()) { + return $this->response()->success([ + 'items' => $this->getStoreStatistics(request()), + ]); + } + + return $this->response()->success( + $this->baseList( + $this->baseCRUD() + ->headerToolbar([ + amis('filter-toggler')->align('right'), + ]) + ->footerToolbar([]) + ->bulkActions([]) + ->filter($this->baseFilter()->body([ + amis()->GroupControl()->mode('horizontal')->body([ + amis()->DateRangeControl('date_range', '日期') + ->valueFormat('YYYY-MM-DD') + ->columnRatio(6), + amis()->InputCityControl('region', '区域') + ->allowDistrict(false) + ->extractValue(false), + ]), + ])) + ->columns([ + amis()->TableColumn('ranking', '排序'), + amis()->TableColumn('title', '门店'), + amis()->TableColumn('sales', '收入')->sortable(), + amis()->TableColumn('expenditure', '支出')->sortable(), + ]) + ) + ); + } + + protected function getStoreStatistics(Request $request): array + { + $stores = Store::filter($request->only(['region']), StoreFilter::class) + ->get(['id', 'title']); + + /** @var \Illuminate\Database\Eloquent\Collection */ + $statistics = Ledger::select([ + 'store_id', + DB::raw('SUM(sales) as sales'), + DB::raw('SUM(expenditure) as expenditure'), + ]) + ->filter($request->input(), LedgerItemFilter::class) + ->groupBy('store_id') + ->get() + ->keyBy('store_id'); + + // 排序规则 + $sortBy = [ + [$request->input('orderBy') ?: 'sales', $request->input('orderDir') ?: 'desc'], + ]; + + return $stores->map(function ($store) use ($statistics) { + $statistic = $statistics->get($store->id); + return [ + 'title' => $store->title, + 'sales' => trim_zeros($statistic->sales ?? '0'), + 'expenditure' => trim_zeros($statistic->expenditure ?? '0'), + ]; + }) + ->sortBy($sortBy) + ->values() + ->map(fn ($statistic, $key) => array_merge($statistic, ['ranking' => $key + 1])) + ->all(); + } +} diff --git a/app/Admin/Filters/LedgerFilter.php b/app/Admin/Filters/LedgerFilter.php index e17881b..5a80b70 100644 --- a/app/Admin/Filters/LedgerFilter.php +++ b/app/Admin/Filters/LedgerFilter.php @@ -3,21 +3,41 @@ namespace App\Admin\Filters; use EloquentFilter\ModelFilter; +use Illuminate\Support\Arr; class LedgerFilter extends ModelFilter { public function store($id) { - $this->query->where('store_id', $id); + $this->where('store_id', $id); } public function dateRange($dateRange) { - $this->query->whereBetween('date', explode(',', $dateRange)); + $this->whereBetween('date', explode(',', $dateRange)); } public function status($status) { - $this->query->whereIn('ledger_status', explode(',', $status)); + $this->whereIn('ledger_status', explode(',', $status)); + } + + public function region($region) + { + if (! is_array($region)) { + return; + } + + $provinceCode = Arr::get($region, 'provinceCode'); + $cityCode = Arr::get($region, 'cityCode'); + + if (empty($provinceCode) && empty($cityCode)) { + return; + } + + $this->related('store', function($query) use ($provinceCode, $cityCode) { + $query->when($provinceCode, fn ($query) => $query->where('region->provinceCode', $provinceCode)) + ->when($cityCode, fn ($query) => $query->where('region->cityCode', $cityCode)); + }); } } diff --git a/app/Admin/routes.php b/app/Admin/routes.php index c59dd6b..7c0b993 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -3,6 +3,7 @@ use App\Admin\Controllers\BaseKeywordController; use App\Admin\Controllers\Finance\LedgerController; use App\Admin\Controllers\Finance\SalesStatisticController; +use App\Admin\Controllers\Finance\StoreStatisticController; use App\Admin\Controllers\Hr\EmployeeController; use App\Admin\Controllers\Hr\RestController; use App\Admin\Controllers\Hr\SignController; @@ -88,6 +89,8 @@ Route::group([ $router->post('ledgers/{ledger}/approval', [LedgerController::class, 'approval'])->name('ledgers.approval'); // 销售统计 $router->get('sales-statistics', [SalesStatisticController::class, 'index'])->name('sales_statistics.index'); + // 门店统计 + $router->get('store-statistics', [StoreStatisticController::class, 'index'])->name('store_statistics.index'); }); /* diff --git a/database/seeders/AdminPermissionSeeder.php b/database/seeders/AdminPermissionSeeder.php index fe75d8c..297e135 100644 --- a/database/seeders/AdminPermissionSeeder.php +++ b/database/seeders/AdminPermissionSeeder.php @@ -151,6 +151,15 @@ class AdminPermissionSeeder extends Seeder 'index' => '销售统计', ], ], + 'store_statistics' => [ + 'name' => '门店统计', + 'icon' => 'material-symbols:store-rounded', + 'uri' => '/finance/store-statistics', + 'resource' => false, + 'children' => [ + 'index' => '门店统计', + ], + ], ], ],