Compare commits

..

No commits in common. "master" and "nim_style" have entirely different histories.

30 changed files with 542 additions and 1739 deletions

View File

@ -24,7 +24,6 @@ class PipiCarInstallCommands extends InstallCommand
$this->packages = [
'main', // Главная страница
'address', // Главная страница
'auto_brands', // Бренд авто
'auto_bodywork', // Кузов авто
'auto_colors', // Цвета авто
@ -38,8 +37,7 @@ class PipiCarInstallCommands extends InstallCommand
'auto', // авто
'applications', //Заявки
'pipi_users', // Добавление логики для пользователей
'auto_calendar', // Добавление логики для пользователей
'faq' // FAQ
'auto_calendar' // Добавление логики для пользователей
];
}

View File

@ -5,11 +5,8 @@ namespace App\Http\Controllers;
use A7kz\Platform\Models\UniModel;
use A7kz\Platform\Modules\Platform\Acl\Facades\Acl;
use A7kz\Platform\Modules\Platform\Segment\Facades\Segment;
use App\Http\Notifications\TelegramNotification;
use App\Models\User;
use App\Modules\applications\Enum\ApplicationStatus;
use App\Modules\auto\Enums\AutoStatusEnums;
use App\Service\DepositService;
use Carbon\CarbonPeriod;
use Exception;
use Illuminate\Http\JsonResponse;
@ -61,101 +58,73 @@ class MobileApiController extends Controller
return response()->json($data);
}
/**
* @throws Exception
*/
public function sendApplication(Request $request): JsonResponse {
try {
$data = $request->all();
$data['started_at'] = Carbon::createFromFormat('d-m-Y H:i', $data['started_at']);
$data['ended_at'] = Carbon::createFromFormat('d-m-Y H:i', $data['ended_at']);
$authToken = null;
$data['car_id'] = null;
if ($request->header('Authorization')) {
$user = auth()->guard('api')->user();
$data['user_id'] = $user?->id;
$authToken = $request->header('Authorization');
$data = $request->all();
$data['started_at'] = Carbon::parse($data['started_at'])?->format('Y-m-d H:i:s');
$data['ended_at'] = Carbon::parse($data['ended_at'])?->format('Y-m-d H:i:s');
$authToken = null;
$data['car_id'] = null;
if ($request->header('Authorization')) {
$user = auth()->guard('api')->user();
$data['user_id'] = $user?->id;
$authToken = $request->header('Authorization');
} else {
$user = UniModel::model('core_users')
->where('email', $data['email'])
->first();
if ($user) {
$data['user_id'] = $user->id;
$tokenResult = $user->createToken('auth_token');
$authToken = $tokenResult->accessToken;
} else {
$user = User::where('email', $data['email'])->first();
if ($user) {
$data['user_id'] = $user->id;
$tokenResult = $user->createToken('auth_token');
$authToken = $tokenResult->plainTextToken;
} else {
$data['user_id'] = null;
}
$data['user_id'] = null;
}
$response = [
'status' => 'OK',
'message' => 'Заявка создана',
];
$car = $this->getAvailableCar($data['started_at'], $data['ended_at'], $data['mark_id'], $data['color_code']);
$status = ApplicationStatus::pending->value;
if (!isset($car)) {
return response()->json('Нет свободных машин');
}
$data['car_id'] = $car->id;
if ($data['user_id']) {
$period = CarbonPeriod::create($data['started_at'], $data['ended_at']);
$dates = array_map(fn($date) => $date->toDateString(), iterator_to_array($period));
foreach ($dates as $date) {
UniModel::model('pipi_auto_calendar')->create([
'auto_id' => $data['car_id'],
'date' => $date,
'status' => AutoStatusEnums::Rent->name
]);
}
$status = ApplicationStatus::approved->value;
$response['auth_token'] = $authToken;
$response['message'] = 'Заявка создана, и вы были автоматически авторизованы';
} elseif (!isset($data['user_id'])) {
$response['message'] = 'Заявка создана, с вами свяжется наш оператор';
}
$address_end = UniModel::model('pipi_address')->where('name', $data['address_end'])->first();
$address_start = UniModel::model('pipi_address')->where('name', $data['address_start'])->first();
$service = new DepositService;
$sums = $service->calculateSummary($data['mark_id'], $data['started_at'], $data['ended_at']);
$sum = $sums['discounted_sum'];
if ($data['deposit'] != $sums['deposit_base']) {
$sum += $data['deposit'];
}
UniModel::model('pipi_applications')->create([
'rent_day' => $data['rent_day'],
'started_at' => $data['started_at'],
'ended_at' => $data['ended_at'],
'user_id' => $data['user_id'],
'phone' => $user?->phone ?? $data['phone'] ?? '' ,
'car_id' => $data['car_id'],
'user_name' => $user?->name ?? $data['name'] ?? null,
'user_surname' => $user?->name ?? $data['surname'] ?? null,
'user_email' => $user?->email ?? $data['email'] ?? null,
'address_end' => $address_end?->id,
'address_start' => $address_start?->id,
'deposit' => $data['deposit'] ?? null,
'status' => $status,
'sum' => $sum
]);
$notification = [];
$notification['Адрес получения'] = $data['address_start'];
$notification['Адрес возврата'] = $data['address_end'];
$notification['Дата и время начала'] = $data['started_at'];
$notification['Дата и время возврата'] = $data['ended_at'];
$notification['Телефон'] = $data['phone'] ?? $user?->phone ?? null;
$notification['Имя'] = $data['name'] ?? $user?->name ?? null;
$notification['Почта'] = $data['email'] ?? $user?->email ?? null;
$notification['Сообщение'] = __('Новая заявка');
$this->sendNotification($notification);
return response()->json($response);
} catch (Exception $e) {
return response()->json($e->getMessage());
}
$response = [
'status' => 'OK',
'message' => 'Заявка создана',
];
$car = $this->getAvailableCar($data['started_at'], $data['ended_at'], $data['mark_id'], $data['color_code']);
if (!isset($car)) {
return response()->json('Нет свободных машин');
}
$data['car_id'] = $car->id;
if ($data['user_id']) {
$period = CarbonPeriod::create($data['started_at'], $data['ended_at']);
$dates = array_map(fn($date) => $date->toDateString(), iterator_to_array($period));
foreach ($dates as $date) {
UniModel::model('pipi_auto_calendar')->create([
'auto_id' => $data['car_id'],
'date' => $date,
'status' => AutoStatusEnums::Rent->name
]);
}
$response['auth_token'] = $authToken;
$response['message'] = 'Заявка создана, и вы были автоматически авторизованы';
} elseif (!isset($data['user_id'])) {
$response['message'] = 'Заявка создана, с вами свяжется наш оператор';
}
UniModel::model('pipi_applications')->create([
'rent_day' => $data['rent_day'],
'started_at' => $data['started_at'],
'ended_at' => $data['ended_at'],
'user_id' => $data['user_id'],
'phone' => $data['phone'],
'car_id' => $data['car_id'],
'user_name' => $data['name'] ?? null,
'user_surname' => $data['surname'] ?? null,
'user_email' => $data['email'] ?? null,
'status' => ApplicationStatus::pending->value
]);
return response()->json($response);
}
public function getApplications(): JsonResponse
@ -170,33 +139,20 @@ class MobileApiController extends Controller
]);
}
$status = $request->query('status');
$data = [];
$query = UniModel::model('pipi_applications')->where('user_id', $user->id);
if (isset($status)) {
$query->where('status', $status);
} else {
$query->whereIn('status', [ApplicationStatus::approved->value, ApplicationStatus::review->value]);
}
$applications = $query->orderBy('id', 'desc')->get();
foreach ($applications as $app) {
$applications = [];
$application = UniModel::model('pipi_applications')->where('user_id', $user->id)->get();
foreach ($application as $app) {
$car = UniModel::model('pipi_auto')->find($app->car_id);
$model = UniModel::model('pipi_brand_models')->find($car->model_id);
$photo = UniModel::model('core_files')->find($model->photo_id);
$photo = url('api/files/file/' . ltrim($photo?->path, '/'));
$class_id = UniModel::model('pipi_auto_classes')->where('id', $model->class_id)->first();
$bodywork_id = UniModel::model('pipi_auto_bodywork')->where('id', $model->bodywork_id)->first();
$address = UniModel::model('pipi_address')->where('id', $app->address_end)->first();
$data[] = [
$applications[] = [
'id' => $app->id,
'rent_day' => $app->rent_day,
'started_at' => $app->started_at,
'ended_at' => $app->ended_at,
'status' => ApplicationStatus::from($app->status)->getName(),
'sum' => $app->sum ?? 0,
'address_end' => $address->name ?? '',
'car' => [
'name' => $model->name,
'year' => $model->year,
@ -212,10 +168,10 @@ class MobileApiController extends Controller
]
];
}
if (empty($data)) {
$data = (object) $data;
if (empty($applications)) {
$applications = (object) $applications;
}
return response()->json($data);
return response()->json($applications);
}
public function login(Request $request)
@ -242,18 +198,16 @@ class MobileApiController extends Controller
}
}
public function checkAvailableCar($started_at = null, $ended_at = null, $modelId = null, $color = null): JsonResponse
{
if (!$started_at || !$ended_at || !$modelId || !$color) {
public function checkAvailableCar($started_at = null, $ended_at = null, $modelId = null): bool {
if (!$started_at || !$ended_at || !$modelId) {
$request = request();
$started_at = $started_at ?? $request->input('started_at');
$ended_at = $ended_at ?? $request->input('ended_at');
$modelId = $modelId ?? $request->input('model_id');
$color = Unimodel::model('pipi_auto_colors')->where('code', $request->input('color'))->first()->id;
}
if (!$started_at || !$ended_at || !$modelId || !$color) {
return response()->json(['available' => false]);
if (!$started_at || !$ended_at || !$modelId) {
return false;
}
$started_at = Carbon::parse($started_at)->format('Y-m-d');
@ -261,7 +215,6 @@ class MobileApiController extends Controller
$cars = UniModel::model('pipi_auto')
->where('is_inactive', '=', false)
->where('model_id', $modelId)
->where('color_id', $color)
->get();
$busyCars = UniModel::model('pipi_auto_calendar')
->whereBetween('date', [$started_at, $ended_at])
@ -270,9 +223,11 @@ class MobileApiController extends Controller
$availableCars = $cars->reject(fn($car) => in_array($car->id, $busyCars));
return response()->json(['available' => $availableCars->isNotEmpty()]);
return $availableCars->isNotEmpty();
}
public function getAvailableMarksList(Request $request): JsonResponse
{
$started_at = $request->query('started_at');
@ -285,11 +240,11 @@ class MobileApiController extends Controller
$availableMarks = [];
if ($class) {
$class_id = UniModel::model('pipi_auto_classes')->where('name', $class)->first()?->id;
$class_id = UniModel::model('pipi_auto_classes')->where('name', $class)->first()->id;
$marks = $marks->where('class_id', $class_id);
}
if ($bodywork) {
$bodywork_id = UniModel::model('pipi_auto_bodywork')->where('name', $bodywork)->first()?->id;
$bodywork_id = UniModel::model('pipi_auto_bodywork')->where('name', $bodywork)->first()->id;
$marks = $marks->where('bodywork_id', $bodywork_id);
}
@ -298,13 +253,13 @@ class MobileApiController extends Controller
continue;
}
$tariffs = UniModel::model('pipi_auto_tariffs')->where('model_id', $mark->id)->get();
if ($tariffs->isEmpty()) {
if (!$this->checkAvailableCar($started_at, $ended_at, $mark->id)) {
continue;
}
$cars = UniModel::model('pipi_auto')->where('model_id', $mark->id)->get();
if ($cars->isEmpty()) {
continue;
}
@ -334,7 +289,7 @@ class MobileApiController extends Controller
$carsByColor = $cars->groupBy('color_id');
foreach ($carsByColor as $carColor => $carsOfColor) {
$carColor = UniModel::model('pipi_auto_colors')->find($carColor);
$carColor = UniModel::model('pipi_auto_colors')->find($carColor)->code;
$colorPath = $path;
if ($carsOfColor->first()->photo_id) {
@ -342,14 +297,14 @@ class MobileApiController extends Controller
$colorPath = url('api/files/file/' . ltrim($photo->path, '/'));
}
$key = $mark->name . '-' . $mark->year . '-' . $carColor->code;
$key = $mark->name . '-' . $mark->year . '-' . $carColor;
$availableMarks[$key] = [
'id' => $mark->id,
'brand' => $brand?->name,
'mark' => $mark->name,
'year' => $mark->year,
'color' => $carColor->code,
'color' => $carColor,
'configuration' => $equipment?->name,
'people' => $mark?->people ?? '5',
'actuator' => $mark?->actuator ?? 'Передний',
@ -362,7 +317,6 @@ class MobileApiController extends Controller
'deposit' => $tariffs_new[0]['deposit'] ?? 30000,
'conditioner' => $mark?->conditioner,
'photo' => $colorPath,
'available' => $this->checkAvailableCar($started_at, $ended_at, $mark->id, $carColor->id)->getData()->available,
'tariffs' => $tariffs_new,
];
}
@ -382,7 +336,6 @@ class MobileApiController extends Controller
$cars = UniModel::model('pipi_auto')
->where('color_id', $color)
->where('model_id', $modelId)
->whereNull('regular_user')
->get();
$busyCars = UniModel::model('pipi_auto_calendar')
->whereBetween('date', [$started_at, $ended_at])
@ -406,14 +359,45 @@ class MobileApiController extends Controller
$start = Carbon::createFromFormat('d-m-Y H:i', $started_at);
$end = Carbon::createFromFormat('d-m-Y H:i', $ended_at);
try {
$service = new DepositService();
$summary = $service->calculateSummary($mark_id, $start, $end);
$days = $start->diffInDays($end);
return response()->json($summary);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 400);
// Получаем тарифы
$tariffs = UniModel::model('pipi_auto_tariffs')
->where('model_id', $mark_id)
->get();
if (!isset($tariffs)) {
return response()->json(__('Отсутсвуют данные по машине'));
}
$discountRate = null;
$basePrice = null;
foreach ($tariffs as $range) {
if ($range->day_range_start == 1 && $range->day_range_end == 2) {
$basePrice = $range->base_rate;
}
if ($days >= $range->day_range_start && $days <= $range->day_range_end) {
$discountRate = $range->base_rate;
}
}
// Если не нашли подходящий тариф, но дней больше 30 — применяем скидку
if (is_null($discountRate) && $days > 30 && count($tariffs) > 0) {
$discountRate = round($tariffs[0]->base_rate * 0.6);
}
// Считаем суммы
$baseSum = $basePrice ? $basePrice * $days : null;
$discountedSum = $discountRate ? $discountRate * $days : null;
return response()->json([
'days' => $days,
'base_price_per_day' => $basePrice,
'discount_price_per_day' => $discountRate,
'base_sum' => $baseSum,
'discounted_sum' => $discountedSum,
]);
}
public function closeOrder(Request $request): JsonResponse
@ -428,7 +412,7 @@ class MobileApiController extends Controller
]);
}
if (count($request->file('photos')) < 5) {
if (count($request->file('photos')) != 5) {
return response()->json([
'success' => false,
'message' => __('Неверное количество фотографий')
@ -439,13 +423,8 @@ class MobileApiController extends Controller
$files[] = $this->uploadFile($photo);
}
$application->photos = $files;
$application->status = ApplicationStatus::review->value;
$application->save();
$notification = [];
$notification['ID заявки'] = $application->id;
$notification['Сообщение'] = __('Получена заявка на закрытие');
$this->sendNotification($notification);
return response()->json([
'success' => true,
'message' => __('Фото получены ожидайте закрытия заявки после проверки оператором'),
@ -477,62 +456,4 @@ class MobileApiController extends Controller
$folder2 = substr($storage_file_name, 2, 3);
return $folder1 . '/' . $folder2 . '/' . $storage_file_name . '.' . $ext;
}
public function getAddress(): JsonResponse
{
$addresses = UniModel::model('pipi_address')->get();
foreach ($addresses as $address) {
$data[$address->id] = $address->name;
}
if (empty($data)) {
$data = (object) [];
}
return response()->json($data);
}
public function getFAQCategory(): JsonResponse
{
$faqs = UniModel::model('pipi_faq')->distinct('category')->get();
$data = [];
foreach ($faqs as $faq) {
$data[] = $faq->category;
}
return response()->json($data);
}
public function getFAQSubCategory(Request $request): JsonResponse
{
$category = $request->query('category');
$query = UniModel::model('pipi_faq');
if (isset($category)) {
$query->where('category', $category);
}
$faqs = $query->distinct('subcategory')->get();
$data = [];
foreach ($faqs as $faq) {
$data[] = $faq->subcategory;
}
return response()->json($data);
}
public function getFAQText(Request $request): JsonResponse
{
$subcategory = $request->query('subcategory');
$query = UniModel::model('pipi_faq')->where('subcategory', $subcategory)->first();
$text = $query?->text;
return response()->json($text ?? '');
}
private function sendNotification(array $data): void
{
foreach ($data as $key => $value) {
$text[$key] = $key . ': ' . $value;
}
Notification::send([$text], new TelegramNotification($text));
}
}

View File

@ -32,26 +32,19 @@ class TelegramNotification extends Notification
public function toTelegram($notifiable)
{
$message = TelegramMessage::create()
->to(config('services.telegram-bot-api.id'))
->content($notifiable['Сообщение']);
$excludedFields = [
'Сообщение',
'id',
'created_at',
'updated_at',
'token'
];
foreach ($notifiable as $field => $value) {
if (in_array($field, $excludedFields) || empty($value)) {
continue;
}
$message->line($value);
}
return $message;
print_r($notifiable);
return TelegramMessage::create()
->to(env('TELEGRAM_CHAT_ID'))
->content($notifiable['car'])
->line('')
->line($notifiable['base_price'])
->line($notifiable['period'])
->line($notifiable['pickup_location'])
->line($notifiable['return_location'])
->line($notifiable['customer_type'])
->line($notifiable['customer_full_name'])
->line($notifiable['customer_email'])
->line($notifiable['customer_phone']);
}
/**

View File

@ -18,8 +18,13 @@ class Seed
Nav::add(new NavItem('pipicar', '/app/main', 1, 'Главная', ["admin","director","coworker","observer","moderator"], 'star'));
Nav::add(new NavItem('pipicar-nav-auto', null, 1, 'Автомобили', ["admin"], 'car-front'));
Nav::add(new NavItem('auto', '/app/pipicar.auto', 1, 'Машины', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('marks', '/app/pipicar.auto_brands', 1, 'Марки', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('classes', '/app/pipicar.auto_classes', 1, 'Классы', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('colors', '/app/pipicar.auto_colors', 1, 'Цвета', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('tariffs', '/app/pipicar.auto_tariffs', 1, 'Тарифы', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('types', '/app/pipicar.auto_types', 1, 'Типы', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('models', '/app/pipicar.brand_models', 1, 'Модели', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('orders', '/app/pipicar.auto_orders', 1, 'Заявки', ["admin"], 'car-front'), 'pipicar-nav-auto');
Nav::add(new NavItem('models', '/app/pipicar.auto_orders', 1, 'Заявки', ["admin"], 'car-front'), 'pipicar-nav-auto');
}
private static function nav(): void
{}

View File

@ -57,7 +57,7 @@ class RouteServiceProvider extends ServiceProvider
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(1000)->by(optional($request->user())->id ?: $request->ip());
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});
}
}

View File

@ -1,90 +0,0 @@
<?php
namespace App\Service;
use A7kz\Platform\Models\UniModel;
use Illuminate\Support\Carbon;
class DepositService
{
public function calcWithoutDeposit($deposit, $day): float {
$percentRates = [
3.00, 3.00, 3.00, 4.05, 5.10, 6.15, 7.20, 8.25, 9.30,
10.35, 11.40, 12.45, 13.00, 13.00, 13.00, 13.00, 13.00, 13.00, 13.00,
13.00, 13.00, 13.00, 13.00, 13.00, 13.00, 13.00, 13.00, 13.00, 13.00, 13.00,
13.44, 13.88, 14.32, 14.76, 15.20
];
$ndsRate = 0.12;
$lastKnownRate = 15.20;
$index = $day - 1;
$percent = $percentRates[$index] ?? $lastKnownRate;
if ($percent === null) {
return 0.0;
}
$sum = $deposit * ($percent / 100) * (1 + $ndsRate);
return round($sum, 2);
}
/**
* @throws \Exception
*/
public function calculateSummary(int $markId, $start, $end): array
{
$fullDays = $start->copy()->diffInDays($end); // 7
$totalHours = $start->copy()->diffInHours($end); // 191 часов
$hoursRemainder = $totalHours - ($fullDays * 24); // остаток: 191 - 7*24 = 191 - 168 = 23
$threshold = 9; // если остаток по часам больше этого порога, то добавим день
$days = $fullDays + ($hoursRemainder > $threshold ? 1 : 0);
$tariffs = UniModel::model('pipi_auto_tariffs')
->where('model_id', $markId)
->get();
if ($tariffs->isEmpty()) {
throw new \Exception(__('Отсутствуют данные по машине'));
}
$basePrice = null;
$discountRate = null;
$deposit = null;
foreach ($tariffs as $range) {
if ($range->day_range_start == 1 && $range->day_range_end == 2) {
$basePrice = $range->base_rate;
}
if (isset($range->deposit)) {
$deposit = $range->deposit;
}
if ($days >= $range->day_range_start && $days <= $range->day_range_end) {
$discountRate = $range->base_rate;
}
}
// Если не нашли скидку, но дней > 30
if (is_null($discountRate) && $days > 30 && $basePrice) {
$discountRate = round($basePrice * 0.6);
}
$baseSum = $basePrice ? $basePrice * $days : null;
$discountedSum = $discountRate ? $discountRate * $days : null;
return [
'days' => $days,
'base_price_per_day' => $basePrice,
'discount_price_per_day' => $discountRate,
'base_sum' => $baseSum,
'discounted_sum' => $discountedSum,
'deposit_base' => $deposit,
'without_deposit' => $this->calcWithoutDeposit($deposit, $days),
];
}
}

View File

@ -12,12 +12,11 @@
"license": "MIT",
"require": {
"php": "^8.1",
"a7kz/platform": "*",
"guzzlehttp/guzzle": "^7.2",
"laravel-notification-channels/telegram": "^5.0",
"laravel/framework": "^10.10",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8"
"laravel/tinker": "^2.8",
"a7kz/platform": "*"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",

672
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -14,11 +14,6 @@ return [
|
*/
'telegram-bot-api' => [
'token' => env('TELEGRAM_BOT_TOKEN'),
'id' => env('TELEGRAM_CHAT_ID'),
],
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),

View File

@ -13,7 +13,7 @@ $approvedApplications = UniModel::model('pipi_applications', Acl::connection())
// Заявки с ожидающим статусом
$pendingApplications = UniModel::model('pipi_applications', Acl::connection())
->where('user_id', auth()->user()->id)
->where('status', ApplicationStatus::approved)
->where('status', ApplicationStatus::reserved)
->get();
?>

View File

@ -1,9 +0,0 @@
{
"admin": [
"default",
"add",
"show",
"edit",
"delete"
]
}

View File

@ -1,104 +0,0 @@
{
"module": "pipicar",
"name": "pipicar.address",
"type": "crud",
"title": "Адреса",
"withHeader": false,
"data": {
"table": "pipi_address",
"pk": "id",
"limit": 25,
"segment": true,
"timestamp": false,
"fields": {
"id": {
"type": "pk"
},
"name": {
"type": "string"
}
}
},
"ui": {
"grid": {
"title": "Адреса",
"component": "App.components.Grid",
"cols": [
{
"name": "name",
"caption": "Адрес"
}
],
"action": {
"head": [
"add"
],
"row": [
"edit",
"delete"
]
},
"filter": {
"template": "app.base.crud.filter",
"rows": [
{
"cols": [
{
"size": 6,
"input": {
"name": "name",
"label": "Название цвета"
}
}
]
}
]
}
},
"forms": {
"add": {
"title": "Адрес",
"template": "app.base.crud.form",
"component": "App.components.Show",
"form": {
"submits": "struct:crud.form.edit.submits",
"rows": [
{
"cols": [
{
"size": 6,
"input": {
"name": "name",
"label": "Название"
}
}
]
}
]
}
},
"edit": {
"title": "Адрес",
"template": "app.base.crud.form",
"component": "App.components.Show",
"form": {
"rows": [
{
"cols": [
{
"size": 6,
"input": {
"name": "name",
"label": "Название"
}
}
]
}
],
"submits": "struct:crud.form.edit.submits"
}
}
}
},
"actions": "struct:crud.actions"
}

View File

@ -1,23 +0,0 @@
<?php
use A7kz\Platform\Modules\Platform\Segment\Facades\Segment;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
$segments = Segment::listActive();
foreach ($segments as $segment) {
Schema::connection($segment->connector)->create('pipi_address', static function (Blueprint $table) {
$table->id();
$table->string('name')->nullable()->comment('Наименование');
$table->timestamps();
$table->softDeletes();
});
}
}
};

View File

@ -1,38 +0,0 @@
<?php
use A7kz\Platform\Models\UniModel;
use A7kz\Platform\Modules\Platform\Core\Facades\Core;
use A7kz\Platform\Modules\Platform\Segment\Facades\Segment;
use Carbon\Carbon;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use \A7kz\Platform\Commands\InstallScript;
return new class extends \A7kz\Platform\Commands\InstallScript {
public function install($module_name, $module_version)
{
}
public function update($module_name, $module_version): void
{
$this->upgrade();
}
private function upgrade(): void
{
$segments = Segment::listActive();
foreach ($segments as $segment) {
if (!Schema::connection($segment->connector)->hasTable('pipi_address')) {
Schema::connection($segment->connector)->create('pipi_address', static function (Blueprint $table) {
$table->id();
$table->string('name')->nullable()->comment('Наименование');
$table->timestamps();
$table->softDeletes();
});
}
}
}
};

View File

@ -7,19 +7,6 @@ enum ApplicationStatus: string
case pending = 'pending';
case approved = 'approved';
case rejected = 'rejected';
case completed = 'completed';
case review = 'review';
function getName(): string
{
return match ($this) {
self::pending => 'В обработке',
self::approved => 'Бронь',
self::rejected => 'Отклонена',
self::completed => 'Завершена',
self::review => 'На рассмотрении',
};
}
case reserved = 'reserved'; //статус брони
}

View File

@ -1,117 +0,0 @@
<?php
namespace App\Modules\applications\Logic;
use A7kz\Platform\Models\UniModel;
use A7kz\Platform\Modules\Platform\Acl\Facades\Acl;
use A7kz\Platform\Modules\Platform\Core\Services\Base\Logic;
use App\Http\Notifications\TelegramNotification;
use App\Models\User;
use App\Modules\applications\Enum\ApplicationStatus;
use App\Modules\auto\Enums\AutoStatusEnums;
use Carbon\CarbonPeriod;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;
class Approve extends Logic
{
public function run()
{
$user = new User();
if (!isset($this->row->user_id)) {
$checkUser = UniModel::model('core_users')->where('email', $this->row->user_email)->first();
if (isset($checkUser)) {
$this->row->user_id = $checkUser->id;
$this->row->save();
} else {
$password = Str::random(8);
$user->forceFill([
'name' => $this->row->user_name . ' ' . $this->row->surname_name,
'username' => $this->row->user_email,
'email' => $this->row->user_email,
'phone' => $this->row->user_phone,
'password' => Hash::make($password),
'active' => true
]);
$user->save();
$this->row->user_id = $user->id;
$this->row->save();
$userRole = UniModel::model('core_roles')
->where('alias', 'user')
->first();
if (!empty($userRole)) {
$userRoleModel = UniModel::model('core_user_roles');
$userRoleModel->forceFill([
'user_id' => $user->id,
'role_id' => $userRole->id
]);
$userRoleModel->save();
}
$company = UniModel::model(config('platform.company.tables.company'))
->firstOrCreate([
'name' => $this->row->user_name . ' ' . $this->row->surname_name,
'biniin' => $this->row->biniin ?? '',
'fullname' => $this->row->user_name . ' ' . $this->row->surname_name,
'segment' => 'sol'
]);
$ucr = UniModel::model(config('platform.company.tables.company_user_role'))
->firstOrCreate([
'user_id' => $user->id,
'role_id' => 7,
'company_id' => $company->id
]);
if (!$ucr) {
UniModel::model(config('platform.company.tables.company_user_role'))
->create([
'user_id' => $user->id,
'role_id' => 6,
'company_id' => $company->id
]);
}
UniModel::model(config('platform.company.tables.company_user'))
->firstOrCreate([
'company_id' => $company->id,
'user_id' => $user->id,
'default' => true,
]);
$text = [
'Сообщение' => __('Пользователь создан'),
'Логин' => 'Логин: ' . $user->email,
'Пароль' => 'Пароль: ' . $password
];
Notification::send([$text], new TelegramNotification($text));
}
}
$this->fillCalendar($this->row);
$this->row->status = ApplicationStatus::approved->value;
$this->row->save();
return $this->response();
}
private function fillCalendar($row): void
{
$period = CarbonPeriod::create($row->started_at, $row->ended_at);
$dates = array_map(fn($date) => $date->toDateString(), iterator_to_array($period));
foreach ($dates as $date) {
UniModel::model('pipi_auto_calendar')->create([
'auto_id' => $row->car_id,
'date' => $date,
'status' => AutoStatusEnums::Rent->name
]);
}
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace App\Modules\applications\Logic;
use A7kz\Platform\Models\UniModel;
use A7kz\Platform\Modules\Platform\Acl\Facades\Acl;
use A7kz\Platform\Modules\Platform\Core\Services\Base\Logic;
use App\Models\User;
use App\Modules\applications\Enum\ApplicationStatus;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
class Cancel extends Logic
{
public function run()
{
if (Carbon::now() > $this->row->ended_at) {
$this->message = 'Вы не можете завершить заявку после даты окончания аренды';
return $this->response();
}
$car = $this->row->car_id;
if (isset($car)) {
UniModel::model('pipi_auto_calendar')
->where('date', '>=', Carbon::now()->toDateString())
->where('date', '<=', $this->row->ended_at)
->where('auto_id', $car)->delete();
}
$this->row->status = ApplicationStatus::rejected->value;
$this->row->ended_at = Carbon::now();
$this->row->save();
return $this->response();
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Modules\applications\Logic;
use A7kz\Platform\Models\UniModel;
use A7kz\Platform\Modules\Platform\Acl\Facades\Acl;
use A7kz\Platform\Modules\Platform\Core\Services\Base\Logic;
use App\Models\User;
use App\Modules\applications\Enum\ApplicationStatus;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
class Complete extends Logic
{
public function run()
{
$this->row->status = ApplicationStatus::completed->value;
$this->row->save();
return $this->response();
}
}

View File

@ -35,6 +35,9 @@
"phone": {
"type": "string"
},
"status": {
"type": "string"
},
"user_name": {
"type": "string"
},
@ -44,12 +47,6 @@
"user_email": {
"type": "string"
},
"sum": {
"type": "int"
},
"deposit": {
"type": "int"
},
"started_at": {
"type": "date",
"validation": "required|date"
@ -61,25 +58,6 @@
"photos": {
"type": "json",
"validation": "nullable"
},
"address_start": {
"type": "foreign",
"table": "pipi_address",
"foreign": "id",
"display": [
"name"
],
"validation": "required|integer"
},
"address_end": {
"type": "foreign",
"table": "pipi_address",
"alias": "address_end",
"foreign": "id",
"display": [
"name"
],
"validation": "required|integer"
}
}
},
@ -92,17 +70,19 @@
{ "name": "car_id", "caption": "Машина" },
{ "name": "user_id", "caption": "Пользователь" },
{ "name": "phone", "caption": "Телефон" },
{ "name": "status", "caption": "Статус" },
{ "name": "user_name", "caption": "Имя" },
{ "name": "user_surname", "caption": "Фамилия" },
{ "name": "user_email", "caption": "Email" },
{ "name": "started_at", "caption": "Начало" },
{ "name": "ended_at", "caption": "Дата окончания" }
{ "name": "ended_at", "caption": "Окончания" }
],
"action": {
"head": [
"add"
],
"row": [
"edit",
"delete"
]
},
@ -139,7 +119,8 @@
},
{
"cols": [
{ "size": 6, "input": { "name": "phone", "label": "Телефон" } }
{ "size": 6, "input": { "name": "phone", "label": "Телефон" } },
{ "size": 6, "input": { "name": "status", "label": "Статус" } }
]
},
{
@ -156,15 +137,7 @@
},
{
"cols": [
{ "size": 3, "input": { "name": "address_start", "label": "Адрес получения" } },
{ "size": 3, "input": { "name": "address_end", "label": "Конечный адрес" } },
{ "size": 3, "input": { "name": "sum", "label": "Сумма" } },
{ "size": 3, "input": { "name": "deposit", "label": "Депозит" } }
]
},
{
"cols": [
{ "size": 6, "input": { "name": "ended_at", "label": "Дата окончания" } },
{ "size": 6, "input": { "name": "ended_at", "label": "Окончания" } },
{ "size": 6, "input": { "name": "photos", "label": "Фотографии", "type": "files"} }
]
}
@ -185,103 +158,30 @@
},
{
"cols": [
{ "size": 4, "input": { "name": "phone", "label": "Телефон" } },
{ "size": 4, "input": { "name": "user_name", "label": "Имя" } },
{ "size": 4, "input": { "name": "user_surname", "label": "Фамилия" } }
{ "size": 6, "input": { "name": "phone", "label": "Телефон" } },
{ "size": 6, "input": { "name": "status", "label": "Статус" } }
]
},
{
"cols": [
{ "size": 4, "input": { "name": "user_email", "label": "Email" } },
{ "size": 4, "input": { "name": "started_at", "label": "Начало" } },
{ "size": 4, "input": { "name": "ended_at", "label": "Дата окончания" } }
{ "size": 6, "input": { "name": "user_name", "label": "Имя" } },
{ "size": 6, "input": { "name": "user_surname", "label": "Фамилия" } }
]
},
{
"cols": [
{ "size": 3, "input": { "name": "address_start", "label": "Адрес получения" } },
{ "size": 3, "input": { "name": "address_end", "label": "Конечный адрес" } },
{ "size": 3, "input": { "name": "sum", "label": "Сумма" } },
{ "size": 3, "input": { "name": "deposit", "label": "Депозит" } }
{ "size": 6, "input": { "name": "user_email", "label": "Email" } },
{ "size": 6, "input": { "name": "started_at", "label": "Начало" } }
]
},
{
"cols": [
{ "size": 6, "input": { "name": "ended_at", "label": "Окончания" } },
{ "size": 6, "input": { "name": "photos", "label": "Фотографии", "type": "files"} }
]
}
],
"submits": [
"struct:crud.form.submit.save",
"struct:crud.form.submit.close"
]
}
},
"show": {
"title": "Заявки",
"template": "app.base.crud.form",
"component": "App.components.Show",
"form": {
"rows": [
{
"cols": [
{ "size": 6, "input": { "name": "car_id", "label": "Машина" } },
{ "size": 6, "input": { "name": "user_id", "label": "Пользователь" } }
]
},
{
"cols": [
{ "size": 4, "input": { "name": "phone", "label": "Телефон" } },
{ "size": 4, "input": { "name": "user_name", "label": "Имя" } },
{ "size": 4, "input": { "name": "user_surname", "label": "Фамилия" } }
]
},
{
"cols": [
{ "size": 4, "input": { "name": "user_email", "label": "Email" } },
{ "size": 4, "input": { "name": "started_at", "label": "Начало" } },
{ "size": 4, "input": { "name": "ended_at", "label": "Дата окончания" } }
]
},
{
"cols": [
{ "size": 3, "input": { "name": "address_start", "label": "Адрес получения" } },
{ "size": 3, "input": { "name": "address_end", "label": "Конечный адрес" } },
{ "size": 3, "input": { "name": "sum", "label": "Сумма" } },
{ "size": 3, "input": { "name": "deposit", "label": "Депозит" } }
]
},
{
"cols": [
{ "size": 6, "input": { "name": "photos", "label": "Фотографии", "type": "files"} }
]
}
],
"submits": [
{
"type": "logic",
"name": "App.Modules.applications.Logic.Approve",
"label": "Подтвердить заявку",
"btn": "btn btn-success",
"condition": "status,==,pending"
},
{
"type": "logic",
"name": "App.Modules.applications.Logic.Cancel",
"label": "Отменить заявку",
"btn": "btn btn-danger",
"condition": "status,==,approved"
},
{
"type": "logic",
"name": "App.Modules.applications.Logic.Complete",
"label": "Завершить заявку",
"btn": "btn btn-success",
"condition": "status,==,review"
},
"struct:crud.form.submit.save",
"struct:crud.form.submit.close"
]
"submits": "struct:crud.form.edit.submits"
}
}
}

