diff --git a/VERSION.txt b/VERSION.txt index 2a06a418a773..fad8076f307d 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.6.4 \ No newline at end of file +5.6.5 \ No newline at end of file diff --git a/app/DataMapper/Tax/US/Rule.php b/app/DataMapper/Tax/US/Rule.php index 59cd75943ca5..469d16c74a6f 100644 --- a/app/DataMapper/Tax/US/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -48,7 +48,6 @@ class Rule extends BaseRule implements RuleInterface { $this->tax_rate1 = $item->tax_rate1; - $this->tax_name1 = $item->tax_name1; return $this; @@ -117,6 +116,9 @@ class Rule extends BaseRule implements RuleInterface if(in_array($this->tax_data?->txbService,['Y','L'])) { $this->default($item); } + else { + $this->taxExempt($item); + } return $this; } diff --git a/app/Filters/ExpenseFilters.php b/app/Filters/ExpenseFilters.php index 325606b30664..6260c1b04b3c 100644 --- a/app/Filters/ExpenseFilters.php +++ b/app/Filters/ExpenseFilters.php @@ -106,10 +106,24 @@ class ExpenseFilters extends QueryFilters return $this->builder; } + /** + * Filter expenses that only have invoices + * + * @param string $value + * @return Builder + */ public function has_invoices(string $value = ''): Builder { - if ($value == 'true') { - return $this->builder->whereNotNull('invoice_id'); + $split = explode(",", $value); + + if (is_array($split) && in_array($split[0], ['client', 'project'])) { + + $search_key = $split[0] == 'client' ? 'client_id' : 'project_id'; + + return $this->builder->whereHas('invoice', function ($query) use ($search_key, $split){ + $query->where($search_key, $this->decodePrimaryKey($split[1])) + ->whereIn('status_id', [\App\Models\Invoice::STATUS_DRAFT, \App\Models\Invoice::STATUS_SENT, \App\Models\Invoice::STATUS_PARTIAL]); + }); } return $this->builder; diff --git a/app/Http/Controllers/ConnectedAccountController.php b/app/Http/Controllers/ConnectedAccountController.php index c93d05477935..614a4ccb9717 100644 --- a/app/Http/Controllers/ConnectedAccountController.php +++ b/app/Http/Controllers/ConnectedAccountController.php @@ -90,14 +90,15 @@ class ConnectedAccountController extends BaseController private function handleMicrosoftOauth($request) { - nlog($request->all()); + $access_token = false; + $access_token = $request->has('access_token') ? $request->input('access_token') : $request->input('accessToken'); - if (!$request->has('access_token')) { + if (!$access_token) { return response()->json(['message' => 'No access_token parameter found!'], 400); } $graph = new \Microsoft\Graph\Graph(); - $graph->setAccessToken($request->input('access_token')); + $graph->setAccessToken($access_token); $user = $graph->createRequest("GET", "/me") ->setReturnType(Model\User::class) diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 6f515dbcf920..1c691f36f577 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -408,7 +408,7 @@ class InvoiceController extends BaseController $invoice->service() ->triggeredActions($request) - ->touchPdf() + ->deletePdf() ->adjustInventory($old_invoice); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); @@ -740,7 +740,8 @@ class InvoiceController extends BaseController } break; case 'cancel': - $invoice = $invoice->service()->handleCancellation()->touchPdf()->save(); + $invoice = $invoice->service()->handleCancellation()->deletePdf()->save(); + // $invoice = $invoice->service()->handleCancellation()->touchPdf()->save(); if (! $bulk) { $this->itemResponse($invoice); diff --git a/app/Jobs/Bank/MatchBankTransactions.php b/app/Jobs/Bank/MatchBankTransactions.php index 31c1e1538059..f2a44c60c809 100644 --- a/app/Jobs/Bank/MatchBankTransactions.php +++ b/app/Jobs/Bank/MatchBankTransactions.php @@ -362,7 +362,7 @@ class MatchBankTransactions implements ShouldQueue $this->invoice ->service() ->applyNumber() - ->touchPdf() + ->deletePdf() ->save(); $payment->ledger() diff --git a/app/Jobs/Mail/NinjaMailerJob.php b/app/Jobs/Mail/NinjaMailerJob.php index 3b8611bb3f1e..0e81d544f9be 100644 --- a/app/Jobs/Mail/NinjaMailerJob.php +++ b/app/Jobs/Mail/NinjaMailerJob.php @@ -243,19 +243,20 @@ class NinjaMailerJob implements ShouldQueue case 'gmail': $this->mailer = 'gmail'; $this->setGmailMailer(); - return; + return $this; case 'office365': + case 'microsoft': $this->mailer = 'office365'; $this->setOfficeMailer(); - return; + return $this; case 'client_postmark': $this->mailer = 'postmark'; $this->setPostmarkMailer(); - return; + return $this; case 'client_mailgun': $this->mailer = 'mailgun'; $this->setMailgunMailer(); - return; + return $this; default: break; @@ -264,6 +265,8 @@ class NinjaMailerJob implements ShouldQueue if (Ninja::isSelfHost()) { $this->setSelfHostMultiMailer(); } + + return $this; } /** diff --git a/app/Jobs/Util/Import.php b/app/Jobs/Util/Import.php index 674b4e7c0579..b45f3ead4cfa 100644 --- a/app/Jobs/Util/Import.php +++ b/app/Jobs/Util/Import.php @@ -11,85 +11,89 @@ namespace App\Jobs\Util; -use App\DataMapper\Analytics\MigrationFailure; -use App\DataMapper\CompanySettings; -use App\Exceptions\ClientHostedMigrationException; -use App\Exceptions\MigrationValidatorFailed; -use App\Exceptions\ResourceDependencyMissing; +use Exception; +use App\Models\Task; +use App\Models\User; +use App\Utils\Ninja; +use App\Models\Quote; +use App\Models\Client; +use App\Models\Credit; +use App\Models\Vendor; +use App\Models\Company; +use App\Models\Expense; +use App\Models\Invoice; +use App\Models\Payment; +use App\Models\Product; +use App\Models\Project; +use App\Models\TaxRate; +use App\Models\Activity; +use App\Models\Document; +use App\Libraries\MultiDB; +use App\Models\TaskStatus; +use App\Models\PaymentTerm; +use Illuminate\Support\Str; +use App\Factory\UserFactory; +use App\Factory\QuoteFactory; +use App\Models\ClientContact; +use Illuminate\Bus\Queueable; use App\Factory\ClientFactory; -use App\Factory\CompanyLedgerFactory; use App\Factory\CreditFactory; +use App\Factory\VendorFactory; +use App\Models\CompanyGateway; +use Illuminate\Support\Carbon; use App\Factory\InvoiceFactory; use App\Factory\PaymentFactory; use App\Factory\ProductFactory; -use App\Factory\QuoteFactory; -use App\Factory\RecurringInvoiceFactory; use App\Factory\TaxRateFactory; -use App\Factory\UserFactory; -use App\Factory\VendorFactory; -use App\Http\Requests\Company\UpdateCompanyRequest; -use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule; -use App\Http\ValidationRules\ValidUserForCompany; -use App\Jobs\Company\CreateCompanyToken; -use App\Jobs\Mail\NinjaMailerJob; -use App\Jobs\Mail\NinjaMailerObject; -use App\Jobs\Ninja\CheckCompanyData; -use App\Libraries\MultiDB; -use App\Mail\Migration\StripeConnectMigration; -use App\Mail\MigrationCompleted; -use App\Models\Activity; -use App\Models\Client; -use App\Models\ClientContact; -use App\Models\ClientGatewayToken; -use App\Models\Company; -use App\Models\CompanyGateway; -use App\Models\Credit; -use App\Models\Document; -use App\Models\Expense; +use App\Jobs\Util\VersionCheck; use App\Models\ExpenseCategory; -use App\Models\Invoice; -use App\Models\Payment; -use App\Models\PaymentTerm; -use App\Models\Product; -use App\Models\Project; -use App\Models\Quote; +use App\Utils\Traits\MakesHash; +use App\Mail\MigrationCompleted; use App\Models\RecurringExpense; use App\Models\RecurringInvoice; -use App\Models\Task; -use App\Models\TaskStatus; -use App\Models\TaxRate; -use App\Models\User; -use App\Models\Vendor; -use App\Repositories\ClientContactRepository; -use App\Repositories\ClientRepository; -use App\Repositories\CompanyRepository; -use App\Repositories\CreditRepository; -use App\Repositories\Migration\InvoiceMigrationRepository; -use App\Repositories\Migration\PaymentMigrationRepository; -use App\Repositories\ProductRepository; -use App\Repositories\UserRepository; -use App\Repositories\VendorContactRepository; -use App\Repositories\VendorRepository; -use App\Utils\Ninja; -use App\Utils\Traits\CleanLineItems; -use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver; -use App\Utils\Traits\MakesHash; -use App\Utils\Traits\SavesDocuments; use App\Utils\Traits\Uploadable; -use Exception; -use Illuminate\Bus\Queueable; +use App\Jobs\Mail\NinjaMailerJob; +use Illuminate\Http\UploadedFile; +use App\Models\ClientGatewayToken; +use Illuminate\Support\Facades\DB; +use App\DataMapper\CompanySettings; +use Illuminate\Support\Facades\App; +use App\Jobs\Mail\NinjaMailerObject; +use App\Jobs\Ninja\CheckCompanyData; +use App\Repositories\UserRepository; +use App\Utils\Traits\CleanLineItems; +use App\Utils\Traits\SavesDocuments; +use Illuminate\Support\Facades\Mail; +use App\Factory\CompanyLedgerFactory; +use App\Repositories\ClientRepository; +use App\Repositories\CreditRepository; +use App\Repositories\VendorRepository; +use Illuminate\Queue\SerializesModels; +use Turbo124\Beacon\Facades\LightLogs; +use App\Repositories\CompanyRepository; +use App\Repositories\ProductRepository; +use App\Factory\RecurringInvoiceFactory; +use App\Jobs\Company\CreateCompanyToken; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Support\Facades\Validator; +use Modules\Admin\Jobs\Account\NinjaUser; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Http\UploadedFile; -use Illuminate\Queue\InteractsWithQueue; +use App\DataMapper\ClientRegistrationFields; +use App\Exceptions\MigrationValidatorFailed; +use App\Exceptions\ResourceDependencyMissing; +use App\Repositories\ClientContactRepository; +use App\Repositories\VendorContactRepository; +use App\DataMapper\Analytics\MigrationFailure; +use App\Mail\Migration\StripeConnectMigration; +use App\Http\ValidationRules\ValidUserForCompany; +use App\Exceptions\ClientHostedMigrationException; +use App\Http\Requests\Company\UpdateCompanyRequest; use Illuminate\Queue\Middleware\WithoutOverlapping; -use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\App; -use Illuminate\Support\Facades\Mail; -use Illuminate\Support\Facades\Validator; -use Illuminate\Support\Str; -use Turbo124\Beacon\Facades\LightLogs; +use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver; +use App\Repositories\Migration\InvoiceMigrationRepository; +use App\Repositories\Migration\PaymentMigrationRepository; +use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule; class Import implements ShouldQueue { @@ -1505,7 +1509,19 @@ class Import implements ShouldQueue false ); - $this->saveDocument($uploaded_file, $entity, $is_public = true); + // $this->saveDocument($uploaded_file, $entity, $is_public = true); + + $document = (new \App\Jobs\Util\UploadFile( + $uploaded_file, + \App\Jobs\Util\UploadFile::DOCUMENT, + $this->user, + $this->company, + $entity, + null, + true + ))->handle(); + + } catch(\Exception $e) { //do nothing, gracefully :) } diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index e11234f6785c..879b759204fd 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -208,7 +208,8 @@ class ReminderJob implements ShouldQueue ->markSent() ->save(); - $invoice->service()->touchPdf(true); + //30-6-2023 - fix for duplicate touching + // $invoice->service()->touchPdf(true); $enabled_reminder = 'enable_'.$reminder_template; if ($reminder_template == 'endless_reminder') { @@ -268,7 +269,6 @@ class ReminderJob implements ShouldQueue } return [$late_fee_amount, $late_fee_percent]; - // return $this->setLateFee($invoice, $late_fee_amount, $late_fee_percent); } /** diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index e33453092eeb..fc3dbface7c3 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -11,15 +11,17 @@ namespace App\Models; +use Illuminate\Support\Str; +use Illuminate\Support\Carbon; +use App\Utils\Traits\MakesHash; +use App\Jobs\Entity\CreateRawPdf; use App\Jobs\Util\WebhookHandler; use App\Models\Traits\Excludable; -use App\Utils\Traits\MakesHash; +use Illuminate\Database\Eloquent\Model; +use App\Jobs\Vendor\CreatePurchaseOrderPdf; use App\Utils\Traits\UserSessionAttributes; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException; -use Illuminate\Support\Carbon; -use Illuminate\Support\Str; /** * Class BaseModel @@ -255,4 +257,30 @@ class BaseModel extends Model WebhookHandler::dispatch($event_id, $this, $this->company, $additional_data); } } + + /** + * Returns the base64 encoded PDF string of the entity + */ + public function fullscreenPdfViewer($invitation = null): string + { + + if (! $invitation) { + if ($this->invitations()->exists()) { + $invitation = $this->invitations()->first(); + } else { + $this->service()->createInvitations(); + $invitation = $this->invitations()->first(); + } + } + + if (! $invitation) { + throw new \Exception('Hard fail, could not create an invitation.'); + } + + if($this instanceof \App\Models\PurchaseOrder) + return "data:application/pdf;base64,".base64_encode((new CreatePurchaseOrderPdf($invitation, $invitation->company->db))->rawPdf()); + + return "data:application/pdf;base64,".base64_encode((new CreateRawPdf($invitation, $invitation->company->db))->handle()); + + } } diff --git a/app/Models/Client.php b/app/Models/Client.php index 5a4ec88f74b1..959243ccb768 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -544,9 +544,9 @@ class Client extends BaseModel implements HasLocalePreference return $this->settings->{$setting}; } elseif (is_bool($this->settings->{$setting})) { return $this->settings->{$setting}; - } elseif (is_int($this->settings->{$setting})) { //10-08-2022 integer client values are not being passed back! This resolves it. + } elseif (is_int($this->settings->{$setting})) { return $this->settings->{$setting}; - } elseif(is_float($this->settings->{$setting})) { //10-08-2022 integer client values are not being passed back! This resolves it. + } elseif(is_float($this->settings->{$setting})) { return $this->settings->{$setting}; } } diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index dfdf67c2691a..f390f21fc507 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -14,6 +14,7 @@ namespace App\Models; use App\Utils\Ninja; use Illuminate\Support\Carbon; use App\Utils\Traits\MakesDates; +use App\Jobs\Entity\CreateRawPdf; use App\Helpers\Invoice\InvoiceSum; use App\Jobs\Entity\CreateEntityPdf; use App\Utils\Traits\MakesReminders; @@ -683,6 +684,7 @@ class Invoice extends BaseModel public function pdf_file_path($invitation = null, string $type = 'path', bool $portal = false) { + if (! $invitation) { if ($this->invitations()->exists()) { $invitation = $this->invitations()->first(); @@ -725,7 +727,6 @@ class Invoice extends BaseModel return Storage::disk(config('filesystems.default'))->{$type}($file_path); } - try { $file_exists = Storage::disk('public')->exists($file_path); } catch (\Exception $e) { diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 4bad13f054a3..4f2c6807b706 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -525,7 +525,7 @@ class BaseDriver extends AbstractPaymentDriver $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get(); $invoices->each(function ($invoice) { - $invoice->service()->touchPdf(); + $invoice->service()->deletePdf(); }); $invoices->first()->invitations->each(function ($invitation) use ($nmo) { @@ -570,7 +570,7 @@ class BaseDriver extends AbstractPaymentDriver $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get(); $invoices->each(function ($invoice) { - $invoice->service()->touchPdf(); + $invoice->service()->deletePdf(); }); $invoices->first()->invitations->each(function ($invitation) use ($nmo) { diff --git a/app/PaymentDrivers/Eway/CreditCard.php b/app/PaymentDrivers/Eway/CreditCard.php index f93057719b34..969aa3ec670b 100644 --- a/app/PaymentDrivers/Eway/CreditCard.php +++ b/app/PaymentDrivers/Eway/CreditCard.php @@ -70,12 +70,17 @@ class CreditCard $response = $this->eway_driver->init()->eway->createCustomer(\Eway\Rapid\Enum\ApiMethod::DIRECT, $transaction); - $response_status = ErrorCode::getStatus($response->ResponseMessage); + if(property_exists($response, 'ResponseMessage')) + $response_status = ErrorCode::getStatus($response->ResponseMessage); if (! $response_status['success']) { + $this->eway_driver->sendFailureMail($response_status['message']); - throw new PaymentFailed($response_status['message'], 400); + $this->logResponse($response); + + + throw new PaymentFailed($response_status['message'] ?? 'Unknown response from gateway, please contact you merchant.', 400); } //success @@ -94,6 +99,8 @@ class CreditCard $token = $this->eway_driver->storeGatewayToken($cgt, []); + $this->logResponse($response); + return $token; } @@ -135,7 +142,7 @@ class CreditCard $amount = array_sum(array_column($this->eway_driver->payment_hash->invoices(), 'amount')) + $this->eway_driver->payment_hash->fee_total; - $description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->eway_driver->client->present()->name()}"; + // $description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->eway_driver->client->present()->name()}"; $transaction = [ 'Payment' => [ @@ -152,29 +159,6 @@ class CreditCard $this->logResponse($response); - // if(!$response || !property_exists($response, 'ResponseMessage')) - // throw new PaymentFailed('The gateway did not return a valid response. Please check your gateway credentials.', 400); - - // $response_status = ErrorCode::getStatus($response->ResponseMessage); - - // if(!$response_status['success']){ - - // if($response->getErrors()) - // { - // $message = false; - - // foreach ($response->getErrors() as $error) { - // $message = \Eway\Rapid::getMessage($error); - // } - - // $return_message = $message ?: $response_status['message']; - // } - - // $this->eway_driver->sendFailureMail($response_status['message']); - - // throw new PaymentFailed($response_status['message'], 400); - // } - if ($response->TransactionStatus) { $payment = $this->storePayment($response); } else { diff --git a/app/PaymentDrivers/PayTrace/CreditCard.php b/app/PaymentDrivers/PayTrace/CreditCard.php index a07f17e5ea4e..720d5f0b02be 100644 --- a/app/PaymentDrivers/PayTrace/CreditCard.php +++ b/app/PaymentDrivers/PayTrace/CreditCard.php @@ -123,6 +123,7 @@ class CreditCard 'city' => $this->paytrace->client->city, 'state' => $this->paytrace->client->state, 'zip' => $this->paytrace->client->postal_code, + 'country' => $this->paytrace->client->country->iso_3166_2 ]; return $data; @@ -177,6 +178,7 @@ class CreditCard 'customer_id' => $token, 'integrator_id' => $this->paytrace->company_gateway->getConfigField('integratorId'), 'amount' => $request->input('amount_with_fee'), + 'invoice_id' => $this->harvestInvoiceId(), ]; $response = $this->paytrace->gatewayRequest('/v1/transactions/sale/by_customer', $data); diff --git a/app/Repositories/TaskStatusRepository.php b/app/Repositories/TaskStatusRepository.php index 4cadccd4d645..c3702136056c 100644 --- a/app/Repositories/TaskStatusRepository.php +++ b/app/Repositories/TaskStatusRepository.php @@ -38,13 +38,15 @@ class TaskStatusRepository extends BaseRepository public function archive($task_status) { - $task_status = TaskStatus::where('id', $task_status->id) + $task_status = TaskStatus::withTrashed() + ->where('id', $task_status->id) ->where('company_id', $task_status->company_id) ->first(); $new_status = $task_status ? $task_status->id : null; - Task::where('status_id', $task_status->id) + Task::withTrashed() + ->where('status_id', $task_status->id) ->where('company_id', $task_status->company_id) ->update(['status_id' => $new_status]); diff --git a/app/Services/Credit/ApplyPayment.php b/app/Services/Credit/ApplyPayment.php index 0f8af8b4aef7..70639ce02001 100644 --- a/app/Services/Credit/ApplyPayment.php +++ b/app/Services/Credit/ApplyPayment.php @@ -137,7 +137,7 @@ class ApplyPayment ->updateBalance($this->amount_applied * -1) ->updatePaidToDate($this->amount_applied) ->updateStatus() - ->touchPdf() + ->deletePdf() ->save(); $this->credit @@ -147,7 +147,7 @@ class ApplyPayment event(new InvoiceWasUpdated($this->invoice, $this->invoice->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); if ((int) $this->invoice->balance == 0) { - $this->invoice->service()->touchPdf(); + $this->invoice->service()->deletePdf(); $this->invoice = $this->invoice->fresh(); event(new InvoiceWasPaid($this->invoice, $this->payment, $this->payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); } diff --git a/app/Services/Credit/CreditService.php b/app/Services/Credit/CreditService.php index ccc61a03d555..811ea7e4cf45 100644 --- a/app/Services/Credit/CreditService.php +++ b/app/Services/Credit/CreditService.php @@ -11,15 +11,17 @@ namespace App\Services\Credit; -use App\Factory\PaymentFactory; -use App\Jobs\Entity\CreateEntityPdf; -use App\Jobs\Util\UnlinkFile; +use App\Utils\Ninja; use App\Models\Credit; use App\Models\Payment; use App\Models\PaymentType; +use App\Jobs\Util\UnlinkFile; +use App\Factory\PaymentFactory; +use App\Utils\Traits\MakesHash; +use App\Jobs\Entity\CreateEntityPdf; use App\Repositories\CreditRepository; use App\Repositories\PaymentRepository; -use App\Utils\Traits\MakesHash; +use Illuminate\Support\Facades\Storage; class CreditService { @@ -235,7 +237,24 @@ class CreditService public function deletePdf() { $this->credit->invitations->each(function ($invitation) { - (new UnlinkFile(config('filesystems.default'), $this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'))->handle(); + // (new UnlinkFile(config('filesystems.default'), $this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'))->handle(); + + //30-06-2023 + try { + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + Storage::disk(config('filesystems.default'))->delete($this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'); + // } + + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + if (Ninja::isHosted()) { + Storage::disk('public')->delete($this->credit->client->credit_filepath($invitation).$this->credit->numberFormatter().'.pdf'); + } + } catch (\Exception $e) { + nlog($e->getMessage()); + } + + + }); return $this; diff --git a/app/Services/Credit/MarkSent.php b/app/Services/Credit/MarkSent.php index 3402a019aab9..890f6887c741 100644 --- a/app/Services/Credit/MarkSent.php +++ b/app/Services/Credit/MarkSent.php @@ -42,7 +42,7 @@ class MarkSent ->setStatus(Credit::STATUS_SENT) ->applyNumber() ->adjustBalance($this->credit->amount) - ->touchPdf() + ->deletePdf() ->save(); $this->client diff --git a/app/Services/Email/Email.php b/app/Services/Email/Email.php index ac606a0b2e9e..4a9663ea43f4 100644 --- a/app/Services/Email/Email.php +++ b/app/Services/Email/Email.php @@ -432,6 +432,7 @@ class Email implements ShouldQueue $this->setGmailMailer(); return $this; case 'office365': + case 'microsoft': $this->mailer = 'office365'; $this->setOfficeMailer(); return $this; @@ -445,7 +446,8 @@ class Email implements ShouldQueue return $this; default: - break; + $this->mailer = config('mail.default'); + return $this; } if (Ninja::isSelfHost()) { diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php index d17ebd8b41f5..9d225c4cc310 100644 --- a/app/Services/Email/EmailDefaults.php +++ b/app/Services/Email/EmailDefaults.php @@ -307,9 +307,6 @@ class EmailDefaults $xinvoice_path = $this->email->email_object->entity->service()->getEInvoice(); - // $xinvoice_path = (new CreateEInvoice($this->email->email_object->entity, true, stream_get_meta_data($tempfile)['uri']))->handle(); - // $this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode($pdf), 'name' => $this->email->email_object->entity->numberFormatter().'.pdf']]); - if(Storage::disk(config('filesystems.default'))->exists($xinvoice_path)) $this->email->email_object->attachments = array_merge($this->email->email_object->attachments, [['file' => base64_encode(Storage::get($xinvoice_path)), 'name' => explode(".", $this->email->email_object->entity->getFileName('xml'))[0]."-xinvoice.xml"]]); diff --git a/app/Services/Invoice/ApplyPayment.php b/app/Services/Invoice/ApplyPayment.php index 542e871876b4..a0a2fb5f83fd 100644 --- a/app/Services/Invoice/ApplyPayment.php +++ b/app/Services/Invoice/ApplyPayment.php @@ -92,7 +92,7 @@ class ApplyPayment extends AbstractService } }); - $this->invoice->service()->applyNumber()->workFlow()->touchPdf()->save(); + $this->invoice->service()->applyNumber()->workFlow()->deletePdf()->save(); return $this->invoice; } diff --git a/app/Services/Invoice/HandleCancellation.php b/app/Services/Invoice/HandleCancellation.php index fe83909e3e36..2fcd20013fee 100644 --- a/app/Services/Invoice/HandleCancellation.php +++ b/app/Services/Invoice/HandleCancellation.php @@ -44,9 +44,7 @@ class HandleCancellation extends AbstractService $this->invoice->balance = 0; $this->invoice = $this->invoice->service()->setStatus(Invoice::STATUS_CANCELLED)->save(); - //adjust client balance $this->invoice->client->service()->updateBalance($adjustment)->save(); - // $this->invoice->fresh(); $this->invoice->service()->workFlow()->save(); @@ -54,16 +52,6 @@ class HandleCancellation extends AbstractService event('eloquent.updated: App\Models\Invoice', $this->invoice); - $transaction = [ - 'invoice' => $this->invoice->transaction_event(), - 'payment' => [], - 'client' => $this->invoice->client->transaction_event(), - 'credit' => [], - 'metadata' => [], - ]; - - // TransactionLog::dispatch(TransactionEvent::INVOICE_CANCELLED, $transaction, $this->invoice->company->db); - return $this->invoice; } diff --git a/app/Services/Invoice/InvoiceService.php b/app/Services/Invoice/InvoiceService.php index f0dc04a88a26..d5a0715ee7d2 100644 --- a/app/Services/Invoice/InvoiceService.php +++ b/app/Services/Invoice/InvoiceService.php @@ -115,7 +115,6 @@ class InvoiceService */ public function applyPayment(Payment $payment, float $payment_amount) { - // $this->deletePdf(); $this->invoice = $this->markSent()->save(); $this->invoice = (new ApplyPayment($this->invoice, $payment, $payment_amount))->run(); @@ -339,7 +338,7 @@ class InvoiceService return $item; })->toArray(); - $this->touchPdf(); + $this->deletePdf(); return $this; } @@ -348,13 +347,15 @@ class InvoiceService { $this->invoice->load('invitations'); + //30-06-2023 $this->invoice->invitations->each(function ($invitation) { try { - if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { Storage::disk(config('filesystems.default'))->delete($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf'); - } + // } - if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + if (Ninja::isHosted()) { Storage::disk('public')->delete($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf'); } } catch (\Exception $e) { @@ -371,11 +372,12 @@ class InvoiceService $this->invoice->invitations->each(function ($invitation) { try { - if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { Storage::disk(config('filesystems.default'))->delete($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml")); - } + // } - if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml"))) { + if (Ninja::isHosted()) { Storage::disk('public')->delete($this->invoice->client->e_invoice_filepath($invitation).$this->invoice->getFileName("xml")); } } catch (\Exception $e) { @@ -403,7 +405,7 @@ class InvoiceService })->toArray(); $this->invoice = $this->invoice->calc()->getInvoice(); - $this->touchPdf(); + $this->deletePdf(); /* 24-03-2022 */ $new_balance = $this->invoice->balance; diff --git a/app/Services/Invoice/MarkPaid.php b/app/Services/Invoice/MarkPaid.php index a056da4b6038..6b2c515a97cb 100644 --- a/app/Services/Invoice/MarkPaid.php +++ b/app/Services/Invoice/MarkPaid.php @@ -102,7 +102,7 @@ class MarkPaid extends AbstractService $this->invoice ->service() ->applyNumber() - ->touchPdf() + ->deletePdf() ->save(); $payment->ledger() diff --git a/app/Services/Invoice/TriggeredActions.php b/app/Services/Invoice/TriggeredActions.php index 7c6e7c4f6ad9..3dae6339b743 100644 --- a/app/Services/Invoice/TriggeredActions.php +++ b/app/Services/Invoice/TriggeredActions.php @@ -54,7 +54,7 @@ class TriggeredActions extends AbstractService } if ($this->request->has('send_email') && $this->request->input('send_email') == 'true') { - $this->invoice->service()->markSent()->touchPdf()->save(); + $this->invoice->service()->markSent()->save(); $this->sendEmail(); $this->updated = false; } diff --git a/app/Services/Payment/UpdateInvoicePayment.php b/app/Services/Payment/UpdateInvoicePayment.php index 4f088eacfc9f..882cba3e95bd 100644 --- a/app/Services/Payment/UpdateInvoicePayment.php +++ b/app/Services/Payment/UpdateInvoicePayment.php @@ -79,7 +79,7 @@ class UpdateInvoicePayment $invoice = $invoice->service() ->clearPartial() ->updateStatus() - ->touchPdf() + ->deletePdf() ->workFlow() ->save(); diff --git a/app/Services/PurchaseOrder/GetPurchaseOrderPdf.php b/app/Services/PurchaseOrder/GetPurchaseOrderPdf.php index dde550a269c7..40044b90b6e5 100644 --- a/app/Services/PurchaseOrder/GetPurchaseOrderPdf.php +++ b/app/Services/PurchaseOrder/GetPurchaseOrderPdf.php @@ -19,11 +19,8 @@ use Illuminate\Support\Facades\Storage; class GetPurchaseOrderPdf extends AbstractService { - public function __construct(PurchaseOrder $purchase_order, VendorContact $contact = null) + public function __construct(public PurchaseOrder $purchase_order, public ?VendorContact $contact = null) { - $this->purchase_order = $purchase_order; - - $this->contact = $contact; } public function run() diff --git a/app/Services/PurchaseOrder/TriggeredActions.php b/app/Services/PurchaseOrder/TriggeredActions.php index 02e9e0bdc871..38510fc70962 100644 --- a/app/Services/PurchaseOrder/TriggeredActions.php +++ b/app/Services/PurchaseOrder/TriggeredActions.php @@ -43,10 +43,6 @@ class TriggeredActions extends AbstractService $this->purchase_order = $this->purchase_order->service()->markSent()->touchPdf()->save(); } - // if ($this->request->has('cancel') && $this->request->input('cancel') == 'true') { - // $this->purchase_order = $this->purchase_order->service()->handleCancellation()->save(); - // } - if ($this->request->has('save_default_footer') && $this->request->input('save_default_footer') == 'true') { $company = $this->purchase_order->company; $settings = $company->settings; diff --git a/app/Services/Quote/MarkSent.php b/app/Services/Quote/MarkSent.php index fa9c75a565b9..9969cbfc3f41 100644 --- a/app/Services/Quote/MarkSent.php +++ b/app/Services/Quote/MarkSent.php @@ -47,7 +47,7 @@ class MarkSent ->service() ->setStatus(Quote::STATUS_SENT) ->applyNumber() - ->touchPdf() + ->deletePdf() ->save(); event(new QuoteWasMarkedSent($this->quote, $this->quote->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null))); diff --git a/app/Services/Quote/QuoteService.php b/app/Services/Quote/QuoteService.php index fe6189106c47..45b38cc0b52b 100644 --- a/app/Services/Quote/QuoteService.php +++ b/app/Services/Quote/QuoteService.php @@ -19,6 +19,7 @@ use App\Exceptions\QuoteConversion; use App\Jobs\Entity\CreateEntityPdf; use App\Repositories\QuoteRepository; use App\Events\Quote\QuoteWasApproved; +use Illuminate\Support\Facades\Storage; class QuoteService { @@ -115,7 +116,7 @@ class QuoteService $this->invoice ->service() ->markSent() - ->touchPdf() + ->deletePdf() ->save(); } @@ -224,7 +225,22 @@ class QuoteService public function deletePdf() { $this->quote->invitations->each(function ($invitation) { - (new UnlinkFile(config('filesystems.default'), $this->quote->client->quote_filepath($invitation).$this->quote->numberFormatter().'.pdf'))->handle(); + // (new UnlinkFile(config('filesystems.default'), $this->quote->client->quote_filepath($invitation).$this->quote->numberFormatter().'.pdf'))->handle(); + + //30-06-2023 + try { + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + Storage::disk(config('filesystems.default'))->delete($this->quote->client->quote_filepath($invitation).$this->quote->numberFormatter().'.pdf'); + // } + + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + if (Ninja::isHosted()) { + Storage::disk('public')->delete($this->quote->client->quote_filepath($invitation).$this->quote->numberFormatter().'.pdf'); + } + } catch (\Exception $e) { + nlog($e->getMessage()); + } + }); return $this; diff --git a/app/Services/Recurring/RecurringService.php b/app/Services/Recurring/RecurringService.php index c962b5f45f45..721b94dcd183 100644 --- a/app/Services/Recurring/RecurringService.php +++ b/app/Services/Recurring/RecurringService.php @@ -11,11 +11,13 @@ namespace App\Services\Recurring; +use App\Utils\Ninja; use App\Jobs\Util\UnlinkFile; use App\Models\RecurringQuote; use Illuminate\Support\Carbon; use App\Models\RecurringExpense; use App\Models\RecurringInvoice; +use Illuminate\Support\Facades\Storage; use App\Jobs\RecurringInvoice\SendRecurring; class RecurringService @@ -88,7 +90,22 @@ class RecurringService public function deletePdf() { $this->recurring_entity->invitations->each(function ($invitation) { - (new UnlinkFile(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf'))->handle(); + // (new UnlinkFile(config('filesystems.default'), $this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf'))->handle(); + + //30-06-2023 + try { + Storage::disk(config('filesystems.default'))->delete($this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf'); + // if (Storage::disk(config('filesystems.default'))->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + // } + + // if (Ninja::isHosted() && Storage::disk('public')->exists($this->invoice->client->invoice_filepath($invitation).$this->invoice->numberFormatter().'.pdf')) { + Storage::disk('public')->delete($this->recurring_entity->client->recurring_invoice_filepath($invitation) . $this->recurring_entity->numberFormatter().'.pdf'); + if (Ninja::isHosted()) { + } + } catch (\Exception $e) { + nlog($e->getMessage()); + } + }); diff --git a/app/Services/Subscription/SubscriptionService.php b/app/Services/Subscription/SubscriptionService.php index 955e567f62f1..782f08446b6a 100644 --- a/app/Services/Subscription/SubscriptionService.php +++ b/app/Services/Subscription/SubscriptionService.php @@ -188,7 +188,7 @@ class SubscriptionService //update the invoice and attach to the recurring invoice!!!!! $invoice->recurring_id = $recurring_invoice->id; $invoice->is_proforma = false; - $invoice->service()->touchPdf(); + $invoice->service()->deletePdf(); $invoice->save(); $contact = $invoice->client->contacts()->whereNotNull('email')->first(); @@ -821,13 +821,15 @@ class SubscriptionService $invoice->is_proforma = false; $invoice->save(); + // 29-06-2023 handle webhooks for payment intent - user may not be present. + $context = [ 'context' => 'change_plan', 'recurring_invoice' => $recurring_invoice->hashed_id, 'invoice' => $this->encodePrimaryKey($payment_hash->fee_invoice_id), 'client' => $recurring_invoice->client->hashed_id, 'subscription' => $this->subscription->hashed_id, - 'contact' => auth()->guard('contact')->user()->hashed_id, + 'contact' => auth()->guard('contact')->user()?->hashed_id ?? $recurring_invoice->client->contacts()->first()->hashed_id, 'account_key' => $recurring_invoice->client->custom_value2, ]; diff --git a/app/Transformers/CompanyTransformer.php b/app/Transformers/CompanyTransformer.php index 0d6241ca8c17..32f4966ba1b6 100644 --- a/app/Transformers/CompanyTransformer.php +++ b/app/Transformers/CompanyTransformer.php @@ -203,6 +203,7 @@ class CompanyTransformer extends EntityTransformer 'has_e_invoice_certificate_passphrase' => $company->e_invoice_certificate_passphrase ? true : false, 'invoice_task_project_header' => (bool) $company->invoice_task_project_header, 'invoice_task_item_description' => (bool) $company->invoice_task_item_description, + 'origin_tax_data' => $company->origin_tax_data ?: new \stdClass, ]; } diff --git a/app/Transformers/ExpenseTransformer.php b/app/Transformers/ExpenseTransformer.php index 7fb5c8826638..48a3ed7c4a7d 100644 --- a/app/Transformers/ExpenseTransformer.php +++ b/app/Transformers/ExpenseTransformer.php @@ -14,12 +14,12 @@ namespace App\Transformers; use App\Models\Vendor; use App\Models\Expense; use App\Models\Invoice; -use App\Models\Document; use App\Models\ExpenseCategory; +use App\Transformers\ExpenseCategoryTransformer; +use App\Models\Document; use App\Utils\Traits\MakesHash; use League\Fractal\Resource\Item; use Illuminate\Database\Eloquent\SoftDeletes; -use App\Transformers\ExpenseCategoryTransformer; /** * class ExpenseTransformer. diff --git a/config/ninja.php b/config/ninja.php index 482d0131229a..86642783a8a0 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -15,8 +15,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => '5.6.4', - 'app_tag' => '5.6.4', + 'app_version' => '5.6.5', + 'app_tag' => '5.6.5', 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', ''), diff --git a/database/migrations/2023_04_27_045639_add_kmher_language.php b/database/migrations/2023_04_27_045639_add_kmher_language.php index 9d2d7a994f95..69e7fb433cb3 100644 --- a/database/migrations/2023_04_27_045639_add_kmher_language.php +++ b/database/migrations/2023_04_27_045639_add_kmher_language.php @@ -25,9 +25,12 @@ return new class extends Migration Language::create(['id' => 38, 'name' => 'Khmer', 'locale' => 'km_KH']); } - Schema::table('companies', function (Blueprint $table) { - $table->dropColumn('enable_e_invoice'); - }); + if (Schema::hasColumn('companies', 'enable_e_invoice')) + { + Schema::table('companies', function (Blueprint $table) { + $table->dropColumn('enable_e_invoice'); + }); + } Company::query()->cursor()->each(function ($company){ $company->tax_data = new TaxModel(); diff --git a/phpstan.neon b/phpstan.neon index 8bbf3a89f08a..148ec353ba82 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,8 +6,6 @@ parameters: ignoreErrors: - '#Call to an undefined method .*badMethod\(\)#' - '#Call to an undefined method Illuminate\Database\Eloquent\Builder::exclude#' - parallel: - maximumNumberOfProcesses: 8 level: 4 paths: - 'app/' diff --git a/resources/views/portal/ninja2020/credits/show-fullscreen.blade.php b/resources/views/portal/ninja2020/credits/show-fullscreen.blade.php index 438899646beb..3655bf8f68e4 100644 --- a/resources/views/portal/ninja2020/credits/show-fullscreen.blade.php +++ b/resources/views/portal/ninja2020/credits/show-fullscreen.blade.php @@ -1,2 +1,3 @@ - + + + diff --git a/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php b/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php index d15cebda832d..8de82110754e 100644 --- a/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php +++ b/resources/views/portal/ninja2020/invoices/show-fullscreen.blade.php @@ -1,2 +1,2 @@ - + + diff --git a/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php b/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php index e69de29bb2d1..2aa2bf4b229b 100644 --- a/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php +++ b/resources/views/portal/ninja2020/purchase_orders/show-fullscreen.blade.php @@ -0,0 +1,2 @@ + + diff --git a/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php b/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php index 0cfa59fec10b..22043d045fe8 100644 --- a/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php +++ b/resources/views/portal/ninja2020/quotes/show-fullscreen.blade.php @@ -1,2 +1,3 @@ - + + + diff --git a/resources/views/portal/ninja2020/statement/index.blade.php b/resources/views/portal/ninja2020/statement/index.blade.php index 14ea29843d5c..f0780705b29a 100644 --- a/resources/views/portal/ninja2020/statement/index.blade.php +++ b/resources/views/portal/ninja2020/statement/index.blade.php @@ -45,6 +45,7 @@ @include('portal.ninja2020.components.pdf-viewer', ['url' => route('client.statement.raw')]) + @endsection @push('footer') diff --git a/routes/client.php b/routes/client.php index 0a95a333c600..f708f64113a5 100644 --- a/routes/client.php +++ b/routes/client.php @@ -40,7 +40,7 @@ Route::get('tmp_pdf/{hash}', [App\Http\Controllers\ClientPortal\TempRouteControl Route::get('client/key_login/{contact_key}', [App\Http\Controllers\ClientPortal\ContactHashLoginController::class, 'login'])->name('client.contact_login')->middleware(['domain_db','contact_key_login']); Route::get('client/magic_link/{magic_link}', [App\Http\Controllers\ClientPortal\ContactHashLoginController::class, 'magicLink'])->name('client.contact_magic_link')->middleware(['domain_db','contact_key_login']); -Route::get('documents/{document_hash}', [App\Http\Controllers\ClientPortal\DocumentController::class, 'publicDownload'])->name('documents.public_download')->middleware(['domain_db','token_auth']); +Route::get('documents/{document_hash}', [App\Http\Controllers\ClientPortal\DocumentController::class, 'publicDownload'])->name('documents.public_download')->middleware(['api_db','token_auth']); Route::get('error', [App\Http\Controllers\ClientPortal\ContactHashLoginController::class, 'errorPage'])->name('client.error'); Route::get('client/payment/{contact_key}/{payment_id}', [App\Http\Controllers\ClientPortal\InvitationController::class, 'paymentRouter'])->middleware(['domain_db','contact_key_login']); Route::get('client/ninja/{contact_key}/{company_key}', [App\Http\Controllers\ClientPortal\NinjaPlanController::class, 'index'])->name('client.ninja_contact_login')->middleware(['domain_db']); diff --git a/tests/Feature/ExpenseApiTest.php b/tests/Feature/ExpenseApiTest.php index 2e9b1405479f..ef6fadf3d019 100644 --- a/tests/Feature/ExpenseApiTest.php +++ b/tests/Feature/ExpenseApiTest.php @@ -31,6 +31,8 @@ class ExpenseApiTest extends TestCase use DatabaseTransactions; use MockAccountData; + public $faker; + protected function setUp() :void { parent::setUp();