Fix bi
This commit is contained in:
parent
cb1b619a8b
commit
f5fe4e25a0
851
api/v2/app/src/controllers/DealController.php
Normal file
851
api/v2/app/src/controllers/DealController.php
Normal file
@ -0,0 +1,851 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Deal;
|
||||
use App\Models\DealInnerDoc;
|
||||
use App\Models\Document;
|
||||
use App\Models\EventClient;
|
||||
use App\Models\InnerDoc;
|
||||
use App\Models\Requisition;
|
||||
use App\Models\UserClientEvent;
|
||||
use App\Models\UserObjectEvent;
|
||||
use App\Services\Documents\DocumentService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class DealController extends Controller
|
||||
{
|
||||
/**
|
||||
* Получить список сделок
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return Deal::all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать новую сделку
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'side_one' => 'required|string',
|
||||
'side_two' => 'required|string',
|
||||
'side_one_requisition_id' => 'nullable|integer|exists:requisitions,id',
|
||||
'side_two_requisition_id' => 'nullable|integer|exists:requisitions,id',
|
||||
'side_one_object_id' => 'nullable|integer',
|
||||
'side_two_object_id' => 'nullable|integer',
|
||||
'side_one_clients' => 'nullable|array',
|
||||
'side_two_clients' => 'nullable|array',
|
||||
'contract_price' => 'nullable|numeric',
|
||||
'object_price' => 'nullable|numeric',
|
||||
'commission_buyer' => 'nullable|numeric',
|
||||
'commission_seller' => 'nullable|numeric',
|
||||
'agent_commission' => 'nullable|numeric',
|
||||
'total_commission' => 'nullable|numeric',
|
||||
'sellers_count' => 'nullable|integer',
|
||||
'buyers_count' => 'nullable|integer',
|
||||
'proxy_sale' => 'nullable|boolean',
|
||||
'e_registration' => 'nullable|boolean',
|
||||
'mortgaged' => 'nullable|boolean',
|
||||
'mortgagee' => 'nullable|string',
|
||||
'debt_amount' => 'nullable|numeric',
|
||||
'has_counter_agent' => 'nullable|boolean',
|
||||
'counter_agent_name' => 'nullable|string',
|
||||
'counter_agent_commission' => 'nullable|numeric',
|
||||
'counter_agent_phone' => 'nullable|string',
|
||||
'deal_date' => 'nullable|date',
|
||||
'employees' => 'nullable|array',
|
||||
'employees.*' => 'integer|exists:users,id',
|
||||
]);
|
||||
|
||||
$this->validateRequisitionsNotInDeal($validatedData['side_one_requisition_id'] ?? null);
|
||||
$this->validateRequisitionsNotInDeal($validatedData['side_two_requisition_id'] ?? null);
|
||||
|
||||
$deal = Deal::create($validatedData);
|
||||
|
||||
if (!empty($validatedData['employees'])) {
|
||||
$deal->users()->sync($validatedData['employees']);
|
||||
}
|
||||
|
||||
if (!empty($validatedData['side_one_requisition_id'])) {
|
||||
Requisition::where('id', $validatedData['side_one_requisition_id'])
|
||||
->update(['deal_id' => $deal->id]);
|
||||
|
||||
// Автоматическое добавление ответственных за сделку в партнёры заявки стороны 1
|
||||
if (isset($validatedData['employees'])) {
|
||||
$sideOneReq = Requisition::find($validatedData['side_one_requisition_id']);
|
||||
if ($sideOneReq) {
|
||||
$whoWork = (int) $sideOneReq->who_work;
|
||||
$currentUserId = auth()->id();
|
||||
|
||||
$doers = is_string($sideOneReq->doers) ? json_decode($sideOneReq->doers, true) : ($sideOneReq->doers ?? []);
|
||||
if (!is_array($doers)) $doers = [];
|
||||
|
||||
$newDoers = $doers;
|
||||
$changed = false;
|
||||
|
||||
foreach ($validatedData['employees'] as $employeeId) {
|
||||
$employeeId = (int) $employeeId;
|
||||
|
||||
if ($employeeId === $whoWork || $employeeId === $currentUserId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists($employeeId, $doers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$exists = EventClient::where('req_id', $validatedData['side_one_requisition_id'])
|
||||
->where('from_id', $employeeId)
|
||||
->where('event', 'transmitted_parallel')
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
EventClient::create([
|
||||
'user_id' => $whoWork ?: $currentUserId,
|
||||
'req_id' => $validatedData['side_one_requisition_id'],
|
||||
'from_id' => $employeeId,
|
||||
'client_id' => $sideOneReq->client_id,
|
||||
'event' => 'transmitted_parallel',
|
||||
'read' => 0,
|
||||
'send_telegramm' => 1,
|
||||
]);
|
||||
|
||||
$newDoers[$employeeId] = 0;
|
||||
$changed = true;
|
||||
|
||||
DB::table('requisition_doers')->insert([
|
||||
'requisition_id' => $validatedData['side_one_requisition_id'],
|
||||
'user_id' => $employeeId,
|
||||
'confirm' => 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
$sideOneReq->update([
|
||||
'doers' => json_encode($newDoers),
|
||||
'doers_confirm' => 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($validatedData['side_two_requisition_id'])) {
|
||||
Requisition::where('id', $validatedData['side_two_requisition_id'])
|
||||
->update(['deal_id' => $deal->id]);
|
||||
}
|
||||
|
||||
$clientEvents = [];
|
||||
|
||||
// Получаем ID клиентов стороны 1
|
||||
$sideOneClientIds = [];
|
||||
if (!empty($validatedData['side_one_clients'])) {
|
||||
foreach ($validatedData['side_one_clients'] as $client) {
|
||||
if (is_array($client) && isset($client['id'])) {
|
||||
$sideOneClientIds[] = $client['id'];
|
||||
} elseif (is_numeric($client)) {
|
||||
$sideOneClientIds[] = (int) $client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем клиента из заявки, если его нет в списке
|
||||
$sideOneClientId = $validatedData['side_one_requisition_id']
|
||||
? Requisition::where('id', $validatedData['side_one_requisition_id'])->value('client_id')
|
||||
: null;
|
||||
|
||||
if ($sideOneClientId && !in_array($sideOneClientId, $sideOneClientIds)) {
|
||||
$sideOneClientIds[] = $sideOneClientId;
|
||||
}
|
||||
|
||||
// Получаем who_work для всех клиентов стороны 1
|
||||
$sideOneClientWhoWork = [];
|
||||
if (!empty($sideOneClientIds)) {
|
||||
$sideOneClientWhoWork = Client::whereIn('id', $sideOneClientIds)
|
||||
->pluck('who_work', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// Создаем события для клиентов стороны 1
|
||||
if (!empty($validatedData['side_one_requisition_id']) && !empty($sideOneClientIds)) {
|
||||
$defaultUserId = Requisition::where('id', $validatedData['side_one_requisition_id'])->value('who_work') ?: auth()->id();
|
||||
|
||||
foreach ($sideOneClientIds as $clientId) {
|
||||
$userId = $sideOneClientWhoWork[$clientId] ?? $defaultUserId;
|
||||
|
||||
if (empty($userId)) {
|
||||
$userId = auth()->id();
|
||||
}
|
||||
|
||||
$clientEvents[] = [
|
||||
'user_id' => $userId,
|
||||
'client_id' => $clientId,
|
||||
'req_id' => 0,
|
||||
'type' => 'deal',
|
||||
'deal_id' => $deal->id,
|
||||
'name' => 'Участие в сделке (сторона 1)',
|
||||
'schedule_date' => $validatedData['deal_date'] ?? now(),
|
||||
'comment' => 'Создание сделки: ' . $validatedData['title'],
|
||||
'create_date' => now(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Получаем ID клиентов стороны 2
|
||||
$sideTwoClientIds = [];
|
||||
if (!empty($validatedData['side_two_clients'])) {
|
||||
foreach ($validatedData['side_two_clients'] as $client) {
|
||||
if (is_array($client) && isset($client['id'])) {
|
||||
$sideTwoClientIds[] = $client['id'];
|
||||
} elseif (is_numeric($client)) {
|
||||
$sideTwoClientIds[] = (int) $client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем клиента из заявки, если его нет в списке
|
||||
$sideTwoClientId = $validatedData['side_two_requisition_id']
|
||||
? Requisition::where('id', $validatedData['side_two_requisition_id'])->value('client_id')
|
||||
: null;
|
||||
|
||||
if ($sideTwoClientId && !in_array($sideTwoClientId, $sideTwoClientIds)) {
|
||||
$sideTwoClientIds[] = $sideTwoClientId;
|
||||
}
|
||||
|
||||
// Получаем who_work для всех клиентов стороны 2
|
||||
$sideTwoClientWhoWork = [];
|
||||
if (!empty($sideTwoClientIds)) {
|
||||
$sideTwoClientWhoWork = Client::whereIn('id', $sideTwoClientIds)
|
||||
->pluck('who_work', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// Создаем события для клиентов стороны 2
|
||||
if (!empty($validatedData['side_two_requisition_id']) && !empty($sideTwoClientIds)) {
|
||||
$defaultUserId = Requisition::where('id', $validatedData['side_two_requisition_id'])->value('who_work') ?: auth()->id();
|
||||
|
||||
foreach ($sideTwoClientIds as $clientId) {
|
||||
$userId = $sideTwoClientWhoWork[$clientId] ?? $defaultUserId;
|
||||
|
||||
if (empty($userId)) {
|
||||
$userId = auth()->id();
|
||||
}
|
||||
|
||||
$clientEvents[] = [
|
||||
'user_id' => $userId,
|
||||
'client_id' => $clientId,
|
||||
'req_id' => 0,
|
||||
'type' => 'deal',
|
||||
'deal_id' => $deal->id,
|
||||
'name' => 'Участие в сделке (сторона 2)',
|
||||
'schedule_date' => $validatedData['deal_date'] ?? now(),
|
||||
'comment' => 'Создание сделки: ' . $validatedData['title'],
|
||||
'create_date' => now(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($clientEvents)) {
|
||||
UserClientEvent::insert($clientEvents);
|
||||
}
|
||||
|
||||
// События для заявок
|
||||
$reqEvents = [];
|
||||
|
||||
if (!empty($validatedData['side_one_requisition_id'])) {
|
||||
$sideOneReqUserId = Requisition::where('id', $validatedData['side_one_requisition_id'])->value('who_work') ?: auth()->id();
|
||||
$reqEvents[] = [
|
||||
'user_id' => $sideOneReqUserId,
|
||||
'client_id' => 0,
|
||||
'req_id' => $validatedData['side_one_requisition_id'],
|
||||
'type' => 'deal',
|
||||
'deal_id' => $deal->id,
|
||||
'name' => 'Привязана к сделке (сторона 1)',
|
||||
'schedule_date' => $validatedData['deal_date'] ?? now(),
|
||||
'comment' => 'Создание сделки: ' . $validatedData['title'],
|
||||
'create_date' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($validatedData['side_two_requisition_id'])) {
|
||||
$sideTwoReqUserId = Requisition::where('id', $validatedData['side_two_requisition_id'])->value('who_work') ?: auth()->id();
|
||||
$reqEvents[] = [
|
||||
'user_id' => $sideTwoReqUserId,
|
||||
'client_id' => 0,
|
||||
'req_id' => $validatedData['side_two_requisition_id'],
|
||||
'type' => 'deal',
|
||||
'deal_id' => $deal->id,
|
||||
'name' => 'Привязана к сделке (сторона 2)',
|
||||
'schedule_date' => $validatedData['deal_date'] ?? now(),
|
||||
'comment' => 'Создание сделки: ' . $validatedData['title'],
|
||||
'create_date' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($reqEvents)) {
|
||||
UserClientEvent::insert($reqEvents);
|
||||
}
|
||||
|
||||
$objectIds = array_unique(array_filter([
|
||||
$validatedData['side_one_object_id'] ?? null,
|
||||
$validatedData['side_two_object_id'] ?? null,
|
||||
]));
|
||||
|
||||
if (!empty($objectIds)) {
|
||||
$objectEvents = [];
|
||||
foreach ($objectIds as $objectId) {
|
||||
$objectEvents[] = [
|
||||
'user_id' => auth()->id(),
|
||||
'object_id' => $objectId,
|
||||
'type' => 'deal',
|
||||
'deal_id' => $deal->id,
|
||||
'schedule_date' => $validatedData['deal_date'] ?? now(),
|
||||
'sum' => $validatedData['contract_price'] ?? null,
|
||||
'comment' => 'Создание сделки: ' . $validatedData['title'],
|
||||
'create_date' => now(),
|
||||
];
|
||||
}
|
||||
UserObjectEvent::insert($objectEvents);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'deal' => $deal,
|
||||
'message' => 'Сделка успешно создана',
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить сделку по ID
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
$deal = Deal::with([
|
||||
'innerDocs.creator' => function($query) {
|
||||
$query->select('id', 'last_name', 'first_name');
|
||||
},
|
||||
'innerDocs.deals' => function($query) {
|
||||
$query->select([
|
||||
'deals.id',
|
||||
'deal_inner_doc.inner_doc_id',
|
||||
'deal_inner_doc.side_one',
|
||||
'deal_inner_doc.side_two',
|
||||
'deal_inner_doc.comment'
|
||||
]);
|
||||
}
|
||||
])->findOrFail($id);
|
||||
|
||||
$deal->innerDocs->each(function ($doc) {
|
||||
collect($doc->deals)->each(function ($deal) {
|
||||
$this->enrichSideWithClientData($deal, 'side_one');
|
||||
$this->enrichSideWithClientData($deal, 'side_two');
|
||||
});
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $deal,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function enrichSideWithClientData($deal, $sideField)
|
||||
{
|
||||
if (empty($deal->pivot->{$sideField})) {
|
||||
$deal->{$sideField} = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$sideData = json_decode($deal->pivot->{$sideField}, true);
|
||||
$clientIds = array_column($sideData, 'client_id');
|
||||
|
||||
$clients = Client::whereIn('id', $clientIds)
|
||||
->select('id', 'fio', 'phone')
|
||||
->get()
|
||||
->keyBy('id');
|
||||
|
||||
$enrichedSide = array_map(function ($item) use ($clients) {
|
||||
$client = $clients[$item['client_id'] ?? null];
|
||||
return [
|
||||
'role' => $item['role'],
|
||||
'client_id' => $item['client_id'],
|
||||
'fio' => $client->fio ?? 'Неизвестный клиент',
|
||||
'phone' => $client->phone ?? null
|
||||
];
|
||||
}, $sideData);
|
||||
|
||||
$deal->{$sideField} = $enrichedSide;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить сделку
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'sometimes|string|max:255',
|
||||
'side_one' => 'sometimes|string',
|
||||
'side_two' => 'sometimes|string',
|
||||
'side_one_requisition_id' => 'nullable|integer|exists:requisitions,id',
|
||||
'side_two_requisition_id' => 'nullable|integer|exists:requisitions,id',
|
||||
'side_one_object_id' => 'nullable|integer',
|
||||
'side_two_object_id' => 'nullable|integer',
|
||||
'side_one_clients' => 'nullable|array',
|
||||
'side_two_clients' => 'nullable|array',
|
||||
'contract_price' => 'nullable|numeric',
|
||||
'object_price' => 'nullable|numeric',
|
||||
'commission_buyer' => 'nullable|numeric',
|
||||
'commission_seller' => 'nullable|numeric',
|
||||
'agent_commission' => 'nullable|numeric',
|
||||
'total_commission' => 'nullable|numeric',
|
||||
'sellers_count' => 'nullable|integer',
|
||||
'buyers_count' => 'nullable|integer',
|
||||
'proxy_sale' => 'nullable|boolean',
|
||||
'e_registration' => 'nullable|boolean',
|
||||
'mortgaged' => 'nullable|boolean',
|
||||
'mortgagee' => 'nullable|string',
|
||||
'debt_amount' => 'nullable|numeric',
|
||||
'has_counter_agent' => 'nullable|boolean',
|
||||
'counter_agent_name' => 'nullable|string',
|
||||
'counter_agent_commission' => 'nullable|numeric',
|
||||
'counter_agent_phone' => 'nullable|string',
|
||||
'deal_date' => 'nullable|date',
|
||||
'employees' => 'nullable|array',
|
||||
'employees.*' => 'integer|exists:users,id',
|
||||
'documents' => 'nullable|array',
|
||||
'documents.*' => 'integer',
|
||||
]);
|
||||
|
||||
$deal = Deal::findOrFail($id);
|
||||
|
||||
$currentReqIds = array_filter([
|
||||
$deal->side_one_requisition_id,
|
||||
$deal->side_two_requisition_id,
|
||||
]);
|
||||
|
||||
$this->validateRequisitionsNotInDeal($validatedData['side_one_requisition_id'] ?? null, $currentReqIds);
|
||||
$this->validateRequisitionsNotInOtherDeal(
|
||||
$validatedData['side_two_requisition_id'] ?? null,
|
||||
$id
|
||||
);
|
||||
|
||||
$deal->update($validatedData);
|
||||
|
||||
if (!empty($validatedData['employees'])) {
|
||||
$deal->users()->sync($validatedData['employees']);
|
||||
}
|
||||
|
||||
if (isset($validatedData['documents'])) {
|
||||
$existingDocIds = Document::active()
|
||||
->where('target_id', $id)
|
||||
->where('target_type', 10)
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
|
||||
$removedDocIds = array_diff($existingDocIds, $validatedData['documents']);
|
||||
if (!empty($removedDocIds)) {
|
||||
Document::whereIn('id', $removedDocIds)->each->softDelete();
|
||||
}
|
||||
|
||||
$addedDocIds = array_diff($validatedData['documents'], $existingDocIds);
|
||||
if (!empty($addedDocIds)) {
|
||||
Document::whereIn('id', $addedDocIds)
|
||||
->where('target_id', 0)
|
||||
->update(['target_id' => $id]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($deal->side_one_requisition_id)) {
|
||||
Requisition::where('id', $deal->side_one_requisition_id)
|
||||
->update(['deal_id' => $id]);
|
||||
|
||||
if (isset($validatedData['employees'])) {
|
||||
$sideOneReq = Requisition::find($deal->side_one_requisition_id);
|
||||
if ($sideOneReq) {
|
||||
$whoWork = (int) $sideOneReq->who_work;
|
||||
$currentUserId = auth()->id();
|
||||
|
||||
$doers = is_string($sideOneReq->doers) ? json_decode($sideOneReq->doers, true) : ($sideOneReq->doers ?? []);
|
||||
if (!is_array($doers)) $doers = [];
|
||||
|
||||
$newDoers = $doers;
|
||||
$changed = false;
|
||||
|
||||
foreach ($validatedData['employees'] as $employeeId) {
|
||||
$employeeId = (int) $employeeId;
|
||||
|
||||
if ($employeeId === $whoWork || $employeeId === $currentUserId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (array_key_exists($employeeId, $doers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$exists = EventClient::where('req_id', $deal->side_one_requisition_id)
|
||||
->where('from_id', $employeeId)
|
||||
->where('event', 'transmitted_parallel')
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
EventClient::create([
|
||||
'user_id' => $whoWork ?: $currentUserId,
|
||||
'req_id' => $deal->side_one_requisition_id,
|
||||
'from_id' => $employeeId,
|
||||
'client_id' => $sideOneReq->client_id,
|
||||
'event' => 'transmitted_parallel',
|
||||
'read' => 0,
|
||||
'send_telegramm' => 1,
|
||||
]);
|
||||
|
||||
$newDoers[$employeeId] = 0;
|
||||
$changed = true;
|
||||
|
||||
DB::table('requisition_doers')->insert([
|
||||
'requisition_id' => $deal->side_one_requisition_id,
|
||||
'user_id' => $employeeId,
|
||||
'confirm' => 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($changed) {
|
||||
$sideOneReq->update([
|
||||
'doers' => json_encode($newDoers),
|
||||
'doers_confirm' => 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($deal->side_two_requisition_id)) {
|
||||
Requisition::where('id', $deal->side_two_requisition_id)
|
||||
->update(['deal_id' => $id]);
|
||||
}
|
||||
|
||||
// Получаем ID клиентов стороны 1
|
||||
$sideOneClientIds = [];
|
||||
if (!empty($validatedData['side_one_clients'])) {
|
||||
foreach ($validatedData['side_one_clients'] as $client) {
|
||||
if (is_array($client) && isset($client['id'])) {
|
||||
$sideOneClientIds[] = $client['id'];
|
||||
} elseif (is_numeric($client)) {
|
||||
$sideOneClientIds[] = (int) $client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sideOneClientId = $deal->side_one_requisition_id
|
||||
? Requisition::where('id', $deal->side_one_requisition_id)->value('client_id')
|
||||
: null;
|
||||
|
||||
if ($sideOneClientId && !in_array($sideOneClientId, $sideOneClientIds)) {
|
||||
$sideOneClientIds[] = $sideOneClientId;
|
||||
}
|
||||
|
||||
// Получаем who_work для всех клиентов стороны 1
|
||||
$sideOneClientWhoWork = [];
|
||||
if (!empty($sideOneClientIds)) {
|
||||
$sideOneClientWhoWork = Client::whereIn('id', $sideOneClientIds)
|
||||
->pluck('who_work', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// Обновляем события для клиентов стороны 1
|
||||
if (!empty($deal->side_one_requisition_id)) {
|
||||
$defaultUserId = Requisition::where('id', $deal->side_one_requisition_id)->value('who_work') ?: auth()->id();
|
||||
|
||||
// Сначала удаляем старые события для этой стороны, чтобы избежать дублирования
|
||||
UserClientEvent::where('deal_id', $id)
|
||||
->where('type', 'deal')
|
||||
->where('name', 'like', '%сторона 1%')
|
||||
->delete();
|
||||
|
||||
foreach ($sideOneClientIds as $clientId) {
|
||||
$userId = $sideOneClientWhoWork[$clientId] ?? $defaultUserId;
|
||||
|
||||
if (empty($userId)) {
|
||||
$userId = auth()->id();
|
||||
}
|
||||
|
||||
UserClientEvent::create([
|
||||
'user_id' => $userId,
|
||||
'client_id' => $clientId,
|
||||
'req_id' => 0,
|
||||
'type' => 'deal',
|
||||
'deal_id' => $id,
|
||||
'name' => 'Участие в сделке (сторона 1)',
|
||||
'schedule_date' => $deal->deal_date,
|
||||
'comment' => 'Обновление сделки: ' . $deal->title,
|
||||
'create_date' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Получаем ID клиентов стороны 2
|
||||
$sideTwoClientIds = [];
|
||||
if (!empty($validatedData['side_two_clients'])) {
|
||||
foreach ($validatedData['side_two_clients'] as $client) {
|
||||
if (is_array($client) && isset($client['id'])) {
|
||||
$sideTwoClientIds[] = $client['id'];
|
||||
} elseif (is_numeric($client)) {
|
||||
$sideTwoClientIds[] = (int) $client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sideTwoClientId = $deal->side_two_requisition_id
|
||||
? Requisition::where('id', $deal->side_two_requisition_id)->value('client_id')
|
||||
: null;
|
||||
|
||||
if ($sideTwoClientId && !in_array($sideTwoClientId, $sideTwoClientIds)) {
|
||||
$sideTwoClientIds[] = $sideTwoClientId;
|
||||
}
|
||||
|
||||
// Получаем who_work для всех клиентов стороны 2
|
||||
$sideTwoClientWhoWork = [];
|
||||
if (!empty($sideTwoClientIds)) {
|
||||
$sideTwoClientWhoWork = Client::whereIn('id', $sideTwoClientIds)
|
||||
->pluck('who_work', 'id')
|
||||
->toArray();
|
||||
}
|
||||
|
||||
// Обновляем события для клиентов стороны 2
|
||||
if (!empty($deal->side_two_requisition_id)) {
|
||||
$defaultUserId = Requisition::where('id', $deal->side_two_requisition_id)->value('who_work') ?: auth()->id();
|
||||
|
||||
// Сначала удаляем старые события для этой стороны
|
||||
UserClientEvent::where('deal_id', $id)
|
||||
->where('type', 'deal')
|
||||
->where('name', 'like', '%сторона 2%')
|
||||
->delete();
|
||||
|
||||
foreach ($sideTwoClientIds as $clientId) {
|
||||
$userId = $sideTwoClientWhoWork[$clientId] ?? $defaultUserId;
|
||||
|
||||
UserClientEvent::create([
|
||||
'user_id' => $userId,
|
||||
'client_id' => $clientId,
|
||||
'req_id' => 0,
|
||||
'type' => 'deal',
|
||||
'deal_id' => $id,
|
||||
'name' => 'Участие в сделке (сторона 2)',
|
||||
'schedule_date' => $deal->deal_date,
|
||||
'comment' => 'Обновление сделки: ' . $deal->title,
|
||||
'create_date' => now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем события для заявок
|
||||
foreach ([$deal->side_one_requisition_id, $deal->side_two_requisition_id] as $reqId) {
|
||||
if (empty($reqId)) continue;
|
||||
|
||||
// Удаляем старые события для этой заявки
|
||||
UserClientEvent::where('deal_id', $id)
|
||||
->where('req_id', $reqId)
|
||||
->where('type', 'deal')
|
||||
->delete();
|
||||
|
||||
$reqUserId = Requisition::where('id', $reqId)->value('who_work') ?: auth()->id();
|
||||
|
||||
UserClientEvent::create([
|
||||
'user_id' => $reqUserId,
|
||||
'client_id' => 0,
|
||||
'req_id' => $reqId,
|
||||
'type' => 'deal',
|
||||
'deal_id' => $id,
|
||||
'name' => 'Связана со сделкой',
|
||||
'schedule_date' => $deal->deal_date,
|
||||
'comment' => 'Обновление сделки: ' . $deal->title,
|
||||
'create_date' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Обновляем события для объектов
|
||||
$objectIds = array_unique(array_filter([
|
||||
$deal->side_one_object_id ?? null,
|
||||
$deal->side_two_object_id ?? null,
|
||||
]));
|
||||
|
||||
// Удаляем старые события для объектов
|
||||
UserObjectEvent::where('deal_id', $id)
|
||||
->where('type', 'deal')
|
||||
->delete();
|
||||
|
||||
foreach ($objectIds as $objectId) {
|
||||
UserObjectEvent::create([
|
||||
'user_id' => auth()->id(),
|
||||
'object_id' => $objectId,
|
||||
'type' => 'deal',
|
||||
'deal_id' => $id,
|
||||
'schedule_date' => $deal->deal_date,
|
||||
'sum' => $deal->contract_price,
|
||||
'comment' => 'Обновление сделки: ' . $deal->title,
|
||||
'create_date' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $deal,
|
||||
'message' => 'Сделка успешно обновлена',
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить сделку
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
Deal::destroy($id);
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
|
||||
public function assignEmployees(Request $request, $dealId)
|
||||
{
|
||||
$deal = Deal::find($dealId);
|
||||
|
||||
if ($deal) {
|
||||
$employeeIds = $request->input('employee_ids');
|
||||
$deal->users()->sync($employeeIds);
|
||||
return response()->json(['message' => 'Сотрудники успешно назначены']);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Сделка не найдена'], 404);
|
||||
}
|
||||
|
||||
public function getDealEmployees($dealId)
|
||||
{
|
||||
$deal = Deal::find($dealId);
|
||||
|
||||
if (!$deal) {
|
||||
return response()->json(['message' => 'Сделка не найдена'], 404);
|
||||
}
|
||||
|
||||
$employees = $deal->users()
|
||||
->select([
|
||||
'users.id',
|
||||
'users.fio',
|
||||
'users.first_name',
|
||||
'users.last_name',
|
||||
'users.middle_name',
|
||||
'users.phone',
|
||||
'users.role_id',
|
||||
'users.user_logo'
|
||||
])
|
||||
->with('role:id,name')
|
||||
->where('blocked', 0)
|
||||
->get();
|
||||
|
||||
return response()->json($employees);
|
||||
}
|
||||
|
||||
public function storeDocument(Request $request)
|
||||
{
|
||||
$data = $request->validate([
|
||||
'template_data' => 'required|array',
|
||||
'template_data.blank_id' => 'required|integer|exists:inner_blanks,id',
|
||||
'template_data.object_id' => 'nullable|integer|exists:objects,id',
|
||||
'template_data.sobstv' => 'nullable|array',
|
||||
'template_data.clients' => 'nullable|array',
|
||||
'deal_id' => 'nullable|integer|exists:deals,id',
|
||||
'side_one' => 'nullable|array',
|
||||
'side_two' => 'nullable|array',
|
||||
'comment' => 'nullable|string|max:500'
|
||||
]);
|
||||
|
||||
try {
|
||||
$clientIds = $data['template_data']['clients'] ?? [];
|
||||
$sobstvIds = $data['template_data']['sobstv'] ?? [];
|
||||
|
||||
$mainClientId = !empty($clientIds) ? $clientIds[0] : null;
|
||||
$mainSobstvId = !empty($sobstvIds) ? $sobstvIds[0] : null;
|
||||
|
||||
$result = app(DocumentService::class)->fillFromTemplate(
|
||||
$data['template_data']['blank_id'],
|
||||
$mainClientId,
|
||||
$data['template_data']['object_id'] ?? null,
|
||||
$clientIds,
|
||||
$sobstvIds
|
||||
);
|
||||
|
||||
$dealDoc = DealInnerDoc::create([
|
||||
'deal_id' => $data['deal_id'] ?? null,
|
||||
'inner_doc_id' => $result['doc_id'],
|
||||
'side_one' => $data['side_one'] ?? null,
|
||||
'side_two' => $data['side_two'] ?? null,
|
||||
'comment' => $data['comment'] ?? null
|
||||
]);
|
||||
|
||||
$innerDoc = InnerDoc::with([
|
||||
'creator:id,last_name,first_name',
|
||||
'deals' => function ($query) {
|
||||
$query->select(
|
||||
'deals.id',
|
||||
'deal_inner_doc.side_one',
|
||||
'deal_inner_doc.side_two',
|
||||
'deal_inner_doc.comment'
|
||||
);
|
||||
}
|
||||
])->findOrFail($result['doc_id']);
|
||||
|
||||
foreach ($innerDoc->deals as $deal) {
|
||||
$this->enrichSideWithClientData($deal, 'side_one');
|
||||
$this->enrichSideWithClientData($deal, 'side_two');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $innerDoc
|
||||
], 201);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateRequisitionsNotInDeal($requisitionId, $excludeIds = [])
|
||||
{
|
||||
if (empty($requisitionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_array($requisitionId, $excludeIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requisition = Requisition::find($requisitionId);
|
||||
|
||||
if ($requisition && !is_null($requisition->deal_id)) {
|
||||
throw \Illuminate\Validation\ValidationException::withMessages([
|
||||
'requisition_id' => "Заявка #{$requisitionId} уже привязана к сделке #{$requisition->deal_id}"
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateRequisitionsNotInOtherDeal($requisitionId, $currentDealId)
|
||||
{
|
||||
if (empty($requisitionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requisition = Requisition::find($requisitionId);
|
||||
|
||||
if ($requisition && !is_null($requisition->deal_id) && $requisition->deal_id != $currentDealId) {
|
||||
throw \Illuminate\Validation\ValidationException::withMessages([
|
||||
'requisition_id' => "Заявка #{$requisitionId} уже привязана к другой сделке #{$requisition->deal_id}"
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
162
engine/classes/BiAnalyticsLogger.php
Normal file
162
engine/classes/BiAnalyticsLogger.php
Normal file
@ -0,0 +1,162 @@
|
||||
<?php
|
||||
/**
|
||||
* Класс для логирования событий в BI Analytics (outbox)
|
||||
*
|
||||
* Используется для отправки событий из JoyWork в ClickHouse
|
||||
* через таблицу bi_analytics_events_outbox
|
||||
*/
|
||||
class BiAnalyticsLogger {
|
||||
|
||||
/**
|
||||
* @var MysqlPdo Подключение к базе данных
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @var int ID компании (agency_id)
|
||||
*/
|
||||
private $companyId;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* @param MysqlPdo $db Подключение к БД
|
||||
* @param int $companyId ID компании
|
||||
*/
|
||||
public function __construct($db, $companyId) {
|
||||
$this->db = $db;
|
||||
$this->companyId = (int)$companyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать событие в outbox
|
||||
*
|
||||
* @param string $entityType Тип сущности: 'client' или 'requisition'
|
||||
* @param int $entityId ID сущности
|
||||
* @param string $eventType Тип события
|
||||
* @param int $actorUserId ID пользователя
|
||||
* @param array $before Данные до изменения
|
||||
* @param array $after Данные после изменения
|
||||
*
|
||||
* @return string UUID созданного события
|
||||
* @throws Exception
|
||||
*/
|
||||
public function logEvent($entityType, $entityId, $eventType, $actorUserId, $before = array(), $after = array()) {
|
||||
|
||||
// Валидация
|
||||
if (!in_array($entityType, array('client', 'requisition'))) {
|
||||
throw new Exception("Invalid entity type: " . $entityType);
|
||||
}
|
||||
|
||||
$entityId = (int)$entityId;
|
||||
$actorUserId = (int)$actorUserId;
|
||||
|
||||
// Определяем изменённые поля
|
||||
$changedFields = array_keys(array_diff_assoc($after, $before));
|
||||
|
||||
// Формируем payload
|
||||
$payload = array(
|
||||
'schema_version' => 1,
|
||||
'entity' => array(
|
||||
'type' => $entityType,
|
||||
'id' => $entityId,
|
||||
'company_id' => $this->companyId
|
||||
),
|
||||
'occurred_at' => date('c'),
|
||||
'actor_user_id' => $actorUserId,
|
||||
'event' => array(
|
||||
'type' => $eventType,
|
||||
'changed_fields' => $changedFields
|
||||
),
|
||||
'before' => $before,
|
||||
'after' => $after
|
||||
);
|
||||
|
||||
// Генерируем UUID v4
|
||||
$eventUuid = $this->generateUuid();
|
||||
|
||||
// Вставляем в outbox с prepared statement
|
||||
$payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// Экранируем строковые значения для безопасной вставки
|
||||
$escapedEventUuid = $this->db->quote($eventUuid);
|
||||
$escapedCompanyId = (int)$this->companyId; // целое число не требует экранирования
|
||||
$escapedEntityType = $this->db->quote($entityType);
|
||||
$escapedEntityId = (int)$entityId;
|
||||
$escapedEventType = $this->db->quote($eventType);
|
||||
$escapedActorUserId = (int)$actorUserId;
|
||||
$escapedPayloadJson = $this->db->quote($payloadJson);
|
||||
|
||||
$sql = "INSERT INTO bi_analytics_events_outbox
|
||||
(event_uuid, company_id, entity_type, entity_id, event_type, occurred_at, actor_user_id, payload_json)
|
||||
VALUES ($escapedEventUuid, $escapedCompanyId, $escapedEntityType, $escapedEntityId, $escapedEventType, NOW(3), $escapedActorUserId, $escapedPayloadJson)";
|
||||
|
||||
try {
|
||||
$this->db->query($sql);
|
||||
return $eventUuid;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Failed to insert event: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать событие: клиент создан
|
||||
*/
|
||||
public function logClientCreated($clientId, $actorUserId, $clientData) {
|
||||
return $this->logEvent('client', $clientId, 'client.created', $actorUserId, array(), $clientData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать событие: клиент обновлён
|
||||
*/
|
||||
public function logClientUpdated($clientId, $actorUserId, $before, $after) {
|
||||
return $this->logEvent('client', $clientId, 'client.updated', $actorUserId, $before, $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать событие: заявка создана
|
||||
*/
|
||||
public function logRequisitionCreated($requisitionId, $actorUserId, $requisitionData) {
|
||||
return $this->logEvent('requisition', $requisitionId, 'requisition.created', $actorUserId, array(), $requisitionData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать событие: заявка обновлена
|
||||
*/
|
||||
public function logRequisitionUpdated($requisitionId, $actorUserId, $before, $after) {
|
||||
return $this->logEvent('requisition', $requisitionId, 'requisition.updated', $actorUserId, $before, $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать событие: заявка перешла на этап
|
||||
*/
|
||||
public function logRequisitionMovedToStep($requisitionId, $actorUserId, $fromStepId, $toStepId) {
|
||||
$before = array('step_id' => $fromStepId);
|
||||
$after = array('step_id' => $toStepId);
|
||||
return $this->logEvent('requisition', $requisitionId, 'requisition.moved_to_step', $actorUserId, $before, $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать событие: смена ответственного
|
||||
*/
|
||||
public function logResponsibleChanged($entityType, $entityId, $actorUserId, $fromUserId, $toUserId) {
|
||||
$before = array('who_work' => $fromUserId);
|
||||
$after = array('who_work' => $toUserId);
|
||||
return $this->logEvent($entityType, $entityId, 'responsible_changed', $actorUserId, $before, $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Сгенерировать UUID v4
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generateUuid() {
|
||||
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
|
||||
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff),
|
||||
mt_rand(0, 0xffff) & 0x0fff | 0x4000,
|
||||
mt_rand(0, 0x3fff) | 0x8000,
|
||||
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user