|
|
@@ -30,6 +30,7 @@ use App\Models\User;
|
|
|
use App\Services\FileService;
|
|
|
use Illuminate\Http\RedirectResponse;
|
|
|
use Illuminate\Http\Request;
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
use ZipArchive;
|
|
|
@@ -163,13 +164,79 @@ class OrderController extends Controller
|
|
|
|
|
|
// меняем список товаров заказа только если статус новый
|
|
|
if($products && $quantities && ($order->order_status_id == 1)) {
|
|
|
- // remove from products_sku
|
|
|
- ProductSKU::query()->where('order_id', $order->id)->delete();
|
|
|
- foreach ($products as $key => $product) {
|
|
|
- for($i = 0; $i < $quantities[$key]; $i++) {
|
|
|
+ $desiredCounts = [];
|
|
|
+ foreach ($products as $key => $productId) {
|
|
|
+ $quantity = (int)($quantities[$key] ?? 0);
|
|
|
+ if ($quantity < 1) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!isset($desiredCounts[$productId])) {
|
|
|
+ $desiredCounts[$productId] = 0;
|
|
|
+ }
|
|
|
+ $desiredCounts[$productId] += $quantity;
|
|
|
+ }
|
|
|
+
|
|
|
+ $attachedSkus = ProductSKU::query()
|
|
|
+ ->where('order_id', $order->id)
|
|
|
+ ->whereNotNull('maf_order_id')
|
|
|
+ ->with('product:id,article')
|
|
|
+ ->get();
|
|
|
+
|
|
|
+ $attachedByProduct = [];
|
|
|
+ foreach ($attachedSkus as $sku) {
|
|
|
+ if (!isset($attachedByProduct[$sku->product_id])) {
|
|
|
+ $attachedByProduct[$sku->product_id] = 0;
|
|
|
+ }
|
|
|
+ $attachedByProduct[$sku->product_id]++;
|
|
|
+ }
|
|
|
+
|
|
|
+ $currentCounts = ProductSKU::query()
|
|
|
+ ->where('order_id', $order->id)
|
|
|
+ ->selectRaw('product_id, COUNT(*) as cnt')
|
|
|
+ ->groupBy('product_id')
|
|
|
+ ->pluck('cnt', 'product_id')
|
|
|
+ ->map(fn ($count) => (int)$count)
|
|
|
+ ->toArray();
|
|
|
+
|
|
|
+ $errors = [];
|
|
|
+ foreach ($attachedByProduct as $productId => $attachedCount) {
|
|
|
+ $desiredCount = $desiredCounts[$productId] ?? 0;
|
|
|
+ if ($desiredCount < $attachedCount) {
|
|
|
+ $article = $attachedSkus->firstWhere('product_id', $productId)?->product?->article ?? ('ID ' . $productId);
|
|
|
+ $errors[] = "Нельзя удалить привязанные МАФ {$article}: привязано {$attachedCount} шт.";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Если есть хотя бы один привязанный МАФ, запрещаем добавлять новые позиции/количество.
|
|
|
+ if (!empty($attachedByProduct)) {
|
|
|
+ foreach ($desiredCounts as $productId => $desiredCount) {
|
|
|
+ $currentCount = $currentCounts[$productId] ?? 0;
|
|
|
+ if ($desiredCount > $currentCount) {
|
|
|
+ $errors[] = 'Нельзя добавлять новые позиции МАФ, пока на площадке есть привязанные МАФ.';
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($errors)) {
|
|
|
+ return redirect()->back()->withInput()->with(['danger' => $errors]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Удаляем только непривязанные SKU и пересоздаём их по новому списку.
|
|
|
+ ProductSKU::query()
|
|
|
+ ->where('order_id', $order->id)
|
|
|
+ ->whereNull('maf_order_id')
|
|
|
+ ->delete();
|
|
|
+
|
|
|
+ foreach ($desiredCounts as $productId => $desiredCount) {
|
|
|
+ $attachedCount = $attachedByProduct[$productId] ?? 0;
|
|
|
+ $toCreate = $desiredCount - $attachedCount;
|
|
|
+
|
|
|
+ for ($i = 0; $i < $toCreate; $i++) {
|
|
|
ProductSKU::query()->create([
|
|
|
'order_id' => $order->id,
|
|
|
- 'product_id' => $product,
|
|
|
+ 'product_id' => $productId,
|
|
|
'status' => 'требуется'
|
|
|
]);
|
|
|
}
|
|
|
@@ -228,23 +295,64 @@ class OrderController extends Controller
|
|
|
*/
|
|
|
public function getMafToOrder(Order $order)
|
|
|
{
|
|
|
- foreach ($order->products_sku as $product_sku) {
|
|
|
- // dont connect maf to order if already connected
|
|
|
- if($product_sku->maf_order) {
|
|
|
- continue;
|
|
|
+ $attached = 0;
|
|
|
+ $missingByProduct = [];
|
|
|
+
|
|
|
+ DB::transaction(function () use ($order, &$attached, &$missingByProduct) {
|
|
|
+ foreach ($order->products_sku as $productSku) {
|
|
|
+ // Уже привязан, пропускаем
|
|
|
+ if ($productSku->maf_order_id) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $mafOrder = MafOrder::query()
|
|
|
+ ->where('product_id', $productSku->product_id)
|
|
|
+ ->where('in_stock', '>', 0)
|
|
|
+ ->orderBy('created_at')
|
|
|
+ ->lockForUpdate()
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if (!$mafOrder) {
|
|
|
+ $article = $productSku->product?->article ?? ('ID ' . $productSku->product_id);
|
|
|
+ if (!isset($missingByProduct[$productSku->product_id])) {
|
|
|
+ $missingByProduct[$productSku->product_id] = ['article' => $article, 'count' => 0];
|
|
|
+ }
|
|
|
+ $missingByProduct[$productSku->product_id]['count']++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $productSku->update(['maf_order_id' => $mafOrder->id, 'status' => 'отгружен']);
|
|
|
+ $mafOrder->decrement('in_stock');
|
|
|
+ $attached++;
|
|
|
}
|
|
|
+ });
|
|
|
|
|
|
- $mafOrder = MafOrder::query()
|
|
|
- ->where('product_id', $product_sku->product_id)
|
|
|
- ->where('in_stock', '>' , 0)
|
|
|
- ->orderBy('created_at')
|
|
|
- ->first();
|
|
|
- $product_sku->update(['maf_order_id' => $mafOrder->id, 'status' => 'отгружен']);
|
|
|
- $mafOrder->decrement('in_stock');
|
|
|
- unset($mafOrder, $product_sku);
|
|
|
- }
|
|
|
$order->autoChangeStatus();
|
|
|
- return redirect()->route('order.show', $order);
|
|
|
+
|
|
|
+ $success = [];
|
|
|
+ $danger = [];
|
|
|
+
|
|
|
+ if ($attached > 0) {
|
|
|
+ $success[] = "Привязано МАФ: {$attached}.";
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($missingByProduct as $missing) {
|
|
|
+ $danger[] = "Не удалось привязать {$missing['count']} шт. МАФ {$missing['article']}: нет остатка на складе.";
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($attached === 0 && empty($danger)) {
|
|
|
+ $danger[] = 'Нет МАФ для привязки: все МАФы на площадке уже привязаны.';
|
|
|
+ }
|
|
|
+
|
|
|
+ $flash = [];
|
|
|
+ if (!empty($success)) {
|
|
|
+ $flash['success'] = $success;
|
|
|
+ }
|
|
|
+ if (!empty($danger)) {
|
|
|
+ $flash['danger'] = $danger;
|
|
|
+ }
|
|
|
+
|
|
|
+ return redirect()->route('order.show', $order)->with($flash);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -260,12 +368,56 @@ class OrderController extends Controller
|
|
|
|
|
|
public function revertMaf(Order $order)
|
|
|
{
|
|
|
- foreach ($order->products_sku as $maf) {
|
|
|
- MafOrder::query()->where('id', $maf->maf_order_id)->increment('in_stock');
|
|
|
- $maf->update(['maf_order_id' => null, 'status' => 'требуется']);
|
|
|
+ $detached = 0;
|
|
|
+ $notFoundMafOrders = 0;
|
|
|
+
|
|
|
+ DB::transaction(function () use ($order, &$detached, &$notFoundMafOrders) {
|
|
|
+ foreach ($order->products_sku as $maf) {
|
|
|
+ if (!$maf->maf_order_id) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $affectedRows = MafOrder::query()
|
|
|
+ ->where('id', $maf->maf_order_id)
|
|
|
+ ->lockForUpdate()
|
|
|
+ ->increment('in_stock');
|
|
|
+
|
|
|
+ if (!$affectedRows) {
|
|
|
+ $notFoundMafOrders++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ $maf->update(['maf_order_id' => null, 'status' => 'требуется']);
|
|
|
+ $detached++;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ $order->autoChangeStatus();
|
|
|
+
|
|
|
+ $success = [];
|
|
|
+ $danger = [];
|
|
|
+
|
|
|
+ if ($detached > 0) {
|
|
|
+ $success[] = "Отвязано МАФ: {$detached}.";
|
|
|
}
|
|
|
|
|
|
- return redirect()->route('order.show', $order);
|
|
|
+ if ($notFoundMafOrders > 0) {
|
|
|
+ $danger[] = "Не удалось отвязать {$notFoundMafOrders} шт. МАФ: связанный заказ МАФ не найден.";
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($detached === 0 && empty($danger)) {
|
|
|
+ $danger[] = 'Нет МАФ для отвязки: на площадке нет привязанных МАФ.';
|
|
|
+ }
|
|
|
+
|
|
|
+ $flash = [];
|
|
|
+ if (!empty($success)) {
|
|
|
+ $flash['success'] = $success;
|
|
|
+ }
|
|
|
+ if (!empty($danger)) {
|
|
|
+ $flash['danger'] = $danger;
|
|
|
+ }
|
|
|
+
|
|
|
+ return redirect()->route('order.show', $order)->with($flash);
|
|
|
}
|
|
|
|
|
|
public function moveMaf(Request $request)
|