Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

DocumentDefault.php 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <?php
  2. namespace App\Models\Setting;
  3. use App\Casts\TrimLeadingZeroCast;
  4. use App\Enums\DocumentType;
  5. use App\Enums\Font;
  6. use App\Enums\PaymentTerms;
  7. use App\Enums\Template;
  8. use App\Traits\Blamable;
  9. use App\Traits\CompanyOwned;
  10. use Database\Factories\Setting\DocumentDefaultFactory;
  11. use Illuminate\Database\Eloquent\Builder;
  12. use Illuminate\Database\Eloquent\Casts\AsArrayObject;
  13. use Illuminate\Database\Eloquent\Casts\Attribute;
  14. use Illuminate\Database\Eloquent\Factories\Factory;
  15. use Illuminate\Database\Eloquent\Factories\HasFactory;
  16. use Illuminate\Database\Eloquent\Model;
  17. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  18. use Illuminate\Support\Facades\Storage;
  19. use Wallo\FilamentCompanies\FilamentCompanies;
  20. class DocumentDefault extends Model
  21. {
  22. use Blamable;
  23. use CompanyOwned;
  24. use HasFactory;
  25. protected $table = 'document_defaults';
  26. protected $fillable = [
  27. 'company_id',
  28. 'type',
  29. 'logo',
  30. 'show_logo',
  31. 'number_prefix',
  32. 'number_digits',
  33. 'number_next',
  34. 'payment_terms',
  35. 'header',
  36. 'subheader',
  37. 'terms',
  38. 'footer',
  39. 'accent_color',
  40. 'font',
  41. 'template',
  42. 'item_name',
  43. 'unit_name',
  44. 'price_name',
  45. 'amount_name',
  46. 'created_by',
  47. 'updated_by',
  48. ];
  49. protected $casts = [
  50. 'show_logo' => 'boolean',
  51. 'number_next' => TrimLeadingZeroCast::class,
  52. 'payment_terms' => PaymentTerms::class,
  53. 'font' => Font::class,
  54. 'template' => Template::class,
  55. 'item_name' => AsArrayObject::class,
  56. 'unit_name' => AsArrayObject::class,
  57. 'price_name' => AsArrayObject::class,
  58. 'amount_name' => AsArrayObject::class,
  59. ];
  60. protected $appends = [
  61. 'logo_url',
  62. ];
  63. protected function logoUrl(): Attribute
  64. {
  65. return Attribute::get(static function (mixed $value, array $attributes): ?string {
  66. return $attributes['logo'] ? Storage::disk('public')->url($attributes['logo']) : null;
  67. });
  68. }
  69. public function company(): BelongsTo
  70. {
  71. return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
  72. }
  73. public function createdBy(): BelongsTo
  74. {
  75. return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
  76. }
  77. public function updatedBy(): BelongsTo
  78. {
  79. return $this->belongsTo(FilamentCompanies::userModel(), 'updated_by');
  80. }
  81. public function scopeType(Builder $query, string | DocumentType $type): Builder
  82. {
  83. return $query->where($this->qualifyColumn('type'), $type);
  84. }
  85. public function scopeInvoice(Builder $query): Builder
  86. {
  87. return $query->scopes(['type' => [DocumentType::Invoice]]);
  88. }
  89. public function scopeBill(Builder $query): Builder
  90. {
  91. return $query->scopes(['type' => [DocumentType::Bill]]);
  92. }
  93. public static function availableNumberDigits(): array
  94. {
  95. return array_combine(range(1, 20), range(1, 20));
  96. }
  97. public static function getNumberNext(?bool $padded = null, ?bool $format = null, ?string $prefix = null, int | string | null $digits = null, int | string | null $next = null, ?string $type = null): string
  98. {
  99. $initializeAttributes = new static;
  100. [$number_prefix, $number_digits, $number_next] = $initializeAttributes->initializeAttributes($prefix, $digits, $next, $type);
  101. if ($format) {
  102. return $number_prefix . static::getPaddedNumberNext($number_next, $number_digits);
  103. }
  104. if ($padded) {
  105. return static::getPaddedNumberNext($number_next, $number_digits);
  106. }
  107. return $number_next;
  108. }
  109. public function initializeAttributes(?string $prefix, int | string | null $digits, int | string | null $next, ?string $type): array
  110. {
  111. $number_prefix = $prefix ?? $this->getAttributeFromArray('number_prefix');
  112. $number_digits = $digits ?? $this->getAttributeFromArray('number_digits');
  113. $number_next = $next ?? $this->getAttributeFromArray('number_next');
  114. if ($type) {
  115. $attributes = static::getAttributesByType($type);
  116. $number_prefix = $attributes['number_prefix'] ?? $number_prefix;
  117. $number_digits = $attributes['number_digits'] ?? $number_digits;
  118. $number_next = $attributes['number_next'] ?? $number_next;
  119. }
  120. return [$number_prefix, $number_digits, $number_next];
  121. }
  122. public static function getAttributesByType(?string $type): array
  123. {
  124. $model = new static;
  125. $attributes = $model->newQuery()->type($type)->first();
  126. return $attributes ? $attributes->toArray() : [];
  127. }
  128. /**
  129. * Get the next number with padding for dynamic display purposes.
  130. * Even if number_next is a string, it will be cast to an integer.
  131. */
  132. public static function getPaddedNumberNext(int | string | null $number_next, int | string | null $number_digits): string
  133. {
  134. return str_pad($number_next, $number_digits, '0', STR_PAD_LEFT);
  135. }
  136. public static function getAvailableItemNameOptions(): array
  137. {
  138. $options = [
  139. 'items' => 'Items',
  140. 'products' => 'Products',
  141. 'services' => 'Services',
  142. 'other' => 'Other',
  143. ];
  144. return array_map(translate(...), $options);
  145. }
  146. public static function getAvailableUnitNameOptions(): array
  147. {
  148. $options = [
  149. 'quantity' => 'Quantity',
  150. 'hours' => 'Hours',
  151. 'other' => 'Other',
  152. ];
  153. return array_map(translate(...), $options);
  154. }
  155. public static function getAvailablePriceNameOptions(): array
  156. {
  157. $options = [
  158. 'price' => 'Price',
  159. 'rate' => 'Rate',
  160. 'other' => 'Other',
  161. ];
  162. return array_map(translate(...), $options);
  163. }
  164. public static function getAvailableAmountNameOptions(): array
  165. {
  166. $options = [
  167. 'amount' => 'Amount',
  168. 'total' => 'Total',
  169. 'other' => 'Other',
  170. ];
  171. return array_map(translate(...), $options);
  172. }
  173. public function getLabelOptionFor(string $optionType, ?string $optionValue)
  174. {
  175. $optionValue = $optionValue ?? $this->{$optionType}['option'];
  176. if (! $optionValue) {
  177. return null;
  178. }
  179. $options = match ($optionType) {
  180. 'item_name' => static::getAvailableItemNameOptions(),
  181. 'unit_name' => static::getAvailableUnitNameOptions(),
  182. 'price_name' => static::getAvailablePriceNameOptions(),
  183. 'amount_name' => static::getAvailableAmountNameOptions(),
  184. default => [],
  185. };
  186. return $options[$optionValue] ?? null;
  187. }
  188. protected static function newFactory(): Factory
  189. {
  190. return DocumentDefaultFactory::new();
  191. }
  192. }