generated from liutk/owl-admin-base
上报数据
parent
c12615cb47
commit
efff9c2c03
|
|
@ -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')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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>',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
@ -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' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 系统管理
|
||||
|
|
|
|||
|
|
@ -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' => '驳回原因',
|
||||
],
|
||||
];
|
||||
Loading…
Reference in New Issue