From 10903c83284257ad2e2eea402cf22a6ceb890ec1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Nov 2022 20:57:08 +1100 Subject: [PATCH 01/24] Add a new design - Calm --- app/Services/PdfMaker/Design.php | 3 +- .../2022_11_16_093535_calmness_design.php | 49 +++ resources/views/pdf-designs/calmness.html | 394 ------------------ 3 files changed, 51 insertions(+), 395 deletions(-) create mode 100644 database/migrations/2022_11_16_093535_calmness_design.php delete mode 100644 resources/views/pdf-designs/calmness.html diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php index 03e5e686a2ae..b47884b63a93 100644 --- a/app/Services/PdfMaker/Design.php +++ b/app/Services/PdfMaker/Design.php @@ -73,7 +73,8 @@ class Design extends BaseDesign const PLAIN = 'plain'; const PLAYFUL = 'playful'; const CUSTOM = 'custom'; - + const CALM = 'calm'; + const DELIVERY_NOTE = 'delivery_note'; const STATEMENT = 'statement'; const PURCHASE_ORDER = 'purchase_order'; diff --git a/database/migrations/2022_11_16_093535_calmness_design.php b/database/migrations/2022_11_16_093535_calmness_design.php new file mode 100644 index 000000000000..677d1d273e50 --- /dev/null +++ b/database/migrations/2022_11_16_093535_calmness_design.php @@ -0,0 +1,49 @@ + 'Calm', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true]; + + $design = Design::create($design); + + $template = new PdfMakerDesign(strtolower($design->name)); + $template->document(); + + $design_object = new \stdClass; + $design_object->includes = $template->getSectionHTML('style'); + $design_object->header = $template->getSectionHTML('header'); + $design_object->body = $template->getSectionHTML('body'); + $design_object->product = ''; + $design_object->task = ''; + $design_object->footer = $template->getSectionHTML('footer'); + + $design->design = $design_object; + $design->save(); + + + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +}; diff --git a/resources/views/pdf-designs/calmness.html b/resources/views/pdf-designs/calmness.html deleted file mode 100644 index 2ec356cecf02..000000000000 --- a/resources/views/pdf-designs/calmness.html +++ /dev/null @@ -1,394 +0,0 @@ - - - - - - - - - - - - - - - - - - -
-
 
-
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-

$entity_label

-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
- -
- - - - - - - - \ No newline at end of file From a0e186d139c589657c46081b4f5ba236b5a26b6a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Nov 2022 20:57:23 +1100 Subject: [PATCH 02/24] calm design --- resources/views/pdf-designs/calm.html | 394 ++++++++++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100644 resources/views/pdf-designs/calm.html diff --git a/resources/views/pdf-designs/calm.html b/resources/views/pdf-designs/calm.html new file mode 100644 index 000000000000..142904fc11d4 --- /dev/null +++ b/resources/views/pdf-designs/calm.html @@ -0,0 +1,394 @@ + + + + + + + + + + + + + + + + + + +
+
 
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+ +
+

$entity_label

