Work on tests
This commit is contained in:
parent
efe78bb49f
commit
b320915fc1
@ -20,6 +20,5 @@ enum IconEnum: string
|
|||||||
|
|
||||||
case TAB_ALL = 'lucide-layout-grid';
|
case TAB_ALL = 'lucide-layout-grid';
|
||||||
case TAB_OVERDUE = 'lucide-calendar-clock';
|
case TAB_OVERDUE = 'lucide-calendar-clock';
|
||||||
case TAB_UNPRINTED = ' lucide-printer';
|
case TAB_UNPRINTED = 'lucide-printer';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
use App\Enums\IconEnum;
|
use App\Enums\IconEnum;
|
||||||
use App\Enums\InvoiceStatus;
|
use App\Enums\InvoiceStatus;
|
||||||
use App\Filament\Admin\Resources\InvoiceResource;
|
use App\Filament\Admin\Resources\InvoiceResource;
|
||||||
use App\Models\Invoice;
|
|
||||||
use Filament\Actions;
|
use Filament\Actions;
|
||||||
use Filament\Resources\Components\Tab;
|
use Filament\Resources\Components\Tab;
|
||||||
use Filament\Resources\Pages\ListRecords;
|
use Filament\Resources\Pages\ListRecords;
|
||||||
@ -22,12 +21,6 @@ public function getTabs(): array
|
|||||||
|
|
||||||
'unpaid' => Tab::make('Unpaid')
|
'unpaid' => Tab::make('Unpaid')
|
||||||
->query(fn ($query) => $query->where('status', InvoiceStatus::UNPAID))
|
->query(fn ($query) => $query->where('status', InvoiceStatus::UNPAID))
|
||||||
->badge(function () {
|
|
||||||
$count = Invoice::where('status', InvoiceStatus::UNPAID)->count();
|
|
||||||
|
|
||||||
return $count > 0 ? $count : null;
|
|
||||||
})
|
|
||||||
->badgeColor(InvoiceStatus::UNPAID->getColor())
|
|
||||||
->icon(InvoiceStatus::UNPAID->getIcon()),
|
->icon(InvoiceStatus::UNPAID->getIcon()),
|
||||||
|
|
||||||
'paid' => Tab::make('Paid')
|
'paid' => Tab::make('Paid')
|
||||||
|
@ -125,6 +125,10 @@ public static function form(Form $form): Form
|
|||||||
|
|
||||||
Section::make()
|
Section::make()
|
||||||
->schema([
|
->schema([
|
||||||
|
Placeholder::make('ID')
|
||||||
|
->label('Order ID')
|
||||||
|
->content(fn (Order $record): ?string => $record->internal_po),
|
||||||
|
|
||||||
Placeholder::make('created_at')
|
Placeholder::make('created_at')
|
||||||
->label('Created')
|
->label('Created')
|
||||||
->content(fn (Order $record): ?string => $record->created_at?->diffForHumans()),
|
->content(fn (Order $record): ?string => $record->created_at?->diffForHumans()),
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
#[ObservedBy(InvoiceObserver::class)]
|
#[ObservedBy(InvoiceObserver::class)]
|
||||||
|
|
||||||
@ -39,19 +40,23 @@ class Invoice extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'has_gst' => 'boolean',
|
'has_gst' => 'boolean',
|
||||||
'has_pst' => 'boolean',
|
'has_pst' => 'boolean',
|
||||||
'date' => 'datetime',
|
'date' => 'datetime',
|
||||||
'total' => 'decimal:2',
|
'status' => InvoiceStatus::class,
|
||||||
'subtotal' => 'decimal:2',
|
'subtotal' => 'float',
|
||||||
'status' => InvoiceStatus::class,
|
'pst_amount' => 'float',
|
||||||
|
'gst_amount' => 'float',
|
||||||
|
'total' => 'float',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function calculateTotals(): void
|
public function calculateTotals(): void
|
||||||
{
|
{
|
||||||
|
$this->refresh();
|
||||||
$this->loadMissing('orders');
|
$this->loadMissing('orders');
|
||||||
|
|
||||||
if ($this->orders->isEmpty()) {
|
if ($this->orders->isEmpty()) {
|
||||||
|
|
||||||
$this->subtotal = 0;
|
$this->subtotal = 0;
|
||||||
$this->gst_amount = 0;
|
$this->gst_amount = 0;
|
||||||
$this->pst_amount = 0;
|
$this->pst_amount = 0;
|
||||||
@ -60,8 +65,11 @@ public function calculateTotals(): void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$subtotal = $this->orders->sum(fn (Order $order) => $order->total_service_price);
|
$subtotal = $this->orders->sum(fn (Order $order) => $order->total_service_price);
|
||||||
|
Log::debug('subtotal: '.$subtotal);
|
||||||
|
|
||||||
$this->subtotal = $subtotal;
|
$this->subtotal = $subtotal;
|
||||||
|
$this->saveQuietly();
|
||||||
|
|
||||||
$gstAmount = $this->calculateTaxAmount($subtotal, $this->gst_rate, $this->has_gst);
|
$gstAmount = $this->calculateTaxAmount($subtotal, $this->gst_rate, $this->has_gst);
|
||||||
$pstAmount = $this->calculateTaxAmount($subtotal, $this->pst_rate, $this->has_pst);
|
$pstAmount = $this->calculateTaxAmount($subtotal, $this->pst_rate, $this->has_pst);
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
use App\Enums\OrderStatus;
|
use App\Enums\OrderStatus;
|
||||||
use App\Enums\OrderType;
|
use App\Enums\OrderType;
|
||||||
|
use App\Observers\OrderObserver;
|
||||||
use Database\Factories\OrderFactory;
|
use Database\Factories\OrderFactory;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -15,6 +17,8 @@
|
|||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
#[ObservedBy(OrderObserver::class)]
|
||||||
|
|
||||||
class Order extends Model
|
class Order extends Model
|
||||||
{
|
{
|
||||||
/** @use HasFactory<OrderFactory> */
|
/** @use HasFactory<OrderFactory> */
|
||||||
@ -50,8 +54,17 @@ class Order extends Model
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'status' => OrderStatus::class,
|
'status' => OrderStatus::class,
|
||||||
'order_type' => OrderType::class,
|
'order_type' => OrderType::class,
|
||||||
|
'rush' => 'bool',
|
||||||
|
'repeat' => 'bool',
|
||||||
|
'new_art' => 'bool',
|
||||||
|
'event' => 'bool',
|
||||||
|
'digitizing' => 'bool',
|
||||||
|
'garments' => 'bool',
|
||||||
|
'supplied_file' => 'bool',
|
||||||
|
'printed' => 'bool',
|
||||||
|
'pre_production' => 'bool',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static function boot(): void
|
public static function boot(): void
|
||||||
@ -109,13 +122,13 @@ public function getTotalProductQuantityAttribute(): int
|
|||||||
|
|
||||||
public function getTotalServicePriceAttribute(): float
|
public function getTotalServicePriceAttribute(): float
|
||||||
{
|
{
|
||||||
$total = 0;
|
$total = 0.00;
|
||||||
|
|
||||||
foreach ($this->productServices as $service) {
|
foreach ($this->productServices as $service) {
|
||||||
$total += $service->amount * $service->amount_price;
|
$total += $service->amount * $service->amount_price;
|
||||||
}
|
}
|
||||||
|
|
||||||
return number_format($total, 2);
|
return $total;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function active(): bool
|
public function active(): bool
|
||||||
|
@ -2,12 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Observers\ProductServiceObserver;
|
||||||
use Database\Factories\ProductServiceFactory;
|
use Database\Factories\ProductServiceFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
#[ObservedBy(ProductServiceObserver::class)]
|
||||||
|
|
||||||
class ProductService extends Model
|
class ProductService extends Model
|
||||||
{
|
{
|
||||||
/** @use HasFactory<ProductServiceFactory> */
|
/** @use HasFactory<ProductServiceFactory> */
|
||||||
@ -38,6 +42,10 @@ public function getServiceDetailsAttribute(): string
|
|||||||
{
|
{
|
||||||
$file = $this->serviceFile;
|
$file = $this->serviceFile;
|
||||||
|
|
||||||
|
if (! $file) {
|
||||||
|
return 'Error: could not find service file';
|
||||||
|
}
|
||||||
|
|
||||||
return $file->name.', '.$this->placement.', '.$file->width.' W, '.$file->height.' H';
|
return $file->name.', '.$this->placement.', '.$file->width.' W, '.$file->height.' H';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
65
app/Observers/OrderObserver.php
Normal file
65
app/Observers/OrderObserver.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Observers;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Order;
|
||||||
|
|
||||||
|
class OrderObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the Order "created" event.
|
||||||
|
*/
|
||||||
|
public function created(Order $order): void
|
||||||
|
{
|
||||||
|
if ($order->invoice()->exists()) {
|
||||||
|
$order->invoice->calculateTotals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Order "updated" event.
|
||||||
|
*/
|
||||||
|
public function updated(Order $order): void
|
||||||
|
{
|
||||||
|
if ($order->invoice()->exists()) {
|
||||||
|
$order->invoice->calculateTotals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Order "saving" event.
|
||||||
|
*/
|
||||||
|
public function saved(Order $order): void
|
||||||
|
{
|
||||||
|
if ($order->isDirty(['invoice_id']) && Invoice::where('id', $order->invoice_id)->exists()) {
|
||||||
|
$order->invoice->calculateTotals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Order "deleted" event.
|
||||||
|
*/
|
||||||
|
public function deleted(Order $order): void
|
||||||
|
{
|
||||||
|
if ($order->invoice()->exists()) {
|
||||||
|
$order->invoice->calculateTotals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Order "restored" event.
|
||||||
|
*/
|
||||||
|
public function restored(Order $order): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the Order "force deleted" event.
|
||||||
|
*/
|
||||||
|
public function forceDeleted(Order $order): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
64
app/Observers/ProductServiceObserver.php
Normal file
64
app/Observers/ProductServiceObserver.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Observers;
|
||||||
|
|
||||||
|
use App\Models\ProductService;
|
||||||
|
|
||||||
|
class ProductServiceObserver
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle the ProductService "created" event.
|
||||||
|
*/
|
||||||
|
public function created(ProductService $productService): void
|
||||||
|
{
|
||||||
|
$this->calculateInvoiceTotalsIfExists($productService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the ProductService "updated" event.
|
||||||
|
*/
|
||||||
|
public function updated(ProductService $productService): void
|
||||||
|
{
|
||||||
|
$this->calculateInvoiceTotalsIfExists($productService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saved(ProductService $productService): void
|
||||||
|
{
|
||||||
|
$this->calculateInvoiceTotalsIfExists($productService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the ProductService "deleted" event.
|
||||||
|
*/
|
||||||
|
public function deleted(ProductService $productService): void
|
||||||
|
{
|
||||||
|
$this->calculateInvoiceTotalsIfExists($productService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the ProductService "restored" event.
|
||||||
|
*/
|
||||||
|
public function restored(ProductService $productService): void
|
||||||
|
{
|
||||||
|
$this->calculateInvoiceTotalsIfExists($productService);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the ProductService "force deleted" event.
|
||||||
|
*/
|
||||||
|
public function forceDeleted(ProductService $productService): void
|
||||||
|
{
|
||||||
|
$this->calculateInvoiceTotalsIfExists($productService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateInvoiceTotalsIfExists(ProductService $productService): void
|
||||||
|
{
|
||||||
|
if ($productService->order()->exists()) {
|
||||||
|
if ($productService->order->invoice()->exists()) {
|
||||||
|
|
||||||
|
$productService->order->invoice->calculateTotals();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ class CustomerFactory extends Factory
|
|||||||
{
|
{
|
||||||
protected $model = Customer::class;
|
protected $model = Customer::class;
|
||||||
|
|
||||||
public function definition()
|
public function definition(): array
|
||||||
{
|
{
|
||||||
$company_name = $this->faker->company();
|
$company_name = $this->faker->company();
|
||||||
$internal_name = explode(',', $company_name);
|
$internal_name = explode(',', $company_name);
|
||||||
|
@ -15,7 +15,7 @@ class InvoiceFactory extends Factory
|
|||||||
|
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
$customer = Customer::all()->shuffle()->first();
|
$customer = Customer::all()->shuffle()->firstOrFail();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'created_at' => Carbon::now()->subDays(rand(1, 30)),
|
'created_at' => Carbon::now()->subDays(rand(1, 30)),
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use App\Enums\InvoiceStatus;
|
|
||||||
use App\Filament\Admin\Resources\InvoiceResource\Pages\CreateInvoice;
|
|
||||||
use App\Models\Customer;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use App\Models\TaxRate;
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
|
||||||
|
|
||||||
it('can create an invoice', function () {
|
|
||||||
$customer = Customer::factory()->create(); // Generates a customer
|
|
||||||
$user = User::factory(['is_admin' => true])->create();
|
|
||||||
$pst_rate = TaxRate::where('name', 'PST')->value('value') ?? 0;
|
|
||||||
$gst_rate = TaxRate::where('name', 'GST')->value('value') ?? 0;
|
|
||||||
|
|
||||||
$this->actingAs($user);
|
|
||||||
|
|
||||||
$formData = [
|
|
||||||
'customer_id' => $customer->id,
|
|
||||||
'date' => now()->toDateString(),
|
|
||||||
'due_date' => now()->addDays(7)->toDateString(),
|
|
||||||
'status' => InvoiceStatus::UNPAID->value,
|
|
||||||
'has_gst' => true,
|
|
||||||
'has_pst' => true,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Step 3: Submit the form and create an invoice
|
|
||||||
$this->livewire(CreateInvoice::class)
|
|
||||||
->fillForm($formData) // Simulates filling the form
|
|
||||||
->call('create') // Submits the form
|
|
||||||
->assertHasNoErrors(); // Verifies no validation errors occurred
|
|
||||||
|
|
||||||
// Step 4: Assert the invoice was successfully created in the database
|
|
||||||
$this->assertDatabaseHas('invoices', [
|
|
||||||
'internal_id' => 'INV400001',
|
|
||||||
'customer_id' => $formData['customer_id'],
|
|
||||||
'status' => $formData['status'],
|
|
||||||
'has_gst' => $formData['has_gst'],
|
|
||||||
'has_pst' => $formData['has_pst'],
|
|
||||||
'gst_rate' => $gst_rate,
|
|
||||||
'pst_rate' => $pst_rate,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// it can add orders
|
|
||||||
|
|
||||||
// it correctly calculates tax
|
|
@ -11,9 +11,18 @@
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
pest()->extend(Tests\TestCase::class)
|
pest()->extend(Tests\TestCase::class)
|
||||||
->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
|
// ->use(Illuminate\Foundation\Testing\DatabaseMigrations::class)
|
||||||
->in('Feature');
|
// ->use(RefreshDatabase::class)
|
||||||
|
->beforeEach(function () {
|
||||||
|
DB::beginTransaction();
|
||||||
|
})
|
||||||
|
->afterEach(function () {
|
||||||
|
DB::rollBack();
|
||||||
|
})
|
||||||
|
->in('Unit');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -26,9 +35,9 @@
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
expect()->extend('toBeOne', function () {
|
//expect()->extend('toBeOne', function () {
|
||||||
return $this->toBe(1);
|
// return $this->toBe(1);
|
||||||
});
|
//});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
254
tests/Unit/InvoiceTest.php
Normal file
254
tests/Unit/InvoiceTest.php
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\InvoiceStatus;
|
||||||
|
use App\Filament\Admin\Resources\InvoiceResource\Pages\CreateInvoice;
|
||||||
|
use App\Models\Customer;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Order;
|
||||||
|
use App\Models\ProductService;
|
||||||
|
use App\Models\TaxRate;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
it('can create an invoice', function () {
|
||||||
|
$user = User::factory(['is_admin' => true])->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
$customer = Customer::factory()->create(); // Generates a customer
|
||||||
|
$pst_rate = TaxRate::where('name', 'PST')->value('value') ?? 0;
|
||||||
|
$gst_rate = TaxRate::where('name', 'GST')->value('value') ?? 0;
|
||||||
|
|
||||||
|
$formData = [
|
||||||
|
'customer_id' => $customer->id,
|
||||||
|
'date' => now()->toDateString(),
|
||||||
|
'due_date' => now()->addDays(7)->toDateString(),
|
||||||
|
'status' => InvoiceStatus::UNPAID->value,
|
||||||
|
'has_gst' => true,
|
||||||
|
'has_pst' => true,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->livewire(CreateInvoice::class)
|
||||||
|
->fillForm($formData)
|
||||||
|
->call('create')
|
||||||
|
->assertHasNoErrors();
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('invoices', [
|
||||||
|
'internal_id' => 'INV400001',
|
||||||
|
'customer_id' => $formData['customer_id'],
|
||||||
|
'status' => $formData['status'],
|
||||||
|
'has_gst' => $formData['has_gst'],
|
||||||
|
'has_pst' => $formData['has_pst'],
|
||||||
|
'gst_rate' => $gst_rate,
|
||||||
|
'pst_rate' => $pst_rate,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$invoice = Invoice::where('internal_id', 'INV400001')->firstOrFail();
|
||||||
|
|
||||||
|
$this->assertEquals($invoice->orders->isEmpty(), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add orders to an invoice', function () {
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$invoice = Invoice::factory()->create(['customer_id' => $customer->id]);
|
||||||
|
$orders = Order::factory()->for($customer)->count(3)->create();
|
||||||
|
|
||||||
|
$invoice->orders()->saveMany($orders);
|
||||||
|
|
||||||
|
$this->assertEquals($invoice->orders->count(), 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly calculates tax amounts', function () {
|
||||||
|
/** Setup **/
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$order = Order::factory()
|
||||||
|
->for($customer)
|
||||||
|
->has(ProductService::factory()->count(3))
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$invoice = Invoice::factory(['has_gst' => true, 'has_pst' => true])->create(['customer_id' => $customer->id]);
|
||||||
|
$order->invoice()->associate($invoice)->save();
|
||||||
|
|
||||||
|
/** Action **/
|
||||||
|
$subtotal = $order->total_service_price;
|
||||||
|
$pst_amount = $subtotal * $invoice->pst_rate / 100;
|
||||||
|
$gst_amount = $subtotal * $invoice->gst_rate / 100;
|
||||||
|
$total = $subtotal + $pst_amount + $gst_amount;
|
||||||
|
|
||||||
|
/** Assertions **/
|
||||||
|
$this->assertSame($invoice->subtotal, $subtotal);
|
||||||
|
$this->assertSame($invoice->gst_amount, $gst_amount);
|
||||||
|
$this->assertSame($invoice->pst_amount, $pst_amount);
|
||||||
|
$this->assertSame($invoice->total, $total);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly re-calculates tax amounts when a product service gets added to an order', function () {
|
||||||
|
/** Setup **/
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$invoice = Invoice::factory(['has_gst' => true, 'has_pst' => true])->create(['customer_id' => $customer->id]);
|
||||||
|
$order = Order::factory()->for($customer)->create();
|
||||||
|
|
||||||
|
$order->invoice()->associate($invoice)->save();
|
||||||
|
|
||||||
|
ProductService::factory()->create(['order_id' => $order->id]);
|
||||||
|
|
||||||
|
/** Action **/
|
||||||
|
$subtotal = $order->total_service_price;
|
||||||
|
$pst_amount = $subtotal * $invoice->pst_rate / 100;
|
||||||
|
$gst_amount = $subtotal * $invoice->gst_rate / 100;
|
||||||
|
$total = $subtotal + $pst_amount + $gst_amount;
|
||||||
|
|
||||||
|
$invoice->refresh();
|
||||||
|
|
||||||
|
/** Assertions **/
|
||||||
|
$this->assertSame($invoice->subtotal, $subtotal);
|
||||||
|
$this->assertSame($invoice->gst_amount, $gst_amount);
|
||||||
|
$this->assertSame($invoice->pst_amount, $pst_amount);
|
||||||
|
$this->assertSame($invoice->total, $total);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly re-calculates tax amounts when a product service gets removed from an order', function () {
|
||||||
|
/** Setup **/
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$invoice = Invoice::factory(['has_gst' => true, 'has_pst' => true])->create(['customer_id' => $customer->id]);
|
||||||
|
$order = Order::factory()->for($customer)->create();
|
||||||
|
|
||||||
|
$order->invoice()->associate($invoice)->save();
|
||||||
|
|
||||||
|
$productService1 = ProductService::factory()->create(['order_id' => $order->id]);
|
||||||
|
$productService2 = ProductService::factory()->create(['order_id' => $order->id]);
|
||||||
|
|
||||||
|
/** Action **/
|
||||||
|
$productService2->delete(); // Remove one product service
|
||||||
|
|
||||||
|
$order->refresh();
|
||||||
|
$invoice->refresh();
|
||||||
|
|
||||||
|
$subtotal = $order->total_service_price;
|
||||||
|
$pstAmount = $subtotal * $invoice->pst_rate / 100;
|
||||||
|
$gstAmount = $subtotal * $invoice->gst_rate / 100;
|
||||||
|
$total = $subtotal + $pstAmount + $gstAmount;
|
||||||
|
|
||||||
|
/** Assertions **/
|
||||||
|
$this->assertSame($invoice->subtotal, $subtotal);
|
||||||
|
$this->assertSame($invoice->pst_amount, $pstAmount);
|
||||||
|
$this->assertSame($invoice->gst_amount, $gstAmount);
|
||||||
|
$this->assertSame($invoice->total, $total);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly re-calculates tax amounts when an order gets added', function () {
|
||||||
|
/** Setup **/
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$order = Order::factory()->for($customer)->create();
|
||||||
|
|
||||||
|
ProductService::factory()->count(3)->create(['order_id' => $order->id]);
|
||||||
|
|
||||||
|
$invoice = Invoice::factory(['has_gst' => true, 'has_pst' => true])->create(['customer_id' => $customer->id]);
|
||||||
|
$order->invoice()->associate($invoice)->save();
|
||||||
|
|
||||||
|
/** Action **/
|
||||||
|
$newOrder = Order::factory()->for($customer)->create();
|
||||||
|
ProductService::factory()->count(3)->create(['order_id' => $newOrder->id]);
|
||||||
|
|
||||||
|
$newOrder->invoice()->associate($invoice)->save();
|
||||||
|
|
||||||
|
$subtotal = $order->total_service_price + $newOrder->total_service_price;
|
||||||
|
$pst_amount = $subtotal * $invoice->pst_rate / 100;
|
||||||
|
$gst_amount = $subtotal * $invoice->gst_rate / 100;
|
||||||
|
$total = $subtotal + $pst_amount + $gst_amount;
|
||||||
|
|
||||||
|
/** Assertions **/
|
||||||
|
$this->assertSame($invoice->orders->count(), 2);
|
||||||
|
$this->assertSame($invoice->subtotal, $subtotal);
|
||||||
|
$this->assertSame($invoice->gst_amount, $gst_amount);
|
||||||
|
$this->assertSame($invoice->pst_amount, $pst_amount);
|
||||||
|
$this->assertSame($invoice->total, $total);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly re-calculates tax amounts when an order gets removed', function () {
|
||||||
|
/** Setup **/
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$orders = Order::factory()->for($customer)
|
||||||
|
->has(ProductService::factory()->count(2))
|
||||||
|
->count(2)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$invoice = Invoice::factory(['has_gst' => true, 'has_pst' => true])->create(['customer_id' => $customer->id]);
|
||||||
|
$orders->each(fn (Order $order) => $order->invoice()->associate($invoice)->save());
|
||||||
|
|
||||||
|
$orders[0]->delete();
|
||||||
|
|
||||||
|
/** Action **/
|
||||||
|
$subtotal = $orders[1]->total_service_price;
|
||||||
|
$pst_amount = $subtotal * $invoice->pst_rate / 100;
|
||||||
|
$gst_amount = $subtotal * $invoice->gst_rate / 100;
|
||||||
|
$total = $subtotal + $pst_amount + $gst_amount;
|
||||||
|
|
||||||
|
/** Assertions **/
|
||||||
|
$this->assertSame($invoice->orders->count(), 1);
|
||||||
|
$this->assertSame($invoice->subtotal, $subtotal);
|
||||||
|
$this->assertSame($invoice->gst_amount, $gst_amount);
|
||||||
|
$this->assertSame($invoice->pst_amount, $pst_amount);
|
||||||
|
$this->assertSame($invoice->total, $total);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly re-calculates tax amounts when an order\'s product service\'s quantity gets changed', function () {
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$order = Order::factory()
|
||||||
|
->for($customer)
|
||||||
|
->has(ProductService::factory()->count(3))
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$invoice = Invoice::factory(['has_gst' => true, 'has_pst' => true])->create(['customer_id' => $customer->id]);
|
||||||
|
$order->invoice()->associate($invoice)->save();
|
||||||
|
|
||||||
|
$service = $invoice->orders->first()->productServices->first();
|
||||||
|
$service->amount += 10;
|
||||||
|
$service->save();
|
||||||
|
|
||||||
|
$invoice->refresh();
|
||||||
|
$order->refresh();
|
||||||
|
|
||||||
|
/** Action **/
|
||||||
|
$subtotal = $order->total_service_price;
|
||||||
|
$pst_amount = $subtotal * $invoice->pst_rate / 100;
|
||||||
|
$gst_amount = $subtotal * $invoice->gst_rate / 100;
|
||||||
|
$total = $subtotal + $pst_amount + $gst_amount;
|
||||||
|
|
||||||
|
/** Assertions **/
|
||||||
|
$this->assertSame($invoice->subtotal, $subtotal);
|
||||||
|
$this->assertSame($invoice->gst_amount, $gst_amount);
|
||||||
|
$this->assertSame($invoice->pst_amount, $pst_amount);
|
||||||
|
$this->assertSame($invoice->total, $total);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('correctly re-calculates tax amounts when an order\'s product service\'s amount gets changed', function () {
|
||||||
|
$customer = Customer::factory()->create();
|
||||||
|
$order = Order::factory()
|
||||||
|
->for($customer)
|
||||||
|
->has(ProductService::factory()->count(3))
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$invoice = Invoice::factory(['has_gst' => true, 'has_pst' => true])->create(['customer_id' => $customer->id]);
|
||||||
|
$order->invoice()->associate($invoice)->save();
|
||||||
|
|
||||||
|
$service = $invoice->orders->first()->productServices->first();
|
||||||
|
$service->amount_price += 10;
|
||||||
|
$service->save();
|
||||||
|
|
||||||
|
$invoice->refresh();
|
||||||
|
$order->refresh();
|
||||||
|
|
||||||
|
/** Action **/
|
||||||
|
$subtotal = $order->total_service_price;
|
||||||
|
$pst_amount = $subtotal * $invoice->pst_rate / 100;
|
||||||
|
$gst_amount = $subtotal * $invoice->gst_rate / 100;
|
||||||
|
$total = $subtotal + $pst_amount + $gst_amount;
|
||||||
|
|
||||||
|
/** Assertions **/
|
||||||
|
$this->assertSame($invoice->subtotal, $subtotal);
|
||||||
|
$this->assertSame($invoice->gst_amount, $gst_amount);
|
||||||
|
$this->assertSame($invoice->pst_amount, $pst_amount);
|
||||||
|
$this->assertSame($invoice->total, $total);
|
||||||
|
});
|
@ -15,14 +15,17 @@
|
|||||||
use App\Models\ServiceFile;
|
use App\Models\ServiceFile;
|
||||||
use App\Models\ServiceType;
|
use App\Models\ServiceType;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||||
|
|
||||||
use function Pest\Livewire\livewire;
|
use function Pest\Livewire\livewire;
|
||||||
|
|
||||||
|
uses(DatabaseMigrations::class);
|
||||||
|
|
||||||
it('can create an order and all associated models and save it to the database', function () {
|
it('can create an order and all associated models and save it to the database', function () {
|
||||||
$type = fake()->randomElement(OrderType::cases());
|
$type = fake()->randomElement(OrderType::cases());
|
||||||
$status = fake()->randomElement(OrderStatus::cases());
|
$status = fake()->randomElement(OrderStatus::cases());
|
||||||
$customer = Customer::factory()->create();
|
$customer = Customer::factory()->create();
|
||||||
$contact = Contact::factory()->for($customer)->create();
|
// $contact = Contact::factory()->for($customer)->create();
|
||||||
$attributes = array_map(fn ($case) => $case->value, OrderAttributes::cases());
|
$attributes = array_map(fn ($case) => $case->value, OrderAttributes::cases());
|
||||||
$orderDate = today();
|
$orderDate = today();
|
||||||
$dueDate = today()->addDays(10);
|
$dueDate = today()->addDays(10);
|
||||||
@ -144,15 +147,15 @@
|
|||||||
$status,
|
$status,
|
||||||
$dueDate->format('Y-m-d'),
|
$dueDate->format('Y-m-d'),
|
||||||
'Notes go here! Here\'s the notes!',
|
'Notes go here! Here\'s the notes!',
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
1,
|
true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Order Products
|
// Order Products
|
Loading…
x
Reference in New Issue
Block a user