View File

@ -48,25 +48,6 @@ return new class extends \A7kz\Platform\Commands\InstallScript {
$table->json('photos')->nullable();
});
}
if (!Schema::connection($segment->connector)->hasColumn('pipi_applications', 'address_start')) {
Schema::connection($segment->connector)->table('pipi_applications', static function (Blueprint $table) {
$table->unsignedBigInteger('address_end')->nullable();
$table->unsignedBigInteger('address_start')->nullable();
});
}
if (!Schema::connection($segment->connector)->hasColumn('pipi_applications', 'sum')) {
Schema::connection($segment->connector)->table('pipi_applications', static function (Blueprint $table) {
$table->integer('sum')->nullable();
});
}
if (!Schema::connection($segment->connector)->hasColumn('pipi_applications', 'deposit')) {
Schema::connection($segment->connector)->table('pipi_applications', static function (Blueprint $table) {
$table->integer('deposit')->nullable();
});
}
}
}
};

View File

@ -153,14 +153,6 @@
"photo_id": {
"type": "int",
"validation": "nullable|int"
},
"regular_user": {
"type": "foreign",
"table": "core_users",
"foreign": "id",
"display": ["username"],
"nullable": true,
"comment": "Постоянный клиент"
}
}
},
@ -309,8 +301,8 @@
{
"size": 4,
"input": {
"name": "regular_user",
"label": "Постоянный клиент"
"name": "owner_id",
"label": "Владелец"
}
},
{
@ -338,24 +330,21 @@
"size": 4,
"input": {
"name": "code",
"label": "Код",
"readonly": true
"label": "Код"
}
},
{
"size": 4,
"input": {
"name": "name",
"label": "Наименование",
"readonly": true
"label": "Наименование"
}
},
{
"size": 4,
"input": {
"name": "state_number",
"label": "Госномер",
"readonly": true
"label": "Госномер"
}
}
]
@ -366,24 +355,21 @@
"size": 4,
"input": {
"name": "brand_id",
"label": "Марка",
"readonly": true
"label": "Марка"
}
},
{
"size": 4,
"input": {
"name": "model_id",
"label": "Модель",
"readonly": true
"label": "Модель"
}
},
{
"size": 4,
"input": {
"name": "color_id",
"label": "Цвет",
"readonly": true
"label": "Цвет"
}
}
]
@ -394,23 +380,21 @@
"size": 4,
"input": {
"name": "manufacture_year",
"label": "Год производства",
"readonly": true
"label": "Год производства"
}
},
{
"size": 4,
"input": {
"name": "estimated_cost",
"label": "Оценочная стоимость",
"readonly": true
"label": "Оценочная стоимость"
}
},
{
"size": 4,
"input": {
"name": "regular_user",
"label": "Постоянный клиент"
"name": "owner_id",
"label": "Владелец"
}
},
{

View File

@ -145,12 +145,6 @@ return new class extends \A7kz\Platform\Commands\InstallScript {
$table->unsignedBigInteger('photo_id')->nullable();
});
}
if (!Schema::connection($segment->connector)->hasColumn('pipi_auto', 'regular_user')) {
Schema::connection($segment->connector)->table('pipi_auto', static function (Blueprint $table) {
$table->unsignedBigInteger('regular_user')->nullable();
});
}
}
}

