generated from liutk/owl-admin-base
admin 审核流程
parent
8f7cd01505
commit
803d154b76
|
|
@ -24,7 +24,6 @@ class RestController extends AdminController
|
||||||
$this->createTypeButton('drawer', 'xl')->visible(Admin::user()->can('admin.hr.rests.create')),
|
$this->createTypeButton('drawer', 'xl')->visible(Admin::user()->can('admin.hr.rests.create')),
|
||||||
...$this->baseHeaderToolBar(),
|
...$this->baseHeaderToolBar(),
|
||||||
])
|
])
|
||||||
->bulkActions([])
|
|
||||||
->filter($this->baseFilter()->body([
|
->filter($this->baseFilter()->body([
|
||||||
amis()->GroupControl()->mode('horizontal')->body([
|
amis()->GroupControl()->mode('horizontal')->body([
|
||||||
amisMake()->TextControl()->name('employee_name')->label(__('employee_sign.employee_id'))->placeholder(__('employee.name').'/'.__('employee.phone'))->columnRatio(3)->clearable(),
|
amisMake()->TextControl()->name('employee_name')->label(__('employee_sign.employee_id'))->placeholder(__('employee.name').'/'.__('employee.phone'))->columnRatio(3)->clearable(),
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ use App\Admin\Controllers\AdminController;
|
||||||
use App\Admin\Services\EmployeeSignService;
|
use App\Admin\Services\EmployeeSignService;
|
||||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||||
use App\Enums\{SignType, SignStatus};
|
|
||||||
use Slowlyo\OwlAdmin\Admin;
|
use Slowlyo\OwlAdmin\Admin;
|
||||||
|
use App\Enums\{SignType, SignStatus, SignTime};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 考勤打卡
|
* 考勤打卡
|
||||||
|
|
@ -36,6 +36,7 @@ class SignController extends AdminController
|
||||||
]),
|
]),
|
||||||
]))
|
]))
|
||||||
->columns([
|
->columns([
|
||||||
|
amisMake()->TableColumn()->name('date')->label(__('employee_sign.date')),
|
||||||
amisMake()->TableColumn()->name('store.title')->label(__('employee_sign.store_id')),
|
amisMake()->TableColumn()->name('store.title')->label(__('employee_sign.store_id')),
|
||||||
amisMake()->TableColumn()->name('employee.name')->label(__('employee.name')),
|
amisMake()->TableColumn()->name('employee.name')->label(__('employee.name')),
|
||||||
amisMake()->TableColumn()->name('sign_type')->label(__('employee_sign.sign_type'))
|
amisMake()->TableColumn()->name('sign_type')->label(__('employee_sign.sign_type'))
|
||||||
|
|
@ -57,9 +58,39 @@ class SignController extends AdminController
|
||||||
|
|
||||||
public function detail(): Form
|
public function detail(): Form
|
||||||
{
|
{
|
||||||
return $this->baseDetail()->title('')->body(amisMake()->Property()->items([
|
$detail = amisMake()->Property()->items([
|
||||||
|
['label' => __('employee_sign.date'), 'content' => '${date}'],
|
||||||
['label' => __('employee_sign.store_id'), 'content' => '${store.title}'],
|
['label' => __('employee_sign.store_id'), 'content' => '${store.title}'],
|
||||||
['label' => __('employee.name'), 'content' => '${employee.name}'],
|
['label' => __('employee.name'), 'content' => '${employee.name}'],
|
||||||
]));
|
|
||||||
|
['label' => __('employee_sign.sign_status'), 'content' => amisMake()->Mapping()->name('sign_status')->map(SignStatus::options())],
|
||||||
|
['label' => __('employee_sign.sign_type'), 'content' => amisMake()->Mapping()->name('sign_type')->map(SignType::options())],
|
||||||
|
['label' => __('employee_sign.remarks'), 'content' => '${remarks}'],
|
||||||
|
|
||||||
|
['label' => __('employee_sign.first_time'), 'content' => '${first_time}'],
|
||||||
|
['label' => __('employee_sign.last_time'), 'content' => '${last_time}', 'span' => 2],
|
||||||
|
]);
|
||||||
|
$logs = amisMake()->Service()
|
||||||
|
->id('employee-sign-log-table')
|
||||||
|
->initFetch(false)
|
||||||
|
->api(
|
||||||
|
amisMake()->BaseApi()->method('get')->url(admin_url('api/employee-sign-logs'))->data(['date' => '${date}', 'employee_id' => '${employee_id}'])
|
||||||
|
)
|
||||||
|
->body(
|
||||||
|
amisMake()->Table()->columns([
|
||||||
|
amisMake()->TableColumn()->name('sign_time')->label(__('employee_sign_log.sign_time'))->set('type', 'mapping')->map(SignTime::options()),
|
||||||
|
amisMake()->TableColumn()->name('time')->label(__('employee_sign_log.time')),
|
||||||
|
amisMake()->TableColumn()->name('sign_type')->label(__('employee_sign_log.sign_type'))->set('type', 'mapping')->map(SignType::options()),
|
||||||
|
amisMake()->TableColumn()->name('remarks')->label(__('employee_sign_log.remarks')),
|
||||||
|
amisMake()->TableColumn()->name('position.address')->label(__('employee_sign_log.position')),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
return $this->baseDetail()->title('')->onEvent([
|
||||||
|
'inited' => [
|
||||||
|
'actions' => [
|
||||||
|
['actionType' => 'reload', 'componentId' => 'employee-sign-log-table'],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])->body([$detail, amisMake()->Divider()->title(__('employee_sign.log')), $logs]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers\Hr;
|
||||||
|
|
||||||
|
use App\Admin\Controllers\AdminController;
|
||||||
|
use App\Models\EmployeeSignLog;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class SignLogController extends AdminController
|
||||||
|
{
|
||||||
|
public function shareList(Request $request)
|
||||||
|
{
|
||||||
|
$with = $request->input('with', []);
|
||||||
|
$list = EmployeeSignLog::with($with)->filter($request->all())->orderBy('time', 'asc')->get();
|
||||||
|
return $this->response()->success($list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers\Hr;
|
||||||
|
|
||||||
|
use App\Admin\Controllers\AdminController;
|
||||||
|
use App\Admin\Services\EmployeeSignRepairService;
|
||||||
|
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||||
|
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||||
|
use Slowlyo\OwlAdmin\Admin;
|
||||||
|
use App\Enums\{EmployeeStatus, SignTime, CheckStatus};
|
||||||
|
use App\Models\EmployeeSignRepair;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 补卡申请
|
||||||
|
*/
|
||||||
|
class SignRepairController extends AdminController
|
||||||
|
{
|
||||||
|
protected string $serviceName = EmployeeSignRepairService::class;
|
||||||
|
|
||||||
|
public function list(): Page
|
||||||
|
{
|
||||||
|
$crud = $this->baseCRUD()
|
||||||
|
->tableLayout('fixed')
|
||||||
|
->headerToolbar([
|
||||||
|
$this->createTypeButton('drawer', 'xl')->visible(Admin::user()->can('admin.hr.repairs.create')),
|
||||||
|
...$this->baseHeaderToolBar(),
|
||||||
|
])
|
||||||
|
->bulkActions([])
|
||||||
|
->filter($this->baseFilter()->body([
|
||||||
|
amis()->GroupControl()->mode('horizontal')->body([
|
||||||
|
amisMake()->SelectControl()->name('store_id')->label(__('employee_sign_repair.store_id'))
|
||||||
|
->source(admin_url('api/stores?_all=1'))
|
||||||
|
->labelField('title')
|
||||||
|
->valueField('id')
|
||||||
|
->searchable()
|
||||||
|
->columnRatio(3)
|
||||||
|
->clearable(),
|
||||||
|
amisMake()->TextControl()->name('employee_name')->label(__('employee_sign_repair.employee_id'))
|
||||||
|
->placeholder(__('employee.name').'/'.__('employee.phone'))
|
||||||
|
->columnRatio(3)
|
||||||
|
->clearable(),
|
||||||
|
]),
|
||||||
|
]))
|
||||||
|
->columns([
|
||||||
|
amisMake()->Column()->name('id')->label(__('employee_sign_repair.id')),
|
||||||
|
amisMake()->Column()->name('store.title')->label(__('employee_sign_repair.store_id')),
|
||||||
|
amisMake()->Column()->name('employee.name')->label(__('employee_sign_repair.employee_id')),
|
||||||
|
amisMake()->Column()->name('date')->label(__('employee_sign_repair.date')),
|
||||||
|
amisMake()->Column()->name('repair_type')->label(__('employee_sign_repair.repair_type'))
|
||||||
|
->set('type', 'mapping')
|
||||||
|
->map(SignTime::options()),
|
||||||
|
amisMake()->Column()->name('check_status')->label(__('employee_sign_repair.check_status'))
|
||||||
|
->set('type', 'mapping')
|
||||||
|
->map(CheckStatus::options()),
|
||||||
|
$this->rowActions([
|
||||||
|
$this->rowShowButton()->visible(Admin::user()->can('admin.hr.repairs.view')),
|
||||||
|
$this->rowEditTypeButton('drawer', 'xl')->visible(Admin::user()->can('admin.hr.repairs.update')),
|
||||||
|
$this->rowDeleteButton()->visible(Admin::user()->can('admin.hr.repairs.delete')),
|
||||||
|
amisMake()->AjaxAction()
|
||||||
|
->label('发起审核')
|
||||||
|
->level('link')
|
||||||
|
->api(amisMake()->BaseApi()->url(admin_url('api/workflow/apply'))->method('post')->data([
|
||||||
|
'subject_type' => (new EmployeeSignRepair)->getMorphClass(),
|
||||||
|
'subject_id' => '${id}',
|
||||||
|
'user' => '${employee_id}'
|
||||||
|
]))
|
||||||
|
->confirmText(__('admin.confirm'))
|
||||||
|
->visibleOn('${OR(check_status == '.CheckStatus::None->value.', check_status == '.CheckStatus::Cancel->value.', check_status == '.CheckStatus::Fail->value.')}'),
|
||||||
|
amisMake()->AjaxAction()
|
||||||
|
->label('审核通过')
|
||||||
|
->level('link')
|
||||||
|
->api(amisMake()->BaseApi()->url(admin_url('api/workflow/cancel'))->method('post')->data([
|
||||||
|
'subject_type' => EmployeeSignRepair::class,
|
||||||
|
'subject_id' => '${id}',
|
||||||
|
]))
|
||||||
|
->confirmText(__('admin.confirm'))
|
||||||
|
->visibleOn('${check_status == '.CheckStatus::Processing->value.'}'),
|
||||||
|
amisMake()->AjaxAction()
|
||||||
|
->label('审核不通过')
|
||||||
|
->level('link')
|
||||||
|
->api(amisMake()->BaseApi()->url(admin_url('api/workflow/cancel'))->method('post')->data([
|
||||||
|
'subject_type' => EmployeeSignRepair::class,
|
||||||
|
'subject_id' => '${id}',
|
||||||
|
]))
|
||||||
|
->confirmText(__('admin.confirm'))
|
||||||
|
->visibleOn('${check_status == '.CheckStatus::Processing->value.'}'),
|
||||||
|
amisMake()->AjaxAction()
|
||||||
|
->label('取消审核')
|
||||||
|
->level('link')
|
||||||
|
->api(amisMake()->BaseApi()->url(admin_url('api/workflow/cancel'))->method('post')->data([
|
||||||
|
'subject_type' => EmployeeSignRepair::class,
|
||||||
|
'subject_id' => '${id}',
|
||||||
|
]))
|
||||||
|
->confirmText(__('admin.confirm'))
|
||||||
|
->visibleOn('${check_status == '.CheckStatus::Processing->value.'}'),
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->baseList($crud);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function form($edit): Form
|
||||||
|
{
|
||||||
|
return $this->baseForm()->title('')->body([
|
||||||
|
amisMake()->SelectControl()->name('employee_id')->label(__('employee_sign.employee_id'))
|
||||||
|
->source(admin_url('api/employees?_all=1&store_id_gt=0&employee_status='.EmployeeStatus::Online->value))
|
||||||
|
->labelField('name')
|
||||||
|
->valueField('id')
|
||||||
|
->searchable()
|
||||||
|
->joinValues(false)
|
||||||
|
->extractValue()
|
||||||
|
->required(),
|
||||||
|
amisMake()->DateControl()->format('YYYY-MM-DD HH:mm:ss')->name('date')->label(__('employee_sign_repair.date'))->required(),
|
||||||
|
amisMake()->SelectControl()->options(SignTime::options())->name('repair_type')->label(__('employee_sign_repair.repair_type'))->required(),
|
||||||
|
amisMake()->TextControl()->name('reason')->label(__('employee_sign_repair.reason'))->required(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function detail(): Form
|
||||||
|
{
|
||||||
|
$detail = amisMake()->Property()->items([
|
||||||
|
]);
|
||||||
|
return $this->baseDetail()->title('')->body($detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,13 +37,12 @@ class EmployeeController extends AdminController
|
||||||
]),
|
]),
|
||||||
]))
|
]))
|
||||||
->columns([
|
->columns([
|
||||||
amisMake()->TableColumn()->name('id')->label(__('employee.id')),
|
|
||||||
amisMake()->TableColumn()->name('store.title')->label(__('employee.store_id')),
|
amisMake()->TableColumn()->name('store.title')->label(__('employee.store_id')),
|
||||||
amisMake()->TableColumn()->name('name')->label(__('employee.name')),
|
amisMake()->TableColumn()->name('name')->label(__('employee.name')),
|
||||||
// amisMake()->TableColumn()->name('store.master_id')->label(__('store.master_id'))->set('type', 'tpl')->tpl('${store.master_id == id ? "店长" : "--"}'),
|
// amisMake()->TableColumn()->name('store.master_id')->label(__('store.master_id'))->set('type', 'tpl')->tpl('${store.master_id == id ? "店长" : "--"}'),
|
||||||
amisMake()->TableColumn()->name('phone')->label(__('employee.phone')),
|
amisMake()->TableColumn()->name('phone')->label(__('employee.phone')),
|
||||||
$this->rowActions([
|
$this->rowActions([
|
||||||
$this->rowDeleteButton()->visible($user->can('admin.store.employees.delete')),
|
$this->rowDeleteButton()->hiddenOn('${store.master_id == id}')->visible($user->can('admin.store.employees.delete')),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ use App\Models\Employee;
|
||||||
use App\Models\Keyword;
|
use App\Models\Keyword;
|
||||||
use Slowlyo\OwlAdmin\Renderers\Form;
|
use Slowlyo\OwlAdmin\Renderers\Form;
|
||||||
use Slowlyo\OwlAdmin\Renderers\Page;
|
use Slowlyo\OwlAdmin\Renderers\Page;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审核流程管理
|
* 审核流程管理
|
||||||
|
|
@ -86,6 +89,51 @@ class WorkflowController extends AdminController
|
||||||
return $this->baseDetail()->title('')->body($detail);
|
return $this->baseDetail()->title('')->body($detail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function apply(Request $request)
|
||||||
|
{
|
||||||
|
$type = $request->input('subject_type');
|
||||||
|
$id = $request->input('subject_id');
|
||||||
|
$subject = Relation::getMorphedModel($type);
|
||||||
|
$user = $request->input('user');
|
||||||
|
$model = (new $subject)->findOrFail($id);
|
||||||
|
if ($request->filled('user')) {
|
||||||
|
$user = Employee::findOrFail($request->input('user'));
|
||||||
|
} else {
|
||||||
|
$user = $model->employee;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
if (!$this->service->apply($model, $user)) {
|
||||||
|
return $this->response()->fail($this->service->getError());
|
||||||
|
}
|
||||||
|
DB::commit();
|
||||||
|
return $this->response()->success();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
return $this->response()->fail($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancel(Request $request)
|
||||||
|
{
|
||||||
|
$type = $request->input('subject_type');
|
||||||
|
$id = $request->input('subject_id');
|
||||||
|
$model = (new $type)->findOrFail($id);
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
if (!$this->service->cancel($model)) {
|
||||||
|
return $this->response()->fail($this->service->getError());
|
||||||
|
}
|
||||||
|
DB::commit();
|
||||||
|
return $this->response()->success();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
return $this->response()->fail($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getJobOptions()
|
public function getJobOptions()
|
||||||
{
|
{
|
||||||
if (! $this->jobOptions) {
|
if (! $this->jobOptions) {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,11 @@ class EmployeeFilter extends ModelFilter
|
||||||
$this->where('store_id', $key);
|
$this->where('store_id', $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function storeIdGt($key)
|
||||||
|
{
|
||||||
|
$this->where('store_id', '>', 0);
|
||||||
|
}
|
||||||
|
|
||||||
public function masterStore($key)
|
public function masterStore($key)
|
||||||
{
|
{
|
||||||
$this->where('master_store_id', $key);
|
$this->where('master_store_id', $key);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Filters;
|
||||||
|
|
||||||
|
use EloquentFilter\ModelFilter;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class EmployeeSignLogFilter extends ModelFilter
|
||||||
|
{
|
||||||
|
protected $drop_id = false;
|
||||||
|
|
||||||
|
public function employeeId($key)
|
||||||
|
{
|
||||||
|
$this->where('employee_id', $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeId($key)
|
||||||
|
{
|
||||||
|
$this->where('store_id', $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function date($key)
|
||||||
|
{
|
||||||
|
$date = Carbon::createFromFormat('Y-m-d', $key);
|
||||||
|
$this->whereBetween('time', [$date->copy()->startOfDay(), $date->copy()->endOfDay()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Filters;
|
||||||
|
|
||||||
|
use EloquentFilter\ModelFilter;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class EmployeeSignRepairFilter extends ModelFilter
|
||||||
|
{
|
||||||
|
protected $drop_id = false;
|
||||||
|
|
||||||
|
public function employeeId($key)
|
||||||
|
{
|
||||||
|
$this->where('employee_id', $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeId($key)
|
||||||
|
{
|
||||||
|
$this->where('store_id', $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function date($key)
|
||||||
|
{
|
||||||
|
$date = Carbon::createFromFormat('Y-m-d', $key);
|
||||||
|
$this->whereBetween('time', [$date->copy()->startOfDay(), $date->copy()->endOfDay()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ class CheckPermission
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Admin::response()->fail(__('admin.unauthorized'));
|
return Admin::response()->fail(__('admin.unauthorized'), ['route' => $request->route()->getName()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkRoutePermission(Request $request): bool
|
protected function checkRoutePermission(Request $request): bool
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Services;
|
||||||
|
|
||||||
|
use App\Admin\Filters\EmployeeSignRepairFilter;
|
||||||
|
use App\Models\EmployeeSignRepair;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use App\Models\Employee;
|
||||||
|
|
||||||
|
class EmployeeSignRepairService extends BaseService
|
||||||
|
{
|
||||||
|
protected array $withRelationships = ['employee', 'store'];
|
||||||
|
|
||||||
|
protected string $modelName = EmployeeSignRepair::class;
|
||||||
|
|
||||||
|
protected string $modelFilterName = EmployeeSignRepairFilter::class;
|
||||||
|
|
||||||
|
public function store($data): bool
|
||||||
|
{
|
||||||
|
$data = $this->resloveData($data);
|
||||||
|
|
||||||
|
$validate = $this->validate($data);
|
||||||
|
if ($validate !== true) {
|
||||||
|
$this->setError($validate);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->modelName::create($data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resloveData($data, $model = null)
|
||||||
|
{
|
||||||
|
// 获取员工所在的门店
|
||||||
|
if (!isset($data['store_id']) && isset($data['employee_id'])) {
|
||||||
|
$data['store_id'] = Employee::where('id', $data['employee_id'])->value('store_id');
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate($data, $model = null)
|
||||||
|
{
|
||||||
|
$createRules = [
|
||||||
|
'date' => ['required'],
|
||||||
|
'store_id' => ['required'],
|
||||||
|
'employee_id' => ['required'],
|
||||||
|
'reason' => ['required'],
|
||||||
|
'repair_type' => ['required'],
|
||||||
|
];
|
||||||
|
$updateRules = [];
|
||||||
|
$message = [
|
||||||
|
'date.required' => __('employee_sign.date') . '必填',
|
||||||
|
'store_id.required' => __('employee_sign.store_id') . '必填',
|
||||||
|
'employee_id.required' => __('employee_sign.employee_id') . '必填',
|
||||||
|
'reason.required' => __('employee_sign.reason') . '必填',
|
||||||
|
'repair_type.required' => __('employee_sign.repair_type') . '必填',
|
||||||
|
];
|
||||||
|
$validator = Validator::make($data, $model ? $updateRules : $createRules, $message);
|
||||||
|
if ($validator->fails()) {
|
||||||
|
return $validator->errors()->first();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ use App\Models\EmployeeRest;
|
||||||
use App\Models\Employee;
|
use App\Models\Employee;
|
||||||
use App\Enums\SignType;
|
use App\Enums\SignType;
|
||||||
use App\Enums\SignStatus;
|
use App\Enums\SignStatus;
|
||||||
|
use App\Enums\SignTime;
|
||||||
|
|
||||||
class EmployeeSignService extends BaseService
|
class EmployeeSignService extends BaseService
|
||||||
{
|
{
|
||||||
|
|
@ -26,6 +27,7 @@ class EmployeeSignService extends BaseService
|
||||||
$date = now()->subDay();
|
$date = now()->subDay();
|
||||||
$start = $date->copy()->startOfDay();
|
$start = $date->copy()->startOfDay();
|
||||||
$end = $date->copy()->endOfDay();
|
$end = $date->copy()->endOfDay();
|
||||||
|
// 打卡日志
|
||||||
$list = EmployeeSignLog::whereBetween('time', [$start, $end])->get();
|
$list = EmployeeSignLog::whereBetween('time', [$start, $end])->get();
|
||||||
// 休息的员工
|
// 休息的员工
|
||||||
$restEmployeeIds = EmployeeRest::whereBetWeen('date', [$start, $end])->pluck('employee_id');
|
$restEmployeeIds = EmployeeRest::whereBetWeen('date', [$start, $end])->pluck('employee_id');
|
||||||
|
|
@ -37,18 +39,18 @@ class EmployeeSignService extends BaseService
|
||||||
$status = 0;
|
$status = 0;
|
||||||
// 外勤打卡-事由
|
// 外勤打卡-事由
|
||||||
$remarks = null;
|
$remarks = null;
|
||||||
// 上班时间: 12:00 前打卡的都算
|
// 上班打卡
|
||||||
$firstTime = null;
|
$firstTime = null;
|
||||||
if ($item = $logs->where('time', '<=', $date->format('Y-m-d 12:00'))->sortBy('time')->first()) {
|
if ($item = $logs->where('sign_time', SignTime::Morning)->sortBy('time')->first()) {
|
||||||
$firstTime = $item->time;
|
$firstTime = $item->time;
|
||||||
$status ++;
|
$status ++;
|
||||||
if ($item->sign_type == SignType::Outside) {
|
if ($item->sign_type == SignType::Outside) {
|
||||||
$remarks = $item->remarks;
|
$remarks = $item->remarks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 下班时间: 24:00 前打卡的都算
|
// 下班打卡
|
||||||
$lastTime = null;
|
$lastTime = null;
|
||||||
if ($item = $logs->where('time', '<=', $date->format('Y-m-d 24:00'))->sortByDesc('time')->first()) {
|
if ($item = $logs->where('sign_time', SignTime::Afternoon)->sortByDesc('time')->first()) {
|
||||||
$lastTime = $item->time;
|
$lastTime = $item->time;
|
||||||
$status ++;
|
$status ++;
|
||||||
if ($item->sign_type == SignType::Outside) {
|
if ($item->sign_type == SignType::Outside) {
|
||||||
|
|
@ -56,7 +58,7 @@ class EmployeeSignService extends BaseService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 打卡类型
|
// 打卡类型
|
||||||
$type = SignType::Absent;
|
$type = SignType::None;
|
||||||
if ($status > 0) {
|
if ($status > 0) {
|
||||||
$type = $logs->where('sign_type', SignType::Outside)->count() > 0 ? SignType::Outside : SignType::Normal;
|
$type = $logs->where('sign_type', SignType::Outside)->count() > 0 ? SignType::Outside : SignType::Normal;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class StoreService extends BaseService
|
||||||
$model = $this->modelName::create($data);
|
$model = $this->modelName::create($data);
|
||||||
|
|
||||||
// 绑定店长
|
// 绑定店长
|
||||||
// Employee::where('id', $data['master_id'])->update(['store_id' => $model->id]);
|
Employee::where('id', $data['master_id'])->update(['store_id' => $model->id]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -48,9 +48,9 @@ class StoreService extends BaseService
|
||||||
}
|
}
|
||||||
|
|
||||||
// 还原以前的店长
|
// 还原以前的店长
|
||||||
// if (isset($data['master_id']) && $model->master_id != $data['master_id']) {
|
if (isset($data['master_id']) && $model->master_id != $data['master_id']) {
|
||||||
// Employee::where('id', $model->master_id)->update(['store_id' => 0]);
|
Employee::where('id', $model->master_id)->update(['store_id' => 0]);
|
||||||
// }
|
}
|
||||||
|
|
||||||
return $model->update($data);
|
return $model->update($data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
namespace App\Admin\Services;
|
namespace App\Admin\Services;
|
||||||
|
|
||||||
use App\Enums\CheckType;
|
use App\Enums\{CheckType, CheckStatus};
|
||||||
use App\Models\Employee;
|
use App\Models\{Employee, Store, Keyword, Workflow, WorkflowLog};
|
||||||
use App\Models\Keyword;
|
|
||||||
use App\Models\Workflow;
|
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
|
use App\Contracts\Checkable;
|
||||||
|
|
||||||
class WorkFlowService extends BaseService
|
class WorkFlowService extends BaseService
|
||||||
{
|
{
|
||||||
|
|
@ -17,10 +16,95 @@ class WorkFlowService extends BaseService
|
||||||
|
|
||||||
protected string $modelFilterName = '';
|
protected string $modelFilterName = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发起审核申请
|
||||||
|
* 1. 待审核 Model 实现 Checkable
|
||||||
|
* 2. 生成全部的审核流程
|
||||||
|
*
|
||||||
|
* @param Checkable $subject 待审核记录
|
||||||
|
* @param Employee $user 申请人
|
||||||
|
*
|
||||||
|
* @return boolean true: 成功, false: 失败, $this->getError(): 错误消息
|
||||||
|
*/
|
||||||
|
public function apply(Checkable $subject, Employee $user)
|
||||||
|
{
|
||||||
|
if ($subject->check_status === CheckStatus::Success->value) {
|
||||||
|
return $this->setError('已经审核通过');
|
||||||
|
}
|
||||||
|
if ($subject->check_status === CheckStatus::Processing->value) {
|
||||||
|
return $this->setError('正在审核中');
|
||||||
|
}
|
||||||
|
$result = $subject->checkApplyPre();
|
||||||
|
if ($result !== true) {
|
||||||
|
return $this->setError($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
$workflow = Workflow::where('key', $subject->getCheckKey())->first();
|
||||||
|
// 没有配置审核流程, 直接通过
|
||||||
|
if (!$workflow || !$workflow->config) {
|
||||||
|
$subject->checkSuccess();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$jobs = Keyword::where('parent_key', 'job')->get();
|
||||||
|
$config = collect($workflow->config)->sortBy('sort');
|
||||||
|
$batchId = WorkflowLog::max('id') + 1;
|
||||||
|
$subjectData = $subject->toArray();
|
||||||
|
foreach($config as $item) {
|
||||||
|
$checkValue = '';
|
||||||
|
$checkName = '';
|
||||||
|
// 职位审核
|
||||||
|
if ($item['type'] == CheckType::Job->value) {
|
||||||
|
// 没有门店, 则跳过
|
||||||
|
if (!$user->store_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 所属门店的职位审核
|
||||||
|
$store = Store::findOrFail($user->store_id);
|
||||||
|
$job = $jobs->firstWhere('key', $item['value']);
|
||||||
|
$checkValue = $store->id . '-' . $job->key;
|
||||||
|
$checkName = $store->title . '-' . $job->name;
|
||||||
|
}
|
||||||
|
// 指定用户审核
|
||||||
|
else if ($item['type'] == CheckType::User->value) {
|
||||||
|
$checkUser = Employee::findOrFail($item['value']);
|
||||||
|
$checkValue = $checkUser->id;
|
||||||
|
$checkName = $checkUser->name;
|
||||||
|
} else {
|
||||||
|
return $this->setError('未知的审核类型: ' . $item['type']);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$subject->workflows()->create([
|
||||||
|
'batch_id' => $batchId,
|
||||||
|
'check_type' => $item['type'],
|
||||||
|
'check_value' => $checkValue,
|
||||||
|
'check_name' => $checkName,
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'subject_data' => $subjectData,
|
||||||
|
'sort' => $item['sort']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$subject->checkApply();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消审核
|
||||||
|
*/
|
||||||
|
public function cancel(Checkable $subject)
|
||||||
|
{
|
||||||
|
$subject->workflows()->whereIn('check_status', [CheckStatus::None, CheckStatus::Processing])->delete();
|
||||||
|
$subject->checkCancel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function resloveData($data, $model = null)
|
public function resloveData($data, $model = null)
|
||||||
{
|
{
|
||||||
if (isset($data['config'])) {
|
if (isset($data['config'])) {
|
||||||
foreach ($data['config'] as $key => &$item) {
|
foreach ($data['config'] as $key => &$item) {
|
||||||
|
if (!$item) {
|
||||||
|
$data['config'] = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
$item['title'] = match ($item['type']) {
|
$item['title'] = match ($item['type']) {
|
||||||
CheckType::Job->value => CheckType::Job->text(),
|
CheckType::Job->value => CheckType::Job->text(),
|
||||||
CheckType::User->value => CheckType::User->text(),
|
CheckType::User->value => CheckType::User->text(),
|
||||||
|
|
@ -45,6 +129,7 @@ class WorkFlowService extends BaseService
|
||||||
$createRules = [
|
$createRules = [
|
||||||
'key' => ['required', Rule::unique('workflows', 'key')],
|
'key' => ['required', Rule::unique('workflows', 'key')],
|
||||||
'name' => ['required'],
|
'name' => ['required'],
|
||||||
|
'config' => ['required', 'array'],
|
||||||
];
|
];
|
||||||
$updateRules = [
|
$updateRules = [
|
||||||
'key' => [Rule::unique('workflows', 'key')->ignore($model?->id)],
|
'key' => [Rule::unique('workflows', 'key')->ignore($model?->id)],
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ use App\Admin\Controllers\Finance\LedgerController;
|
||||||
use App\Admin\Controllers\Hr\EmployeeController;
|
use App\Admin\Controllers\Hr\EmployeeController;
|
||||||
use App\Admin\Controllers\Hr\RestController;
|
use App\Admin\Controllers\Hr\RestController;
|
||||||
use App\Admin\Controllers\Hr\SignController;
|
use App\Admin\Controllers\Hr\SignController;
|
||||||
|
use App\Admin\Controllers\Hr\SignLogController;
|
||||||
|
use App\Admin\Controllers\Hr\SignRepairController;
|
||||||
use App\Admin\Controllers\Store\DeviceController;
|
use App\Admin\Controllers\Store\DeviceController;
|
||||||
use App\Admin\Controllers\Store\EmployeeController as StoreEmployeeController;
|
use App\Admin\Controllers\Store\EmployeeController as StoreEmployeeController;
|
||||||
use App\Admin\Controllers\Store\StoreController;
|
use App\Admin\Controllers\Store\StoreController;
|
||||||
|
|
@ -27,6 +29,8 @@ Route::group([
|
||||||
|
|
||||||
$router->resource('index', \App\Admin\Controllers\HomeController::class);
|
$router->resource('index', \App\Admin\Controllers\HomeController::class);
|
||||||
|
|
||||||
|
$router->get('dashboard', [\App\Admin\Controllers\HomeController::class, 'index'])->name('home');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| 门店管理
|
| 门店管理
|
||||||
|
|
@ -71,6 +75,8 @@ Route::group([
|
||||||
$router->resource('rests', RestController::class)->only(['index', 'create', 'store', 'destroy']);
|
$router->resource('rests', RestController::class)->only(['index', 'create', 'store', 'destroy']);
|
||||||
// 打卡情况
|
// 打卡情况
|
||||||
$router->resource('signs', SignController::class)->only(['index', 'show']);
|
$router->resource('signs', SignController::class)->only(['index', 'show']);
|
||||||
|
// 补卡申请
|
||||||
|
$router->resource('repairs', SignRepairController::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -140,6 +146,10 @@ Route::group([
|
||||||
], function (Router $router) {
|
], function (Router $router) {
|
||||||
$router->get('stores', [StoreController::class, 'shareList']);
|
$router->get('stores', [StoreController::class, 'shareList']);
|
||||||
$router->get('employees', [EmployeeController::class, 'shareList']);
|
$router->get('employees', [EmployeeController::class, 'shareList']);
|
||||||
|
$router->get('employee-sign-logs', [SignLogController::class, 'shareList']);
|
||||||
$router->get('keywords/tree-list', [KeywordController::class, 'getTreeList'])->name('api.keywords.tree-list');
|
$router->get('keywords/tree-list', [KeywordController::class, 'getTreeList'])->name('api.keywords.tree-list');
|
||||||
|
|
||||||
|
$router->post('workflow/apply', [WorkflowController::class, 'apply']);
|
||||||
|
$router->post('workflow/cancel', [WorkflowController::class, 'cancel']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use App\Admin\Services\EmployeeSignService;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class EmployeeSign extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'app:employee-sign';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = '员工签到情况';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
EmployeeSignService::make()->signResult();
|
||||||
|
DB::commit();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
logger('app:employee-sign error');
|
||||||
|
logger()->error($e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,13 +2,31 @@
|
||||||
|
|
||||||
namespace App\Enums;
|
namespace App\Enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核状态
|
||||||
|
*/
|
||||||
enum CheckStatus: int
|
enum CheckStatus: int
|
||||||
{
|
{
|
||||||
case None = 0;
|
/**
|
||||||
case Processing = 1;
|
* 未审核
|
||||||
case Success = 2;
|
*/
|
||||||
case Fail = 3;
|
case None = 1;
|
||||||
case Cancel = 4;
|
/**
|
||||||
|
* 审核中
|
||||||
|
*/
|
||||||
|
case Processing = 2;
|
||||||
|
/**
|
||||||
|
* 审核通过
|
||||||
|
*/
|
||||||
|
case Success = 3;
|
||||||
|
/**
|
||||||
|
* 审核不通过
|
||||||
|
*/
|
||||||
|
case Fail = 4;
|
||||||
|
/**
|
||||||
|
* 已取消
|
||||||
|
*/
|
||||||
|
case Cancel = 5;
|
||||||
|
|
||||||
public static function options(): array
|
public static function options(): array
|
||||||
{
|
{
|
||||||
|
|
@ -25,21 +43,4 @@ enum CheckStatus: int
|
||||||
{
|
{
|
||||||
return data_get(self::options(), $this->value);
|
return data_get(self::options(), $this->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function coplorMap()
|
|
||||||
{
|
|
||||||
// 'active' | 'inactive' | 'error' | 'success' | 'processing' | 'warning' |
|
|
||||||
return [
|
|
||||||
self::None->value => 'active',
|
|
||||||
self::Processing->value => 'processing',
|
|
||||||
self::Success->value => 'success',
|
|
||||||
self::Fail->value => 'error',
|
|
||||||
self::Cancel->value => 'inactive',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function color()
|
|
||||||
{
|
|
||||||
return data_get(self::coplorMap(), $this->value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,24 @@
|
||||||
|
|
||||||
namespace App\Enums;
|
namespace App\Enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核类型
|
||||||
|
*/
|
||||||
enum CheckType: string
|
enum CheckType: string
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 职位
|
||||||
|
*/
|
||||||
case Job = 'job';
|
case Job = 'job';
|
||||||
|
/**
|
||||||
|
* 员工
|
||||||
|
*/
|
||||||
case User = 'user';
|
case User = 'user';
|
||||||
|
|
||||||
public static function options()
|
public static function options()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
self::Job->value => '职务',
|
self::Job->value => '职位',
|
||||||
self::User->value => '员工',
|
self::User->value => '员工',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,13 @@ namespace App\Enums;
|
||||||
*/
|
*/
|
||||||
enum EmployeeStatus: int
|
enum EmployeeStatus: int
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 在职
|
||||||
|
*/
|
||||||
case Online = 1;
|
case Online = 1;
|
||||||
|
/**
|
||||||
|
* 离职
|
||||||
|
*/
|
||||||
case Offline = 2;
|
case Offline = 2;
|
||||||
|
|
||||||
public static function map()
|
public static function map()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打卡时机
|
||||||
|
*/
|
||||||
|
enum SignTime: int
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 上班打卡
|
||||||
|
*/
|
||||||
|
case Morning = 1;
|
||||||
|
/**
|
||||||
|
* 下班打卡
|
||||||
|
*/
|
||||||
|
case Afternoon = 2;
|
||||||
|
|
||||||
|
public static function options()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::Morning->value => '上班',
|
||||||
|
self::Afternoon->value => '下班',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function text()
|
||||||
|
{
|
||||||
|
return data_get(self::options(), $this->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,18 +2,30 @@
|
||||||
|
|
||||||
namespace App\Enums;
|
namespace App\Enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打卡类型
|
||||||
|
*/
|
||||||
enum SignType: int
|
enum SignType: int
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* 正常打卡
|
||||||
|
*/
|
||||||
case Normal = 1;
|
case Normal = 1;
|
||||||
|
/**
|
||||||
|
* 外勤打卡
|
||||||
|
*/
|
||||||
case Outside = 2;
|
case Outside = 2;
|
||||||
case Absent = 3;
|
/**
|
||||||
|
* 未打卡
|
||||||
|
*/
|
||||||
|
case None = 3;
|
||||||
|
|
||||||
public static function options()
|
public static function options()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
self::Normal->value => '正常打卡',
|
self::Normal->value => '正常打卡',
|
||||||
self::Outside->value => '外勤打卡',
|
self::Outside->value => '外勤打卡',
|
||||||
self::Absent->value => '旷工',
|
self::None->value => '未打卡',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,8 +40,8 @@ enum SignType: int
|
||||||
'label' => self::Outside->text(),
|
'label' => self::Outside->text(),
|
||||||
'color' => '#d97116',
|
'color' => '#d97116',
|
||||||
],
|
],
|
||||||
self::Absent->value => [
|
self::None->value => [
|
||||||
'label' => self::Absent->text(),
|
'label' => self::None->text(),
|
||||||
'color' => '#a61922',
|
'color' => '#a61922',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,28 +2,36 @@
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Enums\SignType;
|
use App\Enums\{SignType, SignTime};
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use App\Traits\HasDateTimeFormatter;
|
use App\Traits\HasDateTimeFormatter;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use EloquentFilter\Filterable;
|
||||||
|
use App\Admin\Filters\EmployeeSignLogFilter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 员工-打卡流水
|
* 员工-打卡流水
|
||||||
*/
|
*/
|
||||||
class EmployeeSignLog extends Model
|
class EmployeeSignLog extends Model
|
||||||
{
|
{
|
||||||
use HasDateTimeFormatter, HasFactory;
|
use HasDateTimeFormatter, HasFactory, Filterable;
|
||||||
|
|
||||||
protected $table = 'employee_sign_logs';
|
protected $table = 'employee_sign_logs';
|
||||||
|
|
||||||
protected $fillable = ['store_id', 'employee_id', 'sign_type', 'remarks', 'position', 'time'];
|
protected $fillable = ['store_id', 'employee_id', 'sign_type', 'sign_time', 'remarks', 'position', 'time'];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'sign_type' => SignType::class,
|
'sign_type' => SignType::class,
|
||||||
|
'sign_time' => SignTime::class,
|
||||||
'position' => 'json',
|
'position' => 'json',
|
||||||
'time' => 'datetime',
|
'time' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function modelFilter()
|
||||||
|
{
|
||||||
|
return \App\Admin\Filters\EmployeeSignRepairFilter::class;
|
||||||
|
}
|
||||||
|
|
||||||
public function store()
|
public function store()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Store::class, 'store_id');
|
return $this->belongsTo(Store::class, 'store_id');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use App\Enums\SignTime;
|
||||||
|
use App\Traits\HasDateTimeFormatter;
|
||||||
|
use EloquentFilter\Filterable;
|
||||||
|
use App\Contracts\Checkable;
|
||||||
|
use App\Traits\HasCheckable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 补卡申请
|
||||||
|
*/
|
||||||
|
class EmployeeSignRepair extends Model implements Checkable
|
||||||
|
{
|
||||||
|
use HasDateTimeFormatter, Filterable, HasCheckable;
|
||||||
|
|
||||||
|
protected $table = 'employee_sign_repairs';
|
||||||
|
|
||||||
|
protected $fillable = ['date', 'store_id', 'employee_id', 'reason', 'repair_type', 'check_status', 'checked_at'];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'date' => 'date:Y-m-d',
|
||||||
|
'checked_at' => 'datetime',
|
||||||
|
'repair_type' => SignTime::class
|
||||||
|
];
|
||||||
|
|
||||||
|
public function modelFilter()
|
||||||
|
{
|
||||||
|
return EmployeeSignLogFilter::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Store::class, 'store_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function employee()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Employee::class, 'employee_id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ class AppServiceProvider extends ServiceProvider
|
||||||
Relation::enforceMorphMap(
|
Relation::enforceMorphMap(
|
||||||
collect([
|
collect([
|
||||||
\App\Models\AdminUser::class,
|
\App\Models\AdminUser::class,
|
||||||
|
\App\Models\EmployeeSignRepair::class,
|
||||||
])->mapWithKeys(fn ($model) => [(new $model)->getTable() => $model])->all()
|
])->mapWithKeys(fn ($model) => [(new $model)->getTable() => $model])->all()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
use App\Enums\CheckStatus;
|
||||||
|
use App\Models\WorkflowLog;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
trait HasCheckable
|
||||||
|
{
|
||||||
|
public function checkApply()
|
||||||
|
{
|
||||||
|
$this->update([
|
||||||
|
'check_status' => CheckStatus::Processing,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkSuccess()
|
||||||
|
{
|
||||||
|
$this->update([
|
||||||
|
'check_status' => CheckStatus::Success,
|
||||||
|
'checked_at' => now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkFail()
|
||||||
|
{
|
||||||
|
$this->update([
|
||||||
|
'check_status' => CheckStatus::Fail,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkCancel()
|
||||||
|
{
|
||||||
|
$this->update([
|
||||||
|
'check_status' => CheckStatus::Cancel,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCheckKey()
|
||||||
|
{
|
||||||
|
return Str::snake(class_basename(__CLASS__));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function workflows()
|
||||||
|
{
|
||||||
|
return $this->morphMany(WorkflowLog::class, 'subject');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许修改
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function canEdit(): bool
|
||||||
|
{
|
||||||
|
return !($this->check_status === CheckStatus::Processing);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询审核通过的记录
|
||||||
|
*
|
||||||
|
* @param Builder $q
|
||||||
|
*/
|
||||||
|
public function scopeChecked(Builder $q): Builder
|
||||||
|
{
|
||||||
|
return $q->where('check_status', CheckStatus::Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核成功前, 会调用该方法
|
||||||
|
* 返回错误信息 阻止审核通过
|
||||||
|
*/
|
||||||
|
public function checkSuccessPre()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核申请前, 会调用此方法
|
||||||
|
* 返回错误信息 阻止申请
|
||||||
|
*/
|
||||||
|
public function checkApplyPre()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function boot()
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
static::updating(function ($model) {
|
||||||
|
// 修改审核通过的内容, 需要重新审核
|
||||||
|
if (isset($model->checkListen) && count($model->checkListen) > 0 && $model->isDirty($model->checkListen) && $model->check_status === CheckStatus::Success) {
|
||||||
|
$model->check_status = CheckStatus::None;
|
||||||
|
$model->checked_at = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
static::deleting(function ($model) {
|
||||||
|
// 删除审核记录
|
||||||
|
$model->workflows()->delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许删除,已取消或已拒绝的
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function canDel(): bool
|
||||||
|
{
|
||||||
|
return $this->check_status === CheckStatus::Cancel || $this->check_status === CheckStatus::Fail || $this->check_status === CheckStatus::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,18 +24,18 @@ class EmployeeFactory extends Factory
|
||||||
$name = $faker->name;
|
$name = $faker->name;
|
||||||
$phone = $faker->phoneNumber();
|
$phone = $faker->phoneNumber();
|
||||||
|
|
||||||
// $adminUser = AdminUser::create([
|
$adminUser = AdminUser::create([
|
||||||
// 'username' => $phone,
|
'username' => $phone,
|
||||||
// 'password' => bcrypt($phone),
|
// 123456
|
||||||
// 'name' => $name,
|
'password' => '$12$exmmsMLDmZO4aNug/a4CquCU8237FNrwMa4S9EawhvO0XaIqZSD9i',
|
||||||
// ]);
|
'name' => $name,
|
||||||
// $adminUser = AdminUser::first();
|
]);
|
||||||
return [
|
return [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'phone' => $phone,
|
'phone' => $phone,
|
||||||
'prize_images' => ['https://via.placeholder.com/100x100.png'],
|
'prize_images' => ['https://via.placeholder.com/100x100.png'],
|
||||||
'skill_images' => ['https://via.placeholder.com/100x100.png'],
|
'skill_images' => ['https://via.placeholder.com/100x100.png'],
|
||||||
// 'admin_user_id' => $adminUser->id,
|
'admin_user_id' => $adminUser->id,
|
||||||
'join_at' => now(),
|
'join_at' => now(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ namespace Database\Factories;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use App\Models\EmployeeSignLog;
|
use App\Models\EmployeeSignLog;
|
||||||
use App\Models\Employee;
|
use App\Models\Employee;
|
||||||
use App\Enums\SignType;
|
use App\Enums\{SignType, SignTime};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\EmployeeSign>
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\EmployeeSign>
|
||||||
|
|
@ -21,11 +21,13 @@ class EmployeeSignLogFactory extends Factory
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
$employee = Employee::where('store_id', '>', 0)->inRandomOrder()->first();
|
$employee = Employee::where('store_id', '>', 0)->inRandomOrder()->first();
|
||||||
$type = $this->faker->randomElement(SignType::class);
|
$type = $this->faker->randomElement([SignType::Normal, SignType::Outside]);
|
||||||
|
$time = $this->faker->randomElement(SignTime::class);
|
||||||
return [
|
return [
|
||||||
'store_id' => $employee->store_id,
|
'store_id' => $employee->store_id,
|
||||||
'employee_id' => $employee->id,
|
'employee_id' => $employee->id,
|
||||||
'sign_type' => $type,
|
'sign_type' => $type,
|
||||||
|
'sign_time' => $time,
|
||||||
'remarks' => $type == SignType::Outside ? '我在外面的' : '',
|
'remarks' => $type == SignType::Outside ? '我在外面的' : '',
|
||||||
'position' => ['province' => '重庆', 'city' => '重庆市', 'address' => '重庆市南川区东城街道办事处东环路三号'],
|
'position' => ['province' => '重庆', 'city' => '重庆市', 'address' => '重庆市南川区东城街道办事处东环路三号'],
|
||||||
'time' => $this->faker->dateTimeBetween('-7 days', 'now')
|
'time' => $this->faker->dateTimeBetween('-7 days', 'now')
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class StoreFactory extends Factory
|
||||||
{
|
{
|
||||||
return $this->afterMaking(function (Store $model) {
|
return $this->afterMaking(function (Store $model) {
|
||||||
})->afterCreating(function (Store $model) {
|
})->afterCreating(function (Store $model) {
|
||||||
// Employee::where('id', $model->master_id)->update(['store_id' => $model->id]);
|
Employee::where('id', $model->master_id)->update(['store_id' => $model->id]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use App\Enums\CheckStatus;
|
||||||
|
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
|
|
@ -34,7 +35,7 @@ return new class extends Migration
|
||||||
$table->unsignedBigInteger('check_user_id')->nullable()->comment('实际审核人(admin_users.id)');
|
$table->unsignedBigInteger('check_user_id')->nullable()->comment('实际审核人(admin_users.id)');
|
||||||
$table->timestamp('checked_at')->nullable()->comment('审核时间');
|
$table->timestamp('checked_at')->nullable()->comment('审核时间');
|
||||||
$table->string('remarks')->nullable()->comment('审核备注');
|
$table->string('remarks')->nullable()->comment('审核备注');
|
||||||
$table->unsignedTinyInteger('check_status')->default(0)->comment('审核状态(0: 待审核, 1: 审核通过, 2: 审核不通过)');
|
$table->unsignedTinyInteger('check_status')->default(CheckStatus::None->value)->comment('审核状态');
|
||||||
$table->unsignedInteger('sort')->default(0)->comment('顺序(asc)');
|
$table->unsignedInteger('sort')->default(0)->comment('顺序(asc)');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use App\Enums\CheckStatus;
|
||||||
|
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
|
|
@ -31,6 +32,7 @@ return new class extends Migration
|
||||||
$table->foreignId('store_id')->comment('门店, stores.id');
|
$table->foreignId('store_id')->comment('门店, stores.id');
|
||||||
$table->foreignId('employee_id')->comment('员工, employees.id');
|
$table->foreignId('employee_id')->comment('员工, employees.id');
|
||||||
$table->unsignedInteger('sign_type')->default(1)->comment('类别(1: 正常打卡, 2: 外勤)');
|
$table->unsignedInteger('sign_type')->default(1)->comment('类别(1: 正常打卡, 2: 外勤)');
|
||||||
|
$table->unsignedInteger('sign_time')->default(1)->comment('打卡时间(1: 上班, 2: 下班)');
|
||||||
$table->string('remarks')->nullable()->comment('备注');
|
$table->string('remarks')->nullable()->comment('备注');
|
||||||
$table->json('position')->comment('打卡位置');
|
$table->json('position')->comment('打卡位置');
|
||||||
$table->timestamp('time')->comment('打卡时间');
|
$table->timestamp('time')->comment('打卡时间');
|
||||||
|
|
@ -55,7 +57,7 @@ return new class extends Migration
|
||||||
$table->string('reason')->comment('补卡原因');
|
$table->string('reason')->comment('补卡原因');
|
||||||
$table->unsignedInteger('repair_type')->default(1)->comment('上班/下班 补卡');
|
$table->unsignedInteger('repair_type')->default(1)->comment('上班/下班 补卡');
|
||||||
|
|
||||||
$table->unsignedInteger('check_status')->default(0)->comment('审核状态');
|
$table->unsignedInteger('check_status')->default(CheckStatus::None->value)->comment('审核状态');
|
||||||
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
|
$table->timestamp('checked_at')->nullable()->comment('审核通过时间');
|
||||||
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,13 @@ class AdminPermissionSeeder extends Seeder
|
||||||
'icon' => '',
|
'icon' => '',
|
||||||
'uri' => '/hr/signs',
|
'uri' => '/hr/signs',
|
||||||
'resource' => ['list', 'view'],
|
'resource' => ['list', 'view'],
|
||||||
]
|
],
|
||||||
|
'repairs' => [
|
||||||
|
'name' => '补卡申请',
|
||||||
|
'icon' => '',
|
||||||
|
'uri' => '/hr/repairs',
|
||||||
|
'resource' => true,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ class AdminSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
|
$now = now();
|
||||||
// 创建初始用户
|
// 创建初始用户
|
||||||
DB::table('admin_users')->truncate();
|
DB::table('admin_users')->truncate();
|
||||||
DB::table('admin_users')->insert([
|
DB::table('admin_users')->insert([
|
||||||
|
|
@ -21,6 +22,14 @@ class AdminSeeder extends Seeder
|
||||||
'password' => bcrypt('admin'),
|
'password' => bcrypt('admin'),
|
||||||
'name' => 'Administrator',
|
'name' => 'Administrator',
|
||||||
]);
|
]);
|
||||||
|
DB::table('employees')->insert([
|
||||||
|
'name' => 'admin',
|
||||||
|
'phone' => '12345678900',
|
||||||
|
'admin_user_id' => 1,
|
||||||
|
'join_at' => $now,
|
||||||
|
'created_at' => $now,
|
||||||
|
'updated_at' => $now,
|
||||||
|
]);
|
||||||
|
|
||||||
// 创建初始角色
|
// 创建初始角色
|
||||||
DB::table('admin_roles')->truncate();
|
DB::table('admin_roles')->truncate();
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class EmployeeSeeder extends Seeder
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
DB::table('employee_jobs')->truncate();
|
DB::table('employee_jobs')->truncate();
|
||||||
Employee::truncate();
|
Employee::where('admin_user_id', '!=', 1)->delete();
|
||||||
(new EmployeeFactory)->count(100)->create(['admin_user_id' => 1]);
|
(new EmployeeFactory)->count(100)->create(['admin_user_id' => 1]);
|
||||||
|
|
||||||
Store::truncate();
|
Store::truncate();
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,13 @@ class WorkflowSeeder extends Seeder
|
||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
|
$now = now();
|
||||||
|
$config = [
|
||||||
|
["sort" => 1, "type" => "user", "user" => 1, "title" => "员工", "value" => 1, "subTitle" => "Admin"],
|
||||||
|
];
|
||||||
Workflow::truncate();
|
Workflow::truncate();
|
||||||
|
Workflow::insert([
|
||||||
|
['key' => 'employee_sign_repair', 'name' => '补卡申请', 'config' => json_encode($config), 'created_at' => now(), 'updated_at' => now()]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ return [
|
||||||
'delete' => '删除',
|
'delete' => '删除',
|
||||||
'copy' => '复制',
|
'copy' => '复制',
|
||||||
'confirm_delete' => '确认删除选中项?',
|
'confirm_delete' => '确认删除选中项?',
|
||||||
|
'confirm' => '是否确定?',
|
||||||
'back' => '返回',
|
'back' => '返回',
|
||||||
'reset' => '重置',
|
'reset' => '重置',
|
||||||
'search' => '搜索',
|
'search' => '搜索',
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ return [
|
||||||
'updated_at' => '更新时间',
|
'updated_at' => '更新时间',
|
||||||
|
|
||||||
'rest' => '休息日',
|
'rest' => '休息日',
|
||||||
|
'log' => '打卡日志',
|
||||||
|
|
||||||
'date' => '日期',
|
'date' => '日期',
|
||||||
'store_id' => '门店',
|
'store_id' => '门店',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => 'ID',
|
||||||
|
'created_at' => '创建时间',
|
||||||
|
'updated_at' => '更新时间',
|
||||||
|
'store_id' => '门店',
|
||||||
|
'employee_id' => '员工',
|
||||||
|
'sign_type' => '打卡类型',
|
||||||
|
'sign_time' => '上班/下班',
|
||||||
|
'time' => '打卡时间',
|
||||||
|
'remarks' => '事由',
|
||||||
|
'position' => '位置',
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => 'ID',
|
||||||
|
'created_at' => '创建时间',
|
||||||
|
'updated_at' => '更新时间',
|
||||||
|
|
||||||
|
'date' => '补卡日期',
|
||||||
|
'store_id' => '门店',
|
||||||
|
'employee_id' => '员工',
|
||||||
|
'reason' => '补卡原因',
|
||||||
|
'repair_type' => '上班/下班',
|
||||||
|
'check_status' => '审核状态',
|
||||||
|
'checked_at' => '审核通过时间',
|
||||||
|
];
|
||||||
Loading…
Reference in New Issue