数据上报

main
Jing Li 2024-04-12 18:24:00 +08:00
parent 2e309cda86
commit 9aff66fa23
6 changed files with 250 additions and 22 deletions

View File

@ -0,0 +1,197 @@
<?php
namespace App\Http\Controllers\Api\Ledger;
use App\Exceptions\RuntimeException;
use App\Http\Controllers\Api\Controller;
use App\Models\Keyword;
use App\Models\Ledger;
use App\Models\LedgerItem;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Throwable;
class LedgerController extends Controller
{
public function store(Request $request)
{
/** @var \App\Models\Employee */
$user = $request->user();
if (! $user->isStoreMaster()) {
throw new RuntimeException('非店长不可上报数据');
}
// 是否是彩票店数据上报
$isLotteryLedger = $user->store->isLotteryStore();
$validated = $request->validate(
rules: [
'date' => ['bail', 'required', 'date_format:Y-m-d'],
'items' => $isLotteryLedger ? ['bail', 'required', 'array'] : ['bail', 'array'],
'new_customers' => ['bail', 'required', 'int', 'min:0'],
'sales' => ['bail', 'required', 'numeric', 'min:0'],
'expenditure' => ['bail', 'required', 'numeric', 'min:0'],
'handover_amount' => ['bail', 'required', 'numeric', 'min:0'],
'photos' => ['bail', 'required', 'array'],
],
attributes: [
'date' => '日期',
'items' => '彩种数据',
'new_customers' => '新增客户',
'sales' => '销售合计',
'expenditure' => $isLotteryLedger ? '兑奖合计' : '支出合计',
'handover_amount' => '交账金额',
'photos' => '时段报表照片',
],
);
/** @var \Illuminate\Database\Eloquent\Collection */
$lotteryTypes = Keyword::filter(['parent_key' => 'lottery_type'])
->oldest('sort')
->get()
// 过滤未绑定上报数据类型的彩种
->filter(fn (Keyword $type) => (string) $type->value !== '');
// 上报数据项的格式:
// [
// ['id' => '上报数据类型1', 'sales' => '销售金额', 'expenditure' => '兑奖金额'],
// ['id' => '上报数据类型2', 'sales' => '销售金额', 'expenditure' => '兑奖金额'],
// ]
if ($isLotteryLedger) {
$items = collect($validated['items'])->keyBy('id');
/** @var \App\Models\Keyword */
foreach ($lotteryTypes as $lotteryType) {
$item = $items->get($lotteryType->value);
if (is_null($item)) {
throw new RuntimeException("{$lotteryType->name}未填写上报数据");
}
Validator::validate(
data: $item,
rules: [
'sales' => ['bail', 'required', 'numeric', 'min:0'],
'expenditure' => ['bail', 'required', 'numeric', 'min:0'],
],
attributes: [
'sales' => "[$lotteryType->name]销售金额",
'expenditure' => "[$lotteryType->name]兑奖金额",
],
);
}
}
/** @var \App\Models\Ledger|null */
$ledger = Ledger::where('store_id', $user->store_id)
->where('date', $validated['date'])
->first();
if ($ledger && ! $ledger->allowReReport()) {
throw new RuntimeException('上报数据已更新,不可重新上传');
}
$ratio = bcdiv($user->store->profit_ratio, 100, 2);
// 计算预期佣金
$validated['expected_commission'] = bcmul($validated['sales'], $ratio, 2);
// 计算预期收益
$validated['expected_income'] = bcsub($validated['expected_commission'], $validated['expenditure'], 2);
try {
DB::beginTransaction();
if (is_null($ledger)) {
$ledger = Ledger::create(
array_merge($validated, ['store_id' => $user->store_id])
);
} else {
$ledger->update($validated);
$ledger->items()->delete();
}
LedgerItem::insert(
collect(
$isLotteryLedger ? $validated['items'] : [
[
'id' => 'ledger_item_type_other',
'sales' => $ledger->sales,
'expenditure' => $ledger->expenditure,
],
]
)->map(fn ($item) => [
'date' => $ledger->date,
'store_id' => $ledger->store_id,
'ledger_id' => $ledger->id,
'ledger_item_type_id' => $item['id'],
'sales' => $item['sales'],
'expenditure' => $item['expenditure'],
'created_at' => $ledger->updated_at,
'updated_at' => $ledger->updated_at,
])->all()
);
DB::commit();
} catch (Throwable $e) {
DB::rollBack();
throw tap($e, fn ($e) => report($e));
}
return $this->prepareLedger($ledger);
}
public function show(string $date, Request $request)
{
/** @var \App\Models\Employee */
$user = $request->user();
/** @var \App\Models\Ledger|null */
$ledger = Ledger::with(['items'])
->where('store_id', $user->store_id)
->where('date', $date)
->first();
$data = null;
if ($ledger) {
$data = [
'date' => $ledger->date,
'items' => $ledger->items->map(fn ($item) => [
'id' => $item->ledger_item_type_id,
'sales' => $item->sales,
'expenditure' => $item->expenditure,
]),
'new_customers' => $ledger->new_customers,
'sales' => $ledger->sales,
'expenditure' => $ledger->expenditure,
'handover_amount' => $ledger->handover_amount,
'photos' => $ledger->photos,
];
}
return [
'data' => $ledger ? $this->prepareLedger($ledger) : null,
];
}
protected function prepareLedger(Ledger $ledger)
{
return [
'date' => $ledger->date,
'items' => $ledger->items->map(fn ($item) => [
'id' => $item->ledger_item_type_id,
'sales' => $item->sales,
'expenditure' => $item->expenditure,
]),
'new_customers' => $ledger->new_customers,
'sales' => $ledger->sales,
'expenditure' => $ledger->expenditure,
'handover_amount' => $ledger->handover_amount,
'photos' => $ledger->photos,
];
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers\Api\Ledger;
use App\Http\Controllers\Api\Controller;
use App\Models\Keyword;
class LotteryTypeController extends Controller
{
public function index()
{
/** @var \Illuminate\Database\Eloquent\Collection */
$lotteryTypes = Keyword::filter(['parent_key' => 'lottery_type'])->oldest('sort')->get();
return $lotteryTypes
->filter(fn (Keyword $type) => (string) $type->value !== '')
->map(fn ($item) => [
'id' => $item->value,
'name' => $item->name,
]);
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\Keyword;
class LotteryTypeController extends Controller
{
public function index()
{
/** @var \Illuminate\Database\Eloquent\Collection */
$lotteryTypes = Keyword::filter(['parent_key' => 'lottery_type'])->oldest('sort')->get();
return $lotteryTypes->map(fn ($item) => [
'id' => $item->value,
'name' => $item->name,
]);
}
}

View File

@ -19,6 +19,9 @@ class Ledger extends Model
protected $attributes = [
'new_customers' => 0,
'ledger_amount' => null,
'actual_commission' => null,
'actual_income' => null,
];
protected $fillable = [
@ -46,6 +49,14 @@ class Ledger extends Model
return $this->hasMany(LedgerItem::class);
}
/**
* 是否允许重新上报
*/
public function allowReReport(): bool
{
return is_null($this->ledger_amount) && is_null($this->actual_commission) && is_null($this->actual_income);
}
protected function ledgerDifference(): Attribute
{
return Attribute::make(

View File

@ -62,6 +62,19 @@ class Store extends Model
return $this->hasMany(Employee::class, 'store_id');
}
public function ledgers()
{
return $this->hasMany(Ledger::class);
}
/**
* 确认此门店是否是彩票店
*/
public function isLotteryStore(): bool
{
return preg_match('/^store_category_lottery_/', $this->category_id);
}
protected function businessStatusText(): Attribute
{
return new Attribute(

View File

@ -3,7 +3,8 @@
use App\Http\Controllers\Api\Auth\AccessTokenController;
use App\Http\Controllers\Api\ComplaintController;
use App\Http\Controllers\Api\FeedbackController;
use App\Http\Controllers\Api\LotteryTypeController;
use App\Http\Controllers\Api\Ledger\LedgerController;
use App\Http\Controllers\Api\Ledger\LotteryTypeController;
use App\Http\Controllers\Api\StatsController;
use Illuminate\Support\Facades\Route;
@ -22,8 +23,11 @@ Route::group([
Route::get('/stats/dashboard', [StatsController::class, 'dashboard']);
// 彩种类型
Route::get('lottery-types', [LotteryTypeController::class, 'index']);
// 数据上报
Route::post('/ledger/ledgers', [LedgerController::class, 'store']);
Route::get('/ledger/ledgers/{date}', [LedgerController::class, 'show']);
Route::get('/ledger/lottery-types', [LotteryTypeController::class, 'index']);
// 举报投诉
Route::post('complaints', [ComplaintController::class, 'store']);
// 意见箱