where($key, '!=', $value); }); TextInput::macro('money', function (string | Closure | null $currency = null, bool $useAffix = true): static { $currency ??= CurrencyAccessor::getDefaultCurrency(); if ($useAffix) { $this ->prefix(static function (TextInput $component) use ($currency) { $currency = $component->evaluate($currency); return currency($currency)->getPrefix(); }) ->suffix(static function (TextInput $component) use ($currency) { $currency = $component->evaluate($currency); return currency($currency)->getSuffix(); }); } $this->mask(RawJs::make('$money($input)')) ->afterStateHydrated(function (TextInput $component, ?int $state) { if (blank($state)) { return; } $formatted = CurrencyConverter::convertCentsToFormatSimple($state, 'USD'); $component->state($formatted); }) ->dehydrateStateUsing(function (?string $state): ?int { if (blank($state)) { return null; } // Remove thousand separators $cleaned = str_replace(',', '', $state); // If no decimal point, assume it's whole dollars (add .00) if (! str_contains($cleaned, '.')) { $cleaned .= '.00'; } // Convert to float then to cents (integer) return (int) round((float) $cleaned * 100); }); return $this; }); TextInput::macro('rate', function (string | Closure | null $computation = null, string | Closure | null $currency = null, bool $showAffix = true): static { return $this ->when( $showAffix, fn (TextInput $component) => $component ->prefix(function (TextInput $component) use ($computation, $currency) { $evaluatedComputation = $component->evaluate($computation); $evaluatedCurrency = $component->evaluate($currency); return ratePrefix($evaluatedComputation, $evaluatedCurrency); }) ->suffix(function (TextInput $component) use ($computation, $currency) { $evaluatedComputation = $component->evaluate($computation); $evaluatedCurrency = $component->evaluate($currency); return rateSuffix($evaluatedComputation, $evaluatedCurrency); }) ) ->mask(static function (TextInput $component) use ($computation, $currency) { $computation = $component->evaluate($computation); $currency = $component->evaluate($currency); $computationEnum = AdjustmentComputation::parse($computation); if ($computationEnum->isPercentage()) { return rateMask(computation: $computation); } return moneyMask($currency); }) ->rule(static function (TextInput $component) use ($computation) { return static function (string $attribute, $value, Closure $fail) use ($computation, $component) { $computation = $component->evaluate($computation); $numericValue = (float) $value; if ($computation instanceof BackedEnum) { $computation = $computation->value; } if ($computation === 'percentage' || $computation === 'compound') { if ($numericValue < 0 || $numericValue > 100) { $fail(translate('The rate must be between 0 and 100.')); } } elseif ($computation === 'fixed' && $numericValue < 0) { $fail(translate('The rate must be greater than 0.')); } }; }); }); TextColumn::macro('coloredDescription', function (string | Htmlable | Closure | null $description, string $color = 'danger') { $this->description(static function (TextColumn $column) use ($description, $color): Htmlable { $description = $column->evaluate($description); return new HtmlString("{$description}"); }); return $this; }); TextColumn::macro('hideOnTabs', function (array $tabs): static { $this->toggleable(isToggledHiddenByDefault: function (HasTable $livewire) use ($tabs) { return in_array($livewire->activeTab, $tabs); }); return $this; }); TextColumn::macro('showOnTabs', function (array $tabs): static { $this->toggleable(isToggledHiddenByDefault: function (HasTable $livewire) use ($tabs) { return ! in_array($livewire->activeTab, $tabs); }); return $this; }); TextColumn::macro('defaultDateFormat', function (): static { $dateFormat = CompanySettingsService::getDefaultDateFormat(); $this->date($dateFormat); return $this; }); DatePicker::macro('defaultDateFormat', function (): static { $dateFormat = CompanySettingsService::getDefaultDateFormat(); $this->displayFormat($dateFormat); return $this; }); TextColumn::macro('currency', function (string | Closure | null $currency = null, ?bool $convert = null): static { $currency ??= CurrencyAccessor::getDefaultCurrency(); $convert ??= false; $this->formatStateUsing(static function (TextColumn $column, $state) use ($currency, $convert): ?string { if (blank($state)) { return null; } $currency = $column->evaluate($currency); $convert = $column->evaluate($convert); return money($state, $currency, $convert)->format(); }); return $this; }); TextEntry::macro('currency', function (string | Closure | null $currency = null, ?bool $convert = null): static { $currency ??= CurrencyAccessor::getDefaultCurrency(); $convert ??= false; $this->formatStateUsing(static function (TextEntry $entry, $state) use ($currency, $convert): ?string { if (blank($state)) { return null; } $currency = $entry->evaluate($currency); $convert = $entry->evaluate($convert); return money($state, $currency, $convert)->format(); }); return $this; }); TextColumn::macro('currencyWithConversion', function (string | Closure | null $currency = null, ?bool $convertFromCents = null): static { $currency ??= CurrencyAccessor::getDefaultCurrency(); $convertFromCents ??= true; $this->formatStateUsing(static function (TextColumn $column, $state) use ($currency, $convertFromCents): ?string { if (blank($state)) { return null; } $currency = $column->evaluate($currency); $showCurrency = $currency !== CurrencyAccessor::getDefaultCurrency(); if ($convertFromCents) { $balanceInCents = $state; } else { $balanceInCents = CurrencyConverter::convertToCents($state, $currency); } if ($balanceInCents < 0) { return '(' . CurrencyConverter::formatCentsToMoney(abs($balanceInCents), $currency, $showCurrency) . ')'; } return CurrencyConverter::formatCentsToMoney($balanceInCents, $currency, $showCurrency); }); $this->description(static function (TextColumn $column, $state) use ($currency, $convertFromCents): ?string { if (blank($state)) { return null; } $oldCurrency = $column->evaluate($currency); $newCurrency = CurrencyAccessor::getDefaultCurrency(); if ($oldCurrency === $newCurrency) { return null; } if ($convertFromCents) { $balanceInCents = $state; } else { $balanceInCents = CurrencyConverter::convertToCents($state, $oldCurrency); } $convertedBalanceInCents = CurrencyConverter::convertBalance($balanceInCents, $oldCurrency, $newCurrency); if ($convertedBalanceInCents < 0) { return '(' . CurrencyConverter::formatCentsToMoney(abs($convertedBalanceInCents), $newCurrency, true) . ')'; } return CurrencyConverter::formatCentsToMoney($convertedBalanceInCents, $newCurrency, true); }); return $this; }); TextEntry::macro('currencyWithConversion', function (string | Closure | null $currency = null): static { $currency ??= CurrencyAccessor::getDefaultCurrency(); $this->formatStateUsing(static function (TextEntry $entry, $state) use ($currency): ?string { if (blank($state)) { return null; } $currency = $entry->evaluate($currency); return CurrencyConverter::formatToMoney($state, $currency); }); $this->helperText(static function (TextEntry $entry, $state) use ($currency): ?string { if (blank($state)) { return null; } $oldCurrency = $entry->evaluate($currency); $newCurrency = CurrencyAccessor::getDefaultCurrency(); if ($oldCurrency === $newCurrency) { return null; } $balanceInCents = CurrencyConverter::convertToCents($state, $oldCurrency); $convertedBalanceInCents = CurrencyConverter::convertBalance($balanceInCents, $oldCurrency, $newCurrency); return CurrencyConverter::formatCentsToMoney($convertedBalanceInCents, $newCurrency, true); }); return $this; }); Field::macro('validateAccountCode', function (string | Closure | null $subtype = null): static { $this ->rules([ fn (Field $component): Closure => static function (string $attribute, $value, Closure $fail) use ($subtype, $component) { $subtype = $component->evaluate($subtype); $chartSubtype = AccountSubtype::find($subtype); $type = $chartSubtype->type; if (! AccountCode::isValidCode($value, $type)) { $message = AccountCode::getMessage($type); $fail($message); } }, ]); return $this; }); TextColumn::macro('rate', function (string | Closure | null $computation = null): static { $this->formatStateUsing(static function (TextColumn $column, $state) use ($computation): ?string { $computation = $column->evaluate($computation); return rateFormat(state: $state, computation: $computation); }); return $this; }); Field::macro('softRequired', function (): static { $this ->required() ->markAsRequired(false); return $this; }); // In your macro - simpler logic TextColumn::macro('asRelativeDay', function (?string $timezone = null): static { $this->formatStateUsing(function (TextColumn $column, mixed $state) use ($timezone) { if (blank($state)) { return null; } $timezone ??= $column->getTimezone() ?? CompanySettingsService::getDefaultTimezone(); // Use shiftTimezone to shift UTC calendar date to the specified timezone // Using setTimezone would convert which is wrong for calendar dates $date = Carbon::parse($state)->shiftTimezone($timezone); if ($date->isToday()) { return 'Today'; } elseif ($date->isTomorrow()) { return 'Tomorrow'; } elseif ($date->isYesterday()) { return 'Yesterday'; } return $date->diffForHumans([ 'options' => CarbonInterface::ONE_DAY_WORDS, 'skip' => ['month', 'week'], // Skip larger units, force days and years only 'parts' => 2, 'join' => ', ', ]); }); return $this; }); TextEntry::macro('asRelativeDay', function (?string $timezone = null): static { $this->formatStateUsing(function (TextEntry $entry, mixed $state) use ($timezone) { if (blank($state)) { return null; } $timezone ??= $entry->getTimezone() ?? CompanySettingsService::getDefaultTimezone(); // Use shiftTimezone to shift UTC calendar date to the specified timezone // Using setTimezone would convert which is wrong for calendar dates $date = Carbon::parse($state)->shiftTimezone($timezone); if ($date->isToday()) { return 'Today'; } elseif ($date->isTomorrow()) { return 'Tomorrow'; } elseif ($date->isYesterday()) { return 'Yesterday'; } return $date->diffForHumans([ 'options' => CarbonInterface::ONE_DAY_WORDS, 'skip' => ['month', 'week'], // Skip larger units, force days and years only 'parts' => 2, 'join' => ', ', ]); }); return $this; }); TextEntry::macro('link', function (bool $condition = true): static { if ($condition) { $this ->limit(50) ->openUrlInNewTab() ->icon('heroicon-o-arrow-top-right-on-square') ->iconColor('primary') ->iconPosition(IconPosition::After); } return $this; }); Money::macro('swapAmountFor', function ($newCurrency) { $oldCurrency = $this->currency->getCurrency(); $balanceInSubunits = $this->getAmount(); $oldCurrencySubunit = currency($oldCurrency)->getSubunit(); $newCurrencySubunit = currency($newCurrency)->getSubunit(); $balanceInMajorUnits = $balanceInSubunits / $oldCurrencySubunit; $oldRate = currency($oldCurrency)->getRate(); $newRate = currency($newCurrency)->getRate(); $ratio = $newRate / $oldRate; $convertedBalanceInMajorUnits = $balanceInMajorUnits * $ratio; $roundedConvertedBalanceInMajorUnits = round($convertedBalanceInMajorUnits, currency($newCurrency)->getPrecision()); $convertedBalanceInSubunits = $roundedConvertedBalanceInMajorUnits * $newCurrencySubunit; return (int) round($convertedBalanceInSubunits); }); Money::macro('formatWithCode', function (bool $codeBefore = false) { $formatted = $this->format(); $currencyCode = $this->currency->getCurrency(); if ($codeBefore) { return $currencyCode . ' ' . $formatted; } return $formatted . ' ' . $currencyCode; }); Currency::macro('getEntity', function () { $currencyCode = $this->getCurrency(); $entity = config("money.currencies.{$currencyCode}.entity"); return $entity ?? $currencyCode; }); Currency::macro('getCodePrefix', function () { if ($this->isSymbolFirst()) { return ''; } return ' ' . $this->getCurrency(); }); Currency::macro('getCodeSuffix', function () { if ($this->isSymbolFirst()) { return ' ' . $this->getCurrency(); } return ''; }); Carbon::macro('toDefaultDateFormat', function () { $dateFormat = CompanySettingsService::getDefaultDateFormat(); return $this->format($dateFormat); }); Carbon::macro('toCompanyTimezone', function () { $timezone = CompanySettingsService::getDefaultTimezone(); // This will convert the date to the company's timezone, possibly changing the date and time return $this->setTimezone($timezone); }); Carbon::macro('asCompanyTimezone', function () { $timezone = CompanySettingsService::getDefaultTimezone(); // This will only change the timezone without converting the date and time return $this->shiftTimezone($timezone); }); ExportColumn::macro('money', function () { $this->formatStateUsing(static function ($state) { if (blank($state) || ! is_int($state)) { return 0.00; } return CurrencyConverter::convertCentsToFloat($state); }); return $this; }); ExportColumn::macro('date', function () { $this->formatStateUsing(static function ($state) { if (blank($state)) { return null; } try { return Carbon::parse($state)->toDateString(); } catch (\Exception) { return null; } }); return $this; }); ExportColumn::macro('dateTime', function () { $this->formatStateUsing(static function ($state) { if (blank($state)) { return null; } try { return Carbon::parse($state)->toDateTimeString(); } catch (\Exception) { return null; } }); return $this; }); ExportColumn::macro('enum', function () { $this->formatStateUsing(static function ($state) { if (blank($state)) { return null; } if (! ($state instanceof BackedEnum)) { return $state; } if ($state instanceof HasLabel) { return $state->getLabel(); } return $state->value; }); return $this; }); } }