From efff9c2c037df4572cf4d433c961d9d91000dbe5 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Wed, 27 Mar 2024 23:09:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E6=8A=A5=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Finance/LedgerController.php | 170 ++++++++++++++++++ app/Admin/Filters/LedgerFilter.php | 23 +++ app/Admin/Services/LedgerService.php | 47 +++++ app/Admin/routes.php | 20 ++- app/Enums/LedgerStatus.php | 41 +++++ app/Models/Ledger.php | 69 +++++++ ...2024_03_27_090406_create_ledgers_table.php | 39 ++++ database/seeders/AdminPermissionSeeder.php | 20 +++ lang/zh_CN/finance.php | 22 +++ 9 files changed, 448 insertions(+), 3 deletions(-) create mode 100644 app/Admin/Controllers/Finance/LedgerController.php create mode 100644 app/Admin/Filters/LedgerFilter.php create mode 100644 app/Admin/Services/LedgerService.php create mode 100644 app/Enums/LedgerStatus.php create mode 100644 app/Models/Ledger.php create mode 100644 database/migrations/2024_03_27_090406_create_ledgers_table.php create mode 100644 lang/zh_CN/finance.php diff --git a/app/Admin/Controllers/Finance/LedgerController.php b/app/Admin/Controllers/Finance/LedgerController.php new file mode 100644 index 0000000..0a7f1a7 --- /dev/null +++ b/app/Admin/Controllers/Finance/LedgerController.php @@ -0,0 +1,170 @@ +baseCRUD() + ->tableLayout('fixed') + ->headerToolbar([ + ...$this->baseHeaderToolBar(), + ]) + ->bulkActions([]) + ->filter($this->baseFilter()->body([ + amis()->GroupControl()->mode('horizontal')->body([ + amis()->SelectControl('store_id', __('finance.ledger.store')) + ->source(admin_url('api/stores')) + ->labelField('title') + ->valueField('id') + ->clearable() + ->columnRatio(3), + amis()->SelectControl('status', __('finance.ledger.ledger_status')) + ->multiple() + ->options(LedgerStatus::options()) + ->columnRatio(3), + amis()->DateRangeControl('date_range', __('finance.ledger.date')) + ->valueFormat('YYYY-MM-DD'), + ]), + ])) + ->columns([ + amis()->TableColumn()->name('id')->label(__('finance.ledger.id')), + amis()->TableColumn()->name('date')->label(__('finance.ledger.date')), + amis()->TableColumn()->name('store.title')->label(__('finance.ledger.store')), + amis()->TableColumn()->name('sales')->label(__('finance.ledger.sales')), + amis()->TableColumn()->name('expenditure')->label(__('finance.ledger.expenditure')), + amis()->TableColumn()->name('expected_commission')->label(__('finance.ledger.expected_commission')), + amis()->TableColumn()->name('actual_commission')->label(__('finance.ledger.actual_commission')), + amis()->TableColumn()->name('expected_income')->label(__('finance.ledger.expected_income')), + amis()->TableColumn()->name('actual_income')->label(__('finance.ledger.actual_income')), + amis()->TableColumn() + ->name('ledger_status') + ->label(__('finance.ledger.ledger_status')) + ->type('mapping') + ->map(LedgerStatus::labelMap()), + amis()->TableColumn()->name('created_at')->label(__('finance.ledger.created_at')), + $this->rowActions([ + $this->rowApprovalButton() + ->visibleOn('${ledger_status == 2}'), + $this->rowEditButton(true) + ->visible(Admin::user()->can('admin.finance.ledgers.update')) + ->visibleOn('${ledger_status == 1 || ledger_status == 4}'), + $this->rowShowButton() + ->visible(Admin::user()->can('admin.finance.ledgers.view')), + ]), + ]); + + return $this->baseList($crud); + } + + public function form(): Form + { + return $this->baseForm()->title('')->body([ + amis()->NumberControl() + ->name('actual_commission') + ->label(__('finance.ledger.actual_commission')) + ->precision(2) + ->showSteps(false) + ->required(), + + amis()->NumberControl() + ->name('actual_income') + ->label(__('finance.ledger.actual_income')) + ->precision(2) + ->showSteps(false) + ->required(), + ]); + } + + public function detail(): Form + { + return $this->baseDetail()->title()->body([ + amis()->Property()->title('上报数据')->column(2)->items([ + ['label' => __('finance.ledger.date'), 'content' => '${date}'], + ['label' => __('finance.ledger.store'), 'content' => '${store.title}'], + ['label' => __('finance.ledger.sales'), 'content' => '${sales}'], + ['label' => __('finance.ledger.expenditure'), 'content' => '${expenditure}'], + ['label' => __('finance.ledger.expected_commission'), 'content' => '${expected_commission}'], + ['label' => __('finance.ledger.actual_commission'), 'content' => '${actual_commission}'], + ['label' => __('finance.ledger.expected_income'), 'content' => '${expected_income}'], + ['label' => __('finance.ledger.actual_income'), 'content' => '${actual_income}'], + ['label' => __('finance.ledger.photos'), 'content' => amis()->Images()->enlargeAble()->source('${photos}')->enlargeWithGallary(), 'span' => 2], + ['label' => __('finance.ledger.ledger_status'), 'content' => amis()->Mapping()->map(LedgerStatus::labelMap())->value('${ledger_status}'), 'span' => 2], + ]) + ]); + } + + /** + * 审核 + */ + public function approval($id, Request $request) + { + $validator = Validator::make( + data: $request->all(), + rules: [ + 'approval_result' => ['bail', 'required', Rule::in([1, 2])], + 'failed_reason' => ['bail', 'required_if:approval_result,2'], + ], + attributes: [ + 'approval_result' => __('finance.ledger.approval_result'), + 'failed_reason' => __('finance.ledger.failed_reason'), + ], + ); + + if ($validator->fails()) { + return $this->response()->fail($validator->errors()->first()); + } + + $ledger = Ledger::findOrFail($id); + + if ($ledger->ledger_status !== LedgerStatus::Processing) { + return $this->response()->fail('上报数据不是待审核状态'); + } + + if ($request->input('approval_result') == 1) { + $ledger->ledger_status = LedgerStatus::Passed; + } else { + $ledger->ledger_status = LedgerStatus::Rejected; + } + + $ledger->save(); + + return $this->response()->success(null, '操作成功'); + } + + protected function rowApprovalButton() + { + return amis()->DialogAction()->icon('fa-regular fa-check-circle')->label(__('finance.ledger.approval'))->level('link')->dialog( + amis()->Dialog()->title(__('finance.ledger.approval'))->body([ + amis()->Form()->title('') + ->api('post:'.admin_url('finance/ledgers/${id}/approval')) + ->body([ + amis()->RadiosControl('approval_result', __('finance.ledger.approval_result')) + ->options([ + ['label' => '通过', 'value' => 1], + ['label' => '驳回', 'value' => 2], + ]) + ->selectFirst() + ->required(), + amis()->TextareaControl('failed_reason', __('finance.ledger.failed_reason')) + ->visibleOn('${approval_result == 2}') + ->required(), + ]), + ])->size('md') + ); + } +} diff --git a/app/Admin/Filters/LedgerFilter.php b/app/Admin/Filters/LedgerFilter.php new file mode 100644 index 0000000..e17881b --- /dev/null +++ b/app/Admin/Filters/LedgerFilter.php @@ -0,0 +1,23 @@ +query->where('store_id', $id); + } + + public function dateRange($dateRange) + { + $this->query->whereBetween('date', explode(',', $dateRange)); + } + + public function status($status) + { + $this->query->whereIn('ledger_status', explode(',', $status)); + } +} diff --git a/app/Admin/Services/LedgerService.php b/app/Admin/Services/LedgerService.php new file mode 100644 index 0000000..40f3668 --- /dev/null +++ b/app/Admin/Services/LedgerService.php @@ -0,0 +1,47 @@ + ['bail', 'required', 'numeric'], + 'actual_income' => ['bail', 'required', 'numeric'], + ], [], [ + 'actual_commission' => __('finance.ledger.actual_commission'), + 'actual_income' => __('finance.ledger.actual_income'), + ]); + + $model = $this->query()->whereKey($primaryKey)->firstOrFail(); + + switch ($model->ledger_status) { + case LedgerStatus::Processing: + abort(400, '不能编辑待审核的上报数据'); + break; + + case LedgerStatus::Passed: + abort(400, '不能编辑已完成的上报数据'); + break; + } + + return $model->update([ + 'actual_commission' => $data['actual_commission'], + 'actual_income' => $data['actual_income'], + 'ledger_status' => LedgerStatus::Processing, + ]); + } +} diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 948fd82..3008c6b 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -1,15 +1,16 @@ resource('rests', RestController::class); }); + /* + |-------------------------------------------------------------------------- + | 财务管理 + |-------------------------------------------------------------------------- + */ + $router->group([ + 'prefix' => 'finance', + 'as' => 'finance.', + ], function (Router $router) { + // 上报数据 + $router->resource('ledgers', LedgerController::class); + $router->post('ledgers/{ledger}/approval', [LedgerController::class, 'approval'])->name('ledgers.approval'); + }); /* |-------------------------------------------------------------------------- diff --git a/app/Enums/LedgerStatus.php b/app/Enums/LedgerStatus.php new file mode 100644 index 0000000..235d11d --- /dev/null +++ b/app/Enums/LedgerStatus.php @@ -0,0 +1,41 @@ + '待编辑', + static::Processing => '待审核', + static::Passed => '已完成', + static::Rejected => '未通过', + }; + } + + public static function options(): array + { + return collect(self::cases()) + ->map(fn (LedgerStatus $case) => [ + 'label' => $case->label(), + 'value' => $case->value, + ]) + ->all(); + } + + public static function labelMap(): array + { + return [ + static::Pending->value => '待编辑', + static::Processing->value => '待审核', + static::Passed->value => '已完成', + static::Rejected->value => '未通过', + ]; + } +} diff --git a/app/Models/Ledger.php b/app/Models/Ledger.php new file mode 100644 index 0000000..76d78ad --- /dev/null +++ b/app/Models/Ledger.php @@ -0,0 +1,69 @@ + LedgerStatus::Pending, + ]; + + protected $casts = [ + 'ledger_status' => LedgerStatus::class, + ]; + + protected $fillable = [ + 'store_id', + 'date', + 'sales', + 'expenditure', + 'expected_commission', + 'actual_commission', + 'expected_income', + 'actual_income', + 'photos', + 'ledger_status', + ]; + + public function store(): BelongsTo + { + return $this->belongsTo(Store::class); + } + + protected function photos(): Attribute + { + return Attribute::make( + get: function (mixed $value) { + if (! is_array($photos = json_decode($value ?? '', true))) { + $photos = []; + } + return $photos; + }, + set: fn (mixed $value) => json_encode(is_array($value) ? $value : []), + ); + } + + protected function ledgerStatusColor(): Attribute + { + return Attribute::make( + get: fn (mixed $value) => $this->ledger_status->color(), + ); + } + + protected function ledgerStatusLabel(): Attribute + { + return Attribute::make( + get: fn (mixed $value) => $this->ledger_status->label(), + ); + } +} diff --git a/database/migrations/2024_03_27_090406_create_ledgers_table.php b/database/migrations/2024_03_27_090406_create_ledgers_table.php new file mode 100644 index 0000000..ae732f3 --- /dev/null +++ b/database/migrations/2024_03_27_090406_create_ledgers_table.php @@ -0,0 +1,39 @@ +id(); + $table->bigInteger('store_id')->unsigned()->comment('门店ID'); + $table->date('date')->comment('日期'); + $table->decimal('sales', 10, 2)->comment('销量'); + $table->decimal('expenditure', 10, 2)->comment('支出'); + $table->decimal('expected_commission', 10, 2)->comment('预期佣金=销售金额*佣金比例'); + $table->decimal('actual_commission', 10, 2)->nullable()->comment('实际佣金'); + $table->decimal('expected_income', 10, 2)->comment('预期收益=预计佣金-支出'); + $table->decimal('actual_income', 10, 2)->nullable()->comment('实际收益'); + $table->text('photos')->nullable()->comment('照片'); + $table->tinyInteger('ledger_status')->default(1)->comment('状态: 1 待编辑, 2 待审核, 3 已通过, 4 未通过'); + $table->timestamps(); + + $table->unique(['store_id', 'date']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('ledgers'); + } +}; diff --git a/database/seeders/AdminPermissionSeeder.php b/database/seeders/AdminPermissionSeeder.php index a129b7d..0ee99e3 100644 --- a/database/seeders/AdminPermissionSeeder.php +++ b/database/seeders/AdminPermissionSeeder.php @@ -119,6 +119,26 @@ class AdminPermissionSeeder extends Seeder ], ], + /* + |-------------------------------------------------------------------------- + | 财务报表 + |-------------------------------------------------------------------------- + */ + 'finance' => [ + 'name' => '财务报表', + 'icon' => 'material-symbols:finance-mode', + 'uri' => '/finance', + 'children' => [ + 'ledgers' => [ + 'name' => '上报数据', + 'icon' => 'mdi:database', + 'uri' => '/finance/ledgers', + 'resource' => ['list', 'update', 'view'], + 'children' => [], + ], + ], + ], + /* |-------------------------------------------------------------------------- | 系统管理 diff --git a/lang/zh_CN/finance.php b/lang/zh_CN/finance.php new file mode 100644 index 0000000..a98bf5c --- /dev/null +++ b/lang/zh_CN/finance.php @@ -0,0 +1,22 @@ + [ + 'id' => 'ID', + 'date' => '日期', + 'store' => '门店', + 'sales' => '销量', + 'expenditure' => '支出', + 'expected_commission' => '预期佣金', + 'actual_commission' => '实际佣金', + 'expected_income' => '预期收益', + 'actual_income' => '实际收益', + 'photos' => '上传图片', + 'ledger_status' => '状态', + 'created_at' => '创建时间', + 'updated_at' => '更新时间', + 'approval' => '审核', + 'approval_result' => '审核结果', + 'failed_reason' => '驳回原因', + ], +];