File: /var/www/ipsremont-demo/app/Services/Status/StatusService.php
<?php
namespace App\Services\Status;
use App\Models\ActTc;
use App\Models\ActtcChanges;
use App\Models\Order;
use App\Models\OrderChanges;
use App\Models\PartsStorage;
use App\Models\Repair\Repair;
use App\Models\RepairChanges;
use App\Models\Service\Service;
use App\Models\Shipment;
use App\Models\ShipmentChanges;
use App\Models\ShipmentDpdChanges;
use App\Models\ShipmentSdekChanges;
use App\Repository\Address\AddressRepository;
use App\Repository\Repair\RepairRepository;
use App\Repository\Status\StatusRepository;
use App\Services\Email\SendEmailService;
use App\Services\Log\LogService;
use App\Services\Movement\MovementService;
use App\Services\Repair\RepairService;
use Illuminate\Support\Facades\DB;
use PHPUnit\Exception;
class StatusService
{
// https://api-docs.cdek.ru/29948360.html
const SDEK_STATUS_ACCEPTED = 'ACCEPTED'; // Принят
const SDEK_STATUS_CREATED = 'CREATED'; // Создан
const SDEK_STATUS_REMOVED = 'REMOVED'; // Отменена
const SDEK_STATUS_READY_FOR_APPOINTMENT = 'READY_FOR_APPOINTMENT'; // Готова к назначению
const SDEK_STATUS_APPOINTED_COURIER = 'APPOINTED_COURIER'; // Назначен курьер
const SDEK_STATUS_DONE = 'DONE'; // Выполнена
const SDEK_STATUS_PROBLEM_DETECTED = 'PROBLEM_DETECTED'; // Выявлена проблема
const SDEK_STATUS_PROCESSING_REQUIRED = 'PROCESSING_REQUIRED'; // Требует обработки
const SDEK_STATUS_INVALID = 'INVALID'; // Некорректная заявка
// Недокументированные статусы
const SDEK_STATUS_DELIVERED = 'DELIVERED'; // Вручен
const SDEK_STATUS_TAKEN_BY_COURIER = 'TAKEN_BY_COURIER'; // Выдан на доставку
const SDEK_STATUS_ACCEPTED_AT_PICK_UP_POINT = 'ACCEPTED_AT_PICK_UP_POINT'; // Принят на склад до востребования
const SDEK_STATUS_ACCEPTED_IN_RECIPIENT_CITY = 'ACCEPTED_IN_RECIPIENT_CITY'; // Встречен в г. получателе
const SDEK_STATUS_SENT_TO_RECIPIENT_CITY = 'SENT_TO_RECIPIENT_CITY'; // Отправлен в г. получатель
const SDEK_STATUS_TAKEN_BY_TRANSPORTER_FROM_TRANSIT_CITY = 'TAKEN_BY_TRANSPORTER_FROM_TRANSIT_CITY'; // Сдан перевозчику в г. транзите
const SDEK_STATUS_READY_FOR_SHIPMENT_IN_TRANSIT_CITY = 'READY_FOR_SHIPMENT_IN_TRANSIT_CITY'; // Выдан на отправку в г. транзите
const SDEK_STATUS_ACCEPTED_AT_TRANSIT_WAREHOUSE = 'ACCEPTED_AT_TRANSIT_WAREHOUSE'; // Принят на склад транзита
const SDEK_STATUS_ACCEPTED_IN_TRANSIT_CITY = 'ACCEPTED_IN_TRANSIT_CITY'; // Встречен в г. транзите
const SDEK_STATUS_SENT_TO_TRANSIT_CITY = 'SENT_TO_TRANSIT_CITY'; // Отправлен в г. транзит
const SDEK_STATUS_TAKEN_BY_TRANSPORTER_FROM_SENDER_CITY = 'TAKEN_BY_TRANSPORTER_FROM_SENDER_CITY'; // Сдан перевозчику в г. отправителе
const SDEK_STATUS_READY_FOR_SHIPMENT_IN_SENDER_CITY = 'READY_FOR_SHIPMENT_IN_SENDER_CITY'; // Выдан на отправку в г. отправителе
const SDEK_STATUS_RECEIVED_AT_SHIPMENT_WAREHOUSE = 'RECEIVED_AT_SHIPMENT_WAREHOUSE'; // Принят на склад отправителя
const DPD_STATUS_NEW_ORDER_BY_CLIENT = 'NewOrderByClient'; // Оформлен новый заказ по инициативе клиента
const DPD_STATUS_NOT_DONE = 'NotDone'; // Заказ отменен
const DPD_STATUS_ON_TERMINAL_PICKUP = 'OnTerminalPickup'; // Посылка находится на терминале приема отправления
const DPD_STATUS_ON_ROAD = 'OnRoad'; // Посылка находится в пути (внутренняя перевозка DPD)
const DPD_STATUS_ON_TERMINAL = 'OnTerminal'; // Посылка находится на транзитном терминале
const DPD_STATUS_ON_TERMINAL_DELIVERY = 'OnTerminalDelivery'; // Посылка находится на терминале доставки
const DPD_STATUS_DELIVERING = 'Delivering'; // Посылка выведена на доставку
const DPD_STATUS_DELIVERED = 'Delivered'; // Посылка доставлена получателю
const DPD_STATUS_LOST = 'Lost'; // Посылка утеряна
const DPD_STATUS_PROBLEM = 'Problem'; // С посылкой возникла проблемная ситуация
const DPD_STATUS_RETURNED_FROM_DELIVERY = 'ReturnedFromDelivery'; // Посылка возвращена с доставки
const DPD_STATUS_NEW_ORDER_BY_DPD = 'NewOrderByDPD'; // Оформлен новый заказ по инициативе DPD
protected StatusRepository $repository;
public function __construct()
{
$this->repository = new StatusRepository();
}
public static function getRepairAll()
{
return StatusRepository::getRepair()->get()
->flatMap(function ($item) {
return [$item->slug => $item->getName()];
})
->all();
}
public static function getActtcAll(): array
{
return StatusRepository::getActTc()->get()->flatMap(fn($item) => [$item->slug => $item->getName()])->all();
}
public static function getShipmentAll()
{
return StatusRepository::getShipment()->get()
->flatMap(function ($item) {
return [$item->slug => $item->getName()];
})
->all();
}
public static function getRepairAllForEmail()
{
return StatusRepository::getRepair()->get()
->flatMap(function ($item) {
return ['repair-' . $item->slug => $item->getName()];
})
->all();
}
public static function getShipmentAllForEmail()
{
return StatusRepository::getShipment()->get()
->flatMap(function ($item) {
return ['shipment-' . $item->slug => $item->getName()];
})
->all();
}
public static function getRepairForForm()
{
return StatusRepository::getRepair()
->where('slug', '<>', 'report')
->get()
->flatMap(function ($item) {
return [$item->slug => $item->getName()];
})
->all();
}
public static function getRepairBySlug($slug)
{
$query = StatusRepository::getRepair();
return $query->bySlug($slug)->first();
}
public static function getOrderAll()
{
return StatusRepository::getOrder()->get()
->flatMap(function ($item) {
return [$item->slug => $item->getName()];
})
->all();
}
public static function getOrderAllForEmail()
{
return StatusRepository::getOrder()->get()
->flatMap(function ($item) {
return ['order-' . $item->slug => $item->getName()];
})
->all();
}
public static function getOrderBySlug($slug)
{
$query = StatusRepository::getOrder();
return $query->bySlug($slug)->first();
}
public static function getOrderByName($name)
{
$query = StatusRepository::getOrder();
return $query->where(['name' => $name])->first();
}
public static function getActTcBySlug($slug)
{
$query = StatusRepository::getActTc();
return $query->bySlug($slug)->first();
}
public static function getShipmentBySlug($slug)
{
$query = StatusRepository::getShipment();
return $query->bySlug($slug)->first();
}
/**
* Изменения статуса заявки
*
* @param Order $model
* @param string $status
*/
public static function statusChanged($model, $status)
{
$order_changes = new OrderChanges();
$order_changes->_save($model->id, $status);
if (in_array($status, [Order::STATUS_CANCELED, Order::STATUS_COMPLETED])) {
$model = Order::withoutEvents(function () use ($model) {
$model->bottom = 1;
$model->save();
return $model;
});
}
if ($model->repair_id) {
/** @var Repair $repair */
$repair = RepairService::getById($model->repair_id);
if (in_array($repair->status, $repair->getEditableStatuses())) {
if (in_array($status, [Order::STATUS_RESERVED, Order::STATUS_CONFIRMED])) {
$repair->status = 'ordered_parts';
$repair->save();
}
if (in_array($status, [Order::STATUS_PARTIALLY_SHIPPED, Order::STATUS_SHIPPED])) {
$repair->status = $status;
$repair->save();
}
// #61079 Наряд: При отмене заявки из 1С наряд оставлять без изменений
// if (Order::STATUS_CANCELED == $status) {
// $count = Order::query()->where('repair_id', $model->repair_id)->count();
// if ($count == 1) {
// $repair->status = Repair::STATUS_CANCELED;
// $repair->save();
// }
// }
}
}
$address_id = $model->getSelectedAddressId();
$main_address_id = AddressRepository::getMainByServiceId($model->service_id)->id;
if ($address_id != $main_address_id && $model->status == 'new') {
SendEmailService::orderEditMail($model, 'order-new-with-new-address');
} else {
SendEmailService::orderChangeStatusMail($model);
}
}
// изменения статуса наряда
public static function repairStatusChanged($model, $status = null)
{
$status = $status ?? $model->status;
$model_changes = new RepairChanges();
$model_changes->_save($model->id, $status);
if ($status == 'canceled' || $status == 'completed' || $status == 'report') {
$model = Repair::withoutEvents(function () use ($model) {
$model->bottom = 1;
$model->save();
return $model;
});
}
// и когда для сервиса пришли из 1с данные (это будет новый импорт, пока этой ситуации нет)
if ($status == 'completed') {
$service = $model->service;
if ($service->leftovers_btn) {
Service::withoutEvents(function () use ($service) {
$service->leftovers_btn = 0;
$service->save();
return $service;
});
}
$type = PartsStorage::service;
foreach ($model->orders as $order) {
foreach ($order->order_items as $item) {
$part_id = $item->part->id;
$external_warehouse_id = $model->service->external_warehouse_id;
try {
DB::statement("UPDATE `parts_storage` SET amount=amount-$item->amount WHERE `part_id`='$part_id' AND `external_warehouse_id`='$external_warehouse_id' AND `type`='$type'");
MovementService::addMovement($item->part->id, $model->id, $item->amount, '-', $model->service->id);
} catch (Exception $e) {
}
}
}
$model->price = RepairRepository::getPrice($model->service_id, $model->category_id, $model->device_external_id);
$model = Repair::withoutEvents(function () use ($model) {
$model->close_date = date('Y-m-01', $model->closed_at->getTimestamp());
$model->save();
LogService::updated($model);
return $model;
});
$orders = $model->orders()
->whereNotIn('status', ['completed', 'canceled'])
->get();
if ($orders) {
foreach ($orders as $order) {
$order->status = 'completed';
$order->save();
}
}
}
SendEmailService::repairChangeStatusMail($model);
}
// изменения статуса акта технического состояния
public static function acttcStatusChanged($model, $event)
{
$model_changes = new ActtcChanges();
$model_changes->_save($model->id, $model->status);
if ($model->isAllowManager()) {
RepairService::closeFromAct($model->repair_id, 'completed', Repair::CATEGORY_1, $model->created_at, 'Выдача акта ТС');
$model = ActTc::withoutEvents(function () use ($model) {
$model->bottom = 1;
$model->save();
return $model;
});
}
SendEmailService::acttcMail($model, $event);
}
/** @param Shipment $shipment */
public static function shipmentStatusChanged($shipment) // изменения статуса отгрузки
{
$shipmentChanges = new ShipmentChanges();
$shipmentChanges->_save($shipment->id, $shipment->status);
if (Shipment::STATUS_DELIVERED == $shipment->status) {
$order = $shipment->order;
$order->status = Order::STATUS_COMPLETED;
$order->save();
}
SendEmailService::shipmentMail($shipment);
}
public static function shipmentSdekStatusChanged(Shipment $shipment, $status, $i) // изменения статуса сдэк
{
if (!isset($shipment->shipment_sdek_changes[$i])) {
$shipmentSdekChanges = new ShipmentSdekChanges();
$date = '';
if (isset($status->date_time) && $status->date_time) {
$date = date('Y-m-d H:i:s', strtotime($status->date_time));
}
$reason_code = (isset($status->reason_code)) ? $status->reason_code : '';
$shipmentSdekChanges->_save($shipment->id, $status->code, $status->name, $date, $status->city, $reason_code);
$newShipmentStatus = null;
switch ($status->code) {
case self::SDEK_STATUS_ACCEPTED: // Принят
case self::SDEK_STATUS_CREATED: // Создан
$newShipmentStatus = Shipment::STATUS_NEW; // Отправлена
break;
case self::SDEK_STATUS_REMOVED: // Отменена
$newShipmentStatus = Shipment::STATUS_NOT_DELIVERED; // Возврат
break;
case self::SDEK_STATUS_READY_FOR_APPOINTMENT: // Готова к назначению
case self::SDEK_STATUS_APPOINTED_COURIER: // Назначен курьер
case self::SDEK_STATUS_TAKEN_BY_COURIER: // Выдан на доставку
case self::SDEK_STATUS_ACCEPTED_AT_PICK_UP_POINT: // Принят на склад до востребования
case self::SDEK_STATUS_ACCEPTED_IN_RECIPIENT_CITY: // Встречен в г. получателе
case self::SDEK_STATUS_SENT_TO_RECIPIENT_CITY: // Отправлен в г. получатель
case self::SDEK_STATUS_TAKEN_BY_TRANSPORTER_FROM_TRANSIT_CITY: // Сдан перевозчику в г. транзите
case self::SDEK_STATUS_READY_FOR_SHIPMENT_IN_TRANSIT_CITY: // Выдан на отправку в г. транзите
case self::SDEK_STATUS_ACCEPTED_AT_TRANSIT_WAREHOUSE: // Принят на склад транзита
case self::SDEK_STATUS_ACCEPTED_IN_TRANSIT_CITY: // Встречен в г. транзите
case self::SDEK_STATUS_SENT_TO_TRANSIT_CITY: // Отправлен в г. транзит
case self::SDEK_STATUS_TAKEN_BY_TRANSPORTER_FROM_SENDER_CITY: // Сдан перевозчику в г. отправителе
case self::SDEK_STATUS_READY_FOR_SHIPMENT_IN_SENDER_CITY: // Выдан на отправку в г. отправителе
case self::SDEK_STATUS_RECEIVED_AT_SHIPMENT_WAREHOUSE: // Принят на склад отправителя
$newShipmentStatus = Shipment::STATUS_DELIVERING; // Доставка
break;
case self::SDEK_STATUS_DONE: // Выполнена
case self::SDEK_STATUS_DELIVERED: // Вручен
$newShipmentStatus = Shipment::STATUS_DELIVERED; // Вручено
break;
case self::SDEK_STATUS_PROBLEM_DETECTED: // Выявлена проблема
case self::SDEK_STATUS_PROCESSING_REQUIRED: // Требует обработки
case self::SDEK_STATUS_INVALID: // Некорректная заявка
$newShipmentStatus = Shipment::STATUS_INVALID; // Ошибка
break;
}
if (!empty($newShipmentStatus)) {
$shipment->status_update($newShipmentStatus);
}
}
}
public static function shipmentPekStatusChanged($shipment, $s) // изменения статуса пек
{
//
if ($s->code == 'Принят' || $s->code == 'Оформлен' || $s->code == 'В пути' || $s->code == 'Прибыл') {
$shipment->status_update('new');
} elseif ($s->code == 'Готов к выдаче') {
$shipment->status_update('awaiting_delivery');
} elseif ($s->code == 'Доставлен' || $s->code == 'Выдан на доставку' || $s->code == 'Выдан на складе') {
$shipment->status_update('delivered');
} elseif ($s->code == 'Возвращен отправителю' || $s->code == 'Отправлен на возврат'
|| $s->code == 'Утилизирован') {
$shipment->status_update('not_delivered');
}
}
/**
* @param Shipment $shipment
* @param object $status
* @param int $i
*
* @return void
*/
public static function shipmentDpdStatusChanged(Shipment $shipment, $status, $i)
{
if (!isset($shipment->shipment_dpd_changes[$i])) {
$shipmentDpdChanges = new ShipmentDpdChanges();
$shipmentDpdChanges->_save($status, $shipment->id);
$newShipmentStatus = null;
switch ($shipmentDpdChanges->new_state) {
case self::DPD_STATUS_ON_TERMINAL_PICKUP: // Посылка находится на терминале приема отправления
case self::DPD_STATUS_ON_ROAD: // Посылка находится в пути (внутренняя перевозка DPD)
case self::DPD_STATUS_ON_TERMINAL: // Посылка находится на транзитном терминале
case self::DPD_STATUS_ON_TERMINAL_DELIVERY: // Посылка находится на терминале доставки
$newShipmentStatus = Shipment::STATUS_NEW; // Отправлена
break;
case self::DPD_STATUS_DELIVERING: // Посылка выведена на доставку
case self::DPD_STATUS_RETURNED_FROM_DELIVERY: // Посылка возвращена с доставки
case self::DPD_STATUS_NEW_ORDER_BY_DPD: // Оформлен новый заказ по инициативе DPD
$newShipmentStatus = Shipment::STATUS_DELIVERING; // Доставка
break;
case self::DPD_STATUS_DELIVERED: // Посылка доставлена получателю
$newShipmentStatus = Shipment::STATUS_DELIVERED; // Вручено
break;
case self::DPD_STATUS_NEW_ORDER_BY_CLIENT: // Оформлен новый заказ по инициативе клиента
case self::DPD_STATUS_NOT_DONE: // Заказ отменен
case self::DPD_STATUS_LOST: // Посылка утеряна
case self::DPD_STATUS_PROBLEM: // С посылкой возникла проблемная ситуация
$newShipmentStatus = Shipment::STATUS_INVALID; // Ошибка
break;
}
if (!empty($newShipmentStatus)) {
$shipment->status_update($newShipmentStatus);
}
}
}
}