+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + + + + + \ No newline at end of file From fbaa27f6731567cd8252be576643e82c00e63f6d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Nov 2022 21:07:55 +1100 Subject: [PATCH 03/24] Tweak migrations for new invoice design --- database/migrations/2022_11_16_093535_calmness_design.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/database/migrations/2022_11_16_093535_calmness_design.php b/database/migrations/2022_11_16_093535_calmness_design.php index 677d1d273e50..73f1909da80a 100644 --- a/database/migrations/2022_11_16_093535_calmness_design.php +++ b/database/migrations/2022_11_16_093535_calmness_design.php @@ -16,9 +16,12 @@ return new class extends Migration public function up() { - $design = ['name' => 'Calm', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true]; + $design_array = ['name' => 'Calm', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true]; - $design = Design::create($design); + $design = Design::where('name', 'Calm')->whereNull('company_id')->first(); + + if(!$design) + $design = Design::create($design_array); $template = new PdfMakerDesign(strtolower($design->name)); $template->document(); @@ -34,7 +37,6 @@ return new class extends Migration $design->design = $design_object; $design->save(); - } /** From f0e51e5a67d6f1f1a3ed5cd3fddd34566720e1bf Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Nov 2022 22:52:26 +1100 Subject: [PATCH 04/24] Fixes for gocardless events --- README.md | 11 +++++------ app/PaymentDrivers/GoCardlessPaymentDriver.php | 12 ++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5426d3a18220..0c5c0004200b 100644 --- a/README.md +++ b/README.md @@ -26,15 +26,14 @@ All Pro and Enterprise features from the hosted app are included in the open-cod * [Support Forum](https://forum.invoiceninja.com) * [StackOverflow](https://stackoverflow.com/tags/invoice-ninja/) -## Mobile App -* [iPhone](https://apps.apple.com/us/app/invoice-ninja-v5/id1503970375#?platform=iphone) +## Mobile Apps +* [iPhone](https://apps.apple.com/app/id1503970375?platform=iphone) * [Android](https://play.google.com/store/apps/details?id=com.invoiceninja.app) -* [Linux](https://github.com/invoiceninja/flutter-mobile) -## Desktop App -* [MacOS](https://apps.apple.com/app/id1503970375) +## Desktop Apps +* [macOS](https://apps.apple.com/app/id1503970375?platform=mac) * [Windows](https://microsoft.com/en-us/p/invoice-ninja/9n3f2bbcfdr6) -* [MacOS Desktop](https://snapcraft.io/invoiceninja) +* [Linux](https://snapcraft.io/invoiceninja) ## Installation Options diff --git a/app/PaymentDrivers/GoCardlessPaymentDriver.php b/app/PaymentDrivers/GoCardlessPaymentDriver.php index a1db5c298a48..149e1d6843bb 100644 --- a/app/PaymentDrivers/GoCardlessPaymentDriver.php +++ b/app/PaymentDrivers/GoCardlessPaymentDriver.php @@ -291,13 +291,13 @@ class GoCardlessPaymentDriver extends BaseDriver return response()->json([], 200); } - $this->go_cardless->setPaymentHash($hash); + $this->setPaymentHash($hash); - $billing_request = $this->go_cardless->gateway->billingRequests()->get( + $billing_request = $this->gateway->billingRequests()->get( $event['links']['billing_request'] ); - $payment = $this->go_cardless->gateway->payments()->get( + $payment = $this->gateway->payments()->get( $billing_request->payment_request->links->payment ); @@ -305,7 +305,7 @@ class GoCardlessPaymentDriver extends BaseDriver $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($hash->invoices(), 'invoice_id')))->withTrashed()->get(); - $this->go_cardless->client = $invoices->first()->client; + $this->client = $invoices->first()->client; $invoices->each(function ($invoice){ @@ -347,12 +347,12 @@ class GoCardlessPaymentDriver extends BaseDriver $data = [ 'payment_method' => $payment->links->mandate, 'payment_type' => PaymentType::INSTANT_BANK_PAY, - 'amount' => $this->go_cardless->payment_hash->data->amount_with_fee, + 'amount' => $this->payment_hash->data->amount_with_fee, 'transaction_reference' => $payment->id, 'gateway_type_id' => GatewayType::INSTANT_BANK_PAY, ]; - $payment = $this->go_cardless->createPayment($data, Payment::STATUS_COMPLETED); + $payment = $this->createPayment($data, Payment::STATUS_COMPLETED); $payment->status_id = Payment::STATUS_COMPLETED; $payment->save(); From 2b5285a58a37eb4d76b07b0a77abd9fb264d0025 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 16 Nov 2022 23:08:03 +1100 Subject: [PATCH 05/24] FIxes for new calm design --- .../2022_11_16_093535_calmness_design.php | 34 ++++++++++--------- database/seeders/DesignSeeder.php | 1 + resources/views/pdf-designs/calm.html | 6 ++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/database/migrations/2022_11_16_093535_calmness_design.php b/database/migrations/2022_11_16_093535_calmness_design.php index 73f1909da80a..898d96549a87 100644 --- a/database/migrations/2022_11_16_093535_calmness_design.php +++ b/database/migrations/2022_11_16_093535_calmness_design.php @@ -1,10 +1,11 @@ 'Calm', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true]; + if (Ninja::isHosted()) { + $design = new Design(); - $design = Design::where('name', 'Calm')->whereNull('company_id')->first(); + $design->name = 'Calm'; + $design->is_custom = false; + $design->design = ''; + $design->is_active = true; - if(!$design) - $design = Design::create($design_array); + $design->save(); + } elseif (Design::count() !== 0) { + $design = new Design(); - $template = new PdfMakerDesign(strtolower($design->name)); - $template->document(); + $design->name = 'Calm'; + $design->is_custom = false; + $design->design = ''; + $design->is_active = true; - $design_object = new \stdClass; - $design_object->includes = $template->getSectionHTML('style'); - $design_object->header = $template->getSectionHTML('header'); - $design_object->body = $template->getSectionHTML('body'); - $design_object->product = ''; - $design_object->task = ''; - $design_object->footer = $template->getSectionHTML('footer'); + $design->save(); + } - $design->design = $design_object; - $design->save(); + \Illuminate\Support\Facades\Artisan::call('ninja:design-update'); } diff --git a/database/seeders/DesignSeeder.php b/database/seeders/DesignSeeder.php index fb17cba8ff2e..f1a1fd0ede38 100644 --- a/database/seeders/DesignSeeder.php +++ b/database/seeders/DesignSeeder.php @@ -38,6 +38,7 @@ class DesignSeeder extends Seeder ['id' => 8, 'name' => 'Hipster', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true], ['id' => 9, 'name' => 'Playful', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true], ['id' => 10, 'name' => 'Tech', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true], + ['id' => 11, 'name' => 'Calm', 'user_id' => null, 'company_id' => null, 'is_custom' => false, 'design' => '', 'is_active' => true], ]; foreach ($designs as $design) { diff --git a/resources/views/pdf-designs/calm.html b/resources/views/pdf-designs/calm.html index 142904fc11d4..0ebcd8d26f85 100644 --- a/resources/views/pdf-designs/calm.html +++ b/resources/views/pdf-designs/calm.html @@ -291,6 +291,7 @@ /** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/ +
@@ -302,7 +303,7 @@
-
+
@@ -317,7 +318,7 @@
-

$entity_label

+

$entity_label

@@ -344,6 +345,7 @@
+
From f3fdd1f7fd5076ccb8f12a97d45f2dd3e3bfb78c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Nov 2022 08:43:17 +1100 Subject: [PATCH 06/24] Prevent deleted/archived/completed transactions from being re-converted --- .../BankTransaction/MatchBankTransactionRequest.php | 1 + app/Jobs/Bank/MatchBankTransactions.php | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php b/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php index 76dc1e349c87..635485322710 100644 --- a/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php +++ b/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php @@ -37,6 +37,7 @@ class MatchBankTransactionRequest extends Request $rules['transactions.*.ninja_category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['transactions.*.vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; + $rules['transactions.*.id'] = 'bail|sometimes|exists:bank_transactions,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; return $rules; diff --git a/app/Jobs/Bank/MatchBankTransactions.php b/app/Jobs/Bank/MatchBankTransactions.php index 17e3a61a960d..d99c8fadb644 100644 --- a/app/Jobs/Bank/MatchBankTransactions.php +++ b/app/Jobs/Bank/MatchBankTransactions.php @@ -160,6 +160,9 @@ class MatchBankTransactions implements ShouldQueue { $this->bt = BankTransaction::find($input['id']); + if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED) + return $this; + $_invoices = Invoice::withTrashed()->find($this->getInvoices($input['invoice_ids'])); $amount = $this->bt->amount; @@ -180,6 +183,10 @@ class MatchBankTransactions implements ShouldQueue //if there is a category id, pull it from Yodlee and insert - or just reuse!! $this->bt = BankTransaction::find($input['id']); + if(!$this->bt || $this->bt->status_id == BankTransaction::STATUS_CONVERTED) + return $this; + + $expense = ExpenseFactory::create($this->bt->company_id, $this->bt->user_id); $expense->category_id = $this->resolveCategory($input); $expense->amount = $this->bt->amount; From b3e0da52b615d410cb1ec13662efa62971a2d96d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Nov 2022 10:15:22 +1100 Subject: [PATCH 07/24] Fixes for bank transaction tests --- .../Requests/BankTransaction/MatchBankTransactionRequest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php b/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php index 635485322710..280565926737 100644 --- a/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php +++ b/app/Http/Requests/BankTransaction/MatchBankTransactionRequest.php @@ -31,13 +31,12 @@ class MatchBankTransactionRequest extends Request $rules = [ 'transactions' => 'bail|array', - 'transactions.*.id' => 'bail|required', 'transactions.*.invoice_ids' => 'nullable|string|sometimes', ]; $rules['transactions.*.ninja_category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; $rules['transactions.*.vendor_id'] = 'bail|sometimes|exists:vendors,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; - $rules['transactions.*.id'] = 'bail|sometimes|exists:bank_transactions,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; + $rules['transactions.*.id'] = 'bail|required|exists:bank_transactions,id,company_id,'.auth()->user()->company()->id.',is_deleted,0'; return $rules; From 43e74d62939fe505079d3fb33b7a6e8d0aa5d025 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Nov 2022 11:15:10 +1100 Subject: [PATCH 08/24] Fixes for playful design --- resources/views/pdf-designs/playful.html | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/resources/views/pdf-designs/playful.html b/resources/views/pdf-designs/playful.html index 4d308a1a645a..4e30191d59a0 100644 --- a/resources/views/pdf-designs/playful.html +++ b/resources/views/pdf-designs/playful.html @@ -323,7 +323,7 @@ /** For more info, please check our docs: https://invoiceninja.github.io **/ /** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/ - +
@@ -335,7 +335,6 @@ @@ -380,7 +378,7 @@
-
@@ -367,8 +366,7 @@
-
-
+
- +
-
diff --git a/resources/views/pdf-designs/business.html b/resources/views/pdf-designs/business.html index fe8285c334ac..d4c0da6f2c44 100644 --- a/resources/views/pdf-designs/business.html +++ b/resources/views/pdf-designs/business.html @@ -276,6 +276,36 @@ overflow-wrap: break-word; } + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } + /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/calm.html b/resources/views/pdf-designs/calm.html index 0ebcd8d26f85..bbd8887835ca 100644 --- a/resources/views/pdf-designs/calm.html +++ b/resources/views/pdf-designs/calm.html @@ -264,6 +264,35 @@ [data-ref="total_table-public_notes"] { font-weight: normal; } [data-ref="total_table-terms"] { font-weight: normal; } + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/clean.html b/resources/views/pdf-designs/clean.html index b584d6c6be45..1f9fcebbd282 100644 --- a/resources/views/pdf-designs/clean.html +++ b/resources/views/pdf-designs/clean.html @@ -257,6 +257,36 @@ overflow-wrap: break-word; } + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } + /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/creative.html b/resources/views/pdf-designs/creative.html index 4bf969e71715..1f7f6dbe8050 100644 --- a/resources/views/pdf-designs/creative.html +++ b/resources/views/pdf-designs/creative.html @@ -229,6 +229,35 @@ overflow-wrap: break-word; } + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/elegant.html b/resources/views/pdf-designs/elegant.html index 98b39ca60bba..649160995e15 100644 --- a/resources/views/pdf-designs/elegant.html +++ b/resources/views/pdf-designs/elegant.html @@ -233,7 +233,36 @@ max-width: 300px; overflow-wrap: break-word; } - + + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/hipster.html b/resources/views/pdf-designs/hipster.html index b14f8c2fe586..d3201b1bc92c 100644 --- a/resources/views/pdf-designs/hipster.html +++ b/resources/views/pdf-designs/hipster.html @@ -251,6 +251,35 @@ overflow-wrap: break-word; } + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/modern.html b/resources/views/pdf-designs/modern.html index 4dfea2f4daa7..e47ebffd3db4 100644 --- a/resources/views/pdf-designs/modern.html +++ b/resources/views/pdf-designs/modern.html @@ -278,6 +278,35 @@ overflow-wrap: break-word; } + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/plain.html b/resources/views/pdf-designs/plain.html index 30acac638bfe..a1f2906f6aff 100644 --- a/resources/views/pdf-designs/plain.html +++ b/resources/views/pdf-designs/plain.html @@ -221,6 +221,35 @@ overflow-wrap: break-word; } + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ diff --git a/resources/views/pdf-designs/playful.html b/resources/views/pdf-designs/playful.html index 4e30191d59a0..84ac8eb6f294 100644 --- a/resources/views/pdf-designs/playful.html +++ b/resources/views/pdf-designs/playful.html @@ -293,6 +293,36 @@ max-width: 300px; overflow-wrap: break-word; } + + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ diff --git a/resources/views/pdf-designs/tech.html b/resources/views/pdf-designs/tech.html index cac33a111716..e9ff50fe6242 100644 --- a/resources/views/pdf-designs/tech.html +++ b/resources/views/pdf-designs/tech.html @@ -257,7 +257,36 @@ max-width: 300px; overflow-wrap: break-word; } - + + .stamp { + transform: rotate(12deg); + color: #555; + font-size: 3rem; + font-weight: 700; + border: 0.25rem solid #555; + display: inline-block; + padding: 0.25rem 1rem; + text-transform: uppercase; + border-radius: 1rem; + font-family: 'Courier'; + mix-blend-mode: multiply; + z-index:200 !important; + position: fixed; + text-align: center; + } + + .is-paid { + color: #D23; + border: 1rem double #D23; + transform: rotate(-5deg); + font-size: 6rem; + font-family: "Open sans", Helvetica, Arial, sans-serif; + border-radius: 0; + padding: 0.5rem; + opacity: 0.2; + z-index:200 !important; + position: fixed; + } /** Useful snippets, uncomment to enable. **/ /** Hide company logo **/ From 5cd2a7afe487018213511662a32c0d1c9b9319ce Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Nov 2022 17:31:52 +1100 Subject: [PATCH 13/24] minor fixes --- app/Console/Commands/SendRemindersCron.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Console/Commands/SendRemindersCron.php b/app/Console/Commands/SendRemindersCron.php index f8f69a30dd60..ad0b99387a65 100644 --- a/app/Console/Commands/SendRemindersCron.php +++ b/app/Console/Commands/SendRemindersCron.php @@ -175,7 +175,7 @@ class SendRemindersCron extends Command /**Refresh Invoice values*/ $invoice->calc()->getInvoice()->save(); $invoice->fresh(); - $invoice->service()->deletePdf(); + $invoice->service()->deletePdf()->save(); /* Refresh the client here to ensure the balance is fresh */ $client = $invoice->client; From 1d6b7d3b55a2fd7961440ebf800727a03594170a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Nov 2022 17:38:27 +1100 Subject: [PATCH 14/24] Late fee tests --- tests/Unit/AutoBillInvoiceTest.php | 23 -------- tests/Unit/LateFeeTest.php | 86 ++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 23 deletions(-) create mode 100644 tests/Unit/LateFeeTest.php diff --git a/tests/Unit/AutoBillInvoiceTest.php b/tests/Unit/AutoBillInvoiceTest.php index df73de11f07a..aa8ef0281e66 100644 --- a/tests/Unit/AutoBillInvoiceTest.php +++ b/tests/Unit/AutoBillInvoiceTest.php @@ -48,27 +48,4 @@ class AutoBillInvoiceTest extends TestCase $this->assertEquals($this->client->fresh()->credit_balance, 0); } - // public function testAutoBillSetOffFunctionality() - // { - - // $settings = $this->company->settings; - // $settings->use_credits_payment = 'off'; - - // $this->company->settings = $settings; - // $this->company->save(); - - // $this->assertEquals($this->client->balance, 10); - // $this->assertEquals($this->client->paid_to_date, 0); - // $this->assertEquals($this->client->credit_balance, 10); - - // $this->invoice->service()->markSent()->autoBill()->save(); - - // $this->assertNotNull($this->invoice->payments()); - // $this->assertEquals(0, $this->invoice->payments()->sum('payments.amount')); - - // $this->assertEquals($this->client->balance, 10); - // $this->assertEquals($this->client->paid_to_date, 0); - // $this->assertEquals($this->client->credit_balance, 10); - - // } } diff --git a/tests/Unit/LateFeeTest.php b/tests/Unit/LateFeeTest.php new file mode 100644 index 000000000000..28ef22a50d4e --- /dev/null +++ b/tests/Unit/LateFeeTest.php @@ -0,0 +1,86 @@ +makeTestData(); + } + + public function testLateFeeBalances() + { + + $this->assertEquals(10, $this->client->balance); + $this->assertEquals(10, $this->invoice->balance); + + $this->invoice = $this->setLateFee($this->invoice, 5, 0); + + $this->assertEquals(15, $this->client->fresh()->balance); + $this->assertEquals(15, $this->invoice->fresh()->balance); + + } + + private function setLateFee($invoice, $amount, $percent) :Invoice + { + + $temp_invoice_balance = $invoice->balance; + + if ($amount <= 0 && $percent <= 0) { + return $invoice; + } + + $fee = $amount; + + if ($invoice->partial > 0) { + $fee += round($invoice->partial * $percent / 100, 2); + } else { + $fee += round($invoice->balance * $percent / 100, 2); + } + + $invoice_item = new InvoiceItem; + $invoice_item->type_id = '5'; + $invoice_item->product_key = trans('texts.fee'); + $invoice_item->notes = ctrans('texts.late_fee_added', ['date' => now()]); + $invoice_item->quantity = 1; + $invoice_item->cost = $fee; + + $invoice_items = $invoice->line_items; + $invoice_items[] = $invoice_item; + + $invoice->line_items = $invoice_items; + + /**Refresh Invoice values*/ + $invoice = $invoice->calc()->getInvoice(); + + $invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance)->save(); + $invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}"); + + return $invoice; + } + +} From 050079b76a14645c631016aee032b3ccc3b39496 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Nov 2022 18:56:52 +1100 Subject: [PATCH 15/24] Wind back some changes for react fallback routes --- app/Http/Controllers/BaseController.php | 36 ------------------------- routes/web.php | 4 +-- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index d62d14c19037..56400e7e1285 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -1000,42 +1000,6 @@ class BaseController extends Controller return redirect('/setup'); } - public function reactCatch() - { - - if ((bool) $this->checkAppSetup() !== false && $account = Account::first()) { - if (config('ninja.require_https') && ! request()->isSecure()) { - return redirect()->secure(request()->getRequestUri()); - } - - $data = []; - - //pass report errors bool to front end - $data['report_errors'] = Ninja::isSelfHost() ? $account->report_errors : true; - - //pass referral code to front end - $data['rc'] = request()->has('rc') ? request()->input('rc') : ''; - $data['build'] = request()->has('build') ? request()->input('build') : ''; - $data['login'] = request()->has('login') ? request()->input('login') : 'false'; - $data['signup'] = request()->has('signup') ? request()->input('signup') : 'false'; - - $data['user_agent'] = request()->server('HTTP_USER_AGENT'); - - $data['path'] = $this->setBuild(); - - $this->buildCache(); - - if (Ninja::isSelfHost() && $account->set_react_as_default_ap) { - return view('react.index', $data); - } else { - abort('page not found', 404); - } - } - - return redirect('/setup'); - } - - private function setBuild() { $build = ''; diff --git a/routes/web.php b/routes/web.php index 9e5d3bf5fb0f..26ddc312bec6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -58,6 +58,4 @@ Route::get('yodlee/onboard/{token}', [YodleeController::class, 'auth'])->name('y Route::get('checkout/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', [Checkout3dsController::class, 'index'])->middleware('domain_db')->name('checkout.3ds_redirect'); Route::get('mollie/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', [Mollie3dsController::class, 'index'])->middleware('domain_db')->name('mollie.3ds_redirect'); Route::get('gocardless/ibp_redirect/{company_key}/{company_gateway_id}/{hash}', [GoCardlessController::class, 'ibpRedirect'])->middleware('domain_db')->name('gocardless.ibp_redirect'); -Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']); - -Route::fallback([BaseController::class, 'reactCatch']); \ No newline at end of file +Route::get('.well-known/apple-developer-merchantid-domain-association', [ApplePayDomainController::class, 'showAppleMerchantId']); \ No newline at end of file From fd3d9aa9317c933275108417287d34b96dc89834 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Nov 2022 19:11:05 +1100 Subject: [PATCH 16/24] Tests for recurring invoice variables --- tests/Feature/RecurringInvoiceTest.php | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/Feature/RecurringInvoiceTest.php b/tests/Feature/RecurringInvoiceTest.php index b995bc47e135..2c076d214d2e 100644 --- a/tests/Feature/RecurringInvoiceTest.php +++ b/tests/Feature/RecurringInvoiceTest.php @@ -11,6 +11,7 @@ namespace Tests\Feature; +use App\Factory\InvoiceItemFactory; use App\Factory\InvoiceToRecurringInvoiceFactory; use App\Factory\RecurringInvoiceToInvoiceFactory; use App\Models\Client; @@ -51,6 +52,51 @@ class RecurringInvoiceTest extends TestCase $this->makeTestData(); } + public function testPostRecurringInvoiceWithPlaceholderVariables() + { + $line_items = []; + + $item = InvoiceItemFactory::create(); + $item->quantity = 1; + $item->cost = 10; + $item->task_id = $this->encodePrimaryKey($this->task->id); + $item->expense_id = $this->encodePrimaryKey($this->expense->id); + $item->description = "Hello this is the month of :MONTH"; + + $line_items[] = $item; + + + $data = [ + 'frequency_id' => 1, + 'status_id' => 1, + 'discount' => 0, + 'is_amount_discount' => 1, + 'po_number' => '3434343', + 'public_notes' => 'notes', + 'is_deleted' => 0, + 'custom_value1' => 0, + 'custom_value2' => 0, + 'custom_value3' => 0, + 'custom_value4' => 0, + 'status' => 1, + 'client_id' => $this->encodePrimaryKey($this->client->id), + 'line_items' => $line_items, + 'remaining_cycles' => -1, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/recurring_invoices/', $data) + ->assertStatus(200); + + $arr = $response->json(); + $this->assertEquals(RecurringInvoice::STATUS_DRAFT, $arr['data']['status_id']); + + $this->assertIsArray($arr['data']['line_items']); + } + + public function testPostRecurringInvoice() { $data = [ From c9bebde8f4184b721a18e10f75fd08b2dc4142a1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Nov 2022 07:19:02 +1100 Subject: [PATCH 17/24] Minor fixes for date range calculation for :WEEK --- app/Utils/Helpers.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php index fee637ec83e7..25a36e55d09e 100644 --- a/app/Utils/Helpers.php +++ b/app/Utils/Helpers.php @@ -114,7 +114,7 @@ class Helpers return ''; } - // 04-10-2022 Return Early if no reserved keywords are present, this is a very expenseive process + // 04-10-2022 Return Early if no reserved keywords are present, this is a very expensive process $string_hit = false; foreach ( [':MONTH',':YEAR',':QUARTER',':WEEK'] as $string ) @@ -144,21 +144,21 @@ class Helpers ':QUARTER' => 'Q'.now()->quarter, ':WEEK_BEFORE' => \sprintf( '%s %s %s', - Carbon::now()->subDays(7)->translatedFormat($entity->date_format()), + Carbon::now()->subDays(6)->translatedFormat($entity->date_format()), ctrans('texts.to'), Carbon::now()->translatedFormat($entity->date_format()) ), ':WEEK_AHEAD' => \sprintf( '%s %s %s', - Carbon::now()->addDays(7)->translatedFormat($entity->date_format()), + Carbon::now()->addDays(6)->translatedFormat($entity->date_format()), ctrans('texts.to'), - Carbon::now()->addDays(14)->translatedFormat($entity->date_format()) + Carbon::now()->addDays(13)->translatedFormat($entity->date_format()) ), ':WEEK' => \sprintf( '%s %s %s', Carbon::now()->translatedFormat($entity->date_format()), ctrans('texts.to'), - Carbon::now()->addDays(7)->translatedFormat($entity->date_format()) + Carbon::now()->addDays(6)->translatedFormat($entity->date_format()) ), ], 'raw' => [ From a6079b0cc11a3f407af2d67647bbe55f95bf0b01 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Nov 2022 07:45:14 +1100 Subject: [PATCH 18/24] Fixes for variables in recurring invoices --- app/Repositories/BaseRepository.php | 2 +- tests/Feature/RecurringInvoiceTest.php | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/Repositories/BaseRepository.php b/app/Repositories/BaseRepository.php index 4d9451dcf83a..110567910cda 100644 --- a/app/Repositories/BaseRepository.php +++ b/app/Repositories/BaseRepository.php @@ -187,7 +187,7 @@ class BaseRepository if(!$model->id){ $this->new_model = true; - if(is_array($model->line_items)) + if(is_array($model->line_items) && !($model instanceof RecurringInvoice)) { $model->line_items = (collect($model->line_items))->map(function ($item) use($model,$client) { diff --git a/tests/Feature/RecurringInvoiceTest.php b/tests/Feature/RecurringInvoiceTest.php index 2c076d214d2e..61f99b0cd9f3 100644 --- a/tests/Feature/RecurringInvoiceTest.php +++ b/tests/Feature/RecurringInvoiceTest.php @@ -61,7 +61,7 @@ class RecurringInvoiceTest extends TestCase $item->cost = 10; $item->task_id = $this->encodePrimaryKey($this->task->id); $item->expense_id = $this->encodePrimaryKey($this->expense->id); - $item->description = "Hello this is the month of :MONTH"; + $item->notes = "Hello this is the month of :MONTH"; $line_items[] = $item; @@ -93,7 +93,9 @@ class RecurringInvoiceTest extends TestCase $arr = $response->json(); $this->assertEquals(RecurringInvoice::STATUS_DRAFT, $arr['data']['status_id']); - $this->assertIsArray($arr['data']['line_items']); + $notes = end($arr['data']['line_items'])['notes']; + + $this->assertTrue(str_contains($notes, ':MONTH')); } From c7362aefef0d8c97b4533bfb325b3effa5895f4f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Nov 2022 12:32:40 +1100 Subject: [PATCH 19/24] Fixes for Fortre payment driver and service fees --- app/PaymentDrivers/Forte/CreditCard.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/PaymentDrivers/Forte/CreditCard.php b/app/PaymentDrivers/Forte/CreditCard.php index 336ef7b525b1..2b851a88b7fb 100644 --- a/app/PaymentDrivers/Forte/CreditCard.php +++ b/app/PaymentDrivers/Forte/CreditCard.php @@ -92,12 +92,14 @@ class CreditCard $payment_hash = PaymentHash::where('hash', $request->input('payment_hash'))->firstOrFail(); $amount_with_fee = $payment_hash->data->total->amount_with_fee; $invoice_totals = $payment_hash->data->total->invoice_totals; - $fee_total = 0; + $fee_total = null; $fees_and_limits = $this->forte->company_gateway->getFeesAndLimits(GatewayType::CREDIT_CARD); if(property_exists($fees_and_limits, 'fee_percent') && $fees_and_limits->fee_percent > 0) { + $fee_total = 0; + for ($i = ($invoice_totals * 100) ; $i < ($amount_with_fee * 100); $i++) { $calculated_fee = ( 3 * $i) / 100; $calculated_amount_with_fee = round(($i + $calculated_fee) / 100,2); From 6db1dae3b05926cf9a98ea3d42eb45442d5cee82 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Nov 2022 10:20:25 +1100 Subject: [PATCH 20/24] Update filters --- app/Filters/QuoteFilters.php | 46 +++++++++++++++++++++++++ app/Filters/RecurringInvoiceFilters.php | 41 ++++++++++++++++++++++ lang/en/texts.php | 4 +++ 3 files changed, 91 insertions(+) diff --git a/app/Filters/QuoteFilters.php b/app/Filters/QuoteFilters.php index f619559b621c..9942ee909c29 100644 --- a/app/Filters/QuoteFilters.php +++ b/app/Filters/QuoteFilters.php @@ -11,6 +11,7 @@ namespace App\Filters; +use App\Models\Quote; use App\Models\User; use Illuminate\Database\Eloquent\Builder; @@ -41,6 +42,51 @@ class QuoteFilters extends QueryFilters }); } + /** + * Filter based on client status. + * + * Statuses we need to handle + * - all + * - active + * - paused + * - completed + * + * @param string client_status The invoice status as seen by the client + * @return Builder + */ + public function client_status(string $value = '') :Builder + { + if (strlen($value) == 0) { + return $this->builder; + } + + $status_parameters = explode(',', $value); + + if (in_array('all', $status_parameters)) { + return $this->builder; + } + + if (in_array('draft', $status_parameters)) { + $this->builder->where('status_id', Quote::STATUS_DRAFT); + } + + if (in_array('sent', $status_parameters)) { + $this->builder->where('status_id', Quote::STATUS_SENT); + } + + if (in_array('approved', $status_parameters)) { + $this->builder->where('status_id', Quote::STATUS_APPROVED); + } + + if (in_array('expired', $status_parameters)) { + $this->builder->where('status_id', Quote::STATUS_SENT) + ->where('due_date', '<=', now()->toDateString()); + } + + return $this->builder; + } + + /** * Filters the list based on the status * archived, active, deleted. diff --git a/app/Filters/RecurringInvoiceFilters.php b/app/Filters/RecurringInvoiceFilters.php index 39f0bcaa0de7..541f24c03617 100644 --- a/app/Filters/RecurringInvoiceFilters.php +++ b/app/Filters/RecurringInvoiceFilters.php @@ -11,6 +11,7 @@ namespace App\Filters; +use App\Models\RecurringInvoice; use App\Models\User; use Illuminate\Database\Eloquent\Builder; @@ -40,6 +41,46 @@ class RecurringInvoiceFilters extends QueryFilters }); } + /** + * Filter based on client status. + * + * Statuses we need to handle + * - all + * - active + * - paused + * - completed + * + * @param string client_status The invoice status as seen by the client + * @return Builder + */ + public function client_status(string $value = '') :Builder + { + if (strlen($value) == 0) { + return $this->builder; + } + + $status_parameters = explode(',', $value); + + if (in_array('all', $status_parameters)) { + return $this->builder; + } + + if (in_array('active', $status_parameters)) { + $this->builder->where('status_id', RecurringInvoice::STATUS_ACTIVE); + } + + if (in_array('paused', $status_parameters)) { + $this->builder->where('status_id', RecurringInvoice::STATUS_PAUSED); + } + + if (in_array('completed', $status_parameters)) { + $this->builder->where('status_id', RecurringInvoice::STATUS_COMPLETED); + } + + return $this->builder; + } + + /** * Filters the list based on the status * archived, active, deleted. diff --git a/lang/en/texts.php b/lang/en/texts.php index fe2e75ac1590..3817b0d65129 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -4839,6 +4839,10 @@ $LANG = array( 'show_tasks_in_client_portal' => 'Show Tasks in Client Portal', 'notification_quote_expired_subject' => 'Quote :invoice has expired for :client', 'notification_quote_expired' => 'The following Quote :invoice for client :client and :amount has now expired.', + 'auto_sync' => 'Auto Sync', + 'refresh_accounts' => 'Refresh Accounts', + 'upgrade_to_connect_bank_account' => 'Upgrade to Enterprise to connect your bank account', + 'click_here_to_connect_bank_account' => 'Click here to connect your bank account', ); return $LANG; From eaedcba6d057dd78d3761e8e2387ea660e68b489 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Nov 2022 10:23:29 +1100 Subject: [PATCH 21/24] update for helpers --- app/Utils/Helpers.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php index 25a36e55d09e..e512ee3cb73b 100644 --- a/app/Utils/Helpers.php +++ b/app/Utils/Helpers.php @@ -144,9 +144,9 @@ class Helpers ':QUARTER' => 'Q'.now()->quarter, ':WEEK_BEFORE' => \sprintf( '%s %s %s', - Carbon::now()->subDays(6)->translatedFormat($entity->date_format()), + Carbon::now()->subDays(7)->translatedFormat($entity->date_format()), ctrans('texts.to'), - Carbon::now()->translatedFormat($entity->date_format()) + Carbon::now()->subDays(1)->translatedFormat($entity->date_format()) ), ':WEEK_AHEAD' => \sprintf( '%s %s %s', @@ -156,9 +156,9 @@ class Helpers ), ':WEEK' => \sprintf( '%s %s %s', - Carbon::now()->translatedFormat($entity->date_format()), + Carbon::now()->subDays(7)->translatedFormat($entity->date_format()), ctrans('texts.to'), - Carbon::now()->addDays(6)->translatedFormat($entity->date_format()) + Carbon::now()->addDays(13)->translatedFormat($entity->date_format()) ), ], 'raw' => [ From 1c89a39d569ef5184757cac1d84300a9f23ad9ee Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Nov 2022 10:58:32 +1100 Subject: [PATCH 22/24] Clean input for custom css --- .../Requests/Company/UpdateCompanyRequest.php | 18 ++++++++++++++++++ .../portal/ninja2020/layout/app.blade.php | 2 +- .../ninja2020/layout/vendor_app.blade.php | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php index 37cf05e5400e..94797598d43c 100644 --- a/app/Http/Requests/Company/UpdateCompanyRequest.php +++ b/app/Http/Requests/Company/UpdateCompanyRequest.php @@ -22,6 +22,14 @@ class UpdateCompanyRequest extends Request { use MakesHash; + private array $protected_input = [ + 'client_portal_privacy_policy', + 'client_portal_terms', + 'portal_custom_footer', + 'portal_custom_css', + 'portal_custom_head' + ]; + /** * Determine if the user is authorized to make this request. * @@ -32,6 +40,8 @@ class UpdateCompanyRequest extends Request return auth()->user()->can('edit', $this->company); } + + public function rules() { $input = $this->all(); @@ -90,6 +100,14 @@ class UpdateCompanyRequest extends Request { $account = $this->company->account; + if(Ninja::isHosted()) + { + foreach($this->protected_input as $protected_var) + { + $settings[$protected_var] = str_replace("script", "", $settings[$protected_var]); + } + } + if (! $account->isFreeHostedClient()) { return $settings; } diff --git a/resources/views/portal/ninja2020/layout/app.blade.php b/resources/views/portal/ninja2020/layout/app.blade.php index e2aed0225bbb..a68a47b90b41 100644 --- a/resources/views/portal/ninja2020/layout/app.blade.php +++ b/resources/views/portal/ninja2020/layout/app.blade.php @@ -163,7 +163,7 @@ @yield('footer') @stack('footer') - @if((bool) \App\Utils\Ninja::isSelfHost() && !empty($client->getSetting('portal_custom_footer'))) + @if($company && $company->account->isPaid() && !empty($client->getSetting('portal_custom_footer')))
{!! $client->getSetting('portal_custom_footer') !!}
diff --git a/resources/views/portal/ninja2020/layout/vendor_app.blade.php b/resources/views/portal/ninja2020/layout/vendor_app.blade.php index 87f03f3d8026..baf55d4945d4 100644 --- a/resources/views/portal/ninja2020/layout/vendor_app.blade.php +++ b/resources/views/portal/ninja2020/layout/vendor_app.blade.php @@ -164,7 +164,7 @@ @yield('footer') @stack('footer') - @if((bool) \App\Utils\Ninja::isSelfHost() && !empty($settings->portal_custom_footer)) + @if($company && $company->account->isPaid() && !empty($settings->portal_custom_footer))
{!! $settings->portal_custom_footer !!}
From b988614e3897aa609c3bdae701f498944ba88dae Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Nov 2022 13:37:27 +1100 Subject: [PATCH 23/24] Implement additional filters for list views --- app/Filters/BankTransactionFilters.php | 49 +++++++++++++++++++ app/Filters/ExpenseFilters.php | 49 +++++++++++++++++++ app/Filters/PurchaseOrderFilters.php | 24 ++++----- app/Filters/TaskFilters.php | 31 ++++++++++++ .../Invoice/InvoiceEmailFailedActivity.php | 2 - 5 files changed, 142 insertions(+), 13 deletions(-) diff --git a/app/Filters/BankTransactionFilters.php b/app/Filters/BankTransactionFilters.php index f76cc81cfdb7..e7cff05feee9 100644 --- a/app/Filters/BankTransactionFilters.php +++ b/app/Filters/BankTransactionFilters.php @@ -55,6 +55,55 @@ class BankTransactionFilters extends QueryFilters } + +/** + * Filter based on client status. + * + * Statuses we need to handle + * - all + * - unmatched + * - matched + * - converted + * - deposits + * - withdrawals + * + * @return Builder + */ + public function client_status(string $value = '') :Builder + { + if (strlen($value) == 0) { + return $this->builder; + } + + $status_parameters = explode(',', $value); + + if (in_array('all', $status_parameters)) { + return $this->builder; + } + + if (in_array('unmatched', $status_parameters)) { + $this->builder->where('status_id', BankTransaction::STATUS_UNMATCHED); + } + + if (in_array('matched', $status_parameters)) { + $this->builder->where('status_id', BankTransaction::STATUS_MATCHED); + } + + if (in_array('converted', $status_parameters)) { + $this->builder->where('status_id', BankTransaction::STATUS_CONVERTED); + } + + if (in_array('deposits', $status_parameters)) { + $this->builder->where('base_type', 'CREDIT'); + } + + if (in_array('withdrawals', $status_parameters)) { + $this->builder->where('base_type', 'DEBIT'); + } + + return $this->builder; + } + /** * Filters the list based on the status * archived, active, deleted. diff --git a/app/Filters/ExpenseFilters.php b/app/Filters/ExpenseFilters.php index 174acff790af..d7e10ac0f4f1 100644 --- a/app/Filters/ExpenseFilters.php +++ b/app/Filters/ExpenseFilters.php @@ -44,6 +44,55 @@ class ExpenseFilters extends QueryFilters }); } + /** + * Filter based on client status. + * + * Statuses we need to handle + * - all + * - logged + * - pending + * - invoiced + * - paid + * - unpaid + * + * @return Builder + */ + public function client_status(string $value = '') :Builder + { + if (strlen($value) == 0) { + return $this->builder; + } + + $status_parameters = explode(',', $value); + + if (in_array('all', $status_parameters)) { + return $this->builder; + } + + if (in_array('logged', $status_parameters)) { + $this->builder->where('amount', '>', 0); + } + + if (in_array('pending', $status_parameters)) { + $this->builder->whereNull('invoice_id')->whereNotNull('payment_date'); + } + + if (in_array('invoiced', $status_parameters)) { + $this->builder->whereNotNull('invoice_id'); + } + + if (in_array('paid', $status_parameters)) { + $this->builder->whereNotNull('payment_date'); + } + + if (in_array('unpaid', $status_parameters)) { + $this->builder->whereNull('payment_date'); + } + + return $this->builder; + } + + /** * Filters the list based on the status * archived, active, deleted. diff --git a/app/Filters/PurchaseOrderFilters.php b/app/Filters/PurchaseOrderFilters.php index 3aa91958ef36..2d9f957d75f6 100644 --- a/app/Filters/PurchaseOrderFilters.php +++ b/app/Filters/PurchaseOrderFilters.php @@ -17,19 +17,20 @@ use Illuminate\Database\Eloquent\Builder; class PurchaseOrderFilters extends QueryFilters { + /** * Filter based on client status. * * Statuses we need to handle * - all - * - paid - * - unpaid - * - overdue - * - reversed + * - draft + * - sent + * - accepted + * - cancelled * * @return Builder */ - public function credit_status(string $value = '') :Builder + public function client_status(string $value = '') :Builder { if (strlen($value) == 0) { return $this->builder; @@ -45,16 +46,17 @@ class PurchaseOrderFilters extends QueryFilters $this->builder->where('status_id', PurchaseOrder::STATUS_DRAFT); } - if (in_array('partial', $status_parameters)) { - $this->builder->where('status_id', PurchaseOrder::STATUS_PARTIAL); + if (in_array('sent', $status_parameters)) { + $this->builder->where('status_id', PurchaseOrder::STATUS_SENT); } - if (in_array('applied', $status_parameters)) { - $this->builder->where('status_id', PurchaseOrder::STATUS_APPLIED); + if (in_array('accepted', $status_parameters)) { + $this->builder->where('status_id', PurchaseOrder::STATUS_ACCEPTED); } - //->where('due_date', '>', Carbon::now()) - //->orWhere('partial_due_date', '>', Carbon::now()); + if (in_array('cancelled', $status_parameters)) { + $this->builder->where('status_id', PurchaseOrder::STATUS_CANCELLED); + } return $this->builder; } diff --git a/app/Filters/TaskFilters.php b/app/Filters/TaskFilters.php index d0f7f2419c05..f8278f506043 100644 --- a/app/Filters/TaskFilters.php +++ b/app/Filters/TaskFilters.php @@ -41,6 +41,37 @@ class TaskFilters extends QueryFilters }); } + + /** + * Filter based on client status. + * + * Statuses we need to handle + * - all + * - invoiced + * + * @param string client_status The invoice status as seen by the client + * @return Builder + */ + public function client_status(string $value = '') :Builder + { + if (strlen($value) == 0) { + return $this->builder; + } + + $status_parameters = explode(',', $value); + + if (in_array('all', $status_parameters)) { + return $this->builder; + } + + if (in_array('invoiced', $status_parameters)) { + $this->builder->whereNotNull('invoice_id'); + } + + return $this->builder; + } + + /** * Filters the list based on the status * archived, active, deleted. diff --git a/app/Listeners/Invoice/InvoiceEmailFailedActivity.php b/app/Listeners/Invoice/InvoiceEmailFailedActivity.php index 122bdc972379..b46493528fb8 100644 --- a/app/Listeners/Invoice/InvoiceEmailFailedActivity.php +++ b/app/Listeners/Invoice/InvoiceEmailFailedActivity.php @@ -21,8 +21,6 @@ class InvoiceEmailFailedActivity implements ShouldQueue { protected $activity_repo; - public $delay = 5; - /** * Create the event listener. * From 03639af5a3c234492ebb9356f03bc66c57a08f35 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 20 Nov 2022 09:21:49 +1100 Subject: [PATCH 24/24] v5.5.41 --- VERSION.txt | 2 +- config/ninja.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 8fa0157a0207..8a55abf0b000 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.5.40 \ No newline at end of file +5.5.41 \ No newline at end of file diff --git a/config/ninja.php b/config/ninja.php index a3bb71301c2d..edfc4c68b0f2 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -14,8 +14,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.5.40', - 'app_tag' => '5.5.40', + 'app_version' => '5.5.41', + 'app_tag' => '5.5.41', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''),