View File

@ -1,9 +0,0 @@
{
"admin": [
"default",
"add",
"show",
"edit",
"delete"
]
}

View File

@ -1,149 +0,0 @@
{
"module": "pipicar",
"name": "pipicar.faq",
"type": "crud",
"title": "FAQ",
"withHeader": false,
"data": {
"table": "pipi_faq",
"pk": "id",
"limit": 25,
"segment": true,
"timestamp": false,
"fields": {
"id": {
"type": "pk"
},
"text": {
"type": "text"
},
"category": {
"type": "string"
},
"subcategory": {
"type": "string"
}
}
},
"ui": {
"grid": {
"title": "FAQ",
"component": "App.components.Grid",
"cols": [
{
"name": "category",
"caption": "Категория"
},
{
"name": "subcategory",
"caption": "Подкатегория"
}
],
"action": {
"head": [
"add"
],
"row": [
"edit",
"delete"
]
},
"filter": {
"template": "app.base.crud.filter",
"rows": [
{
"cols": [
{
"size": 6,
"input": {
"name": "category",
"label": "Категория"
}
},
{
"size": 6,
"input": {
"name": "subcategory",
"label": "Подкатегория"
}
}
]
}
]
}
},
"forms": {
"add": {
"title": "FAQ",
"template": "app.base.crud.form",
"component": "App.components.Show",
"form": {
"submits": "struct:crud.form.edit.submits",
"rows": [
{
"cols": [
{
"size": 6,
"input": {
"name": "category",
"label": "Категория"
}
},
{
"size": 6,
"input": {
"name": "subcategory",
"label": "Подкатегория"
}
},
{
"size": 12,
"input": {
"name": "text",
"label": "Текст"
}
}
]
}
]
}
},
"edit": {
"title": "Редактирование типа",
"template": "app.base.crud.form",
"component": "App.components.Show",
"form": {
"rows": [
{
"cols": [
{
"size": 6,
"input": {
"name": "category",
"label": "Категория"
}
},
{
"size": 6,
"input": {
"name": "subcategory",
"label": "Подкатегория"
}
},
{
"size": 12,
"input": {
"name": "text",
"label": "Текст"
}
}
]
}
],
"submits": "struct:crud.form.edit.submits"
}
}
}
},
"actions": "struct:crud.actions"
}

