migrate to gtea from bistbucket
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Addon\AddonStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Addon\AddonUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\AddonRepository;
|
||||
|
||||
class AddonController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private AddonRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Addon has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(AddonStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Addon has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Addon has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(AddonUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Addon has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Addon has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\AddonFood\AddonFoodStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\AddonFood\AddonFoodUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\AddonFoodRepository;
|
||||
|
||||
class AddonFoodController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private AddonFoodRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'AddonFood has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(AddonFoodStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'AddonFood has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'AddonFood has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(AddonFoodUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'AddonFood has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'AddonFood has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\CustomerAddress\CustomerAddressStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\CustomerAddress\CustomerAddressUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\CustomerAddressRepository;
|
||||
|
||||
class CustomerAddressController extends Controller
|
||||
{
|
||||
public function __construct(private CustomerAddressRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'CustomerAddress has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(CustomerAddressStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'CustomerAddress has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'CustomerAddress has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(CustomerAddressUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'CustomerAddress has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'CustomerAddress has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Customer\CustomerStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Customer\CustomerUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\CustomerRepository;
|
||||
|
||||
class CustomerController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private CustomerRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Customer has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(CustomerStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Customer has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Customer has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(CustomerUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Customer has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Customer has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Models\Customer;
|
||||
use Modules\Restaurant\Models\Ingredient;
|
||||
use Modules\Restaurant\Models\Order;
|
||||
use Modules\Restaurant\Models\OrderItem;
|
||||
use Modules\Restaurant\Models\Reservation;
|
||||
use Modules\Restaurant\Models\ReservationUnavailable;
|
||||
use Modules\Restaurant\Models\Stock;
|
||||
use Modules\Restaurant\Models\Supplier;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$restaurantId = $request->restaurant_id ?? getUserRestaurantId();
|
||||
|
||||
// 🗓 Convert date filters properly
|
||||
$start = $request->filled('start_date') ? date('Y-m-d', strtotime($request->start_date)) : Carbon::now()->startOfMonth()->toDateString();
|
||||
$end = $request->filled('end_date') ? date('Y-m-d', strtotime($request->end_date)) : Carbon::now()->toDateString();
|
||||
|
||||
/** =====================
|
||||
* 🧾 1. Order & Sales Stats
|
||||
* ===================== */
|
||||
$orderQuery = Order::query()
|
||||
->with(['customer:id,name', 'waiter:id,first_name'])
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->whereBetween(DB::raw('DATE(order_date)'), [$start, $end]);
|
||||
|
||||
$totalOrders = (clone $orderQuery)->count();
|
||||
$totalSalesAmount = (clone $orderQuery)->sum('grand_total');
|
||||
$completedOrders = (clone $orderQuery)->where('status', 'completed')->count();
|
||||
$pendingOrders = (clone $orderQuery)->where('status', 'pending')->count();
|
||||
|
||||
/** =====================
|
||||
* 🍽️ 2. Latest Orders (5)
|
||||
* ===================== */
|
||||
$latestOrders = (clone $orderQuery)
|
||||
->latest('id')
|
||||
->take(5)
|
||||
->get([
|
||||
'id',
|
||||
'order_number',
|
||||
'grand_total',
|
||||
'status',
|
||||
'order_date',
|
||||
'customer_id',
|
||||
'waiter_id',
|
||||
])
|
||||
->map(function ($order) {
|
||||
return [
|
||||
'id' => $order->id,
|
||||
'order_number' => $order->order_number,
|
||||
'grand_total' => $order->grand_total,
|
||||
'status' => $order->status,
|
||||
'order_date' => $order->order_date,
|
||||
'customer_name' => $order->customer?->name,
|
||||
'waiter_name' => $order->waiter?->first_name,
|
||||
];
|
||||
});
|
||||
|
||||
/** =====================
|
||||
* 📦 3. Ingredients & Stock
|
||||
* ===================== */
|
||||
$totalIngredients = Ingredient::where('restaurant_id', $restaurantId)->count();
|
||||
$lowStockIngredients = Ingredient::where('restaurant_id', $restaurantId)
|
||||
->whereColumn('stock_quantity', '<=', 'low_stock_alert')
|
||||
->count();
|
||||
|
||||
$totalStockValue = Ingredient::where('restaurant_id', $restaurantId)
|
||||
->sum(DB::raw('stock_quantity * cost_per_unit'));
|
||||
|
||||
/** =====================
|
||||
* 🧍 4. Customers & Suppliers
|
||||
* ===================== */
|
||||
$totalCustomers = Customer::when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))->count();
|
||||
$totalSuppliers = Supplier::when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))->count();
|
||||
|
||||
/** =====================
|
||||
* 📅 5. Reservations
|
||||
* ===================== */
|
||||
$latestReservations = Reservation::with(['customer:id,name,phone', 'table:id,name'])
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->latest('id')
|
||||
->take(5)
|
||||
->get(['id', 'customer_id', 'table_id', 'reservation_date', 'start_time', 'status'])
|
||||
->map(function ($item) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'customer_id' => $item->customer_id,
|
||||
'customer_name' => $item->customer?->name,
|
||||
'customer_phone' => $item->customer?->phone,
|
||||
'table_name' => $item->table?->name,
|
||||
'reservation_date' => $item->reservation_date,
|
||||
'start_time' => $item->start_time,
|
||||
'status' => $item->status,
|
||||
];
|
||||
});
|
||||
|
||||
$latestUnavailable = ReservationUnavailable::with('table:id,name')
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->latest('id')
|
||||
->take(5)
|
||||
->get(['id', 'table_id', 'date', 'start_time', 'end_time', 'reason'])
|
||||
->map(function ($item) {
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'table_name' => $item->table?->name,
|
||||
'date' => $item->date,
|
||||
'start_time' => $item->start_time,
|
||||
'end_time' => $item->end_time,
|
||||
'reason' => $item->reason,
|
||||
];
|
||||
});
|
||||
|
||||
/** =====================
|
||||
* 📊 6. Stocks (movement summary)
|
||||
* ===================== */
|
||||
$stockSummary = Stock::select(
|
||||
'type',
|
||||
DB::raw('SUM(quantity) as total_qty'),
|
||||
DB::raw('SUM(total_cost) as total_cost')
|
||||
)
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->whereBetween(DB::raw('DATE(created_at)'), [$start, $end])
|
||||
->groupBy('type')
|
||||
->get()
|
||||
->keyBy('type');
|
||||
|
||||
/** =====================
|
||||
* 💹 7. Sales & Purchase Comparison
|
||||
* ===================== */
|
||||
$salesItems = OrderItem::when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->whereBetween(DB::raw('DATE(created_at)'), [$start, $end])
|
||||
->select(DB::raw('SUM(quantity) as total_qty'), DB::raw('SUM(total) as total_sales'))
|
||||
->first();
|
||||
|
||||
/** =====================
|
||||
* 📈 8. Orders Chart Data (daily, weekly, monthly)
|
||||
* ===================== */
|
||||
// --- Daily orders (last 30 days) ---
|
||||
$dailyOrders = Order::select(
|
||||
DB::raw('DATE(order_date) as date'),
|
||||
DB::raw('COUNT(*) as total')
|
||||
)
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
// ->where('status', 'completed')
|
||||
->where('order_date', '>=', now()->subDays(30))
|
||||
->groupBy(DB::raw('DATE(order_date)'))
|
||||
->orderBy('date')
|
||||
->get();
|
||||
|
||||
// --- Weekly orders (last 7 days) ---
|
||||
$weeklyOrders = Order::select(
|
||||
DB::raw('DATE(order_date) as date'),
|
||||
DB::raw('COUNT(*) as total')
|
||||
)
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
// ->where('status', 'completed')
|
||||
->where('order_date', '>=', now()->subDays(7))
|
||||
->groupBy(DB::raw('DATE(order_date)'))
|
||||
->orderBy('date')
|
||||
->get();
|
||||
|
||||
// --- Monthly orders (last 12 months, always show all months) ---
|
||||
$monthlyOrdersRaw = Order::select(
|
||||
DB::raw('DATE_FORMAT(order_date, "%Y-%m") as month'),
|
||||
DB::raw('COUNT(*) as total')
|
||||
)
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->where('order_date', '>=', now()->subMonths(12))
|
||||
->groupBy(DB::raw('DATE_FORMAT(order_date, "%Y-%m")'))
|
||||
->orderBy('month')
|
||||
->pluck('total', 'month'); // → key = month (YYYY-MM), value = total count
|
||||
|
||||
// --- Build full 12-month list ---
|
||||
$months = collect(range(0, 11))
|
||||
->map(function ($i) use ($monthlyOrdersRaw) {
|
||||
$date = Carbon::now()->subMonths(11 - $i);
|
||||
$monthKey = $date->format('Y-m');
|
||||
|
||||
return [
|
||||
'month' => $date->format('F Y'), // Example: "Octobor 2025"
|
||||
'total' => $monthlyOrdersRaw[$monthKey] ?? 0,
|
||||
];
|
||||
});
|
||||
|
||||
/** =====================
|
||||
* 📈 9. Final Dashboard Response
|
||||
* ===================== */
|
||||
return $this->responseSuccess([
|
||||
'filters' => [
|
||||
'start_date' => $start,
|
||||
'end_date' => $end,
|
||||
'restaurant_id' => $restaurantId,
|
||||
],
|
||||
'stats' => [
|
||||
'orders' => [
|
||||
'total' => $totalOrders,
|
||||
'completed' => $completedOrders,
|
||||
'pending' => $pendingOrders,
|
||||
'sales_amount' => round($totalSalesAmount, 2),
|
||||
],
|
||||
'ingredients' => [
|
||||
'total' => $totalIngredients,
|
||||
'low_stock' => $lowStockIngredients,
|
||||
'stock_value' => round($totalStockValue, 2),
|
||||
],
|
||||
'customers' => $totalCustomers,
|
||||
'suppliers' => $totalSuppliers,
|
||||
],
|
||||
'stock_summary' => $stockSummary,
|
||||
'latest_orders' => $latestOrders,
|
||||
'latest_reservations' => $latestReservations,
|
||||
'latest_unavailable_tables' => $latestUnavailable,
|
||||
'sales_summary' => [
|
||||
'total_items_sold' => (int) ($salesItems->total_qty ?? 0),
|
||||
'total_sales' => round($salesItems->total_sales ?? 0, 2),
|
||||
],
|
||||
'charts' => [
|
||||
'daily_orders' => $dailyOrders,
|
||||
'weekly_orders' => $weeklyOrders,
|
||||
'monthly_orders' => $months,
|
||||
],
|
||||
], 'Dashboard data fetched successfully.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\DeliveryCharge\DeliveryChargeStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\DeliveryCharge\DeliveryChargeUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\DeliveryChargeRepository;
|
||||
|
||||
class DeliveryChargeController extends Controller
|
||||
{
|
||||
public function __construct(private DeliveryChargeRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'DeliveryCharge has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(DeliveryChargeStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'DeliveryCharge has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'DeliveryCharge has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(DeliveryChargeUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'DeliveryCharge has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'DeliveryCharge has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Http\Requests\FoodAvailability\FoodAvailabilityStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\FoodAvailability\FoodAvailabilityUpdateRequest;
|
||||
use Modules\Restaurant\Models\FoodAvailability;
|
||||
|
||||
class FoodAvailabilityController extends Controller
|
||||
{
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$perPage = $request->input('perPage', 15);
|
||||
$sortBy = $request->input('sortBy', 'id');
|
||||
$sortDir = $request->input('sortDir', 'desc');
|
||||
|
||||
$search = $request->input('search');
|
||||
$foodItemId = $request->input('food_item_id');
|
||||
$day = $request->input('day'); // Saturday, Sunday etc.
|
||||
$startDate = $request->input('start_date');
|
||||
$endDate = $request->input('end_date');
|
||||
|
||||
$query = FoodAvailability::query()
|
||||
->with('foodItem:id,name')
|
||||
->with('times:id,food_availability_id,open_time,close_time')
|
||||
->where('restaurant_id', getUserRestaurantId());
|
||||
|
||||
// 🔍 SEARCH Filter
|
||||
if (! empty($search)) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('day', 'LIKE', "%{$search}%")
|
||||
->orWhereHas('foodItem', function ($fi) use ($search) {
|
||||
$fi->where('name', 'LIKE', "%{$search}%");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 🍽 Filter by food item
|
||||
if (! empty($foodItemId)) {
|
||||
$query->where('food_item_id', $foodItemId);
|
||||
}
|
||||
|
||||
// 📅 Filter by specific day
|
||||
if (! empty($day)) {
|
||||
$query->where('day', $day);
|
||||
}
|
||||
|
||||
// 📆 Filter by date range (start_date & end_date)
|
||||
if (! empty($startDate) && ! empty($endDate)) {
|
||||
$query->whereBetween('created_at', [$startDate, $endDate]);
|
||||
}
|
||||
|
||||
// 🔽 Sorting
|
||||
$query->orderBy($sortBy, $sortDir);
|
||||
|
||||
// 📄 Pagination
|
||||
$data = $query->paginate($perPage)->appends($request->query());
|
||||
|
||||
return $this->responseSuccess($data, 'Availability list fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FoodAvailabilityStoreRequest $request): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($request->availability as $item) {
|
||||
|
||||
$availability = FoodAvailability::updateOrCreate(
|
||||
[
|
||||
'restaurant_id' => getUserRestaurantId(),
|
||||
'food_item_id' => $request->food_item_id,
|
||||
'day' => $item['day'],
|
||||
],
|
||||
[
|
||||
'is_available' => true,
|
||||
]
|
||||
);
|
||||
|
||||
// Remove old times for fresh update
|
||||
$availability->times()->delete();
|
||||
|
||||
foreach ($item['times'] as $slot) {
|
||||
$availability->times()->create([
|
||||
'open_time' => $slot['open_time'],
|
||||
'close_time' => $slot['close_time'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'Food availability created successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$data = FoodAvailability::with('times')->findOrFail($id);
|
||||
|
||||
return $this->responseSuccess($data, 'Availability fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(FoodAvailabilityUpdateRequest $request, $foodItemId): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
|
||||
foreach ($request->availability as $item) {
|
||||
|
||||
// Find existing day or create new
|
||||
$availability = FoodAvailability::firstOrCreate(
|
||||
[
|
||||
'restaurant_id' => $restaurantId,
|
||||
'food_item_id' => $foodItemId,
|
||||
'day' => $item['day'],
|
||||
],
|
||||
['is_available' => true]
|
||||
);
|
||||
|
||||
// Current time slots in DB
|
||||
$existingTimes = $availability->times()->get();
|
||||
|
||||
$newTimes = collect($item['times']);
|
||||
|
||||
// Delete removed slots
|
||||
$existingTimes->each(function ($slot) use ($newTimes) {
|
||||
if (! $newTimes->contains(fn ($t) => $t['open_time'] == $slot->open_time && $t['close_time'] == $slot->close_time)) {
|
||||
$slot->delete();
|
||||
}
|
||||
});
|
||||
|
||||
// Add or update new slots
|
||||
foreach ($item['times'] as $slot) {
|
||||
// Use food_availability_id in the condition to prevent duplicates
|
||||
$availability->times()->updateOrCreate(
|
||||
[
|
||||
'food_availability_id' => $availability->id,
|
||||
'open_time' => $slot['open_time'],
|
||||
'close_time' => $slot['close_time'],
|
||||
],
|
||||
[
|
||||
'open_time' => $slot['open_time'],
|
||||
'close_time' => $slot['close_time'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'Food availability updated successfully (attach/detach).');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$item = FoodAvailability::findOrFail($id);
|
||||
|
||||
$item->times()->delete();
|
||||
$item->delete();
|
||||
|
||||
return $this->responseSuccess([], 'Availability deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\FoodCategory\FoodCategoryStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\FoodCategory\FoodCategoryUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\FoodCategoryRepository;
|
||||
|
||||
class FoodCategoryController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private FoodCategoryRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'FoodCategory has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FoodCategoryStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'FoodCategory has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'FoodCategory has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(FoodCategoryUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'FoodCategory has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'FoodCategory has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Restaurant\Http\Requests\FoodItem\FoodItemStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\FoodItem\FoodItemUpdateRequest;
|
||||
use Modules\Restaurant\Models\FoodItem;
|
||||
use Modules\Restaurant\Repositories\FoodItemRepository;
|
||||
|
||||
class FoodItemController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private FoodItemRepository $repo) {}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
// Base query with eager loading
|
||||
$query = FoodItem::with([
|
||||
'category',
|
||||
'menuCategory',
|
||||
'menuSection',
|
||||
'variants',
|
||||
'defaultVariant',
|
||||
'availabilities',
|
||||
'addons',
|
||||
]);
|
||||
|
||||
// Optional: Search by name or description
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->get('search');
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('description', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// Optional: Filter by category
|
||||
if ($request->filled('category_id')) {
|
||||
$query->where('category_id', $request->get('category_id'));
|
||||
}
|
||||
|
||||
// Optional: Filter by restaurant
|
||||
if ($request->filled('restaurant_id')) {
|
||||
$query->where('restaurant_id', $request->get('restaurant_id'));
|
||||
}
|
||||
|
||||
// Optional: Filter by food type (e.g. veg/non-veg)
|
||||
if ($request->filled('food_type')) {
|
||||
$query->where('food_type', $request->get('food_type'));
|
||||
}
|
||||
|
||||
// Optional: Filter by is_party / is_dinner / is_lunch
|
||||
foreach (['is_party', 'is_dinner', 'is_lunch', 'is_chef_special', 'is_popular_item'] as $flag) {
|
||||
if ($request->filled($flag)) {
|
||||
$query->where($flag, $request->boolean($flag));
|
||||
}
|
||||
}
|
||||
|
||||
// Pagination: default 20 per page
|
||||
$perPage = $request->get('perPage', 20);
|
||||
|
||||
$results = $query->latest()->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($results, 'Food items fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FoodItemStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'FoodItem has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'FoodItem has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(FoodItemUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'FoodItem has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'FoodItem has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Restaurant\Http\Requests\FoodReview\FoodReviewStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\FoodReview\FoodReviewUpdateRequest;
|
||||
use Modules\Restaurant\Models\FoodReview;
|
||||
|
||||
class FoodReviewController extends Controller
|
||||
{
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
|
||||
$perPage = $request->get('per_page', 10);
|
||||
|
||||
$reviews = FoodReview::with([
|
||||
'replies' => function ($q) {
|
||||
$q->where('status', 1)->latest();
|
||||
},
|
||||
])
|
||||
->where('restaurant_id', $restaurantId)
|
||||
->orderBy('is_featured', 'desc')
|
||||
->orderBy('position', 'asc')
|
||||
->latest()
|
||||
->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($reviews, 'Food reviews fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FoodReviewStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
$customerId = getUserId();
|
||||
|
||||
$exists = FoodReview::where([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'food_item_id' => $request->food_item_id,
|
||||
'customer_id' => $customerId,
|
||||
])->exists();
|
||||
|
||||
if ($exists) {
|
||||
return $this->responseError([], 'You have already reviewed this food.');
|
||||
}
|
||||
|
||||
$data = $request->only(['rating', 'review', 'video_url']);
|
||||
$data['restaurant_id'] = $restaurantId;
|
||||
$data['food_item_id'] = $request->food_item_id;
|
||||
$data['customer_id'] = $customerId;
|
||||
$data['status'] = 'pending';
|
||||
|
||||
/** IMAGE UPLOAD (JSON) */
|
||||
if ($request->hasFile('images')) {
|
||||
$images = [];
|
||||
foreach ($request->file('images') as $img) {
|
||||
$images[] = fileUploader('food_reviews/images/', 'png', $img);
|
||||
}
|
||||
$data['images'] = $images;
|
||||
}
|
||||
|
||||
/** VIDEO FILE */
|
||||
if ($request->hasFile('video_file')) {
|
||||
$data['video_file'] = fileUploader(
|
||||
'food_reviews/videos/',
|
||||
'mp4',
|
||||
$request->file('video_file')
|
||||
);
|
||||
}
|
||||
|
||||
$review = FoodReview::create($data);
|
||||
|
||||
return $this->responseSuccess($review, 'Review submitted successfully. Awaiting approval.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show($id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
|
||||
$review = FoodReview::with([
|
||||
'replies' => function ($q) {
|
||||
$q->where('status', 1)->latest();
|
||||
},
|
||||
])
|
||||
->where('restaurant_id', $restaurantId)
|
||||
->findOrFail($id);
|
||||
|
||||
return $this->responseSuccess($review, 'Food review fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(FoodReviewUpdateRequest $request, $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
$customerId = getUserId();
|
||||
|
||||
$review = FoodReview::where([
|
||||
'id' => $id,
|
||||
'restaurant_id' => $restaurantId,
|
||||
'customer_id' => $customerId,
|
||||
])->firstOrFail();
|
||||
|
||||
if ($review->status === 'approved') {
|
||||
return $this->responseError([], 'Approved reviews cannot be edited.');
|
||||
}
|
||||
|
||||
$data = $request->only(['rating', 'review', 'video_url']);
|
||||
|
||||
/** IMAGE JSON UPDATE */
|
||||
$images = $review->images ?? [];
|
||||
|
||||
// Remove selected images
|
||||
if ($request->filled('remove_images')) {
|
||||
foreach ($request->remove_images as $removeImg) {
|
||||
if (in_array($removeImg, $images)) {
|
||||
fileRemover('food_reviews/images/', $removeImg);
|
||||
$images = array_values(array_diff($images, [$removeImg]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new images
|
||||
if ($request->hasFile('images')) {
|
||||
foreach ($request->file('images') as $img) {
|
||||
$images[] = fileUploader('food_reviews/images/', 'png', $img);
|
||||
}
|
||||
}
|
||||
|
||||
$data['images'] = $images;
|
||||
|
||||
/** VIDEO FILE UPDATE */
|
||||
if ($request->hasFile('video_file')) {
|
||||
$data['video_file'] = fileUploader(
|
||||
'food_reviews/videos/',
|
||||
'mp4',
|
||||
$request->file('video_file'),
|
||||
$review->video_file
|
||||
);
|
||||
}
|
||||
|
||||
$review->update($data);
|
||||
|
||||
return $this->responseSuccess($review, 'Review updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy($id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
$customerId = getUserId();
|
||||
|
||||
$review = FoodReview::where([
|
||||
'id' => $id,
|
||||
'restaurant_id' => $restaurantId,
|
||||
'customer_id' => $customerId,
|
||||
])->firstOrFail();
|
||||
|
||||
if ($review->status === 'approved') {
|
||||
return $this->responseError([], 'Approved reviews cannot be deleted.');
|
||||
}
|
||||
|
||||
// Delete images
|
||||
if (! empty($review->images)) {
|
||||
foreach ($review->images as $img) {
|
||||
fileRemover('food_reviews/images/', $img);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete video
|
||||
if ($review->video_file) {
|
||||
fileRemover('food_reviews/videos/', $review->video_file);
|
||||
}
|
||||
|
||||
$review->delete();
|
||||
|
||||
return $this->responseSuccess([], 'Review deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Restaurant\Http\Requests\FoodReviewReply\FoodReviewReplyStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\FoodReviewReply\FoodReviewReplyUpdateRequest;
|
||||
use Modules\Restaurant\Models\FoodReviewReply;
|
||||
|
||||
class FoodReviewReplyController extends Controller
|
||||
{
|
||||
/**
|
||||
* List replies for a review
|
||||
*/
|
||||
public function index(Request $request, $reviewId): JsonResponse
|
||||
{
|
||||
try {
|
||||
$perPage = $request->get('per_page', 10);
|
||||
|
||||
$replies = FoodReviewReply::where('review_id', $reviewId)
|
||||
->where('status', 1)
|
||||
->latest()
|
||||
->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($replies, 'Replies fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a reply
|
||||
*/
|
||||
public function store(FoodReviewReplyStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
$userId = getUserId();
|
||||
$userType = $request->user_type ?? 'customer'; // default customer
|
||||
|
||||
$data = [
|
||||
'restaurant_id' => $restaurantId,
|
||||
'review_id' => $request->review_id,
|
||||
'user_type' => $userType,
|
||||
'message' => $request->message,
|
||||
];
|
||||
|
||||
// Assign correct ID based on type
|
||||
if ($userType === 'admin') {
|
||||
$data['user_id'] = $userId;
|
||||
} else {
|
||||
$data['customer_id'] = $userId;
|
||||
}
|
||||
|
||||
/** Attachments upload */
|
||||
if ($request->hasFile('attachments')) {
|
||||
$attachments = [];
|
||||
foreach ($request->file('attachments') as $file) {
|
||||
$attachments[] = fileUploader('review_replies/attachments/', 'png', $file);
|
||||
}
|
||||
$data['attachments'] = $attachments;
|
||||
}
|
||||
|
||||
$reply = FoodReviewReply::create($data);
|
||||
|
||||
return $this->responseSuccess($reply, 'Reply created successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show single reply
|
||||
*/
|
||||
public function show($id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$reply = FoodReviewReply::findOrFail($id);
|
||||
|
||||
return $this->responseSuccess($reply, 'Reply fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a reply
|
||||
*/
|
||||
public function update(FoodReviewReplyUpdateRequest $request, $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$userId = getUserId();
|
||||
$reply = FoodReviewReply::findOrFail($id);
|
||||
|
||||
// Permission: Only the owner (admin/moderator or customer) can edit
|
||||
if (($reply->user_type === 'admin' && $reply->user_id !== $userId) ||
|
||||
($reply->user_type === 'customer' && $reply->customer_id !== $userId)
|
||||
) {
|
||||
return $this->responseError([], 'You are not authorized to edit this reply.');
|
||||
}
|
||||
|
||||
$data = $request->only(['message']);
|
||||
|
||||
$attachments = $reply->attachments ?? [];
|
||||
|
||||
// Remove selected attachments
|
||||
if ($request->filled('remove_attachments')) {
|
||||
foreach ($request->remove_attachments as $removeFile) {
|
||||
if (in_array($removeFile, $attachments)) {
|
||||
fileRemover('review_replies/attachments/', $removeFile);
|
||||
$attachments = array_values(array_diff($attachments, [$removeFile]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new attachments
|
||||
if ($request->hasFile('attachments')) {
|
||||
foreach ($request->file('attachments') as $file) {
|
||||
$attachments[] = fileUploader('review_replies/attachments/', 'png', $file);
|
||||
}
|
||||
}
|
||||
|
||||
$data['attachments'] = $attachments;
|
||||
|
||||
$reply->update($data);
|
||||
|
||||
return $this->responseSuccess($reply, 'Reply updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a reply
|
||||
*/
|
||||
public function destroy($id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$userId = getUserId();
|
||||
$reply = FoodReviewReply::findOrFail($id);
|
||||
|
||||
// Permission: Only owner can delete
|
||||
if (($reply->user_type === 'admin' && $reply->user_id !== $userId) ||
|
||||
($reply->user_type === 'customer' && $reply->customer_id !== $userId)
|
||||
) {
|
||||
return $this->responseError([], 'You are not authorized to delete this reply.');
|
||||
}
|
||||
|
||||
// Delete attachments
|
||||
if (! empty($reply->attachments)) {
|
||||
foreach ($reply->attachments as $file) {
|
||||
fileRemover('review_replies/attachments/', $file);
|
||||
}
|
||||
}
|
||||
|
||||
$reply->delete();
|
||||
|
||||
return $this->responseSuccess([], 'Reply deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\FoodVariant\FoodVariantStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\FoodVariant\FoodVariantUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\FoodVariantRepository;
|
||||
|
||||
class FoodVariantController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private FoodVariantRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'FoodVariant has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FoodVariantStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'FoodVariant has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'FoodVariant has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(FoodVariantUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'FoodVariant has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'FoodVariant has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Restaurant\Http\Requests\FoodVariantIngredient\FoodVariantIngredientStoreRequest;
|
||||
use Modules\Restaurant\Models\FoodVariant;
|
||||
use Modules\Restaurant\Models\FoodVariantIngredient;
|
||||
|
||||
class FoodVariantIngredientController extends Controller
|
||||
{
|
||||
use Authenticatable;
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
try {
|
||||
$perPage = $request->get('perPage', 10);
|
||||
|
||||
$variants = FoodVariant::with(['ingredients.ingredient'])
|
||||
->when($request->search, fn ($q) => $q->where('name', 'like', "%{$request->search}%"))
|
||||
->whereHas('ingredients') // only variants that have ingredients
|
||||
->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess([
|
||||
'current_page' => $variants->currentPage(),
|
||||
'data' => $variants->map(function ($variant) {
|
||||
return [
|
||||
'variant_id' => $variant->id,
|
||||
'variant_name' => $variant->name,
|
||||
'ingredients' => $variant->ingredients->map(function ($fvIng) {
|
||||
return [
|
||||
'id' => $fvIng->ingredient->id ?? null,
|
||||
'ingredient_name' => $fvIng->ingredient->name ?? null,
|
||||
'quantity' => $fvIng->quantity,
|
||||
'wastage_percentage' => $fvIng->wastage_percentage,
|
||||
'unit_conversion_factor' => $fvIng->unit_conversion_factor,
|
||||
'optional' => $fvIng->optional,
|
||||
'notes' => $fvIng->notes,
|
||||
'status' => $fvIng->status,
|
||||
];
|
||||
}),
|
||||
];
|
||||
}),
|
||||
'first_page_url' => $variants->url(1),
|
||||
'from' => $variants->firstItem(),
|
||||
'last_page' => $variants->lastPage(),
|
||||
'last_page_url' => $variants->url($variants->lastPage()),
|
||||
'links' => $variants->linkCollection()->toArray(),
|
||||
'next_page_url' => $variants->nextPageUrl(),
|
||||
'path' => $request->url(),
|
||||
'per_page' => $variants->perPage(),
|
||||
'prev_page_url' => $variants->previousPageUrl(),
|
||||
'to' => $variants->lastItem(),
|
||||
'total' => $variants->total(),
|
||||
], 'Variant list fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FoodVariantIngredientStoreRequest $request)
|
||||
{
|
||||
try {
|
||||
$food_variant_id = $request->food_variant_id;
|
||||
$ingredients = $request->ingredients;
|
||||
$restaurant_id = $this->getCurrentRestaurantId();
|
||||
|
||||
$created = [];
|
||||
foreach ($ingredients as $item) {
|
||||
$ingredient_id = $item['ingredient_id'];
|
||||
|
||||
// Check if this combination already exists
|
||||
$exists = FoodVariantIngredient::where([
|
||||
'restaurant_id' => $restaurant_id,
|
||||
'food_variant_id' => $food_variant_id,
|
||||
'ingredient_id' => $ingredient_id,
|
||||
])->exists();
|
||||
|
||||
if ($exists) {
|
||||
// Skip duplicate
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prepare data
|
||||
$item['restaurant_id'] = $restaurant_id;
|
||||
$item['food_variant_id'] = $food_variant_id;
|
||||
|
||||
$created[] = FoodVariantIngredient::create($item);
|
||||
}
|
||||
|
||||
return $this->responseSuccess([
|
||||
'status' => true,
|
||||
'message' => 'FoodVariantIngredients created successfully (duplicates skipped).',
|
||||
'data' => $created,
|
||||
'errors' => null,
|
||||
], 'Ingredients processed successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([
|
||||
'status' => false,
|
||||
'message' => 'Failed to create.',
|
||||
'data' => null,
|
||||
'errors' => $e->getMessage(),
|
||||
], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
try {
|
||||
$variant = FoodVariant::with(['ingredients.ingredient'])->findOrFail($id);
|
||||
|
||||
$data = [
|
||||
'variant_id' => $variant->id,
|
||||
'variant_name' => $variant->name,
|
||||
'ingredients' => $variant->ingredients->map(function ($fvIng) {
|
||||
return [
|
||||
'id' => $fvIng->ingredient->id ?? null,
|
||||
'ingredient_name' => $fvIng->ingredient->name ?? null,
|
||||
'quantity' => $fvIng->quantity,
|
||||
'wastage_percentage' => $fvIng->wastage_percentage,
|
||||
'unit_conversion_factor' => $fvIng->unit_conversion_factor,
|
||||
'optional' => $fvIng->optional,
|
||||
'notes' => $fvIng->notes,
|
||||
'status' => $fvIng->status,
|
||||
];
|
||||
}),
|
||||
];
|
||||
|
||||
return $this->responseSuccess($data, 'FoodVariant with ingredients fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], 'FoodVariant not found.');
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Update
|
||||
public function update(Request $request, $food_variant_id)
|
||||
{
|
||||
try {
|
||||
$ingredients = $request->ingredients ?? [];
|
||||
|
||||
// Fetch the variant with its current ingredients
|
||||
$variant = FoodVariant::with('ingredients')->findOrFail($food_variant_id);
|
||||
|
||||
$existingIds = $variant->ingredients->pluck('ingredient_id')->toArray();
|
||||
$incomingIds = collect($ingredients)->pluck('ingredient_id')->toArray();
|
||||
|
||||
// Delete removed ingredients
|
||||
$toDelete = array_diff($existingIds, $incomingIds);
|
||||
if (! empty($toDelete)) {
|
||||
FoodVariantIngredient::where('food_variant_id', $food_variant_id)
|
||||
->whereIn('ingredient_id', $toDelete)
|
||||
->delete();
|
||||
}
|
||||
|
||||
$updatedOrCreated = [];
|
||||
// Add or update ingredients
|
||||
foreach ($ingredients as $item) {
|
||||
$item['restaurant_id'] = $this->getCurrentRestaurantId();
|
||||
$item['food_variant_id'] = $food_variant_id;
|
||||
|
||||
$fvIng = FoodVariantIngredient::updateOrCreate(
|
||||
[
|
||||
'food_variant_id' => $food_variant_id,
|
||||
'ingredient_id' => $item['ingredient_id'],
|
||||
],
|
||||
$item
|
||||
);
|
||||
|
||||
$updatedOrCreated[] = $fvIng;
|
||||
}
|
||||
|
||||
// Fetch updated variant data
|
||||
$variant = FoodVariant::with('ingredients.ingredient')->find($food_variant_id);
|
||||
|
||||
$data = [
|
||||
'variant_id' => $variant->id,
|
||||
'variant_name' => $variant->name,
|
||||
'ingredients' => $variant->ingredients->map(function ($fvIng) {
|
||||
return [
|
||||
'id' => $fvIng->ingredient->id ?? null,
|
||||
'ingredient_name' => $fvIng->ingredient->name ?? null,
|
||||
'quantity' => $fvIng->quantity,
|
||||
'wastage_percentage' => $fvIng->wastage_percentage,
|
||||
'unit_conversion_factor' => $fvIng->unit_conversion_factor,
|
||||
'optional' => $fvIng->optional,
|
||||
'notes' => $fvIng->notes,
|
||||
'status' => $fvIng->status,
|
||||
];
|
||||
}),
|
||||
];
|
||||
|
||||
return $this->responseSuccess($data, 'FoodVariant ingredients updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Delete
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
try {
|
||||
$food_variant_id = $request->food_variant_id;
|
||||
$ingredient_id = $request->ingredient_id ?? null;
|
||||
|
||||
if (! $food_variant_id) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Food Variant ID is required.',
|
||||
'data' => null,
|
||||
'errors' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
$query = FoodVariantIngredient::where('food_variant_id', $food_variant_id);
|
||||
|
||||
// If ingredient_id is provided, delete only that ingredient
|
||||
if ($ingredient_id) {
|
||||
$query->where('ingredient_id', $ingredient_id);
|
||||
}
|
||||
|
||||
$deleted = $query->delete();
|
||||
|
||||
if ($deleted) {
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => $ingredient_id ?
|
||||
'Single ingredient removed successfully.' :
|
||||
'All ingredients of the variant removed successfully.',
|
||||
'data' => null,
|
||||
'errors' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'No matching record found.',
|
||||
'data' => null,
|
||||
'errors' => null,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Failed to delete.',
|
||||
'data' => null,
|
||||
'errors' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\FoodWaste\FoodWasteStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\FoodWaste\FoodWasteUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\FoodWasteRepository;
|
||||
|
||||
class FoodWasteController extends Controller
|
||||
{
|
||||
public function __construct(private FoodWasteRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'FoodWaste has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(FoodWasteStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'FoodWaste has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'FoodWaste has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(FoodWasteUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'FoodWaste has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'FoodWaste has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Ingredient\IngredientStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Ingredient\IngredientUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\IngredientRepository;
|
||||
|
||||
class IngredientController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private IngredientRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Ingredient has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(IngredientStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Ingredient has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Ingredient has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(IngredientUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Ingredient has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Ingredient has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Helper\IngredientDamageHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Http\Requests\IngredientDamage\IngredientDamageStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\IngredientDamage\IngredientDamageUpdateRequest;
|
||||
use Modules\Restaurant\Models\IngredientDamage;
|
||||
use Modules\Restaurant\Repositories\IngredientDamageRepository;
|
||||
|
||||
class IngredientDamageController extends Controller
|
||||
{
|
||||
use Authenticatable, RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private IngredientDamageRepository $repo) {}
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$query = IngredientDamage::query()
|
||||
->with([
|
||||
'ingredient:id,name,unit_id',
|
||||
'reportedBy:id,first_name',
|
||||
])
|
||||
->when(
|
||||
$request->restaurant_id,
|
||||
fn ($q) => $q->where('restaurant_id', $request->restaurant_id)
|
||||
);
|
||||
|
||||
// 🔍 Search by ingredient name or reason
|
||||
if ($search = $request->input('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('reason', 'like', "%{$search}%")
|
||||
->orWhereHas('ingredient', fn ($i) => $i->where('name', 'like', "%{$search}%"));
|
||||
});
|
||||
}
|
||||
|
||||
// 🗓️ Filter by date range
|
||||
if ($start = $request->input('start_date') and $end = $request->input('end_date')) {
|
||||
$query->whereBetween(DB::raw('DATE(created_at)'), [$start, $end]);
|
||||
}
|
||||
|
||||
// 🔁 Sorting
|
||||
$sortBy = $request->input('sort_by', 'created_at');
|
||||
$sortOrder = $request->input('sort_order', 'desc');
|
||||
$query->orderBy($sortBy, $sortOrder);
|
||||
|
||||
// 📄 Pagination
|
||||
$perPage = (int) $request->input('perPage', 15);
|
||||
$damageList = $query->paginate($perPage);
|
||||
|
||||
// 📊 Metadata (total damaged qty & value)
|
||||
$meta = [
|
||||
'total_damage_qty' => (float) $query->clone()->sum('quantity'),
|
||||
'total_damage_value' => (float) $query->clone()->sum(DB::raw('quantity * unit_cost')),
|
||||
'total_records' => $damageList->total(),
|
||||
];
|
||||
|
||||
return $this->responseSuccess([
|
||||
'data' => $damageList,
|
||||
'meta' => $meta,
|
||||
], 'IngredientDamage has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(IngredientDamageStoreRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
try {
|
||||
$data['restaurant_id'] = $this->getCurrentRestaurantId();
|
||||
$damage = IngredientDamageHelper::createDamage($data);
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Ingredient damage has been recorded successfully.',
|
||||
'data' => $damage,
|
||||
]);
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Database error: '.$exception->getMessage(),
|
||||
'data' => null,
|
||||
], 500);
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'data' => null,
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess([], 'IngredientDamage has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(IngredientDamageUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess([], 'IngredientDamage has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess([], 'IngredientDamage has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\KitchenAssign\KitchenAssignStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\KitchenAssign\KitchenAssignUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\KitchenAssignRepository;
|
||||
|
||||
class KitchenAssignController extends Controller
|
||||
{
|
||||
public function __construct(private KitchenAssignRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'KitchenAssign has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(KitchenAssignStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'KitchenAssign has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'KitchenAssign has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(KitchenAssignUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'KitchenAssign has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'KitchenAssign has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Kitchen\KitchenStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Kitchen\KitchenUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\KitchenRepository;
|
||||
|
||||
class KitchenController extends Controller
|
||||
{
|
||||
public function __construct(private KitchenRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Kitchen has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(KitchenStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Kitchen has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Kitchen has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(KitchenUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Kitchen has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Kitchen has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\LoyaltyPoint\LoyaltyPointStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\LoyaltyPoint\LoyaltyPointUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\LoyaltyPointRepository;
|
||||
|
||||
class LoyaltyPointController extends Controller
|
||||
{
|
||||
public function __construct(private LoyaltyPointRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'LoyaltyPoint has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(LoyaltyPointStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'LoyaltyPoint has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'LoyaltyPoint has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(LoyaltyPointUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'LoyaltyPoint has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'LoyaltyPoint has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\LoyaltyPointSetting\LoyaltyPointSettingStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\LoyaltyPointSetting\LoyaltyPointSettingUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\LoyaltyPointSettingRepository;
|
||||
|
||||
class LoyaltyPointSettingController extends Controller
|
||||
{
|
||||
public function __construct(private LoyaltyPointSettingRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'LoyaltyPointSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(LoyaltyPointSettingStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'LoyaltyPointSetting has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'LoyaltyPointSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(LoyaltyPointSettingUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'LoyaltyPointSetting has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'LoyaltyPointSetting has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\LoyaltyRedemption\LoyaltyRedemptionStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\LoyaltyRedemption\LoyaltyRedemptionUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\LoyaltyRedemptionRepository;
|
||||
|
||||
class LoyaltyRedemptionController extends Controller
|
||||
{
|
||||
public function __construct(private LoyaltyRedemptionRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'LoyaltyRedemption has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(LoyaltyRedemptionStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'LoyaltyRedemption has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'LoyaltyRedemption has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(LoyaltyRedemptionUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'LoyaltyRedemption has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'LoyaltyRedemption has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\MenuCategory\MenuCategoryStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\MenuCategory\MenuCategoryUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\MenuCategoryRepository;
|
||||
|
||||
class MenuCategoryController extends Controller
|
||||
{
|
||||
public function __construct(private MenuCategoryRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'MenuCategory has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(MenuCategoryStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'MenuCategory has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'MenuCategory has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(MenuCategoryUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'MenuCategory has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'MenuCategory has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\MenuSection\MenuSectionStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\MenuSection\MenuSectionUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\MenuSectionRepository;
|
||||
|
||||
class MenuSectionController extends Controller
|
||||
{
|
||||
public function __construct(private MenuSectionRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'MenuSection has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(MenuSectionStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'MenuSection has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'MenuSection has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(MenuSectionUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'MenuSection has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'MenuSection has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\MenuType\MenuTypeStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\MenuType\MenuTypeUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\MenuTypeRepository;
|
||||
|
||||
class MenuTypeController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private MenuTypeRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'MenuType has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(MenuTypeStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'MenuType has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'MenuType has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(MenuTypeUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'MenuType has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'MenuType has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Enum\ActionStatus;
|
||||
use App\Helper\OrderHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Mail\OrderInvoiceMail;
|
||||
use App\Services\Firebase\FirebaseService;
|
||||
use App\Traits\Authenticatable;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use App\Traits\Trackable;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Modules\Authentication\Models\User;
|
||||
use Modules\Restaurant\Http\Requests\Order\OrderStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Order\OrderUpdateRequest;
|
||||
use Modules\Restaurant\Http\Resources\OrderResource;
|
||||
use Modules\Restaurant\Models\Order;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
use Authenticatable, RequestSanitizerTrait, Trackable;
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$query = Order::select('orders.*')
|
||||
->leftJoin('customers', 'orders.customer_id', '=', 'customers.id')
|
||||
->leftJoin('users as waiters', 'orders.waiter_id', '=', 'waiters.id')
|
||||
->with(['items.variation', 'items.food', 'customer', 'table', 'paymentMethod', 'waiter']);
|
||||
|
||||
if ($search = request('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$search = strtolower($search); // ensure lowercase
|
||||
|
||||
$q->whereRaw('LOWER(orders.order_type) like ?', ["%{$search}%"])
|
||||
->orWhereRaw('LOWER(orders.status) like ?', ["%{$search}%"])
|
||||
->orWhereRaw('LOWER(orders.order_number) like ?', ["%{$search}%"])
|
||||
->orWhereRaw('LOWER(customers.name) like ?', ["%{$search}%"])
|
||||
->orWhereRaw('LOWER(customers.phone) like ?', ["%{$search}%"])
|
||||
->orWhereRaw('LOWER(waiters.first_name) like ?', ["%{$search}%"]);
|
||||
});
|
||||
}
|
||||
|
||||
if (request('from_date') && request('to_date')) {
|
||||
$query->whereBetween('orders.created_at', [
|
||||
request('from_date').' 00:00:00',
|
||||
request('to_date').' 23:59:59',
|
||||
]);
|
||||
}
|
||||
|
||||
// Paginate the query
|
||||
$orders = $query->orderBy('orders.created_at', 'desc')
|
||||
->paginate(request('perPage', 10));
|
||||
|
||||
// Convert to resource collection **with pagination meta**
|
||||
$ordersResource = OrderResource::collection($orders)->response()->getData(true);
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Orders have been fetched successfully.',
|
||||
'data' => $ordersResource['data'], // resource items
|
||||
'meta' => $ordersResource['meta'], // pagination info
|
||||
'links' => $ordersResource['links'], // pagination links
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(OrderStoreRequest $request, FirebaseService $firebase): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$restaurantId = $this->getCurrentRestaurantId();
|
||||
$orderNumber = OrderHelper::generateOrderNumber($restaurantId);
|
||||
|
||||
// =========================
|
||||
// 🔹 Calculate Subtotal With Addons
|
||||
// =========================
|
||||
|
||||
$subtotal = 0;
|
||||
|
||||
foreach ($request->items as $item) {
|
||||
|
||||
// Base price calculation
|
||||
$baseTotal = $item['price'] * $item['quantity'];
|
||||
|
||||
// Addon calculation
|
||||
$addonTotal = 0;
|
||||
|
||||
if (! empty($item['addons'])) {
|
||||
foreach ($item['addons'] as $addon) {
|
||||
$addonTotal += $addon['price'];
|
||||
}
|
||||
|
||||
// Multiply addons by quantity
|
||||
$addonTotal = $addonTotal * $item['quantity'];
|
||||
}
|
||||
|
||||
// Add item total to subtotal
|
||||
$subtotal += ($baseTotal + $addonTotal);
|
||||
}
|
||||
|
||||
// Discount & Tax
|
||||
$discount = $request->discount ?? 0;
|
||||
$tax = $request->tax ?? 0;
|
||||
|
||||
// Grand Total
|
||||
$grandTotal = $subtotal - $discount + $tax;
|
||||
|
||||
// =========================
|
||||
// 🔹 Create Order
|
||||
// =========================
|
||||
|
||||
$order = Order::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'order_number' => $orderNumber,
|
||||
'order_type' => $request->order_type,
|
||||
'status' => $request->status,
|
||||
'table_id' => $request->table_id,
|
||||
'waiter_id' => $request->waiter_id,
|
||||
'customer_id' => $request->customer_id,
|
||||
'payment_method_id' => $request->payment_method_id,
|
||||
'payment_status' => $request->payment_status ?? false,
|
||||
'subtotal' => $subtotal,
|
||||
'discount' => $discount,
|
||||
'tax' => $tax,
|
||||
'grand_total' => $grandTotal,
|
||||
'order_date' => Carbon::now(),
|
||||
'added_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
// =========================
|
||||
// 🔹 Create Order Items
|
||||
// =========================
|
||||
foreach ($request->items as $item) {
|
||||
$addonTotal = 0;
|
||||
if (! empty($item['addons'])) {
|
||||
foreach ($item['addons'] as $addon) {
|
||||
$addonTotal += $addon['price'];
|
||||
}
|
||||
}
|
||||
$itemTotal =
|
||||
($item['price'] * $item['quantity']) +
|
||||
$addonTotal;
|
||||
|
||||
$order->items()->create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'food_item_id' => $item['food_item_id'],
|
||||
'food_variant_id' => $item['food_variant_id'] ?? null,
|
||||
'quantity' => $item['quantity'],
|
||||
'price' => $item['price'],
|
||||
'total' => $itemTotal,
|
||||
'addons' => ! empty($item['addons'])
|
||||
? json_encode($item['addons'])
|
||||
: null,
|
||||
]);
|
||||
|
||||
/*
|
||||
// 🔽 Ingredient deduction (optional)
|
||||
foreach ($item['ingredients'] ?? [] as $ingredient) {
|
||||
$ingredientModel = Ingredient::find($ingredient['id']);
|
||||
if ($ingredientModel) {
|
||||
$deductQty = $ingredient['quantity_required'] * $item['quantity'];
|
||||
$ingredientModel->decrement('available_quantity', $deductQty);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// =========================
|
||||
// 🔹 Action Log
|
||||
// =========================
|
||||
$user = auth()->user();
|
||||
$this->trackAction(ActionStatus::OrderCreate, User::class, $user->id, $order);
|
||||
|
||||
DB::commit();
|
||||
|
||||
// Push Notification
|
||||
// if ($user?->fcm_token) {
|
||||
// $result = $firebase->sendNotification(
|
||||
// $user->fcm_token,
|
||||
// 'Order Confirmed',
|
||||
// "Your order #{$order->id} has been confirmed!",
|
||||
// ['order_id' => $order->id]
|
||||
// );
|
||||
|
||||
// if ($result === true) {
|
||||
// return response()->json(['message' => 'Notification sent successfully']);
|
||||
// } else {
|
||||
// return response()->json(['message' => 'Failed to send notification', 'error' => $result], 500);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Directly send email
|
||||
// if ($order->customer?->email) {
|
||||
// try {
|
||||
// $result = shop_mailer_send($order->restaurant_id, $order->customer?->email, new OrderInvoiceMail($order));
|
||||
// if (! $result['status']) {
|
||||
// Log::warning("Order invoice email not sent for shop {$order->restaurant_id}: ".$result['message']);
|
||||
// }
|
||||
// Mail::to($order->customer?->email)->send(new OrderInvoiceMail($order));
|
||||
// } catch (Exception $ex) {
|
||||
// Log::error("Invoice email failed for order {$order->id}: ".$ex->getMessage());
|
||||
// }
|
||||
// }
|
||||
|
||||
// =========================
|
||||
// 🔹 Load Response Data
|
||||
// =========================
|
||||
$ordersResource = new OrderResource($order);
|
||||
|
||||
return $this->responseSuccess($ordersResource, 'Order created successfully.');
|
||||
} catch (Exception $e) {
|
||||
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], 'Order creation failed: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$order = Order::with([
|
||||
'items.food',
|
||||
'items.variation', // add this relation
|
||||
'customer',
|
||||
'table',
|
||||
'paymentMethod',
|
||||
])->findOrFail($id);
|
||||
|
||||
return $this->responseSuccess($order, 'Order has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(OrderUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$order = Order::with('items')->findOrFail($id);
|
||||
|
||||
// Recalculate total
|
||||
$totalAmount = collect($request->items)->sum(function ($item) {
|
||||
return $item['quantity'] * $item['price'];
|
||||
});
|
||||
|
||||
// Update order (order_number stays untouched)
|
||||
$order->update([
|
||||
'order_type' => $request->order_type,
|
||||
'status' => $request->status,
|
||||
'table_id' => $request->table_id,
|
||||
'payment_method_id' => $request->payment_method_id,
|
||||
'payment_status' => $request->payment_status ?? false,
|
||||
'waiter_id' => $request->waiter_id,
|
||||
'customer_id' => $request->customer_id,
|
||||
'grand_total' => $totalAmount,
|
||||
'updated_by' => Carbon::now(),
|
||||
]);
|
||||
|
||||
$existingItemIds = $order->items->pluck('id')->toArray();
|
||||
$requestItemIds = collect($request->items)->pluck('id')->filter()->toArray();
|
||||
|
||||
// 1️⃣ Remove items that are not in request anymore
|
||||
$itemsToDelete = array_diff($existingItemIds, $requestItemIds);
|
||||
if (! empty($itemsToDelete)) {
|
||||
$order->items()->whereIn('id', $itemsToDelete)->delete();
|
||||
}
|
||||
|
||||
// 2️⃣ Add or update items
|
||||
foreach ($request->items as $item) {
|
||||
if (isset($item['id']) && in_array($item['id'], $existingItemIds)) {
|
||||
// Update existing item
|
||||
$order->items()->where('id', $item['id'])->update([
|
||||
'restaurant_id' => $order->restaurant_id,
|
||||
'food_item_id' => $item['food_item_id'],
|
||||
'food_variant_id' => $item['food_variant_id'] ?? null,
|
||||
'quantity' => $item['quantity'],
|
||||
'price' => $item['price'],
|
||||
'total' => $item['quantity'] * $item['price'],
|
||||
]);
|
||||
} else {
|
||||
// Add new item
|
||||
$order->items()->create([
|
||||
'restaurant_id' => $order->restaurant_id,
|
||||
'food_item_id' => $item['food_item_id'],
|
||||
'food_variant_id' => $item['food_variant_id'] ?? null,
|
||||
'quantity' => $item['quantity'],
|
||||
'price' => $item['price'],
|
||||
'total' => $item['quantity'] * $item['price'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($order->load('items'), 'Order has been updated successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], 'Failed to update order: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$order = Order::with('items')->findOrFail($id);
|
||||
|
||||
// Delete related order items
|
||||
foreach ($order->items as $item) {
|
||||
$item->delete();
|
||||
}
|
||||
|
||||
// Delete the order
|
||||
$order->delete();
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'Order and related items have been deleted successfully.');
|
||||
} catch (\Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], 'Failed to delete order: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\OrderTracking\OrderTrackingStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\OrderTracking\OrderTrackingUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\OrderTrackingRepository;
|
||||
|
||||
class OrderTrackingController extends Controller
|
||||
{
|
||||
public function __construct(private OrderTrackingRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'OrderTracking has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(OrderTrackingStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'OrderTracking has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'OrderTracking has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(OrderTrackingUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'OrderTracking has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'OrderTracking has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\PaymentMethod\PaymentMethodStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\PaymentMethod\PaymentMethodUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\PaymentMethodRepository;
|
||||
|
||||
class PaymentMethodController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private PaymentMethodRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'PaymentMethod has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(PaymentMethodStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'PaymentMethod has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'PaymentMethod has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(PaymentMethodUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'PaymentMethod has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'PaymentMethod has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Helper\PurchaseHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use App\Traits\Trackable;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Http\Requests\Purchase\PurchaseStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Purchase\PurchaseUpdateRequest;
|
||||
use Modules\Restaurant\Models\Purchase;
|
||||
use Modules\Restaurant\Models\PurchaseItem;
|
||||
use Modules\Restaurant\Models\Stock;
|
||||
use Modules\Restaurant\Repositories\PurchaseRepository;
|
||||
|
||||
class PurchaseController extends Controller
|
||||
{
|
||||
use Authenticatable, RequestSanitizerTrait, Trackable;
|
||||
|
||||
public function __construct(private PurchaseRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$filters = request()->all();
|
||||
$data = $this->repo->getAll($filters);
|
||||
|
||||
return $this->responseSuccess($data, 'Purchase list fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(PurchaseStoreRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
// 1️⃣ Create Purchase
|
||||
$restaurantId = $this->getCurrentRestaurantId();
|
||||
$invoiceNo = PurchaseHelper::generateInvoiceNumber($restaurantId);
|
||||
$purchase = Purchase::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'supplier_id' => $data['supplier_id'] ?? null,
|
||||
'invoice_no' => $invoiceNo,
|
||||
'purchase_date' => $data['purchase_date'] ?? Carbon::now(),
|
||||
'sub_total' => $data['sub_total'],
|
||||
'discount_amount' => $data['discount_amount'] ?? 0,
|
||||
'tax_amount' => $data['tax_amount'] ?? 0,
|
||||
'total_amount' => $data['total_amount'],
|
||||
'payment_status' => $data['payment_status'] ?? 'unpaid',
|
||||
'payment_method' => $data['payment_method'] ?? null,
|
||||
'purchase_type' => $data['purchase_type'] ?? 'ingredient',
|
||||
'created_by' => $data['created_by'] ?? null,
|
||||
'notes' => $data['notes'] ?? null,
|
||||
]);
|
||||
|
||||
// 2️⃣ Loop through items and create Purchase Items + Stock
|
||||
foreach ($data['items'] as $item) {
|
||||
$purchaseItem = PurchaseItem::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'purchase_id' => $purchase->id,
|
||||
'ingredient_id' => $item['ingredient_id'],
|
||||
'food_variant_id' => $item['food_variant_id'] ?? null,
|
||||
'quantity' => $item['quantity'],
|
||||
'unit_price' => $item['unit_price'],
|
||||
'total_cost' => $item['total_cost'],
|
||||
'tax_amount' => $item['tax_amount'] ?? 0,
|
||||
'discount_amount' => $item['discount_amount'] ?? 0,
|
||||
'batch_no' => $item['batch_no'] ?? null,
|
||||
'expiry_date' => $item['expiry_date'] ?? null,
|
||||
'received_quantity' => $item['received_quantity'] ?? $item['quantity'],
|
||||
'wastage_quantity' => $item['wastage_quantity'] ?? 0,
|
||||
]);
|
||||
|
||||
// 3️⃣ Update Stock
|
||||
Stock::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'ingredient_id' => $item['ingredient_id'],
|
||||
'type' => 'purchase',
|
||||
'quantity' => $item['received_quantity'] ?? $item['quantity'],
|
||||
'unit_cost' => $item['unit_price'],
|
||||
'total_cost' => $item['total_cost'],
|
||||
'reference_type' => 'PurchaseItem',
|
||||
'purchase_id' => $purchaseItem->id,
|
||||
'batch_no' => $item['batch_no'] ?? null,
|
||||
'expiry_date' => $item['expiry_date'] ?? null,
|
||||
'added_by' => $data['created_by'] ?? null,
|
||||
'remarks' => 'Initial purchase stock',
|
||||
]);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess($purchase->load('items'), 'Purchase has been created successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
|
||||
$purchase = Purchase::with([
|
||||
'supplier:id,name,phone',
|
||||
'creator:id,first_name,last_name',
|
||||
'items' => function ($q) {
|
||||
$q->with([
|
||||
'ingredient:id,name,unit_id',
|
||||
'ingredient.unit:id,name',
|
||||
]);
|
||||
},
|
||||
])
|
||||
->where('restaurant_id', $restaurantId)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
|
||||
if (! $purchase) {
|
||||
return $this->responseError([], 'Purchase not found.', 404);
|
||||
}
|
||||
|
||||
return $this->responseSuccess($purchase, 'Purchase fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(PurchaseUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$restaurantId = $this->getCurrentRestaurantId();
|
||||
|
||||
// 1️⃣ Fetch Purchase
|
||||
$purchase = Purchase::with('items')->findOrFail($id);
|
||||
|
||||
// 2️⃣ Update Purchase fields
|
||||
$purchase->update([
|
||||
'supplier_id' => $data['supplier_id'] ?? null,
|
||||
'purchase_date' => $data['purchase_date'] ?? $purchase->purchase_date,
|
||||
'sub_total' => $data['sub_total'],
|
||||
'discount_amount' => $data['discount_amount'] ?? 0,
|
||||
'tax_amount' => $data['tax_amount'] ?? 0,
|
||||
'total_amount' => $data['total_amount'],
|
||||
'payment_status' => $data['payment_status'] ?? $purchase->payment_status,
|
||||
'payment_method' => $data['payment_method'] ?? $purchase->payment_method,
|
||||
'purchase_type' => $data['purchase_type'] ?? $purchase->purchase_type,
|
||||
'created_by' => $data['created_by'] ?? $purchase->created_by,
|
||||
'notes' => $data['notes'] ?? null,
|
||||
]);
|
||||
|
||||
// Collect item IDs included in request
|
||||
$incomingItemIds = collect($data['items'])
|
||||
->pluck('id')
|
||||
->filter()
|
||||
->toArray();
|
||||
|
||||
// 3️⃣ Remove items that are NOT in request
|
||||
$itemsToDelete = $purchase->items()
|
||||
->whereNotIn('id', $incomingItemIds)
|
||||
->get();
|
||||
|
||||
foreach ($itemsToDelete as $oldItem) {
|
||||
// Delete stock related to this item
|
||||
Stock::where('reference_type', 'PurchaseItem')
|
||||
->where('purchase_id', $oldItem->id)
|
||||
->delete();
|
||||
|
||||
// Delete purchase item
|
||||
$oldItem->delete();
|
||||
}
|
||||
|
||||
// 4️⃣ Update or Create items
|
||||
foreach ($data['items'] as $item) {
|
||||
// If item has ID → update
|
||||
if (! empty($item['id'])) {
|
||||
$purchaseItem = PurchaseItem::find($item['id']);
|
||||
$purchaseItem->update([
|
||||
'ingredient_id' => $item['ingredient_id'],
|
||||
'food_variant_id' => $item['food_variant_id'] ?? null,
|
||||
'quantity' => $item['quantity'],
|
||||
'unit_price' => $item['unit_price'],
|
||||
'total_cost' => $item['total_cost'],
|
||||
'tax_amount' => $item['tax_amount'] ?? 0,
|
||||
'discount_amount' => $item['discount_amount'] ?? 0,
|
||||
'batch_no' => $item['batch_no'] ?? null,
|
||||
'expiry_date' => $item['expiry_date'] ?? null,
|
||||
'received_quantity' => $item['received_quantity'] ?? $item['quantity'],
|
||||
'wastage_quantity' => $item['wastage_quantity'] ?? 0,
|
||||
]);
|
||||
|
||||
// Update stock row
|
||||
Stock::where('reference_type', 'PurchaseItem')
|
||||
->where('purchase_id', $purchaseItem->id)
|
||||
->update([
|
||||
'ingredient_id' => $item['ingredient_id'],
|
||||
'quantity' => $item['received_quantity'] ?? $item['quantity'],
|
||||
'unit_cost' => $item['unit_price'],
|
||||
'total_cost' => $item['total_cost'],
|
||||
'batch_no' => $item['batch_no'] ?? null,
|
||||
'expiry_date' => $item['expiry_date'] ?? null,
|
||||
'added_by' => $data['created_by'] ?? $purchase->created_by,
|
||||
'remarks' => 'Updated purchase stock',
|
||||
]);
|
||||
} else {
|
||||
// No ID → Create new item
|
||||
$purchaseItem = PurchaseItem::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'purchase_id' => $purchase->id,
|
||||
'ingredient_id' => $item['ingredient_id'],
|
||||
'food_variant_id' => $item['food_variant_id'] ?? null,
|
||||
'quantity' => $item['quantity'],
|
||||
'unit_price' => $item['unit_price'],
|
||||
'total_cost' => $item['total_cost'],
|
||||
'tax_amount' => $item['tax_amount'] ?? 0,
|
||||
'discount_amount' => $item['discount_amount'] ?? 0,
|
||||
'batch_no' => $item['batch_no'] ?? null,
|
||||
'expiry_date' => $item['expiry_date'] ?? null,
|
||||
'received_quantity' => $item['received_quantity'] ?? $item['quantity'],
|
||||
'wastage_quantity' => $item['wastage_quantity'] ?? 0,
|
||||
]);
|
||||
|
||||
// Create stock for new item
|
||||
Stock::create([
|
||||
'restaurant_id' => $restaurantId,
|
||||
'ingredient_id' => $item['ingredient_id'],
|
||||
'type' => 'purchase',
|
||||
'quantity' => $item['received_quantity'] ?? $item['quantity'],
|
||||
'unit_cost' => $item['unit_price'],
|
||||
'total_cost' => $item['total_cost'],
|
||||
'reference_type' => 'PurchaseItem',
|
||||
'purchase_id' => $purchaseItem->id,
|
||||
'batch_no' => $item['batch_no'] ?? null,
|
||||
'expiry_date' => $item['expiry_date'] ?? null,
|
||||
'added_by' => $data['created_by'] ?? null,
|
||||
'remarks' => 'Newly added purchase item',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess(
|
||||
$purchase->load('items'),
|
||||
'Purchase has been updated successfully.'
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$restaurantId = getUserRestaurantId();
|
||||
|
||||
$purchase = Purchase::where('restaurant_id', $restaurantId)
|
||||
->where('id', $id)
|
||||
->first();
|
||||
|
||||
if (! $purchase) {
|
||||
return $this->responseError([], 'Purchase not found.', 404);
|
||||
}
|
||||
|
||||
// 1️⃣ Delete related Purchase Items + Stock
|
||||
foreach ($purchase->items as $item) {
|
||||
// Delete stock entries created from this item
|
||||
Stock::where('reference_type', 'PurchaseItem')
|
||||
->where('purchase_id', $item->id)
|
||||
->delete();
|
||||
|
||||
// Delete purchase item
|
||||
$item->delete();
|
||||
}
|
||||
|
||||
// 2️⃣ Delete Main Purchase
|
||||
$purchase->delete();
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'Purchase deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Helper\PurchaseReturnHelper;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Restaurant\Models\Purchase;
|
||||
use Modules\Restaurant\Models\PurchaseReturn;
|
||||
|
||||
class PurchaseReturnController extends Controller
|
||||
{
|
||||
/**
|
||||
* List purchase returns with pagination, search, and metadata
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$filters = $request->all();
|
||||
$data = $this->getAll($filters);
|
||||
|
||||
return $this->responseSuccess($data, 'Purchase returns fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new purchase return
|
||||
*/
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$request->validate([
|
||||
'purchase_id' => 'required|exists:purchases,id',
|
||||
'items' => 'required|array|min:1',
|
||||
'items.*.purchase_item_id' => 'required|exists:purchase_items,id',
|
||||
'items.*.quantity' => 'required|numeric|min:0.01',
|
||||
'items.*.reason' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
try {
|
||||
$purchaseId = $request->purchase_id;
|
||||
$items = $request->items;
|
||||
$userId = auth()->id();
|
||||
|
||||
// Use the helper to process returns
|
||||
$result = PurchaseReturnHelper::processReturn($purchaseId, $items, $userId);
|
||||
|
||||
if (! $result['status']) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => $result['message'],
|
||||
'data' => [],
|
||||
], 400);
|
||||
}
|
||||
|
||||
// Build response with purchase and item details
|
||||
$purchase = Purchase::with(['supplier', 'items.ingredient', 'items.returns.processedBy'])->findOrFail($purchaseId);
|
||||
|
||||
$totals = [
|
||||
'sub_total' => $purchase->items->sum(fn ($i) => $i->quantity * $i->unit_price),
|
||||
'total_returned_quantity' => $purchase->items->sum(fn ($i) => $i->returned_quantity),
|
||||
'total_returned_value' => $purchase->items->sum(fn ($i) => $i->returns->sum(fn ($r) => $r->total_cost)),
|
||||
];
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Purchase return processed successfully.',
|
||||
'data' => [
|
||||
'purchase_id' => $purchase->id,
|
||||
'supplier' => $purchase->supplier,
|
||||
'restaurant' => $purchase->restaurant,
|
||||
'items' => $purchase->items->map(function ($item) {
|
||||
return [
|
||||
'purchase_item_id' => $item->id,
|
||||
'ingredient' => $item->ingredient,
|
||||
'quantity' => $item->quantity,
|
||||
'unit_price' => $item->unit_price,
|
||||
'total_cost' => $item->quantity * $item->unit_price,
|
||||
'received_quantity' => $item->received_quantity,
|
||||
'wastage_quantity' => $item->wastage_quantity,
|
||||
'returned_quantity' => $item->returned_quantity,
|
||||
'batch_no' => $item->batch_no,
|
||||
'expiry_date' => $item->expiry_date,
|
||||
'returns' => $item->returns->map(function ($r) {
|
||||
return [
|
||||
'return_id' => $r->id,
|
||||
'quantity' => $r->quantity,
|
||||
'unit_cost' => $r->unit_cost,
|
||||
'total_cost' => $r->total_cost,
|
||||
'reason' => $r->reason,
|
||||
'return_date' => $r->created_at->toDateString(),
|
||||
'processed_by' => $r->processedBy,
|
||||
];
|
||||
}),
|
||||
];
|
||||
}),
|
||||
'totals' => $totals,
|
||||
],
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'data' => null,
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show single purchase return
|
||||
*/
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
$return = PurchaseReturn::with(['restaurant', 'purchase', 'purchaseItem', 'ingredient', 'processedBy'])
|
||||
->findOrFail($id);
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Purchase return fetched successfully.',
|
||||
'data' => $return,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getAll(array $filters)
|
||||
{
|
||||
$query = PurchaseReturn::with(['purchase', 'purchaseItem', 'ingredient', 'processedBy']);
|
||||
|
||||
// === Filters ===
|
||||
if (! empty($filters['ingredient_id'])) {
|
||||
$query->where('ingredient_id', $filters['ingredient_id']);
|
||||
}
|
||||
|
||||
if (! empty($filters['purchase_id'])) {
|
||||
$query->where('purchase_id', $filters['purchase_id']);
|
||||
}
|
||||
|
||||
if (! empty($filters['restaurant_id'])) {
|
||||
$query->where('restaurant_id', $filters['restaurant_id']);
|
||||
}
|
||||
|
||||
if (! empty($filters['search'])) {
|
||||
$search = $filters['search'];
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('reason', 'like', "%$search%")
|
||||
->orWhere('batch_no', 'like', "%$search%");
|
||||
});
|
||||
}
|
||||
|
||||
// === Sorting ===
|
||||
$sortBy = $filters['sort_by'] ?? 'return_date';
|
||||
$sortOrder = $filters['sort_order'] ?? 'desc';
|
||||
$query->orderBy($sortBy, $sortOrder);
|
||||
|
||||
// === Pagination ===
|
||||
$perPage = $filters['perPage'] ?? 10;
|
||||
$paginator = $query->paginate($perPage);
|
||||
|
||||
// === Return same structure as Purchase list ===
|
||||
return [
|
||||
'current_page' => $paginator->currentPage(),
|
||||
'data' => $paginator->items(),
|
||||
'first_page_url' => $paginator->url(1),
|
||||
'from' => $paginator->firstItem(),
|
||||
'last_page' => $paginator->lastPage(),
|
||||
'last_page_url' => $paginator->url($paginator->lastPage()),
|
||||
'links' => $paginator->linkCollection(),
|
||||
'next_page_url' => $paginator->nextPageUrl(),
|
||||
'path' => $paginator->path(),
|
||||
'perPage' => $paginator->perPage(),
|
||||
'prev_page_url' => $paginator->previousPageUrl(),
|
||||
'to' => $paginator->lastItem(),
|
||||
'total' => $paginator->total(),
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\QRMenuSetting\QRMenuSettingStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\QRMenuSetting\QRMenuSettingUpdateRequest;
|
||||
use Modules\Restaurant\Models\QRMenuSetting;
|
||||
use Modules\Restaurant\Repositories\QRMenuSettingRepository;
|
||||
|
||||
class QRMenuSettingController extends Controller
|
||||
{
|
||||
public function __construct(private QRMenuSettingRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
$qrCode = QRMenuSetting::where('restaurant_id', getUserRestaurantId())->first();
|
||||
|
||||
return $this->responseSuccess($qrCode, 'QRMenuSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(QRMenuSettingStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'QRMenuSetting has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'QRMenuSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(QRMenuSettingUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'QRMenuSetting has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'QRMenuSetting has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Modules\Restaurant\Models\Ingredient;
|
||||
use Modules\Restaurant\Models\Purchase;
|
||||
use Modules\Restaurant\Models\PurchaseItem;
|
||||
use Modules\Restaurant\Models\Stock;
|
||||
|
||||
class ReportController extends Controller
|
||||
{
|
||||
/**
|
||||
* Ingredient report (restaurant-wise) with date range and search/filter
|
||||
* GET /api/reports/ingredients
|
||||
* params: restaurant_id, start_date, end_date, search, page, perPage
|
||||
*/
|
||||
public function ingredients(Request $request): JsonResponse
|
||||
{
|
||||
$v = Validator::make($request->all(), [
|
||||
'restaurant_id' => 'nullable|integer|exists:restaurants,id',
|
||||
'start_date' => 'nullable|date',
|
||||
'end_date' => 'nullable|date',
|
||||
'search' => 'nullable|string',
|
||||
'page' => 'nullable|integer',
|
||||
'perPage' => 'nullable|integer',
|
||||
]);
|
||||
|
||||
if ($v->fails()) {
|
||||
return response()->json(['errors' => $v->errors()], 422);
|
||||
}
|
||||
|
||||
$start = $request->input('start_date');
|
||||
$end = $request->input('end_date');
|
||||
$restaurantId = getUserRestaurantId();
|
||||
$search = $request->input('search');
|
||||
$perPage = $request->input('perPage', 25);
|
||||
|
||||
$query = Ingredient::query()
|
||||
->leftJoin('units', 'units.id', '=', 'ingredients.unit_id') // join units
|
||||
->select([
|
||||
'ingredients.id',
|
||||
'ingredients.name',
|
||||
'ingredients.unit_id',
|
||||
'units.name as unit_name', // include unit name
|
||||
'ingredients.cost_per_unit',
|
||||
'ingredients.last_purchase_price',
|
||||
'ingredients.restaurant_id',
|
||||
DB::raw('IFNULL(SUM(stocks_in.quantity),0) as total_in'),
|
||||
DB::raw('IFNULL(SUM(stocks_out.quantity),0) as total_out'),
|
||||
DB::raw('IFNULL(SUM(stocks_in.total_cost),0) as total_in_cost'),
|
||||
])
|
||||
->leftJoin('stocks as stocks_in', function ($join) use ($start, $end, $restaurantId) {
|
||||
$join->on('stocks_in.ingredient_id', '=', 'ingredients.id')
|
||||
->whereIn('stocks_in.type', ['purchase', 'return', 'transfer_in'])
|
||||
->whereNull('stocks_in.deleted_at');
|
||||
|
||||
if ($start) {
|
||||
$join->whereDate('stocks_in.created_at', '>=', $start);
|
||||
}
|
||||
if ($end) {
|
||||
$join->whereDate('stocks_in.created_at', '<=', $end);
|
||||
}
|
||||
if ($restaurantId) {
|
||||
$join->where('stocks_in.restaurant_id', $restaurantId);
|
||||
}
|
||||
})
|
||||
->leftJoin('stocks as stocks_out', function ($join) use ($start, $end, $restaurantId) {
|
||||
$join->on('stocks_out.ingredient_id', '=', 'ingredients.id')
|
||||
->whereIn('stocks_out.type', ['usage', 'damage', 'transfer_out'])
|
||||
->whereNull('stocks_out.deleted_at');
|
||||
|
||||
if ($start) {
|
||||
$join->whereDate('stocks_out.created_at', '>=', $start);
|
||||
}
|
||||
if ($end) {
|
||||
$join->whereDate('stocks_out.created_at', '<=', $end);
|
||||
}
|
||||
if ($restaurantId) {
|
||||
$join->where('stocks_out.restaurant_id', $restaurantId);
|
||||
}
|
||||
})
|
||||
->groupBy([
|
||||
'ingredients.id',
|
||||
'ingredients.name',
|
||||
'ingredients.unit_id',
|
||||
'units.name', // include in groupBy
|
||||
'ingredients.cost_per_unit',
|
||||
'ingredients.last_purchase_price',
|
||||
'ingredients.restaurant_id',
|
||||
]);
|
||||
|
||||
if ($restaurantId) {
|
||||
$query->where('ingredients.restaurant_id', $restaurantId);
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$query->where('ingredients.name', 'like', "%{$search}%");
|
||||
}
|
||||
|
||||
$paginated = $query->paginate($perPage);
|
||||
|
||||
// Enrich each item with computed fields
|
||||
$paginated->getCollection()->transform(function ($item) use ($start, $restaurantId) {
|
||||
$currentStock = $this->currentStockForIngredient($item->id, $restaurantId);
|
||||
$openingStock = $this->openingStockForIngredient($item->id, $start, $restaurantId);
|
||||
$unitCost = $item->cost_per_unit
|
||||
?: $item->last_purchase_price
|
||||
?: $this->avgUnitCostFromStocks($item->id, $restaurantId);
|
||||
$marketValue = round($currentStock * ($unitCost ?? 0), 2);
|
||||
|
||||
return array_merge($item->toArray(), [
|
||||
'opening_stock' => round($openingStock, 2),
|
||||
'current_stock' => round($currentStock, 2),
|
||||
'estimated_market_value' => $marketValue,
|
||||
]);
|
||||
});
|
||||
|
||||
return $this->responseSuccess($paginated, 'Ingredients report fetched successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stock report (stock movements) date-to-date
|
||||
* GET /api/reports/stocks
|
||||
* params: restaurant_id, start_date, end_date, ingredient_id, type, page, perPage
|
||||
*/
|
||||
public function stocks(Request $request): JsonResponse
|
||||
{
|
||||
$v = Validator::make($request->all(), [
|
||||
'restaurant_id' => 'nullable|integer|exists:restaurants,id',
|
||||
'start_date' => 'nullable|date',
|
||||
'end_date' => 'nullable|date',
|
||||
'ingredient_id' => 'nullable|integer|exists:ingredients,id',
|
||||
'type' => 'nullable|in:purchase,usage,return,damage,transfer_in,transfer_out',
|
||||
'page' => 'nullable|integer',
|
||||
'perPage' => 'nullable|integer',
|
||||
]);
|
||||
|
||||
if ($v->fails()) {
|
||||
return response()->json(['errors' => $v->errors()], 422);
|
||||
}
|
||||
|
||||
$q = Stock::query()->with(['ingredient'])
|
||||
->when($request->restaurant_id ?? getUserRestaurantId(), fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->when($request->ingredient_id, fn ($q, $id) => $q->where('ingredient_id', $id))
|
||||
->when($request->type, fn ($q, $t) => $q->where('type', $t))
|
||||
->when($request->start_date, fn ($q, $s) => $q->whereDate('created_at', '>=', $s))
|
||||
->when($request->end_date, fn ($q, $e) => $q->whereDate('created_at', '<=', $e))
|
||||
->orderBy('created_at', 'desc');
|
||||
|
||||
$perPage = $request->input('perPage', 25);
|
||||
$result = $q->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($result, 'Stock reports successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Purchase report date-to-date
|
||||
* GET /api/reports/purchases
|
||||
* params: restaurant_id, start_date, end_date, supplier_id, payment_status
|
||||
*/
|
||||
public function purchases(Request $request): JsonResponse
|
||||
{
|
||||
$v = Validator::make($request->all(), [
|
||||
'restaurant_id' => 'nullable|integer|exists:restaurants,id',
|
||||
'supplier_id' => 'nullable|integer|exists:suppliers,id',
|
||||
'start_date' => 'nullable|date',
|
||||
'end_date' => 'nullable|date',
|
||||
'payment_status' => 'nullable|in:paid,unpaid,partial',
|
||||
'page' => 'nullable|integer',
|
||||
'perPage' => 'nullable|integer',
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return response()->json(['errors' => $v->errors()], 422);
|
||||
}
|
||||
|
||||
$q = Purchase::with(['supplier', 'items.ingredient'])
|
||||
->when($request->restaurant_id ?? getUserRestaurantId(), fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->when($request->supplier_id, fn ($q, $s) => $q->where('supplier_id', $s))
|
||||
->when($request->payment_status, fn ($q, $p) => $q->where('payment_status', $p))
|
||||
->when($request->start_date, fn ($q, $s) => $q->whereDate('purchase_date', '>=', $s))
|
||||
->when($request->end_date, fn ($q, $e) => $q->whereDate('purchase_date', '<=', $e))
|
||||
->orderBy('purchase_date', 'desc');
|
||||
|
||||
$perPage = $request->input('perPage', 25);
|
||||
$res = $q->paginate($perPage);
|
||||
|
||||
return $this->responseSuccess($res, 'Purchase reports successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Purchase ingredient with sales items countable for sales-estimate report
|
||||
* Combines purchases and sold menu items to estimate ingredient usage & profitability
|
||||
* GET /api/reports/purchase-estimate
|
||||
* params: restaurant_id, start_date, end_date
|
||||
*/
|
||||
public function purchaseEstimate(Request $request): JsonResponse
|
||||
{
|
||||
$v = Validator::make($request->all(), [
|
||||
'restaurant_id' => 'nullable|integer|exists:restaurants,id',
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date',
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return response()->json(['errors' => $v->errors()], 422);
|
||||
}
|
||||
|
||||
$start = date('Y-m-d', strtotime($request->start_date));
|
||||
$end = date('Y-m-d', strtotime($request->end_date));
|
||||
$restaurantId = $request->restaurant_id ?? getUserRestaurantId();
|
||||
|
||||
// 🛒 Purchased quantities per ingredient
|
||||
$purchases = PurchaseItem::select(
|
||||
'ingredient_id',
|
||||
DB::raw('SUM(received_quantity) as purchased_qty'),
|
||||
DB::raw('SUM(total_cost) as purchased_cost')
|
||||
)
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r))
|
||||
->whereBetween(DB::raw('DATE(created_at)'), [$start, $end])
|
||||
->groupBy('ingredient_id')
|
||||
->get()
|
||||
->keyBy('ingredient_id');
|
||||
|
||||
// 🍽️ Estimate ingredient usage from sales
|
||||
$salesEstimate = DB::table('order_items')
|
||||
->select(
|
||||
'food_variant_ingredients.ingredient_id',
|
||||
DB::raw('SUM(order_items.quantity * food_variant_ingredients.quantity * (1 + food_variant_ingredients.wastage_percentage / 100)) as estimated_used')
|
||||
)
|
||||
->join('food_variants', 'order_items.food_variant_id', '=', 'food_variants.id')
|
||||
->join('food_variant_ingredients', 'food_variant_ingredients.food_variant_id', '=', 'food_variants.id')
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('order_items.restaurant_id', $r))
|
||||
->whereBetween(DB::raw('DATE(order_items.created_at)'), [$start, $end])
|
||||
->groupBy('food_variant_ingredients.ingredient_id')
|
||||
->get()
|
||||
->keyBy('ingredient_id');
|
||||
|
||||
// 🧾 Merge purchase + estimated usage
|
||||
$ingredientIds = collect(array_unique(array_merge(
|
||||
$purchases->keys()->toArray(),
|
||||
$salesEstimate->keys()->toArray()
|
||||
)));
|
||||
|
||||
$report = [];
|
||||
foreach ($ingredientIds as $ingId) {
|
||||
$ingredient = Ingredient::find($ingId);
|
||||
$p = $purchases->get($ingId);
|
||||
$s = $salesEstimate->get($ingId);
|
||||
|
||||
$pQty = $p->purchased_qty ?? 0;
|
||||
$pCost = $p->purchased_cost ?? 0;
|
||||
$used = $s->estimated_used ?? 0;
|
||||
$remaining = max(0, $pQty - $used);
|
||||
|
||||
$unitCost = $ingredient->cost_per_unit
|
||||
?: ($pQty ? ($pCost / $pQty) : ($ingredient->last_purchase_price ?? 0));
|
||||
|
||||
$report[] = [
|
||||
'ingredient_id' => $ingId,
|
||||
'ingredient_name' => $ingredient->name ?? 'Unknown',
|
||||
'purchased_qty' => (float) $pQty,
|
||||
'purchased_cost' => (float) $pCost,
|
||||
'estimated_used' => (float) $used,
|
||||
'estimated_remaining' => (float) $remaining,
|
||||
'unit_cost_used' => round($unitCost, 2),
|
||||
'estimated_remaining_value' => round($remaining * $unitCost, 2),
|
||||
];
|
||||
}
|
||||
|
||||
return $this->responseSuccess([
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'data' => $report,
|
||||
], 'Purchase Estimate reports successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Opening stock (date-to-date): returns opening stock at start_date, current stock and market value
|
||||
* GET /api/reports/opening-stock
|
||||
* params: restaurant_id, start_date, end_date, ingredient_id
|
||||
*/
|
||||
public function openingStock(Request $request): JsonResponse
|
||||
{
|
||||
$v = Validator::make($request->all(), [
|
||||
'restaurant_id' => 'nullable|integer|exists:restaurants,id',
|
||||
'ingredient_id' => 'nullable|integer|exists:ingredients,id',
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'nullable|date',
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return response()->json(['errors' => $v->errors()], 422);
|
||||
}
|
||||
|
||||
$start = $request->start_date;
|
||||
$restaurantId = $request->restaurant_id ?? getUserRestaurantId();
|
||||
|
||||
$ingQuery = Ingredient::query()->when($request->ingredient_id, fn ($q, $id) => $q->where('id', $id))
|
||||
->when($restaurantId, fn ($q, $r) => $q->where('restaurant_id', $r));
|
||||
|
||||
$results = $ingQuery->get()->map(function ($ingredient) use ($start, $restaurantId) {
|
||||
$opening = $this->openingStockForIngredient($ingredient->id, $start, $restaurantId);
|
||||
$current = $this->currentStockForIngredient($ingredient->id, $restaurantId);
|
||||
$unitCost = $ingredient->cost_per_unit ?: $ingredient->last_purchase_price ?: $this->avgUnitCostFromStocks($ingredient->id, $restaurantId);
|
||||
$marketValue = round($current * ($unitCost ?? 0), 2);
|
||||
|
||||
return [
|
||||
'ingredient_id' => $ingredient->id,
|
||||
'ingredient_name' => $ingredient->name,
|
||||
'opening_stock' => round($opening, 2),
|
||||
'current_stock' => round($current, 2),
|
||||
'unit_cost' => round($unitCost ?? 0, 2),
|
||||
'market_value' => $marketValue,
|
||||
];
|
||||
});
|
||||
|
||||
return $this->responseSuccess(['start_date' => $start, 'data' => $results], 'Opening Stock reports successfully.');
|
||||
}
|
||||
|
||||
// ---------------- Helper methods ----------------
|
||||
|
||||
/**
|
||||
* Calculate opening stock for ingredient before the given date (exclusive)
|
||||
*/
|
||||
protected function openingStockForIngredient($ingredientId, $startDate = null, $restaurantId = null): float|int
|
||||
{
|
||||
if (! $startDate) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$pos = ['purchase', 'return', 'transfer_in'];
|
||||
$neg = ['usage', 'damage', 'transfer_out'];
|
||||
|
||||
$q = Stock::where('ingredient_id', $ingredientId)->whereDate('created_at', '<', $startDate)->whereNull('deleted_at');
|
||||
if ($restaurantId) {
|
||||
$q->where('restaurant_id', $restaurantId);
|
||||
}
|
||||
|
||||
$in = (float) $q->whereIn('type', $pos)->sum('quantity');
|
||||
$out = (float) $q->whereIn('type', $neg)->sum('quantity');
|
||||
|
||||
return $in - $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current stock calculated from stocks table (fallback to ingredients.stock_quantity)
|
||||
*/
|
||||
protected function currentStockForIngredient($ingredientId, $restaurantId = null): float|int
|
||||
{
|
||||
$pos = ['purchase', 'return', 'transfer_in'];
|
||||
$neg = ['usage', 'damage', 'transfer_out'];
|
||||
|
||||
$q = Stock::where('ingredient_id', $ingredientId)->whereNull('deleted_at');
|
||||
if ($restaurantId) {
|
||||
$q->where('restaurant_id', $restaurantId);
|
||||
}
|
||||
|
||||
$in = (float) $q->whereIn('type', $pos)->sum('quantity');
|
||||
$out = (float) $q->whereIn('type', $neg)->sum('quantity');
|
||||
|
||||
$delta = $in - $out;
|
||||
|
||||
// fallback to ingredients.stock_quantity if stocks table is sparse
|
||||
$ingredient = Ingredient::find($ingredientId);
|
||||
$ingQty = $ingredient ? (float) $ingredient->stock_quantity : 0;
|
||||
|
||||
// Use the most reliable value: if ingredient.stock_quantity > 0, assume it's authoritative
|
||||
if ($ingQty > 0) {
|
||||
return $ingQty;
|
||||
}
|
||||
|
||||
return $delta;
|
||||
}
|
||||
|
||||
protected function avgUnitCostFromStocks($ingredientId, $restaurantId = null): float|int|null
|
||||
{
|
||||
$q = Stock::where('ingredient_id', $ingredientId)->whereNotNull('unit_cost')->whereNull('deleted_at');
|
||||
if ($restaurantId) {
|
||||
$q->where('restaurant_id', $restaurantId);
|
||||
}
|
||||
|
||||
$totalQty = (float) $q->sum('quantity');
|
||||
$totalCost = (float) $q->sum('total_cost');
|
||||
|
||||
if ($totalQty <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $totalCost / $totalQty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Restaurant\Models\Reservation;
|
||||
use Modules\Restaurant\Repositories\ReservationRepository;
|
||||
|
||||
class ReservationController extends Controller
|
||||
{
|
||||
public function __construct(private ReservationRepository $repo) {}
|
||||
|
||||
use Authenticatable;
|
||||
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$filters = $request->all();
|
||||
$data = $this->repo->getAll($filters);
|
||||
|
||||
return $this->responseSuccess($data, 'Reservation list fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$validated = $request->validate([
|
||||
'table_id' => 'required|integer|exists:tables,id',
|
||||
'customer_id' => 'nullable|integer|exists:customers,id',
|
||||
'reservation_date' => 'required|date',
|
||||
'start_time' => 'required|date_format:H:i',
|
||||
'end_time' => 'required|date_format:H:i|after:start_time',
|
||||
'people_count' => 'required|integer|min:1',
|
||||
'status' => 'nullable|in:booked,free,upcoming,cancelled',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$validated['restaurant_id'] = $this->getCurrentRestaurantId();
|
||||
$data = $this->repo->store($validated);
|
||||
|
||||
// TODO
|
||||
// $user = auth()->user();
|
||||
// // Action Log info
|
||||
// $this->trackAction(ActionStatus::OrderCreate, User::class, $user->id, $order);
|
||||
|
||||
// // Push Notification
|
||||
// if ($user?->fcm_token) {
|
||||
// $result = $firebase->sendNotification(
|
||||
// $user->fcm_token,
|
||||
// 'Order Confirmed',
|
||||
// "Your order #{$order->id} has been confirmed!",
|
||||
// ['order_id' => $order->id]
|
||||
// );
|
||||
|
||||
// if ($result === true) {
|
||||
// return response()->json(['message' => 'Notification sent successfully']);
|
||||
// } else {
|
||||
// return response()->json(['message' => 'Failed to send notification', 'error' => $result], 500);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Prepare data for markdown email
|
||||
// $data = [
|
||||
// 'customer_name' => $order->customer->name ?? 'Valued Customer',
|
||||
// 'order_id' => $order->tracking_number,
|
||||
// 'order_date' => Carbon::parse($order->created_at)->format('d-m-Y'),
|
||||
// 'payment_method' => $order->payment_method ?? 'N/A',
|
||||
// 'order_status' => $order->order_status ?? 'Pending',
|
||||
// 'products' => $products,
|
||||
// 'subtotal' => $order->amount ?? 0,
|
||||
// 'shipping_cost' => $order->delivery_fee ?? 0,
|
||||
// 'total_amount' => $order->paid_total ?? 0,
|
||||
// 'customer_address' => $order->shipping_address ?? 'N/A',
|
||||
// 'store_name' => get_setting('company_name', $restaurant_id) ?? 'Our Store',
|
||||
// ];
|
||||
// Directly send email
|
||||
// $result = shop_mailer_send($order->restaurant_id, $order->customer?->email, new OrderInvoiceMail($data));
|
||||
// if (! $result['status']) {
|
||||
// Log::warning("Order invoice email not sent for shop {$order->restaurant_id}: " . $result['message']);
|
||||
// }
|
||||
// Mail::to($email)->send(new OrderInvoiceMail($data));
|
||||
|
||||
return $this->responseSuccess($data, 'Reservation created successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$reservation = Reservation::with(['table', 'customer'])->findOrFail($id);
|
||||
|
||||
return $this->responseSuccess($reservation, 'Reservation details fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$reservation = Reservation::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'status' => 'nullable|in:booked,free,upcoming,cancelled',
|
||||
'note' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$reservation->update($validated);
|
||||
|
||||
return $this->responseSuccess($reservation, 'Reservation updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$reservation = Reservation::findOrFail($id);
|
||||
$reservation->delete();
|
||||
|
||||
return $this->responseSuccess([], 'Reservation deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\ReservationSetting\ReservationSettingStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\ReservationSetting\ReservationSettingUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\ReservationSettingRepository;
|
||||
|
||||
class ReservationSettingController extends Controller
|
||||
{
|
||||
public function __construct(private ReservationSettingRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'ReservationSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(ReservationSettingStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'ReservationSetting has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'ReservationSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(ReservationSettingUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'ReservationSetting has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'ReservationSetting has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\Authenticatable;
|
||||
use Exception;
|
||||
use Illuminate\Http\Request;
|
||||
use Modules\Restaurant\Models\ReservationUnavailable;
|
||||
|
||||
class ReservationUnavailableController extends Controller
|
||||
{
|
||||
use Authenticatable;
|
||||
|
||||
/**
|
||||
* Display a listing of unavailable tables.
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
try {
|
||||
$query = ReservationUnavailable::with('table');
|
||||
|
||||
if ($request->filled('table_id')) {
|
||||
$query->where('table_id', $request->table_id);
|
||||
}
|
||||
|
||||
if ($request->filled('date')) {
|
||||
$query->where('date', $request->date);
|
||||
}
|
||||
|
||||
$data = $query->latest()->paginate($request->get('perPage', 10));
|
||||
|
||||
return $this->responseSuccess($data, 'Unavailable table list fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new unavailable record.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
try {
|
||||
$validated = $request->validate([
|
||||
'table_id' => 'required|exists:tables,id',
|
||||
'date' => 'required|date',
|
||||
'start_time' => 'required|date_format:H:i',
|
||||
'end_time' => 'required|date_format:H:i|after:start_time',
|
||||
'reason' => 'nullable|string|max:255',
|
||||
'is_recurring' => 'boolean',
|
||||
]);
|
||||
|
||||
$validated['restaurant_id'] = $this->getCurrentRestaurantId();
|
||||
$data = ReservationUnavailable::create($validated);
|
||||
|
||||
return $this->responseSuccess($data, 'Table marked as unavailable successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show unavailable table details.
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
try {
|
||||
$record = ReservationUnavailable::with('table')->findOrFail($id);
|
||||
|
||||
return $this->responseSuccess($record, 'Unavailable record details fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update unavailable record.
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
try {
|
||||
$record = ReservationUnavailable::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'table_id' => 'sometimes|exists:tables,id',
|
||||
'date' => 'sometimes|date',
|
||||
'start_time' => 'sometimes|date_format:H:i',
|
||||
'end_time' => 'sometimes|date_format:H:i|after:start_time',
|
||||
'reason' => 'nullable|string|max:255',
|
||||
'is_recurring' => 'boolean',
|
||||
]);
|
||||
|
||||
$record->update($validated);
|
||||
|
||||
return $this->responseSuccess($record, 'Unavailable record updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete unavailable record.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
try {
|
||||
$record = ReservationUnavailable::findOrFail($id);
|
||||
$record->delete();
|
||||
|
||||
return $this->responseSuccess([], 'Unavailable record removed successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Http\Requests\RestaurantSchedule\RestaurantScheduleStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\RestaurantSchedule\RestaurantScheduleUpdateRequest;
|
||||
use Modules\Restaurant\Models\RestaurantSchedule;
|
||||
|
||||
class RestaurantScheduleController extends Controller
|
||||
{
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$perPage = $request->input('perPage', 15);
|
||||
$sortBy = $request->input('sortBy', 'id');
|
||||
$sortDir = $request->input('sortDir', 'desc');
|
||||
$search = $request->input('search');
|
||||
|
||||
$query = RestaurantSchedule::query()
|
||||
->with('times:id,restaurant_schedule_id,open_time,close_time')
|
||||
->where('restaurant_id', getUserRestaurantId());
|
||||
|
||||
// 🔍 Search Filter
|
||||
if (! empty($search)) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('day', 'LIKE', "%{$search}%")
|
||||
->orWhere('start_date', 'LIKE', "%{$search}%")
|
||||
->orWhere('end_date', 'LIKE', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 🔽 Sorting
|
||||
$query->orderBy($sortBy, $sortDir);
|
||||
|
||||
// 📄 Pagination
|
||||
$data = $query->paginate($perPage)->appends($request->query());
|
||||
|
||||
return $this->responseSuccess($data, 'Restaurant schedule retrieved successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(RestaurantScheduleStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
foreach ($request->schedules as $item) {
|
||||
|
||||
// Create / Update
|
||||
$schedule = RestaurantSchedule::updateOrCreate([
|
||||
'restaurant_id' => getUserRestaurantId(),
|
||||
'day' => $item['day'],
|
||||
], [
|
||||
'is_open' => $item['is_open'] ?? true,
|
||||
]);
|
||||
|
||||
// Clean old times
|
||||
$schedule->times()->delete();
|
||||
|
||||
// Add new slots
|
||||
foreach ($item['times'] as $slot) {
|
||||
$schedule->times()->create([
|
||||
'open_time' => $slot['open_time'],
|
||||
'close_time' => $slot['close_time'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->responseSuccess([], 'Restaurant schedule added successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$data = RestaurantSchedule::with('times')->findOrFail($id);
|
||||
|
||||
return $this->responseSuccess($data, 'Schedule retrieved successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(RestaurantScheduleUpdateRequest $request, $restaurantId): JsonResponse
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($request->schedules as $item) {
|
||||
|
||||
// Find existing schedule day or create new
|
||||
$schedule = RestaurantSchedule::firstOrCreate(
|
||||
[
|
||||
'restaurant_id' => $restaurantId,
|
||||
'day' => $item['day'],
|
||||
],
|
||||
['is_open' => $item['is_open'] ?? true]
|
||||
);
|
||||
|
||||
// Update is_open if changed
|
||||
$schedule->update(['is_open' => $item['is_open'] ?? true]);
|
||||
|
||||
// Existing time slots
|
||||
$existingTimes = $schedule->times()->get();
|
||||
|
||||
$newTimes = collect($item['times']);
|
||||
|
||||
// Delete removed slots
|
||||
$existingTimes->each(function ($slot) use ($newTimes) {
|
||||
if (! $newTimes->contains(fn ($t) => $t['open_time'] == $slot->open_time && $t['close_time'] == $slot->close_time)) {
|
||||
$slot->delete();
|
||||
}
|
||||
});
|
||||
|
||||
// Add or update new slots
|
||||
foreach ($item['times'] as $slot) {
|
||||
$schedule->times()->updateOrCreate(
|
||||
[
|
||||
'open_time' => $slot['open_time'],
|
||||
'close_time' => $slot['close_time'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $this->responseSuccess([], 'Restaurant schedule updated successfully (attach/detach).');
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$schedule = RestaurantSchedule::findOrFail($id);
|
||||
$schedule->times()->delete();
|
||||
$schedule->delete();
|
||||
|
||||
return $this->responseSuccess([], 'Schedule deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Supplier\SupplierStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Supplier\SupplierUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\SupplierRepository;
|
||||
|
||||
class SupplierController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private SupplierRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Supplier has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(SupplierStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Supplier has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Supplier has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(SupplierUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Supplier has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Supplier has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Traits\RequestSanitizerTrait;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Table\TableStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Table\TableUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\TableRepository;
|
||||
|
||||
class TableController extends Controller
|
||||
{
|
||||
use RequestSanitizerTrait;
|
||||
|
||||
public function __construct(private TableRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Table has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(TableStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Table has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Table has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(TableUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Table has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Table has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\TaxSetting\TaxSettingStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\TaxSetting\TaxSettingUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\TaxSettingRepository;
|
||||
|
||||
class TaxSettingController extends Controller
|
||||
{
|
||||
public function __construct(private TaxSettingRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'TaxSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(TaxSettingStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'TaxSetting has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'TaxSetting has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(TaxSettingUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'TaxSetting has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'TaxSetting has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Unit\UnitStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Unit\UnitUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\UnitRepository;
|
||||
|
||||
class UnitController extends Controller
|
||||
{
|
||||
public function __construct(private UnitRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Unit has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(UnitStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Unit has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Unit has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(UnitUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Unit has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Unit has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Modules\Restaurant\Http\Requests\Zone\ZoneStoreRequest;
|
||||
use Modules\Restaurant\Http\Requests\Zone\ZoneUpdateRequest;
|
||||
use Modules\Restaurant\Repositories\ZoneRepository;
|
||||
|
||||
class ZoneController extends Controller
|
||||
{
|
||||
public function __construct(private ZoneRepository $repo) {}
|
||||
|
||||
public function index(): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getAll(request()->all()), 'Zone has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function store(ZoneStoreRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->create($request->all()), 'Zone has been created successfully.');
|
||||
} catch (\Illuminate\Database\QueryException $exception) {
|
||||
return $this->responseError([], 'Database error: '.$exception->getMessage());
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->getById($id), 'Zone has been fetched successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update(ZoneUpdateRequest $request, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->update($id, $request->all()), 'Zone has been updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
return $this->responseSuccess($this->repo->delete($id), 'Zone has been deleted successfully.');
|
||||
} catch (Exception $e) {
|
||||
return $this->responseError([], $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Addon;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AddonStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'price' => 'required|numeric|min:0|max:999999.99',
|
||||
'vat' => 'nullable|numeric|min:0|max:99.99',
|
||||
'pst' => 'nullable|numeric|min:0|max:99.99',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Addon;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AddonUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'price' => 'required|numeric|min:0|max:999999.99',
|
||||
'vat' => 'nullable|numeric|min:0|max:99.99',
|
||||
'pst' => 'nullable|numeric|min:0|max:99.99',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\AddonFood;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AddonFoodStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'addon_id' => 'required|integer|exists:addons,id',
|
||||
'food_item_id' => 'required|integer|exists:food_items,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\AddonFood;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AddonFoodUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'addon_id' => 'required|integer|exists:addons,id',
|
||||
'food_item_id' => 'required|integer|exists:food_items,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Customer;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CustomerStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'nullable|email|max:255',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'avatar' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
|
||||
'status' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Customer;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CustomerUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'nullable|email|max:255',
|
||||
'phone' => 'nullable|string|max:20',
|
||||
'address' => 'nullable|string|max:255',
|
||||
'avatar' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
|
||||
'status' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\CustomerAddress;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CustomerAddressStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\CustomerAddress;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CustomerAddressUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\DeliveryCharge;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DeliveryChargeStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\DeliveryCharge;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class DeliveryChargeUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodAvailability;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodAvailabilityStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'required|exists:food_items,id',
|
||||
'availability' => 'required|array|min:1',
|
||||
'availability.*.day' => 'required|in:Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday',
|
||||
'availability.*.times' => 'required|array|min:1',
|
||||
'availability.*.times.*.open_time' => 'required|date_format:H:i',
|
||||
'availability.*.times.*.close_time' => 'required|date_format:H:i|after:availability.*.times.*.open_time',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodAvailability;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodAvailabilityUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'availability' => 'required|array|min:1',
|
||||
'availability.*.day' => 'required|in:Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday',
|
||||
'availability.*.times' => 'required|array|min:1',
|
||||
'availability.*.times.*.open_time' => 'required|date_format:H:i',
|
||||
'availability.*.times.*.close_time' => 'required|date_format:H:i|after:availability.*.times.*.open_time',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodCategory;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodCategoryStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048', // for file upload
|
||||
'parent_id' => 'nullable|exists:food_categories,id',
|
||||
'is_offer' => 'nullable|boolean',
|
||||
'start_date' => 'nullable|date|before_or_equal:end_date',
|
||||
'end_date' => 'nullable|date|after_or_equal:start_date',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodCategory;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodCategoryUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'image' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048', // for file upload
|
||||
'parent_id' => 'nullable|exists:food_categories,id',
|
||||
'is_offer' => 'nullable|boolean',
|
||||
'start_date' => 'nullable|date|before_or_equal:end_date',
|
||||
'end_date' => 'nullable|date|after_or_equal:start_date',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodItem;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class FoodItemStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'category_id' => 'required|exists:food_categories,id',
|
||||
'menu_category_id' => 'nullable|exists:menu_categories,id',
|
||||
'menu_section_id' => 'nullable|exists:menu_sections,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'slug' => 'nullable|string|max:255|unique:food_items,slug',
|
||||
'description' => 'nullable|string',
|
||||
'points' => 'nullable',
|
||||
'food_type' => 'required|in:Veg,Non-Veg',
|
||||
'is_featured' => 'nullable',
|
||||
'is_party' => 'nullable',
|
||||
'is_dinner' => 'nullable',
|
||||
'is_lunch' => 'nullable',
|
||||
'is_popular_item' => 'nullable',
|
||||
'is_chef_special' => 'nullable',
|
||||
'prep_time' => [
|
||||
'nullable',
|
||||
'regex:/^\d{2}:\d{2}(:\d{2})?$/',
|
||||
],
|
||||
'cooking_time' => [
|
||||
'nullable',
|
||||
'regex:/^\d{2}:\d{2}(:\d{2})?$/',
|
||||
],
|
||||
'vat' => 'nullable',
|
||||
'pst' => 'nullable',
|
||||
'ingredients_cost' => 'nullable',
|
||||
'allergens' => 'nullable|string|max:255',
|
||||
'notes' => 'nullable|string',
|
||||
'image' => 'nullable|image|mimes:jpg,jpeg,png|max:2048',
|
||||
|
||||
'variants' => 'required|array|min:1',
|
||||
'variants.*.name' => 'required|string|max:255',
|
||||
'variants.*.price' => 'required',
|
||||
'variants.*.offer_price' => 'nullable',
|
||||
'variants.*.discount' => 'nullable',
|
||||
'variants.*.unit_id' => [
|
||||
'required',
|
||||
Rule::exists('units', 'id')->whereNull('deleted_at'),
|
||||
],
|
||||
'variants.*.is_default' => 'required',
|
||||
'variants.*.stock_tracking' => 'nullable',
|
||||
'variants.*.ingredients_cost' => 'nullable',
|
||||
'variants.*.profit_margin' => 'nullable',
|
||||
'variants.*.alert_stock_quantity' => 'nullable',
|
||||
'variants.*.combo_type' => 'nullable|in:single,combo,add-on',
|
||||
'variants.*.is_active_offer' => 'nullable',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodItem;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class FoodItemUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'category_id' => 'required|exists:food_categories,id',
|
||||
'menu_category_id' => 'nullable|exists:menu_categories,id',
|
||||
'menu_section_id' => 'nullable|exists:menu_sections,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'points' => 'nullable',
|
||||
'food_type' => 'required|in:Veg,Non-Veg',
|
||||
'is_party' => 'nullable',
|
||||
'is_dinner' => 'nullable',
|
||||
'is_lunch' => 'nullable',
|
||||
'is_popular_item' => 'nullable',
|
||||
'is_chef_special' => 'nullable',
|
||||
'cooking_time' => 'required',
|
||||
'components' => 'nullable|string',
|
||||
'variants' => 'required|array|min:1',
|
||||
'variants.*.name' => 'required|string|max:255',
|
||||
'variants.*.price' => 'required',
|
||||
'variants.*.unit_id' => [
|
||||
'required',
|
||||
Rule::exists('units', 'id')->whereNull('deleted_at'),
|
||||
],
|
||||
'variants.*.is_default' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodReview;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodReviewStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'required|integer|exists:food_items,id',
|
||||
'rating' => 'required|integer|min:1|max:5',
|
||||
'review' => 'required|string',
|
||||
'images.*' => 'nullable|image|max:2048',
|
||||
'video_url' => 'nullable|url',
|
||||
'video_file' => 'nullable|mimes:mp4,webm,mov|max:20480',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodReview;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodReviewUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'rating' => 'required|integer|min:1|max:5',
|
||||
'review' => 'required|string',
|
||||
'images.*' => 'nullable|image|max:2048',
|
||||
'video_url' => 'nullable|url',
|
||||
'video_file' => 'nullable|mimes:mp4,webm,mov|max:20480',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodReviewReply;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodReviewReplyStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'review_id' => 'required|exists:food_reviews,id',
|
||||
'message' => 'nullable|string',
|
||||
'attachments.*' => 'nullable|file|mimes:jpg,jpeg,png,pdf,docx,mp4,webm|max:20480', // 20MB max
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodReviewReply;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodReviewReplyUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'message' => 'nullable|string',
|
||||
'attachments.*' => 'nullable|file|mimes:jpg,jpeg,png,pdf,docx,mp4,webm|max:20480',
|
||||
'remove_attachments' => 'nullable|array', // names of attachments to remove
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodVariant;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodVariantStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'required|integer|exists:food_items,id',
|
||||
'unit_id' => 'required|exists:units,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'price' => 'required|numeric|min:0|max:999999.99',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodVariant;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodVariantUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'required|integer|exists:food_items,id',
|
||||
'unit_id' => 'required|exists:units,id',
|
||||
'name' => 'required|string|max:255',
|
||||
'price' => 'required|numeric|min:0|max:999999.99',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodVariantIngredient;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodVariantIngredientStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_variant_id' => 'required|exists:food_variants,id',
|
||||
'ingredients' => 'required|array|min:1',
|
||||
'ingredients.*.ingredient_id' => 'required|exists:ingredients,id',
|
||||
'ingredients.*.quantity' => 'required|numeric|min:0',
|
||||
'ingredients.*.wastage_percentage' => 'nullable|numeric|min:0',
|
||||
'ingredients.*.unit_conversion_factor' => 'nullable|numeric|min:0',
|
||||
'ingredients.*.optional' => 'nullable|in:Y,N',
|
||||
'ingredients.*.notes' => 'nullable|string',
|
||||
'ingredients.*.status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodVariantIngredient;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodVariantIngredientUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_variant_id' => 'sometimes|required|exists:food_variants,id',
|
||||
'ingredient_id' => 'sometimes|required|exists:ingredients,id',
|
||||
'quantity' => 'sometimes|required|numeric|min:0',
|
||||
'wastage_percentage' => 'nullable|numeric|min:0',
|
||||
'unit_conversion_factor' => 'nullable|numeric|min:0',
|
||||
'optional' => 'nullable|in:Y,N',
|
||||
'notes' => 'nullable|string',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodWaste;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodWasteStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'required|exists:food_items,id',
|
||||
'food_variant_id' => 'nullable|exists:food_variants,id',
|
||||
'quantity' => 'required|numeric|min:0.01',
|
||||
'reason' => 'required|string|max:255',
|
||||
'notes' => 'nullable|string',
|
||||
'wasted_at' => 'nullable|date',
|
||||
'approve' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\FoodWaste;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class FoodWasteUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'required|exists:food_items,id',
|
||||
'food_variant_id' => 'nullable|exists:food_variants,id',
|
||||
'quantity' => 'required|numeric|min:0.01',
|
||||
'reason' => 'required|string|max:255',
|
||||
'notes' => 'nullable|string',
|
||||
'wasted_at' => 'nullable|date',
|
||||
'approve' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Ingredient;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class IngredientStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// ===== Basic Info =====
|
||||
'name' => 'required|string|max:255',
|
||||
'unit_id' => 'required|exists:units,id',
|
||||
'stock_quantity' => 'nullable|numeric|min:0',
|
||||
'cost_per_unit' => 'nullable|numeric|min:0',
|
||||
'low_stock_alert' => 'nullable|numeric|min:0',
|
||||
'image' => 'nullable|image',
|
||||
|
||||
// ===== Supplier & Purchase Info =====
|
||||
'supplier_id' => 'nullable|exists:suppliers,id',
|
||||
'last_purchase_price' => 'nullable|numeric|min:0',
|
||||
'last_purchase_date' => 'nullable|date',
|
||||
|
||||
// ===== Units & Conversions =====
|
||||
'conversion_factor' => 'nullable|numeric|min:0.01',
|
||||
|
||||
// ===== Stock & Inventory =====
|
||||
'reserved_quantity' => 'nullable|numeric|min:0',
|
||||
'wastage_quantity' => 'nullable|numeric|min:0',
|
||||
|
||||
// ===== Categorization =====
|
||||
'category' => 'nullable|string|max:100',
|
||||
'notes' => 'nullable|string|max:2000',
|
||||
|
||||
// ===== Status =====
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Ingredient;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class IngredientUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// ===== Basic Info =====
|
||||
'name' => 'required|string|max:255',
|
||||
'unit_id' => 'required|exists:units,id',
|
||||
'stock_quantity' => 'nullable|numeric|min:0',
|
||||
'cost_per_unit' => 'nullable|numeric|min:0',
|
||||
'low_stock_alert' => 'nullable|numeric|min:0',
|
||||
'image' => 'nullable|image',
|
||||
|
||||
// ===== Supplier & Purchase Info =====
|
||||
'supplier_id' => 'nullable|exists:suppliers,id',
|
||||
'last_purchase_price' => 'nullable|numeric|min:0',
|
||||
'last_purchase_date' => 'nullable|date',
|
||||
|
||||
// ===== Units & Conversions =====
|
||||
'conversion_factor' => 'nullable|numeric|min:0.01',
|
||||
|
||||
// ===== Stock & Inventory =====
|
||||
'reserved_quantity' => 'nullable|numeric|min:0',
|
||||
'wastage_quantity' => 'nullable|numeric|min:0',
|
||||
|
||||
// ===== Categorization =====
|
||||
'category' => 'nullable|string|max:100',
|
||||
'notes' => 'nullable|string|max:2000',
|
||||
|
||||
// ===== Status =====
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\IngredientDamage;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class IngredientDamageStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'purchase_id' => ['required', 'exists:purchases,id'],
|
||||
'ingredient_id' => ['required', 'exists:ingredients,id'],
|
||||
'batch_no' => ['nullable', 'string'],
|
||||
'quantity' => ['required', 'numeric', 'min:0.01'],
|
||||
'unit_cost' => ['nullable', 'numeric', 'min:0'],
|
||||
'reason' => ['nullable', 'string'],
|
||||
'damage_date' => ['nullable', 'date'],
|
||||
'reported_by' => ['nullable', 'exists:users,id'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\IngredientDamage;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class IngredientDamageUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Kitchen;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class KitchenStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:100'],
|
||||
'serial' => ['required', 'integer', 'min:0'],
|
||||
'status' => ['nullable', 'in:0,1'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Kitchen;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class KitchenUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:100'],
|
||||
'serial' => ['required', 'integer', 'min:0'],
|
||||
'status' => ['nullable', 'in:0,1'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\KitchenAssign;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class KitchenAssignStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'kitchen_id' => ['required', 'integer', 'exists:kitchens,id'],
|
||||
'user_id' => ['required', 'integer', 'exists:users,id'],
|
||||
'status' => ['nullable', 'in:0,1'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\KitchenAssign;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class KitchenAssignUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'kitchen_id' => ['required', 'integer', 'exists:kitchens,id'],
|
||||
'user_id' => ['required', 'integer', 'exists:users,id'],
|
||||
'status' => ['nullable', 'in:0,1'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\LoyaltyPoint;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoyaltyPointStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'order_id' => 'nullable|exists:orders,id',
|
||||
'food_item_id' => 'nullable|exists:food_items,id',
|
||||
'points' => 'required',
|
||||
'type' => 'required|in:earned,redeemed,manual',
|
||||
'reason' => 'nullable|string|max:255',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\LoyaltyPoint;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoyaltyPointUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'order_id' => 'nullable|exists:orders,id',
|
||||
'food_item_id' => 'nullable|exists:food_items,id',
|
||||
'points' => 'required',
|
||||
'type' => 'required|in:earned,redeemed,manual',
|
||||
'reason' => 'nullable|string|max:255',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\LoyaltyPointSetting;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoyaltyPointSettingStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:12'],
|
||||
'from_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'to_amount' => ['nullable', 'numeric', 'min:0', 'gte:from_amount'],
|
||||
'amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'earn_point' => ['required', 'integer', 'min:0'],
|
||||
'status' => ['nullable', 'integer', 'in:0,1'], // 1=Active, 2=InActive
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\LoyaltyPointSetting;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoyaltyPointSettingUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:12'],
|
||||
'from_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'to_amount' => ['nullable', 'numeric', 'min:0', 'gte:from_amount'],
|
||||
'amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'earn_point' => ['required', 'integer', 'min:0'],
|
||||
'status' => ['nullable', 'integer', 'in:0,1'], // 1=Active, 2=InActive
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\LoyaltyRedemption;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoyaltyRedemptionStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'nullable|exists:food_items,id',
|
||||
'points_used' => 'required',
|
||||
'price' => 'nullable|numeric|min:0',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\LoyaltyRedemption;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoyaltyRedemptionUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'food_item_id' => 'nullable|exists:food_items,id',
|
||||
'points_used' => 'required',
|
||||
'price' => 'nullable|numeric|min:0',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\MenuCategory;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MenuCategoryStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:100',
|
||||
'slug' => 'nullable|string|max:150',
|
||||
'icon' => 'nullable|image',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'status' => 'required|boolean',
|
||||
'priority' => 'nullable|integer|min:0',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'name.required' => 'Category name is required.',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\MenuCategory;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MenuCategoryUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:100',
|
||||
'slug' => 'nullable|string|max:150',
|
||||
'icon' => 'nullable|image',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'status' => 'required|boolean',
|
||||
'priority' => 'nullable|integer|min:0',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'name.required' => 'Category name is required.',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\MenuSection;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MenuSectionStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'menu_category_id' => 'required|exists:menu_categories,id',
|
||||
'name' => 'required|string|max:100',
|
||||
'slug' => 'nullable|string|max:150',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'priority' => 'nullable|integer|min:0',
|
||||
'status' => 'required|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'menu_category_id.required' => 'Please select a menu category.',
|
||||
'menu_category_id.exists' => 'The selected category does not exist.',
|
||||
'name.required' => 'Section name is required.',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\MenuSection;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MenuSectionUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'menu_category_id' => 'required|exists:menu_categories,id',
|
||||
'name' => 'required|string|max:100',
|
||||
'slug' => 'nullable|string|max:150',
|
||||
'description' => 'nullable|string|max:500',
|
||||
'priority' => 'nullable|integer|min:0',
|
||||
'status' => 'required|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'menu_category_id.required' => 'Please select a menu category.',
|
||||
'menu_category_id.exists' => 'The selected category does not exist.',
|
||||
'name.required' => 'Section name is required.',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\MenuType;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MenuTypeStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'icon' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048', // for file upload
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\MenuType;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MenuTypeUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'icon' => 'nullable|image|mimes:jpeg,png,jpg,webp|max:2048', // for file upload
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Order;
|
||||
|
||||
use App\Enum\OrderStatus;
|
||||
use App\Enum\OrderType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CustomerOrderStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// 🔹 Core order fields
|
||||
'order_type' => 'required|in:'.implode(',', OrderType::values()),
|
||||
'status' => 'nullable|in:'.implode(',', OrderStatus::values()),
|
||||
|
||||
'table_id' => 'nullable|exists:tables,id',
|
||||
'waiter_id' => 'nullable|exists:users,id',
|
||||
'driver_id' => 'nullable|exists:users,id',
|
||||
'customer_id' => 'nullable|exists:customers,id',
|
||||
|
||||
// 🔹 Order identification
|
||||
'order_number' => 'nullable|string|max:50|unique:orders,order_number',
|
||||
'reference' => 'nullable|string|max:100',
|
||||
|
||||
// 🔹 Customer info
|
||||
'customer_name' => 'nullable|string|max:255',
|
||||
'customer_phone' => 'nullable|string|max:20',
|
||||
'delivery_address' => 'nullable|string|max:500',
|
||||
|
||||
// 🔹 Financials
|
||||
'subtotal' => 'nullable|numeric|min:0',
|
||||
'discount' => 'nullable|numeric|min:0',
|
||||
'tax' => 'nullable|numeric|min:0',
|
||||
'service_charge' => 'nullable|numeric|min:0',
|
||||
'tip_amount' => 'nullable|numeric|min:0',
|
||||
'grand_total' => 'required|numeric|min:0',
|
||||
|
||||
// 🔹 Payment
|
||||
'payment_method_id' => 'required|exists:payment_methods,id',
|
||||
'payment_status' => 'nullable|boolean',
|
||||
'payment_reference' => 'nullable|string|max:100',
|
||||
'paid_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Operational
|
||||
'order_source' => 'nullable|in:POS,Web,MobileApp,ThirdParty',
|
||||
'estimated_ready_time' => 'nullable|date',
|
||||
'delivered_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Items (nested validation)
|
||||
'items.*.food_variant_id' => 'nullable|exists:food_variants,id',
|
||||
'items.*.addons' => 'nullable|array',
|
||||
'items.*.addons.*.name' => 'nullable|string|max:255',
|
||||
'items.*.addons.*.price' => 'nullable|numeric|min:0',
|
||||
|
||||
// 🔹 Audit
|
||||
'added_by' => 'nullable|exists:users,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Order;
|
||||
|
||||
use App\Enum\OrderStatus;
|
||||
use App\Enum\OrderType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CustomerOrderUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// 🔹 Core order fields
|
||||
'order_type' => 'required|in:'.implode(',', OrderType::values()),
|
||||
'status' => 'nullable|in:'.implode(',', OrderStatus::values()),
|
||||
|
||||
'table_id' => 'nullable|exists:tables,id',
|
||||
'waiter_id' => 'nullable|exists:users,id',
|
||||
'driver_id' => 'nullable|exists:users,id',
|
||||
'customer_id' => 'nullable|exists:customers,id',
|
||||
|
||||
// 🔹 Order identification
|
||||
'order_number' => 'nullable|string|max:50|unique:orders,order_number',
|
||||
'reference' => 'nullable|string|max:100',
|
||||
|
||||
// 🔹 Customer info
|
||||
'customer_name' => 'nullable|string|max:255',
|
||||
'customer_phone' => 'nullable|string|max:20',
|
||||
'delivery_address' => 'nullable|string|max:500',
|
||||
|
||||
// 🔹 Financials
|
||||
'subtotal' => 'nullable|numeric|min:0',
|
||||
'discount' => 'nullable|numeric|min:0',
|
||||
'tax' => 'nullable|numeric|min:0',
|
||||
'service_charge' => 'nullable|numeric|min:0',
|
||||
'tip_amount' => 'nullable|numeric|min:0',
|
||||
'grand_total' => 'required|numeric|min:0',
|
||||
|
||||
// 🔹 Payment
|
||||
'payment_method_id' => 'required|exists:payment_methods,id',
|
||||
'payment_status' => 'nullable|boolean',
|
||||
'payment_reference' => 'nullable|string|max:100',
|
||||
'paid_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Operational
|
||||
'order_source' => 'nullable|in:POS,Web,MobileApp,ThirdParty',
|
||||
'estimated_ready_time' => 'nullable|date',
|
||||
'delivered_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Items (nested validation)
|
||||
'items' => 'required|array|min:1',
|
||||
'items.*.food_item_id' => 'required|exists:food_items,id',
|
||||
'items.*.quantity' => 'required|integer|min:1',
|
||||
'items.*.price' => 'required|numeric|min:0',
|
||||
|
||||
// 🔹 Audit
|
||||
'updated_by' => 'nullable|exists:users,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Order;
|
||||
|
||||
use App\Enum\OrderStatus;
|
||||
use App\Enum\OrderType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// 🔹 Core order fields
|
||||
'order_type' => 'required|in:'.implode(',', OrderType::values()),
|
||||
'status' => 'required|in:'.implode(',', OrderStatus::values()),
|
||||
|
||||
'table_id' => 'nullable|exists:tables,id',
|
||||
'waiter_id' => 'nullable|exists:users,id',
|
||||
'driver_id' => 'nullable|exists:users,id',
|
||||
'customer_id' => 'nullable|exists:customers,id',
|
||||
|
||||
// 🔹 Order identification
|
||||
'order_number' => 'nullable|string|max:50|unique:orders,order_number',
|
||||
'reference' => 'nullable|string|max:100',
|
||||
|
||||
// 🔹 Customer info
|
||||
'customer_name' => 'nullable|string|max:255',
|
||||
'customer_phone' => 'nullable|string|max:20',
|
||||
'delivery_address' => 'nullable|string|max:500',
|
||||
|
||||
// 🔹 Financials
|
||||
'subtotal' => 'nullable|numeric|min:0',
|
||||
'discount' => 'nullable|numeric|min:0',
|
||||
'tax' => 'nullable|numeric|min:0',
|
||||
'service_charge' => 'nullable|numeric|min:0',
|
||||
'tip_amount' => 'nullable|numeric|min:0',
|
||||
'grand_total' => 'required|numeric|min:0',
|
||||
|
||||
// 🔹 Payment
|
||||
'payment_method_id' => 'required|exists:payment_methods,id',
|
||||
'payment_status' => 'nullable|boolean',
|
||||
'payment_reference' => 'nullable|string|max:100',
|
||||
'paid_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Operational
|
||||
'order_source' => 'nullable|in:POS,Web,MobileApp,ThirdParty',
|
||||
'estimated_ready_time' => 'nullable|date',
|
||||
'delivered_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Items (nested validation)
|
||||
'items.*.food_variant_id' => 'nullable|exists:food_variants,id',
|
||||
'items.*.addons' => 'nullable|array',
|
||||
'items.*.addons.*.name' => 'nullable|string|max:255',
|
||||
'items.*.addons.*.price' => 'nullable|numeric|min:0',
|
||||
|
||||
// 🔹 Audit
|
||||
'added_by' => 'nullable|exists:users,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Order;
|
||||
|
||||
use App\Enum\OrderStatus;
|
||||
use App\Enum\OrderType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// 🔹 Core order fields
|
||||
'order_type' => 'required|in:'.implode(',', OrderType::values()),
|
||||
'status' => 'required|in:'.implode(',', OrderStatus::values()),
|
||||
|
||||
'table_id' => 'nullable|exists:tables,id',
|
||||
'waiter_id' => 'nullable|exists:users,id',
|
||||
'driver_id' => 'nullable|exists:users,id',
|
||||
'customer_id' => 'nullable|exists:customers,id',
|
||||
|
||||
// 🔹 Order identification
|
||||
'order_number' => 'nullable|string|max:50|unique:orders,order_number',
|
||||
'reference' => 'nullable|string|max:100',
|
||||
|
||||
// 🔹 Customer info
|
||||
'customer_name' => 'nullable|string|max:255',
|
||||
'customer_phone' => 'nullable|string|max:20',
|
||||
'delivery_address' => 'nullable|string|max:500',
|
||||
|
||||
// 🔹 Financials
|
||||
'subtotal' => 'nullable|numeric|min:0',
|
||||
'discount' => 'nullable|numeric|min:0',
|
||||
'tax' => 'nullable|numeric|min:0',
|
||||
'service_charge' => 'nullable|numeric|min:0',
|
||||
'tip_amount' => 'nullable|numeric|min:0',
|
||||
'grand_total' => 'required|numeric|min:0',
|
||||
|
||||
// 🔹 Payment
|
||||
'payment_method_id' => 'required|exists:payment_methods,id',
|
||||
'payment_status' => 'nullable|boolean',
|
||||
'payment_reference' => 'nullable|string|max:100',
|
||||
'paid_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Operational
|
||||
'order_source' => 'nullable|in:POS,Web,MobileApp,ThirdParty',
|
||||
'estimated_ready_time' => 'nullable|date',
|
||||
'delivered_at' => 'nullable|date',
|
||||
|
||||
// 🔹 Items (nested validation)
|
||||
'items' => 'required|array|min:1',
|
||||
'items.*.food_item_id' => 'required|exists:food_items,id',
|
||||
'items.*.quantity' => 'required|integer|min:1',
|
||||
'items.*.price' => 'required|numeric|min:0',
|
||||
|
||||
// 🔹 Audit
|
||||
'updated_by' => 'nullable|exists:users,id',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\OrderItem;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderItemStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\OrderItem;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderItemUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\OrderTracking;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderTrackingStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\OrderTracking;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class OrderTrackingUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\PaymentMethod;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PaymentMethodStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'type' => 'nullable|in:gateway,cash,card,wallet',
|
||||
'credentials' => 'nullable|array', // since it's JSON, validate as array
|
||||
'is_default' => 'nullable|boolean',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\PaymentMethod;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PaymentMethodUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'type' => 'nullable|in:gateway,cash,card,wallet',
|
||||
'credentials' => 'nullable|array', // since it's JSON, validate as array
|
||||
'is_default' => 'nullable|boolean',
|
||||
'status' => 'nullable|in:0,1',
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Purchase;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PurchaseStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'supplier_id' => ['nullable', 'exists:suppliers,id'],
|
||||
'purchase_date' => ['required', 'date'],
|
||||
|
||||
// Financials
|
||||
'sub_total' => ['required', 'numeric', 'min:0'],
|
||||
'discount_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'tax_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'total_amount' => ['required', 'numeric', 'min:0'],
|
||||
'payment_status' => ['nullable', Rule::in(['paid', 'unpaid', 'partial'])],
|
||||
'payment_method' => ['nullable', Rule::in(['cash', 'bank', 'credit'])],
|
||||
'purchase_type' => ['nullable', Rule::in(['ingredient', 'equipment', 'others'])],
|
||||
'created_by' => ['nullable', 'exists:users,id'],
|
||||
'notes' => ['nullable', 'string'],
|
||||
|
||||
// Items array
|
||||
'items' => ['required', 'array', 'min:1'],
|
||||
'items.*.ingredient_id' => ['required', 'exists:ingredients,id'],
|
||||
'items.*.food_variant_id' => ['nullable', 'exists:food_variants,id'],
|
||||
'items.*.quantity' => ['required', 'numeric', 'min:0.01'],
|
||||
'items.*.unit_price' => ['required', 'numeric', 'min:0'],
|
||||
'items.*.total_cost' => ['required', 'numeric', 'min:0'],
|
||||
'items.*.tax_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'items.*.discount_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'items.*.batch_no' => ['nullable', 'string'],
|
||||
'items.*.expiry_date' => ['nullable', 'date'],
|
||||
'items.*.received_quantity' => ['nullable', 'numeric', 'min:0'],
|
||||
'items.*.wastage_quantity' => ['nullable', 'numeric', 'min:0'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\Purchase;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PurchaseUpdateRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'supplier_id' => ['nullable', 'exists:suppliers,id'],
|
||||
'purchase_date' => ['required', 'date'],
|
||||
|
||||
// Financials
|
||||
'sub_total' => ['required', 'numeric', 'min:0'],
|
||||
'discount_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'tax_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'total_amount' => ['required', 'numeric', 'min:0'],
|
||||
|
||||
'payment_status' => ['nullable', Rule::in(['paid', 'unpaid', 'partial'])],
|
||||
'payment_method' => ['nullable', Rule::in(['cash', 'bank', 'credit'])],
|
||||
'purchase_type' => ['nullable', Rule::in(['ingredient', 'equipment', 'others'])],
|
||||
'created_by' => ['nullable', 'exists:users,id'],
|
||||
'notes' => ['nullable', 'string'],
|
||||
|
||||
// Items
|
||||
'items' => ['required', 'array', 'min:1'],
|
||||
|
||||
// id is optional — if present = update, if missing = create
|
||||
'items.*.id' => ['nullable', 'exists:purchase_items,id'],
|
||||
|
||||
'items.*.ingredient_id' => ['required', 'exists:ingredients,id'],
|
||||
'items.*.food_variant_id' => ['nullable', 'exists:food_variants,id'],
|
||||
'items.*.quantity' => ['required', 'numeric', 'min:0.01'],
|
||||
'items.*.unit_price' => ['required', 'numeric', 'min:0'],
|
||||
'items.*.total_cost' => ['required', 'numeric', 'min:0'],
|
||||
'items.*.tax_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'items.*.discount_amount' => ['nullable', 'numeric', 'min:0'],
|
||||
'items.*.batch_no' => ['nullable', 'string'],
|
||||
'items.*.expiry_date' => ['nullable', 'date'],
|
||||
'items.*.received_quantity' => ['nullable', 'numeric', 'min:0'],
|
||||
'items.*.wastage_quantity' => ['nullable', 'numeric', 'min:0'],
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Restaurant\Http\Requests\PurchaseItem;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PurchaseItemStoreRequest extends FormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
// validation rules
|
||||
];
|
||||
}
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user