BF race condition
This commit is contained in:
parent
c43910b927
commit
a12738db1c
|
|
@ -373,6 +373,16 @@ class CartItem extends Model
|
|||
* Update the booking dates for this cart item.
|
||||
* Automatically recalculates price based on the new date range.
|
||||
*
|
||||
* IMPORTANT: This method uses cart-aware pricing!
|
||||
* For pool products, it automatically considers which price tiers are already
|
||||
* used in the cart to determine the next available price based on the pricing
|
||||
* strategy (LOWEST, HIGHEST, AVERAGE).
|
||||
*
|
||||
* The method passes the NEW dates to getCurrentPrice() to ensure accurate
|
||||
* pricing calculations. Without passing dates, the pricing logic would use
|
||||
* stale dates from the cart item before the update, potentially selecting
|
||||
* the wrong price tier.
|
||||
*
|
||||
* NOTE: This method allows setting any dates, even if they're not available.
|
||||
* Use the is_ready_to_checkout attribute to check if the dates are valid.
|
||||
*
|
||||
|
|
@ -392,7 +402,14 @@ class CartItem extends Model
|
|||
if (is_string($until)) {
|
||||
$until = \Carbon\Carbon::parse($until);
|
||||
}
|
||||
if ($from >= $until && $until) {
|
||||
|
||||
// Validate that both dates are provided
|
||||
if (!$from || !$until) {
|
||||
throw new \Exception("Both 'from' and 'until' dates are required.");
|
||||
}
|
||||
|
||||
// Validate date order
|
||||
if ($from >= $until) {
|
||||
throw new \Exception("The 'from' date must be before the 'until' date.");
|
||||
}
|
||||
|
||||
|
|
@ -406,8 +423,9 @@ class CartItem extends Model
|
|||
$days = $this->calculateBookingDays($from, $until);
|
||||
|
||||
// Get current price per day
|
||||
$pricePerDay = $product->getCurrentPrice();
|
||||
$regularPricePerDay = $product->getCurrentPrice(false) ?? $pricePerDay;
|
||||
// Pass dates to ensure accurate pricing for pool products during date updates
|
||||
$pricePerDay = $product->getCurrentPrice(null, $this->cart, $from, $until);
|
||||
$regularPricePerDay = $product->getCurrentPrice(false, $this->cart, $from, $until) ?? $pricePerDay;
|
||||
|
||||
// Calculate new prices
|
||||
$pricePerUnit = $pricePerDay * $days;
|
||||
|
|
@ -439,22 +457,24 @@ class CartItem extends Model
|
|||
$from = \Carbon\Carbon::parse($from);
|
||||
}
|
||||
|
||||
// Refresh to get current state
|
||||
$this->refresh();
|
||||
|
||||
if ($this->until && $from >= $this->until) {
|
||||
throw new InvalidDateRangeException();
|
||||
}
|
||||
|
||||
// Refresh to get current state before checking
|
||||
$this->refresh();
|
||||
// Get the current until date before updating
|
||||
$currentUntil = $this->until;
|
||||
|
||||
$this->update(['from' => $from]);
|
||||
$this->refresh();
|
||||
|
||||
// If both dates are now set, recalculate pricing
|
||||
if ($this->until) {
|
||||
return $this->updateDates($this->from, $this->until);
|
||||
// If both dates are set, use updateDates to recalculate pricing
|
||||
if ($currentUntil) {
|
||||
return $this->updateDates($from, $currentUntil);
|
||||
}
|
||||
|
||||
return $this;
|
||||
// Otherwise just update the from date
|
||||
$this->update(['from' => $from]);
|
||||
return $this->fresh();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -471,21 +491,23 @@ class CartItem extends Model
|
|||
$until = \Carbon\Carbon::parse($until);
|
||||
}
|
||||
|
||||
// Refresh to get current state
|
||||
$this->refresh();
|
||||
|
||||
if ($this->from && $this->from >= $until) {
|
||||
throw new InvalidDateRangeException();
|
||||
}
|
||||
|
||||
// Refresh to get current state before checking
|
||||
$this->refresh();
|
||||
// Get the current from date before updating
|
||||
$currentFrom = $this->from;
|
||||
|
||||
// If both dates are set, use updateDates to recalculate pricing
|
||||
if ($currentFrom) {
|
||||
return $this->updateDates($currentFrom, $until);
|
||||
}
|
||||
|
||||
// Otherwise just update the until date
|
||||
$this->update(['until' => $until]);
|
||||
$this->refresh();
|
||||
|
||||
// If both dates are now set, recalculate pricing
|
||||
if ($this->from) {
|
||||
return $this->updateDates($this->from, $this->until);
|
||||
}
|
||||
|
||||
return $this;
|
||||
return $this->fresh();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,10 +369,45 @@ class Product extends Model implements Purchasable, Cartable
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the current price with pool product inheritance support
|
||||
* Get the current price with pool product inheritance support and cart-aware pricing.
|
||||
*
|
||||
* IMPORTANT: This method handles cart-aware pricing automatically!
|
||||
*
|
||||
* For pool products, this method:
|
||||
* - Automatically retrieves the cart from session or authenticated user if not provided
|
||||
* - Considers which price tiers are already used in the cart
|
||||
* - Returns the next available price based on the pricing strategy (LOWEST, HIGHEST, AVERAGE)
|
||||
*
|
||||
* ⚠️ COMMON MISTAKE: Do NOT call getNextAvailablePoolPriceConsideringCart() directly!
|
||||
* Always use this method instead, as it handles cart resolution and edge cases properly.
|
||||
*
|
||||
* Example usage:
|
||||
* ```php
|
||||
* ✅ CORRECT: Let getCurrentPrice handle cart resolution
|
||||
* $price = $product->getCurrentPrice();
|
||||
*
|
||||
* ✅ CORRECT: Pass cart explicitly if you have it
|
||||
* $price = $product->getCurrentPrice(null, $cart);
|
||||
*
|
||||
* ✅ CORRECT: Pass dates for booking calculations
|
||||
* $price = $product->getCurrentPrice(null, $cart, $fromDate, $untilDate);
|
||||
*
|
||||
* ❌ WRONG: Bypasses cart resolution and session handling
|
||||
* $price = $product->getNextAvailablePoolPriceConsideringCart($cart, null);
|
||||
* ```
|
||||
*
|
||||
* @param bool|null $sales_price Whether to get sale price (null = auto-detect)
|
||||
* @param mixed $cart Optional cart instance (auto-resolved from session/user if not provided)
|
||||
* @param \DateTimeInterface|null $from Optional start date for booking calculations
|
||||
* @param \DateTimeInterface|null $until Optional end date for booking calculations
|
||||
* @return float|null The current price, or null if unavailable
|
||||
*/
|
||||
public function getCurrentPrice(bool|null $sales_price = null, mixed $cart = null): ?float
|
||||
{
|
||||
public function getCurrentPrice(
|
||||
bool|null $sales_price = null,
|
||||
mixed $cart = null,
|
||||
?\DateTimeInterface $from = null,
|
||||
?\DateTimeInterface $until = null
|
||||
): ?float {
|
||||
// If this is a pool product, use cart-aware pricing if cart is provided
|
||||
if ($this->isPool()) {
|
||||
// If no cart provided, try to get the cart from session first, then user's cart
|
||||
|
|
@ -396,7 +431,7 @@ class Product extends Model implements Purchasable, Cartable
|
|||
if ($cart) {
|
||||
// Cart-aware: Use smarter pricing that considers which price tiers are used
|
||||
// This returns null if no items are available (all sold out)
|
||||
return $this->getNextAvailablePoolPriceConsideringCart($cart, $sales_price);
|
||||
return $this->getNextAvailablePoolPriceConsideringCart($cart, $sales_price, $from, $until);
|
||||
}
|
||||
|
||||
// No cart: Get inherited price from single items
|
||||
|
|
|
|||
|
|
@ -361,8 +361,9 @@ class PoolPerMinutePricingTest extends TestCase
|
|||
/** @test */
|
||||
public function it_updates_pool_cart_item_from_date_recalculates_per_minute_price()
|
||||
{
|
||||
$from = Carbon::now()->addDays(5)->setTime(12, 0, 0);
|
||||
$until = Carbon::now()->addDays(6)->setTime(12, 0, 0); // 24 hours
|
||||
$now = Carbon::now();
|
||||
$from = $now->copy()->addDays(5)->setTime(12, 0, 0);
|
||||
$until = $now->copy()->addDays(6)->setTime(12, 0, 0); // 24 hours
|
||||
|
||||
$cart = \Blax\Shop\Models\Cart::factory()->create([
|
||||
'customer_id' => $this->user->id,
|
||||
|
|
@ -374,7 +375,7 @@ class PoolPerMinutePricingTest extends TestCase
|
|||
$this->assertEquals(30.00, $cartItem->price);
|
||||
|
||||
// Update from date to make it 30 hours (1.25 days)
|
||||
$newFrom = Carbon::now()->addDays(5)->setTime(6, 0, 0);
|
||||
$newFrom = $now->copy()->addDays(5)->setTime(6, 0, 0);
|
||||
$cartItem->setFromDate($newFrom);
|
||||
|
||||
// Price should be $30 * 1.25 = $37.50
|
||||
|
|
|
|||
Loading…
Reference in New Issue