View File

@ -1,25 +0,0 @@
<?php
use A7kz\Platform\Modules\Platform\Segment\Facades\Segment;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
$segments = Segment::listActive();
foreach ($segments as $segment) {
Schema::connection($segment->connector)->create('pipi_faq', static function (Blueprint $table) {
$table->id();
$table->longText('text')->nullable()->comment('Текст');
$table->string('category')->nullable()->comment('Категория');
$table->string('subcategory')->nullable()->comment('Подкатегория');
$table->timestamps();
$table->softDeletes();
});
}
}
};

View File

@ -1,40 +0,0 @@
<?php
use A7kz\Platform\Models\UniModel;
use A7kz\Platform\Modules\Platform\Core\Facades\Core;
use A7kz\Platform\Modules\Platform\Segment\Facades\Segment;
use Carbon\Carbon;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;
use \A7kz\Platform\Commands\InstallScript;
return new class extends \A7kz\Platform\Commands\InstallScript {
public function install($module_name, $module_version)
{
}
public function update($module_name, $module_version): void
{
$this->upgrade();
}
private function upgrade(): void
{
$segments = Segment::listActive();
foreach ($segments as $segment) {
if (!Schema::connection($segment->connector)->hasTable('pipi_faq')) {
Schema::connection($segment->connector)->create('pipi_faq', static function (Blueprint $table) {
$table->id();
$table->longText('text')->nullable()->comment('Текст');
$table->string('category')->nullable()->comment('Категория');
$table->string('subcategory')->nullable()->comment('Подкатегория');
$table->timestamps();
$table->softDeletes();
});
}
}
}
};

