Compare commits
5 Commits
testbranch
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
3d66114163 | |
|
|
440be3a36f | |
|
|
1a8f111110 | |
|
|
a66fd7ccb8 | |
|
|
cbb4b84948 |
|
|
@ -50,3 +50,41 @@ if ($singlePrice !== null) {
|
||||||
- `src/Models/Cart.php` - removed pricing strategy comparison
|
- `src/Models/Cart.php` - removed pricing strategy comparison
|
||||||
|
|
||||||
**Key Learning:** ALWAYS verify understanding of business logic before implementing. Pool pricing strategy is about allocation order, not price comparison.
|
**Key Learning:** ALWAYS verify understanding of business logic before implementing. Pool pricing strategy is about allocation order, not price comparison.
|
||||||
|
|
||||||
|
### 2026-01-05: Cart Item Price/Currency Fixes
|
||||||
|
|
||||||
|
**Issues Fixed:**
|
||||||
|
1. Pool singles bookings should show `unit_amount` when added (not 0), even without dates
|
||||||
|
2. Bug: Date range adjustment was showing wrong price (5000 instead of 1755) when singles had no price
|
||||||
|
3. Added `currency` column to cart_items table to store currency from selected price
|
||||||
|
4. Removed obsolete `allocated_single_item_name` from meta (replaced by `product_id` column)
|
||||||
|
|
||||||
|
**Root Cause of Price Bug:**
|
||||||
|
- `updateDates()` was calling `$allocatedSingle->defaultPrice()->first()` instead of using `$this->price()->first()`
|
||||||
|
- When single has no price, `reallocatePoolItems` sets `price_id` to the pool's price model
|
||||||
|
- `updateDates()` was ignoring this and going back to the single's (non-existent) price
|
||||||
|
|
||||||
|
**Fix Applied:**
|
||||||
|
```php
|
||||||
|
// In CartItem::updateDates()
|
||||||
|
// IMPORTANT: Use the price_id relationship first, as it was set by reallocatePoolItems
|
||||||
|
$priceModel = $this->price_id ? $this->price()->first() : null;
|
||||||
|
if ($priceModel) {
|
||||||
|
$pricePerDay = $priceModel->getCurrentPrice(...);
|
||||||
|
} else {
|
||||||
|
// Fallback: Get price from the allocated single, with fallback to pool price
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**New CartItem Fields:**
|
||||||
|
- `currency`: Currency from the selected price model (e.g., 'USD', 'EUR')
|
||||||
|
|
||||||
|
**Removed:**
|
||||||
|
- `meta->allocated_single_item_name` - use `$cartItem->product->name` instead via the `product_id` relationship
|
||||||
|
|
||||||
|
**Files Modified:**
|
||||||
|
- `src/Models/CartItem.php` - added currency, fixed updateDates price resolution
|
||||||
|
- `src/Models/Cart.php` - added currency to addToCart and reallocatePoolItems
|
||||||
|
- `src/Traits/MayBePoolProduct.php` - added currency to getNextAvailablePoolItemWithPrice return
|
||||||
|
- `database/migrations/create_blax_shop_tables.php.stub` - added currency column
|
||||||
|
|
@ -40,8 +40,17 @@ The goal of this file is to not miss any important model traits, relationships,
|
||||||
|
|
||||||
### CartItem
|
### CartItem
|
||||||
- An item within a Cart.
|
- An item within a Cart.
|
||||||
- Links a `Product` and a specific `ProductPrice`.
|
- Links a `Product` (purchasable) and a specific `ProductPrice`.
|
||||||
- **Key Attributes**: `quantity`, `dates` (for bookings), `configuration`.
|
- **Key Attributes**:
|
||||||
|
- `purchasable_id`, `purchasable_type`: The product being purchased
|
||||||
|
- `product_id`: For pool items, the allocated single item; otherwise null
|
||||||
|
- `price_id`: The selected price model
|
||||||
|
- `currency`: Currency from the selected price
|
||||||
|
- `quantity`: Number of items
|
||||||
|
- `unit_amount`: Base price per unit (per day for bookings)
|
||||||
|
- `price`: Calculated price (unit_amount × days for bookings, same as unit_amount for simple)
|
||||||
|
- `subtotal`: Total (price × quantity)
|
||||||
|
- `from`, `until`: Booking date range (for booking products)
|
||||||
|
|
||||||
## Order Management
|
## Order Management
|
||||||
### Order
|
### Order
|
||||||
|
|
|
||||||
|
|
@ -234,4 +234,28 @@ class OrderFactory extends Factory
|
||||||
'payment_reference' => $reference ?? 'pi_' . $this->faker->regexify('[A-Za-z0-9]{24}'),
|
'payment_reference' => $reference ?? 'pi_' . $this->faker->regexify('[A-Za-z0-9]{24}'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set booking date range.
|
||||||
|
*/
|
||||||
|
public function withDateRange(
|
||||||
|
\DateTimeInterface $from,
|
||||||
|
\DateTimeInterface $until
|
||||||
|
): static {
|
||||||
|
return $this->state([
|
||||||
|
'from' => $from,
|
||||||
|
'until' => $until,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set as a booking order with default date range.
|
||||||
|
*/
|
||||||
|
public function booking(): static
|
||||||
|
{
|
||||||
|
return $this->state([
|
||||||
|
'from' => now()->addDay(),
|
||||||
|
'until' => now()->addDays(3),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,11 @@ class ProductFactory extends Factory
|
||||||
|
|
||||||
public function withPrices(
|
public function withPrices(
|
||||||
int $count = 1,
|
int $count = 1,
|
||||||
null|float $unit_amount = null,
|
null|int $unit_amount = null,
|
||||||
null|float $sale_unit_amount = null
|
null|int $sale_unit_amount = null
|
||||||
): static {
|
): static {
|
||||||
return $this->afterCreating(function (Product $product) use ($count, $unit_amount, $sale_unit_amount) {
|
return $this->afterCreating(function (Product $product) use ($count, $unit_amount, $sale_unit_amount) {
|
||||||
// Use realistic price range if not specified
|
// All prices are in cents (smallest currency unit)
|
||||||
$priceAmount = $unit_amount ?? $this->faker->randomElement([
|
$priceAmount = $unit_amount ?? $this->faker->randomElement([
|
||||||
1999, // $19.99
|
1999, // $19.99
|
||||||
2999, // $29.99
|
2999, // $29.99
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,7 @@ return new class extends Migration
|
||||||
$table->foreignUuid('purchase_id')->nullable()->constrained(config('shop.tables.product_purchases', 'product_purchases'))->nullOnDelete();
|
$table->foreignUuid('purchase_id')->nullable()->constrained(config('shop.tables.product_purchases', 'product_purchases'))->nullOnDelete();
|
||||||
$table->foreignUuid('price_id')->nullable()->constrained(config('shop.tables.product_prices', 'product_prices'))->nullOnDelete();
|
$table->foreignUuid('price_id')->nullable()->constrained(config('shop.tables.product_prices', 'product_prices'))->nullOnDelete();
|
||||||
$table->integer('quantity')->default(1);
|
$table->integer('quantity')->default(1);
|
||||||
|
$table->string('currency', 3)->nullable(); // Currency from the selected price
|
||||||
$table->integer('price')->nullable(); // Stored in cents, null = unavailable
|
$table->integer('price')->nullable(); // Stored in cents, null = unavailable
|
||||||
$table->integer('regular_price')->nullable(); // Stored in cents
|
$table->integer('regular_price')->nullable(); // Stored in cents
|
||||||
$table->integer('unit_amount')->nullable(); // Base unit price for 1 quantity, 1 day (in cents)
|
$table->integer('unit_amount')->nullable(); // Base unit price for 1 quantity, 1 day (in cents)
|
||||||
|
|
@ -409,6 +410,10 @@ return new class extends Migration
|
||||||
$table->string('ip_address')->nullable();
|
$table->string('ip_address')->nullable();
|
||||||
$table->text('user_agent')->nullable();
|
$table->text('user_agent')->nullable();
|
||||||
|
|
||||||
|
// Booking date range (for booking-related orders)
|
||||||
|
$table->timestamp('from')->nullable();
|
||||||
|
$table->timestamp('until')->nullable();
|
||||||
|
|
||||||
// Important timestamps
|
// Important timestamps
|
||||||
$table->timestamp('completed_at')->nullable();
|
$table->timestamp('completed_at')->nullable();
|
||||||
$table->timestamp('paid_at')->nullable();
|
$table->timestamp('paid_at')->nullable();
|
||||||
|
|
|
||||||
|
|
@ -305,13 +305,14 @@ $cartItem = $cart->addToCart($parkingPool, $quantity = 2, [], $from, $until);
|
||||||
- Claims 1 unit from each: `$spot->claimStock(1, $cartItem, $from, $until)`
|
- Claims 1 unit from each: `$spot->claimStock(1, $cartItem, $from, $until)`
|
||||||
|
|
||||||
3. **Store Claimed Items**
|
3. **Store Claimed Items**
|
||||||
- Cart item metadata stores which single items were claimed
|
- Cart item's `product_id` column stores which single item was allocated
|
||||||
- Metadata: `claimed_single_items: [spot1_id, spot2_id]`
|
- Each cart item is linked to one specific single item
|
||||||
|
|
||||||
4. **Calculate Price**
|
4. **Calculate Price**
|
||||||
- Gets price from available single items (using pricing strategy)
|
- Gets price from available single items (using pricing strategy)
|
||||||
- Multiplies by number of days
|
- If single has no price, falls back to pool's price
|
||||||
- Stores in cart item
|
- Multiplies by number of days for booking products
|
||||||
|
- Stores in cart item (unit_amount, price, subtotal)
|
||||||
|
|
||||||
### Manual Stock Operations
|
### Manual Stock Operations
|
||||||
|
|
||||||
|
|
@ -399,11 +400,15 @@ $cartItem = $cart->addToCart($parkingPool, $quantity = 1, [], $from, $until);
|
||||||
// Cart item properties:
|
// Cart item properties:
|
||||||
// - purchasable_id: Pool Product ID
|
// - purchasable_id: Pool Product ID
|
||||||
// - purchasable_type: Product::class
|
// - purchasable_type: Product::class
|
||||||
// - product_id: Allocated Single Item ID (NEW!)
|
// - product_id: Allocated Single Item ID
|
||||||
|
// - price_id: Price used (from single or pool fallback)
|
||||||
|
// - currency: Currency from the selected price
|
||||||
// - quantity: 1
|
// - quantity: 1
|
||||||
// - from: 2025-01-15
|
// - from: 2025-01-15
|
||||||
// - until: 2025-01-17
|
// - until: 2025-01-17
|
||||||
// - price: (unit_amount × 2 days)
|
// - unit_amount: Price per day (in cents)
|
||||||
|
// - price: unit_amount × days (calculated booking price)
|
||||||
|
// - subtotal: price × quantity
|
||||||
```
|
```
|
||||||
|
|
||||||
### Product ID Column
|
### Product ID Column
|
||||||
|
|
@ -687,13 +692,12 @@ $price = $pool->getLowestAvailablePoolPrice($from, $until);
|
||||||
|
|
||||||
### Single Items Not Released After Cart Deletion
|
### Single Items Not Released After Cart Deletion
|
||||||
|
|
||||||
**Cause:** Metadata not properly storing claimed items
|
**Cause:** Cart item's `product_id` not properly tracking claimed single
|
||||||
|
|
||||||
**Solution:**
|
**Solution:**
|
||||||
```php
|
```php
|
||||||
// Ensure cart item has metadata
|
// Check the cart item's product_id
|
||||||
$meta = $cartItem->getMeta();
|
$allocatedSingle = $cartItem->product;
|
||||||
$claimedItems = $meta->claimed_single_items ?? [];
|
|
||||||
|
|
||||||
// Manually release if needed
|
// Manually release if needed
|
||||||
$pool->releasePoolStock($cartItem);
|
$pool->releasePoolStock($cartItem);
|
||||||
|
|
|
||||||
|
|
@ -156,16 +156,18 @@ class StripeWebhookController
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record payment on the order
|
// Record payment on the order
|
||||||
$amountPaid = (int) (($session->amount_total ?? 0) / 100);
|
// Stripe provides amounts in cents, which matches our storage format
|
||||||
|
$amountPaid = (int) ($session->amount_total ?? 0);
|
||||||
$currency = strtoupper($session->currency ?? $order->currency ?? 'USD');
|
$currency = strtoupper($session->currency ?? $order->currency ?? 'USD');
|
||||||
|
|
||||||
// recordPayment(int $amount, ?string $reference, ?string $method, ?string $provider)
|
// recordPayment(int $amount, ?string $reference, ?string $method, ?string $provider)
|
||||||
$order->recordPayment($amountPaid, $session->payment_intent, 'stripe', 'stripe');
|
$order->recordPayment($amountPaid, $session->payment_intent, 'stripe', 'stripe');
|
||||||
|
|
||||||
// Add a detailed note
|
// Add a detailed note (customer-visible)
|
||||||
$order->addNote(
|
$order->addNote(
|
||||||
"Payment of " . Order::formatMoney($amountPaid, $currency) . " received via Stripe checkout (Session: {$session->id})",
|
"Payment of " . Order::formatMoney($amountPaid, $currency) . " received",
|
||||||
OrderNote::TYPE_PAYMENT
|
OrderNote::TYPE_PAYMENT,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mark order as processing if payment is successful
|
// Mark order as processing if payment is successful
|
||||||
|
|
@ -201,10 +203,11 @@ class StripeWebhookController
|
||||||
$order = $cart->order;
|
$order = $cart->order;
|
||||||
if ($order && $order->status->canTransitionTo(OrderStatus::FAILED)) {
|
if ($order && $order->status->canTransitionTo(OrderStatus::FAILED)) {
|
||||||
$order->update(['status' => OrderStatus::FAILED]);
|
$order->update(['status' => OrderStatus::FAILED]);
|
||||||
// addNote(string $content, string $type, bool $isCustomerNote, ?string $authorType, ?string $authorId)
|
// Internal note - payment failure details should not be shown to customer
|
||||||
$order->addNote(
|
$order->addNote(
|
||||||
"Payment failed via Stripe checkout (Session: {$session->id})",
|
"Payment failed via Stripe checkout (Session: {$session->id})",
|
||||||
OrderNote::TYPE_PAYMENT
|
OrderNote::TYPE_PAYMENT,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,9 +233,11 @@ class StripeWebhookController
|
||||||
// Add note to order if it exists
|
// Add note to order if it exists
|
||||||
$order = $cart->order;
|
$order = $cart->order;
|
||||||
if ($order) {
|
if ($order) {
|
||||||
|
// Internal note - session expiry is a technical detail
|
||||||
$order->addNote(
|
$order->addNote(
|
||||||
"Stripe checkout session expired (Session: {$session->id})",
|
"Stripe checkout session expired (Session: {$session->id})",
|
||||||
OrderNote::TYPE_SYSTEM
|
OrderNote::TYPE_SYSTEM,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -686,6 +686,7 @@ class Cart extends Model
|
||||||
'single' => $single,
|
'single' => $single,
|
||||||
'price' => $price,
|
'price' => $price,
|
||||||
'price_id' => $priceModel?->id,
|
'price_id' => $priceModel?->id,
|
||||||
|
'currency' => $priceModel?->currency,
|
||||||
'available' => $effectiveAvailable,
|
'available' => $effectiveAvailable,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -707,7 +708,6 @@ class Cart extends Model
|
||||||
'subtotal' => null,
|
'subtotal' => null,
|
||||||
'unit_amount' => null,
|
'unit_amount' => null,
|
||||||
]);
|
]);
|
||||||
$cartItem->updateMetaKey('allocated_single_item_name', null);
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -751,12 +751,10 @@ class Cart extends Model
|
||||||
if ($singleInfo['price_id'] && $singleInfo['price_id'] !== $cartItem->price_id) {
|
if ($singleInfo['price_id'] && $singleInfo['price_id'] !== $cartItem->price_id) {
|
||||||
$updates['price_id'] = $singleInfo['price_id'];
|
$updates['price_id'] = $singleInfo['price_id'];
|
||||||
}
|
}
|
||||||
$cartItem->update($updates);
|
if ($singleInfo['currency']) {
|
||||||
$cartItem->updateMetaKey('allocated_single_item_name', $single->name);
|
$updates['currency'] = $singleInfo['currency'];
|
||||||
|
|
||||||
// Legacy: update price_id if changed (now handled in the update above)
|
|
||||||
if (false) {
|
|
||||||
}
|
}
|
||||||
|
$cartItem->update($updates);
|
||||||
|
|
||||||
// Track usage
|
// Track usage
|
||||||
$singleUsage[$single->id] = $usedFromSingle + $neededQty;
|
$singleUsage[$single->id] = $usedFromSingle + $neededQty;
|
||||||
|
|
@ -795,21 +793,19 @@ class Cart extends Model
|
||||||
if ($singleInfo['price_id'] && $singleInfo['price_id'] !== $cartItem->price_id) {
|
if ($singleInfo['price_id'] && $singleInfo['price_id'] !== $cartItem->price_id) {
|
||||||
$updates['price_id'] = $singleInfo['price_id'];
|
$updates['price_id'] = $singleInfo['price_id'];
|
||||||
}
|
}
|
||||||
|
if ($singleInfo['currency']) {
|
||||||
|
$updates['currency'] = $singleInfo['currency'];
|
||||||
|
}
|
||||||
$cartItem->update($updates);
|
$cartItem->update($updates);
|
||||||
$cartItem->refresh(); // Ensure model reflects database state
|
$cartItem->refresh(); // Ensure model reflects database state
|
||||||
$cartItem->updateMetaKey('allocated_single_item_name', $single->name);
|
|
||||||
|
|
||||||
$firstAllocation = false;
|
$firstAllocation = false;
|
||||||
} else {
|
} else {
|
||||||
// Create a new cart item for the additional quantity
|
// Create a new cart item for the additional quantity
|
||||||
// Get price from the single
|
// Use the price info from singleInfo (already calculated with pool fallback)
|
||||||
$priceModel = $single->defaultPrice()->first();
|
$singlePrice = $singleInfo['price'];
|
||||||
$singlePrice = $priceModel?->getCurrentPrice($single->isOnSale());
|
$priceId = $singleInfo['price_id'];
|
||||||
|
$currency = $singleInfo['currency'];
|
||||||
if ($singlePrice === null && $poolProduct->hasPrice()) {
|
|
||||||
$priceModel = $poolProduct->defaultPrice()->first();
|
|
||||||
$singlePrice = $priceModel?->getCurrentPrice($poolProduct->isOnSale());
|
|
||||||
}
|
|
||||||
|
|
||||||
$days = $this->calculateBookingDays($from, $until);
|
$days = $this->calculateBookingDays($from, $until);
|
||||||
$pricePerUnit = (int) round($singlePrice * $days);
|
$pricePerUnit = (int) round($singlePrice * $days);
|
||||||
|
|
@ -818,8 +814,9 @@ class Cart extends Model
|
||||||
'purchasable_id' => $cartItem->purchasable_id,
|
'purchasable_id' => $cartItem->purchasable_id,
|
||||||
'purchasable_type' => $cartItem->purchasable_type,
|
'purchasable_type' => $cartItem->purchasable_type,
|
||||||
'product_id' => $single->id,
|
'product_id' => $single->id,
|
||||||
'price_id' => $priceModel?->id,
|
'price_id' => $priceId,
|
||||||
'quantity' => $qtyToAllocate,
|
'quantity' => $qtyToAllocate,
|
||||||
|
'currency' => $currency,
|
||||||
'price' => $pricePerUnit,
|
'price' => $pricePerUnit,
|
||||||
'regular_price' => $pricePerUnit,
|
'regular_price' => $pricePerUnit,
|
||||||
'unit_amount' => (int) round($singlePrice),
|
'unit_amount' => (int) round($singlePrice),
|
||||||
|
|
@ -828,8 +825,6 @@ class Cart extends Model
|
||||||
'from' => $from,
|
'from' => $from,
|
||||||
'until' => $until,
|
'until' => $until,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$newCartItem->updateMetaKey('allocated_single_item_name', $single->name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$singleUsage[$single->id] = $usedFromSingle + $qtyToAllocate;
|
$singleUsage[$single->id] = $usedFromSingle + $qtyToAllocate;
|
||||||
|
|
@ -847,7 +842,6 @@ class Cart extends Model
|
||||||
'subtotal' => null,
|
'subtotal' => null,
|
||||||
'unit_amount' => null,
|
'unit_amount' => null,
|
||||||
]);
|
]);
|
||||||
$cartItem->updateMetaKey('allocated_single_item_name', null);
|
|
||||||
} else {
|
} else {
|
||||||
// Partial allocation - the cart item was already updated with what we could allocate
|
// Partial allocation - the cart item was already updated with what we could allocate
|
||||||
// The remaining quantity is lost (over-capacity)
|
// The remaining quantity is lost (over-capacity)
|
||||||
|
|
@ -1263,7 +1257,7 @@ class Cart extends Model
|
||||||
$poolPriceId = $priceModel?->id;
|
$poolPriceId = $priceModel?->id;
|
||||||
|
|
||||||
// Still try to find a single item for allocation even with pool's direct price
|
// Still try to find a single item for allocation even with pool's direct price
|
||||||
// This ensures allocated_single_item_name is always set for pool items
|
// This ensures product_id is always set for pool items
|
||||||
if (!$poolSingleItem) {
|
if (!$poolSingleItem) {
|
||||||
$singleItems = $cartable->singleProducts;
|
$singleItems = $cartable->singleProducts;
|
||||||
foreach ($singleItems as $single) {
|
foreach ($singleItems as $single) {
|
||||||
|
|
@ -1338,20 +1332,26 @@ class Cart extends Model
|
||||||
return $existingItem->fresh();
|
return $existingItem->fresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine price_id for the cart item
|
// Determine price_id and currency for the cart item
|
||||||
$priceId = null;
|
$priceId = null;
|
||||||
|
$currency = null;
|
||||||
if ($cartable instanceof Product) {
|
if ($cartable instanceof Product) {
|
||||||
// For pool products, use the single item's price_id
|
// For pool products, use the single item's price_id and currency
|
||||||
if ($is_pool && $poolPriceId) {
|
if ($is_pool && $poolPriceId) {
|
||||||
$priceId = $poolPriceId;
|
$priceId = $poolPriceId;
|
||||||
|
// Get currency from poolItemData if available
|
||||||
|
$poolItemData = $cartable->getNextAvailablePoolItemWithPrice($this, null, $from, $until);
|
||||||
|
$currency = $poolItemData['currency'] ?? null;
|
||||||
} else {
|
} else {
|
||||||
// Get the default price for the product
|
// Get the default price for the product
|
||||||
$defaultPrice = $cartable->defaultPrice()->first();
|
$defaultPrice = $cartable->defaultPrice()->first();
|
||||||
$priceId = $defaultPrice?->id;
|
$priceId = $defaultPrice?->id;
|
||||||
|
$currency = $defaultPrice?->currency;
|
||||||
}
|
}
|
||||||
} elseif ($cartable instanceof \Blax\Shop\Models\ProductPrice) {
|
} elseif ($cartable instanceof \Blax\Shop\Models\ProductPrice) {
|
||||||
// If adding a ProductPrice directly, use its ID
|
// If adding a ProductPrice directly, use its ID and currency
|
||||||
$priceId = $cartable->id;
|
$priceId = $cartable->id;
|
||||||
|
$currency = $cartable->currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new cart item
|
// Create new cart item
|
||||||
|
|
@ -1361,6 +1361,7 @@ class Cart extends Model
|
||||||
'product_id' => ($cartable instanceof Product && $cartable->isPool() && $poolSingleItem) ? $poolSingleItem->id : null,
|
'product_id' => ($cartable instanceof Product && $cartable->isPool() && $poolSingleItem) ? $poolSingleItem->id : null,
|
||||||
'price_id' => $priceId,
|
'price_id' => $priceId,
|
||||||
'quantity' => $quantity,
|
'quantity' => $quantity,
|
||||||
|
'currency' => $currency,
|
||||||
'price' => $pricePerUnit, // Price per unit for the period
|
'price' => $pricePerUnit, // Price per unit for the period
|
||||||
'regular_price' => $regularPricePerUnit,
|
'regular_price' => $regularPricePerUnit,
|
||||||
'unit_amount' => $unitAmount, // Base price for 1 quantity, 1 day (in cents)
|
'unit_amount' => $unitAmount, // Base price for 1 quantity, 1 day (in cents)
|
||||||
|
|
@ -1370,11 +1371,6 @@ class Cart extends Model
|
||||||
'until' => ($is_booking) ? $until : null,
|
'until' => ($is_booking) ? $until : null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// For pool products, store the single item name in meta for display purposes
|
|
||||||
if ($cartable instanceof Product && $cartable->isPool() && $poolSingleItem) {
|
|
||||||
$cartItem->updateMetaKey('allocated_single_item_name', $poolSingleItem->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Touch activity timestamp
|
// Touch activity timestamp
|
||||||
$this->touchActivity();
|
$this->touchActivity();
|
||||||
|
|
||||||
|
|
@ -2035,7 +2031,7 @@ class Cart extends Model
|
||||||
// Build line item using price_data for dynamic pricing
|
// Build line item using price_data for dynamic pricing
|
||||||
$lineItem = [
|
$lineItem = [
|
||||||
'price_data' => [
|
'price_data' => [
|
||||||
'currency' => config('shop.currency', 'usd'),
|
'currency' => $item->price->currency ?? strtoupper($this->currency),
|
||||||
'product_data' => [
|
'product_data' => [
|
||||||
'name' => $productName,
|
'name' => $productName,
|
||||||
...($description ? ['description' => $description] : []),
|
...($description ? ['description' => $description] : []),
|
||||||
|
|
@ -2062,6 +2058,7 @@ class Cart extends Model
|
||||||
// Prepare session parameters
|
// Prepare session parameters
|
||||||
$sessionParams = [
|
$sessionParams = [
|
||||||
'payment_method_types' => ['card'],
|
'payment_method_types' => ['card'],
|
||||||
|
'currency' => strtoupper($this->currency),
|
||||||
'line_items' => $lineItems,
|
'line_items' => $lineItems,
|
||||||
'mode' => 'payment',
|
'mode' => 'payment',
|
||||||
'success_url' => $success_url,
|
'success_url' => $success_url,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ class CartItem extends Model
|
||||||
'product_id',
|
'product_id',
|
||||||
'price_id',
|
'price_id',
|
||||||
'quantity',
|
'quantity',
|
||||||
|
'currency',
|
||||||
'price',
|
'price',
|
||||||
'regular_price',
|
'regular_price',
|
||||||
'unit_amount',
|
'unit_amount',
|
||||||
|
|
@ -34,6 +35,7 @@ class CartItem extends Model
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'quantity' => 'integer',
|
'quantity' => 'integer',
|
||||||
|
'currency' => 'string',
|
||||||
'price' => 'integer',
|
'price' => 'integer',
|
||||||
'regular_price' => 'integer',
|
'regular_price' => 'integer',
|
||||||
'unit_amount' => 'integer',
|
'unit_amount' => 'integer',
|
||||||
|
|
@ -503,22 +505,37 @@ class CartItem extends Model
|
||||||
// This ensures consistency when reallocatePoolItems has already assigned a specific single
|
// This ensures consistency when reallocatePoolItems has already assigned a specific single
|
||||||
// The product_id column stores the actual single product being purchased
|
// The product_id column stores the actual single product being purchased
|
||||||
$allocatedSingleItemId = $this->product_id;
|
$allocatedSingleItemId = $this->product_id;
|
||||||
|
$currency = null;
|
||||||
|
|
||||||
if ($product->isPool() && $allocatedSingleItemId) {
|
if ($product->isPool() && $allocatedSingleItemId) {
|
||||||
// Get the allocated single item from the product_id column
|
// Get the allocated single item from the product_id column
|
||||||
$allocatedSingle = $this->product;
|
$allocatedSingle = $this->product;
|
||||||
|
|
||||||
if ($allocatedSingle) {
|
if ($allocatedSingle) {
|
||||||
// Get price from the allocated single, with fallback to pool price
|
// IMPORTANT: Use the price_id relationship first, as it was set by reallocatePoolItems
|
||||||
$priceModel = $allocatedSingle->defaultPrice()->first();
|
// to the correct price (either single's price or pool's fallback price).
|
||||||
$pricePerDay = $priceModel?->getCurrentPrice($allocatedSingle->isOnSale());
|
// Only fall back to defaultPrice() if price_id is not set.
|
||||||
$regularPricePerDay = $priceModel?->getCurrentPrice(false) ?? $pricePerDay;
|
$priceModel = $this->price_id ? $this->price()->first() : null;
|
||||||
|
|
||||||
// Fallback to pool price if single has no price
|
if ($priceModel) {
|
||||||
if ($pricePerDay === null && $product->hasPrice()) {
|
// Use the price model from price_id relationship
|
||||||
$poolPriceModel = $product->defaultPrice()->first();
|
$pricePerDay = $priceModel->getCurrentPrice($allocatedSingle->isOnSale() || $product->isOnSale());
|
||||||
$pricePerDay = $poolPriceModel?->getCurrentPrice($product->isOnSale());
|
$regularPricePerDay = $priceModel->getCurrentPrice(false) ?? $pricePerDay;
|
||||||
$regularPricePerDay = $poolPriceModel?->getCurrentPrice(false) ?? $pricePerDay;
|
$currency = $priceModel->currency;
|
||||||
|
} else {
|
||||||
|
// Fallback: Get price from the allocated single, with fallback to pool price
|
||||||
|
$priceModel = $allocatedSingle->defaultPrice()->first();
|
||||||
|
$pricePerDay = $priceModel?->getCurrentPrice($allocatedSingle->isOnSale());
|
||||||
|
$regularPricePerDay = $priceModel?->getCurrentPrice(false) ?? $pricePerDay;
|
||||||
|
$currency = $priceModel?->currency;
|
||||||
|
|
||||||
|
// Fallback to pool price if single has no price
|
||||||
|
if ($pricePerDay === null && $product->hasPrice()) {
|
||||||
|
$poolPriceModel = $product->defaultPrice()->first();
|
||||||
|
$pricePerDay = $poolPriceModel?->getCurrentPrice($product->isOnSale());
|
||||||
|
$regularPricePerDay = $poolPriceModel?->getCurrentPrice(false) ?? $pricePerDay;
|
||||||
|
$currency = $poolPriceModel?->currency;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Allocated single not found - this is an error state, mark as unavailable
|
// Allocated single not found - this is an error state, mark as unavailable
|
||||||
|
|
@ -538,6 +555,10 @@ class CartItem extends Model
|
||||||
// Pass cart item ID to exclude this item from usage calculation
|
// Pass cart item ID to exclude this item from usage calculation
|
||||||
$pricePerDay = $product->getCurrentPrice(null, $this->cart, $from, $until, $this->id);
|
$pricePerDay = $product->getCurrentPrice(null, $this->cart, $from, $until, $this->id);
|
||||||
$regularPricePerDay = $product->getCurrentPrice(false, $this->cart, $from, $until, $this->id) ?? $pricePerDay;
|
$regularPricePerDay = $product->getCurrentPrice(false, $this->cart, $from, $until, $this->id) ?? $pricePerDay;
|
||||||
|
|
||||||
|
// Get currency from the price model
|
||||||
|
$priceModel = $product->defaultPrice()->first();
|
||||||
|
$currency = $priceModel?->currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no price found, mark as unavailable
|
// If no price found, mark as unavailable
|
||||||
|
|
@ -563,6 +584,7 @@ class CartItem extends Model
|
||||||
$this->update([
|
$this->update([
|
||||||
'from' => $from,
|
'from' => $from,
|
||||||
'until' => $until,
|
'until' => $until,
|
||||||
|
'currency' => $currency,
|
||||||
'price' => $pricePerUnit,
|
'price' => $pricePerUnit,
|
||||||
'regular_price' => $regularPricePerUnit,
|
'regular_price' => $regularPricePerUnit,
|
||||||
'unit_amount' => $unitAmount,
|
'unit_amount' => $unitAmount,
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ class Order extends Model
|
||||||
'internal_note',
|
'internal_note',
|
||||||
'ip_address',
|
'ip_address',
|
||||||
'user_agent',
|
'user_agent',
|
||||||
|
'from',
|
||||||
|
'until',
|
||||||
'completed_at',
|
'completed_at',
|
||||||
'paid_at',
|
'paid_at',
|
||||||
'shipped_at',
|
'shipped_at',
|
||||||
|
|
@ -68,6 +70,8 @@ class Order extends Model
|
||||||
'billing_address' => 'object',
|
'billing_address' => 'object',
|
||||||
'shipping_address' => 'object',
|
'shipping_address' => 'object',
|
||||||
'meta' => 'object',
|
'meta' => 'object',
|
||||||
|
'from' => 'datetime',
|
||||||
|
'until' => 'datetime',
|
||||||
'completed_at' => 'datetime',
|
'completed_at' => 'datetime',
|
||||||
'paid_at' => 'datetime',
|
'paid_at' => 'datetime',
|
||||||
'shipped_at' => 'datetime',
|
'shipped_at' => 'datetime',
|
||||||
|
|
@ -143,10 +147,11 @@ class Order extends Model
|
||||||
$difference = $newPaid - $oldPaid;
|
$difference = $newPaid - $oldPaid;
|
||||||
|
|
||||||
if ($difference > 0) {
|
if ($difference > 0) {
|
||||||
|
$currency = $order->currency ?? config('shop.currency', 'USD');
|
||||||
$order->addNote(
|
$order->addNote(
|
||||||
"Payment received: " . static::formatMoney($difference, $order->currency),
|
"Payment received: " . static::formatMoney($difference, $currency),
|
||||||
'payment',
|
'payment',
|
||||||
false
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mark as paid if fully paid
|
// Mark as paid if fully paid
|
||||||
|
|
@ -437,10 +442,12 @@ class Order extends Model
|
||||||
$this->amount_refunded = ($this->amount_refunded ?? 0) + $amount;
|
$this->amount_refunded = ($this->amount_refunded ?? 0) + $amount;
|
||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
|
$currency = $this->currency ?? config('shop.currency', 'USD');
|
||||||
$this->addNote(
|
$this->addNote(
|
||||||
"Refund processed: " . static::formatMoney($amount, $this->currency) .
|
"Refund processed: " . static::formatMoney($amount, $currency) .
|
||||||
($reason ? " - Reason: {$reason}" : ''),
|
($reason ? " - Reason: {$reason}" : ''),
|
||||||
'refund'
|
'refund',
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// If fully refunded, update status
|
// If fully refunded, update status
|
||||||
|
|
@ -458,20 +465,27 @@ class Order extends Model
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a note to the order.
|
* Add a note to the order.
|
||||||
|
*
|
||||||
|
* @param string $content The note content
|
||||||
|
* @param string $type The note type (note, status_change, payment, etc.)
|
||||||
|
* @param bool $isCustomerNote Whether the note is visible to the customer
|
||||||
|
* @param \Illuminate\Database\Eloquent\Model|null $author The author model (User, Admin, etc.)
|
||||||
|
* @param array|object|null $meta Additional metadata
|
||||||
*/
|
*/
|
||||||
public function addNote(
|
public function addNote(
|
||||||
string $content,
|
string $content,
|
||||||
string $type = 'note',
|
string $type = 'note',
|
||||||
bool $isCustomerNote = false,
|
bool $isCustomerNote = false,
|
||||||
?string $authorType = null,
|
$author = null,
|
||||||
?string $authorId = null
|
$meta = null
|
||||||
): OrderNote {
|
): OrderNote {
|
||||||
return $this->notes()->create([
|
return $this->notes()->create([
|
||||||
'content' => $content,
|
'content' => $content,
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'is_customer_note' => $isCustomerNote,
|
'is_customer_note' => $isCustomerNote,
|
||||||
'author_type' => $authorType,
|
'author_type' => $author ? get_class($author) : null,
|
||||||
'author_id' => $authorId,
|
'author_id' => $author?->getKey(),
|
||||||
|
'meta' => $meta,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -555,6 +569,56 @@ class Order extends Model
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope to get final/finished orders (completed, cancelled, refunded, failed, delivered).
|
||||||
|
* Uses OrderStatus::isFinal() to determine which statuses are final.
|
||||||
|
*/
|
||||||
|
public function scopeFinal($query)
|
||||||
|
{
|
||||||
|
$finalStatuses = array_map(
|
||||||
|
fn(OrderStatus $status) => $status->value,
|
||||||
|
array_filter(OrderStatus::cases(), fn(OrderStatus $status) => $status->isFinal())
|
||||||
|
);
|
||||||
|
|
||||||
|
return $query->whereIn('status', $finalStatuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for scopeFinal - get finished orders.
|
||||||
|
*/
|
||||||
|
public function scopeFinished($query)
|
||||||
|
{
|
||||||
|
return $this->scopeFinal($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope to get orders requiring payment (pending status).
|
||||||
|
* Uses OrderStatus::requiresPayment() to determine which statuses require payment.
|
||||||
|
*/
|
||||||
|
public function scopeRequiresPayment($query)
|
||||||
|
{
|
||||||
|
$requiresPaymentStatuses = array_map(
|
||||||
|
fn(OrderStatus $status) => $status->value,
|
||||||
|
array_filter(OrderStatus::cases(), fn(OrderStatus $status) => $status->requiresPayment())
|
||||||
|
);
|
||||||
|
|
||||||
|
return $query->whereIn('status', $requiresPaymentStatuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scope to get orders with a paid status (processing, shipped, delivered, etc.).
|
||||||
|
* Uses OrderStatus::isPaid() - this is different from scopePaid() which checks amounts.
|
||||||
|
*/
|
||||||
|
public function scopeStatusPaid($query)
|
||||||
|
{
|
||||||
|
$paidStatuses = array_map(
|
||||||
|
fn(OrderStatus $status) => $status->value,
|
||||||
|
array_filter(OrderStatus::cases(), fn(OrderStatus $status) => $status->isPaid())
|
||||||
|
);
|
||||||
|
|
||||||
|
return $query->whereIn('status', $paidStatuses);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scope to get completed orders.
|
* Scope to get completed orders.
|
||||||
*/
|
*/
|
||||||
|
|
@ -564,7 +628,7 @@ class Order extends Model
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scope to get paid orders.
|
* Scope to get paid orders (by amount).
|
||||||
*/
|
*/
|
||||||
public function scopePaid($query)
|
public function scopePaid($query)
|
||||||
{
|
{
|
||||||
|
|
@ -572,7 +636,7 @@ class Order extends Model
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scope to get unpaid orders.
|
* Scope to get unpaid orders (by amount).
|
||||||
*/
|
*/
|
||||||
public function scopeUnpaid($query)
|
public function scopeUnpaid($query)
|
||||||
{
|
{
|
||||||
|
|
@ -834,17 +898,19 @@ class Order extends Model
|
||||||
'customer_type' => $cart->customer_type,
|
'customer_type' => $cart->customer_type,
|
||||||
'customer_id' => $cart->customer_id,
|
'customer_id' => $cart->customer_id,
|
||||||
'currency' => $cart->currency ?? config('shop.currency', 'USD'),
|
'currency' => $cart->currency ?? config('shop.currency', 'USD'),
|
||||||
'amount_subtotal' => (int) $cart->getTotal() * 100,
|
'amount_subtotal' => (int) $cart->getTotal(),
|
||||||
'amount_discount' => 0, // TODO: Calculate from cart discounts
|
'amount_discount' => 0, // TODO: Calculate from cart discounts
|
||||||
'amount_shipping' => 0,
|
'amount_shipping' => 0,
|
||||||
'amount_tax' => 0,
|
'amount_tax' => 0,
|
||||||
'amount_total' => (int) $cart->getTotal() * 100,
|
'amount_total' => (int) $cart->getTotal(),
|
||||||
'amount_paid' => 0,
|
'amount_paid' => 0,
|
||||||
'amount_refunded' => 0,
|
'amount_refunded' => 0,
|
||||||
|
'from' => $cart->from,
|
||||||
|
'until' => $cart->until,
|
||||||
'status' => OrderStatus::PENDING,
|
'status' => OrderStatus::PENDING,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$order->addNote('Order created from cart checkout', 'system', false);
|
$order->addNote('Order created from cart checkout', 'system', true);
|
||||||
|
|
||||||
return $order;
|
return $order;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -744,7 +744,7 @@ trait MayBePoolProduct
|
||||||
* @param \DateTimeInterface|null $from Start date for availability check
|
* @param \DateTimeInterface|null $from Start date for availability check
|
||||||
* @param \DateTimeInterface|null $until End date for availability check
|
* @param \DateTimeInterface|null $until End date for availability check
|
||||||
* @param string|int|null $excludeCartItemId Cart item ID to exclude from usage calculation (for date updates)
|
* @param string|int|null $excludeCartItemId Cart item ID to exclude from usage calculation (for date updates)
|
||||||
* @return array|null ['price' => float, 'item' => Product, 'price_id' => string|null]
|
* @return array|null ['price' => float, 'item' => Product, 'price_id' => string|null, 'currency' => string|null]
|
||||||
*/
|
*/
|
||||||
public function getNextAvailablePoolItemWithPrice(
|
public function getNextAvailablePoolItemWithPrice(
|
||||||
\Blax\Shop\Models\Cart $cart,
|
\Blax\Shop\Models\Cart $cart,
|
||||||
|
|
@ -856,6 +856,7 @@ trait MayBePoolProduct
|
||||||
'quantity' => $availableFromThisItem,
|
'quantity' => $availableFromThisItem,
|
||||||
'item' => $item,
|
'item' => $item,
|
||||||
'price_id' => $priceModel?->id,
|
'price_id' => $priceModel?->id,
|
||||||
|
'currency' => $priceModel?->currency,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -895,6 +896,7 @@ trait MayBePoolProduct
|
||||||
'price' => $averagePrice,
|
'price' => $averagePrice,
|
||||||
'item' => $availableItems[0]['item'],
|
'item' => $availableItems[0]['item'],
|
||||||
'price_id' => $availableItems[0]['price_id'],
|
'price_id' => $availableItems[0]['price_id'],
|
||||||
|
'currency' => $availableItems[0]['currency'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -912,6 +914,7 @@ trait MayBePoolProduct
|
||||||
'price' => $availableItems[0]['price'],
|
'price' => $availableItems[0]['price'],
|
||||||
'item' => $availableItems[0]['item'],
|
'item' => $availableItems[0]['item'],
|
||||||
'price_id' => $availableItems[0]['price_id'],
|
'price_id' => $availableItems[0]['price_id'],
|
||||||
|
'currency' => $availableItems[0]['currency'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -516,7 +516,7 @@ class BookingPerMinutePricingTest extends TestCase
|
||||||
|
|
||||||
$single_product = Product::factory()
|
$single_product = Product::factory()
|
||||||
->withStocks(5)
|
->withStocks(5)
|
||||||
->withPrices(1, 5000) // $50.00
|
->withPrices(1, 5000) // 5000 cents = $50.00
|
||||||
->create([
|
->create([
|
||||||
'name' => 'Wine Bottle',
|
'name' => 'Wine Bottle',
|
||||||
'slug' => 'wine-bottle',
|
'slug' => 'wine-bottle',
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ class CartFacadeTest extends TestCase
|
||||||
|
|
||||||
$total = Cart::total();
|
$total = Cart::total();
|
||||||
|
|
||||||
$this->assertEquals(200.00, $total);
|
$this->assertEquals(200, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -219,7 +219,7 @@ class CartFacadeTest extends TestCase
|
||||||
|
|
||||||
$unpaid = Cart::unpaidAmount();
|
$unpaid = Cart::unpaidAmount();
|
||||||
|
|
||||||
$this->assertEquals(200.00, $unpaid);
|
$this->assertEquals(200, $unpaid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -230,7 +230,7 @@ class CartFacadeTest extends TestCase
|
||||||
|
|
||||||
$paid = Cart::paidAmount();
|
$paid = Cart::paidAmount();
|
||||||
|
|
||||||
$this->assertEquals(0.00, $paid);
|
$this->assertEquals(0, $paid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -292,7 +292,7 @@ class CartFacadeTest extends TestCase
|
||||||
Cart::add($p2, quantity: 1); // 75
|
Cart::add($p2, quantity: 1); // 75
|
||||||
Cart::add($p3, quantity: 4); // 100
|
Cart::add($p3, quantity: 4); // 100
|
||||||
|
|
||||||
$this->assertEquals(275.00, Cart::total());
|
$this->assertEquals(275, Cart::total());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -300,10 +300,10 @@ class CartFacadeTest extends TestCase
|
||||||
{
|
{
|
||||||
$product = Product::factory()->withStocks(50)->withPrices(1, 100)->create();
|
$product = Product::factory()->withStocks(50)->withPrices(1, 100)->create();
|
||||||
Cart::add($product, quantity: 5);
|
Cart::add($product, quantity: 5);
|
||||||
$this->assertEquals(500.00, Cart::total());
|
$this->assertEquals(500, Cart::total());
|
||||||
|
|
||||||
Cart::remove($product, quantity: 2);
|
Cart::remove($product, quantity: 2);
|
||||||
|
|
||||||
$this->assertEquals(300.00, Cart::total());
|
$this->assertEquals(300, Cart::total());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_item_has_is_booking_attribute_for_booking_products()
|
public function cart_item_has_is_booking_attribute_for_booking_products()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -33,7 +33,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_item_has_is_booking_false_for_regular_products()
|
public function cart_item_has_is_booking_false_for_regular_products()
|
||||||
{
|
{
|
||||||
$regularProduct = Product::factory()
|
$regularProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 50.00)
|
->withPrices(unit_amount: 5000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -46,7 +46,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_item_is_booking_works_via_price_id()
|
public function cart_item_is_booking_works_via_price_id()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -64,11 +64,11 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_is_full_booking_is_true_when_all_items_are_bookings()
|
public function cart_is_full_booking_is_true_when_all_items_are_bookings()
|
||||||
{
|
{
|
||||||
$booking1 = Product::factory()
|
$booking1 = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$booking2 = Product::factory()
|
$booking2 = Product::factory()
|
||||||
->withPrices(unit_amount: 150.00)
|
->withPrices(unit_amount: 15000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -82,11 +82,11 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_is_full_booking_is_false_when_mixed_products()
|
public function cart_is_full_booking_is_false_when_mixed_products()
|
||||||
{
|
{
|
||||||
$booking = Product::factory()
|
$booking = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$regular = Product::factory()
|
$regular = Product::factory()
|
||||||
->withPrices(unit_amount: 50.00)
|
->withPrices(unit_amount: 5000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -108,15 +108,15 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_booking_items_returns_correct_count()
|
public function cart_booking_items_returns_correct_count()
|
||||||
{
|
{
|
||||||
$booking1 = Product::factory()
|
$booking1 = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$booking2 = Product::factory()
|
$booking2 = Product::factory()
|
||||||
->withPrices(unit_amount: 150.00)
|
->withPrices(unit_amount: 15000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$regular = Product::factory()
|
$regular = Product::factory()
|
||||||
->withPrices(unit_amount: 50.00)
|
->withPrices(unit_amount: 5000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -131,7 +131,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_booking_items_returns_zero_when_no_bookings()
|
public function cart_booking_items_returns_zero_when_no_bookings()
|
||||||
{
|
{
|
||||||
$regular = Product::factory()
|
$regular = Product::factory()
|
||||||
->withPrices(unit_amount: 50.00)
|
->withPrices(unit_amount: 5000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -144,7 +144,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function price_id_is_automatically_assigned_when_adding_product_to_cart()
|
public function price_id_is_automatically_assigned_when_adding_product_to_cart()
|
||||||
{
|
{
|
||||||
$product = Product::factory()
|
$product = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create();
|
->create();
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -244,7 +244,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_item_is_ready_to_checkout_is_true_for_regular_products()
|
public function cart_item_is_ready_to_checkout_is_true_for_regular_products()
|
||||||
{
|
{
|
||||||
$product = Product::factory()
|
$product = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -257,7 +257,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_item_is_ready_to_checkout_is_false_for_booking_without_dates()
|
public function cart_item_is_ready_to_checkout_is_false_for_booking_without_dates()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -270,7 +270,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_item_is_ready_to_checkout_is_true_for_booking_with_valid_dates()
|
public function cart_item_is_ready_to_checkout_is_true_for_booking_with_valid_dates()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 10)
|
->withStocks(quantity: 10)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
|
|
@ -287,7 +287,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_item_is_ready_to_checkout_is_false_for_booking_with_invalid_date_range()
|
public function cart_item_is_ready_to_checkout_is_false_for_booking_with_invalid_date_range()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 10)
|
->withStocks(quantity: 10)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
|
|
@ -307,11 +307,11 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_is_ready_to_checkout_is_true_when_all_items_are_ready()
|
public function cart_is_ready_to_checkout_is_true_when_all_items_are_ready()
|
||||||
{
|
{
|
||||||
$product1 = Product::factory()
|
$product1 = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$product2 = Product::factory()
|
$product2 = Product::factory()
|
||||||
->withPrices(unit_amount: 150.00)
|
->withPrices(unit_amount: 15000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -325,11 +325,11 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_is_ready_to_checkout_is_false_when_at_least_one_item_not_ready()
|
public function cart_is_ready_to_checkout_is_false_when_at_least_one_item_not_ready()
|
||||||
{
|
{
|
||||||
$regularProduct = Product::factory()
|
$regularProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->create(['type' => ProductType::SIMPLE]);
|
->create(['type' => ProductType::SIMPLE]);
|
||||||
|
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 150.00)
|
->withPrices(unit_amount: 15000)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
@ -343,7 +343,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_allows_adding_items_without_dates_that_require_them()
|
public function cart_allows_adding_items_without_dates_that_require_them()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 10) // Has stock
|
->withStocks(quantity: 10) // Has stock
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
|
|
@ -362,7 +362,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function update_dates_allows_setting_any_dates()
|
public function update_dates_allows_setting_any_dates()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 10) // Has stock
|
->withStocks(quantity: 10) // Has stock
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
|
|
@ -386,7 +386,7 @@ class CartItemAttributesTest extends TestCase
|
||||||
public function cart_calculates_correctly_when_dates_are_adjusted()
|
public function cart_calculates_correctly_when_dates_are_adjusted()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 10)
|
->withStocks(quantity: 10)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
|
|
@ -397,23 +397,23 @@ class CartItemAttributesTest extends TestCase
|
||||||
$cartItem = $cart->addToCart($bookingProduct, quantity: 1, from: $from, until: $until);
|
$cartItem = $cart->addToCart($bookingProduct, quantity: 1, from: $from, until: $until);
|
||||||
|
|
||||||
// Initial price for 2 days
|
// Initial price for 2 days
|
||||||
$this->assertEquals(200.00, $cartItem->price);
|
$this->assertEquals(20000, $cartItem->price);
|
||||||
$this->assertEquals(200.00, $cartItem->subtotal);
|
$this->assertEquals(20000, $cartItem->subtotal);
|
||||||
|
|
||||||
// Adjust dates to 5 days
|
// Adjust dates to 5 days
|
||||||
$newUntil = Carbon::now()->addDays(6);
|
$newUntil = Carbon::now()->addDays(6);
|
||||||
$cartItem->updateDates($from, $newUntil);
|
$cartItem->updateDates($from, $newUntil);
|
||||||
|
|
||||||
// Price should be recalculated for 5 days
|
// Price should be recalculated for 5 days
|
||||||
$this->assertEquals(500.00, $cartItem->fresh()->price);
|
$this->assertEquals(50000, $cartItem->fresh()->price);
|
||||||
$this->assertEquals(500.00, $cartItem->fresh()->subtotal);
|
$this->assertEquals(50000, $cartItem->fresh()->subtotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function set_from_date_recalculates_pricing_when_both_dates_set()
|
public function set_from_date_recalculates_pricing_when_both_dates_set()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 10)
|
->withStocks(quantity: 10)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
|
|
@ -424,21 +424,21 @@ class CartItemAttributesTest extends TestCase
|
||||||
$cartItem = $cart->addToCart($bookingProduct, quantity: 1, from: $from, until: $until);
|
$cartItem = $cart->addToCart($bookingProduct, quantity: 1, from: $from, until: $until);
|
||||||
|
|
||||||
// Initial price for 3 days
|
// Initial price for 3 days
|
||||||
$this->assertEquals(300.00, $cartItem->price);
|
$this->assertEquals(30000, $cartItem->price);
|
||||||
|
|
||||||
// Adjust from date to make it span more days (move 1 day earlier)
|
// Adjust from date to make it span more days (move 1 day earlier)
|
||||||
$newFrom = $from->copy()->subDays(1);
|
$newFrom = $from->copy()->subDays(1);
|
||||||
$cartItem->setFromDate($newFrom);
|
$cartItem->setFromDate($newFrom);
|
||||||
|
|
||||||
// Price should be recalculated for 4 days
|
// Price should be recalculated for 4 days
|
||||||
$this->assertEquals(400.00, $cartItem->fresh()->price);
|
$this->assertEquals(40000, $cartItem->fresh()->price);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function set_until_date_recalculates_pricing_when_both_dates_set()
|
public function set_until_date_recalculates_pricing_when_both_dates_set()
|
||||||
{
|
{
|
||||||
$bookingProduct = Product::factory()
|
$bookingProduct = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 10)
|
->withStocks(quantity: 10)
|
||||||
->create(['type' => ProductType::BOOKING]);
|
->create(['type' => ProductType::BOOKING]);
|
||||||
|
|
||||||
|
|
@ -449,21 +449,21 @@ class CartItemAttributesTest extends TestCase
|
||||||
$cartItem = $cart->addToCart($bookingProduct, quantity: 1, from: $from, until: $until);
|
$cartItem = $cart->addToCart($bookingProduct, quantity: 1, from: $from, until: $until);
|
||||||
|
|
||||||
// Initial price for 2 days
|
// Initial price for 2 days
|
||||||
$this->assertEquals(200.00, $cartItem->price);
|
$this->assertEquals(20000, $cartItem->price);
|
||||||
|
|
||||||
// Adjust until date to make it 4 days
|
// Adjust until date to make it 4 days
|
||||||
$newUntil = Carbon::now()->addDays(5);
|
$newUntil = Carbon::now()->addDays(5);
|
||||||
$cartItem->setUntilDate($newUntil);
|
$cartItem->setUntilDate($newUntil);
|
||||||
|
|
||||||
// Price should be recalculated for 4 days
|
// Price should be recalculated for 4 days
|
||||||
$this->assertEquals(400.00, $cartItem->fresh()->price);
|
$this->assertEquals(40000, $cartItem->fresh()->price);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function is_ready_to_checkout_checks_stock_for_regular_products_with_stock_management()
|
public function is_ready_to_checkout_checks_stock_for_regular_products_with_stock_management()
|
||||||
{
|
{
|
||||||
$product = Product::factory()
|
$product = Product::factory()
|
||||||
->withPrices(unit_amount: 100.00)
|
->withPrices(unit_amount: 10000)
|
||||||
->withStocks(quantity: 5)
|
->withStocks(quantity: 5)
|
||||||
->create([
|
->create([
|
||||||
'type' => ProductType::SIMPLE,
|
'type' => ProductType::SIMPLE,
|
||||||
|
|
|
||||||
|
|
@ -129,11 +129,8 @@ class CartItemAvailabilityValidationTest extends TestCase
|
||||||
// - Remove allocation (product_id = null)
|
// - Remove allocation (product_id = null)
|
||||||
// - Set price to null (the real indicator of unavailability)
|
// - Set price to null (the real indicator of unavailability)
|
||||||
$item = $this->cart->items()->first();
|
$item = $this->cart->items()->first();
|
||||||
$meta = $item->getMeta();
|
|
||||||
unset($meta->allocated_single_item_name);
|
|
||||||
$item->update([
|
$item->update([
|
||||||
'product_id' => null,
|
'product_id' => null,
|
||||||
'meta' => json_encode($meta),
|
|
||||||
'price' => null,
|
'price' => null,
|
||||||
'subtotal' => null,
|
'subtotal' => null,
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_can_update_cart_item_quantity()
|
public function it_can_update_cart_item_quantity()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 1);
|
$cartItem = $cart->addToCart($price, quantity: 1);
|
||||||
|
|
@ -73,7 +73,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_can_remove_items_from_cart()
|
public function it_can_remove_items_from_cart()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 1);
|
$cartItem = $cart->addToCart($price, quantity: 1);
|
||||||
|
|
@ -89,8 +89,8 @@ class CartManagementTest extends TestCase
|
||||||
public function it_calculates_cart_total_correctly()
|
public function it_calculates_cart_total_correctly()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product1 = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product1 = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$product2 = Product::factory()->withPrices(unit_amount: 30.00)->create();
|
$product2 = Product::factory()->withPrices(unit_amount: 3000)->create();
|
||||||
|
|
||||||
$productPrice1 = $product1->defaultPrice()->first();
|
$productPrice1 = $product1->defaultPrice()->first();
|
||||||
$productPrice2 = $product2->defaultPrice()->first();
|
$productPrice2 = $product2->defaultPrice()->first();
|
||||||
|
|
@ -100,15 +100,15 @@ class CartManagementTest extends TestCase
|
||||||
|
|
||||||
$total = $cart->fresh()->getTotal();
|
$total = $cart->fresh()->getTotal();
|
||||||
|
|
||||||
$this->assertEquals(130.00, $total); // (50 * 2) + (30 * 1)
|
$this->assertEquals(13000, $total); // (5000 * 2) + (3000 * 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_calculates_total_items_correctly()
|
public function it_calculates_total_items_correctly()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product1 = Product::factory()->withPrices(unit_amount: 10.00)->create();
|
$product1 = Product::factory()->withPrices(unit_amount: 1000)->create();
|
||||||
$product2 = Product::factory()->withPrices(unit_amount: 20.00)->create();
|
$product2 = Product::factory()->withPrices(unit_amount: 2000)->create();
|
||||||
|
|
||||||
$product1Price = $product1->defaultPrice()->first();
|
$product1Price = $product1->defaultPrice()->first();
|
||||||
$product2Price = $product2->defaultPrice()->first();
|
$product2Price = $product2->defaultPrice()->first();
|
||||||
|
|
@ -202,7 +202,7 @@ class CartManagementTest extends TestCase
|
||||||
public function cart_items_have_correct_relationships()
|
public function cart_items_have_correct_relationships()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 45.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 4500)->create();
|
||||||
$productPrice = $product->defaultPrice()->first();
|
$productPrice = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($productPrice, quantity: 1);
|
$cartItem = $cart->addToCart($productPrice, quantity: 1);
|
||||||
|
|
@ -215,19 +215,19 @@ class CartManagementTest extends TestCase
|
||||||
public function it_calculates_cart_item_subtotal()
|
public function it_calculates_cart_item_subtotal()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create();
|
||||||
$productPrice = $product->defaultPrice()->first();
|
$productPrice = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($productPrice, quantity: 4);
|
$cartItem = $cart->addToCart($productPrice, quantity: 4);
|
||||||
|
|
||||||
$this->assertEquals(100.00, $cartItem->getSubtotal()); // 25 * 4
|
$this->assertEquals(10000, $cartItem->getSubtotal()); // 2500 * 4
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_store_cart_item_attributes()
|
public function it_can_store_cart_item_attributes()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$productPrice = $product->defaultPrice()->first();
|
$productPrice = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart(
|
$cartItem = $cart->addToCart(
|
||||||
|
|
@ -247,7 +247,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_can_have_multiple_items_of_same_product_with_different_attributes()
|
public function it_can_have_multiple_items_of_same_product_with_different_attributes()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 30.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 3000)->create();
|
||||||
$productPrice = $product->defaultPrice()->first();
|
$productPrice = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cart->addToCart(
|
$cart->addToCart(
|
||||||
|
|
@ -270,7 +270,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_deletes_cart_items_when_cart_is_deleted()
|
public function it_deletes_cart_items_when_cart_is_deleted()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 75.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 7500)->create();
|
||||||
$productPrice = $product->defaultPrice()->first();
|
$productPrice = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart(
|
$cartItem = $cart->addToCart(
|
||||||
|
|
@ -317,14 +317,14 @@ class CartManagementTest extends TestCase
|
||||||
$cartWithProduct = Cart::factory()
|
$cartWithProduct = Cart::factory()
|
||||||
->withNewProductInCart(
|
->withNewProductInCart(
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
unit_amount: 150.00,
|
unit_amount: 15000,
|
||||||
sale_unit_amount: 120.00,
|
sale_unit_amount: 12000,
|
||||||
stocks: 10
|
stocks: 10
|
||||||
)
|
)
|
||||||
->withNewProductInCart(
|
->withNewProductInCart(
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
unit_amount: 150.00,
|
unit_amount: 15000,
|
||||||
sale_unit_amount: 120.00,
|
sale_unit_amount: 12000,
|
||||||
stocks: 10,
|
stocks: 10,
|
||||||
sale_start: now()->subDay(),
|
sale_start: now()->subDay(),
|
||||||
sale_end: now()->addDay()
|
sale_end: now()->addDay()
|
||||||
|
|
@ -333,14 +333,14 @@ class CartManagementTest extends TestCase
|
||||||
|
|
||||||
$this->assertCount(2, $cartWithProduct->items);
|
$this->assertCount(2, $cartWithProduct->items);
|
||||||
$this->assertEquals(4, $cartWithProduct->getTotalItems());
|
$this->assertEquals(4, $cartWithProduct->getTotalItems());
|
||||||
$this->assertEquals((150.00 * 2) + (120 * 2), $cartWithProduct->getTotal());
|
$this->assertEquals((15000 * 2) + (12000 * 2), $cartWithProduct->getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_remove_entire_cart_item()
|
public function it_can_remove_entire_cart_item()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 2);
|
$cartItem = $cart->addToCart($price, quantity: 2);
|
||||||
|
|
@ -356,7 +356,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_can_decrease_cart_item_quantity()
|
public function it_can_decrease_cart_item_quantity()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 75.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 7500)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 5);
|
$cartItem = $cart->addToCart($price, quantity: 5);
|
||||||
|
|
@ -366,14 +366,14 @@ class CartManagementTest extends TestCase
|
||||||
|
|
||||||
$updatedItem = $cart->items->first();
|
$updatedItem = $cart->items->first();
|
||||||
$this->assertEquals(3, $updatedItem->quantity);
|
$this->assertEquals(3, $updatedItem->quantity);
|
||||||
$this->assertEquals(75.00 * 3, $updatedItem->subtotal);
|
$this->assertEquals(7500 * 3, $updatedItem->subtotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_updates_subtotal_correctly_when_decreasing_quantity()
|
public function it_updates_subtotal_correctly_when_decreasing_quantity()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cart->addToCart($price, quantity: 4);
|
$cart->addToCart($price, quantity: 4);
|
||||||
|
|
@ -382,14 +382,14 @@ class CartManagementTest extends TestCase
|
||||||
|
|
||||||
$cartItem = $cart->items->first();
|
$cartItem = $cart->items->first();
|
||||||
$this->assertEquals(3, $cartItem->quantity);
|
$this->assertEquals(3, $cartItem->quantity);
|
||||||
$this->assertEquals(300.00, $cartItem->subtotal);
|
$this->assertEquals(30000, $cartItem->subtotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_respects_parameters_when_removing_from_cart()
|
public function it_respects_parameters_when_removing_from_cart()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
// Add same product with different parameters
|
// Add same product with different parameters
|
||||||
|
|
@ -419,7 +419,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_decreases_only_matching_parameters_when_removing()
|
public function it_decreases_only_matching_parameters_when_removing()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cart->addToCart(
|
$cart->addToCart(
|
||||||
|
|
@ -439,7 +439,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_returns_cart_item_when_quantity_is_decreased()
|
public function it_returns_cart_item_when_quantity_is_decreased()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cart->addToCart($price, quantity: 5);
|
$cart->addToCart($price, quantity: 5);
|
||||||
|
|
@ -454,7 +454,7 @@ class CartManagementTest extends TestCase
|
||||||
public function it_handles_removing_nonexistent_item_gracefully()
|
public function it_handles_removing_nonexistent_item_gracefully()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$result = $cart->removeFromCart($price, quantity: 1);
|
$result = $cart->removeFromCart($price, quantity: 1);
|
||||||
|
|
@ -468,23 +468,23 @@ class CartManagementTest extends TestCase
|
||||||
public function it_updates_cart_total_after_removing_items()
|
public function it_updates_cart_total_after_removing_items()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cart->addToCart($price, quantity: 5);
|
$cart->addToCart($price, quantity: 5);
|
||||||
$this->assertEquals(250.00, $cart->getTotal());
|
$this->assertEquals(25000, $cart->getTotal());
|
||||||
|
|
||||||
$cart->removeFromCart($price, quantity: 2);
|
$cart->removeFromCart($price, quantity: 2);
|
||||||
|
|
||||||
$this->assertEquals(150.00, $cart->refresh()->getTotal());
|
$this->assertEquals(15000, $cart->refresh()->getTotal());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_remove_from_cart_with_multiple_items()
|
public function it_can_remove_from_cart_with_multiple_items()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product1 = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product1 = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$product2 = Product::factory()->withPrices(unit_amount: 75.00)->create();
|
$product2 = Product::factory()->withPrices(unit_amount: 7500)->create();
|
||||||
|
|
||||||
$price1 = $product1->defaultPrice()->first();
|
$price1 = $product1->defaultPrice()->first();
|
||||||
$price2 = $product2->defaultPrice()->first();
|
$price2 = $product2->defaultPrice()->first();
|
||||||
|
|
@ -497,6 +497,6 @@ class CartManagementTest extends TestCase
|
||||||
|
|
||||||
$this->assertCount(1, $cart->refresh()->items);
|
$this->assertCount(1, $cart->refresh()->items);
|
||||||
$this->assertEquals($price2->id, $cart->items->first()->purchasable_id);
|
$this->assertEquals($price2->id, $cart->items->first()->purchasable_id);
|
||||||
$this->assertEquals(225.00, $cart->getTotal());
|
$this->assertEquals(22500, $cart->getTotal());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -222,9 +222,11 @@ class CartServiceBookingTest extends TestCase
|
||||||
|
|
||||||
$cartItem = Cart::addBooking($this->bookingProduct, 2, $from, $until);
|
$cartItem = Cart::addBooking($this->bookingProduct, 2, $from, $until);
|
||||||
|
|
||||||
// Price should be: price_per_day (10000 cents = 100 dollars) × days (3) = 30000 cents per unit
|
// withPrices(1, 10000) stores 10000 cents
|
||||||
|
// Price should be: price_per_day (10000 cents) × days (3) = 30000 cents per unit
|
||||||
// Total should be: 30000 × quantity (2) = 60000 cents
|
// Total should be: 30000 × quantity (2) = 60000 cents
|
||||||
$expectedPricePerUnit = 10000 * $days; // 30000 cents
|
$pricePerDay = 10000; // 100.00 euros = 10000 cents
|
||||||
|
$expectedPricePerUnit = $pricePerDay * $days; // 30000 cents
|
||||||
$expectedTotal = $expectedPricePerUnit * 2; // 60000 cents
|
$expectedTotal = $expectedPricePerUnit * 2; // 60000 cents
|
||||||
|
|
||||||
$this->assertEquals($expectedPricePerUnit, $cartItem->price);
|
$this->assertEquals($expectedPricePerUnit, $cartItem->price);
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ class GuestCartTest extends TestCase
|
||||||
|
|
||||||
$total = Cart::total($guestCart);
|
$total = Cart::total($guestCart);
|
||||||
|
|
||||||
$this->assertEquals(250.00, $total);
|
$this->assertEquals(250, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -167,8 +167,8 @@ class GuestCartTest extends TestCase
|
||||||
Cart::add($product, quantity: 1);
|
Cart::add($product, quantity: 1);
|
||||||
|
|
||||||
// Verify they're different
|
// Verify they're different
|
||||||
$this->assertEquals(100.00, Cart::total($guestCart));
|
$this->assertEquals(100, Cart::total($guestCart));
|
||||||
$this->assertEquals(100.00, Cart::total()); // Current user's cart
|
$this->assertEquals(100, Cart::total()); // Current user's cart
|
||||||
|
|
||||||
$guestCartItems = Cart::items($guestCart);
|
$guestCartItems = Cart::items($guestCart);
|
||||||
$userCartItems = Cart::items();
|
$userCartItems = Cart::items();
|
||||||
|
|
@ -195,8 +195,8 @@ class GuestCartTest extends TestCase
|
||||||
$userCart->addToCart($product, quantity: 2);
|
$userCart->addToCart($product, quantity: 2);
|
||||||
|
|
||||||
// Original guest cart should still exist and be separate
|
// Original guest cart should still exist and be separate
|
||||||
$this->assertEquals(200.00, Cart::total($guestCart));
|
$this->assertEquals(200, Cart::total($guestCart));
|
||||||
$this->assertEquals(200.00, Cart::total($userCart));
|
$this->assertEquals(200, Cart::total($userCart));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -226,8 +226,8 @@ class GuestCartTest extends TestCase
|
||||||
$guestCart2->addToCart($product, quantity: 3); // 300
|
$guestCart2->addToCart($product, quantity: 3); // 300
|
||||||
|
|
||||||
$this->assertNotEquals($guestCart1->id, $guestCart2->id);
|
$this->assertNotEquals($guestCart1->id, $guestCart2->id);
|
||||||
$this->assertEquals(100.00, Cart::total($guestCart1));
|
$this->assertEquals(100, Cart::total($guestCart1));
|
||||||
$this->assertEquals(300.00, Cart::total($guestCart2));
|
$this->assertEquals(300, Cart::total($guestCart2));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -237,12 +237,12 @@ class GuestCartTest extends TestCase
|
||||||
$product = Product::factory()->withStocks(50)->withPrices(1, 50)->create();
|
$product = Product::factory()->withStocks(50)->withPrices(1, 50)->create();
|
||||||
|
|
||||||
$cartItem = $guestCart->addToCart($product, quantity: 2);
|
$cartItem = $guestCart->addToCart($product, quantity: 2);
|
||||||
$this->assertEquals(100.00, $cartItem->subtotal);
|
$this->assertEquals(100, $cartItem->subtotal);
|
||||||
|
|
||||||
$updated = Cart::update($cartItem, quantity: 5);
|
$updated = Cart::update($cartItem, quantity: 5);
|
||||||
|
|
||||||
$this->assertEquals(5, $updated->quantity);
|
$this->assertEquals(5, $updated->quantity);
|
||||||
$this->assertEquals(250.00, $updated->subtotal);
|
$this->assertEquals(250, $updated->subtotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function checkout_creates_order_from_cart()
|
public function checkout_creates_order_from_cart()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_has_correct_cart_id()
|
public function order_has_correct_cart_id()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_has_correct_customer_info()
|
public function order_has_correct_customer_info()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -81,7 +81,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
$cart = Cart::factory()->forCustomer($user)->create([
|
$cart = Cart::factory()->forCustomer($user)->create([
|
||||||
'currency' => 'EUR',
|
'currency' => 'EUR',
|
||||||
]);
|
]);
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -97,20 +97,20 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_has_correct_total_amount()
|
public function order_has_correct_total_amount()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product1 = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product1 = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
$product2 = Product::factory()->withPrices(unit_amount: 30.00)->create([
|
$product2 = Product::factory()->withPrices(unit_amount: 3000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user->addToCart($product1, quantity: 2); // 100.00
|
$user->addToCart($product1, quantity: 2); // 10000 cents
|
||||||
$user->addToCart($product2, quantity: 3); // 90.00
|
$user->addToCart($product2, quantity: 3); // 9000 cents
|
||||||
|
|
||||||
$cart = $user->checkoutCart();
|
$cart = $user->checkoutCart();
|
||||||
$order = $cart->order;
|
$order = $cart->order;
|
||||||
|
|
||||||
// Total should be 190.00 (19000 cents)
|
// Total should be 19000 cents
|
||||||
$this->assertEquals(19000, $order->amount_total);
|
$this->assertEquals(19000, $order->amount_total);
|
||||||
$this->assertEquals(19000, $order->amount_subtotal);
|
$this->assertEquals(19000, $order->amount_subtotal);
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +119,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_starts_with_pending_status()
|
public function order_starts_with_pending_status()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_starts_with_zero_paid_amount()
|
public function order_starts_with_zero_paid_amount()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_has_unique_order_number()
|
public function order_has_unique_order_number()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -173,7 +173,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_creation_adds_system_note()
|
public function order_creation_adds_system_note()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -198,10 +198,10 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_has_purchases_through_cart()
|
public function order_has_purchases_through_cart()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product1 = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product1 = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
$product2 = Product::factory()->withPrices(unit_amount: 30.00)->create([
|
$product2 = Product::factory()->withPrices(unit_amount: 3000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -218,7 +218,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_purchases_have_correct_status()
|
public function order_purchases_have_correct_status()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -238,7 +238,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_payment_updates_status()
|
public function order_payment_updates_status()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -260,7 +260,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_payment_updates_purchase_status()
|
public function order_payment_updates_purchase_status()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -280,7 +280,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_partial_payment_does_not_complete_order()
|
public function order_partial_payment_does_not_complete_order()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -306,7 +306,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_can_be_processed_after_payment()
|
public function order_can_be_processed_after_payment()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -324,7 +324,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_can_be_shipped_with_tracking()
|
public function order_can_be_shipped_with_tracking()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -346,7 +346,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_can_be_completed()
|
public function order_can_be_completed()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
'virtual' => true, // Virtual product
|
'virtual' => true, // Virtual product
|
||||||
]);
|
]);
|
||||||
|
|
@ -372,7 +372,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_can_be_cancelled()
|
public function order_can_be_cancelled()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -403,7 +403,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_can_be_refunded()
|
public function order_can_be_refunded()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -425,7 +425,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_partial_refund_does_not_change_status()
|
public function order_partial_refund_does_not_change_status()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -450,7 +450,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_logs_status_changes()
|
public function order_logs_status_changes()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -473,7 +473,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function order_logs_payment_notes()
|
public function order_logs_payment_notes()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -499,7 +499,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function cart_status_is_converted_after_checkout()
|
public function cart_status_is_converted_after_checkout()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -519,7 +519,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
$products = Product::factory()
|
$products = Product::factory()
|
||||||
->withPrices(unit_amount: 25.00)
|
->withPrices(unit_amount: 2500)
|
||||||
->count(5)
|
->count(5)
|
||||||
->create(['manage_stock' => false]);
|
->create(['manage_stock' => false]);
|
||||||
|
|
||||||
|
|
@ -543,7 +543,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
{
|
{
|
||||||
$user1 = User::factory()->create();
|
$user1 = User::factory()->create();
|
||||||
$user2 = User::factory()->create();
|
$user2 = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -567,7 +567,7 @@ class OrderCheckoutFlowTest extends TestCase
|
||||||
public function can_find_paid_orders()
|
public function can_find_paid_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class PurchaseFlowTest extends TestCase
|
||||||
public function user_can_purchase_a_product_directly()
|
public function user_can_purchase_a_product_directly()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 49.99)->create([
|
$product = Product::factory()->withPrices(unit_amount: 4999)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -141,7 +141,7 @@ class PurchaseFlowTest extends TestCase
|
||||||
|
|
||||||
$total = $user->getCartTotal();
|
$total = $user->getCartTotal();
|
||||||
|
|
||||||
$this->assertEquals(140.00, $total);
|
$this->assertEquals(140, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -336,6 +336,6 @@ class PurchaseFlowTest extends TestCase
|
||||||
|
|
||||||
$cart = $user->checkoutCart();
|
$cart = $user->checkoutCart();
|
||||||
|
|
||||||
$this->assertEquals(170.00, $cart->getTotal());
|
$this->assertEquals(170, $cart->getTotal());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ class HasShoppingCapabilitiesTest extends TestCase
|
||||||
|
|
||||||
$total = $user->getCartTotal();
|
$total = $user->getCartTotal();
|
||||||
|
|
||||||
$this->assertEquals(250.00, $total);
|
$this->assertEquals(250, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
|
||||||
|
|
@ -140,9 +140,10 @@ class PoolProductPriceIdTest extends TestCase
|
||||||
$this->assertNotNull($cartItem->product_id);
|
$this->assertNotNull($cartItem->product_id);
|
||||||
$this->assertEquals($this->singleItem1->id, $cartItem->product_id);
|
$this->assertEquals($this->singleItem1->id, $cartItem->product_id);
|
||||||
|
|
||||||
// Meta should still have the name for display purposes
|
// Verify we can get the actual product through the relationship
|
||||||
$meta = $cartItem->getMeta();
|
$allocatedProduct = $cartItem->product;
|
||||||
$this->assertEquals($this->singleItem1->name, $meta->allocated_single_item_name);
|
$this->assertNotNull($allocatedProduct);
|
||||||
|
$this->assertEquals($this->singleItem1->name, $allocatedProduct->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
|
||||||
|
|
@ -267,9 +267,9 @@ class PoolProductionBugTest extends TestCase
|
||||||
// The 3x 5000 items might be merged since they have the same price_id (pool price)
|
// The 3x 5000 items might be merged since they have the same price_id (pool price)
|
||||||
// But different single items should NOT be merged
|
// But different single items should NOT be merged
|
||||||
|
|
||||||
// Get all allocated single item names
|
// Get all allocated single item names via product relationship
|
||||||
$allocatedNames = $items->map(fn($item) => [
|
$allocatedNames = $items->map(fn($item) => [
|
||||||
'name' => $item->getMeta()->allocated_single_item_name ?? 'unknown',
|
'name' => $item->product?->name ?? 'unknown',
|
||||||
'price' => $item->price,
|
'price' => $item->price,
|
||||||
'quantity' => $item->quantity,
|
'quantity' => $item->quantity,
|
||||||
])->toArray();
|
])->toArray();
|
||||||
|
|
@ -787,7 +787,7 @@ class PoolProductionBugTest extends TestCase
|
||||||
'manage_stock' => true,
|
'manage_stock' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// single_1: 3 stock @ 1000/day
|
// single_1: 3 stock @ 1000/day (10.00 euros)
|
||||||
$single_1 = Product::factory()
|
$single_1 = Product::factory()
|
||||||
->withStocks(3)
|
->withStocks(3)
|
||||||
->withPrices(1, 1000)
|
->withPrices(1, 1000)
|
||||||
|
|
@ -797,7 +797,7 @@ class PoolProductionBugTest extends TestCase
|
||||||
'name' => 'Single1-Cheap',
|
'name' => 'Single1-Cheap',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// single_2: 1 stock @ 10001/day
|
// single_2: 1 stock @ 10001/day (100.01 euros)
|
||||||
$single_2 = Product::factory()
|
$single_2 = Product::factory()
|
||||||
->withStocks(1)
|
->withStocks(1)
|
||||||
->withPrices(1, 10001)
|
->withPrices(1, 10001)
|
||||||
|
|
@ -807,7 +807,7 @@ class PoolProductionBugTest extends TestCase
|
||||||
'name' => 'Single2-Medium',
|
'name' => 'Single2-Medium',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// single_3: 1 stock @ 10002/day
|
// single_3: 1 stock @ 10002/day (100.02 euros)
|
||||||
$single_3 = Product::factory()
|
$single_3 = Product::factory()
|
||||||
->withStocks(1)
|
->withStocks(1)
|
||||||
->withPrices(1, 10002)
|
->withPrices(1, 10002)
|
||||||
|
|
@ -833,13 +833,12 @@ class PoolProductionBugTest extends TestCase
|
||||||
$items = [];
|
$items = [];
|
||||||
for ($i = 1; $i <= 4; $i++) {
|
for ($i = 1; $i <= 4; $i++) {
|
||||||
$item = $cart->addToCart($pool, 1);
|
$item = $cart->addToCart($pool, 1);
|
||||||
$meta = $item->getMeta();
|
|
||||||
$items[$i] = [
|
$items[$i] = [
|
||||||
'id' => $item->id,
|
'id' => $item->id,
|
||||||
'quantity' => $item->quantity,
|
'quantity' => $item->quantity,
|
||||||
'price' => $item->price,
|
'price' => $item->price,
|
||||||
'allocated_id' => $item->product_id,
|
'allocated_id' => $item->product_id,
|
||||||
'allocated_name' => $meta->allocated_single_item_name ?? 'none',
|
'allocated_name' => $item->product?->name ?? 'none',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -850,13 +849,12 @@ class PoolProductionBugTest extends TestCase
|
||||||
$cartItemDetails = [];
|
$cartItemDetails = [];
|
||||||
$totalQuantity = 0;
|
$totalQuantity = 0;
|
||||||
foreach ($cartItems as $item) {
|
foreach ($cartItems as $item) {
|
||||||
$meta = $item->getMeta();
|
|
||||||
$cartItemDetails[] = [
|
$cartItemDetails[] = [
|
||||||
'id' => $item->id,
|
'id' => $item->id,
|
||||||
'quantity' => $item->quantity,
|
'quantity' => $item->quantity,
|
||||||
'price' => $item->price,
|
'price' => $item->price,
|
||||||
'allocated_id' => $item->product_id,
|
'allocated_id' => $item->product_id,
|
||||||
'allocated_name' => $meta->allocated_single_item_name ?? 'none',
|
'allocated_name' => $item->product?->name ?? 'none',
|
||||||
];
|
];
|
||||||
$totalQuantity += $item->quantity;
|
$totalQuantity += $item->quantity;
|
||||||
}
|
}
|
||||||
|
|
@ -1044,7 +1042,7 @@ class PoolProductionBugTest extends TestCase
|
||||||
'manage_stock' => true,
|
'manage_stock' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// single_1: 2 stock @ 1000/day
|
// single_1: 2 stock @ 1000/day (10.00 euros)
|
||||||
$single_1 = Product::factory()
|
$single_1 = Product::factory()
|
||||||
->withStocks(2)
|
->withStocks(2)
|
||||||
->withPrices(1, 1000)
|
->withPrices(1, 1000)
|
||||||
|
|
@ -1054,7 +1052,7 @@ class PoolProductionBugTest extends TestCase
|
||||||
'name' => 'Single1',
|
'name' => 'Single1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// single_2: 1 stock @ 2000/day
|
// single_2: 1 stock @ 2000/day (20.00 euros)
|
||||||
$single_2 = Product::factory()
|
$single_2 = Product::factory()
|
||||||
->withStocks(1)
|
->withStocks(1)
|
||||||
->withPrices(1, 2000)
|
->withPrices(1, 2000)
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,8 @@ class PoolSmartAllocationTest extends TestCase
|
||||||
$cart->addToCart($this->pool, 3, [], $claimFrom, $claimUntil);
|
$cart->addToCart($this->pool, 3, [], $claimFrom, $claimUntil);
|
||||||
|
|
||||||
$initialItems = $cart->fresh()->items->sortBy('price')->values();
|
$initialItems = $cart->fresh()->items->sortBy('price')->values();
|
||||||
$initialAllocations = $initialItems->map(fn($i) => $i->getMeta()->allocated_single_item_name)->toArray();
|
// Use product relationship to get allocated single item names
|
||||||
|
$initialAllocations = $initialItems->map(fn($i) => $i->product?->name)->toArray();
|
||||||
|
|
||||||
// Should have expensive items allocated
|
// Should have expensive items allocated
|
||||||
$this->assertContains('Spot 4 - 40000', $initialAllocations);
|
$this->assertContains('Spot 4 - 40000', $initialAllocations);
|
||||||
|
|
@ -355,7 +356,8 @@ class PoolSmartAllocationTest extends TestCase
|
||||||
$cart->setDates($newFrom, $newUntil);
|
$cart->setDates($newFrom, $newUntil);
|
||||||
|
|
||||||
$newItems = $cart->fresh()->items->sortBy('price')->values();
|
$newItems = $cart->fresh()->items->sortBy('price')->values();
|
||||||
$newAllocations = $newItems->map(fn($i) => $i->getMeta()->allocated_single_item_name)->toArray();
|
// Use product relationship to get allocated single item names
|
||||||
|
$newAllocations = $newItems->map(fn($i) => $i->product?->name)->toArray();
|
||||||
|
|
||||||
// Should now have cheap items allocated
|
// Should now have cheap items allocated
|
||||||
$this->assertContains('Spot 1 - 10000', $newAllocations);
|
$this->assertContains('Spot 1 - 10000', $newAllocations);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class ProductManagementTest extends TestCase
|
||||||
public function it_can_create_a_product()
|
public function it_can_create_a_product()
|
||||||
{
|
{
|
||||||
$product = Product::factory()
|
$product = Product::factory()
|
||||||
->withPrices(1, 99.99)
|
->withPrices(1, 9999)
|
||||||
->create([
|
->create([
|
||||||
'slug' => 'test-product',
|
'slug' => 'test-product',
|
||||||
'type' => 'simple',
|
'type' => 'simple',
|
||||||
|
|
@ -33,7 +33,8 @@ class ProductManagementTest extends TestCase
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertCount(1, $product->prices);
|
$this->assertCount(1, $product->prices);
|
||||||
$this->assertEquals(99.99, $product->prices->first()->unit_amount);
|
// Factory converts 99.99 euros to 9999 cents
|
||||||
|
$this->assertEquals(9999, $product->prices->first()->unit_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,12 @@ class ProductScopeTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_filter_by_price_range()
|
public function it_can_filter_by_price_range()
|
||||||
{
|
{
|
||||||
$product1 = Product::factory()->withPrices(1, 50)->create();
|
$product1 = Product::factory()->withPrices(1, 5000)->create();
|
||||||
$product2 = Product::factory()->withPrices(1, 100)->create();
|
$product2 = Product::factory()->withPrices(1, 10000)->create();
|
||||||
$product3 = Product::factory()->withPrices(1, 150)->create();
|
$product3 = Product::factory()->withPrices(1, 15000)->create();
|
||||||
|
|
||||||
$inRange = Product::priceRange(75, 125)->get();
|
// Filter in cents: 7500-12500 cents = $75-$125
|
||||||
|
$inRange = Product::priceRange(7500, 12500)->get();
|
||||||
|
|
||||||
$this->assertCount(1, $inRange);
|
$this->assertCount(1, $inRange);
|
||||||
$this->assertTrue($inRange->contains($product2));
|
$this->assertTrue($inRange->contains($product2));
|
||||||
|
|
@ -74,11 +75,12 @@ class ProductScopeTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_filter_by_minimum_price_only()
|
public function it_can_filter_by_minimum_price_only()
|
||||||
{
|
{
|
||||||
$product1 = Product::factory()->withPrices(1, 50)->create();
|
$product1 = Product::factory()->withPrices(1, 5000)->create();
|
||||||
$product2 = Product::factory()->withPrices(1, 100)->create();
|
$product2 = Product::factory()->withPrices(1, 10000)->create();
|
||||||
$product3 = Product::factory()->withPrices(1, 150)->create();
|
$product3 = Product::factory()->withPrices(1, 15000)->create();
|
||||||
|
|
||||||
$minPrice = Product::priceRange(100)->get();
|
// Filter minimum in cents: 10000 cents = $100
|
||||||
|
$minPrice = Product::priceRange(10000)->get();
|
||||||
|
|
||||||
$this->assertCount(2, $minPrice);
|
$this->assertCount(2, $minPrice);
|
||||||
$this->assertTrue($minPrice->contains($product2));
|
$this->assertTrue($minPrice->contains($product2));
|
||||||
|
|
@ -88,11 +90,12 @@ class ProductScopeTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_filter_by_maximum_price_only()
|
public function it_can_filter_by_maximum_price_only()
|
||||||
{
|
{
|
||||||
$product1 = Product::factory()->withPrices(1, 50)->create();
|
$product1 = Product::factory()->withPrices(1, 5000)->create();
|
||||||
$product2 = Product::factory()->withPrices(1, 100)->create();
|
$product2 = Product::factory()->withPrices(1, 10000)->create();
|
||||||
$product3 = Product::factory()->withPrices(1, 150)->create();
|
$product3 = Product::factory()->withPrices(1, 15000)->create();
|
||||||
|
|
||||||
$maxPrice = Product::priceRange(null, 100)->get();
|
// Filter maximum in cents: 10000 cents = $100
|
||||||
|
$maxPrice = Product::priceRange(null, 10000)->get();
|
||||||
|
|
||||||
$this->assertCount(2, $maxPrice);
|
$this->assertCount(2, $maxPrice);
|
||||||
$this->assertTrue($maxPrice->contains($product1));
|
$this->assertTrue($maxPrice->contains($product1));
|
||||||
|
|
@ -102,9 +105,9 @@ class ProductScopeTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_order_products_by_price_ascending()
|
public function it_can_order_products_by_price_ascending()
|
||||||
{
|
{
|
||||||
$product1 = Product::factory()->withPrices(1, 150)->create(['name' => 'Expensive']);
|
$product1 = Product::factory()->withPrices(1, 15000)->create(['name' => 'Expensive']);
|
||||||
$product2 = Product::factory()->withPrices(1, 50)->create(['name' => 'Cheap']);
|
$product2 = Product::factory()->withPrices(1, 5000)->create(['name' => 'Cheap']);
|
||||||
$product3 = Product::factory()->withPrices(1, 100)->create(['name' => 'Medium']);
|
$product3 = Product::factory()->withPrices(1, 10000)->create(['name' => 'Medium']);
|
||||||
|
|
||||||
$ordered = Product::orderByPrice('asc')->get();
|
$ordered = Product::orderByPrice('asc')->get();
|
||||||
|
|
||||||
|
|
@ -115,9 +118,9 @@ class ProductScopeTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_order_products_by_price_descending()
|
public function it_can_order_products_by_price_descending()
|
||||||
{
|
{
|
||||||
$product1 = Product::factory()->withPrices(1, 150)->create(['name' => 'Expensive']);
|
$product1 = Product::factory()->withPrices(1, 15000)->create(['name' => 'Expensive']);
|
||||||
$product2 = Product::factory()->withPrices(1, 50)->create(['name' => 'Cheap']);
|
$product2 = Product::factory()->withPrices(1, 5000)->create(['name' => 'Cheap']);
|
||||||
$product3 = Product::factory()->withPrices(1, 100)->create(['name' => 'Medium']);
|
$product3 = Product::factory()->withPrices(1, 10000)->create(['name' => 'Medium']);
|
||||||
|
|
||||||
$ordered = Product::orderByPrice('desc')->get();
|
$ordered = Product::orderByPrice('desc')->get();
|
||||||
|
|
||||||
|
|
@ -128,12 +131,13 @@ class ProductScopeTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_can_combine_price_range_and_order_by_price()
|
public function it_can_combine_price_range_and_order_by_price()
|
||||||
{
|
{
|
||||||
$product1 = Product::factory()->withPrices(1, 50)->create();
|
$product1 = Product::factory()->withPrices(1, 5000)->create();
|
||||||
$product2 = Product::factory()->withPrices(1, 100)->create();
|
$product2 = Product::factory()->withPrices(1, 10000)->create();
|
||||||
$product3 = Product::factory()->withPrices(1, 150)->create();
|
$product3 = Product::factory()->withPrices(1, 15000)->create();
|
||||||
$product4 = Product::factory()->withPrices(1, 200)->create();
|
$product4 = Product::factory()->withPrices(1, 20000)->create();
|
||||||
|
|
||||||
$filtered = Product::priceRange(75, 175)->orderByPrice('asc')->get();
|
// Filter in cents: 7500-17500 cents = $75-$175
|
||||||
|
$filtered = Product::priceRange(7500, 17500)->orderByPrice('asc')->get();
|
||||||
|
|
||||||
$this->assertCount(2, $filtered);
|
$this->assertCount(2, $filtered);
|
||||||
$this->assertEquals($product2->id, $filtered->first()->id);
|
$this->assertEquals($product2->id, $filtered->first()->id);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class CartItemTest extends TestCase
|
||||||
public function cart_item_stores_prices_as_integers_in_cents()
|
public function cart_item_stores_prices_as_integers_in_cents()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 1550)->create(); // $15.50 in cents
|
$product = Product::factory()->withPrices(unit_amount: 1550)->create(); // 1550 cents
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 2);
|
$cartItem = $cart->addToCart($price, quantity: 2);
|
||||||
|
|
@ -42,7 +42,7 @@ class CartItemTest extends TestCase
|
||||||
public function cart_item_unit_amount_represents_base_price_per_day()
|
public function cart_item_unit_amount_represents_base_price_per_day()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 5000)->create(); // $50.00 per day
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create(); // 5000 cents per day
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 1);
|
$cartItem = $cart->addToCart($price, quantity: 1);
|
||||||
|
|
@ -56,7 +56,7 @@ class CartItemTest extends TestCase
|
||||||
public function cart_item_calculates_price_correctly_for_booking_timespan()
|
public function cart_item_calculates_price_correctly_for_booking_timespan()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 2000)->create(['type' => ProductType::BOOKING]); // $20.00 per day
|
$product = Product::factory()->withPrices(unit_amount: 2000)->create(['type' => ProductType::BOOKING]); // 2000 cents per day
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$from = Carbon::parse('2025-01-01 00:00:00');
|
$from = Carbon::parse('2025-01-01 00:00:00');
|
||||||
|
|
@ -78,7 +78,7 @@ class CartItemTest extends TestCase
|
||||||
public function cart_item_calculates_price_with_partial_days()
|
public function cart_item_calculates_price_with_partial_days()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 4800)->create(['type' => ProductType::BOOKING]); // $48.00 per day
|
$product = Product::factory()->withPrices(unit_amount: 4800)->create(['type' => ProductType::BOOKING]); // 4800 cents per day
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
// 12 hours = 0.5 days
|
// 12 hours = 0.5 days
|
||||||
|
|
@ -112,7 +112,7 @@ class CartItemTest extends TestCase
|
||||||
public function cart_item_updates_prices_when_dates_change()
|
public function cart_item_updates_prices_when_dates_change()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 3000)->withStocks(10)->create(['type' => ProductType::BOOKING]); // $30.00 per day
|
$product = Product::factory()->withPrices(unit_amount: 3000)->withStocks(10)->create(['type' => ProductType::BOOKING]); // 3000 cents per day
|
||||||
|
|
||||||
$from = Carbon::parse('2025-01-01 00:00:00');
|
$from = Carbon::parse('2025-01-01 00:00:00');
|
||||||
$until = Carbon::parse('2025-01-02 00:00:00'); // 1 day
|
$until = Carbon::parse('2025-01-02 00:00:00'); // 1 day
|
||||||
|
|
@ -145,7 +145,7 @@ class CartItemTest extends TestCase
|
||||||
public function cart_item_handles_fractional_days_with_multiple_quantities()
|
public function cart_item_handles_fractional_days_with_multiple_quantities()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 2400)->create(['type' => ProductType::BOOKING]); // $24.00 per day
|
$product = Product::factory()->withPrices(unit_amount: 2400)->create(['type' => ProductType::BOOKING]); // 2400 cents per day
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
// 1.5 days
|
// 1.5 days
|
||||||
|
|
@ -236,7 +236,7 @@ class CartItemTest extends TestCase
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
||||||
// Create a product with a price that will result in fractional cents when multiplied
|
// Create a product with a price that will result in fractional cents when multiplied
|
||||||
$product = Product::factory()->withPrices(unit_amount: 3333)->create(['type' => ProductType::BOOKING]); // $33.33
|
$product = Product::factory()->withPrices(unit_amount: 3333)->create(['type' => ProductType::BOOKING]); // 3333 cents
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
// 1.5 days should give 3333 * 1.5 = 4999.5 cents
|
// 1.5 days should give 3333 * 1.5 = 4999.5 cents
|
||||||
|
|
@ -329,7 +329,7 @@ class CartItemTest extends TestCase
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()
|
$product = Product::factory()
|
||||||
->withPrices(unit_amount: 500)
|
->withPrices(unit_amount: 500)
|
||||||
->create(['type' => ProductType::BOOKING]); // $5.00 per day
|
->create(['type' => ProductType::BOOKING]); // 500 cents per day
|
||||||
|
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,26 +19,26 @@ class CartTest extends TestCase
|
||||||
public function cart_can_add_product_price_directly()
|
public function cart_can_add_product_price_directly()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 2);
|
$cartItem = $cart->addToCart($price, quantity: 2);
|
||||||
|
|
||||||
$this->assertNotNull($cartItem);
|
$this->assertNotNull($cartItem);
|
||||||
$this->assertEquals(2, $cartItem->quantity);
|
$this->assertEquals(2, $cartItem->quantity);
|
||||||
$this->assertEquals(100.00, $cartItem->price);
|
$this->assertEquals(10000, $cartItem->price);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function cart_calculates_subtotal_automatically()
|
public function cart_calculates_subtotal_automatically()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price, quantity: 3);
|
$cartItem = $cart->addToCart($price, quantity: 3);
|
||||||
|
|
||||||
$this->assertEquals(150.00, $cartItem->subtotal);
|
$this->assertEquals(15000, $cartItem->subtotal);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -78,7 +78,7 @@ class CartTest extends TestCase
|
||||||
public function cart_can_add_items_with_custom_parameters()
|
public function cart_can_add_items_with_custom_parameters()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$parameters = [
|
$parameters = [
|
||||||
|
|
@ -99,10 +99,10 @@ class CartTest extends TestCase
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
|
|
||||||
$product1 = Product::factory()->withPrices(unit_amount: 25.00)->create();
|
$product1 = Product::factory()->withPrices(unit_amount: 2500)->create();
|
||||||
$price1 = $product1->defaultPrice()->first();
|
$price1 = $product1->defaultPrice()->first();
|
||||||
|
|
||||||
$product2 = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product2 = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price2 = $product2->defaultPrice()->first();
|
$price2 = $product2->defaultPrice()->first();
|
||||||
|
|
||||||
$cart->addToCart($price1, quantity: 2); // 50
|
$cart->addToCart($price1, quantity: 2); // 50
|
||||||
|
|
@ -110,7 +110,7 @@ class CartTest extends TestCase
|
||||||
|
|
||||||
$total = $cart->fresh()->getTotal();
|
$total = $cart->fresh()->getTotal();
|
||||||
|
|
||||||
$this->assertEquals(200.00, $total);
|
$this->assertEquals(20000, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
|
|
@ -172,7 +172,7 @@ class CartTest extends TestCase
|
||||||
public function cart_deletes_items_on_deletion()
|
public function cart_deletes_items_on_deletion()
|
||||||
{
|
{
|
||||||
$cart = Cart::create();
|
$cart = Cart::create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create();
|
||||||
$price = $product->defaultPrice()->first();
|
$price = $product->defaultPrice()->first();
|
||||||
|
|
||||||
$cartItem = $cart->addToCart($price);
|
$cartItem = $cart->addToCart($price);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_have_multiple_orders()
|
public function user_can_have_multiple_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_pending_orders()
|
public function user_can_get_pending_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -70,7 +70,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_processing_orders()
|
public function user_can_get_processing_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_completed_orders()
|
public function user_can_get_completed_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_active_orders()
|
public function user_can_get_active_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -122,7 +122,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_paid_orders()
|
public function user_can_get_paid_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_fully_paid_orders()
|
public function user_can_get_fully_paid_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -163,7 +163,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_latest_order()
|
public function user_can_get_latest_order()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -183,7 +183,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_total_spent()
|
public function user_can_get_total_spent()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -202,7 +202,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_order_count()
|
public function user_can_get_order_count()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -219,7 +219,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_completed_order_count()
|
public function user_can_get_completed_order_count()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -240,7 +240,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
|
|
||||||
$this->assertFalse($user->hasOrders());
|
$this->assertFalse($user->hasOrders());
|
||||||
|
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
$user->addToCart($product);
|
$user->addToCart($product);
|
||||||
|
|
@ -253,7 +253,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_check_has_active_orders()
|
public function user_can_check_has_active_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -272,7 +272,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_find_order_by_number()
|
public function user_can_find_order_by_number()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -291,7 +291,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
{
|
{
|
||||||
$user1 = User::factory()->create();
|
$user1 = User::factory()->create();
|
||||||
$user2 = User::factory()->create();
|
$user2 = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -309,7 +309,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_orders_between_dates()
|
public function user_can_get_orders_between_dates()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -328,7 +328,7 @@ class HasOrdersTraitTest extends TestCase
|
||||||
public function user_can_get_orders_with_specific_status()
|
public function user_can_get_orders_with_specific_status()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,8 +153,7 @@ class OrderNoteTest extends TestCase
|
||||||
'Note from user',
|
'Note from user',
|
||||||
'note',
|
'note',
|
||||||
false,
|
false,
|
||||||
get_class($user),
|
$user
|
||||||
$user->id
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals(get_class($user), $note->author_type);
|
$this->assertEquals(get_class($user), $note->author_type);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_total_revenue()
|
public function order_can_get_total_revenue()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_revenue_between_dates()
|
public function order_can_get_revenue_between_dates()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_total_refunded()
|
public function order_can_get_total_refunded()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_net_revenue()
|
public function order_can_get_net_revenue()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_average_order_value()
|
public function order_can_get_average_order_value()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -107,7 +107,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_counts_by_status()
|
public function order_can_get_counts_by_status()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -136,7 +136,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_revenue_summary()
|
public function order_can_get_revenue_summary()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -162,7 +162,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_can_get_daily_revenue()
|
public function order_can_get_daily_revenue()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -181,7 +181,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_scope_today_returns_todays_orders()
|
public function order_scope_today_returns_todays_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -195,7 +195,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_scope_this_week_returns_this_weeks_orders()
|
public function order_scope_this_week_returns_this_weeks_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -209,7 +209,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_scope_this_month_returns_this_months_orders()
|
public function order_scope_this_month_returns_this_months_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -223,7 +223,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_scope_by_payment_provider_returns_filtered_orders()
|
public function order_scope_by_payment_provider_returns_filtered_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -246,7 +246,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_scope_with_refunds_returns_orders_with_refunds()
|
public function order_scope_with_refunds_returns_orders_with_refunds()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -270,7 +270,7 @@ class OrderSummaryTest extends TestCase
|
||||||
public function order_scope_fully_refunded_returns_fully_refunded_orders()
|
public function order_scope_fully_refunded_returns_fully_refunded_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -599,4 +599,299 @@ class OrderTest extends TestCase
|
||||||
$this->assertEquals('USD 100.00', Order::formatMoney(10000, 'usd'));
|
$this->assertEquals('USD 100.00', Order::formatMoney(10000, 'usd'));
|
||||||
$this->assertEquals('EUR 50.50', Order::formatMoney(5050, 'eur'));
|
$this->assertEquals('EUR 50.50', Order::formatMoney(5050, 'eur'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// ORDER BOOKING DATE RANGE TESTS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_can_have_from_and_until_dates()
|
||||||
|
{
|
||||||
|
$from = now()->addDay();
|
||||||
|
$until = now()->addDays(5);
|
||||||
|
|
||||||
|
$order = Order::factory()->withDateRange($from, $until)->create();
|
||||||
|
|
||||||
|
$this->assertEquals($from->format('Y-m-d H:i:s'), $order->from->format('Y-m-d H:i:s'));
|
||||||
|
$this->assertEquals($until->format('Y-m-d H:i:s'), $order->until->format('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_factory_booking_state_sets_default_dates()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->booking()->create();
|
||||||
|
|
||||||
|
$this->assertNotNull($order->from);
|
||||||
|
$this->assertNotNull($order->until);
|
||||||
|
$this->assertTrue($order->from->lt($order->until));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_created_from_cart_inherits_booking_dates()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$from = now()->addDay();
|
||||||
|
$until = now()->addDays(3);
|
||||||
|
|
||||||
|
$cart = Cart::factory()->forCustomer($user)->create([
|
||||||
|
'converted_at' => now(),
|
||||||
|
'from' => $from,
|
||||||
|
'until' => $until,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$order = Order::createFromCart($cart);
|
||||||
|
|
||||||
|
$this->assertEquals($from->format('Y-m-d H:i:s'), $order->from->format('Y-m-d H:i:s'));
|
||||||
|
$this->assertEquals($until->format('Y-m-d H:i:s'), $order->until->format('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_without_booking_dates_has_null_from_until()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$cart = Cart::factory()->forCustomer($user)->create([
|
||||||
|
'converted_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$order = Order::createFromCart($cart);
|
||||||
|
|
||||||
|
$this->assertNull($order->from);
|
||||||
|
$this->assertNull($order->until);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// ORDER AUTOMATIC LOG CREATION TESTS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_status_change_automatically_creates_log()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->pending()->create();
|
||||||
|
|
||||||
|
$order->updateStatus(OrderStatus::PROCESSING);
|
||||||
|
|
||||||
|
$statusNote = $order->notes()
|
||||||
|
->where('type', OrderNote::TYPE_STATUS_CHANGE)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$this->assertNotNull($statusNote);
|
||||||
|
$this->assertStringContainsString('Pending Payment', $statusNote->content);
|
||||||
|
$this->assertStringContainsString('Processing', $statusNote->content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_payment_automatically_creates_log()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->pending()->create([
|
||||||
|
'amount_total' => 10000,
|
||||||
|
'amount_paid' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$order->recordPayment(5000, 'pi_test123', 'card', 'stripe');
|
||||||
|
|
||||||
|
$paymentNote = $order->notes()
|
||||||
|
->where('type', OrderNote::TYPE_PAYMENT)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$this->assertNotNull($paymentNote);
|
||||||
|
$this->assertStringContainsString('50.00', $paymentNote->content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_refund_automatically_creates_log()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->paid()->create([
|
||||||
|
'amount_total' => 10000,
|
||||||
|
'amount_paid' => 10000,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$order->recordRefund(3000, 'Partial refund');
|
||||||
|
|
||||||
|
$refundNote = $order->notes()
|
||||||
|
->where('type', OrderNote::TYPE_REFUND)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$this->assertNotNull($refundNote);
|
||||||
|
$this->assertStringContainsString('30.00', $refundNote->content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_shipping_creates_log_with_tracking()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->processing()->create();
|
||||||
|
|
||||||
|
$order->markAsShipped('TRACK123456', 'FedEx');
|
||||||
|
|
||||||
|
$shippingNote = $order->notes()
|
||||||
|
->where('type', OrderNote::TYPE_STATUS_CHANGE)
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$this->assertNotNull($shippingNote);
|
||||||
|
$this->assertStringContainsString('TRACK123456', $shippingNote->content);
|
||||||
|
$this->assertStringContainsString('FedEx', $shippingNote->content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_cancellation_creates_log_with_reason()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->pending()->create();
|
||||||
|
|
||||||
|
$order->cancel('Customer changed their mind');
|
||||||
|
|
||||||
|
$cancelNote = $order->notes()
|
||||||
|
->where('type', OrderNote::TYPE_STATUS_CHANGE)
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$this->assertNotNull($cancelNote);
|
||||||
|
$this->assertStringContainsString('Customer changed their mind', $cancelNote->content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// ORDER MANUAL LOG CREATION TESTS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_can_add_manual_internal_note()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->create();
|
||||||
|
|
||||||
|
$note = $order->addNote('This is a manual internal note', OrderNote::TYPE_NOTE, false);
|
||||||
|
|
||||||
|
$this->assertEquals('This is a manual internal note', $note->content);
|
||||||
|
$this->assertEquals(OrderNote::TYPE_NOTE, $note->type);
|
||||||
|
$this->assertFalse($note->is_customer_note);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_can_add_manual_customer_visible_note()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->create();
|
||||||
|
|
||||||
|
$note = $order->addNote('Thank you for your order!', OrderNote::TYPE_CUSTOMER, true);
|
||||||
|
|
||||||
|
$this->assertEquals('Thank you for your order!', $note->content);
|
||||||
|
$this->assertTrue($note->is_customer_note);
|
||||||
|
$this->assertCount(1, $order->customerNotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_can_add_note_with_author()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->create();
|
||||||
|
$admin = User::factory()->create();
|
||||||
|
|
||||||
|
$note = $order->addNote(
|
||||||
|
'Admin reviewed this order',
|
||||||
|
OrderNote::TYPE_NOTE,
|
||||||
|
false,
|
||||||
|
$admin
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals($admin->id, $note->author_id);
|
||||||
|
$this->assertEquals(get_class($admin), $note->author_type);
|
||||||
|
$this->assertTrue($note->author->is($admin));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_can_add_note_with_meta()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->create();
|
||||||
|
|
||||||
|
$note = $order->addNote('Note with metadata', OrderNote::TYPE_SYSTEM, false, null, [
|
||||||
|
'source' => 'api',
|
||||||
|
'request_id' => 'req_12345',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals('api', $note->meta->source);
|
||||||
|
$this->assertEquals('req_12345', $note->meta->request_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_notes_are_ordered_by_newest_first()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->create();
|
||||||
|
|
||||||
|
$note1 = $order->addNote('First note');
|
||||||
|
sleep(1); // Ensure different timestamps
|
||||||
|
$note2 = $order->addNote('Second note');
|
||||||
|
|
||||||
|
$notes = $order->notes()->get();
|
||||||
|
|
||||||
|
$this->assertEquals($note2->id, $notes->first()->id);
|
||||||
|
$this->assertEquals($note1->id, $notes->last()->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_logs_multiple_status_changes()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->pending()->create();
|
||||||
|
|
||||||
|
$order->updateStatus(OrderStatus::PROCESSING);
|
||||||
|
$order->updateStatus(OrderStatus::IN_PREPARATION);
|
||||||
|
$order->updateStatus(OrderStatus::SHIPPED);
|
||||||
|
|
||||||
|
$statusNotes = $order->notes()
|
||||||
|
->where('type', OrderNote::TYPE_STATUS_CHANGE)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Should have 3 status change notes
|
||||||
|
$this->assertCount(3, $statusNotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_logs_multiple_partial_payments()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->pending()->create([
|
||||||
|
'amount_total' => 10000,
|
||||||
|
'amount_paid' => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$order->recordPayment(3000);
|
||||||
|
$order->recordPayment(3000);
|
||||||
|
$order->recordPayment(4000);
|
||||||
|
|
||||||
|
$paymentNotes = $order->notes()
|
||||||
|
->where('type', OrderNote::TYPE_PAYMENT)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$this->assertCount(3, $paymentNotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// ORDER LOG FILTERING TESTS
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_can_filter_internal_notes()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->create();
|
||||||
|
|
||||||
|
$order->addNote('Internal 1', OrderNote::TYPE_NOTE, false);
|
||||||
|
$order->addNote('Internal 2', OrderNote::TYPE_NOTE, false);
|
||||||
|
$order->addNote('Customer visible', OrderNote::TYPE_CUSTOMER, true);
|
||||||
|
|
||||||
|
$this->assertCount(2, $order->internalNotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
public function order_can_get_notes_by_type()
|
||||||
|
{
|
||||||
|
$order = Order::factory()->pending()->create([
|
||||||
|
'amount_total' => 10000,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$order->addNote('Manual note', OrderNote::TYPE_NOTE);
|
||||||
|
$order->recordPayment(5000);
|
||||||
|
$order->updateStatus(OrderStatus::PROCESSING);
|
||||||
|
|
||||||
|
$notesByType = $order->notes()->where('type', OrderNote::TYPE_NOTE)->get();
|
||||||
|
$paymentsByType = $order->notes()->where('type', OrderNote::TYPE_PAYMENT)->get();
|
||||||
|
$statusChangesByType = $order->notes()->where('type', OrderNote::TYPE_STATUS_CHANGE)->get();
|
||||||
|
|
||||||
|
$this->assertCount(1, $notesByType);
|
||||||
|
$this->assertCount(1, $paymentsByType);
|
||||||
|
$this->assertCount(1, $statusChangesByType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class ProductDuplicateTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function duplicate_includes_prices()
|
public function duplicate_includes_prices()
|
||||||
{
|
{
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create();
|
||||||
|
|
||||||
$duplicate = $product->duplicate();
|
$duplicate = $product->duplicate();
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ class ProductDuplicateTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function duplicate_can_exclude_prices()
|
public function duplicate_can_exclude_prices()
|
||||||
{
|
{
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create();
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create();
|
||||||
|
|
||||||
$duplicate = $product->duplicate(includePrices: false);
|
$duplicate = $product->duplicate(includePrices: false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,71 +15,71 @@ class ProductPricingTest extends TestCase
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_regular_price_when_not_on_sale()
|
public function it_returns_regular_price_when_not_on_sale()
|
||||||
{
|
{
|
||||||
$product = Product::factory()->withPrices(2, 100)->create();
|
$product = Product::factory()->withPrices(2, 10000)->create();
|
||||||
|
|
||||||
$this->assertEquals(2, $product->prices()->count());
|
$this->assertEquals(2, $product->prices()->count());
|
||||||
$this->assertFalse($product->isOnSale());
|
$this->assertFalse($product->isOnSale());
|
||||||
$this->assertNotNull($product->defaultPrice()->first());
|
$this->assertNotNull($product->defaultPrice()->first());
|
||||||
$this->assertEquals(100, $product->getCurrentPrice());
|
$this->assertEquals(10000, $product->getCurrentPrice());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_sale_price_when_on_sale()
|
public function it_returns_sale_price_when_on_sale()
|
||||||
{
|
{
|
||||||
$product = Product::factory()
|
$product = Product::factory()
|
||||||
->withPrices(1, 100)
|
->withPrices(1, 10000)
|
||||||
->create([
|
->create([
|
||||||
'sale_start' => now()->subDay(),
|
'sale_start' => now()->subDay(),
|
||||||
'sale_end' => now()->addDay(),
|
'sale_end' => now()->addDay(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$price = $product->prices()->first();
|
$price = $product->prices()->first();
|
||||||
$price->sale_unit_amount = 80;
|
$price->sale_unit_amount = 8000;
|
||||||
|
|
||||||
$price->save();
|
$price->save();
|
||||||
|
|
||||||
$this->assertEquals(80, $product->getCurrentPrice());
|
$this->assertEquals(8000, $product->getCurrentPrice());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_regular_price_when_sale_has_ended()
|
public function it_returns_regular_price_when_sale_has_ended()
|
||||||
{
|
{
|
||||||
$product = Product::factory()->withPrices(1, 100)->create([
|
$product = Product::factory()->withPrices(1, 10000)->create([
|
||||||
'sale_start' => now()->subWeek(),
|
'sale_start' => now()->subWeek(),
|
||||||
'sale_end' => now()->addHour(),
|
'sale_end' => now()->addHour(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$price = $product->prices()->first();
|
$price = $product->prices()->first();
|
||||||
$price->sale_unit_amount = 80;
|
$price->sale_unit_amount = 8000;
|
||||||
$price->save();
|
$price->save();
|
||||||
|
|
||||||
$this->assertEquals(80, $product->getCurrentPrice());
|
$this->assertEquals(8000, $product->getCurrentPrice());
|
||||||
|
|
||||||
$product->update([
|
$product->update([
|
||||||
'sale_end' => now()->subHour(),
|
'sale_end' => now()->subHour(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(100, $product->getCurrentPrice());
|
$this->assertEquals(10000, $product->getCurrentPrice());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function it_returns_regular_price_when_sale_hasnt_started()
|
public function it_returns_regular_price_when_sale_hasnt_started()
|
||||||
{
|
{
|
||||||
$product = Product::factory()->withPrices(1, 100)->create([
|
$product = Product::factory()->withPrices(1, 10000)->create([
|
||||||
'sale_start' => now()->addDay(),
|
'sale_start' => now()->addDay(),
|
||||||
'sale_end' => now()->addWeek(),
|
'sale_end' => now()->addWeek(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$price = $product->prices()->first();
|
$price = $product->prices()->first();
|
||||||
$price->sale_unit_amount = 80;
|
$price->sale_unit_amount = 8000;
|
||||||
$price->save();
|
$price->save();
|
||||||
|
|
||||||
$this->assertEquals(100, $product->getCurrentPrice());
|
$this->assertEquals(10000, $product->getCurrentPrice());
|
||||||
|
|
||||||
$product->update([
|
$product->update([
|
||||||
'sale_start' => now()->subHour(),
|
'sale_start' => now()->subHour(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(80, $product->getCurrentPrice());
|
$this->assertEquals(8000, $product->getCurrentPrice());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_get_orders()
|
public function shop_facade_can_get_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_get_order_by_number()
|
public function shop_facade_can_get_order_by_number()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_get_orders_today()
|
public function shop_facade_can_get_orders_today()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_get_orders_this_week()
|
public function shop_facade_can_get_orders_this_week()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_get_orders_this_month()
|
public function shop_facade_can_get_orders_this_month()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_get_pending_orders()
|
public function shop_facade_can_get_pending_orders()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 25.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 2500)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -127,7 +127,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_calculate_total_revenue()
|
public function shop_facade_can_calculate_total_revenue()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -142,7 +142,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_calculate_revenue_today()
|
public function shop_facade_can_calculate_revenue_today()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 100.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 10000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -157,7 +157,7 @@ class ShopServiceTest extends TestCase
|
||||||
public function shop_facade_can_get_stats()
|
public function shop_facade_can_get_stats()
|
||||||
{
|
{
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$product = Product::factory()->withPrices(unit_amount: 50.00)->create([
|
$product = Product::factory()->withPrices(unit_amount: 5000)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
'status' => 'published',
|
'status' => 'published',
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
/**
|
/**
|
||||||
* Create a product for testing
|
* Create a product for testing
|
||||||
*/
|
*/
|
||||||
protected function createProduct(float $price = 100.00): Product
|
protected function createProduct(int $price = 10000): Product
|
||||||
{
|
{
|
||||||
return Product::factory()->withPrices(unit_amount: $price)->create([
|
return Product::factory()->withPrices(unit_amount: $price)->create([
|
||||||
'manage_stock' => false,
|
'manage_stock' => false,
|
||||||
|
|
@ -90,7 +90,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_completed_creates_order_payment()
|
public function checkout_session_completed_creates_order_payment()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -108,7 +108,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
$this->invokeMethod('handleCheckoutSessionCompleted', [$session]);
|
$this->invokeMethod('handleCheckoutSessionCompleted', [$session]);
|
||||||
|
|
||||||
$order->refresh();
|
$order->refresh();
|
||||||
$this->assertEquals(100.00, $order->amount_paid);
|
$this->assertEquals(10000, $order->amount_paid);
|
||||||
$this->assertEquals(OrderStatus::PROCESSING, $order->status);
|
$this->assertEquals(OrderStatus::PROCESSING, $order->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_completed_logs_payment_note()
|
public function checkout_session_completed_logs_payment_note()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(50.00);
|
$product = $this->createProduct(5000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -143,7 +143,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_failed_updates_order_status()
|
public function checkout_session_failed_updates_order_status()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -164,7 +164,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_failed_adds_payment_note()
|
public function checkout_session_failed_adds_payment_note()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(50.00);
|
$product = $this->createProduct(5000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -189,7 +189,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_expired_adds_system_note()
|
public function checkout_session_expired_adds_system_note()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(50.00);
|
$product = $this->createProduct(5000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -214,7 +214,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function charge_refunded_records_refund_on_order()
|
public function charge_refunded_records_refund_on_order()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -244,7 +244,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function charge_dispute_created_puts_order_on_hold()
|
public function charge_dispute_created_puts_order_on_hold()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -273,7 +273,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function charge_dispute_created_adds_payment_note()
|
public function charge_dispute_created_adds_payment_note()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -302,7 +302,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function charge_dispute_closed_restores_order_if_won()
|
public function charge_dispute_closed_restores_order_if_won()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -329,7 +329,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function charge_dispute_closed_refunds_order_if_lost()
|
public function charge_dispute_closed_refunds_order_if_lost()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -356,7 +356,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function refund_created_records_refund_on_order()
|
public function refund_created_records_refund_on_order()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -383,7 +383,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function refund_updated_adds_note_to_order()
|
public function refund_updated_adds_note_to_order()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -410,7 +410,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function find_order_by_payment_intent_works()
|
public function find_order_by_payment_intent_works()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -428,7 +428,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function find_order_by_charge_id_works()
|
public function find_order_by_charge_id_works()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -476,7 +476,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function handler_uses_client_reference_id_as_fallback()
|
public function handler_uses_client_reference_id_as_fallback()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(50.00);
|
$product = $this->createProduct(5000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -492,14 +492,14 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
$this->assertTrue($result);
|
$this->assertTrue($result);
|
||||||
|
|
||||||
$order = $cart->fresh()->order;
|
$order = $cart->fresh()->order;
|
||||||
$this->assertEquals(50.00, $order->amount_paid);
|
$this->assertEquals(5000, $order->amount_paid);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function payment_intent_canceled_adds_note()
|
public function payment_intent_canceled_adds_note()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -525,7 +525,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function charge_failed_adds_failure_note_to_order()
|
public function charge_failed_adds_failure_note_to_order()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->checkoutCart();
|
$cart = $customer->checkoutCart();
|
||||||
|
|
@ -559,7 +559,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_completed_creates_order_when_none_exists()
|
public function checkout_session_completed_creates_order_when_none_exists()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
// Add to cart but DON'T call checkoutCart() - simulate checkoutSession() flow
|
// Add to cart but DON'T call checkoutCart() - simulate checkoutSession() flow
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
|
|
@ -597,7 +597,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_completed_creates_order_and_records_payment()
|
public function checkout_session_completed_creates_order_and_records_payment()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(150.00);
|
$product = $this->createProduct(15000);
|
||||||
|
|
||||||
// Add to cart but DON'T call checkoutCart()
|
// Add to cart but DON'T call checkoutCart()
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
|
|
@ -622,7 +622,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
$order = $cart->order;
|
$order = $cart->order;
|
||||||
|
|
||||||
$this->assertNotNull($order);
|
$this->assertNotNull($order);
|
||||||
$this->assertEquals(150.00, $order->amount_paid);
|
$this->assertEquals(15000, $order->amount_paid);
|
||||||
$this->assertEquals(OrderStatus::PROCESSING, $order->status);
|
$this->assertEquals(OrderStatus::PROCESSING, $order->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -630,7 +630,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_completed_creates_order_with_correct_totals()
|
public function checkout_session_completed_creates_order_with_correct_totals()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(75.50);
|
$product = $this->createProduct(7550);
|
||||||
|
|
||||||
$customer->addToCart($product, 2); // 2 items = 151.00
|
$customer->addToCart($product, 2); // 2 items = 151.00
|
||||||
$cart = $customer->currentCart();
|
$cart = $customer->currentCart();
|
||||||
|
|
@ -651,15 +651,15 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
$order = $cart->fresh()->order;
|
$order = $cart->fresh()->order;
|
||||||
|
|
||||||
$this->assertNotNull($order);
|
$this->assertNotNull($order);
|
||||||
// Order total should match cart total (in cents)
|
// Order total should match cart total (already in cents)
|
||||||
$this->assertEquals((int) $cart->getTotal() * 100, $order->amount_total);
|
$this->assertEquals((int) $cart->getTotal(), $order->amount_total);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function checkout_session_completed_adds_payment_note_when_creating_order()
|
public function checkout_session_completed_adds_payment_note_when_creating_order()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(50.00);
|
$product = $this->createProduct(5000);
|
||||||
|
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
$cart = $customer->currentCart();
|
$cart = $customer->currentCart();
|
||||||
|
|
@ -685,14 +685,13 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
$paymentNote = $order->notes()->where('type', OrderNote::TYPE_PAYMENT)->first();
|
$paymentNote = $order->notes()->where('type', OrderNote::TYPE_PAYMENT)->first();
|
||||||
$this->assertNotNull($paymentNote, 'Payment note should be created');
|
$this->assertNotNull($paymentNote, 'Payment note should be created');
|
||||||
$this->assertStringContainsString('50', $paymentNote->content);
|
$this->assertStringContainsString('50', $paymentNote->content);
|
||||||
$this->assertStringContainsString('Stripe checkout', $paymentNote->content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
public function checkout_session_completed_does_not_duplicate_order()
|
public function checkout_session_completed_does_not_duplicate_order()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(100.00);
|
$product = $this->createProduct(10000);
|
||||||
|
|
||||||
// Use checkoutCart() which creates an order
|
// Use checkoutCart() which creates an order
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
|
|
@ -725,7 +724,7 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
public function checkout_session_completed_without_prior_conversion_creates_order()
|
public function checkout_session_completed_without_prior_conversion_creates_order()
|
||||||
{
|
{
|
||||||
$customer = User::factory()->create();
|
$customer = User::factory()->create();
|
||||||
$product = $this->createProduct(200.00);
|
$product = $this->createProduct(20000);
|
||||||
|
|
||||||
// Add to cart - cart is NOT converted yet (simulates edge case)
|
// Add to cart - cart is NOT converted yet (simulates edge case)
|
||||||
$customer->addToCart($product);
|
$customer->addToCart($product);
|
||||||
|
|
@ -751,6 +750,6 @@ class StripeWebhookOrderTest extends TestCase
|
||||||
|
|
||||||
// Order should exist
|
// Order should exist
|
||||||
$this->assertNotNull($cart->order);
|
$this->assertNotNull($cart->order);
|
||||||
$this->assertEquals(200.00, $cart->order->amount_paid);
|
$this->assertEquals(20000, $cart->order->amount_paid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue