diff --git a/app/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php index dad299106c..fb100b9dd1 100644 --- a/app/Http/Controllers/PreviewController.php +++ b/app/Http/Controllers/PreviewController.php @@ -497,8 +497,8 @@ class PreviewController extends BaseController 'variables' => $html->generateLabelsAndValues(), 'process_markdown' => $invoice->client->company->markdown_enabled, 'options' => [ - 'client' => $invitation->invoice->client, - 'entity' => $invitation->invoice, + 'client' => $invoice->client, + 'entity' => $invoice, ] ]; diff --git a/app/Http/Requests/Invoice/StoreInvoiceRequest.php b/app/Http/Requests/Invoice/StoreInvoiceRequest.php index 10ab3a1765..bdcd350177 100644 --- a/app/Http/Requests/Invoice/StoreInvoiceRequest.php +++ b/app/Http/Requests/Invoice/StoreInvoiceRequest.php @@ -73,6 +73,8 @@ class StoreInvoiceRequest extends Request $rules['tax_name2'] = 'bail|sometimes|string|nullable'; $rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['exchange_rate'] = 'bail|sometimes|numeric'; + $rules['partial'] = 'bail|sometimes|nullable|numeric'; + $rules['partial_due_date'] = 'bail|sometimes|required_if:partial,>0|date'; return $rules; } diff --git a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php index 859924029a..eef95d02cf 100644 --- a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php +++ b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php @@ -76,6 +76,8 @@ class UpdateInvoiceRequest extends Request $rules['tax_name3'] = 'bail|sometimes|string|nullable'; $rules['status_id'] = 'bail|sometimes|not_in:5'; //do not allow cancelled invoices to be modfified. $rules['exchange_rate'] = 'bail|sometimes|numeric'; + $rules['partial'] = 'bail|sometimes|nullable|numeric'; + $rules['partial_due_date'] = 'bail|sometimes|required_if:partial,>0|date'; return $rules; } diff --git a/app/Repositories/PaymentRepository.php b/app/Repositories/PaymentRepository.php index f11a6f3ceb..49e5d03498 100644 --- a/app/Repositories/PaymentRepository.php +++ b/app/Repositories/PaymentRepository.php @@ -139,11 +139,8 @@ class PaymentRepository extends BaseRepository $invoices = Invoice::withTrashed()->whereIn('id', array_column($data['invoices'], 'invoice_id'))->get(); - // $payment->invoices()->saveMany($invoices); //25-06-2023 - //todo optimize this into a single query foreach ($data['invoices'] as $paid_invoice) { - // $invoice = Invoice::withTrashed()->whereId($paid_invoice['invoice_id'])->first(); $invoice = $invoices->firstWhere('id', $paid_invoice['invoice_id']); if ($invoice) { diff --git a/app/Services/Invoice/ApplyPayment.php b/app/Services/Invoice/ApplyPayment.php index de4e5791b5..1b87d7214e 100644 --- a/app/Services/Invoice/ApplyPayment.php +++ b/app/Services/Invoice/ApplyPayment.php @@ -47,8 +47,17 @@ class ApplyPayment extends AbstractService $amount_paid = $this->payment_amount * -1; - $this->invoice->service()->clearPartial()->setDueDate()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($amount_paid)->save(); + $this->invoice + ->service() + ->clearPartial() + ->setDueDate() + ->setStatus(Invoice::STATUS_PARTIAL) + ->updateBalance($amount_paid) + ->save(); } + + $this->invoice->service()->checkReminderStatus()->save(); + } else { if ($this->payment_amount == $this->invoice->balance) { $amount_paid = $this->payment_amount * -1; @@ -84,9 +93,6 @@ class ApplyPayment extends AbstractService /* Update Pivot Record amount */ $this->payment->invoices->each(function ($inv) use ($amount_paid) { if ($inv->id == $this->invoice->id) { - // $inv->pivot->amount = ($amount_paid * -1); - // $inv->pivot->save(); - //25-06-2023 $inv->paid_to_date += floatval($amount_paid * -1); $inv->save(); } @@ -96,7 +102,6 @@ class ApplyPayment extends AbstractService ->service() ->applyNumber() ->workFlow() - // ->deletePdf() ->save(); return $this->invoice; diff --git a/app/Services/Invoice/ApplyPaymentAmount.php b/app/Services/Invoice/ApplyPaymentAmount.php index f075a3992b..b200148538 100644 --- a/app/Services/Invoice/ApplyPaymentAmount.php +++ b/app/Services/Invoice/ApplyPaymentAmount.php @@ -31,7 +31,7 @@ class ApplyPaymentAmount extends AbstractService } public function run() - { + {nlog("apply payment amount"); if ($this->invoice->status_id == Invoice::STATUS_DRAFT) { $this->invoice = $this->invoice->service()->markSent()->save(); } @@ -65,15 +65,28 @@ class ApplyPaymentAmount extends AbstractService 'amount' => $payment->amount, ]); - $this->invoice->next_send_date = null; - $this->invoice->service() + $has_partial = $this->invoice->hasPartial(); + + $invoice_service = $this->invoice->service() ->setExchangeRate() ->updateBalance($payment->amount * -1) ->updatePaidToDate($payment->amount) ->setCalculatedStatus() - ->applyNumber() - ->save(); + ->applyNumber(); + + nlog("check for partials"); + + if ($has_partial) { + nlog("has partial"); + $invoice_service->checkReminderStatus(); + } + + if($this->invoice->balance == 0){ + $this->invoice->next_send_date = null; + } + + $this->invoice = $invoice_service->save(); $this->invoice ->client diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index 1122683a77..a42009c124 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -290,6 +290,31 @@ class InvoiceService return $this; } + + /** + * Reset the reminders if only the + * partial has been paid. + * + * We can _ONLY_ call this _IF_ a partial + * amount has been paid, otherwise we end up wiping + * all reminders regardless + * + * @return self + */ + public function checkReminderStatus(): self + { + + if($this->invoice->partial == 0 && $this->invoice->balance > 0) + { + $this->invoice->reminder1_sent = null; + $this->invoice->reminder2_sent = null; + $this->invoice->reminder3_sent = null; + + $this->setReminder(); + } + + return $this; + } public function setReminder($settings = null) { diff --git a/app/Services/Invoice/UpdateReminder.php b/app/Services/Invoice/UpdateReminder.php index f552fb141e..43768b7cb1 100644 --- a/app/Services/Invoice/UpdateReminder.php +++ b/app/Services/Invoice/UpdateReminder.php @@ -49,27 +49,32 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder1 == 'after_invoice_date') { $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder1); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } if (is_null($this->invoice->reminder1_sent) && - $this->invoice->due_date && + ($this->invoice->partial_due_date || $this->invoice->due_date) && $this->settings->schedule_reminder1 == 'before_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder1); + $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays($this->settings->num_days_reminder1); +// nlog("1. {$reminder_date->format('Y-m-d')}"); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } if (is_null($this->invoice->reminder1_sent) && - $this->invoice->due_date && + ($this->invoice->partial_due_date || $this->invoice->due_date) && $this->settings->schedule_reminder1 == 'after_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder1); + + $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays($this->settings->num_days_reminder1); +// nlog("2. {$reminder_date->format('Y-m-d')}"); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } @@ -78,27 +83,33 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder2 == 'after_invoice_date') { $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder2); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } if (is_null($this->invoice->reminder2_sent) && - $this->invoice->due_date && + ($this->invoice->partial_due_date || $this->invoice->due_date) && $this->settings->schedule_reminder2 == 'before_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder2); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays($this->settings->num_days_reminder2); +// nlog("3. {$reminder_date->format('Y-m-d')}"); + + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } if (is_null($this->invoice->reminder2_sent) && - $this->invoice->due_date && + ($this->invoice->partial_due_date || $this->invoice->due_date) && $this->settings->schedule_reminder2 == 'after_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder2); + + $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays($this->settings->num_days_reminder2); +// nlog("4. {$reminder_date->format('Y-m-d')}"); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } @@ -107,27 +118,33 @@ class UpdateReminder extends AbstractService $this->settings->schedule_reminder3 == 'after_invoice_date') { $reminder_date = Carbon::parse($this->invoice->date)->startOfDay()->addDays($this->settings->num_days_reminder3); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } if (is_null($this->invoice->reminder3_sent) && - $this->invoice->due_date && + ($this->invoice->partial_due_date || $this->invoice->due_date) && $this->settings->schedule_reminder3 == 'before_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->subDays($this->settings->num_days_reminder3); + + $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->subDays($this->settings->num_days_reminder3); +// nlog("5. {$reminder_date->format('Y-m-d')}"); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } if (is_null($this->invoice->reminder3_sent) && - $this->invoice->due_date && + ($this->invoice->partial_due_date || $this->invoice->due_date) && $this->settings->schedule_reminder3 == 'after_due_date') { - $reminder_date = Carbon::parse($this->invoice->due_date)->startOfDay()->addDays($this->settings->num_days_reminder3); + + $partial_or_due_date = ($this->invoice->partial > 0 && isset($this->invoice->partial_due_date)) ? $this->invoice->partial_due_date : $this->invoice->due_date; + $reminder_date = Carbon::parse($partial_or_due_date)->startOfDay()->addDays($this->settings->num_days_reminder3); +// nlog("6. {$reminder_date->format('Y-m-d')}"); - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } @@ -140,7 +157,7 @@ class UpdateReminder extends AbstractService $reminder_date = $this->addTimeInterval($this->invoice->last_sent_date, (int) $this->settings->endless_reminder_frequency_id); if ($reminder_date) { - if ($reminder_date->gt(Carbon::parse($this->invoice->next_send_date))) { + if ($reminder_date->gt(now())) { $date_collection->push($reminder_date); } } diff --git a/app/Services/Payment/UpdateInvoicePayment.php b/app/Services/Payment/UpdateInvoicePayment.php index 10243007fc..4f123701ac 100644 --- a/app/Services/Payment/UpdateInvoicePayment.php +++ b/app/Services/Payment/UpdateInvoicePayment.php @@ -55,6 +55,8 @@ class UpdateInvoicePayment $invoice->restore(); } + // $has_partial = $invoice->hasPartial(); + if ($invoice->id == $this->payment_hash->fee_invoice_id) { $paid_amount = $paid_invoice->amount + $this->payment_hash->fee_total; } else { @@ -63,6 +65,8 @@ class UpdateInvoicePayment $client->service()->updatePaidToDate($paid_amount); //always use the payment->amount + $has_partial = $invoice->hasPartial(); + /* Need to determine here is we have an OVER payment - if YES only apply the max invoice amount */ if ($paid_amount > $invoice->partial && $paid_amount > $invoice->balance) { $paid_amount = $invoice->balance; @@ -76,12 +80,16 @@ class UpdateInvoicePayment $invoice->paid_to_date += $paid_amount; $invoice->save(); - $invoice = $invoice->service() - ->clearPartial() - ->updateStatus() - // ->deletePdf() - ->workFlow() - ->save(); + $invoice_service = $invoice->service() + ->clearPartial() + ->updateStatus() + ->workFlow(); + + if ($has_partial) { + $invoice_service->checkReminderStatus(); + } + + $invoice = $invoice_service->save(); if ($invoice->is_proforma) { //keep proforma's hidden diff --git a/composer.lock b/composer.lock index 2ac417355f..f798b135c2 100644 --- a/composer.lock +++ b/composer.lock @@ -6,46 +6,6 @@ ], "content-hash": "08bc4729962b495b68162a069269f74f", "packages": [ - { - "name": "adrienrn/php-mimetyper", - "version": "0.2.2", - "source": { - "type": "git", - "url": "https://github.com/adrienrn/php-mimetyper.git", - "reference": "702e00a604b4baed34d69730ce055e05c0f43932" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/adrienrn/php-mimetyper/zipball/702e00a604b4baed34d69730ce055e05c0f43932", - "reference": "702e00a604b4baed34d69730ce055e05c0f43932", - "shasum": "" - }, - "require": { - "dflydev/apache-mime-types": "^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "MimeTyper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Hussard", - "email": "adrien.ricartnoblet@gmail.com" - } - ], - "description": "PHP mime type and extension mapping library: compatible with Symfony, powered by jshttp/mime-db", - "support": { - "issues": "https://github.com/adrienrn/php-mimetyper/issues", - "source": "https://github.com/adrienrn/php-mimetyper/tree/0.2.2" - }, - "time": "2018-09-27T09:45:05+00:00" - }, { "name": "afosto/yaac", "version": "v1.5.2", @@ -525,16 +485,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.281.11", + "version": "3.281.12", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "9d466efae67d5016ed132fd4ffa1566a7d4cab98" + "reference": "22a92f08758db2b152843ea0875eeee5a467d8ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/9d466efae67d5016ed132fd4ffa1566a7d4cab98", - "reference": "9d466efae67d5016ed132fd4ffa1566a7d4cab98", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/22a92f08758db2b152843ea0875eeee5a467d8ff", + "reference": "22a92f08758db2b152843ea0875eeee5a467d8ff", "shasum": "" }, "require": { @@ -614,9 +574,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.281.11" + "source": "https://github.com/aws/aws-sdk-php/tree/3.281.12" }, - "time": "2023-09-20T19:16:24+00:00" + "time": "2023-09-22T18:12:27+00:00" }, { "name": "bacon/bacon-qr-code", @@ -1147,65 +1107,6 @@ }, "time": "2023-08-25T16:18:39+00:00" }, - { - "name": "dflydev/apache-mime-types", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/dflydev/dflydev-apache-mime-types.git", - "reference": "f30a57e59b7476e4c5270b6a0727d79c9c0eb861" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-apache-mime-types/zipball/f30a57e59b7476e4c5270b6a0727d79c9c0eb861", - "reference": "f30a57e59b7476e4c5270b6a0727d79c9c0eb861", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "require-dev": { - "twig/twig": "1.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-0": { - "Dflydev\\ApacheMimeTypes": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Dragonfly Development Inc.", - "email": "info@dflydev.com", - "homepage": "http://dflydev.com" - }, - { - "name": "Beau Simensen", - "email": "beau@dflydev.com", - "homepage": "http://beausimensen.com" - } - ], - "description": "Apache MIME Types", - "keywords": [ - "apache", - "mime", - "mimetypes" - ], - "support": { - "issues": "https://github.com/dflydev/dflydev-apache-mime-types/issues", - "source": "https://github.com/dflydev/dflydev-apache-mime-types/tree/v1.0.1" - }, - "time": "2013-05-14T02:02:01+00:00" - }, { "name": "dflydev/dot-access-data", "version": "v3.0.2", @@ -1452,16 +1353,16 @@ }, { "name": "doctrine/dbal", - "version": "3.6.6", + "version": "3.6.7", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "63646ffd71d1676d2f747f871be31b7e921c7864" + "reference": "8e0e268052b4a8974cb00215bb2892787021614f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/63646ffd71d1676d2f747f871be31b7e921c7864", - "reference": "63646ffd71d1676d2f747f871be31b7e921c7864", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/8e0e268052b4a8974cb00215bb2892787021614f", + "reference": "8e0e268052b4a8974cb00215bb2892787021614f", "shasum": "" }, "require": { @@ -1477,9 +1378,9 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.29", + "phpstan/phpstan": "1.10.34", "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.9", + "phpunit/phpunit": "9.6.12", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", "squizlabs/php_codesniffer": "3.7.2", @@ -1545,7 +1446,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.6.6" + "source": "https://github.com/doctrine/dbal/tree/3.6.7" }, "funding": [ { @@ -1561,7 +1462,7 @@ "type": "tidelift" } ], - "time": "2023-08-17T05:38:17+00:00" + "time": "2023-09-19T20:15:41+00:00" }, { "name": "doctrine/deprecations", @@ -3385,6 +3286,61 @@ }, "time": "2023-08-14T19:20:53+00:00" }, + { + "name": "horstoeko/mimedb", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/horstoeko/mimedb.git", + "reference": "2790b61cbff7f94ae8f40565761b15beb7792fcb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/horstoeko/mimedb/zipball/2790b61cbff7f94ae8f40565761b15beb7792fcb", + "reference": "2790b61cbff7f94ae8f40565761b15beb7792fcb", + "shasum": "" + }, + "require": { + "php": "^7.3|^7.4|^8.0|^8.1|^8.2|^8.3" + }, + "require-dev": { + "pdepend/pdepend": "^2", + "phploc/phploc": "^7", + "phpmd/phpmd": "^2", + "phpstan/phpstan": "^1.8", + "phpunit/phpunit": "^9", + "sebastian/phpcpd": "^6", + "squizlabs/php_codesniffer": "^3" + }, + "type": "package", + "autoload": { + "psr-4": { + "horstoeko\\mimedb\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Erling", + "email": "daniel@erling.com.de", + "role": "lead" + } + ], + "description": "Get mimetypes by fileextensions and visa versa", + "homepage": "https://github.com/horstoeko/mimedb", + "keywords": [ + "file extension", + "mimetype" + ], + "support": { + "issues": "https://github.com/horstoeko/mimedb/issues", + "source": "https://github.com/horstoeko/mimedb/tree/v1.0.5" + }, + "time": "2023-09-22T20:17:48+00:00" + }, { "name": "horstoeko/stringmanagement", "version": "v1.0.11", @@ -3441,22 +3397,22 @@ }, { "name": "horstoeko/zugferd", - "version": "v1.0.28", + "version": "v1.0.29", "source": { "type": "git", "url": "https://github.com/horstoeko/zugferd.git", - "reference": "be78b1b53a46e94a69b92dcff1e909180170583c" + "reference": "9fb81e2e9a16d10bec8bf655484aae11bdca1997" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/horstoeko/zugferd/zipball/be78b1b53a46e94a69b92dcff1e909180170583c", - "reference": "be78b1b53a46e94a69b92dcff1e909180170583c", + "url": "https://api.github.com/repos/horstoeko/zugferd/zipball/9fb81e2e9a16d10bec8bf655484aae11bdca1997", + "reference": "9fb81e2e9a16d10bec8bf655484aae11bdca1997", "shasum": "" }, "require": { - "adrienrn/php-mimetyper": "^0.2", "ext-simplexml": "*", "goetas-webservices/xsd2php-runtime": "^0.2.13", + "horstoeko/mimedb": "^1", "horstoeko/stringmanagement": "^1", "jms/serializer": "^3", "php": "^7.3|^7.4|^8.0|^8.1", @@ -3508,9 +3464,9 @@ ], "support": { "issues": "https://github.com/horstoeko/zugferd/issues", - "source": "https://github.com/horstoeko/zugferd/tree/v1.0.28" + "source": "https://github.com/horstoeko/zugferd/tree/v1.0.29" }, - "time": "2023-09-12T14:54:01+00:00" + "time": "2023-09-23T06:15:04+00:00" }, { "name": "http-interop/http-factory-guzzle", @@ -5227,16 +5183,16 @@ }, { "name": "league/csv", - "version": "9.10.0", + "version": "9.11.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "d24b0d484812313b07ab74b0fe4db9661606df6c" + "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/d24b0d484812313b07ab74b0fe4db9661606df6c", - "reference": "d24b0d484812313b07ab74b0fe4db9661606df6c", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/33149c4bea4949aa4fa3d03fb11ed28682168b39", + "reference": "33149c4bea4949aa4fa3d03fb11ed28682168b39", "shasum": "" }, "require": { @@ -5311,7 +5267,7 @@ "type": "github" } ], - "time": "2023-08-04T15:12:48+00:00" + "time": "2023-09-23T10:09:54+00:00" }, { "name": "league/flysystem", @@ -8531,16 +8487,16 @@ }, { "name": "psr/http-client", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31" + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31", - "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", "shasum": "" }, "require": { @@ -8577,9 +8533,9 @@ "psr-18" ], "support": { - "source": "https://github.com/php-fig/http-client/tree/1.0.2" + "source": "https://github.com/php-fig/http-client" }, - "time": "2023-04-10T20:12:12+00:00" + "time": "2023-09-23T14:17:50+00:00" }, { "name": "psr/http-factory", @@ -15152,16 +15108,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.27.0", + "version": "v3.28.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "e73ccaae1208f017bb7860986eebb3da48bd25d6" + "reference": "113e09fea3d2306319ffaa2423fe3de768b28cff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/e73ccaae1208f017bb7860986eebb3da48bd25d6", - "reference": "e73ccaae1208f017bb7860986eebb3da48bd25d6", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/113e09fea3d2306319ffaa2423fe3de768b28cff", + "reference": "113e09fea3d2306319ffaa2423fe3de768b28cff", "shasum": "" }, "require": { @@ -15235,7 +15191,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.27.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.28.0" }, "funding": [ { @@ -15243,7 +15199,7 @@ "type": "github" } ], - "time": "2023-09-17T14:37:54+00:00" + "time": "2023-09-22T20:43:40+00:00" }, { "name": "hamcrest/hamcrest-php", diff --git a/tests/Feature/ReminderTest.php b/tests/Feature/ReminderTest.php index ca4265d2eb..f963a04862 100644 --- a/tests/Feature/ReminderTest.php +++ b/tests/Feature/ReminderTest.php @@ -158,6 +158,82 @@ class ReminderTest extends TestCase } + public function testPartialDueDateReminder() + { + + $settings = $this->company->settings; + $settings->enable_reminder1 = true; + $settings->schedule_reminder1 = 'before_due_date'; + $settings->num_days_reminder1 = 1; + $settings->enable_reminder2 = true; + $settings->schedule_reminder2 = 'after_due_date'; + $settings->num_days_reminder2 = 14; + $settings->enable_reminder3 = true; + $settings->schedule_reminder3 = 'after_due_date'; + $settings->num_days_reminder3 = 30; + $settings->timezone_id = '29'; + $settings->entity_send_time = 0; + $settings->endless_reminder_frequency_id = ''; + $settings->enable_reminder_endless = false; + + $this->buildData(($settings)); + + $this->invoice->reminder1_sent = null; + $this->invoice->reminder2_sent = null; + $this->invoice->reminder3_sent = null; + $this->invoice->date = now()->startOfDay(); + $this->invoice->partial = 10; + $this->invoice->amount = 100; + $this->invoice->balance = 100; + $this->invoice->partial_due_date = now()->startOfDay()->addDays(7); + $this->invoice->due_date = now()->startOfDay()->addMonth(); + $this->invoice->service()->setReminder($settings)->save(); + + $this->invoice = $this->invoice->fresh(); + + $this->assertEquals(now()->startOfDay()->addDays(6)->format('Y-m-d'), Carbon::parse($this->invoice->next_send_date)->format('Y-m-d')); + $this->assertTrue($this->invoice->hasPartial()); + $data = [ + 'amount' => 10, + 'client_id' => $this->client->hashed_id, + 'invoices' => [ + [ + 'invoice_id' => $this->invoice->hashed_id, + 'amount' => 10, + ] + ] + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->postJson('/api/v1/payments/', $data); + + $response->assertStatus(200); + + $this->invoice = $this->invoice->fresh(); + + $this->assertEquals(0, $this->invoice->partial); + $this->assertNull($this->invoice->partial_due_date); + $this->assertNull($this->invoice->reminder1_sent); + $this->assertNull($this->invoice->reminder2_sent); + $this->assertNull($this->invoice->reminder3_sent); + + $this->assertEquals(90, $this->invoice->balance); + $this->assertEquals(Carbon::parse($this->invoice->due_date)->startOfDay()->subDay()->format('Y-m-d'), Carbon::parse($this->invoice->next_send_date)->startOfDay()->format('Y-m-d')); + + $this->travelTo(Carbon::parse($this->invoice->due_date)->startOfDay()->subDay()->addHour()); + + (new ReminderJob())->handle(); + $this->invoice = $this->invoice->fresh(); + + $this->assertNotNull($this->invoice->reminder1_sent); + $this->assertEquals(Carbon::parse($this->invoice->due_date)->startOfDay()->addDays(14)->format('Y-m-d'), Carbon::parse($this->invoice->next_send_date)->format('Y-m-d')); + + $this->travelBack(); + + } + public function testsForTranslationsInReminders() { @@ -233,16 +309,6 @@ class ReminderTest extends TestCase $this->assertEquals(103, $fee->cost); $this->assertEquals('Fee added '.now()->format('d/M/Y'), $fee->notes); - - - - - - - // $this->travelTo(now()->addHours(1)); -// } - - $this->travelBack(); } @@ -391,8 +457,8 @@ class ReminderTest extends TestCase $next_send_date = Carbon::parse($this->invoice->next_send_date); $calculatedReminderDate = Carbon::parse($this->invoice->due_date)->subDays(4)->addSeconds($this->invoice->client->timezone_offset()); - nlog($next_send_date->format('Y-m-d h:i:s')); - nlog($calculatedReminderDate->format('Y-m-d h:i:s')); + // nlog($next_send_date->format('Y-m-d h:i:s')); + // nlog($calculatedReminderDate->format('Y-m-d h:i:s')); $this->travelTo($calculatedReminderDate); @@ -413,7 +479,7 @@ class ReminderTest extends TestCase $next_send_date = Carbon::parse($this->invoice->next_send_date); - nlog($next_send_date->format('Y-m-d h:i:s')); + // nlog($next_send_date->format('Y-m-d h:i:s')); $calculatedReminderDate = Carbon::parse($this->invoice->due_date)->subDays(2)->addSeconds($this->invoice->client->timezone_offset()); $this->assertTrue($next_send_date->eq($calculatedReminderDate)); @@ -432,7 +498,7 @@ class ReminderTest extends TestCase $calculatedReminderDate = Carbon::parse($this->invoice->due_date)->addDays(3)->addSeconds($this->invoice->client->timezone_offset()); $this->assertTrue($next_send_date->eq($calculatedReminderDate)); - nlog($next_send_date->format('Y-m-d h:i:s')); + // nlog($next_send_date->format('Y-m-d h:i:s')); } public function testReminderQueryCatchesDate()