View File

@ -9,7 +9,6 @@
namespace App\Modules\main\Components;
use A7kz\Platform\Models\UniModel;
use A7kz\Platform\Modules\Platform\Core\Services\Base\Component;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Carbon;
@ -22,11 +21,21 @@ class Main extends Component
parent::__construct($params, $module);
$this->template = 'pipicar::main.views.main';
$request = request();
$class_filters = Request::input('class_filters') ?? null;
$started_at = Request::input('started_at') ?? null;
$ended_at = Request::input('ended_at') ?? null;
$bodywork_filters = Request::input('bodywork_filters') ?? null;
$response = $this->getAuto($request);
$request = new \Illuminate\Http\Request([
'started_at' => $started_at ?? Carbon::now()->toDateString(),
'ended_at' => $ended_at ?? Carbon::now()->addDays(3)->toDateString(),
'class' => $class_filters ?? null,
'bodywork' => $bodywork_filters ?? null,
]);
$this->params['cars'] = collect($response)->filter(function ($item) {
$response = (new \App\Http\Controllers\MobileApiController)->getAvailableMarksList($request);
$this->params['cars'] = collect($response->getData(true))->filter(function ($item) {
return !empty($item['tariffs']);
})->values();
@ -41,142 +50,4 @@ class Main extends Component
// return !empty($item['tariffs']);
// })->values();
}
private function getAuto($request)
{
$started_at = $request->query('started_at');
$ended_at = $request->query('ended_at');
$class = $request->query('class');
$bodywork = $request->query('bodywork');
$marks = UniModel::model('pipi_brand_models')->get();
$availableMarks = [];
if ($class) {
$class_id = UniModel::model('pipi_auto_classes')->where('name', $class)->first()?->id;
$marks = $marks->where('class_id', $class_id);
}
if ($bodywork) {
$bodywork_id = UniModel::model('pipi_auto_bodywork')->where('name', $bodywork)->first()?->id;
$marks = $marks->where('bodywork_id', $bodywork_id);
}
foreach ($marks as $mark) {
if ($mark->name === 'Ввод остатков') {
continue;
}
$tariffs = UniModel::model('pipi_auto_tariffs')->where('model_id', $mark->id)->get();
if ($tariffs->isEmpty()) {
continue;
}
$cars = UniModel::model('pipi_auto')->where('model_id', $mark->id)->get();
if ($cars->isEmpty()) {
continue;
}
$brand = UniModel::model('pipi_auto_brands')->find($mark->brand_id);
$tariffs = UniModel::model('pipi_auto_tariffs')->where('model_id', $mark->id)->get();
$equipment = UniModel::model('pipi_auto_equipment')->where('id', $mark->equipment_id)->first();
$class = UniModel::model('pipi_auto_classes')->find($mark->class_id);
$bodywork = UniModel::model('pipi_auto_bodywork')->find($mark->bodywork_id);
$photo = UniModel::model('core_files')->find($mark?->photo_id);
$path = null;
if ($photo) {
$path = url('api/files/file/' . ltrim($photo->path, '/'));
}
$tariffs_new = [];
foreach ($tariffs as $tariff) {
$tariffs_new[] = [
'name' => $tariff?->name,
'price' => $tariff?->base_rate,
'min' => $tariff?->day_range_start,
'max' => $tariff?->day_range_end,
'deposit' => $tariff?->deposit,
];
}
$carsByColor = $cars->groupBy('color_id');
foreach ($carsByColor as $carColor => $carsOfColor) {
$carColor = UniModel::model('pipi_auto_colors')->find($carColor);
$colorPath = $path;
if ($carsOfColor->first()->photo_id) {
$photo = UniModel::model('core_files')->find($carsOfColor->first()->photo_id);
$colorPath = url('api/files/file/' . ltrim($photo->path, '/'));
}
$key = $mark->name . '-' . $mark->year . '-' . $carColor->code;
$availableMarks[$key] = [
'id' => $mark->id,
'brand' => $brand?->name,
'mark' => $mark->name,
'year' => $mark->year,
'color' => $carColor->code,
'configuration' => $equipment?->name,
'people' => $mark?->people ?? '5',
'actuator' => $mark?->actuator ?? 'Передний',
'fuel_type' => $mark?->fuel_type ?? 'АКПП',
'hp' => $mark?->hp ?? '1.6',
'engine_capacity' => $mark?->engine_capacity ?? '1591',
'fuel_tank' => $mark?->fuel_tank ?? '50',
'class' => $class->name ?? 'Эконом',
'bodywork' => $bodywork->name ?? 'Седан',
'deposit' => $tariffs_new[0]['deposit'] ?? 30000,
'conditioner' => $mark?->conditioner,
'photo' => $colorPath,
'free' => $this->checkAvailableCar($started_at, $ended_at, $mark->id, $carColor),
'tariffs' => $tariffs_new,
];
}
}
if (empty($availableMarks)) {
$availableMarks = (object) [];
}
return $availableMarks;
}
private function checkAvailableCar($started_at = null, $ended_at = null, $modelId = null, $color = null): bool {
if (!$started_at || !$ended_at || !$modelId) {
$request = request();
$started_at = $started_at ?? $request->input('started_at');
$ended_at = $ended_at ?? $request->input('ended_at');
$modelId = $modelId ?? $request->input('model_id');
}
if (is_null($started_at) or is_null($ended_at)) {
$started_at = Carbon::now()->format('Y-m-d');
$ended_at = Carbon::now()->addDays(3)->format('Y-m-d');
}
$started_at = Carbon::parse($started_at)->format('Y-m-d');
$ended_at = Carbon::parse($ended_at)->format('Y-m-d');
$cars = UniModel::model('pipi_auto')
->where('is_inactive', '=', false)
->where('model_id', $modelId)
->where('color_id', $color->id)
->get();
$busyCars = UniModel::model('pipi_auto_calendar')
->whereBetween('date', [$started_at, $ended_at])
->pluck('auto_id')
->toArray();
$availableCars = $cars->reject(fn($car) => in_array($car->id, $busyCars));
return $availableCars->isNotEmpty();
}
public function action_rent()
{
$request = Request::input();
dd($request);
}
}

