get('user.language', config('app.locale')); return in_array($current_lang, config('constants.langs_rtl', [])); } } if (!function_exists('is_persian_lang')) { /** * Check if current language is Persian * * @return bool */ function is_persian_lang() { $current_lang = session()->get('user.language', config('app.locale')); return $current_lang === 'fa'; } } if (!function_exists('get_name_prefixes')) { /** * Get selectable name prefixes for the current locale. * * @param string|null $selected Preserve a legacy/custom value not in the list. * @return array */ function get_name_prefixes($selected = null) { if (! is_persian_lang()) { return []; } $options = []; foreach (config('constants.name_prefixes_fa', []) as $prefix) { $options[$prefix] = $prefix; } if (! empty($selected) && is_scalar($selected) && ! array_key_exists($selected, $options)) { $options = [$selected => $selected] + $options; } return $options; } } if (!function_exists('peek_next_contact_id')) { /** * Preview the next auto-generated contact ID without incrementing the counter. * * @param int|null $business_id * @return string */ function peek_next_contact_id($business_id = null) { if (empty($business_id)) { $business_id = request()->session()->get('user.business_id'); } $ref = \App\ReferenceCount::where('ref_type', 'contacts') ->where('business_id', $business_id) ->first(); $ref_count = ! empty($ref) ? ((int) $ref->ref_count + 1) : 1; return app(\App\Utils\Util::class)->generateReferenceNumber('contacts', $ref_count, $business_id); } } if (!function_exists('peek_next_product_sku')) { /** * Preview the next auto-generated product SKU without creating a product. * * @param int|null $business_id * @return string */ function peek_next_product_sku($business_id = null) { if (empty($business_id)) { $business_id = request()->session()->get('user.business_id'); } $next_id = (int) \App\Product::where('business_id', $business_id)->max('id') + 1; return app(\App\Utils\ProductUtil::class)->generateProductSku($next_id); } } if (!function_exists('persian_date')) { /** * Convert Gregorian date to Persian (Jalali) date * Note: Requires jdf() function or morilog/jalali package * * @param string $date * @param string $format * @return string */ function persian_date($date = null, $format = 'Y/m/d') { if (!$date) { $date = now(); } if (is_string($date)) { $date = \Carbon\Carbon::parse($date); } if (is_persian_lang()) { return persian_number(format_jalali_date($date, $format)); } return $date->format($format); } } if (!function_exists('format_currency_persian')) { /** * Format currency for Persian locale * Format: 100,000 ریال (no decimals, comma separator, "ریال" at the end) * * @param float|int $amount * @param string $currency * @return string */ function format_currency_persian($amount, $currency = 'ریال') { // Format number with comma as thousand separator, no decimals $formatted = number_format((float) $amount, 0, '.', ','); // Convert to Persian numbers $formatted = persian_number($formatted); return $formatted . ' ' . $currency; } } if (!function_exists('persian_month_name')) { /** * Get Persian month name * * @param int $month * @return string */ function persian_month_name($month) { $months = [ 1 => 'فروردین', 2 => 'اردیبهشت', 3 => 'خرداد', 4 => 'تیر', 5 => 'مرداد', 6 => 'شهریور', 7 => 'مهر', 8 => 'آبان', 9 => 'آذر', 10 => 'دی', 11 => 'بهمن', 12 => 'اسفند', ]; return $months[$month] ?? ''; } } if (!function_exists('persian_day_name')) { /** * Get Persian day name * * @param int $day (0 = Sunday, 6 = Saturday) * @return string */ function persian_day_name($day) { $days = [ 0 => 'یکشنبه', 1 => 'دوشنبه', 2 => 'سه‌شنبه', 3 => 'چهارشنبه', 4 => 'پنج‌شنبه', 5 => 'جمعه', 6 => 'شنبه', ]; return $days[$day] ?? ''; } } if (!function_exists('gregorian_to_jalali_parts')) { /** * Convert a Gregorian date to Jalali parts [year, month, day]. */ function gregorian_to_jalali_parts(int $gy, int $gm, int $gd): array { $g_d_m = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; $gy2 = ($gm > 2) ? ($gy + 1) : $gy; $days = 355666 + (365 * $gy) + intdiv($gy2 + 3, 4) - intdiv($gy2 + 99, 100) + intdiv($gy2 + 399, 400) + $gd + $g_d_m[$gm - 1]; $jy = -1595 + (33 * intdiv($days, 12053)); $days %= 12053; $jy += 4 * intdiv($days, 1461); $days %= 1461; if ($days > 365) { $jy += intdiv($days - 1, 365); $days = ($days - 1) % 365; } if ($days < 186) { $jm = 1 + intdiv($days, 31); $jd = 1 + ($days % 31); } else { $jm = 7 + intdiv($days - 186, 30); $jd = 1 + (($days - 186) % 30); } return [$jy, $jm, $jd]; } } if (!function_exists('jalali_to_gregorian_parts')) { /** * Convert a Jalali date to Gregorian parts [year, month, day]. */ function jalali_to_gregorian_parts(int $jy, int $jm, int $jd): array { $jy -= 979; $days = (365 * $jy) + (intdiv($jy, 33) * 8) + intdiv((($jy % 33) + 3), 4) + 78 + $jd + (($jm < 7) ? (($jm - 1) * 31) : ((($jm - 7) * 30) + 186)); $gy = 1600 + (400 * intdiv($days, 146097)); $days %= 146097; if ($days > 36524) { $gy += 100 * intdiv(--$days, 36524); $days %= 36524; if ($days >= 365) { $days++; } } $gy += 4 * intdiv($days, 1461); $days %= 1461; if ($days > 365) { $gy += intdiv($days - 1, 365); $days = ($days - 1) % 365; } $gd = $days + 1; $month_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; if (($gy % 4 === 0 && $gy % 100 !== 0) || ($gy % 400 === 0)) { $month_days[2] = 29; } $gm = 1; while ($gm <= 12 && $gd > $month_days[$gm]) { $gd -= $month_days[$gm]; $gm++; } return [$gy, $gm, $gd]; } } if (!function_exists('jalali_format_from_carbon')) { /** * Format a Carbon date using Jalali calendar tokens. */ function jalali_format_from_carbon(\Carbon\Carbon $carbon, string $format): string { [$jy, $jm, $jd] = gregorian_to_jalali_parts( (int) $carbon->format('Y'), (int) $carbon->format('n'), (int) $carbon->format('j') ); $formatted = strtr($format, [ '%Y' => (string) $jy, '%y' => substr((string) $jy, -2), '%m' => sprintf('%02d', $jm), '%n' => (string) $jm, '%d' => sprintf('%02d', $jd), '%j' => (string) $jd, '%B' => persian_month_name($jm), '%F' => persian_month_name($jm), '%M' => persian_month_name($jm), ]); return preg_replace_callback( '/[YymndFjM]|[HhisAag]/', function (array $matches) use ($jy, $jm, $jd, $carbon) { $token = $matches[0]; return match ($token) { 'Y' => (string) $jy, 'y' => substr((string) $jy, -2), 'm' => sprintf('%02d', $jm), 'n' => (string) $jm, 'd' => sprintf('%02d', $jd), 'j' => (string) $jd, 'F', 'M' => persian_month_name($jm), default => $carbon->format($token), }; }, $formatted ); } } if (!function_exists('format_jalali_date')) { /** * Format a date in Jalali using morilog/jalali when available, otherwise native conversion. */ function format_jalali_date(\Carbon\Carbon $carbon, string $format): string { if (class_exists('\Morilog\Jalali\Jalalian')) { return \Morilog\Jalali\Jalalian::fromCarbon($carbon)->format($format); } return jalali_format_from_carbon($carbon, $format); } } if (!function_exists('format_shamsi_date')) { /** * Format date/time in Jalali if Persian is enabled, otherwise fallback to Gregorian. * * @param \Carbon\Carbon|string $date * @param string|null $format * @param bool $include_time * @param int|null $time_format * @return string|null */ function format_shamsi_date($date, ?string $format = null, bool $include_time = false, ?int $time_format = null) { $format = $format ?? session('business.date_format', 'Y/m/d'); $time_format = $time_format ?? session('business.time_format', 24); if ($include_time) { $format .= ((int) $time_format === 12) ? ' h:i A' : ' H:i'; } try { // Handle 'now' string specially if (is_string($date) && strtolower(trim($date)) === 'now') { $carbon_date = \Carbon\Carbon::now(); } elseif (empty($date)) { return null; } elseif ($date instanceof \Carbon\Carbon) { $carbon_date = $date->copy(); } elseif (is_string($date)) { // Try to parse different date formats try { $carbon_date = \Carbon\Carbon::parse($date); } catch (\Exception $e) { // Try MySQL date format try { $carbon_date = \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $date); } catch (\Exception $e2) { try { $carbon_date = \Carbon\Carbon::createFromFormat('Y-m-d', $date); } catch (\Exception $e3) { \Log::error('Error parsing date in format_shamsi_date: ' . $date); return null; } } } } else { return null; } if (is_persian_lang()) { $formatted = format_jalali_date($carbon_date, $format); return function_exists('persian_number') ? persian_number($formatted) : $formatted; } $formatted = $carbon_date->format($format); return $formatted; } catch (\Exception $e) { \Log::error('Error in format_shamsi_date: ' . $e->getMessage() . ' | Date: ' . (is_string($date) ? $date : 'non-string')); return null; } } } if (!function_exists('format_date')) { /** * Format a date using business settings (Jalali when Persian is enabled). * * @param mixed $date * @param bool $show_time * @param object|null $business_details * @return string|null */ function format_date($date, $show_time = false, $business_details = null) { if (empty($date) && ! (is_string($date) && in_array(strtolower(trim($date)), ['now', 'today'], true))) { return null; } $format = ! empty($business_details) && ! empty($business_details->date_format) ? $business_details->date_format : session('business.date_format', 'Y/m/d'); $time_format = ! empty($business_details) && isset($business_details->time_format) ? $business_details->time_format : session('business.time_format', 24); if (is_string($date) && strtolower(trim($date)) === 'today') { $date = \Carbon\Carbon::today(); } return format_shamsi_date($date, $format, (bool) $show_time, (int) $time_format); } } if (!function_exists('format_datetime')) { /** * Format a date with time using business settings. * * @param mixed $date * @param object|null $business_details * @return string|null */ function format_datetime($date, $business_details = null) { return format_date($date, true, $business_details); } } if (!function_exists('format_time')) { /** * Format time using business settings. * * @param mixed $date * @return string|null */ function format_time($date) { if (empty($date) && ! (is_string($date) && strtolower(trim($date)) === 'now')) { return null; } $time_format = session('business.time_format') == 24 ? 'H:i' : 'h:i A'; try { if (is_string($date) && strtolower(trim($date)) === 'now') { $carbon_date = \Carbon\Carbon::now(); } else { $carbon_date = \Carbon\Carbon::createFromTimestamp(strtotime($date)); } $formatted = $carbon_date->format($time_format); return is_persian_lang() ? persian_number($formatted) : $formatted; } catch (\Exception $e) { return null; } } } if (!function_exists('format_jalali_month')) { /** * Get month name from a date (Jalali when Persian is enabled). */ function format_jalali_month($date): string { if (empty($date)) { return ''; } $carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date); if (is_persian_lang()) { return format_jalali_date($carbon, 'F'); } return $carbon->format('F'); } } if (!function_exists('format_jalali_year')) { /** * Get year from a date (Jalali when Persian is enabled). */ function format_jalali_year($date): string { if (empty($date)) { return ''; } $carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date); if (is_persian_lang()) { return persian_number(format_jalali_date($carbon, 'Y')); } return $carbon->format('Y'); } } if (!function_exists('format_month_year')) { /** * Format month and year label (Jalali when Persian is enabled). */ function format_month_year($date, string $format = 'F Y'): string { if (empty($date)) { return ''; } $carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date); if (is_persian_lang()) { $formatted = format_jalali_date($carbon, $format); return persian_number($formatted); } return $carbon->format($format); } } if (!function_exists('format_month_number_label')) { /** * Format a month number (1-12) as a label for tables/charts. */ function format_month_number_label(int $month, bool $abbreviated = true): string { if (is_persian_lang()) { return persian_month_name($month); } return \Carbon\Carbon::createFromFormat('m', (string) $month)->format($abbreviated ? 'M' : 'F'); } } if (!function_exists('format_relative_time')) { /** * Human-readable relative time (localized for Persian). */ function format_relative_time($date): string { if (empty($date)) { return ''; } $carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date); if (is_persian_lang()) { return persian_number($carbon->locale('fa')->diffForHumans()); } return $carbon->diffForHumans(); } } if (!function_exists('format_shamsi_chart_label')) { /** * Helper to format chart labels in Jalali when needed. * * @param \Carbon\Carbon|string $date * @param string $gregorianFormat * @param string|null $jalaliFormat * @return string */ function format_shamsi_chart_label($date, string $gregorianFormat, ?string $jalaliFormat = null): string { $carbon_date = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date); if (is_persian_lang()) { $format = $jalaliFormat ?? $gregorianFormat; $formatted = format_jalali_date($carbon_date, $format); return function_exists('persian_number') ? persian_number($formatted) : $formatted; } return $carbon_date->format($gregorianFormat); } } if (!function_exists('persian_today')) { /** * Get today's date (or provided date) formatted in Jalali. * * @param string $format * @param \Carbon\Carbon|string|null $date * @return string|null */ function persian_today(string $format = 'Y/m/d', $date = null) { $date = $date ?? now(); return format_shamsi_date($date, $format); } } if (!function_exists('jalali_to_gregorian')) { /** * Convert Jalali date string to Gregorian date string for database storage * * @param string $jalali_date Jalali date string (e.g., "1403/06/13") * @param string|null $jalali_format Format of input date (default: 'Y/m/d') * @param bool $include_time Whether to include time * @return string|null Gregorian date string (e.g., "2024-09-04") */ function jalali_to_gregorian($jalali_date, $jalali_format = null, $include_time = false) { if (empty($jalali_date)) { return null; } if (function_exists('english_number')) { $jalali_date = english_number(trim($jalali_date)); } else { $jalali_date = trim($jalali_date); } if (class_exists('\Morilog\Jalali\Jalalian') && is_persian_lang()) { $jalali_format = $jalali_format ?? session('business.date_format', 'Y/m/d'); $gregorian_format = $include_time ? 'Y-m-d H:i:s' : 'Y-m-d'; try { if ($include_time) { $time_format = session('business.time_format', 24) == 12 ? ' h:i A' : ' H:i'; $jalali_date_obj = \Morilog\Jalali\Jalalian::fromFormat($jalali_format.$time_format, $jalali_date); } else { $jalali_date_obj = \Morilog\Jalali\Jalalian::fromFormat($jalali_format, $jalali_date); } return $jalali_date_obj->toCarbon()->format($gregorian_format); } catch (\Exception $e) { \Log::error('Error converting Jalali to Gregorian: '.$e->getMessage().' | Date: '.$jalali_date); } } $normalized = str_replace('/', '-', $jalali_date); $time_suffix = ''; if ($include_time && preg_match('/(\d{1,2}):(\d{2})/', $jalali_date, $time_matches)) { $hour = (int) $time_matches[1]; $minute = (int) $time_matches[2]; if (session('business.time_format', 24) == 12 && preg_match('/\b(AM|PM)\b/i', $jalali_date, $ampm_matches)) { $ampm = strtoupper($ampm_matches[1]); if ($ampm === 'PM' && $hour < 12) { $hour += 12; } elseif ($ampm === 'AM' && $hour === 12) { $hour = 0; } } $time_suffix = sprintf(' %02d:%02d:00', $hour, $minute); } if (preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})/', $normalized, $matches)) { [$gy, $gm, $gd] = jalali_to_gregorian_parts((int) $matches[1], (int) $matches[2], (int) $matches[3]); return sprintf('%04d-%02d-%02d', $gy, $gm, $gd).$time_suffix; } return $jalali_date; } } if (!function_exists('gregorian_to_jalali')) { /** * Convert Gregorian date string to Jalali date string for display * * @param string|\Carbon\Carbon $gregorian_date Gregorian date string or Carbon instance * @param string|null $jalali_format Format of output date (default: 'Y/m/d') * @param bool $include_time Whether to include time * @return string|null Jalali date string (e.g., "1403/06/13") */ function gregorian_to_jalali($gregorian_date, $jalali_format = null, $include_time = false) { if (empty($gregorian_date)) { return null; } if (!is_persian_lang()) { if ($gregorian_date instanceof \Carbon\Carbon) { $format = $jalali_format ?? session('business.date_format', 'Y/m/d'); if ($include_time) { $time_format = session('business.time_format', 24) == 12 ? ' h:i A' : ' H:i'; $format .= $time_format; } return $gregorian_date->format($format); } return $gregorian_date; } $jalali_format = $jalali_format ?? session('business.date_format', 'Y/m/d'); try { if ($gregorian_date instanceof \Carbon\Carbon) { $carbon_date = $gregorian_date->copy(); } else { $carbon_date = \Carbon\Carbon::parse($gregorian_date); } if ($include_time) { $time_format = session('business.time_format', 24) == 12 ? ' h:i A' : ' H:i'; $formatted = format_jalali_date($carbon_date, $jalali_format.$time_format); } else { $formatted = format_jalali_date($carbon_date, $jalali_format); } return persian_number($formatted); } catch (\Exception $e) { \Log::error('Error converting Gregorian to Jalali: '.$e->getMessage().' | Date: '.$gregorian_date); return null; } } } if (!function_exists('persian_words')) { /** * Convert number to Persian words * * @param int $number * @return string */ function persian_words($number) { $ones = ['', 'یک', 'دو', 'سه', 'چهار', 'پنج', 'شش', 'هفت', 'هشت', 'نه']; $tens = ['', '', 'بیست', 'سی', 'چهل', 'پنجاه', 'شصت', 'هفتاد', 'هشتاد', 'نود']; $hundreds = ['', 'یکصد', 'دویست', 'سیصد', 'چهارصد', 'پانصد', 'ششصد', 'هفتصد', 'هشتصد', 'نهصد']; $teens = ['ده', 'یازده', 'دوازده', 'سیزده', 'چهارده', 'پانزده', 'شانزده', 'هفده', 'هجده', 'نوزده']; if ($number == 0) { return 'صفر'; } if ($number < 0) { return 'منفی ' . persian_words(abs($number)); } $result = ''; // Thousands if ($number >= 1000) { $thousands = floor($number / 1000); $result .= persian_words($thousands) . ' هزار'; $number %= 1000; if ($number > 0) { $result .= ' و '; } } // Hundreds if ($number >= 100) { $result .= $hundreds[floor($number / 100)]; $number %= 100; if ($number > 0) { $result .= ' و '; } } // Tens and ones if ($number >= 20) { $result .= $tens[floor($number / 10)]; $number %= 10; if ($number > 0) { $result .= ' و '; } } elseif ($number >= 10) { $result .= $teens[$number - 10]; $number = 0; } if ($number > 0) { $result .= $ones[$number]; } return trim($result); } }