authCustomer(); app(ConditionalOrderService::class)->assertCustomerCanUse($customer); $items = ConditionalOrder::query() ->with('product') ->where('customer_id', $customer->id) ->orderByDesc('id') ->get(); return response()->json($items); } public function store(Request $request) { $customer = $this->authCustomer(); $service = app(ConditionalOrderService::class); $service->assertCustomerCanUse($customer); $service->assertWithinLimit($customer); $data = $this->validatedPayload($request); $data['customer_id'] = $customer->id; $fee = 0; DB::beginTransaction(); try { $fee = $service->chargeRegistrationFee($customer); $order = ConditionalOrder::create([ ...$data, 'is_active' => $data['is_active'] ?? true, 'status' => ConditionalOrder::STATUS_PENDING, 'fee_charged' => $fee, ]); DB::commit(); } catch (\Throwable $e) { DB::rollBack(); throw $e; } return response()->json($order->load('product'), 201); } public function update(Request $request, int $id) { $customer = $this->authCustomer(); app(ConditionalOrderService::class)->assertCustomerCanUse($customer); $order = ConditionalOrder::query() ->where('customer_id', $customer->id) ->findOrFail($id); if ($order->status !== ConditionalOrder::STATUS_PENDING) { throw new UnprocessableEntityHttpException('فقط سفارش در انتظار قابل ویرایش است.'); } $data = $this->validatedPayload($request, true); unset($data['customer_id']); $order->update($data); return response()->json($order->fresh()->load('product')); } public function destroy(int $id) { $customer = $this->authCustomer(); app(ConditionalOrderService::class)->assertCustomerCanUse($customer); $order = ConditionalOrder::query() ->where('customer_id', $customer->id) ->findOrFail($id); $order->delete(); return response()->json(true); } public function toggleActive(int $id) { $customer = $this->authCustomer(); app(ConditionalOrderService::class)->assertCustomerCanUse($customer); $order = ConditionalOrder::query() ->where('customer_id', $customer->id) ->findOrFail($id); if ($order->status !== ConditionalOrder::STATUS_PENDING) { throw new UnprocessableEntityHttpException('فقط سفارش در انتظار قابل فعال/غیرفعال‌سازی است.'); } $order->is_active = ! $order->is_active; $order->save(); return response()->json(['is_active' => (bool) $order->is_active]); } private function authCustomer(): Customer { $customer = auth('customer')->user(); if (! $customer) { throw new UnprocessableEntityHttpException('احراز هویت نامعتبر است.'); } return $customer; } private function validatedPayload(Request $request, bool $partial = false): array { $rules = [ 'product_id' => ($partial ? 'sometimes|' : '') . 'required|integer|exists:products,id', 'direction' => ($partial ? 'sometimes|' : '') . 'required|integer|in:1,2', 'price_condition' => ($partial ? 'sometimes|' : '') . 'required|in:gte,lte', 'trigger_price' => ($partial ? 'sometimes|' : '') . 'required|numeric|min:1', 'amount' => 'nullable|numeric|min:0', 'number' => 'nullable|numeric|min:0', 'expires_at' => 'nullable|date', 'description' => 'nullable|string|max:2000', 'is_active' => 'sometimes|boolean', ]; $data = $request->validate( $rules, ConditionalOrderValidation::messages(), ConditionalOrderValidation::attributes() ); $amount = isset($data['amount']) ? (float) $data['amount'] : null; $number = isset($data['number']) ? (float) $data['number'] : null; if (! $partial && ($amount === null || $amount <= 0) && ($number === null || $number <= 0)) { throw new UnprocessableEntityHttpException('مقدار گرم یا تعداد را وارد کنید.'); } if (! empty($data['expires_at'])) { try { $expires = \Illuminate\Support\Carbon::parse( $data['expires_at'], config('app.timezone') )->timezone(config('app.timezone')); } catch (\Throwable $e) { throw new UnprocessableEntityHttpException('زمان اعتبار تاریخ معتبری نیست.'); } if ($expires->lte(now())) { throw new UnprocessableEntityHttpException('زمان اعتبار باید بعد از تایم فعلی باشد.'); } // Wall-clock in app TZ; column is DATETIME (not TIMESTAMP) to avoid double TZ shift. $data['expires_at'] = $expires->format('Y-m-d H:i:s'); } $productId = (int) ($data['product_id'] ?? $request->input('product_id') ?? 0); $product = $productId > 0 ? Product::find($productId) : null; if ($product && $amount !== null && $amount > 0 && ($number === null || $number <= 0)) { $group = trim((string) ($product->group_type ?? '')); $isMelted = in_array($group, ['آبشده', 'کاتد مس'], true) || (bool) ($product->trade_by_quantity ?? false) || ((int) $product->id === (int) app(ConfigService::class)->mainProductId()); if (! $isMelted && (float) ($product->amount ?? 0) > 0) { $data['number'] = $amount; $data['amount'] = null; } } return $data; } }