From 4846c9bccc7dcfb784b5d18506f4b4b0b4f1aceb Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 15 Jun 2020 09:34:18 +1000 Subject: [PATCH] Working on reversing an invoice cancellation --- app/Models/Credit.php | 1 + app/Models/Currency.php | 1 + app/Models/Invoice.php | 1 + app/Models/Quote.php | 1 + app/Models/RecurringInvoice.php | 1 + app/Models/RecurringQuote.php | 1 + app/Services/Invoice/HandleCancellation.php | 55 +++++++++++++++++++ app/Services/Invoice/HandleReversal.php | 12 +++- app/Services/Invoice/InvoiceService.php | 7 +++ app/Utils/Traits/Invoice/ActionsInvoice.php | 17 +++++- .../2014_10_13_000000_create_users_table.php | 6 +- 11 files changed, 96 insertions(+), 7 deletions(-) diff --git a/app/Models/Credit.php b/app/Models/Credit.php index a07a422dc0d1..abb776c12e1d 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -70,6 +70,7 @@ class Credit extends BaseModel protected $casts = [ 'line_items' => 'object', + 'backup' => 'object', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', diff --git a/app/Models/Currency.php b/app/Models/Currency.php index 9f945906a887..809766520d17 100644 --- a/app/Models/Currency.php +++ b/app/Models/Currency.php @@ -25,5 +25,6 @@ class Currency extends StaticModel 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', + 'precision' => 'integer', ]; } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index ab3e1babfbf2..edf9766bb51f 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -103,6 +103,7 @@ class Invoice extends BaseModel protected $casts = [ 'line_items' => 'object', + 'backup' => 'object', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', diff --git a/app/Models/Quote.php b/app/Models/Quote.php index c8146faa27e5..362829eefede 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -76,6 +76,7 @@ class Quote extends BaseModel 'due_date' => 'date:Y-m-d', 'partial_due_date' => 'date:Y-m-d', 'line_items' => 'object', + 'backup' => 'object', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 5c4abe763209..8d16415ca94f 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -101,6 +101,7 @@ class RecurringInvoice extends BaseModel protected $casts = [ 'settings' => 'object', 'line_items' => 'object', + 'backup' => 'object', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', diff --git a/app/Models/RecurringQuote.php b/app/Models/RecurringQuote.php index 60310b0e8dd6..7d3fd0c87363 100644 --- a/app/Models/RecurringQuote.php +++ b/app/Models/RecurringQuote.php @@ -83,6 +83,7 @@ class RecurringQuote extends BaseModel protected $casts = [ 'line_items' => 'object', + 'backup' => 'object', 'settings' => 'object', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', diff --git a/app/Services/Invoice/HandleCancellation.php b/app/Services/Invoice/HandleCancellation.php index 867cd189e2d7..50e9d8db5863 100644 --- a/app/Services/Invoice/HandleCancellation.php +++ b/app/Services/Invoice/HandleCancellation.php @@ -45,6 +45,9 @@ class HandleCancellation extends AbstractService } $adjustment = $this->invoice->balance*-1; + + $this->backupCancellation($adjustment); + //set invoice balance to 0 $this->invoice->ledger()->updateInvoiceBalance($adjustment, "Invoice cancellation"); @@ -56,6 +59,58 @@ class HandleCancellation extends AbstractService event(new InvoiceWasCancelled($this->invoice)); + return $this->invoice; } + + public function reverse() + { + + $cancellation = $this->backup->cancellation; + + $adjustment = $cancellation->adjustment*-1; + + $this->invoice->ledger()->updateInvoiceBalance($adjustment, "Invoice cancellation REVERSAL"); + + /* Reverse the invoice status and balance */ + $this->invoice->balance += $adjustment; + $this->invoice->status_id = $cancellation->status_id; + + $this->invoice->client->service()->updateBalance($adjustment)->save(); + + /* Pop the cancellation out of the backup*/ + $backup = $this->invoice->backup; + unset($backup->cancellation); + $this->invoice->backup = $backup; + $this->invoice->save(); + + return $this->invoice; + + } + + /** + * Backup the cancellation in case we ever need to reverse it. + * + * @param float $adjustment The amount the balance has been reduced by to cancel the invoice + * @return void + */ + private function backupCancellation($adjustment) + { + + if(!is_object($this->invoice->backup)){ + $backup = new \stdClass; + $this->invoice->backup = $backup; + } + + $cancellation = new \stdClass; + $cancellation->adjustment = $adjustment; + $cancellation->status_id = $this->invoice->status_id; + + $invoice_backup = $this->invoice->backup; + $invoice_backup->cancellation = $cancellation; + + $this->invoice->backup = $invoice_backup; + $this->invoice->save(); + + } } diff --git a/app/Services/Invoice/HandleReversal.php b/app/Services/Invoice/HandleReversal.php index a72d313772a7..55f5f72b940c 100644 --- a/app/Services/Invoice/HandleReversal.php +++ b/app/Services/Invoice/HandleReversal.php @@ -44,6 +44,10 @@ class HandleReversal extends AbstractService return $this->invoice; } + if($this->invoice->status_id == Invoice::STATUS_CANCELLED) + $this->invoice->service()->reverseCancellation(); + + /*Consider if we have just cancelled the invoice here... this is broken!*/ $balance_remaining = $this->invoice->balance; $total_paid = $this->invoice->amount - $this->invoice->balance; @@ -88,10 +92,12 @@ class HandleReversal extends AbstractService $credit->service()->markSent()->save(); } - /* Set invoice balance to 0 */ - $this->invoice->ledger()->updateInvoiceBalance($balance_remaining*-1, $notes)->save(); - $this->invoice->balance= 0; + /* Set invoice balance to 0 */ + if($this->invoice->balance != 0) + $this->invoice->ledger()->updateInvoiceBalance($balance_remaining*-1, $notes)->save(); + + $this->invoice->balance=0; /* Set invoice status to reversed... somehow*/ $this->invoice->service()->setStatus(Invoice::STATUS_REVERSED)->save(); diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index 589f6a3e24c7..f0b3158618bd 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -129,6 +129,13 @@ class InvoiceService return $this; } + public function reverseCancellation() + { + $this->invoice = (new HandleCancellation($this->invoice))->reverse(); + + return $this; + } + public function markViewed() { $this->invoice->last_viewed = Carbon::now()->format('Y-m-d H:i'); diff --git a/app/Utils/Traits/Invoice/ActionsInvoice.php b/app/Utils/Traits/Invoice/ActionsInvoice.php index 78c344def9de..fb523b8227c1 100644 --- a/app/Utils/Traits/Invoice/ActionsInvoice.php +++ b/app/Utils/Traits/Invoice/ActionsInvoice.php @@ -17,7 +17,10 @@ trait ActionsInvoice { public function invoiceDeletable($invoice) :bool { - if ($invoice->status_id <= Invoice::STATUS_SENT && $invoice->is_deleted == false && $invoice->deleted_at == null && $invoice->balance == 0) { + if ($invoice->status_id <= Invoice::STATUS_SENT && + $invoice->is_deleted == false && + $invoice->deleted_at == null && + $invoice->balance == 0) { return true; } @@ -26,7 +29,10 @@ trait ActionsInvoice public function invoiceCancellable($invoice) :bool { - if (($invoice->status_id == Invoice::STATUS_SENT || $invoice->status_id == Invoice::STATUS_PARTIAL) && $invoice->is_deleted == false && $invoice->deleted_at == null) { + if (($invoice->status_id == Invoice::STATUS_SENT || + $invoice->status_id == Invoice::STATUS_PARTIAL) && + $invoice->is_deleted == false && + $invoice->deleted_at == null) { return true; } @@ -35,7 +41,12 @@ trait ActionsInvoice public function invoiceReversable($invoice) :bool { - if (($invoice->status_id == Invoice::STATUS_SENT || $invoice->status_id == Invoice::STATUS_PARTIAL || $invoice->status_id == Invoice::STATUS_PAID) && $invoice->is_deleted == false && $invoice->deleted_at == null) { + if (($invoice->status_id == Invoice::STATUS_SENT || + $invoice->status_id == Invoice::STATUS_PARTIAL || + $invoice->status_id == Invoice::STATUS_CANCELLED || + $invoice->status_id == Invoice::STATUS_PAID) && + $invoice->is_deleted == false && + $invoice->deleted_at == null) { return true; } diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php index 5c773db50605..35e22822d1c1 100644 --- a/database/migrations/2014_10_13_000000_create_users_table.php +++ b/database/migrations/2014_10_13_000000_create_users_table.php @@ -454,6 +454,7 @@ class CreateUsersTable extends Migration $t->boolean('is_deleted')->default(false); $t->mediumText('line_items')->nullable(); + $t->mediumText('backup')->nullable(); $t->text('footer')->nullable(); $t->text('public_notes')->nullable(); $t->text('private_notes')->nullable(); @@ -530,6 +531,7 @@ class CreateUsersTable extends Migration $t->boolean('is_deleted')->default(false); $t->mediumText('line_items')->nullable(); + $t->mediumText('backup')->nullable(); $t->text('footer')->nullable(); $t->text('public_notes')->nullable(); $t->text('private_notes')->nullable(); @@ -633,6 +635,7 @@ class CreateUsersTable extends Migration $t->boolean('is_deleted')->default(false); $t->mediumText('line_items')->nullable(); + $t->mediumText('backup')->nullable(); $t->text('footer')->nullable(); $t->text('public_notes')->nullable(); $t->text('private_notes')->nullable(); @@ -699,6 +702,7 @@ class CreateUsersTable extends Migration $t->boolean('is_deleted')->default(false); $t->mediumText('line_items')->nullable(); + $t->mediumText('backup')->nullable(); $t->text('footer')->nullable(); $t->text('public_notes')->nullable(); @@ -770,7 +774,7 @@ class CreateUsersTable extends Migration $t->boolean('is_deleted')->default(false); $t->mediumText('line_items')->nullable(); - + $t->mediumText('backup')->nullable(); $t->text('footer')->nullable(); $t->text('public_notes')->nullable();