96 lines
2.5 KiB
PHP
96 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Support\Facades\DB;
|
|
use App\Models\PurchaseItem;
|
|
use App\Models\Wine;
|
|
|
|
class Purchase extends Model
|
|
{
|
|
/** @use HasFactory<\Database\Factories\PurchaseFactory> */
|
|
use HasFactory;
|
|
protected $fillable = [
|
|
'user_id',
|
|
'wine_id',
|
|
'amount',
|
|
'price',
|
|
'status',
|
|
'purchased_at',
|
|
'note',
|
|
];
|
|
|
|
/**
|
|
* Casts for attributes
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $casts = [
|
|
'amount' => 'integer',
|
|
'price' => 'decimal:2',
|
|
'purchased_at' => 'datetime',
|
|
'status' => 'string',
|
|
'note' => 'string',
|
|
];
|
|
|
|
public function user()
|
|
{
|
|
return $this->belongsTo(User::class, 'user_id');
|
|
}
|
|
|
|
public function wine()
|
|
{
|
|
return $this->belongsTo(Wine::class, 'wine_id');
|
|
}
|
|
|
|
/**
|
|
* Purchase has many items (weak entity)
|
|
*/
|
|
public function items(): HasMany
|
|
{
|
|
return $this->hasMany(PurchaseItem::class, 'purchase_id');
|
|
}
|
|
|
|
/**
|
|
* Create a purchase with items and decrement wine stock atomically.
|
|
*
|
|
* $items = [ ['wine_id'=>1,'quantity'=>2,'unit_price'=>12.5], ... ]
|
|
*/
|
|
public static function createWithItems(array $data, array $items): self
|
|
{
|
|
return DB::transaction(function () use ($data, $items) {
|
|
$purchase = self::create($data);
|
|
|
|
foreach ($items as $it) {
|
|
$wine = Wine::findOrFail($it['wine_id']);
|
|
|
|
$qty = (int) ($it['quantity'] ?? 1);
|
|
if ($wine->bottles_in_stock !== null && $wine->bottles_in_stock < $qty) {
|
|
throw new \Exception("Insufficient stock for wine id {$wine->id}");
|
|
}
|
|
|
|
$unit = $it['unit_price'] ?? $wine->price_per_bottle ?? 0;
|
|
$lineTotal = round($qty * $unit, 2);
|
|
|
|
$purchase->items()->create([
|
|
'wine_id' => $wine->getKey(),
|
|
'quantity' => $qty,
|
|
'unit_price' => $unit,
|
|
'line_total' => $lineTotal,
|
|
]);
|
|
|
|
// decrement stock if tracked
|
|
if ($wine->bottles_in_stock !== null) {
|
|
$wine->decrement('bottles_in_stock', $qty);
|
|
}
|
|
}
|
|
|
|
return $purchase;
|
|
});
|
|
}
|
|
|
|
}
|