Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions app/Http/Controllers/Api/V1/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,77 @@ public function botSetting(Request $request, string $key): JsonResponse
],
]);
}

/**
* Get shift configuration
*
* GET /api/v1/settings/shift-config
*/
public function getShiftConfig(Request $request): JsonResponse
{
$dealershipId = $request->query('dealership_id') ? (int) $request->query('dealership_id') : null;

$shiftConfig = [
'shift_1_start_time' => $this->settingsService->getShiftStartTime($dealershipId, 1),
Copy link

@cubic-dev-ai cubic-dev-ai bot Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a dealership lacks its own shift setting, this line falls back to the hard-coded defaults (09:00/18:00/02:00) instead of honoring global overrides because SettingsService::getShiftStartTime only reads the dealership row and returns the default when nothing is found. Please fetch with the dealership→global fallback before defaulting so shared global values are respected.

Prompt for AI agents
Address the following comment on app/Http/Controllers/Api/V1/SettingsController.php at line 271:

<comment>When a dealership lacks its own shift setting, this line falls back to the hard-coded defaults (09:00/18:00/02:00) instead of honoring global overrides because SettingsService::getShiftStartTime only reads the dealership row and returns the default when nothing is found. Please fetch with the dealership→global fallback before defaulting so shared global values are respected.</comment>

<file context>
@@ -257,4 +257,77 @@ public function botSetting(Request $request, string $key): JsonResponse
+        $dealershipId = $request-&gt;query(&#39;dealership_id&#39;) ? (int) $request-&gt;query(&#39;dealership_id&#39;) : null;
+
+        $shiftConfig = [
+            &#39;shift_1_start_time&#39; =&gt; $this-&gt;settingsService-&gt;getShiftStartTime($dealershipId, 1),
+            &#39;shift_1_end_time&#39; =&gt; $this-&gt;settingsService-&gt;getShiftEndTime($dealershipId, 1),
+            &#39;shift_2_start_time&#39; =&gt; $this-&gt;settingsService-&gt;getShiftStartTime($dealershipId, 2),
</file context>
Suggested change
'shift_1_start_time' => $this->settingsService->getShiftStartTime($dealershipId, 1),
'shift_1_start_time' => $this->settingsService->getSettingWithFallback('shift_1_start_time', $dealershipId) ?? $this->settingsService->getShiftStartTime(null, 1),
Fix with Cubic

'shift_1_end_time' => $this->settingsService->getShiftEndTime($dealershipId, 1),
'shift_2_start_time' => $this->settingsService->getShiftStartTime($dealershipId, 2),
'shift_2_end_time' => $this->settingsService->getShiftEndTime($dealershipId, 2),
'late_tolerance_minutes' => $this->settingsService->getLateTolerance($dealershipId),
];

return response()->json([
'success' => true,
'data' => $shiftConfig,
]);
}

/**
* Update shift configuration
*
* POST /api/v1/settings/shift-config
*/
public function updateShiftConfig(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'shift_1_start_time' => ['nullable', 'string', 'regex:/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/'],
'shift_1_end_time' => ['nullable', 'string', 'regex:/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/'],
'shift_2_start_time' => ['nullable', 'string', 'regex:/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/'],
'shift_2_end_time' => ['nullable', 'string', 'regex:/^([0-1][0-9]|2[0-3]):[0-5][0-9]$/'],
'late_tolerance_minutes' => ['nullable', 'integer', 'min:0', 'max:120'],
'dealership_id' => ['nullable', 'integer'],
]);

if ($validator->fails()) {
return response()->json([
'success' => false,
'errors' => $validator->errors(),
], 422);
}

try {
$data = $validator->validated();
$dealershipId = $data['dealership_id'] ?? null;
unset($data['dealership_id']);

$updatedSettings = [];
foreach ($data as $key => $value) {
if ($value !== null) {
$type = $key === 'late_tolerance_minutes' ? 'integer' : 'time';
$this->settingsService->set($key, $value, $dealershipId, $type);
$updatedSettings[$key] = $value;
}
}

return response()->json([
'success' => true,
'message' => 'Shift configuration updated successfully',
'data' => $updatedSettings,
]);
} catch (\InvalidArgumentException $e) {
return response()->json([
'success' => false,
'message' => $e->getMessage(),
], 400);
}
}
}
25 changes: 1 addition & 24 deletions docs/FRONTEND_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1531,19 +1531,12 @@ export interface Setting {
updated_at: string;
}

export interface BotConfig {
telegram_bot_id: string | null;
telegram_bot_username: string | null;
telegram_webhook_url: string | null;
notification_enabled: boolean;
}

export interface ShiftConfig {
shift_1_start_time: string;
shift_1_end_time: string;
shift_2_start_time: string;
shift_2_end_time: string;
allowed_late_minutes: number;
late_tolerance_minutes: number;
}

