diff --git a/.gitignore b/.gitignore
index d45aa27..6e7f083 100644
--- a/.gitignore
+++ b/.gitignore
@@ -107,3 +107,4 @@ fabric.properties
.directory
.directory
public
+_ide_helper.php
diff --git a/app/Filament/Admin/Resources/InvoiceReportResource.php b/app/Filament/Admin/Resources/InvoiceReportResource.php
index 7efc31e..3df58fe 100644
--- a/app/Filament/Admin/Resources/InvoiceReportResource.php
+++ b/app/Filament/Admin/Resources/InvoiceReportResource.php
@@ -75,9 +75,12 @@ public static function table(Table $table): Table
->label('End Date')
->date('Y-m-d'),
TextColumn::make('total')
- ->label('Balance Due')
->weight(FontWeight::Bold)
->money(),
+ TextColumn::make('balance')
+ ->weight(FontWeight::Bold)
+ ->money(),
+ // ->getStateUsing(fn (Invoice))
])
->defaultSort('id', 'desc');
}
diff --git a/app/Filament/Admin/Resources/InvoiceResource.php b/app/Filament/Admin/Resources/InvoiceResource.php
index 3dcbde0..fa0e94b 100644
--- a/app/Filament/Admin/Resources/InvoiceResource.php
+++ b/app/Filament/Admin/Resources/InvoiceResource.php
@@ -112,7 +112,7 @@ public static function form(Form $form): Form
->content(fn (Invoice $record): ?string => $record->internal_id),
Placeholder::make('Amounts')
- ->content(fn (Invoice $record): ?string => 'Total: $'.$record->total.', balance: $'.$record->remainingBalance()),
+ ->content(fn (Invoice $record): ?string => 'Total: $'.number_format($record->total, 2).', balance: $'.number_format($record->remainingBalance(), 2)),
Placeholder::make('Tax Rates when created')
->content(fn (Invoice $record): ?string => $record->gst_rate.'% GST, '.$record->pst_rate.'% PST, '.$record->hst_rate.'% HST'),
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index 7ccc41f..9052d1f 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -45,7 +45,8 @@ class Invoice extends Model
'has_gst' => 'boolean',
'has_pst' => 'boolean',
'has_hst' => 'boolean',
- 'date' => 'datetime',
+ 'date' => 'date',
+ 'due_date' => 'date',
'status' => InvoiceStatus::class,
'subtotal' => 'float',
'pst_amount' => 'float',
diff --git a/app/Models/InvoiceReport.php b/app/Models/InvoiceReport.php
index e2ddd65..3570faf 100644
--- a/app/Models/InvoiceReport.php
+++ b/app/Models/InvoiceReport.php
@@ -25,6 +25,7 @@ class InvoiceReport extends Model
protected $appends = [
'total',
+ 'balance',
];
protected $casts = [
@@ -61,6 +62,11 @@ public function getTotalAttribute()
return $this->invoices()->sum('total');
}
+ public function getBalanceAttribute()
+ {
+ return $this->invoices->sum(fn (Invoice $invoice) => $invoice->remainingBalance());
+ }
+
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class);
diff --git a/app/Observers/InvoiceObserver.php b/app/Observers/InvoiceObserver.php
index b5d86a3..2c86548 100644
--- a/app/Observers/InvoiceObserver.php
+++ b/app/Observers/InvoiceObserver.php
@@ -23,7 +23,7 @@ public function creating(Invoice $invoice): void
public function created(Invoice $invoice): void
{
- $invoice->internal_id = 'INV4'.str_pad($invoice->id, 5, '0', STR_PAD_LEFT);
+ $invoice->internal_id = 'INV4'.str_pad($invoice->id, 4, '0', STR_PAD_LEFT);
$invoice->saveQuietly();
$invoice->calculateTotals();
diff --git a/composer.json b/composer.json
index ff54a78..e0e7c17 100644
--- a/composer.json
+++ b/composer.json
@@ -17,6 +17,7 @@
"spatie/laravel-pdf": "^1.5"
},
"require-dev": {
+ "barryvdh/laravel-ide-helper": "^3.5",
"fakerphp/faker": "^1.23",
"larastan/larastan": "^2.0",
"laravel/pint": "^1.17",
diff --git a/composer.lock b/composer.lock
index 38b51ca..6ec0ef3 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "0c966bb4f2402b99c0398908b98716ba",
+ "content-hash": "766975bfee107a4085b03319fa3b9276",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -7955,6 +7955,152 @@
}
],
"packages-dev": [
+ {
+ "name": "barryvdh/laravel-ide-helper",
+ "version": "v3.5.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/barryvdh/laravel-ide-helper.git",
+ "reference": "980a87e250fc2a7558bc46e07f61c7594500ea53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/980a87e250fc2a7558bc46e07f61c7594500ea53",
+ "reference": "980a87e250fc2a7558bc46e07f61c7594500ea53",
+ "shasum": ""
+ },
+ "require": {
+ "barryvdh/reflection-docblock": "^2.3",
+ "composer/class-map-generator": "^1.0",
+ "ext-json": "*",
+ "illuminate/console": "^11.15",
+ "illuminate/database": "^11.15",
+ "illuminate/filesystem": "^11.15",
+ "illuminate/support": "^11.15",
+ "php": "^8.2"
+ },
+ "require-dev": {
+ "ext-pdo_sqlite": "*",
+ "friendsofphp/php-cs-fixer": "^3",
+ "illuminate/config": "^11.15",
+ "illuminate/view": "^11.15",
+ "mockery/mockery": "^1.4",
+ "orchestra/testbench": "^9.2",
+ "phpunit/phpunit": "^10.5",
+ "spatie/phpunit-snapshot-assertions": "^4 || ^5",
+ "vimeo/psalm": "^5.4",
+ "vlucas/phpdotenv": "^5"
+ },
+ "suggest": {
+ "illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
+ ]
+ },
+ "branch-alias": {
+ "dev-master": "3.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Barryvdh\\LaravelIdeHelper\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Barry vd. Heuvel",
+ "email": "barryvdh@gmail.com"
+ }
+ ],
+ "description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.",
+ "keywords": [
+ "autocomplete",
+ "codeintel",
+ "dev",
+ "helper",
+ "ide",
+ "laravel",
+ "netbeans",
+ "phpdoc",
+ "phpstorm",
+ "sublime"
+ ],
+ "support": {
+ "issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
+ "source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.5.4"
+ },
+ "funding": [
+ {
+ "url": "https://fruitcake.nl",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/barryvdh",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-14T09:07:00+00:00"
+ },
+ {
+ "name": "barryvdh/reflection-docblock",
+ "version": "v2.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/barryvdh/ReflectionDocBlock.git",
+ "reference": "b6ff9f93603561f50e53b64310495d20b8dff5d8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/b6ff9f93603561f50e53b64310495d20b8dff5d8",
+ "reference": "b6ff9f93603561f50e53b64310495d20b8dff5d8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.14|^9"
+ },
+ "suggest": {
+ "dflydev/markdown": "~1.0",
+ "erusev/parsedown": "~1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Barryvdh": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "mike.vanriel@naenius.com"
+ }
+ ],
+ "support": {
+ "source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.3.1"
+ },
+ "time": "2025-01-18T19:26:32+00:00"
+ },
{
"name": "brianium/paratest",
"version": "v7.7.0",
@@ -8048,6 +8194,158 @@
],
"time": "2024-12-11T14:50:44+00:00"
},
+ {
+ "name": "composer/class-map-generator",
+ "version": "1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/class-map-generator.git",
+ "reference": "ffe442c5974c44a9343e37a0abcb1cc37319f5b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/class-map-generator/zipball/ffe442c5974c44a9343e37a0abcb1cc37319f5b9",
+ "reference": "ffe442c5974c44a9343e37a0abcb1cc37319f5b9",
+ "shasum": ""
+ },
+ "require": {
+ "composer/pcre": "^2.1 || ^3.1",
+ "php": "^7.2 || ^8.0",
+ "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.12 || ^2",
+ "phpstan/phpstan-deprecation-rules": "^1 || ^2",
+ "phpstan/phpstan-phpunit": "^1 || ^2",
+ "phpstan/phpstan-strict-rules": "^1.1 || ^2",
+ "phpunit/phpunit": "^8",
+ "symfony/filesystem": "^5.4 || ^6"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\ClassMapGenerator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "https://seld.be"
+ }
+ ],
+ "description": "Utilities to scan PHP code and generate class maps.",
+ "keywords": [
+ "classmap"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/class-map-generator/issues",
+ "source": "https://github.com/composer/class-map-generator/tree/1.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-02-05T10:05:34+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "3.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<1.11.10"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.12 || ^2",
+ "phpstan/phpstan-strict-rules": "^1 || ^2",
+ "phpunit/phpunit": "^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Pcre\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
+ "keywords": [
+ "PCRE",
+ "preg",
+ "regex",
+ "regular expression"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/pcre/issues",
+ "source": "https://github.com/composer/pcre/tree/3.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-12T16:29:46+00:00"
+ },
{
"name": "fakerphp/faker",
"version": "v1.24.1",
diff --git a/gesture-evolution b/gesture-evolution
deleted file mode 160000
index 65fe788..0000000
--- a/gesture-evolution
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 65fe7885813f3d4dc87f0fc52495247592fe276c
diff --git a/public/invoicereport-tnr0005.pdf b/public/invoicereport-tnr0005.pdf
index 6072b7d..79dffc5 100644
Binary files a/public/invoicereport-tnr0005.pdf and b/public/invoicereport-tnr0005.pdf differ
diff --git a/resources/views/pdf/invoice-report.blade.php b/resources/views/pdf/invoice-report.blade.php
index eaef590..184b70b 100644
--- a/resources/views/pdf/invoice-report.blade.php
+++ b/resources/views/pdf/invoice-report.blade.php
@@ -72,7 +72,7 @@
- ${{number_format($invoiceReport->total, 2)}}
+ ${{number_format($invoiceReport->balance, 2)}}
@@ -95,10 +95,10 @@
{{Date::make($invoice->created_at)->format('Y-m-d')}} |
{{$invoice->internal_id}} |
- ${{$invoice->subtotal}} |
+ ${{number_format($invoice->subtotal, 2)}} |
${{number_format($invoice->gst_amount, 2)}} |
{{!$invoice->pst_amount ? '-' : '$'.number_format($invoice->pst_amount, 2)}} |
- ${{$invoice->total}} |
+ ${{number_format($invoice->remainingBalance(), 2)}} |
{{$invoice->status->value}} |
@endforeach
diff --git a/resources/views/pdf/invoice.blade.php b/resources/views/pdf/invoice.blade.php
index 78a1aac..c2645c1 100644
--- a/resources/views/pdf/invoice.blade.php
+++ b/resources/views/pdf/invoice.blade.php
@@ -30,9 +30,9 @@
BILL TO
- {{$invoice->customer->company_name}}
- {{$invoice->customer->billing_address_line_1}}
- {{$invoice->customer->billing_address_line_2}}
+ {{$invoice->customer->company_name ?? ''}}
+ {{$invoice->customer->billing_address_line_1 ?? ''}}
+ {{$invoice->customer->billing_address_line_2 ?? ''}}
@@ -50,13 +50,19 @@
- {{$invoice->internal_id}}
+ @if(isset($invoice->internal_id))
+ {{$invoice->internal_id}}
+ @endif
- {{$invoice->date}}
+ @if(isset($invoice->date))
+ {{$invoice->date->format('Y-m-d')}}
+ @endif
- {{$invoice->due_date}}
+ @if(isset($invoice->due_date))
+ {{$invoice->due_date->format('Y-m-d')}}
+ @endif
@@ -110,7 +116,7 @@
${{number_format($invoice->pst_amount, 2)}}
${{number_format($invoice->total, 2)}}
- ${{number_format($invoice->total, 2)}}
+ ${{number_format($invoice->remainingBalance(), 2)}}
diff --git a/resources/views/pdf/order.blade.php b/resources/views/pdf/order.blade.php
index 88e8868..bc4820f 100644
--- a/resources/views/pdf/order.blade.php
+++ b/resources/views/pdf/order.blade.php
@@ -1,6 +1,6 @@
@extends('layouts.pdf')
-
Top Notch Order {{$order->internal_po}}
+
Top Notch Order {{$order->internal_po ?? ''}}
@@ -135,7 +135,7 @@
-
+
SKU |
Product Name |
@@ -183,7 +183,7 @@
-
+
Placement & Service |
Logo Name & Instructions |
@@ -201,7 +201,7 @@
-
+
{{$service->serviceFile->code}}
diff --git a/tests/Unit/InvoiceTest.php b/tests/Unit/InvoiceTest.php
index 4fc13fd..bc38b64 100644
--- a/tests/Unit/InvoiceTest.php
+++ b/tests/Unit/InvoiceTest.php
@@ -50,7 +50,7 @@
->assertHasNoErrors();
$this->assertDatabaseHas('invoices', [
- 'internal_id' => 'INV400001',
+ 'internal_id' => 'INV40001',
'customer_id' => $formData['customer_id'],
'status' => $formData['status'],
'has_gst' => $formData['has_gst'],
@@ -61,7 +61,7 @@
'hst_rate' => $hst_rate,
]);
- $invoice = Invoice::where('internal_id', 'INV400001')->firstOrFail();
+ $invoice = Invoice::where('internal_id', 'INV40001')->firstOrFail();
$this->assertEquals($invoice->orders->isEmpty(), true);
});
@@ -77,7 +77,6 @@
});
it('correctly calculates tax amounts', function () {
- /** Setup **/
$customer = Customer::factory()->create();
$order = Order::factory()
->for($customer)
@@ -87,13 +86,11 @@
$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);
@@ -101,7 +98,6 @@
});
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();
@@ -110,7 +106,6 @@
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;
@@ -118,7 +113,6 @@
$invoice->refresh();
- /** Assertions **/
$this->assertSame($invoice->subtotal, $subtotal);
$this->assertSame($invoice->gst_amount, $gst_amount);
$this->assertSame($invoice->pst_amount, $pst_amount);
@@ -126,7 +120,6 @@
});
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();
@@ -136,8 +129,7 @@
$productService1 = ProductService::factory()->create(['order_id' => $order->id]);
$productService2 = ProductService::factory()->create(['order_id' => $order->id]);
- /** Action **/
- $productService2->delete(); // Remove one product service
+ $productService2->delete();
$order->refresh();
$invoice->refresh();
@@ -147,7 +139,6 @@
$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);
@@ -155,7 +146,6 @@
});
it('correctly re-calculates tax amounts when an order gets added', function () {
- /** Setup **/
$customer = Customer::factory()->create();
$order = Order::factory()->for($customer)->create();
@@ -164,7 +154,6 @@
$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]);
@@ -175,7 +164,6 @@
$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);
@@ -184,7 +172,6 @@
});
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))
@@ -196,13 +183,11 @@
$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);
@@ -227,13 +212,11 @@
$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);
@@ -257,13 +240,11 @@
$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);