Compare commits
62 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
05d2a3ba8f | |
|
|
bc4c00d967 | |
|
|
142c65cedc | |
|
|
6ad0fe85e6 | |
|
|
ae3bd41178 | |
|
|
dc4bc7f88b | |
|
|
f66ef5c75c | |
|
|
2d5c650310 | |
|
|
92f2ca3229 | |
|
|
db933f8aca | |
|
|
56921dd6d4 | |
|
|
9bf02e2940 | |
|
|
772af756b5 | |
|
|
e0f426ce54 | |
|
|
6c5b7f56df | |
|
|
8736f248dc | |
|
|
430e910afb | |
|
|
5ca3b2655f | |
|
|
52d41a1346 | |
|
|
d9057193b8 | |
|
|
3eea63b147 | |
|
|
c18348a43d | |
|
|
7f72804ce0 | |
|
|
ef9750f729 | |
|
|
ec737e2a49 | |
|
|
737605d066 | |
|
|
4eb7421506 | |
|
|
de78b985ea | |
|
|
928d6e9ad0 | |
|
|
04594bf24c | |
|
|
6a09e53e0b | |
|
|
54d5abfa76 | |
|
|
dfb547b3de | |
|
|
2ba4146f5b | |
|
|
ea036c8913 | |
|
|
83d62045bd | |
|
|
fab3679f53 | |
|
|
23b2fdb22c | |
|
|
75e4922ec5 | |
|
|
e2454b8112 | |
|
|
0feee086f8 | |
|
|
b8054bd149 | |
|
|
4fd2ecc85c | |
|
|
fff0d79335 | |
|
|
54b4271312 | |
|
|
97b31e4a3b | |
|
|
56be064c19 | |
|
|
ec1a2cc27c | |
|
|
8e318a041b | |
|
|
2d5e5a609f | |
|
|
0b87f9f290 | |
|
|
c18dc4f682 | |
|
|
b0ea436307 | |
|
|
a46dc1ec01 | |
|
|
dce5c1e30f | |
|
|
5b33da4b8f | |
|
|
816de5f5ac | |
|
|
bdc40dfbdc | |
|
|
7e56f7da90 | |
|
|
80aae069d0 | |
|
|
f32b389fa3 | |
|
|
faa33860ae |
|
|
@ -24,6 +24,7 @@ class PipiCarInstallCommands extends InstallCommand
|
|||
|
||||
$this->packages = [
|
||||
'main', // Главная страница
|
||||
'address', // Главная страница
|
||||
'auto_brands', // Бренд авто
|
||||
'auto_bodywork', // Кузов авто
|
||||
'auto_colors', // Цвета авто
|
||||
|
|
@ -37,7 +38,8 @@ class PipiCarInstallCommands extends InstallCommand
|
|||
'auto', // авто
|
||||
'applications', //Заявки
|
||||
'pipi_users', // Добавление логики для пользователей
|
||||
'auto_calendar' // Добавление логики для пользователей
|
||||
'auto_calendar', // Добавление логики для пользователей
|
||||
'faq' // FAQ
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ 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;
|
||||
|
|
@ -58,10 +61,14 @@ class MobileApiController extends Controller
|
|||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function sendApplication(Request $request): JsonResponse {
|
||||
try {
|
||||
$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');
|
||||
$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')) {
|
||||
|
|
@ -69,18 +76,15 @@ class MobileApiController extends Controller
|
|||
$data['user_id'] = $user?->id;
|
||||
$authToken = $request->header('Authorization');
|
||||
} else {
|
||||
$user = UniModel::model('core_users')
|
||||
->where('email', $data['email'])
|
||||
->first();
|
||||
$user = User::where('email', $data['email'])->first();
|
||||
|
||||
if ($user) {
|
||||
$data['user_id'] = $user->id;
|
||||
$tokenResult = $user->createToken('auth_token');
|
||||
$authToken = $tokenResult->accessToken;
|
||||
$authToken = $tokenResult->plainTextToken;
|
||||
} else {
|
||||
$data['user_id'] = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$response = [
|
||||
|
|
@ -89,6 +93,7 @@ class MobileApiController extends Controller
|
|||
];
|
||||
|
||||
$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('Нет свободных машин');
|
||||
}
|
||||
|
|
@ -105,26 +110,52 @@ class MobileApiController extends Controller
|
|||
'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' => $data['phone'],
|
||||
'phone' => $user?->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
|
||||
'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());
|
||||
}
|
||||
}
|
||||
|
||||
public function getApplications(): JsonResponse
|
||||
|
|
@ -139,20 +170,33 @@ class MobileApiController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
$applications = [];
|
||||
$application = UniModel::model('pipi_applications')->where('user_id', $user->id)->get();
|
||||
foreach ($application as $app) {
|
||||
$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) {
|
||||
$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();
|
||||
$applications[] = [
|
||||
$address = UniModel::model('pipi_address')->where('id', $app->address_end)->first();
|
||||
$data[] = [
|
||||
'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,
|
||||
|
|
@ -168,10 +212,10 @@ class MobileApiController extends Controller
|
|||
]
|
||||
];
|
||||
}
|
||||
if (empty($applications)) {
|
||||
$applications = (object) $applications;
|
||||
if (empty($data)) {
|
||||
$data = (object) $data;
|
||||
}
|
||||
return response()->json($applications);
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
public function login(Request $request)
|
||||
|
|
@ -198,16 +242,18 @@ class MobileApiController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
public function checkAvailableCar($started_at = null, $ended_at = null, $modelId = null): bool {
|
||||
if (!$started_at || !$ended_at || !$modelId) {
|
||||
public function checkAvailableCar($started_at = null, $ended_at = null, $modelId = null, $color = null): JsonResponse
|
||||
{
|
||||
if (!$started_at || !$ended_at || !$modelId || !$color) {
|
||||
$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) {
|
||||
return false;
|
||||
if (!$started_at || !$ended_at || !$modelId || !$color) {
|
||||
return response()->json(['available' => false]);
|
||||
}
|
||||
|
||||
$started_at = Carbon::parse($started_at)->format('Y-m-d');
|
||||
|
|
@ -215,6 +261,7 @@ 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])
|
||||
|
|
@ -223,11 +270,9 @@ class MobileApiController extends Controller
|
|||
|
||||
$availableCars = $cars->reject(fn($car) => in_array($car->id, $busyCars));
|
||||
|
||||
return $availableCars->isNotEmpty();
|
||||
return response()->json(['available' => $availableCars->isNotEmpty()]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function getAvailableMarksList(Request $request): JsonResponse
|
||||
{
|
||||
$started_at = $request->query('started_at');
|
||||
|
|
@ -240,11 +285,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);
|
||||
}
|
||||
|
||||
|
|
@ -253,13 +298,13 @@ class MobileApiController extends Controller
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!$this->checkAvailableCar($started_at, $ended_at, $mark->id)) {
|
||||
$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;
|
||||
}
|
||||
|
|
@ -289,7 +334,7 @@ class MobileApiController extends Controller
|
|||
$carsByColor = $cars->groupBy('color_id');
|
||||
|
||||
foreach ($carsByColor as $carColor => $carsOfColor) {
|
||||
$carColor = UniModel::model('pipi_auto_colors')->find($carColor)->code;
|
||||
$carColor = UniModel::model('pipi_auto_colors')->find($carColor);
|
||||
|
||||
$colorPath = $path;
|
||||
if ($carsOfColor->first()->photo_id) {
|
||||
|
|
@ -297,14 +342,14 @@ class MobileApiController extends Controller
|
|||
$colorPath = url('api/files/file/' . ltrim($photo->path, '/'));
|
||||
}
|
||||
|
||||
$key = $mark->name . '-' . $mark->year . '-' . $carColor;
|
||||
$key = $mark->name . '-' . $mark->year . '-' . $carColor->code;
|
||||
|
||||
$availableMarks[$key] = [
|
||||
'id' => $mark->id,
|
||||
'brand' => $brand?->name,
|
||||
'mark' => $mark->name,
|
||||
'year' => $mark->year,
|
||||
'color' => $carColor,
|
||||
'color' => $carColor->code,
|
||||
'configuration' => $equipment?->name,
|
||||
'people' => $mark?->people ?? '5',
|
||||
'actuator' => $mark?->actuator ?? 'Передний',
|
||||
|
|
@ -317,6 +362,7 @@ 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,
|
||||
];
|
||||
}
|
||||
|
|
@ -336,6 +382,7 @@ 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])
|
||||
|
|
@ -359,45 +406,14 @@ class MobileApiController extends Controller
|
|||
|
||||
$start = Carbon::createFromFormat('d-m-Y H:i', $started_at);
|
||||
$end = Carbon::createFromFormat('d-m-Y H:i', $ended_at);
|
||||
$days = $start->diffInDays($end);
|
||||
try {
|
||||
$service = new DepositService();
|
||||
$summary = $service->calculateSummary($mark_id, $start, $end);
|
||||
|
||||
// Получаем тарифы
|
||||
$tariffs = UniModel::model('pipi_auto_tariffs')
|
||||
->where('model_id', $mark_id)
|
||||
->get();
|
||||
if (!isset($tariffs)) {
|
||||
return response()->json(__('Отсутсвуют данные по машине'));
|
||||
return response()->json($summary);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json(['error' => $e->getMessage()], 400);
|
||||
}
|
||||
|
||||
$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
|
||||
|
|
@ -412,7 +428,7 @@ class MobileApiController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
if (count($request->file('photos')) != 5) {
|
||||
if (count($request->file('photos')) < 5) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => __('Неверное количество фотографий')
|
||||
|
|
@ -423,8 +439,13 @@ 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' => __('Фото получены ожидайте закрытия заявки после проверки оператором'),
|
||||
|
|
@ -456,4 +477,62 @@ 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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,19 +32,26 @@ class TelegramNotification extends Notification
|
|||
|
||||
public function toTelegram($notifiable)
|
||||
{
|
||||
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']);
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,13 +18,8 @@ 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('models', '/app/pipicar.auto_orders', 1, 'Заявки', ["admin"], 'car-front'), 'pipicar-nav-auto');
|
||||
Nav::add(new NavItem('orders', '/app/pipicar.auto_orders', 1, 'Заявки', ["admin"], 'car-front'), 'pipicar-nav-auto');
|
||||
}
|
||||
private static function nav(): void
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
|
||||
return Limit::perMinute(1000)->by(optional($request->user())->id ?: $request->ip());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
<?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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -12,11 +12,12 @@
|
|||
"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",
|
||||
"a7kz/platform": "*"
|
||||
"laravel/tinker": "^2.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,6 +14,11 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'telegram-bot-api' => [
|
||||
'token' => env('TELEGRAM_BOT_TOKEN'),
|
||||
'id' => env('TELEGRAM_CHAT_ID'),
|
||||
],
|
||||
|
||||
'mailgun' => [
|
||||
'domain' => env('MAILGUN_DOMAIN'),
|
||||
'secret' => env('MAILGUN_SECRET'),
|
||||
|
|
|
|||
|
|
@ -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::reserved)
|
||||
->where('status', ApplicationStatus::approved)
|
||||
->get();
|
||||
?>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"admin": [
|
||||
"default",
|
||||
"add",
|
||||
"show",
|
||||
"edit",
|
||||
"delete"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -7,6 +7,19 @@ enum ApplicationStatus: string
|
|||
case pending = 'pending';
|
||||
case approved = 'approved';
|
||||
case rejected = 'rejected';
|
||||
case reserved = 'reserved'; //статус брони
|
||||
case completed = 'completed';
|
||||
case review = 'review';
|
||||
|
||||
|
||||
function getName(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::pending => 'В обработке',
|
||||
self::approved => 'Бронь',
|
||||
self::rejected => 'Отклонена',
|
||||
self::completed => 'Завершена',
|
||||
self::review => 'На рассмотрении',
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
<?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
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
||||
|
|
@ -35,9 +35,6 @@
|
|||
"phone": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_name": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
@ -47,6 +44,12 @@
|
|||
"user_email": {
|
||||
"type": "string"
|
||||
},
|
||||
"sum": {
|
||||
"type": "int"
|
||||
},
|
||||
"deposit": {
|
||||
"type": "int"
|
||||
},
|
||||
"started_at": {
|
||||
"type": "date",
|
||||
"validation": "required|date"
|
||||
|
|
@ -58,6 +61,25 @@
|
|||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -70,19 +92,17 @@
|
|||
{ "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"
|
||||
]
|
||||
},
|
||||
|
|
@ -119,8 +139,7 @@
|
|||
},
|
||||
{
|
||||
"cols": [
|
||||
{ "size": 6, "input": { "name": "phone", "label": "Телефон" } },
|
||||
{ "size": 6, "input": { "name": "status", "label": "Статус" } }
|
||||
{ "size": 6, "input": { "name": "phone", "label": "Телефон" } }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -137,7 +156,15 @@
|
|||
},
|
||||
{
|
||||
"cols": [
|
||||
{ "size": 6, "input": { "name": "ended_at", "label": "Окончания" } },
|
||||
{ "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": "photos", "label": "Фотографии", "type": "files"} }
|
||||
]
|
||||
}
|
||||
|
|
@ -158,30 +185,103 @@
|
|||
},
|
||||
{
|
||||
"cols": [
|
||||
{ "size": 6, "input": { "name": "phone", "label": "Телефон" } },
|
||||
{ "size": 6, "input": { "name": "status", "label": "Статус" } }
|
||||
{ "size": 4, "input": { "name": "phone", "label": "Телефон" } },
|
||||
{ "size": 4, "input": { "name": "user_name", "label": "Имя" } },
|
||||
{ "size": 4, "input": { "name": "user_surname", "label": "Фамилия" } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"cols": [
|
||||
{ "size": 6, "input": { "name": "user_name", "label": "Имя" } },
|
||||
{ "size": 6, "input": { "name": "user_surname", "label": "Фамилия" } }
|
||||
{ "size": 4, "input": { "name": "user_email", "label": "Email" } },
|
||||
{ "size": 4, "input": { "name": "started_at", "label": "Начало" } },
|
||||
{ "size": 4, "input": { "name": "ended_at", "label": "Дата окончания" } }
|
||||
]
|
||||
},
|
||||
{
|
||||
"cols": [
|
||||
{ "size": 6, "input": { "name": "user_email", "label": "Email" } },
|
||||
{ "size": 6, "input": { "name": "started_at", "label": "Начало" } }
|
||||
{ "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": "photos", "label": "Фотографии", "type": "files"} }
|
||||
]
|
||||
}
|
||||
],
|
||||
"submits": "struct:crud.form.edit.submits"
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,25 @@ 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -153,6 +153,14 @@
|
|||
"photo_id": {
|
||||
"type": "int",
|
||||
"validation": "nullable|int"
|
||||
},
|
||||
"regular_user": {
|
||||
"type": "foreign",
|
||||
"table": "core_users",
|
||||
"foreign": "id",
|
||||
"display": ["username"],
|
||||
"nullable": true,
|
||||
"comment": "Постоянный клиент"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -301,8 +309,8 @@
|
|||
{
|
||||
"size": 4,
|
||||
"input": {
|
||||
"name": "owner_id",
|
||||
"label": "Владелец"
|
||||
"name": "regular_user",
|
||||
"label": "Постоянный клиент"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -330,21 +338,24 @@
|
|||
"size": 4,
|
||||
"input": {
|
||||
"name": "code",
|
||||
"label": "Код"
|
||||
"label": "Код",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"size": 4,
|
||||
"input": {
|
||||
"name": "name",
|
||||
"label": "Наименование"
|
||||
"label": "Наименование",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"size": 4,
|
||||
"input": {
|
||||
"name": "state_number",
|
||||
"label": "Госномер"
|
||||
"label": "Госномер",
|
||||
"readonly": true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -355,21 +366,24 @@
|
|||
"size": 4,
|
||||
"input": {
|
||||
"name": "brand_id",
|
||||
"label": "Марка"
|
||||
"label": "Марка",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"size": 4,
|
||||
"input": {
|
||||
"name": "model_id",
|
||||
"label": "Модель"
|
||||
"label": "Модель",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"size": 4,
|
||||
"input": {
|
||||
"name": "color_id",
|
||||
"label": "Цвет"
|
||||
"label": "Цвет",
|
||||
"readonly": true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -380,21 +394,23 @@
|
|||
"size": 4,
|
||||
"input": {
|
||||
"name": "manufacture_year",
|
||||
"label": "Год производства"
|
||||
"label": "Год производства",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"size": 4,
|
||||
"input": {
|
||||
"name": "estimated_cost",
|
||||
"label": "Оценочная стоимость"
|
||||
"label": "Оценочная стоимость",
|
||||
"readonly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"size": 4,
|
||||
"input": {
|
||||
"name": "owner_id",
|
||||
"label": "Владелец"
|
||||
"name": "regular_user",
|
||||
"label": "Постоянный клиент"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -145,6 +145,12 @@ 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"admin": [
|
||||
"default",
|
||||
"add",
|
||||
"show",
|
||||
"edit",
|
||||
"delete"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
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;
|
||||
|
|
@ -21,21 +22,11 @@ class Main extends Component
|
|||
parent::__construct($params, $module);
|
||||
$this->template = 'pipicar::main.views.main';
|
||||
|
||||
$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;
|
||||
$request = 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,
|
||||
]);
|
||||
$response = $this->getAuto($request);
|
||||
|
||||
$response = (new \App\Http\Controllers\MobileApiController)->getAvailableMarksList($request);
|
||||
|
||||
$this->params['cars'] = collect($response->getData(true))->filter(function ($item) {
|
||||
$this->params['cars'] = collect($response)->filter(function ($item) {
|
||||
return !empty($item['tariffs']);
|
||||
})->values();
|
||||
|
||||
|
|
@ -50,4 +41,142 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,15 @@
|
|||
"add",
|
||||
"show",
|
||||
"edit",
|
||||
"delete"
|
||||
"delete",
|
||||
"rent"
|
||||
],
|
||||
"director": [
|
||||
"user": [
|
||||
"default",
|
||||
"add",
|
||||
"show",
|
||||
"edit",
|
||||
"delete"
|
||||
"delete",
|
||||
"rent"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
@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">Доступные автомобили</p>
|
||||
<p class="title">Выберите лучший автомобиль</p>
|
||||
<p class="top-title">@lang('Доступные автомобили')</p>
|
||||
<p class="title">@lang('Выберите лучший автомобиль')</p>
|
||||
</div>
|
||||
<div class="container my-4">
|
||||
<div class="row">
|
||||
|
|
@ -115,11 +118,10 @@
|
|||
<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'] }}</div>
|
||||
<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="card-car-chars">
|
||||
<div class="add-li"><i class="material-symbols-outlined">ac_unit</i><p> Кондиционер</p></div>
|
||||
<div class="add-li"><i class="material-symbols-outlined">auto_transmission</i><p> {{ $car['fuel_type'] }}</p></div>
|
||||
|
|
@ -140,24 +142,20 @@
|
|||
@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') }}"
|
||||
{{-- 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>
|
||||
|
||||
@else
|
||||
<span class="badge bg-danger text-wrap" style="width: 150px;">
|
||||
@lang('Машина занята выберете другую дату')
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -173,7 +171,8 @@
|
|||
<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">
|
||||
<form method="POST" class="col-12 col-md-7" action="{{ $app->getPath() . '/rent' }}">
|
||||
@csrf
|
||||
<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">
|
||||
|
|
@ -195,7 +194,7 @@
|
|||
<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="7" required="" min="1" step="1">
|
||||
<input type="number" name="days" class="form-control" id="days" value="3" required="" min="1" step="1">
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-text">
|
||||
|
|
@ -224,10 +223,14 @@
|
|||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Место и время получения <b>*</b></label>
|
||||
<input placeholder="Введите адрес получения" type="text" class="form-control" name="pick-up-address" id="pick-up-address" required="">
|
||||
<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>
|
||||
<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="2025-05-02">
|
||||
<input type="date" class="form-control" id="pick-up-date" name="pick-up-date" required="" min="{{now()->format('Y-m-d')}}">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<input type="time" class="form-control" name="pick-up-time" id="pick-up-time" value="09:00" required="">
|
||||
|
|
@ -237,7 +240,11 @@
|
|||
<hr>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Место и время возврата <b>*</b></label>
|
||||
<input placeholder="Введите адрес возврата" type="text" class="form-control" name="return-address" id="return-address" required="">
|
||||
<select required class="form-select" id="return-address" name="return-address">
|
||||
@foreach($address as $addres)
|
||||
<option value="{{ $addres->id }}">{{ $addres->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="row mt-3">
|
||||
<div class="col-6">
|
||||
<input type="date" class="form-control" id="return-date" name="return-date" disabled="" required="">
|
||||
|
|
@ -250,7 +257,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="mb-4 lst-btn">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Забронировать сейчас</button>
|
||||
<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>
|
||||
<p class="add-text2">Бесплатная отмена в любое время</p>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -353,22 +364,34 @@
|
|||
const returnDateInput = document.getElementById('return-date');
|
||||
const daysInput = document.getElementById('days');
|
||||
|
||||
const today = new Date();
|
||||
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);
|
||||
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')}`;
|
||||
}
|
||||
|
||||
daysInput.addEventListener('input', updateReturnDate);
|
||||
pickUpDateInput.addEventListener('change', updateReturnDate);
|
||||
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')}`;
|
||||
}
|
||||
|
||||
pickUpDateInput.value = initialPickUpDate;
|
||||
|
||||
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 = ` ${car.fuel_type}`;
|
||||
|
|
@ -383,11 +406,27 @@
|
|||
document.getElementById('tariffWithMin16').innerHTML = ` ${getTariffPrice(16)} ₸`;
|
||||
document.getElementById('tariffWithMin30').innerHTML = ` ${(getTariffPrice(1) * 0.6).toFixed(0)} ₸`;
|
||||
|
||||
updateReturnDate();
|
||||
updateReturnDate(true);
|
||||
checkAvailableCar(car);
|
||||
});
|
||||
});
|
||||
|
||||
function 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;
|
||||
}
|
||||
}
|
||||
|
||||
const pickUpDate = new Date(pickUpDateInput.value);
|
||||
const rentalDays = parseInt(daysInput.value, 10);
|
||||
|
||||
|
|
@ -402,6 +441,54 @@
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ 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']);
|
||||
|
|
|
|||
Loading…
Reference in New Issue