View File

@ -1,18 +1,16 @@
{
"admin": [
"default",
"add",
"show",
"edit",
"delete",
"rent"
],
"user": [
"default",
"add",
"show",
"edit",
"delete",
"rent"
]
}
"admin": [
"default",
"add",
"show",
"edit",
"delete"
],
"director": [
"default",
"add",
"show",
"edit",
"delete"
]
}

View File

@ -1,10 +1,7 @@
@php $request = \Illuminate\Support\Facades\Request::input();
$address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
@endphp
<div class="container text-center main-page">
<div class="main-page-header">
<p class="top-title">@lang('Доступные автомобили')</p>
<p class="title">@lang('Выберите лучший автомобиль')</p>
<p class="top-title">Доступные автомобили</p>
<p class="title">Выберите лучший автомобиль</p>
</div>
<div class="container my-4">
<div class="row">
@ -118,10 +115,11 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
<div class="row custom-row w-100">
@php $carsValues = array_values($cars->toArray()); @endphp
@foreach($carsValues as $car)
{{-- @dump($car)--}}
<div class="col-12 col-md-6 mb-4">
<div class="car-card flex-grow-1">
<img class="img-fluid" src="{{ $car['photo'] ?? asset('img/default.png') }}" alt="Car Image">
<div class="title">{{ $car['brand'] . ' ' . $car['mark'] . ' - ' . $car['year']. ' ' . \A7kz\Platform\Models\UniModel::model('pipi_auto_colors')->where('code', $car['color'])->first()?->name }}</div>
<div class="title">{{ $car['brand'] . ' ' . $car['mark'] . ' - ' . $car['year'] }}</div>
<div class="card-car-chars">
<div class="add-li"><i class="material-symbols-outlined">ac_unit</i><p>&nbsp;Кондиционер</p></div>
<div class="add-li"><i class="material-symbols-outlined">auto_transmission</i><p>&nbsp;{{ $car['fuel_type'] }}</p></div>
@ -142,20 +140,24 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
@endif
</p>
</div>
@if($car['free'])
<a href="#" class="btn btn-primary open-rent-modal"
data-bs-toggle="modal"
data-bs-target="#rentModal"
data-car='@json($car)'
data-photo="{{ $car['photo'] ?? asset('img/default.png') }}"
>
@lang('Арендовать')
</a>
@else
<span class="badge bg-danger text-wrap" style="width: 150px;">
@lang('Машина занята выберете другую дату')
</span>
@endif
<a href="#" class="btn btn-primary open-rent-modal"
data-bs-toggle="modal"
data-bs-target="#rentModal"
data-car='@json($car)'
data-photo="{{ $car['photo'] ?? asset('img/default.png') }}"
{{-- data-brand="{{ $car['brand'] }}"--}}
{{-- data-mark="{{ $car['mark'] }}"--}}
{{-- data-year="{{ $car['year'] }}"--}}
{{-- data-fuel="{{ $car['fuel_type'] }}"--}}
{{-- data-people="{{ $car['people'] }}"--}}
{{-- data-tariffWithMin1 ="{{ $tariffWithMin1['price'] ?? '' }}"--}}
{{-- data-tariffWithMin3="{{ $tariffWithMin3['price'] ?? '' }}"--}}
{{-- data-tariffWithMin6="{{ $tariffWithMin6['price'] ?? '' }}"--}}
{{-- data-tariffWithMin15="{{ $tariffWithMin15['price'] ?? '' }}"--}}
>
@lang('Арендовать')
</a>
</div>
</div>
</div>
@ -171,8 +173,7 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
<div class="modal-content">
<div class="modal-body">
<div class="row justify-content-center align-items-start">
<form method="POST" class="col-12 col-md-7" action="{{ $app->getPath() . '/rent' }}">
@csrf
<form method="post" class="col-12 col-md-7">
<input hidden="" name="mark_id" id="mark_id" value="">
<input hidden="" name="color" id="color" value="">
<div class="car-card d-flex flex-grow-1 flex-row mb-4">
@ -194,7 +195,7 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
<div class="row justify-content-center align-items-end">
<div class="col-12 col-md-6">
<label for="days" class="form-label">Срок аренды, дней <b>*</b></label>
<input type="number" name="days" class="form-control" id="days" value="3" required="" min="1" step="1">
<input type="number" name="days" class="form-control" id="days" value="7" required="" min="1" step="1">
</div>
<div class="col-12 col-md-6">
<div class="form-text">
@ -223,14 +224,10 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
<div class="card-body">
<div class="mb-3">
<label class="form-label">Место и время получения <b>*</b></label>
<select required class="form-select" name="pick-up-address" id="pick-up-address">
@foreach($address as $addres)
<option value="{{ $addres->id }}">{{ $addres->name }}</option>
@endforeach
</select>
<input placeholder="Введите адрес получения" type="text" class="form-control" name="pick-up-address" id="pick-up-address" required="">
<div class="row mt-3">
<div class="col-6">
<input type="date" class="form-control" id="pick-up-date" name="pick-up-date" required="" min="{{now()->format('Y-m-d')}}">
<input type="date" class="form-control" id="pick-up-date" name="pick-up-date" required="" min="2025-05-02">
</div>
<div class="col-6">
<input type="time" class="form-control" name="pick-up-time" id="pick-up-time" value="09:00" required="">
@ -240,11 +237,7 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
<hr>
<div class="mb-3">
<label class="form-label">Место и время возврата <b>*</b></label>
<select required class="form-select" id="return-address" name="return-address">
@foreach($address as $addres)
<option value="{{ $addres->id }}">{{ $addres->name }}</option>
@endforeach
</select>
<input placeholder="Введите адрес возврата" type="text" class="form-control" name="return-address" id="return-address" required="">
<div class="row mt-3">
<div class="col-6">
<input type="date" class="form-control" id="return-date" name="return-date" disabled="" required="">
@ -257,11 +250,7 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
</div>
</div>
<div class="mb-4 lst-btn">
<button type="submit" class="btn btn-primary btn-lg" id="book-button" style="display: none;">
{{ __('Забронировать сейчас') }}</button>
<div id="not-available-message" class="alert alert-danger" style="display: none;">
{{ __('Машина на эти даты не доступна') }}
</div>
<button type="submit" class="btn btn-primary btn-lg">Забронировать сейчас</button>
<p class="add-text2">Бесплатная отмена в любое время</p>
</div>
</form>
@ -364,34 +353,22 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
const returnDateInput = document.getElementById('return-date');
const daysInput = document.getElementById('days');
const urlParams = new URLSearchParams(window.location.search);
const pickUpDateParam = urlParams.get('started_at');
const returnDateParam = urlParams.get('ended_at');
if (returnDateParam) {
const [day, month, year] = returnDateParam.split('.');
returnDateInput.value = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
}
const today = new Date();
let initialPickUpDate = today.toISOString().split('T')[0];
if (pickUpDateParam) {
const [day, month, year] = pickUpDateParam.split('.');
initialPickUpDate = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
}
const yyyy = today.getFullYear();
const mm = String(today.getMonth() + 1).padStart(2, '0');
const dd = String(today.getDate()).padStart(2, '0');
const todayStr = `${yyyy}-${mm}-${dd}`;
pickUpDateInput.value = todayStr;
pickUpDateInput.setAttribute('min', todayStr);
pickUpDateInput.value = initialPickUpDate;
daysInput.addEventListener('input', () => updateReturnDate());
pickUpDateInput.addEventListener('change', () => updateReturnDate());
daysInput.addEventListener('input', updateReturnDate);
pickUpDateInput.addEventListener('change', updateReturnDate);
document.querySelectorAll('.open-rent-modal').forEach(button => {
button.addEventListener('click', function () {
const car = JSON.parse(this.getAttribute('data-car'));
modal.setAttribute('data-car', JSON.stringify(car));
daysInput.addEventListener('input', () => checkAvailableCar(car));
pickUpDateInput.addEventListener('change', () => checkAvailableCar(car));
document.getElementById('modalCarImage').src = this.getAttribute('data-photo');
document.getElementById('modalCarTitle').innerText = `${car.brand} ${car.mark} - ${car.year}`;
document.getElementById('modalFuel').innerHTML = `&nbsp;${car.fuel_type}`;
@ -406,27 +383,11 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
document.getElementById('tariffWithMin16').innerHTML = `&nbsp;${getTariffPrice(16)} ₸`;
document.getElementById('tariffWithMin30').innerHTML = `&nbsp;${(getTariffPrice(1) * 0.6).toFixed(0)} ₸`;
updateReturnDate(true);
checkAvailableCar(car);
updateReturnDate();
});
});
function updateReturnDate(isInitialLoad = false) {
if (isInitialLoad && pickUpDateParam && returnDateParam) {
const [day1, month1, year1] = pickUpDateParam.split('.');
const [day2, month2, year2] = returnDateParam.split('.');
const pickUpDate = new Date(`${year1}-${month1}-${day1}`);
const returnDate = new Date(`${year2}-${month2}-${day2}`);
const timeDiff = returnDate - pickUpDate;
const rentalDays = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
if (!isNaN(rentalDays) && rentalDays > 0) {
daysInput.value = rentalDays;
}
}
function updateReturnDate() {
const pickUpDate = new Date(pickUpDateInput.value);
const rentalDays = parseInt(daysInput.value, 10);
@ -441,54 +402,6 @@ $address = \A7kz\Platform\Models\UniModel::model('pipi_address')->get();
calculateOther(rentalDays);
}
function checkAvailableCar(car) {
const started_at = document.getElementById('pick-up-date').value;
const ended_at = document.getElementById('return-date').value;
const bookButton = document.getElementById('book-button');
const notAvailableMessage = document.getElementById('not-available-message');
bookButton.style.display = 'none';
notAvailableMessage.style.display = 'none';
if (!started_at || !ended_at) {
console.log('Даты не заполнены');
return;
}
fetch('{{ url('/api/mobile/checkAvailableCar') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
},
body: JSON.stringify({
'started_at': started_at,
'ended_at': ended_at,
'model_id': car.id,
'color': car.color
})
})
.then(async response => {
return response.json();
})
.then(result => {
let isAvailable = result.available;
if (isAvailable) {
bookButton.style.display = 'block';
} else {
notAvailableMessage.style.display = 'block';
notAvailableMessage.textContent = 'Машина на эти даты не доступна';
}
})
.catch(error => {
console.error('Ошибка:', error);
notAvailableMessage.style.display = 'block';
notAvailableMessage.textContent = 'Ошибка при проверке доступности';
});
}
function calculateOther(days) {
if (!days || isNaN(days) || days < 1) {
updatePrices(0, 0, 0, 0);

View File

@ -26,10 +26,6 @@ Route::prefix('1c')->group(function () {
Route::prefix('mobile')->group(function () {
Route::get('getMarks', [MobileApiController::class, 'getMarks']);
Route::get('getFAQCategory', [MobileApiController::class, 'getFAQCategory']);
Route::get('getFAQSubCategory', [MobileApiController::class, 'getFAQSubCategory']);
Route::get('getFAQText', [MobileApiController::class, 'getFAQText']);
Route::get('getAddress', [MobileApiController::class, 'getAddress']);
Route::get('getAvailableMarksList', [MobileApiController::class, 'getAvailableMarksList']);
Route::post('checkAvailableCar', [MobileApiController::class, 'checkAvailableCar']);
Route::post('sendApplication', [MobileApiController::class, 'sendApplication']);