From 23711acc948c04d7080c34f1dacd92fee8c1be93 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 9 Nov 2022 15:44:26 +1100 Subject: [PATCH 01/12] Fixes for bank transactions --- app/Jobs/Ninja/BankTransactionSync.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Jobs/Ninja/BankTransactionSync.php b/app/Jobs/Ninja/BankTransactionSync.php index e06839b8634f..9b2d4c3d00ef 100644 --- a/app/Jobs/Ninja/BankTransactionSync.php +++ b/app/Jobs/Ninja/BankTransactionSync.php @@ -60,14 +60,14 @@ class BankTransactionSync implements ShouldQueue public function syncTransactions() { - $a = Account::with('bank_integrations')->where('auto_sync', true)->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){ + $a = Account::with('bank_integrations')->whereNotNull('bank_integration_account_id')->cursor()->each(function ($account){ // $queue = Ninja::isHosted() ? 'bank' : 'default'; if($account->isPaid() && $account->plan == 'enterprise') { - $account->bank_integrations->each(function ($bank_integration) use ($account){ + $account->bank_integrations()->where('auto_sync', true)->cursor()->each(function ($bank_integration) use ($account){ ProcessBankTransactions::dispatchSync($account->bank_integration_account_id, $bank_integration); From 18a038a34dd96b73ab2b9886a89a4027bab6e697 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 9 Nov 2022 22:22:52 +1100 Subject: [PATCH 02/12] Fixes for 2FA --- app/Http/Controllers/TwilioController.php | 10 ++++------ app/Http/Requests/User/UpdateUserRequest.php | 3 ++- app/Http/ValidationRules/User/HasValidPhoneNumber.php | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/Http/Controllers/TwilioController.php b/app/Http/Controllers/TwilioController.php index 1ca7d31c6d60..2baddfa239fb 100644 --- a/app/Http/Controllers/TwilioController.php +++ b/app/Http/Controllers/TwilioController.php @@ -94,16 +94,13 @@ class TwilioController extends BaseController if($verification_check->status == 'approved'){ - if($request->query('validate_only') == 'true') - return response()->json(['message' => 'SMS verified'], 200); - - $account->account_sms_verified = true; $account->save(); //on confirmation we set the users phone number. $user = auth()->user(); $user->phone = $account->account_sms_verification_number; + $user->verified_phone_number = true; $user->save(); return response()->json(['message' => 'SMS verified'], 200); @@ -126,7 +123,6 @@ class TwilioController extends BaseController $twilio = new Client($sid, $token); - try { $verification = $twilio->verify ->v2 @@ -167,9 +163,11 @@ class TwilioController extends BaseController "code" => $request->code ]); - if($verification_check->status == 'approved'){ + if($request->query('validate_only') == 'true') + return response()->json(['message' => 'SMS verified'], 200); + $user->google_2fa_secret = ''; $user->sms_verification_code = ''; $user->save(); diff --git a/app/Http/Requests/User/UpdateUserRequest.php b/app/Http/Requests/User/UpdateUserRequest.php index c96417ae885c..e8c7010d413b 100644 --- a/app/Http/Requests/User/UpdateUserRequest.php +++ b/app/Http/Requests/User/UpdateUserRequest.php @@ -65,8 +65,9 @@ class UpdateUserRequest extends Request $input['last_name'] = strip_tags($input['last_name']); } - if(array_key_exists('phone', $input) && isset($input['phone']) && strlen($input['phone']) > 1 && ($this->user->phone != $input['phone'])) + if(array_key_exists('phone', $input) && isset($input['phone']) && strlen($input['phone']) > 1 && ($this->user->phone != $input['phone'])){ $this->phone_has_changed = true; + } if(array_key_exists('oauth_provider_id', $input) && $input['oauth_provider_id'] == '') $input['oauth_user_id'] = ''; diff --git a/app/Http/ValidationRules/User/HasValidPhoneNumber.php b/app/Http/ValidationRules/User/HasValidPhoneNumber.php index 38539d72c38f..2be9f867dd85 100644 --- a/app/Http/ValidationRules/User/HasValidPhoneNumber.php +++ b/app/Http/ValidationRules/User/HasValidPhoneNumber.php @@ -68,7 +68,7 @@ class HasValidPhoneNumber implements Rule request()->merge(['validated_phone' => $phone_number->phoneNumber ]); - $user->verified_phone_number = true; + $user->verified_phone_number = false; $user->save(); return true; From e10eb78ac8d87fc07fda60be8610cdf97635b307 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 9 Nov 2022 22:36:05 +1100 Subject: [PATCH 03/12] Fixes for SEPA auto-billing --- app/PaymentDrivers/Stripe/Charge.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/PaymentDrivers/Stripe/Charge.php b/app/PaymentDrivers/Stripe/Charge.php index eb6bc14558e3..691f6ca5bb13 100644 --- a/app/PaymentDrivers/Stripe/Charge.php +++ b/app/PaymentDrivers/Stripe/Charge.php @@ -137,9 +137,6 @@ class Charge return false; } - if($response?->status != 'succeeded') - $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400)); - if ($cgt->gateway_type_id == GatewayType::SEPA) { $payment_method_type = PaymentType::SEPA; $status = Payment::STATUS_PENDING; @@ -148,6 +145,12 @@ class Charge $status = Payment::STATUS_COMPLETED; } + if($response?->status == 'processing'){ + //allows us to jump over the next stage - used for SEPA + }elseif($response?->status != 'succeeded'){ + $this->stripe->processInternallyFailedPayment($this->stripe, new \Exception('Auto billing failed.',400)); + } + $data = [ 'gateway_type_id' => $cgt->gateway_type_id, 'payment_type' => $this->transformPaymentTypeToConstant($payment_method_type), From d70a9ddd05a1c8579b97f593fda0361329cdbf95 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 07:49:45 +1100 Subject: [PATCH 04/12] Rate limit bank syncing internally, not at edge of application --- app/Http/Controllers/BankIntegrationController.php | 7 ++++++- routes/api.php | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/Http/Controllers/BankIntegrationController.php b/app/Http/Controllers/BankIntegrationController.php index d267107ca3c3..b061adc46acf 100644 --- a/app/Http/Controllers/BankIntegrationController.php +++ b/app/Http/Controllers/BankIntegrationController.php @@ -27,7 +27,7 @@ use App\Services\Bank\BankService; use App\Transformers\BankIntegrationTransformer; use App\Utils\Traits\MakesHash; use Illuminate\Http\Request; - +use Illuminate\Support\Facades\Cache; class BankIntegrationController extends BaseController { @@ -572,12 +572,17 @@ class BankIntegrationController extends BaseController $account = auth()->user()->account; + if(Cache::get("throttle_polling:{$account->key}")) + return response()->json(BankIntegration::query()->company(), 200); + $account->bank_integrations->each(function ($bank_integration) use ($account){ ProcessBankTransactions::dispatch($account->bank_integration_account_id, $bank_integration); }); + Cache::put("throttle_polling:{$account->key}", true, 300); + return response()->json(BankIntegration::query()->company(), 200); } diff --git a/routes/api.php b/routes/api.php index 130fb13f67c3..d601bf2bd942 100644 --- a/routes/api.php +++ b/routes/api.php @@ -109,7 +109,7 @@ Route::group(['middleware' => ['throttle:10,1','api_secret_check','email_db']], Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () { Route::put('accounts/{account}', [AccountController::class, 'update'])->name('account.update'); Route::resource('bank_integrations', BankIntegrationController::class); // name = (clients. index / create / show / update / destroy / edit - Route::post('bank_integrations/refresh_accounts', [BankIntegrationController::class, 'refreshAccounts'])->name('bank_integrations.refresh_accounts')->middleware('throttle:1,1'); + Route::post('bank_integrations/refresh_accounts', [BankIntegrationController::class, 'refreshAccounts'])->name('bank_integrations.refresh_accounts')->middleware('throttle:30,1'); Route::post('bank_integrations/remove_account/{acc_id}', [BankIntegrationController::class, 'removeAccount'])->name('bank_integrations.remove_account'); Route::post('bank_integrations/get_transactions/{acc_id}', [BankIntegrationController::class, 'getTransactions'])->name('bank_integrations.transactions')->middleware('throttle:1,1'); From 194defa49c6492d4cce59003469fe8d385e0edab Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 07:55:40 +1100 Subject: [PATCH 05/12] Minor fixes for Paypal --- app/PaymentDrivers/PayPalExpressPaymentDriver.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/PaymentDrivers/PayPalExpressPaymentDriver.php index c69f680dec2b..4c2e5e7db882 100644 --- a/app/PaymentDrivers/PayPalExpressPaymentDriver.php +++ b/app/PaymentDrivers/PayPalExpressPaymentDriver.php @@ -43,6 +43,11 @@ class PayPalExpressPaymentDriver extends BaseDriver ]; } + public function init() + { + return $this; + } + /** * Initialize Omnipay PayPal_Express gateway. * From 3466d5384527e3b4066b37655ed96e343100b30b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 17:28:00 +1100 Subject: [PATCH 06/12] Fixes for the Forte payment driver --- app/Filters/QuoteFilters.php | 9 ++++---- .../ClientPortal/InvoiceController.php | 2 -- .../Requests/Account/UpdateAccountRequest.php | 2 +- .../UpdateBankTransactionRequest.php | 1 - app/PaymentDrivers/Forte/CreditCard.php | 21 ++++++++++++------- app/PaymentDrivers/SquarePaymentDriver.php | 9 -------- 6 files changed, 19 insertions(+), 25 deletions(-) diff --git a/app/Filters/QuoteFilters.php b/app/Filters/QuoteFilters.php index 1c1d298eba21..f619559b621c 100644 --- a/app/Filters/QuoteFilters.php +++ b/app/Filters/QuoteFilters.php @@ -33,10 +33,11 @@ class QuoteFilters extends QueryFilters } return $this->builder->where(function ($query) use ($filter) { - $query->where('quotes.custom_value1', 'like', '%'.$filter.'%') - ->orWhere('quotes.custom_value2', 'like', '%'.$filter.'%') - ->orWhere('quotes.custom_value3', 'like', '%'.$filter.'%') - ->orWhere('quotes.custom_value4', 'like', '%'.$filter.'%'); + $query->where('quotes.number', 'like', '%'.$filter.'%') + ->orwhere('quotes.custom_value1', 'like', '%'.$filter.'%') + ->orWhere('quotes.custom_value2', 'like', '%'.$filter.'%') + ->orWhere('quotes.custom_value3', 'like', '%'.$filter.'%') + ->orWhere('quotes.custom_value4', 'like', '%'.$filter.'%'); }); } diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index 4b835e8ef48c..622b64d20e73 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -56,8 +56,6 @@ class InvoiceController extends Controller { set_time_limit(0); - // $invoice->service()->removeUnpaidGatewayFees()->save(); - $invitation = $invoice->invitations()->where('client_contact_id', auth()->guard('contact')->user()->id)->first(); if ($invitation && auth()->guard('contact') && ! session()->get('is_silent') && ! $invitation->viewed_date) { diff --git a/app/Http/Requests/Account/UpdateAccountRequest.php b/app/Http/Requests/Account/UpdateAccountRequest.php index f39af8be11f5..1ac2534ea642 100644 --- a/app/Http/Requests/Account/UpdateAccountRequest.php +++ b/app/Http/Requests/Account/UpdateAccountRequest.php @@ -26,7 +26,7 @@ class UpdateAccountRequest extends Request */ public function authorize() { - return (auth()->user()->isAdmin() || auth()->user()->isOwner()) && (int) $this->account->id === auth()->user()->account_id; + return (auth()->user()->isAdmin() || auth()->user()->isOwner()) && ($this->account->id == auth()->user()->account_id); } /** diff --git a/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php b/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php index fb5927590d5c..9fa3f7ccb240 100644 --- a/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php +++ b/app/Http/Requests/BankTransaction/UpdateBankTransactionRequest.php @@ -33,7 +33,6 @@ class UpdateBankTransactionRequest extends Request /* Ensure we have a client name, and that all emails are unique*/ $rules = [ 'date' => 'bail|required|date', - 'description' => 'bail|sometimes|string', 'amount' => 'numeric|required', ]; diff --git a/app/PaymentDrivers/Forte/CreditCard.php b/app/PaymentDrivers/Forte/CreditCard.php index 2ff3b94f3e85..336ef7b525b1 100644 --- a/app/PaymentDrivers/Forte/CreditCard.php +++ b/app/PaymentDrivers/Forte/CreditCard.php @@ -89,18 +89,23 @@ class CreditCard public function paymentResponse(PaymentResponseRequest $request) { - $payment_hash = PaymentHash::whereRaw('BINARY `hash`= ?', [$request->input('payment_hash')])->firstOrFail(); + $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; - 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); - if ($calculated_amount_with_fee == $amount_with_fee) { - $fee_total = round($calculated_fee / 100,2); - $amount_with_fee = $calculated_amount_with_fee; - break; + $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) + { + 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); + if ($calculated_amount_with_fee == $amount_with_fee) { + $fee_total = round($calculated_fee / 100,2); + $amount_with_fee = $calculated_amount_with_fee; + break; + } } } diff --git a/app/PaymentDrivers/SquarePaymentDriver.php b/app/PaymentDrivers/SquarePaymentDriver.php index 44acfbfc64e0..2e6c1659f56b 100644 --- a/app/PaymentDrivers/SquarePaymentDriver.php +++ b/app/PaymentDrivers/SquarePaymentDriver.php @@ -106,15 +106,6 @@ class SquarePaymentDriver extends BaseDriver /** @var ApiResponse */ $response = $this->square->getRefundsApi()->refund($body); - // if ($response->isSuccess()) { - // return [ - // 'transaction_reference' => $refund->action_id, - // 'transaction_response' => json_encode($response), - // 'success' => $checkout_payment->status == 'Refunded', - // 'description' => $checkout_payment->status, - // 'code' => $checkout_payment->http_code, - // ]; - // } } public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash) From 40055a0095d0adc932799ec0a6322a61f26cf855 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 18:06:09 +1100 Subject: [PATCH 07/12] Fixes for csv inget --- app/Import/Transformers/Bank/BankTransformer.php | 4 ++-- app/Jobs/Import/CSVIngest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Import/Transformers/Bank/BankTransformer.php b/app/Import/Transformers/Bank/BankTransformer.php index 80086111bc7a..cfdb2b01b9bf 100644 --- a/app/Import/Transformers/Bank/BankTransformer.php +++ b/app/Import/Transformers/Bank/BankTransformer.php @@ -56,10 +56,10 @@ class BankTransformer extends BaseTransformer private function calculateType($transaction) { - if(array_key_exists('bank.base_type', $transaction) && $transaction['bank.base_type'] == 'CREDIT') + if(array_key_exists('bank.base_type', $transaction) && ($transaction['bank.base_type'] == 'CREDIT') || strtolower($transaction['bank.base_type']) == 'deposit') return 'CREDIT'; - if(array_key_exists('bank.base_type', $transaction) && $transaction['bank.base_type'] == 'DEBIT') + if(array_key_exists('bank.base_type', $transaction) && ($transaction['bank.base_type'] == 'DEBIT') || strtolower($transaction['bank.bank_type']) == 'withdrawal') return 'DEBIT'; if(array_key_exists('bank.category_id', $transaction)) diff --git a/app/Jobs/Import/CSVIngest.php b/app/Jobs/Import/CSVIngest.php index fea1c441542f..1f9a652d906a 100644 --- a/app/Jobs/Import/CSVIngest.php +++ b/app/Jobs/Import/CSVIngest.php @@ -83,8 +83,8 @@ class CSVIngest implements ShouldQueue $this->checkContacts(); - if(Ninja::isHosted()) - app('queue.worker')->shouldQuit = 1; + // if(Ninja::isHosted()) + // app('queue.worker')->shouldQuit = 1; } From 9ac8e8b6de965f178154a0f9d4eff2a74eba002d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 18:34:26 +1100 Subject: [PATCH 08/12] Debugging imports --- app/Import/Providers/Csv.php | 4 ++++ app/Jobs/Import/CSVIngest.php | 1 + 2 files changed, 5 insertions(+) diff --git a/app/Import/Providers/Csv.php b/app/Import/Providers/Csv.php index fdf70ca691e7..f718e01d1f8f 100644 --- a/app/Import/Providers/Csv.php +++ b/app/Import/Providers/Csv.php @@ -115,10 +115,14 @@ class Csv extends BaseImport implements ImportInterface $data = $this->getCsvData($entity_type); +nlog($data); + if (is_array($data)) { $data = $this->preTransformCsv($data, $entity_type); } +nlog($data); + if (empty($data)) { $this->entity_count['clients'] = 0; diff --git a/app/Jobs/Import/CSVIngest.php b/app/Jobs/Import/CSVIngest.php index 1f9a652d906a..d61961ed9f69 100644 --- a/app/Jobs/Import/CSVIngest.php +++ b/app/Jobs/Import/CSVIngest.php @@ -76,6 +76,7 @@ class CSVIngest implements ShouldQueue $engine = $this->bootEngine(); foreach (['client', 'product', 'invoice', 'payment', 'vendor', 'expense', 'quote', 'bank_transaction'] as $entity) { + nlog("importing {$entity}"); $engine->import($entity); } From 14569d2d79acb9f52046eb5e165b02015b60ece1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 18:39:21 +1100 Subject: [PATCH 09/12] Clean up from debugging --- app/Import/Providers/Csv.php | 4 ---- app/Jobs/Import/CSVIngest.php | 4 ---- 2 files changed, 8 deletions(-) diff --git a/app/Import/Providers/Csv.php b/app/Import/Providers/Csv.php index f718e01d1f8f..fdf70ca691e7 100644 --- a/app/Import/Providers/Csv.php +++ b/app/Import/Providers/Csv.php @@ -115,14 +115,10 @@ class Csv extends BaseImport implements ImportInterface $data = $this->getCsvData($entity_type); -nlog($data); - if (is_array($data)) { $data = $this->preTransformCsv($data, $entity_type); } -nlog($data); - if (empty($data)) { $this->entity_count['clients'] = 0; diff --git a/app/Jobs/Import/CSVIngest.php b/app/Jobs/Import/CSVIngest.php index d61961ed9f69..95085ce8682b 100644 --- a/app/Jobs/Import/CSVIngest.php +++ b/app/Jobs/Import/CSVIngest.php @@ -76,7 +76,6 @@ class CSVIngest implements ShouldQueue $engine = $this->bootEngine(); foreach (['client', 'product', 'invoice', 'payment', 'vendor', 'expense', 'quote', 'bank_transaction'] as $entity) { - nlog("importing {$entity}"); $engine->import($entity); } @@ -84,9 +83,6 @@ class CSVIngest implements ShouldQueue $this->checkContacts(); - // if(Ninja::isHosted()) - // app('queue.worker')->shouldQuit = 1; - } private function checkContacts() From d0178d22ae392995f69bab9ba018ff789ec56cec Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 19:16:22 +1100 Subject: [PATCH 10/12] item tax amounts --- app/DataMapper/InvoiceItem.php | 3 +++ app/Helpers/Invoice/InvoiceItemSum.php | 10 +++++----- app/Helpers/Invoice/InvoiceItemSumInclusive.php | 4 ++++ app/Utils/HtmlEngine.php | 1 + app/Utils/Traits/MakesInvoiceValues.php | 12 ++++++++---- app/Utils/VendorHtmlEngine.php | 1 + 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/app/DataMapper/InvoiceItem.php b/app/DataMapper/InvoiceItem.php index 0ea1e9ba8228..7f80d86eb9b3 100644 --- a/app/DataMapper/InvoiceItem.php +++ b/app/DataMapper/InvoiceItem.php @@ -44,6 +44,8 @@ class InvoiceItem public $line_total = 0; public $gross_line_total = 0; + + public $gross_tax_total = 0; public $date = ''; @@ -75,6 +77,7 @@ class InvoiceItem 'sort_id' => 'string', 'line_total' => 'float', 'gross_line_total' => 'float', + 'gross_tax_total' => 'float', 'date' => 'string', 'custom_value1' => 'string', 'custom_value2' => 'string', diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 8e3b702d7f6f..d77f9a30c895 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -30,6 +30,8 @@ class InvoiceItemSum private $gross_line_total; + private $gross_tax_total; + private $currency; private $total_taxes; @@ -111,14 +113,10 @@ class InvoiceItemSum $this->setLineTotal($this->getLineTotal() - $this->formatValue($this->item->discount, $this->currency->precision)); } else { - /*Test 16-08-2021*/ $discount = ($this->item->line_total * ($this->item->discount / 100)); + $this->setLineTotal($this->formatValue(($this->getLineTotal() - $discount), $this->currency->precision)); - /*Test 16-08-2021*/ - //replaces the following - - // $this->setLineTotal($this->getLineTotal() - $this->formatValue(round($this->item->line_total * ($this->item->discount / 100), 2), $this->currency->precision)); } $this->item->is_amount_discount = $this->invoice->is_amount_discount; @@ -160,6 +158,8 @@ class InvoiceItemSum $this->item->gross_line_total = $this->getLineTotal() + $item_tax; + $this->item->gross_tax_total = $item_tax; + return $this; } diff --git a/app/Helpers/Invoice/InvoiceItemSumInclusive.php b/app/Helpers/Invoice/InvoiceItemSumInclusive.php index 213600d3501f..6985c72ef42e 100644 --- a/app/Helpers/Invoice/InvoiceItemSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceItemSumInclusive.php @@ -40,6 +40,8 @@ class InvoiceItemSumInclusive private $tax_collection; + private $gross_tax_total; + public function __construct($invoice) { $this->tax_collection = collect([]); @@ -144,6 +146,8 @@ class InvoiceItemSumInclusive $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); } + $this->item->gross_tax_total = $this->formatValue($item_tax, $this->currency->precision); + $this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision)); return $this; diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 24583def9ec0..443c1b377229 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -485,6 +485,7 @@ class HtmlEngine $data['$product.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')]; $data['$product.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.gross_line_total')]; + $data['$product.gross_tax_total'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')]; $data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')]; $data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')]; diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php index 1c30b237b0fc..5bc4b3fccc1a 100644 --- a/app/Utils/Traits/MakesInvoiceValues.php +++ b/app/Utils/Traits/MakesInvoiceValues.php @@ -161,8 +161,6 @@ trait MakesInvoiceValues if (strlen($user_columns) > 1) { foreach ($items as $key => $item) { -// $tmp = str_replace(array_keys($data), array_values($data), $user_columns); -// $tmp = str_replace(array_keys($item), array_values($item), $tmp); $tmp = strtr($user_columns, $data); $tmp = strtr($tmp, $item); @@ -178,8 +176,6 @@ trait MakesInvoiceValues $table_row .= ''; foreach ($items as $key => $item) { - // $tmp = str_replace(array_keys($item), array_values($item), $table_row); - // $tmp = str_replace(array_keys($data), array_values($data), $tmp); $tmp = strtr($table_row, $item); $tmp = strtr($tmp, $data); @@ -210,11 +206,13 @@ trait MakesInvoiceValues 'tax_name1', 'tax_name2', 'tax_name3', + 'gross_tax_total', ], [ 'tax', 'tax', 'tax', + 'tax', ], $columns ); @@ -329,6 +327,12 @@ trait MakesInvoiceValues $data[$key][$table_type.'.gross_line_total'] = ''; } + if (property_exists($item, 'gross_tax_total')) { + $data[$key][$table_type.'.gross_tax_total'] = ($item->gross_tax_total == 0) ? '' : Number::formatMoney($item->gross_tax_total, $entity); + } else { + $data[$key][$table_type.'.gross_tax_total'] = ''; + } + if (isset($item->discount) && $item->discount > 0) { if ($item->is_amount_discount) { $data[$key][$table_type.'.discount'] = Number::formatMoney($item->discount, $entity); diff --git a/app/Utils/VendorHtmlEngine.php b/app/Utils/VendorHtmlEngine.php index b373db554589..2b14951499c2 100644 --- a/app/Utils/VendorHtmlEngine.php +++ b/app/Utils/VendorHtmlEngine.php @@ -355,6 +355,7 @@ class VendorHtmlEngine $data['$product.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')]; $data['$product.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.gross_line_total')]; + $data['$product.gross_tax_total'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')]; $data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')]; $data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')]; From ce30aa7702ff39a6fdea95da89e479280670c362 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 19:59:52 +1100 Subject: [PATCH 11/12] Improve quality of payment emails --- app/Mail/Engine/PaymentEmailEngine.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/Mail/Engine/PaymentEmailEngine.php b/app/Mail/Engine/PaymentEmailEngine.php index 951fe587a3e1..f1eaac47ffb5 100644 --- a/app/Mail/Engine/PaymentEmailEngine.php +++ b/app/Mail/Engine/PaymentEmailEngine.php @@ -257,6 +257,12 @@ class PaymentEmailEngine extends BaseEmailEngine $invoice_field = $invoice->{$field}; + if(in_array($field, ['amount', 'balance'])) + $invoice_field = Number::formatMoney($invoice_field, $this->client); + + if($field == 'due_date') + $invoice_field = $this->translateDate($invoice_field, $this->client->date_format(), $this->client->locale()); + $invoicex .= ctrans('texts.invoice_number_short') . "{$invoice->number} {$invoice_field}"; } From b3d53a7cd8ee4f46ef0efa83738fc3be8e6fb0d5 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 10 Nov 2022 21:57:55 +1100 Subject: [PATCH 12/12] Line item tax amounts --- app/DataMapper/InvoiceItem.php | 4 ++-- app/Helpers/Invoice/InvoiceItemSum.php | 4 ++-- .../Invoice/InvoiceItemSumInclusive.php | 4 ++-- app/Jobs/Bank/MatchBankTransactions.php | 11 +++++++---- app/Models/BankTransaction.php | 19 +++++++++++++++++++ .../BankTransactionRepository.php | 7 +++++++ app/Utils/HtmlEngine.php | 2 +- app/Utils/Traits/MakesInvoiceValues.php | 8 ++++---- app/Utils/VendorHtmlEngine.php | 2 +- 9 files changed, 45 insertions(+), 16 deletions(-) diff --git a/app/DataMapper/InvoiceItem.php b/app/DataMapper/InvoiceItem.php index 7f80d86eb9b3..7b668f309072 100644 --- a/app/DataMapper/InvoiceItem.php +++ b/app/DataMapper/InvoiceItem.php @@ -45,7 +45,7 @@ class InvoiceItem public $gross_line_total = 0; - public $gross_tax_total = 0; + public $tax_amount = 0; public $date = ''; @@ -77,7 +77,7 @@ class InvoiceItem 'sort_id' => 'string', 'line_total' => 'float', 'gross_line_total' => 'float', - 'gross_tax_total' => 'float', + 'tax_amount' => 'float', 'date' => 'string', 'custom_value1' => 'string', 'custom_value2' => 'string', diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index d77f9a30c895..47ce395e4191 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -30,7 +30,7 @@ class InvoiceItemSum private $gross_line_total; - private $gross_tax_total; + private $tax_amount; private $currency; @@ -158,7 +158,7 @@ class InvoiceItemSum $this->item->gross_line_total = $this->getLineTotal() + $item_tax; - $this->item->gross_tax_total = $item_tax; + $this->item->tax_amount = $item_tax; return $this; } diff --git a/app/Helpers/Invoice/InvoiceItemSumInclusive.php b/app/Helpers/Invoice/InvoiceItemSumInclusive.php index 6985c72ef42e..e7332021b379 100644 --- a/app/Helpers/Invoice/InvoiceItemSumInclusive.php +++ b/app/Helpers/Invoice/InvoiceItemSumInclusive.php @@ -40,7 +40,7 @@ class InvoiceItemSumInclusive private $tax_collection; - private $gross_tax_total; + private $tax_amount; public function __construct($invoice) { @@ -146,7 +146,7 @@ class InvoiceItemSumInclusive $this->groupTax($this->item->tax_name3, $this->item->tax_rate3, $item_tax_rate3_total); } - $this->item->gross_tax_total = $this->formatValue($item_tax, $this->currency->precision); + $this->item->tax_amount = $this->formatValue($item_tax, $this->currency->precision); $this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision)); diff --git a/app/Jobs/Bank/MatchBankTransactions.php b/app/Jobs/Bank/MatchBankTransactions.php index 84f3c0f8879f..14ec163292ca 100644 --- a/app/Jobs/Bank/MatchBankTransactions.php +++ b/app/Jobs/Bank/MatchBankTransactions.php @@ -92,11 +92,14 @@ class MatchBankTransactions implements ShouldQueue $this->company = Company::find($this->company_id); - $yodlee = new Yodlee($this->company->account->bank_integration_account_id); + if($this->company->account->bank_integration_account_id) + $yodlee = new Yodlee($this->company->account->bank_integration_account_id); + else + $yodlee = false; $bank_categories = Cache::get('bank_categories'); - if(!$bank_categories){ + if(!$bank_categories && $yodlee){ $_categories = $yodlee->getTransactionCategories(); $this->categories = collect($_categories->transactionCategory); Cache::forever('bank_categories', $this->categories); @@ -159,7 +162,7 @@ class MatchBankTransactions implements ShouldQueue $_invoices = Invoice::withTrashed()->find($this->getInvoices($input['invoice_ids'])); - $amount = $this->bt->amount; + $amount = $this->bt->amount; if($_invoices && $this->checkPayable($_invoices)){ @@ -220,7 +223,7 @@ class MatchBankTransactions implements ShouldQueue $this->applied_amount += $this->invoice->balance; $this->available_balance = $this->available_balance - $this->invoice->balance; } - elseif(floatval($this->invoice->balance) > floatval($this->available_balance) && $this->available_balance > 0) + elseif(floatval($this->invoice->balance) >= floatval($this->available_balance) && $this->available_balance > 0) { $_amount = $this->available_balance; $this->applied_amount += $this->available_balance; diff --git a/app/Models/BankTransaction.php b/app/Models/BankTransaction.php index c603fae0b089..ece2263542df 100644 --- a/app/Models/BankTransaction.php +++ b/app/Models/BankTransaction.php @@ -11,6 +11,7 @@ namespace App\Models; +use App\Models\Invoice; use App\Utils\Traits\MakesHash; use Illuminate\Database\Eloquent\SoftDeletes; @@ -95,4 +96,22 @@ class BankTransaction extends BaseModel return $this->belongsTo(Account::class)->withTrashed(); } + + public function matchInvoiceNumber() + { + + if(strlen($this->description) > 1) + { + + $i = Invoice::where('company_id', $this->company_id) + ->whereIn('status_id', [1,2,3]) + ->where('is_deleted', 0) + ->where('number', 'LIKE', '%'.$this->description.'%') + ->first(); + + return $i ?: false; + } + + return false; + } } \ No newline at end of file diff --git a/app/Repositories/BankTransactionRepository.php b/app/Repositories/BankTransactionRepository.php index 0d48177101a0..ac53b4aee437 100644 --- a/app/Repositories/BankTransactionRepository.php +++ b/app/Repositories/BankTransactionRepository.php @@ -31,6 +31,13 @@ class BankTransactionRepository extends BaseRepository $bank_transaction->save(); + if($bank_transaction->base_type == 'CREDIT' && $invoice = $bank_transaction->matchInvoiceNumber()) + { + $bank_transaction->invoice_ids = $invoice->hashed_id; + $bank_transaction->status_id = BankTransaction::STATUS_MATCHED; + $bank_transaction->save(); + } + return $bank_transaction; } diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 443c1b377229..f800b6db41a5 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -485,7 +485,7 @@ class HtmlEngine $data['$product.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')]; $data['$product.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.gross_line_total')]; - $data['$product.gross_tax_total'] = ['value' => '', 'label' => ctrans('texts.tax')]; + $data['$product.tax_amount'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')]; $data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')]; $data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')]; diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php index 5bc4b3fccc1a..aa265cb749ee 100644 --- a/app/Utils/Traits/MakesInvoiceValues.php +++ b/app/Utils/Traits/MakesInvoiceValues.php @@ -206,7 +206,7 @@ trait MakesInvoiceValues 'tax_name1', 'tax_name2', 'tax_name3', - 'gross_tax_total', + 'tax_amount', ], [ 'tax', @@ -327,10 +327,10 @@ trait MakesInvoiceValues $data[$key][$table_type.'.gross_line_total'] = ''; } - if (property_exists($item, 'gross_tax_total')) { - $data[$key][$table_type.'.gross_tax_total'] = ($item->gross_tax_total == 0) ? '' : Number::formatMoney($item->gross_tax_total, $entity); + if (property_exists($item, 'tax_amount')) { + $data[$key][$table_type.'.tax_amount'] = ($item->tax_amount == 0) ? '' : Number::formatMoney($item->tax_amount, $entity); } else { - $data[$key][$table_type.'.gross_tax_total'] = ''; + $data[$key][$table_type.'.tax_amount'] = ''; } if (isset($item->discount) && $item->discount > 0) { diff --git a/app/Utils/VendorHtmlEngine.php b/app/Utils/VendorHtmlEngine.php index 2b14951499c2..3cdb0c4ac9dd 100644 --- a/app/Utils/VendorHtmlEngine.php +++ b/app/Utils/VendorHtmlEngine.php @@ -355,7 +355,7 @@ class VendorHtmlEngine $data['$product.tax_name3'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.line_total'] = ['value' => '', 'label' => ctrans('texts.line_total')]; $data['$product.gross_line_total'] = ['value' => '', 'label' => ctrans('texts.gross_line_total')]; - $data['$product.gross_tax_total'] = ['value' => '', 'label' => ctrans('texts.tax')]; + $data['$product.tax_amount'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$product.description'] = ['value' => '', 'label' => ctrans('texts.description')]; $data['$product.unit_cost'] = ['value' => '', 'label' => ctrans('texts.unit_cost')]; $data['$product.product1'] = ['value' => '', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'product1')];