SparePartOrder.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. namespace App\Models;
  3. use App\Models\Scopes\YearScope;
  4. use Illuminate\Database\Eloquent\Attributes\ScopedBy;
  5. use Illuminate\Database\Eloquent\Model;
  6. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  7. use Illuminate\Database\Eloquent\Relations\HasMany;
  8. use Illuminate\Database\Eloquent\Relations\MorphTo;
  9. use Illuminate\Database\Eloquent\SoftDeletes;
  10. #[ScopedBy([YearScope::class])]
  11. class SparePartOrder extends Model
  12. {
  13. use SoftDeletes;
  14. const STATUS_ORDERED = 'ordered';
  15. const STATUS_IN_STOCK = 'in_stock';
  16. const STATUS_SHIPPED = 'shipped';
  17. const STATUS_NAMES = [
  18. self::STATUS_ORDERED => 'Заказано',
  19. self::STATUS_IN_STOCK => 'На складе',
  20. self::STATUS_SHIPPED => 'Отгружено',
  21. ];
  22. const DEFAULT_SORT_BY = 'created_at';
  23. protected $fillable = [
  24. 'year',
  25. 'spare_part_id',
  26. 'source_text',
  27. 'sourceable_id',
  28. 'sourceable_type',
  29. 'status',
  30. 'ordered_quantity',
  31. 'remaining_quantity',
  32. 'with_documents',
  33. 'note',
  34. 'user_id',
  35. ];
  36. protected $casts = [
  37. 'with_documents' => 'boolean',
  38. 'ordered_quantity' => 'integer',
  39. 'remaining_quantity' => 'integer',
  40. 'year' => 'integer',
  41. ];
  42. protected static function boot(): void
  43. {
  44. parent::boot();
  45. static::creating(function ($model) {
  46. if (!isset($model->year)) {
  47. $model->year = year();
  48. }
  49. if (!isset($model->remaining_quantity)) {
  50. $model->remaining_quantity = $model->ordered_quantity;
  51. }
  52. });
  53. // Автосмена статуса
  54. static::updating(function ($model) {
  55. if ($model->remaining_quantity === 0 && $model->status !== self::STATUS_SHIPPED) {
  56. $model->status = self::STATUS_SHIPPED;
  57. }
  58. });
  59. }
  60. // Отношения
  61. public function sparePart(): BelongsTo
  62. {
  63. return $this->belongsTo(SparePart::class);
  64. }
  65. public function user(): BelongsTo
  66. {
  67. return $this->belongsTo(User::class);
  68. }
  69. public function sourceable(): MorphTo
  70. {
  71. return $this->morphTo();
  72. }
  73. public function shipments(): HasMany
  74. {
  75. return $this->hasMany(SparePartOrderShipment::class);
  76. }
  77. // Scopes
  78. public function scopeInStock($query)
  79. {
  80. return $query->where('status', self::STATUS_IN_STOCK)
  81. ->where('remaining_quantity', '>', 0);
  82. }
  83. public function scopeWithDocuments($query, bool $withDocs = true)
  84. {
  85. return $query->where('with_documents', $withDocs);
  86. }
  87. // Метод списания
  88. public function shipQuantity(int $quantity, string $note, ?int $reclamationId = null, ?int $userId = null): bool
  89. {
  90. if ($quantity > $this->remaining_quantity) {
  91. return false;
  92. }
  93. // Уменьшаем остаток
  94. $this->remaining_quantity -= $quantity;
  95. $this->save();
  96. // Создаем запись в истории
  97. SparePartOrderShipment::create([
  98. 'spare_part_order_id' => $this->id,
  99. 'quantity' => $quantity,
  100. 'note' => $note,
  101. 'reclamation_id' => $reclamationId,
  102. 'user_id' => $userId ?? auth()->id(),
  103. 'is_automatic' => $reclamationId !== null,
  104. ]);
  105. return true;
  106. }
  107. // Получить имя статуса
  108. public function getStatusNameAttribute(): string
  109. {
  110. return self::STATUS_NAMES[$this->status] ?? $this->status;
  111. }
  112. }