diff --git a/VERSION.txt b/VERSION.txt index bca730d00e5a..d24b80453203 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.7.59 \ No newline at end of file +5.7.60 \ No newline at end of file diff --git a/app/Http/Requests/Credit/UpdateCreditRequest.php b/app/Http/Requests/Credit/UpdateCreditRequest.php index 23ee7007b654..4d15796ac139 100644 --- a/app/Http/Requests/Credit/UpdateCreditRequest.php +++ b/app/Http/Requests/Credit/UpdateCreditRequest.php @@ -60,7 +60,7 @@ class UpdateCreditRequest extends Request $rules['file'] = $this->file_validation; } - $rules['number'] = ['bail', 'sometimes', Rule::unique('credits')->where('company_id', $user->company()->id)->ignore($this->credit->id)]; + $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('credits')->where('company_id', $user->company()->id)->ignore($this->credit->id)]; $rules['client_id'] = ['bail', 'sometimes',Rule::in([$this->credit->client_id])]; diff --git a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php index 319f1d9aab91..1591b9912579 100644 --- a/app/Http/Requests/Invoice/UpdateInvoiceRequest.php +++ b/app/Http/Requests/Invoice/UpdateInvoiceRequest.php @@ -59,9 +59,8 @@ class UpdateInvoiceRequest extends Request $rules['id'] = new LockedInvoiceRule($this->invoice); - $rules['number'] = ['bail', 'sometimes', Rule::unique('invoices')->where('company_id', $user->company()->id)->ignore($this->invoice->id)]; + $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('invoices')->where('company_id', $user->company()->id)->ignore($this->invoice->id)]; - $rules['is_amount_discount'] = ['boolean']; $rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->invoice->client_id])]; $rules['line_items'] = 'array'; diff --git a/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php b/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php index d9549f881f98..800eaf8405ca 100644 --- a/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php +++ b/app/Http/Requests/PurchaseOrder/UpdatePurchaseOrderRequest.php @@ -48,7 +48,7 @@ class UpdatePurchaseOrderRequest extends Request $rules = []; - $rules['number'] = ['bail', 'sometimes', Rule::unique('purchase_orders')->where('company_id', $user->company()->id)->ignore($this->purchase_order->id)]; + $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('purchase_orders')->where('company_id', $user->company()->id)->ignore($this->purchase_order->id)]; $rules['vendor_id'] = ['bail', 'sometimes', Rule::in([$this->purchase_order->vendor_id])]; $rules['line_items'] = 'array'; diff --git a/app/Http/Requests/Quote/UpdateQuoteRequest.php b/app/Http/Requests/Quote/UpdateQuoteRequest.php index 4b473825c88a..4294385b15f2 100644 --- a/app/Http/Requests/Quote/UpdateQuoteRequest.php +++ b/app/Http/Requests/Quote/UpdateQuoteRequest.php @@ -55,7 +55,7 @@ class UpdateQuoteRequest extends Request } - $rules['number'] = ['bail', 'sometimes', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)]; + $rules['number'] = ['bail', 'sometimes', 'nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)]; $rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->quote->client_id])]; @@ -73,6 +73,8 @@ class UpdateQuoteRequest extends Request $input = $this->decodePrimaryKeys($input); + $input['id'] = $this->quote->id; + if (isset($input['line_items'])) { $input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : []; } @@ -85,7 +87,6 @@ class UpdateQuoteRequest extends Request $input['exchange_rate'] = 1; } - $input['id'] = $this->quote->id; $this->replace($input); } diff --git a/app/Jobs/Company/CompanyExport.php b/app/Jobs/Company/CompanyExport.php index 23afdd37738f..0fc36b2500af 100644 --- a/app/Jobs/Company/CompanyExport.php +++ b/app/Jobs/Company/CompanyExport.php @@ -387,19 +387,19 @@ class CompanyExport implements ShouldQueue })->all(); - $this->export_data['bank_integrations'] = $this->company->bank_integrations()->orderBy('id', 'ASC')->cursor()->map(function ($bank_integration) { + $this->export_data['bank_integrations'] = $this->company->bank_integrations()->withTrashed()->orderBy('id', 'ASC')->cursor()->map(function ($bank_integration) { $bank_integration = $this->transformArrayOfKeys($bank_integration, ['account_id','company_id', 'user_id']); - return $bank_integration->makeVisible(['id','user_id','company_id','account_id']); + return $bank_integration->makeVisible(['id','user_id','company_id','account_id','hashed_id']); })->all(); - $this->export_data['bank_transactions'] = $this->company->bank_transactions()->orderBy('id', 'ASC')->cursor()->map(function ($bank_transaction) { - $bank_transaction = $this->transformArrayOfKeys($bank_transaction, ['company_id', 'user_id','bank_integration_id','expense_id','category_id','ninja_category_id','vendor_id']); + $this->export_data['bank_transactions'] = $this->company->bank_transactions()->withTrashed()->orderBy('id', 'ASC')->cursor()->map(function ($bank_transaction) { + $bank_transaction = $this->transformArrayOfKeys($bank_transaction, ['company_id', 'user_id','bank_integration_id','expense_id','ninja_category_id','vendor_id']); return $bank_transaction->makeVisible(['id','user_id','company_id']); })->all(); - $this->export_data['schedulers'] = $this->company->schedulers()->orderBy('id', 'ASC')->cursor()->map(function ($scheduler) { + $this->export_data['schedulers'] = $this->company->schedulers()->withTrashed()->orderBy('id', 'ASC')->cursor()->map(function ($scheduler) { $scheduler = $this->transformArrayOfKeys($scheduler, ['company_id', 'user_id']); return $scheduler->makeVisible(['id','user_id','company_id']); diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index 7c84d721b1e9..44abb45fdf3b 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -63,14 +63,12 @@ use App\Utils\Ninja; use App\Utils\TempFile; use App\Utils\Traits\GeneratesCounter; use App\Utils\Traits\MakesHash; -use function GuzzleHttp\json_encode; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\App; -use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use JsonMachine\JsonDecoder\ExtJsonDecoder; @@ -142,7 +140,6 @@ class CompanyImport implements ShouldQueue 'recurring_expenses', 'expenses', 'tasks', - 'payments', 'company_ledger', 'designs', 'documents', @@ -569,7 +566,7 @@ class CompanyImport implements ShouldQueue ['expenses' => 'expense_id'], ['vendors' => 'vendor_id'], ['expense_categories' => 'ninja_category_id'], - ['expense_categories' => 'category_id'], + // ['expense_categories' => 'category_id'], ['bank_integrations' => 'bank_integration_id'] ], 'bank_transactions', @@ -1143,7 +1140,34 @@ class CompanyImport implements ShouldQueue continue; } + $storage_url = (object)$this->getObject('storage_url', true); + + if (!Storage::exists($document->url) && is_string($storage_url)) { + $url = $storage_url . $document->url; + + $file = @file_get_contents($url); + + + if ($file) { + try { + Storage::disk(config('filesystems.default'))->put($document->url, $file); + + + } catch(\Exception $e) { + nlog($e->getMessage()); + nlog("I could not upload {$document->url}"); + + } + } + else + continue; + + } + else + continue; + $new_document = new Document(); + $new_document->disk = config('filesystems.default'); $new_document->user_id = $this->transformId('users', $document->user_id); $new_document->assigned_user_id = $this->transformId('users', $document->assigned_user_id); $new_document->company_id = $this->company->id; @@ -1169,26 +1193,6 @@ class CompanyImport implements ShouldQueue $new_document->save(['timestamps' => false]); - $storage_url = (object)$this->getObject('storage_url', true); - - if (!Storage::exists($new_document->url) && is_string($storage_url)) { - $url = $storage_url . $new_document->url; - - $file = @file_get_contents($url); - - if ($file) { - try { - Storage::disk(config('filesystems.default'))->put($new_document->url, $file); - - $new_document->disk = config('filesystems.default'); - $new_document->save(); - } catch(\Exception $e) { - nlog($e->getMessage()); - nlog("I could not upload {$new_document->url}"); - $new_document->forceDelete(); - } - } - } } return $this; @@ -1727,7 +1731,9 @@ class CompanyImport implements ShouldQueue */ private function transformId(string $resource, ?string $old): ?int { - if (empty($old)) { + + // WjnegYbwZ1 == 0 return null; + if (empty($old) || $old == 'WjnegYbwZ1') { return null; } @@ -1736,6 +1742,7 @@ class CompanyImport implements ShouldQueue } if (! array_key_exists($resource, $this->ids)) { + $this->sendImportMail("The Import failed due to missing data in the import file. Resource {$resource} not available."); throw new \Exception("Resource {$resource} not available."); @@ -1744,16 +1751,12 @@ class CompanyImport implements ShouldQueue if (! array_key_exists("{$old}", $this->ids[$resource])) { // nlog($this->ids[$resource]); nlog("searching for {$old} in {$resource}"); - - nlog("If we are missing a user - default to the company owner"); if ($resource == 'users') { return $this->company_owner->id; } - $this->sendImportMail("The Import failed due to missing data in the import file. Resource {$resource} not available."); - - nlog($this->ids[$resource]); + $this->sendImportMail("The Import failed due to missing data in the import file. Key {$old} not found in {$resource}."); throw new \Exception("Missing {$resource} key: {$old}"); } diff --git a/app/Jobs/Mail/PaymentFailedMailer.php b/app/Jobs/Mail/PaymentFailedMailer.php index 461b1a01c695..15b9bdb61c8f 100644 --- a/app/Jobs/Mail/PaymentFailedMailer.php +++ b/app/Jobs/Mail/PaymentFailedMailer.php @@ -105,7 +105,7 @@ class PaymentFailedMailer implements ShouldQueue }); //add client payment failures here. - // + if ($this->client->contacts()->whereNotNull('email')->exists() && $this->payment_hash) { $contact = $this->client->contacts()->whereNotNull('email')->first(); diff --git a/app/PaymentDrivers/GoCardless/InstantBankPay.php b/app/PaymentDrivers/GoCardless/InstantBankPay.php index 3d5cf238272a..b6d8f31faea1 100644 --- a/app/PaymentDrivers/GoCardless/InstantBankPay.php +++ b/app/PaymentDrivers/GoCardless/InstantBankPay.php @@ -113,6 +113,10 @@ class InstantBankPay implements MethodInterface return $this->processSuccessfulPayment($payment); } + if ($billing_request->status === 'submitted') { + return $this->processPendingPayment($payment); + } + return $this->processUnsuccessfulPayment($payment); } catch (\Exception $exception) { throw new PaymentFailed( @@ -125,7 +129,40 @@ class InstantBankPay implements MethodInterface /** * Handle pending payments for Instant Bank Transfer. * - * @param ResourcesPayment $payment + * @param \GoCardlessPro\Resources\Payment $payment + * @param array $data + * @return RedirectResponse + */ + public function processPendingPayment(\GoCardlessPro\Resources\Payment $payment, array $data = []) + { + $data = [ + 'payment_method' => $payment->links->mandate, + 'payment_type' => PaymentType::INSTANT_BANK_PAY, + 'amount' => $this->go_cardless->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_PENDING); + + SystemLogger::dispatch( + ['response' => $payment, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_GOCARDLESS, + $this->go_cardless->client, + $this->go_cardless->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->go_cardless->encodePrimaryKey($payment->id)]); + } + + + + /** + * Handle pending payments for Instant Bank Transfer. + * + * @param \GoCardlessPro\Resources\Payment $payment * @param array $data * @return RedirectResponse */ @@ -163,12 +200,12 @@ class InstantBankPay implements MethodInterface { PaymentFailureMailer::dispatch($this->go_cardless->client, $payment->status, $this->go_cardless->client->company, $this->go_cardless->payment_hash->data->amount_with_fee); - PaymentFailureMailer::dispatch( - $this->go_cardless->client, - $payment, - $this->go_cardless->client->company, - $payment->amount - ); + // PaymentFailureMailer::dispatch( + // $this->go_cardless->client, + // $payment, + // $this->go_cardless->client->company, + // $payment->amount + // ); $message = [ 'server_response' => $payment, diff --git a/app/PaymentDrivers/GoCardlessPaymentDriver.php b/app/PaymentDrivers/GoCardlessPaymentDriver.php index 00db105fa717..cca2f9ba8f98 100644 --- a/app/PaymentDrivers/GoCardlessPaymentDriver.php +++ b/app/PaymentDrivers/GoCardlessPaymentDriver.php @@ -165,7 +165,7 @@ class GoCardlessPaymentDriver extends BaseDriver ], ]); - if ($payment->status === 'pending_submission') { + if (in_array($payment->status, ['submitted', 'pending_submission'])) { $this->confirmGatewayFee(); $data = [ diff --git a/config/ninja.php b/config/ninja.php index 94d0a307fc6a..87c3a2fa2a94 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -17,8 +17,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION', '5.7.59'), - 'app_tag' => env('APP_TAG', '5.7.59'), + 'app_version' => env('APP_VERSION', '5.7.60'), + 'app_tag' => env('APP_TAG', '5.7.60'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false),