migrate to gtea from bistbucket
This commit is contained in:
140
public/restaurant/app/Helper/DomainHelper.php
Normal file
140
public/restaurant/app/Helper/DomainHelper.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helper;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class DomainHelper
|
||||
{
|
||||
/**
|
||||
* Get domain info from params or APP_URL, store in session.
|
||||
*/
|
||||
public static function getDomainInfo(?string $domain = null): array
|
||||
{
|
||||
$from = 'host';
|
||||
|
||||
if ($domain) {
|
||||
$from = 'params';
|
||||
} else {
|
||||
$appUrl = config('app.url');
|
||||
$parsedUrl = parse_url($appUrl, PHP_URL_HOST);
|
||||
$domain = $parsedUrl ?: $appUrl;
|
||||
}
|
||||
|
||||
// Check if info already in session
|
||||
$sessionKey = 'domain_info_'.md5($domain);
|
||||
if (Session::has($sessionKey)) {
|
||||
$info = Session::get($sessionKey);
|
||||
$info['from'] = $from;
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = new Client(['timeout' => 10]);
|
||||
$url = 'https://rdap.org/domain/'.urlencode($domain);
|
||||
$res = $client->get($url);
|
||||
$json = json_decode((string) $res->getBody(), true);
|
||||
|
||||
$registration = null;
|
||||
$expiration = null;
|
||||
$eventsList = [];
|
||||
|
||||
if (! empty($json['events']) && is_array($json['events'])) {
|
||||
foreach ($json['events'] as $ev) {
|
||||
if (! empty($ev['eventAction']) && ! empty($ev['eventDate'])) {
|
||||
$type = strtolower($ev['eventAction']);
|
||||
$date = $ev['eventDate'];
|
||||
|
||||
$eventsList[] = [
|
||||
'type' => $type,
|
||||
'date' => $date,
|
||||
];
|
||||
|
||||
if (strpos($type, 'registration') !== false) {
|
||||
$registration = $date;
|
||||
}
|
||||
if (strpos($type, 'expiration') !== false) {
|
||||
$expiration = $date;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$info = [
|
||||
'domain' => $domain,
|
||||
'registration' => $registration ? Carbon::parse($registration)->format('d-F-Y h:i A') : null,
|
||||
'expiration' => $expiration ? Carbon::parse($expiration)->format('d-F-Y h:i A') : null,
|
||||
'events' => $eventsList,
|
||||
'registrar' => $json['entities'][0]['vcardArray'][1][1][3] ?? $json['registrar'] ?? null,
|
||||
'status' => $json['status'] ?? [],
|
||||
'nameservers' => array_map(fn ($ns) => $ns['ldhName'] ?? null, $json['nameservers'] ?? []),
|
||||
'from' => $from,
|
||||
];
|
||||
|
||||
// Store in session
|
||||
Session::put($sessionKey, $info);
|
||||
|
||||
return $info;
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'from' => $from,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check expiration warning (within 60 days, strong alert under 30 days)
|
||||
*
|
||||
* @param string|null $expirationDate ISO8601
|
||||
* @return array ['warning' => bool, 'days_left' => int, 'level' => string, 'message' => ?string]
|
||||
*/
|
||||
public static function expirationWarning(?string $expirationDate): array
|
||||
{
|
||||
if (! $expirationDate) {
|
||||
return [
|
||||
'warning' => false,
|
||||
'days_left' => null,
|
||||
'level' => 'none',
|
||||
'message' => null,
|
||||
];
|
||||
}
|
||||
|
||||
$expiration = Carbon::parse($expirationDate);
|
||||
$now = Carbon::now();
|
||||
|
||||
// Ensure whole day difference (no fractions)
|
||||
$daysLeft = (int) floor($now->diffInDays($expiration, false));
|
||||
|
||||
// Define thresholds
|
||||
$warning = $daysLeft <= 60 && $daysLeft >= 0;
|
||||
$level = 'normal';
|
||||
$message = null;
|
||||
|
||||
if ($daysLeft < 0) {
|
||||
$level = 'expired';
|
||||
$message = "❌ Your domain expired on {$expiration->format('Y-m-d')}. Please renew immediately!";
|
||||
} elseif ($daysLeft <= 7) {
|
||||
$level = 'critical';
|
||||
$message = "⚠️ Domain expiring very soon! Only {$daysLeft} days left (expires {$expiration->format('Y-m-d')}).";
|
||||
} elseif ($daysLeft <= 30) {
|
||||
$level = 'warning';
|
||||
$message = "⚠️ Please renew your domain soon. {$daysLeft} days remaining (expires {$expiration->format('Y-m-d')}).";
|
||||
} elseif ($daysLeft <= 60) {
|
||||
$level = 'notice';
|
||||
$message = "ℹ️ Your domain will expire in {$daysLeft} days on {$expiration->format('Y-m-d')}.";
|
||||
}
|
||||
|
||||
return [
|
||||
'warning' => $warning,
|
||||
'days_left' => $daysLeft,
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
}
|
||||
52
public/restaurant/app/Helper/IngredientDamageHelper.php
Normal file
52
public/restaurant/app/Helper/IngredientDamageHelper.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helper;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Models\Ingredient;
|
||||
use Modules\Restaurant\Models\IngredientDamage;
|
||||
use Modules\Restaurant\Models\Stock;
|
||||
|
||||
class IngredientDamageHelper
|
||||
{
|
||||
public static function createDamage(array $data)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
// 1️⃣ Create ingredient damage record
|
||||
$damage = IngredientDamage::create($data);
|
||||
|
||||
// 2️⃣ Reduce ingredient stock
|
||||
$ingredient = Ingredient::find($data['ingredient_id']);
|
||||
if (! $ingredient) {
|
||||
throw new \Exception('Ingredient not found.');
|
||||
}
|
||||
|
||||
$ingredient->decrement('stock_quantity', $data['quantity']);
|
||||
|
||||
// 3️⃣ Log stock movement
|
||||
Stock::create([
|
||||
'restaurant_id' => $data['restaurant_id'],
|
||||
'ingredient_id' => $data['ingredient_id'],
|
||||
'type' => 'damage',
|
||||
'quantity' => $data['quantity'],
|
||||
'unit_cost' => $data['unit_cost'] ?? null,
|
||||
'total_cost' => isset($data['unit_cost']) ? $data['unit_cost'] * $data['quantity'] : null,
|
||||
'reference_type' => 'IngredientDamage',
|
||||
'purchase_id' => $data['purchase_id'],
|
||||
'batch_no' => $data['batch_no'] ?? null,
|
||||
'expiry_date' => null,
|
||||
'added_by' => $data['reported_by'] ?? auth()->id(),
|
||||
'remarks' => $data['reason'] ?? 'Ingredient damage',
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $damage;
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
public/restaurant/app/Helper/OrderHelper.php
Normal file
47
public/restaurant/app/Helper/OrderHelper.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helper;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Models\Order;
|
||||
|
||||
class OrderHelper
|
||||
{
|
||||
/**
|
||||
* Generate a unique order number for the restaurant.
|
||||
*
|
||||
* Format: ORD-{YYYYMMDD}-{RestaurantId}-{AutoIncrement}
|
||||
*/
|
||||
public static function generateOrderNumber(int $restaurantId): string
|
||||
{
|
||||
$today = Carbon::now()->format('Ymd');
|
||||
|
||||
// Fetch last order (including soft-deleted)
|
||||
$lastOrder = DB::table('orders')
|
||||
->where('restaurant_id', $restaurantId)
|
||||
->whereDate('created_at', Carbon::today())
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
$lastIncrement = 0;
|
||||
|
||||
if ($lastOrder && preg_match('/ORD-\d{8}-\d+-([\d]+)/', $lastOrder->order_number, $matches)) {
|
||||
$lastIncrement = (int) $matches[1];
|
||||
}
|
||||
|
||||
$newIncrement = $lastIncrement + 1;
|
||||
|
||||
// Ensure unique, retry if needed (handles rare race conditions)
|
||||
do {
|
||||
$orderNumber = sprintf('ORD-%s-%d-%04d', $today, $restaurantId, $newIncrement);
|
||||
$exists = Order::withTrashed()->where('order_number', $orderNumber)->exists();
|
||||
|
||||
if ($exists) {
|
||||
$newIncrement++;
|
||||
}
|
||||
} while ($exists);
|
||||
|
||||
return $orderNumber;
|
||||
}
|
||||
}
|
||||
36
public/restaurant/app/Helper/PurchaseHelper.php
Normal file
36
public/restaurant/app/Helper/PurchaseHelper.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helper;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Modules\Restaurant\Models\Purchase;
|
||||
|
||||
class PurchaseHelper
|
||||
{
|
||||
/**
|
||||
* Generate a unique purchase invoice number for a restaurant.
|
||||
*
|
||||
* Format: PUR-{YYYYMMDD}-{RestaurantId}-{AutoIncrement}
|
||||
*/
|
||||
public static function generateInvoiceNumber(int $restaurantId): string
|
||||
{
|
||||
$today = Carbon::now()->format('Ymd');
|
||||
|
||||
// Get last purchase today for this restaurant
|
||||
$lastPurchase = Purchase::where('restaurant_id', $restaurantId)
|
||||
->whereDate('created_at', Carbon::today())
|
||||
->lockForUpdate() // prevent concurrency issues
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
|
||||
$lastIncrement = 0;
|
||||
|
||||
if ($lastPurchase && preg_match('/PUR-\d{8}-\d+-([\d]+)/', $lastPurchase->invoice_no, $matches)) {
|
||||
$lastIncrement = (int) $matches[1];
|
||||
}
|
||||
|
||||
$newIncrement = $lastIncrement + 1;
|
||||
|
||||
return "PUR-{$today}-{$restaurantId}-".str_pad($newIncrement, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
86
public/restaurant/app/Helper/PurchaseReturnHelper.php
Normal file
86
public/restaurant/app/Helper/PurchaseReturnHelper.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helper;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Modules\Restaurant\Models\PurchaseItem;
|
||||
use Modules\Restaurant\Models\PurchaseReturn;
|
||||
use Modules\Restaurant\Models\Stock;
|
||||
|
||||
class PurchaseReturnHelper
|
||||
{
|
||||
/**
|
||||
* Process a return for purchase items.
|
||||
*
|
||||
* @param array $returnItems [ ['purchase_item_id'=>1,'quantity'=>2,'reason'=>'Damaged'], ... ]
|
||||
*/
|
||||
public static function processReturn(int $purchaseId, array $returnItems, ?int $userId = null): array
|
||||
{
|
||||
$results = [];
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
foreach ($returnItems as $item) {
|
||||
$purchaseItem = PurchaseItem::lockForUpdate()->findOrFail($item['purchase_item_id']);
|
||||
|
||||
$returnQty = (float) $item['quantity'];
|
||||
$availableQty = $purchaseItem->received_quantity - $purchaseItem->returned_quantity;
|
||||
|
||||
if ($returnQty <= 0 || $returnQty > $availableQty) {
|
||||
throw new \Exception("Invalid return quantity for ingredient ID {$purchaseItem->ingredient_id}");
|
||||
}
|
||||
|
||||
// 1️⃣ Log return in purchase_returns table
|
||||
$unitCost = $purchaseItem->unit_price;
|
||||
$totalCost = $unitCost * $returnQty;
|
||||
|
||||
$purchaseReturn = PurchaseReturn::create([
|
||||
'restaurant_id' => $purchaseItem->restaurant_id,
|
||||
'purchase_id' => $purchaseItem->purchase_id,
|
||||
'purchase_item_id' => $purchaseItem->id,
|
||||
'ingredient_id' => $purchaseItem->ingredient_id,
|
||||
'quantity' => $returnQty,
|
||||
'unit_cost' => $unitCost,
|
||||
'total_cost' => $totalCost,
|
||||
'reason' => $item['reason'] ?? 'Purchase item return',
|
||||
'processed_by' => $userId,
|
||||
]);
|
||||
|
||||
// 2️⃣ Update purchase_items returned_quantity
|
||||
$purchaseItem->increment('returned_quantity', $returnQty);
|
||||
|
||||
// 3️⃣ Update stock: decrease since returned to supplier
|
||||
Stock::create([
|
||||
'restaurant_id' => $purchaseItem->restaurant_id,
|
||||
'ingredient_id' => $purchaseItem->ingredient_id,
|
||||
'type' => 'return',
|
||||
'quantity' => $returnQty,
|
||||
'unit_cost' => $unitCost,
|
||||
'total_cost' => $totalCost,
|
||||
'reference_type' => 'PurchaseReturn',
|
||||
'purchase_id' => $purchaseItem->purchase_id,
|
||||
'added_by' => $userId,
|
||||
'remarks' => $item['reason'] ?? 'Purchase item return',
|
||||
]);
|
||||
|
||||
// 4️⃣ Update ingredient stock quantity
|
||||
$purchaseItem->ingredient->decrement('stock_quantity', $returnQty);
|
||||
|
||||
$results[] = [
|
||||
'purchase_return_id' => $purchaseReturn->id,
|
||||
'purchase_item_id' => $purchaseItem->id,
|
||||
'ingredient_id' => $purchaseItem->ingredient_id,
|
||||
'returned_quantity' => $returnQty,
|
||||
];
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
return ['status' => true, 'message' => 'Return processed successfully.', 'data' => $results];
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
return ['status' => false, 'message' => $e->getMessage(), 'data' => []];
|
||||
}
|
||||
}
|
||||
}
|
||||
52
public/restaurant/app/Helper/RestaurantHelper.php
Normal file
52
public/restaurant/app/Helper/RestaurantHelper.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helper;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RestaurantHelper
|
||||
{
|
||||
/**
|
||||
* Get restaurant ID based on the domain.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getRestaurantIdByDomain(string $domain)
|
||||
{
|
||||
// Check if domain is provided
|
||||
if (empty($domain)) {
|
||||
return [
|
||||
'error' => 'Domain is required.',
|
||||
'status' => false,
|
||||
];
|
||||
}
|
||||
|
||||
// Check if domain format is valid (e.g., example.com)
|
||||
if (! filter_var($domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
|
||||
return [
|
||||
'error' => 'Invalid domain format.',
|
||||
'status' => false,
|
||||
];
|
||||
}
|
||||
|
||||
// Fetch the restaurant based on the domain
|
||||
$restaurant = DB::table('restaurants')
|
||||
->where('domain', $domain)
|
||||
->select('id')
|
||||
->first();
|
||||
|
||||
// If no matching restaurant is found
|
||||
if (! $restaurant) {
|
||||
return [
|
||||
'error' => 'Restaurant not found for the provided domain.',
|
||||
'status' => false,
|
||||
];
|
||||
}
|
||||
|
||||
// Return the restaurant ID
|
||||
return [
|
||||
'restaurant_id' => $restaurant->id,
|
||||
'status' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
63
public/restaurant/app/Helper/StockHelper.php
Normal file
63
public/restaurant/app/Helper/StockHelper.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helper;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\Restaurant\Models\Ingredient;
|
||||
use Modules\Restaurant\Models\Stock;
|
||||
|
||||
class StockHelper
|
||||
{
|
||||
/**
|
||||
* Update ingredient stock and log the movement.
|
||||
*
|
||||
* @param string $type (purchase, usage, return, damage, transfer_in, transfer_out)
|
||||
* @param float|null $unitCost (cost per unit)
|
||||
* @param string|null $refType (e.g., Purchase, Order, Wastage)
|
||||
*/
|
||||
public static function updateStock(
|
||||
int $ingredientId,
|
||||
float $quantity,
|
||||
string $type,
|
||||
?float $unitCost = null,
|
||||
?string $refType = null,
|
||||
?int $refId = null,
|
||||
?string $batchNo = null,
|
||||
?string $expiryDate = null,
|
||||
?int $restaurantId = null,
|
||||
?int $addedBy = null
|
||||
): void {
|
||||
$ingredient = Ingredient::find($ingredientId);
|
||||
if (! $ingredient) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine signed quantity based on type
|
||||
$signedQty = in_array($type, ['purchase', 'return', 'transfer_in', 'adjust_plus'])
|
||||
? $quantity
|
||||
: -$quantity;
|
||||
|
||||
// Update ingredient stock
|
||||
$ingredient->increment('stock_quantity', $signedQty);
|
||||
|
||||
// Calculate total cost if unit cost is provided
|
||||
$totalCost = $unitCost !== null ? $unitCost * $quantity : null;
|
||||
|
||||
// Record in stocks table
|
||||
Stock::create([
|
||||
'restaurant_id' => $restaurantId ?? $ingredient->restaurant_id ?? null,
|
||||
'ingredient_id' => $ingredientId,
|
||||
'type' => $type,
|
||||
'quantity' => $quantity,
|
||||
'unit_cost' => $unitCost,
|
||||
'total_cost' => $totalCost,
|
||||
'reference_type' => $refType,
|
||||
'purchase_id' => $refId,
|
||||
'batch_no' => $batchNo,
|
||||
'expiry_date' => $expiryDate,
|
||||
'added_by' => $addedBy ?? Auth::id(),
|
||||
'remarks' => ucfirst($type).' stock movement',
|
||||
]);
|
||||
}
|
||||
}
|
||||
345
public/restaurant/app/Helper/helper.php
Normal file
345
public/restaurant/app/Helper/helper.php
Normal file
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Mail\MailManager;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Authentication\Models\Setting;
|
||||
use Modules\Authentication\Models\SubscriptionItem;
|
||||
use Modules\Authentication\Models\User;
|
||||
use Modules\HRM\Models\Attendance;
|
||||
use Modules\HRM\Models\AttendanceLog;
|
||||
use Modules\Restaurant\Models\FoodVariant;
|
||||
|
||||
if (! function_exists('_lang')) {
|
||||
function _lang($string = '')
|
||||
{
|
||||
$targetLang = ! empty(Session::get('locale'))
|
||||
? Session::get('locale')
|
||||
: (! empty(get_option('language')) ? get_option('language') : 'en');
|
||||
|
||||
$json = [];
|
||||
if (file_exists(lang_path()."/$targetLang.json")) {
|
||||
$json = json_decode(file_get_contents(lang_path()."/$targetLang.json"), true);
|
||||
}
|
||||
|
||||
return ! empty($json[$string]) ? $json[$string] : $string;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('get_option')) {
|
||||
function get_option($name)
|
||||
{
|
||||
try {
|
||||
// Check if the database connection is valid
|
||||
DB::connection()->getPdo();
|
||||
|
||||
$setting = DB::table('settings')->where('option_key', $name)->get();
|
||||
if (! $setting->isEmpty()) {
|
||||
return $setting[0]->option_value;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// If database connection fails, return an empty string or log the error
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('get_saas_option')) {
|
||||
function get_saas_option(string $name, $default = '')
|
||||
{
|
||||
try {
|
||||
// Check DB connection
|
||||
DB::connection()->getPdo();
|
||||
|
||||
// Ensure table exists
|
||||
if (! Schema::hasTable('s_a_a_s_settings')) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
// Fetch setting
|
||||
$setting = DB::table('s_a_a_s_settings')
|
||||
->where('name', $name)
|
||||
->first();
|
||||
|
||||
return $setting ? $setting->value : $default;
|
||||
} catch (\Throwable $e) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('setEnvValue')) {
|
||||
function setEnvValue(string $key, string $value): void
|
||||
{
|
||||
$envPath = base_path('.env');
|
||||
|
||||
if (! file_exists($envPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$escapedValue = '"'.trim(str_replace('"', '\"', $value)).'"';
|
||||
$envContent = file_get_contents($envPath);
|
||||
|
||||
if (preg_match("/^{$key}=.*/m", $envContent)) {
|
||||
$envContent = preg_replace(
|
||||
"/^{$key}=.*/m",
|
||||
"{$key}={$escapedValue}",
|
||||
$envContent
|
||||
);
|
||||
} else {
|
||||
$envContent .= PHP_EOL."{$key}={$escapedValue}";
|
||||
}
|
||||
|
||||
file_put_contents($envPath, $envContent);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('fileUploader')) {
|
||||
function fileUploader(string $dir, string $format, $image = null, $oldImage = null)
|
||||
{
|
||||
if ($image == null) {
|
||||
return $oldImage ?? 'def.png';
|
||||
}
|
||||
|
||||
// Delete old image(s) if exist
|
||||
if (! empty($oldImage)) {
|
||||
if (is_array($oldImage)) {
|
||||
foreach ($oldImage as $file) {
|
||||
Storage::disk('public')->delete($dir.$file);
|
||||
}
|
||||
} elseif (is_string($oldImage)) {
|
||||
Storage::disk('public')->delete($dir.$oldImage);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate unique file name
|
||||
$imageName = Carbon::now()->toDateString().'-'.uniqid().'.'.$format;
|
||||
|
||||
// Ensure directory exists
|
||||
if (! Storage::disk('public')->exists($dir)) {
|
||||
Storage::disk('public')->makeDirectory($dir);
|
||||
}
|
||||
|
||||
// Store the uploaded file
|
||||
if ($image instanceof \Illuminate\Http\UploadedFile) {
|
||||
$image->storeAs($dir, $imageName, 'public');
|
||||
} else {
|
||||
throw new \Exception('Invalid file type provided for upload.');
|
||||
}
|
||||
|
||||
return $imageName;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('fileRemover')) {
|
||||
function fileRemover(string $dir, $image)
|
||||
{
|
||||
if (! isset($image)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Storage::disk('public')->exists($dir.$image)) {
|
||||
Storage::disk('public')->delete($dir.$image);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('restaurantUniqueRule')) {
|
||||
function restaurantUniqueRule(string $table, string $column = 'name', ?int $restaurantId = null, ?int $ignoreId = null, string $restaurantColumn = 'restaurant_id')
|
||||
{
|
||||
$restaurantId = $restaurantId ?? auth()->user()->restaurant_id;
|
||||
|
||||
$rule = \Illuminate\Validation\Rule::unique($table, $column)
|
||||
->where($restaurantColumn, $restaurantId);
|
||||
|
||||
if ($ignoreId) {
|
||||
$rule->ignore($ignoreId);
|
||||
}
|
||||
|
||||
return $rule;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('restaurantUniqueRule')) {
|
||||
function getDefaultVariant($foodId)
|
||||
{
|
||||
return FoodVariant::where('food_item_id', $foodId)->where('is_default', true)->first();
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('getUserRestaurantId')) {
|
||||
function getUserRestaurantId()
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $user->restaurant_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('getUserId')) {
|
||||
function getUserId()
|
||||
{
|
||||
$user = auth()->user();
|
||||
if (! $user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $user->id;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('authUser')) {
|
||||
function authUser(): User
|
||||
{
|
||||
$authUser = auth()->user();
|
||||
|
||||
return $authUser;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('updateAttendanceSummary')) {
|
||||
function updateAttendanceSummary($employeeId, $date)
|
||||
{
|
||||
$attendance = Attendance::firstOrCreate([
|
||||
'employee_id' => $employeeId,
|
||||
'date' => $date,
|
||||
]);
|
||||
|
||||
$logs = AttendanceLog::where('employee_id', $employeeId)
|
||||
->whereDate('punch_time', $date)
|
||||
->orderBy('punch_time')
|
||||
->get();
|
||||
|
||||
if ($logs->isEmpty()) {
|
||||
$attendance->update([
|
||||
'first_clock_in' => null,
|
||||
'last_clock_out' => null,
|
||||
'hours_worked' => 0,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$firstIn = $logs->where('type', 'in')->first()?->punch_time;
|
||||
$lastOut = $logs->where('type', 'out')->last()?->punch_time;
|
||||
|
||||
$totalSeconds = 0;
|
||||
$stack = [];
|
||||
foreach ($logs as $log) {
|
||||
if ($log->type === 'in') {
|
||||
$stack[] = $log->punch_time;
|
||||
} elseif ($log->type === 'out' && ! empty($stack)) {
|
||||
$in = array_pop($stack);
|
||||
$out = $log->punch_time;
|
||||
$totalSeconds += Carbon::parse($out)->diffInSeconds(Carbon::parse($in));
|
||||
}
|
||||
}
|
||||
|
||||
$hoursWorked = round($totalSeconds / 3600, 2);
|
||||
|
||||
$attendance->update([
|
||||
'first_clock_in' => $firstIn ? Carbon::parse($firstIn)->format('H:i:s') : null,
|
||||
'last_clock_out' => $lastOut ? Carbon::parse($lastOut)->format('H:i:s') : null,
|
||||
'hours_worked' => $hoursWorked,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('checkPackageAccess')) {
|
||||
function checkPackageAccess()
|
||||
{
|
||||
$subscription = SubscriptionItem::where(['user_id' => Auth::user()->id, 'restaurant_id' => GetUserRestaurantId()])
|
||||
->with('package')->latest('id')
|
||||
->first();
|
||||
|
||||
return $subscription->package?->name;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('get_setting')) {
|
||||
function get_setting($key, $restaurantId = null, $type = 'vendor', $default = null)
|
||||
{
|
||||
$query = Setting::query();
|
||||
if ($restaurantId) {
|
||||
$query->where('restaurant_id', $restaurantId);
|
||||
}
|
||||
|
||||
if ($type) {
|
||||
$query->where('type', $type);
|
||||
}
|
||||
|
||||
$setting = $query->where('key', $key)->first();
|
||||
|
||||
return $setting ? $setting->value : $default;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('shop_mailer_send')) {
|
||||
|
||||
/**
|
||||
* Send email via shop SMTP dynamically
|
||||
*
|
||||
* @param \Illuminate\Mail\Mailable $mailable
|
||||
*/
|
||||
function shop_mailer_send(int $restaurantId, string $toEmail, $mailable): array
|
||||
{
|
||||
// 1. Check if default contact method is email
|
||||
// $defaultMethod = get_setting('default_contact_method', $restaurantId);
|
||||
// if ($defaultMethod !== 'email') {
|
||||
// return [
|
||||
// 'status' => false,
|
||||
// 'message' => 'Default contact method is not email.',
|
||||
// ];
|
||||
// }
|
||||
|
||||
// 2. Get SMTP settings from database
|
||||
$smtp = [
|
||||
'transport' => 'smtp',
|
||||
'host' => get_setting('email_smtp_host', $restaurantId),
|
||||
'port' => get_setting('email_smtp_port', $restaurantId),
|
||||
'encryption' => get_setting('email_smtp_encryption', $restaurantId) ?? 'tls',
|
||||
'username' => get_setting('email_smtp_username', $restaurantId),
|
||||
'password' => get_setting('email_smtp_password', $restaurantId),
|
||||
'timeout' => null,
|
||||
'local_domain' => parse_url(env('APP_URL', 'http://localhost'), PHP_URL_HOST),
|
||||
];
|
||||
|
||||
// 3. Ensure all required credentials exist
|
||||
if (empty($smtp['host']) || empty($smtp['port']) || empty($smtp['username']) || empty($smtp['password'])) {
|
||||
return [
|
||||
'status' => false,
|
||||
'message' => 'SMTP configuration incomplete for this shop.',
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// 4. Dynamically set mailer config
|
||||
config()->set('mail.mailers.dynamic', $smtp);
|
||||
|
||||
// 5. Get dynamic mailer instance
|
||||
$mailer = app(MailManager::class)->mailer('dynamic');
|
||||
|
||||
// 6. Send email
|
||||
$mailer->to($toEmail)->send($mailable);
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
'message' => 'Email sent successfully.',
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'status' => false,
|
||||
'message' => 'Failed to send email: '.$e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user