上报数据

main
Jing Li 2024-03-27 23:09:12 +08:00
parent c12615cb47
commit efff9c2c03
9 changed files with 448 additions and 3 deletions

View File

@ -0,0 +1,170 @@
<?php
namespace App\Admin\Controllers\Finance;
use App\Admin\Controllers\AdminController;
use App\Admin\Services\LedgerService;
use App\Enums\LedgerStatus;
use App\Models\Ledger;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Slowlyo\OwlAdmin\Admin;
use Slowlyo\OwlAdmin\Renderers\Form;
use Slowlyo\OwlAdmin\Renderers\Page;
class LedgerController extends AdminController
{
protected string $serviceName = LedgerService::class;
public function list(): Page
{
$crud = $this->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')
);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Admin\Filters;
use EloquentFilter\ModelFilter;
class LedgerFilter extends ModelFilter
{
public function store($id)
{
$this->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));
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace App\Admin\Services;
use App\Admin\Filters\LedgerFilter;
use App\Enums\LedgerStatus;
use App\Models\Ledger;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
class LedgerService extends BaseService
{
protected string $modelName = Ledger::class;
protected string $modelFilterName = LedgerFilter::class;
protected array $withRelationships = ['store'];
public function update($primaryKey, $data): bool
{
Validator::validate($data, [
'actual_commission' => ['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,
]);
}
}

View File

@ -1,15 +1,16 @@
<?php
use App\Admin\Controllers\BaseKeywordController;
use App\Admin\Controllers\Finance\LedgerController;
use App\Admin\Controllers\Hr\EmployeeController;
use App\Admin\Controllers\Hr\RestController;
use App\Admin\Controllers\Store\StoreController;
use App\Admin\Controllers\Store\EmployeeController as StoreEmployeeController;
use App\Admin\Controllers\Store\DeviceController;
use App\Admin\Controllers\Store\EmployeeController as StoreEmployeeController;
use App\Admin\Controllers\Store\StoreController;
use App\Admin\Controllers\System\AdminMenuController;
use App\Admin\Controllers\System\AdminPermissionController;
use App\Admin\Controllers\System\AdminRoleController;
use App\Admin\Controllers\System\AdminUserController;
use App\Admin\Controllers\BaseKeywordController;
use App\Admin\Controllers\System\KeywordController;
use App\Admin\Controllers\System\WorkflowController;
use Illuminate\Routing\Router;
@ -69,6 +70,19 @@ Route::group([
$router->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');
});
/*
|--------------------------------------------------------------------------

View File

@ -0,0 +1,41 @@
<?php
namespace App\Enums;
enum LedgerStatus: int
{
case Pending = 1;
case Processing = 2;
case Passed = 3;
case Rejected = 4;
public function label(): string
{
return match ($this) {
static::Pending => '待编辑',
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 => '<span class="label label-primary">待编辑</span>',
static::Processing->value => '<span class="label label-warning">待审核</span>',
static::Passed->value => '<span class="label label-success">已完成</span>',
static::Rejected->value => '<span class="label label-danger">未通过</span>',
];
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace App\Models;
use App\Enums\LedgerStatus;
use App\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Ledger extends Model
{
use Filterable, HasFactory, HasDateTimeFormatter;
protected $attributes = [
'ledger_status' => 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(),
);
}
}

View File

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('ledgers', function (Blueprint $table) {
$table->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');
}
};

View File

@ -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' => [],
],
],
],
/*
|--------------------------------------------------------------------------
| 系统管理

View File

@ -0,0 +1,22 @@
<?php
return [
'ledger' => [
'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' => '驳回原因',
],
];