export const settingsApi = {
Expand All @@ -1563,22 +1556,6 @@ export const settingsApi = {
return response.data;
},

// Получить настройки бота
getBotConfig: async (dealership_id?: number): Promise<BotConfig> => {
const response = await apiClient.get<BotConfig>('/settings/bot-config', {
params: { dealership_id },
});
return response.data;
},

// Обновить настройки бота (только Manager/Owner)
updateBotConfig: async (data: Partial<BotConfig>, dealership_id?: number): Promise<void> => {
await apiClient.post('/settings/bot-config', {
...data,
dealership_id,
});
},

// Получить настройки смен
getShiftConfig: async (dealership_id?: number): Promise<ShiftConfig> => {
const response = await apiClient.get<ShiftConfig>('/settings/shift-config', {
Expand Down
2 changes: 0 additions & 2 deletions docs/ROLE_HIERARCHY.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,8 @@ public function updateSensitiveData(Request $request)
### Настройки (Settings)
- `GET /api/v1/settings` - Все авторизованные пользователи
- `GET /api/v1/settings/shift-config` - Все авторизованные пользователи
- `GET /api/v1/settings/bot-config` - Все авторизованные пользователи
- `GET /api/v1/settings/{key}` - Все авторизованные пользователи
- `POST /api/v1/settings/shift-config` - **Только Manager, Owner**
- `POST /api/v1/settings/bot-config` - **Только Manager, Owner**
- `POST /api/v1/settings` - **Только Manager, Owner**
- `PUT /api/v1/settings/{id}` - **Только Manager, Owner**
- `DELETE /api/v1/settings/{id}` - **Только Manager, Owner**
Expand Down
3 changes: 3 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@

// Settings
Route::get('/settings', [SettingsController::class, 'index']);
Route::get('/settings/shift-config', [SettingsController::class, 'getShiftConfig']);
Route::post('/settings/shift-config', [SettingsController::class, 'updateShiftConfig'])
->middleware('role:manager,owner');
Route::get('/settings/{key}', [SettingsController::class, 'show']);
Route::put('/settings/{key}', [SettingsController::class, 'update'])
->middleware('role:manager,owner');
Expand Down
174 changes: 0 additions & 174 deletions swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3495,180 +3495,6 @@ paths:
errors:
type: object

/settings/bot-config:
get:
tags:
- Settings
summary: Получить конфигурацию бота
description: Получение настроек Telegram бота и смен
operationId: getBotConfig
security:
- bearerAuth: []
parameters:
- name: dealership_id
in: query
description: ID автосалона (если не указан, возвращается глобальная конфигурация)
schema:
type: integer
example: 1
responses:
'200':
description: Конфигурация бота
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
data:
type: object
properties:
telegram_bot_id:
type: string
nullable: true
example: "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
telegram_bot_username:
type: string
nullable: true
example: "taskmate_bot"
telegram_webhook_url:
type: string
format: uri
nullable: true
example: "https://example.com/api/webhook"
notification_enabled:
type: boolean
example: true
shift_1_start_time:
type: string
format: time
example: "09:00"
description: Время начала первой смены
shift_1_end_time:
type: string
format: time
example: "18:00"
description: Время окончания первой смены
shift_2_start_time:
type: string
format: time
example: "18:00"
description: Время начала второй смены
shift_2_end_time:
type: string
format: time
example: "02:00"
description: Время окончания второй смены
late_tolerance_minutes:
type: integer
example: 15
description: Допустимое время опоздания в минутах
'401':
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'

post:
tags:
- Settings
summary: Обновить конфигурацию бота
description: Обновление настроек Telegram бота и смен
operationId: updateBotConfig
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
telegram_bot_id:
type: string
nullable: true
description: Telegram Bot ID
example: "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
telegram_bot_username:
type: string
nullable: true
description: Telegram Bot Username
example: "taskmate_bot"
telegram_webhook_url:
type: string
format: uri
nullable: true
description: Telegram Webhook URL
example: "https://example.com/api/webhook"
notification_enabled:
type: boolean
nullable: true
description: Включить/выключить уведомления
example: true
shift_start_time:
type: string
format: time
nullable: true
description: Время начала смены (формат HH:MM)
example: "09:00"
shift_end_time:
type: string
format: time
nullable: true
description: Время окончания смены (формат HH:MM)
example: "18:00"
late_tolerance_minutes:
type: integer
nullable: true
description: Допустимое время опоздания в минутах
example: 15
dealership_id:
type: integer
nullable: true
example: 1
responses:
'200':
description: Конфигурация бота успешно обновлена
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: Bot configuration updated successfully
data:
type: object
additionalProperties:
oneOf:
- type: string
- type: boolean
- type: integer
'401':
description: Неавторизован
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: Ошибка валидации
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
errors:
type: object

/webhook:
post:
tags:
Expand Down