1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-21 17:01:33 +02:00

Merge pull request #4930 from turbo124/v5-stable

Improvements and bug fixes.
This commit is contained in:
David Bomba 2021-02-17 13:58:13 +11:00 committed by GitHub
commit ac0332eacc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
150 changed files with 128975 additions and 126954 deletions

View File

@ -1 +1 @@
5.1.5 5.1.6

View File

@ -73,6 +73,7 @@ class CheckData extends Command
protected $description = 'Check/fix data'; protected $description = 'Check/fix data';
protected $log = ''; protected $log = '';
protected $isValid = true; protected $isValid = true;
public function handle() public function handle()
@ -90,13 +91,10 @@ class CheckData extends Command
$this->checkContacts(); $this->checkContacts();
$this->checkCompanyData(); $this->checkCompanyData();
//$this->checkLogoFiles();
if (! $this->option('client_id')) { if (! $this->option('client_id')) {
$this->checkOAuth(); $this->checkOAuth();
//$this->checkInvitations(); //$this->checkFailedJobs();
$this->checkFailedJobs();
} }
$this->logMessage('Done: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE)); $this->logMessage('Done: '.strtoupper($this->isValid ? Account::RESULT_SUCCESS : Account::RESULT_FAILURE));
@ -298,8 +296,6 @@ class CheckData extends Command
$total_invoice_payments = 0; $total_invoice_payments = 0;
foreach ($client->invoices->where('is_deleted', false)->where('status_id', '>', 1) as $invoice) { foreach ($client->invoices->where('is_deleted', false)->where('status_id', '>', 1) as $invoice) {
// $total_amount = $invoice->payments->whereNull('deleted_at')->sum('pivot.amount');
// $total_refund = $invoice->payments->whereNull('deleted_at')->sum('pivot.refunded');
$total_amount = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.amount'); $total_amount = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.amount');
$total_refund = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.refunded'); $total_refund = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.refunded');
@ -307,15 +303,15 @@ class CheckData extends Command
$total_invoice_payments += ($total_amount - $total_refund); $total_invoice_payments += ($total_amount - $total_refund);
} }
// 10/02/21
foreach ($client->payments as $payment) { foreach ($client->payments as $payment) {
$credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(DB::raw('amount')); $credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(DB::raw('amount'));
} }
if ($credit_total_applied < 0) { if ($credit_total_applied < 0) {
$total_invoice_payments += $credit_total_applied; $total_invoice_payments += $credit_total_applied;
} //todo this is contentious }
// nlog("total invoice payments = {$total_invoice_payments} with client paid to date of of {$client->paid_to_date}");
if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) { if (round($total_invoice_payments, 2) != round($client->paid_to_date, 2)) {
$wrong_paid_to_dates++; $wrong_paid_to_dates++;
@ -393,8 +389,8 @@ class CheckData extends Command
$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance'); $invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
$credit_balance = $client->credits->where('is_deleted', false)->sum('balance'); $credit_balance = $client->credits->where('is_deleted', false)->sum('balance');
if($client->balance != $invoice_balance) // if($client->balance != $invoice_balance)
$invoice_balance -= $credit_balance;//doesn't make sense to remove the credit amount // $invoice_balance -= $credit_balance;//doesn't make sense to remove the credit amount
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first(); $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();

View File

@ -11,6 +11,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Libraries\MultiDB;
use App\Models\Design; use App\Models\Design;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use stdClass; use stdClass;
@ -47,6 +48,30 @@ class DesignUpdate extends Command
* @return mixed * @return mixed
*/ */
public function handle() public function handle()
{
//always return state to first DB
$current_db = config('database.default');
if (! config('ninja.db.multi_db_enabled')) {
$this->handleOnDb();
} else {
//multiDB environment, need to
foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$this->handleOnDb($db);
}
MultiDB::setDB($current_db);
}
}
private function handleOnDb()
{ {
foreach (Design::whereIsCustom(false)->get() as $design) { foreach (Design::whereIsCustom(false)->get() as $design) {
$invoice_design = new \App\Services\PdfMaker\Design(strtolower($design->name)); $invoice_design = new \App\Services\PdfMaker\Design(strtolower($design->name));

View File

@ -13,6 +13,7 @@ namespace App\Console\Commands;
use App\Jobs\Ninja\SendReminders; use App\Jobs\Ninja\SendReminders;
use App\Jobs\Util\WebHookHandler; use App\Jobs\Util\WebHookHandler;
use App\Libraries\MultiDB;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Quote; use App\Models\Quote;
use App\Models\Webhook; use App\Models\Webhook;
@ -58,6 +59,24 @@ class SendRemindersCron extends Command
} }
private function webHookOverdueInvoices() private function webHookOverdueInvoices()
{
if (! config('ninja.db.multi_db_enabled')) {
$this->executeWebhooks();
} else {
//multiDB environment, need to
foreach (MultiDB::$dbs as $db) {
MultiDB::setDB($db);
$this->executeWebhooks();
}
}
}
private function executeWebhooks()
{ {
$invoices = Invoice::where('is_deleted', 0) $invoices = Invoice::where('is_deleted', 0)
->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL]) ->whereIn('status_id', [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])
@ -68,10 +87,7 @@ class SendRemindersCron extends Command
$invoices->each(function ($invoice) { $invoices->each(function ($invoice) {
WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company); WebHookHandler::dispatch(Webhook::EVENT_LATE_INVOICE, $invoice, $invoice->company);
}); });
}
private function webHookExpiredQuotes()
{
$quotes = Quote::where('is_deleted', 0) $quotes = Quote::where('is_deleted', 0)
->where('status_id', Quote::STATUS_SENT) ->where('status_id', Quote::STATUS_SENT)
->whereDate('due_date', '<=', now()->subDays(1)->startOfDay()) ->whereDate('due_date', '<=', now()->subDays(1)->startOfDay())
@ -81,4 +97,6 @@ class SendRemindersCron extends Command
WebHookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company); WebHookHandler::dispatch(Webhook::EVENT_EXPIRED_QUOTE, $quote, $quote->company);
}); });
} }
} }

View File

@ -148,6 +148,7 @@ class CompanySettings extends BaseSettings
public $gmail_sending_user_id = '0'; //@implemented public $gmail_sending_user_id = '0'; //@implemented
public $reply_to_email = ''; //@TODO public $reply_to_email = ''; //@TODO
public $reply_to_name = ''; //@TODO
public $bcc_email = ''; //@TODO public $bcc_email = ''; //@TODO
public $pdf_email_attachment = false; //@implemented public $pdf_email_attachment = false; //@implemented
public $ubl_email_attachment = false; //@implemented public $ubl_email_attachment = false; //@implemented
@ -261,6 +262,7 @@ class CompanySettings extends BaseSettings
public $hide_empty_columns_on_pdf = false; public $hide_empty_columns_on_pdf = false;
public static $casts = [ public static $casts = [
'reply_to_name' => 'string',
'hide_empty_columns_on_pdf' => 'bool', 'hide_empty_columns_on_pdf' => 'bool',
'enable_reminder_endless' => 'bool', 'enable_reminder_endless' => 'bool',
'use_credits_payment' => 'string', 'use_credits_payment' => 'string',

View File

@ -111,6 +111,10 @@ class Handler extends ExceptionHandler
return false; return false;
} }
if (strpos($exception->getMessage(), 'expects parameter 1 to be resource') !== false) {
return false;
}
return true; return true;
} }

View File

@ -11,6 +11,8 @@
namespace App\Helpers\Mail; namespace App\Helpers\Mail;
use App\Utils\TempFile;
use Dacastro4\LaravelGmail\Facade\LaravelGmail;
use Dacastro4\LaravelGmail\Services\Message\Mail; use Dacastro4\LaravelGmail\Services\Message\Mail;
use Illuminate\Mail\Transport\Transport; use Illuminate\Mail\Transport\Transport;
use Swift_Mime_SimpleMessage; use Swift_Mime_SimpleMessage;
@ -27,49 +29,59 @@ class GmailTransport extends Transport
*/ */
protected $gmail; protected $gmail;
/**
* The GMail OAuth Token.
* @var string token
*/
protected $token;
/** /**
* Create a new Gmail transport instance. * Create a new Gmail transport instance.
* *
* @param Mail $gmail * @param Mail $gmail
* @param string $token * @param string $token
*/ */
public function __construct(Mail $gmail, string $token) public function __construct(Mail $gmail)
{ {
$this->gmail = $gmail; $this->gmail = $gmail;
$this->token = $token;
} }
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null) public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{ {
/* For some reason the Injected Mail class carries cached tokens, so we need to reinit the Mail class*/
$this->gmail = null;
$this->gmail = new Mail;
/*We should nest the token in the message and then discard it as needed*/ /*We should nest the token in the message and then discard it as needed*/
$token = $message->getHeaders()->get('GmailToken')->getValue();
$message->getHeaders()->remove('GmailToken');
$this->beforeSendPerformed($message); $this->beforeSendPerformed($message);
$this->gmail->using($this->token); $this->gmail->using($token);
$this->gmail->to($message->getTo()); $this->gmail->to($message->getTo());
$this->gmail->from($message->getFrom()); $this->gmail->from($message->getFrom());
$this->gmail->subject($message->getSubject()); $this->gmail->subject($message->getSubject());
$this->gmail->message($message->getBody()); $this->gmail->message($message->getBody());
//$this->gmail->message($message->toString());
$this->gmail->cc($message->getCc()); $this->gmail->cc($message->getCc());
$this->gmail->bcc($message->getBcc()); $this->gmail->bcc($message->getBcc());
nlog(print_r($message->getChildren(), 1)); foreach ($message->getChildren() as $child)
{
foreach ($message->getChildren() as $child) { nlog("trying to attach");
$this->gmail->attach($child);
} //todo this should 'just work' if($child->getContentType() != 'text/plain')
{
$this->gmail->attach(TempFile::filePath($child->getBody(), $child->getHeaders()->get('Content-Type')->getParameter('name') ));
}
}
$this->gmail->send(); $this->gmail->send();
$this->sendPerformed($message); $this->sendPerformed($message);
return $this->numberOfRecipients($message); return $this->numberOfRecipients($message);
} }
} }

View File

@ -1,17 +1,25 @@
<?php <?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Helpers\Mail; namespace App\Helpers\Mail;
use Illuminate\Mail\MailManager;
use App\CustomMailDriver\CustomTransport;
use Dacastro4\LaravelGmail\Services\Message\Mail; use Dacastro4\LaravelGmail\Services\Message\Mail;
use Illuminate\Mail\TransportManager; use Illuminate\Support\Facades\Config;
class GmailTransportManager extends TransportManager
class GmailTransportManager extends MailManager
{ {
protected function createGmailDriver() protected function createGmailTransport()
{ {
$token = $this->app['config']->get('services.gmail.token', []); return new GmailTransport(new Mail);
$mail = new Mail;
return new GmailTransport($mail, $token);
} }
} }

View File

@ -12,8 +12,10 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Libraries\MultiDB;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Password;
use Illuminate\View\View; use Illuminate\View\View;
@ -65,4 +67,31 @@ class ContactForgotPasswordController extends Controller
{ {
return Password::broker('contacts'); return Password::broker('contacts');
} }
public function sendResetLinkEmail(Request $request)
{
//MultiDB::userFindAndSetDb($request->input('email'));
$user = MultiDB::hasContact(['email' => $request->input('email')]);
$this->validateEmail($request);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$response = $this->broker()->sendResetLink(
$this->credentials($request)
);
if ($request->ajax()) {
return $response == Password::RESET_LINK_SENT
? response()->json(['message' => 'Reset link sent to your email.', 'status' => true], 201)
: response()->json(['message' => 'Email not found', 'status' => false], 401);
}
return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($request, $response)
: $this->sendResetLinkFailedResponse($request, $response);
}
} }

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers\Auth; namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Libraries\MultiDB;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Password;
@ -103,6 +104,10 @@ class ForgotPasswordController extends Controller
*/ */
public function sendResetLinkEmail(Request $request) public function sendResetLinkEmail(Request $request)
{ {
//MultiDB::userFindAndSetDb($request->input('email'));
$user = MultiDB::hasUser(['email' => $request->input('email')]);
$this->validateEmail($request); $this->validateEmail($request);
// We will send the password reset link to this user. Once we have attempted // We will send the password reset link to this user. Once we have attempted

View File

@ -289,7 +289,7 @@ class LoginController extends BaseController
$client = new Google_Client(); $client = new Google_Client();
$client->setClientId(config('ninja.auth.google.client_id')); $client->setClientId(config('ninja.auth.google.client_id'));
$client->setClientSecret(config('ninja.auth.google.client_secret')); $client->setClientSecret(config('ninja.auth.google.client_secret'));
$client->setRedirectUri(config('ninja.app_url'));
$token = $client->authenticate(request()->input('server_auth_code')); $token = $client->authenticate(request()->input('server_auth_code'));
$refresh_token = ''; $refresh_token = '';

View File

@ -21,6 +21,7 @@ use App\Http\Requests\Client\EditClientRequest;
use App\Http\Requests\Client\ShowClientRequest; use App\Http\Requests\Client\ShowClientRequest;
use App\Http\Requests\Client\StoreClientRequest; use App\Http\Requests\Client\StoreClientRequest;
use App\Http\Requests\Client\UpdateClientRequest; use App\Http\Requests\Client\UpdateClientRequest;
use App\Http\Requests\Client\UploadClientRequest;
use App\Jobs\Client\StoreClient; use App\Jobs\Client\StoreClient;
use App\Jobs\Client\UpdateClient; use App\Jobs\Client\UpdateClient;
use App\Models\Client; use App\Models\Client;
@ -29,6 +30,7 @@ use App\Transformers\ClientTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\BulkOptions; use App\Utils\Traits\BulkOptions;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use App\Utils\Traits\Uploadable; use App\Utils\Traits\Uploadable;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -42,6 +44,7 @@ class ClientController extends BaseController
use MakesHash; use MakesHash;
use Uploadable; use Uploadable;
use BulkOptions; use BulkOptions;
use SavesDocuments;
protected $entity_type = Client::class; protected $entity_type = Client::class;
@ -269,6 +272,7 @@ class ClientController extends BaseController
*/ */
public function update(UpdateClientRequest $request, Client $client) public function update(UpdateClientRequest $request, Client $client)
{ {
if ($request->entityIsDeleted($client)) { if ($request->entityIsDeleted($client)) {
return $request->disallowUpdate(); return $request->disallowUpdate();
} }
@ -515,4 +519,66 @@ class ClientController extends BaseController
{ {
//todo //todo
} }
/**
* Update the specified resource in storage.
*
* @param UploadClientRequest $request
* @param Client $client
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/clients/{id}/upload",
* operationId="uploadClient",
* tags={"clients"},
* summary="Uploads a document to a client",
* description="Handles the uploading of a document to a client",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Client Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the client object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Client"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadClientRequest $request, Client $client)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $client);
return $this->itemResponse($client->fresh());
}
} }

View File

@ -13,10 +13,15 @@ namespace App\Http\Controllers\ClientPortal;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\ClientPortal\ShowRecurringInvoiceRequest; use App\Http\Requests\ClientPortal\ShowRecurringInvoiceRequest;
use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\RecurringInvoice\ClientContactRequestCancellationObject;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Notifications\ClientContactRequestCancellation; use App\Notifications\ClientContactRequestCancellation;
use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\View\View; use Illuminate\View\View;
@ -28,6 +33,7 @@ class RecurringInvoiceController extends Controller
{ {
use MakesHash; use MakesHash;
use MakesDates; use MakesDates;
use UserNotifies;
/** /**
* Show the list of recurring invoices. * Show the list of recurring invoices.
@ -57,7 +63,22 @@ class RecurringInvoiceController extends Controller
{ {
//todo double check the user is able to request a cancellation //todo double check the user is able to request a cancellation
//can add locale specific by chaining ->locale(); //can add locale specific by chaining ->locale();
$recurring_invoice->user->notify(new ClientContactRequestCancellation($recurring_invoice, auth()->user()));
$nmo = new NinjaMailerObject;
$nmo->mailable = (new NinjaMailer((new ClientContactRequestCancellationObject($recurring_invoice, auth()->user()))->build()));
$nmo->company = $recurring_invoice->company;
$nmo->settings = $recurring_invoice->company->settings;
$notifiable_users = $this->filterUsersByPermissions($recurring_invoice->company->company_users, $recurring_invoice, ['recurring_cancellation']);
$notifiable_users->each(function ($company_user) use($nmo){
$nmo->to_user = $company_user->user;
NinjaMailerJob::dispatch($nmo);
});
//$recurring_invoice->user->notify(new ClientContactRequestCancellation($recurring_invoice, auth()->user()));
return $this->render('recurring_invoices.cancellation.index', [ return $this->render('recurring_invoices.cancellation.index', [
'invoice' => $recurring_invoice, 'invoice' => $recurring_invoice,

View File

@ -20,6 +20,7 @@ use App\Http\Requests\Company\EditCompanyRequest;
use App\Http\Requests\Company\ShowCompanyRequest; use App\Http\Requests\Company\ShowCompanyRequest;
use App\Http\Requests\Company\StoreCompanyRequest; use App\Http\Requests\Company\StoreCompanyRequest;
use App\Http\Requests\Company\UpdateCompanyRequest; use App\Http\Requests\Company\UpdateCompanyRequest;
use App\Http\Requests\Company\UploadCompanyRequest;
use App\Jobs\Company\CreateCompany; use App\Jobs\Company\CreateCompany;
use App\Jobs\Company\CreateCompanyPaymentTerms; use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses; use App\Jobs\Company\CreateCompanyTaskStatuses;
@ -503,4 +504,65 @@ class CompanyController extends BaseController
return response()->json(['message' => ctrans('texts.success')], 200); return response()->json(['message' => ctrans('texts.success')], 200);
} }
/**
* Update the specified resource in storage.
*
* @param UploadCompanyRequest $request
* @param Company $client
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/companies/{id}/upload",
* operationId="uploadCompanies",
* tags={"companies"},
* summary="Uploads a document to a company",
* description="Handles the uploading of a document to a company",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Company Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the client object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Company"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadCompanyRequest $request, Company $company)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $company);
return $this->itemResponse($company->fresh());
}
} }

View File

@ -14,6 +14,7 @@ use App\Http\Requests\Credit\EditCreditRequest;
use App\Http\Requests\Credit\ShowCreditRequest; use App\Http\Requests\Credit\ShowCreditRequest;
use App\Http\Requests\Credit\StoreCreditRequest; use App\Http\Requests\Credit\StoreCreditRequest;
use App\Http\Requests\Credit\UpdateCreditRequest; use App\Http\Requests\Credit\UpdateCreditRequest;
use App\Http\Requests\Credit\UploadCreditRequest;
use App\Jobs\Entity\EmailEntity; use App\Jobs\Entity\EmailEntity;
use App\Jobs\Invoice\EmailCredit; use App\Jobs\Invoice\EmailCredit;
use App\Models\Client; use App\Models\Client;
@ -24,6 +25,7 @@ use App\Transformers\CreditTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\TempFile; use App\Utils\TempFile;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Http\Response; use Illuminate\Http\Response;
/** /**
@ -32,7 +34,8 @@ use Illuminate\Http\Response;
class CreditController extends BaseController class CreditController extends BaseController
{ {
use MakesHash; use MakesHash;
use SavesDocuments;
protected $entity_type = Credit::class; protected $entity_type = Credit::class;
protected $entity_transformer = CreditTransformer::class; protected $entity_transformer = CreditTransformer::class;
@ -56,7 +59,7 @@ class CreditController extends BaseController
* @OA\Get( * @OA\Get(
* path="/api/v1/credits", * path="/api/v1/credits",
* operationId="getCredits", * operationId="getCredits",
* tags={"invoices"}, * tags={"credits"},
* summary="Gets a list of credits", * summary="Gets a list of credits",
* description="Lists credits, search and filters allow fine grained lists to be generated. * description="Lists credits, search and filters allow fine grained lists to be generated.
* *
@ -576,4 +579,66 @@ class CreditController extends BaseController
return response()->download($file_path); return response()->download($file_path);
} }
/**
* Update the specified resource in storage.
*
* @param UploadCreditRequest $request
* @param Credit $client
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/credits/{id}/upload",
* operationId="uploadCredits",
* tags={"credits"},
* summary="Uploads a document to a credit",
* description="Handles the uploading of a document to a credit",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Credit Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Credit object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Credit"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadCreditRequest $request, Credit $credit)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $credit);
return $this->itemResponse($credit->fresh());
}
} }

View File

@ -21,12 +21,14 @@ use App\Http\Requests\Expense\EditExpenseRequest;
use App\Http\Requests\Expense\ShowExpenseRequest; use App\Http\Requests\Expense\ShowExpenseRequest;
use App\Http\Requests\Expense\StoreExpenseRequest; use App\Http\Requests\Expense\StoreExpenseRequest;
use App\Http\Requests\Expense\UpdateExpenseRequest; use App\Http\Requests\Expense\UpdateExpenseRequest;
use App\Http\Requests\Expense\UploadExpenseRequest;
use App\Models\Expense; use App\Models\Expense;
use App\Repositories\ExpenseRepository; use App\Repositories\ExpenseRepository;
use App\Transformers\ExpenseTransformer; use App\Transformers\ExpenseTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\BulkOptions; use App\Utils\Traits\BulkOptions;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use App\Utils\Traits\Uploadable; use App\Utils\Traits\Uploadable;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -40,7 +42,8 @@ class ExpenseController extends BaseController
use MakesHash; use MakesHash;
use Uploadable; use Uploadable;
use BulkOptions; use BulkOptions;
use SavesDocuments;
protected $entity_type = Expense::class; protected $entity_type = Expense::class;
protected $entity_transformer = ExpenseTransformer::class; protected $entity_transformer = ExpenseTransformer::class;
@ -507,4 +510,65 @@ class ExpenseController extends BaseController
{ {
//todo //todo
} }
/**
* Update the specified resource in storage.
*
* @param UploadExpenseRequest $request
* @param Expense $expense
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/expenses/{id}/upload",
* operationId="uploadExpense",
* tags={"expense"},
* summary="Uploads a document to a expense",
* description="Handles the uploading of a document to a expense",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Expense Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Expense object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Expense"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadExpenseRequest $request, Expense $expense)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $expense);
return $this->itemResponse($expense->fresh());
}
} }

View File

@ -25,6 +25,7 @@ use App\Http\Requests\Invoice\EditInvoiceRequest;
use App\Http\Requests\Invoice\ShowInvoiceRequest; use App\Http\Requests\Invoice\ShowInvoiceRequest;
use App\Http\Requests\Invoice\StoreInvoiceRequest; use App\Http\Requests\Invoice\StoreInvoiceRequest;
use App\Http\Requests\Invoice\UpdateInvoiceRequest; use App\Http\Requests\Invoice\UpdateInvoiceRequest;
use App\Http\Requests\Invoice\UploadInvoiceRequest;
use App\Jobs\Entity\EmailEntity; use App\Jobs\Entity\EmailEntity;
use App\Jobs\Invoice\StoreInvoice; use App\Jobs\Invoice\StoreInvoice;
use App\Jobs\Invoice\ZipInvoices; use App\Jobs\Invoice\ZipInvoices;
@ -38,6 +39,7 @@ use App\Transformers\QuoteTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\TempFile; use App\Utils\TempFile;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
@ -49,7 +51,8 @@ use Illuminate\Support\Facades\Storage;
class InvoiceController extends BaseController class InvoiceController extends BaseController
{ {
use MakesHash; use MakesHash;
use SavesDocuments;
protected $entity_type = Invoice::class; protected $entity_type = Invoice::class;
protected $entity_transformer = InvoiceTransformer::class; protected $entity_transformer = InvoiceTransformer::class;
@ -204,6 +207,7 @@ class InvoiceController extends BaseController
*/ */
public function store(StoreInvoiceRequest $request) public function store(StoreInvoiceRequest $request)
{ {
$client = Client::find($request->input('client_id')); $client = Client::find($request->input('client_id'));
$invoice = $this->invoice_repo->save($request->all(), InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id)); $invoice = $this->invoice_repo->save($request->all(), InvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id));
@ -392,8 +396,6 @@ class InvoiceController extends BaseController
$invoice = $this->invoice_repo->save($request->all(), $invoice); $invoice = $this->invoice_repo->save($request->all(), $invoice);
UnlinkFile::dispatchNow(config('filesystems.default'), $invoice->client->invoice_filepath().$invoice->number.'.pdf');
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars())); event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars()));
return $this->itemResponse($invoice); return $this->itemResponse($invoice);
@ -850,4 +852,65 @@ class InvoiceController extends BaseController
return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500); return response(['message' => 'Oops, something went wrong. Make sure you have symlink to storage/ in public/ directory.'], 500);
} }
} }
/**
* Update the specified resource in storage.
*
* @param UploadInvoiceRequest $request
* @param Invoice $invoice
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/invoices/{id}/upload",
* operationId="uploadInvoice",
* tags={"invoices"},
* summary="Uploads a document to a invoice",
* description="Handles the uploading of a document to a invoice",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Invoice Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Invoice object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Invoice"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadInvoiceRequest $request, Invoice $invoice)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $invoice);
return $this->itemResponse($invoice->fresh());
}
} }

View File

@ -0,0 +1,23 @@
<?php
/**
* @OA\Schema(
* schema="Document",
* type="object",
* @OA\Property(property="id", type="string", example="AS3df3A", description="The design hashed id"),
* @OA\Property(property="user_id", type="string", example="", description="__________"),
* @OA\Property(property="assigned_user_id", type="string", example="", description="__________"),
* @OA\Property(property="project_id", type="string", example="", description="__________"),
* @OA\Property(property="vendor_id", type="string", example="", description="__________"),
* @OA\Property(property="name", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="url", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="preview", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="type", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="disk", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="hash", type="string", example="Beauty", description="The design name"),
* @OA\Property(property="is_deleted", type="boolean", example=true, description="Flag to determine if the design is deleted"),
* @OA\Property(property="is_default", type="boolean", example=true, description="Flag to determine if the document is a default doc"),
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* @OA\Property(property="deleted_at", type="number", format="integer", example="134341234234", description="Timestamp"),
* )
*/

View File

@ -22,12 +22,14 @@ use App\Http\Requests\Payment\RefundPaymentRequest;
use App\Http\Requests\Payment\ShowPaymentRequest; use App\Http\Requests\Payment\ShowPaymentRequest;
use App\Http\Requests\Payment\StorePaymentRequest; use App\Http\Requests\Payment\StorePaymentRequest;
use App\Http\Requests\Payment\UpdatePaymentRequest; use App\Http\Requests\Payment\UpdatePaymentRequest;
use App\Http\Requests\Payment\UploadPaymentRequest;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Payment; use App\Models\Payment;
use App\Repositories\PaymentRepository; use App\Repositories\PaymentRepository;
use App\Transformers\PaymentTransformer; use App\Transformers\PaymentTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -37,6 +39,7 @@ use Illuminate\Http\Response;
class PaymentController extends BaseController class PaymentController extends BaseController
{ {
use MakesHash; use MakesHash;
use SavesDocuments;
protected $entity_type = Payment::class; protected $entity_type = Payment::class;
@ -671,4 +674,65 @@ class PaymentController extends BaseController
return $this->itemResponse($payment); return $this->itemResponse($payment);
} }
/**
* Update the specified resource in storage.
*
* @param UploadPaymentRequest $request
* @param Payment $payment
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/payments/{id}/upload",
* operationId="uploadPayment",
* tags={"payments"},
* summary="Uploads a document to a payment",
* description="Handles the uploading of a document to a payment",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Payment Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Payment object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Payment"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadPaymentRequest $request, Payment $payment)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $payment);
return $this->itemResponse($payment->fresh());
}
} }

View File

@ -19,16 +19,19 @@ use App\Http\Requests\Product\EditProductRequest;
use App\Http\Requests\Product\ShowProductRequest; use App\Http\Requests\Product\ShowProductRequest;
use App\Http\Requests\Product\StoreProductRequest; use App\Http\Requests\Product\StoreProductRequest;
use App\Http\Requests\Product\UpdateProductRequest; use App\Http\Requests\Product\UpdateProductRequest;
use App\Http\Requests\Product\UploadProductRequest;
use App\Models\Product; use App\Models\Product;
use App\Repositories\ProductRepository; use App\Repositories\ProductRepository;
use App\Transformers\ProductTransformer; use App\Transformers\ProductTransformer;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
class ProductController extends BaseController class ProductController extends BaseController
{ {
use MakesHash; use MakesHash;
use SavesDocuments;
protected $entity_type = Product::class; protected $entity_type = Product::class;
@ -476,4 +479,65 @@ class ProductController extends BaseController
return $this->listResponse(Product::withTrashed()->whereIn('id', $this->transformKeys($ids))); return $this->listResponse(Product::withTrashed()->whereIn('id', $this->transformKeys($ids)));
} }
/**
* Update the specified resource in storage.
*
* @param UploadProductRequest $request
* @param Product $product
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/products/{id}/upload",
* operationId="uploadProduct",
* tags={"products"},
* summary="Uploads a document to a product",
* description="Handles the uploading of a document to a product",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Product Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Product object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Product"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadProductRequest $request, Product $product)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $product);
return $this->itemResponse($product->fresh());
}
} }

View File

@ -19,6 +19,7 @@ use App\Http\Requests\Project\EditProjectRequest;
use App\Http\Requests\Project\ShowProjectRequest; use App\Http\Requests\Project\ShowProjectRequest;
use App\Http\Requests\Project\StoreProjectRequest; use App\Http\Requests\Project\StoreProjectRequest;
use App\Http\Requests\Project\UpdateProjectRequest; use App\Http\Requests\Project\UpdateProjectRequest;
use App\Http\Requests\Project\UploadProjectRequest;
use App\Models\Project; use App\Models\Project;
use App\Repositories\ProjectRepository; use App\Repositories\ProjectRepository;
use App\Transformers\ProjectTransformer; use App\Transformers\ProjectTransformer;
@ -503,4 +504,65 @@ class ProjectController extends BaseController
return $this->listResponse(Project::withTrashed()->whereIn('id', $this->transformKeys($ids))); return $this->listResponse(Project::withTrashed()->whereIn('id', $this->transformKeys($ids)));
} }
/**
* Update the specified resource in storage.
*
* @param UploadProductRequest $request
* @param Product $project
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/projects/{id}/upload",
* operationId="uploadProject",
* tags={"projects"},
* summary="Uploads a document to a project",
* description="Handles the uploading of a document to a project",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Project Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Project object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Project"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadProjectRequest $request, Project $project)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $project);
return $this->itemResponse($project->fresh());
}
} }

View File

@ -24,6 +24,7 @@ use App\Http\Requests\Quote\EditQuoteRequest;
use App\Http\Requests\Quote\ShowQuoteRequest; use App\Http\Requests\Quote\ShowQuoteRequest;
use App\Http\Requests\Quote\StoreQuoteRequest; use App\Http\Requests\Quote\StoreQuoteRequest;
use App\Http\Requests\Quote\UpdateQuoteRequest; use App\Http\Requests\Quote\UpdateQuoteRequest;
use App\Http\Requests\Quote\UploadQuoteRequest;
use App\Jobs\Invoice\ZipInvoices; use App\Jobs\Invoice\ZipInvoices;
use App\Models\Client; use App\Models\Client;
use App\Models\Invoice; use App\Models\Invoice;
@ -34,6 +35,7 @@ use App\Transformers\QuoteTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\TempFile; use App\Utils\TempFile;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -43,6 +45,7 @@ use Illuminate\Http\Response;
class QuoteController extends BaseController class QuoteController extends BaseController
{ {
use MakesHash; use MakesHash;
use SavesDocuments;
protected $entity_type = Quote::class; protected $entity_type = Quote::class;
@ -717,4 +720,65 @@ class QuoteController extends BaseController
return response()->download($file_path); return response()->download($file_path);
} }
/**
* Update the specified resource in storage.
*
* @param UploadQuoteRequest $request
* @param Quote $quote
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/quotes/{id}/upload",
* operationId="uploadQuote",
* tags={"quotes"},
* summary="Uploads a document to a quote",
* description="Handles the uploading of a document to a quote",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Quote Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Quote object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Quote"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadQuoteRequest $request, Quote $quote)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $quote);
return $this->itemResponse($quote->fresh());
}
} }

View File

@ -20,10 +20,12 @@ use App\Http\Requests\RecurringInvoice\EditRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\ShowRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\ShowRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\StoreRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\UpdateRecurringInvoiceRequest; use App\Http\Requests\RecurringInvoice\UpdateRecurringInvoiceRequest;
use App\Http\Requests\RecurringInvoice\UploadRecurringInvoiceRequest;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Repositories\RecurringInvoiceRepository; use App\Repositories\RecurringInvoiceRepository;
use App\Transformers\RecurringInvoiceTransformer; use App\Transformers\RecurringInvoiceTransformer;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -33,6 +35,7 @@ use Illuminate\Http\Response;
class RecurringInvoiceController extends BaseController class RecurringInvoiceController extends BaseController
{ {
use MakesHash; use MakesHash;
use SavesDocuments;
protected $entity_type = RecurringInvoice::class; protected $entity_type = RecurringInvoice::class;
@ -680,4 +683,65 @@ class RecurringInvoiceController extends BaseController
break; break;
} }
} }
/**
* Update the specified resource in storage.
*
* @param UploadRecurringInvoiceRequest $request
* @param RecurringInvoice $recurring_invoice
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/recurring_invoices/{id}/upload",
* operationId="uploadRecurringInvoice",
* tags={"recurring_invoices"},
* summary="Uploads a document to a recurring_invoice",
* description="Handles the uploading of a document to a recurring_invoice",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The RecurringInvoice Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the RecurringInvoice object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/RecurringInvoice"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadRecurringInvoiceRequest $request, RecurringInvoice $recurring_invoice)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $recurring_invoice);
return $this->itemResponse($recurring_invoice->fresh());
}
} }

View File

@ -21,12 +21,14 @@ use App\Http\Requests\Task\EditTaskRequest;
use App\Http\Requests\Task\ShowTaskRequest; use App\Http\Requests\Task\ShowTaskRequest;
use App\Http\Requests\Task\StoreTaskRequest; use App\Http\Requests\Task\StoreTaskRequest;
use App\Http\Requests\Task\UpdateTaskRequest; use App\Http\Requests\Task\UpdateTaskRequest;
use App\Http\Requests\Task\UploadTaskRequest;
use App\Models\Task; use App\Models\Task;
use App\Repositories\TaskRepository; use App\Repositories\TaskRepository;
use App\Transformers\TaskTransformer; use App\Transformers\TaskTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\BulkOptions; use App\Utils\Traits\BulkOptions;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use App\Utils\Traits\Uploadable; use App\Utils\Traits\Uploadable;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -40,6 +42,7 @@ class TaskController extends BaseController
use MakesHash; use MakesHash;
use Uploadable; use Uploadable;
use BulkOptions; use BulkOptions;
use SavesDocuments;
protected $entity_type = Task::class; protected $entity_type = Task::class;
@ -506,4 +509,65 @@ class TaskController extends BaseController
{ {
//todo //todo
} }
/**
* Update the specified resource in storage.
*
* @param UploadTaskRequest $request
* @param Task $task
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/tasks/{id}/upload",
* operationId="uploadTask",
* tags={"tasks"},
* summary="Uploads a document to a task",
* description="Handles the uploading of a document to a task",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Task Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Task object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Task"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadTaskRequest $request, Task $task)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $task);
return $this->itemResponse($task->fresh());
}
} }

View File

@ -21,12 +21,14 @@ use App\Http\Requests\Vendor\EditVendorRequest;
use App\Http\Requests\Vendor\ShowVendorRequest; use App\Http\Requests\Vendor\ShowVendorRequest;
use App\Http\Requests\Vendor\StoreVendorRequest; use App\Http\Requests\Vendor\StoreVendorRequest;
use App\Http\Requests\Vendor\UpdateVendorRequest; use App\Http\Requests\Vendor\UpdateVendorRequest;
use App\Http\Requests\Vendor\UploadVendorRequest;
use App\Models\Vendor; use App\Models\Vendor;
use App\Repositories\VendorRepository; use App\Repositories\VendorRepository;
use App\Transformers\VendorTransformer; use App\Transformers\VendorTransformer;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\BulkOptions; use App\Utils\Traits\BulkOptions;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use App\Utils\Traits\Uploadable; use App\Utils\Traits\Uploadable;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -39,6 +41,7 @@ class VendorController extends BaseController
use MakesHash; use MakesHash;
use Uploadable; use Uploadable;
use BulkOptions; use BulkOptions;
use SavesDocuments;
protected $entity_type = Vendor::class; protected $entity_type = Vendor::class;
@ -511,4 +514,65 @@ class VendorController extends BaseController
{ {
//todo //todo
} }
/**
* Update the specified resource in storage.
*
* @param UploadVendorRequest $request
* @param Vendor $vendor
* @return Response
*
*
*
* @OA\Put(
* path="/api/v1/vendors/{id}/upload",
* operationId="uploadVendor",
* tags={"vendors"},
* summary="Uploads a document to a vendor",
* description="Handles the uploading of a document to a vendor",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(ref="#/components/parameters/include"),
* @OA\Parameter(
* name="id",
* in="path",
* description="The Vendor Hashed ID",
* example="D2J234DFA",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="Returns the Vendor object",
* @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Vendor"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
*
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function upload(UploadVendorRequest $request, Vendor $vendor)
{
if ($request->has('documents'))
$this->saveDocuments($request->file('documents'), $vendor);
return $this->itemResponse($vendor->fresh());
}
} }

View File

@ -28,6 +28,7 @@ class CreditsTable extends Component
{ {
$query = Credit::query() $query = Credit::query()
->where('client_id', auth('contact')->user()->client->id) ->where('client_id', auth('contact')->user()->client->id)
->where('status_id', '<>', Credit::STATUS_DRAFT)
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
->paginate($this->per_page); ->paginate($this->per_page);

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Client;
use App\Http\Requests\Request;
class UploadClientRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->client);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Company;
use App\Http\Requests\Request;
class UploadCompanyRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->company);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Credit;
use App\Http\Requests\Request;
class UploadCreditRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->credit);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Expense;
use App\Http\Requests\Request;
class UploadExpenseRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->expense);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Invoice;
use App\Http\Requests\Request;
class UploadInvoiceRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->invoice);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Payment Ninja (https://paymentninja.com).
*
* @link https://github.com/paymentninja/paymentninja source repository
*
* @copyright Copyright (c) 2021. Payment Ninja LLC (https://paymentninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Payment;
use App\Http\Requests\Request;
class UploadPaymentRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->payment);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Product Ninja (https://paymentninja.com).
*
* @link https://github.com/paymentninja/paymentninja source repository
*
* @copyright Copyright (c) 2021. Product Ninja LLC (https://paymentninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Product;
use App\Http\Requests\Request;
class UploadProductRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->product);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Project Ninja (https://paymentninja.com).
*
* @link https://github.com/paymentninja/paymentninja source repository
*
* @copyright Copyright (c) 2021. Project Ninja LLC (https://paymentninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Project;
use App\Http\Requests\Request;
class UploadProjectRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->project);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Quote Ninja (https://paymentninja.com).
*
* @link https://github.com/paymentninja/paymentninja source repository
*
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://paymentninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Quote;
use App\Http\Requests\Request;
class UploadQuoteRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->quote);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Quote Ninja (https://paymentninja.com).
*
* @link https://github.com/paymentninja/paymentninja source repository
*
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://paymentninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\RecurringInvoice;
use App\Http\Requests\Request;
class UploadRecurringInvoiceRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->recurring_invoice);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Quote Ninja (https://paymentninja.com).
*
* @link https://github.com/paymentninja/paymentninja source repository
*
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://paymentninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Task;
use App\Http\Requests\Request;
class UploadTaskRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->task);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -0,0 +1,39 @@
<?php
/**
* Quote Ninja (https://paymentninja.com).
*
* @link https://github.com/paymentninja/paymentninja source repository
*
* @copyright Copyright (c) 2021. Quote Ninja LLC (https://paymentninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Requests\Vendor;
use App\Http\Requests\Request;
class UploadVendorRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize() : bool
{
return auth()->user()->can('edit', $this->vendor);
}
public function rules()
{
$rules = [];
if($this->input('documents'))
$rules['documents'] = 'file|mimes:html,csv,png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:2000000';
return $rules;
}
}

View File

@ -17,8 +17,12 @@ use App\Jobs\Company\CreateCompany;
use App\Jobs\Company\CreateCompanyPaymentTerms; use App\Jobs\Company\CreateCompanyPaymentTerms;
use App\Jobs\Company\CreateCompanyTaskStatuses; use App\Jobs\Company\CreateCompanyTaskStatuses;
use App\Jobs\Company\CreateCompanyToken; use App\Jobs\Company\CreateCompanyToken;
use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Jobs\User\CreateUser; use App\Jobs\User\CreateUser;
use App\Jobs\Util\VersionCheck; use App\Jobs\Util\VersionCheck;
use App\Mail\Admin\AccountCreatedObject;
use App\Models\Account; use App\Models\Account;
use App\Notifications\Ninja\NewAccountCreated; use App\Notifications\Ninja\NewAccountCreated;
use App\Utils\Ninja; use App\Utils\Ninja;
@ -88,7 +92,16 @@ class CreateAccount
$spaa9f78->fresh(); $spaa9f78->fresh();
$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja(); //todo implement SLACK notifications
//$sp035a66->notification(new NewAccountCreated($spaa9f78, $sp035a66))->ninja();
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new AccountCreatedObject($spaa9f78, $sp035a66))->build());
$nmo->company = $sp035a66;
$nmo->to_user = $spaa9f78;
$nmo->settings = $sp035a66->settings;
NinjaMailerJob::dispatchNow($nmo);
VersionCheck::dispatchNow(); VersionCheck::dispatchNow();

View File

@ -16,6 +16,8 @@ use App\Events\Invoice\InvoiceWasEmailed;
use App\Events\Invoice\InvoiceWasEmailedAndFailed; use App\Events\Invoice\InvoiceWasEmailedAndFailed;
use App\Jobs\Mail\BaseMailerJob; use App\Jobs\Mail\BaseMailerJob;
use App\Jobs\Mail\EntityFailedSendMailer; use App\Jobs\Mail\EntityFailedSendMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\TemplateEmail; use App\Mail\TemplateEmail;
use App\Models\Activity; use App\Models\Activity;
@ -36,7 +38,7 @@ use Illuminate\Support\Str;
/*Multi Mailer implemented*/ /*Multi Mailer implemented*/
class EmailEntity extends BaseMailerJob implements ShouldQueue class EmailEntity implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
@ -103,21 +105,16 @@ class EmailEntity extends BaseMailerJob implements ShouldQueue
/* Set DB */ /* Set DB */
MultiDB::setDB($this->company->db); MultiDB::setDB($this->company->db);
/* Set the correct mail driver */ $nmo = new NinjaMailerObject;
$this->setMailDriver(); $nmo->mailable = new TemplateEmail($this->email_entity_builder,$this->invitation->contact);
$nmo->company = $this->company;
$nmo->settings = $this->settings;
$nmo->to_user = $this->invitation->contact;
$nmo->entity_string = $this->entity_string;
$nmo->invitation = $this->invitation;
$nmo->reminder_template = $this->reminder_template;
try { NinjaMailerJob::dispatch($nmo);
Mail::to($this->invitation->contact->email, $this->invitation->contact->present()->name())
->send(
new TemplateEmail(
$this->email_entity_builder,
$this->invitation->contact
)
);
} catch (\Exception $e) {
$this->entityEmailFailed($e->getMessage());
$this->logMailError($e->getMessage(), $this->entity->client);
}
/* Mark entity sent */ /* Mark entity sent */
$this->entity->service()->markSent()->save(); $this->entity->service()->markSent()->save();
@ -136,7 +133,7 @@ class EmailEntity extends BaseMailerJob implements ShouldQueue
} }
} }
/* Switch statement to handling failure notifications */ /* Switch statement to handle failure notifications */
private function entityEmailFailed($message) private function entityEmailFailed($message)
{ {
switch ($this->entity_string) { switch ($this->entity_string) {

View File

@ -1,109 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use App\Libraries\MultiDB;
use App\Mail\Admin\AutoBillingFailureObject;
use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\PaymentFailureObject;
use App\Models\User;
use App\Utils\Traits\Notifications\UserNotifies;
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\Mail;
/*Multi Mailer implemented*/
class AutoBillingFailureMailer extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UserNotifies;
public $client;
public $error;
public $company;
public $payment_hash;
public $settings;
/**
* Create a new job instance.
*
* @param $client
* @param $message
* @param $company
* @param $amount
*/
public function __construct($client, $error, $company, $payment_hash)
{
$this->client = $client;
$this->error = $error;
$this->company = $company;
$this->payment_hash = $payment_hash;
$this->company = $company;
$this->settings = $client->getMergedSettings();
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
/*If we are migrating data we don't want to fire these notification*/
if ($this->company->is_disabled) {
return true;
}
//Set DB
MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
//iterate through company_users
$this->company->company_users->each(function ($company_user) {
//determine if this user has the right permissions
$methods = $this->findCompanyUserNotificationType($company_user, ['payment_failure','all_notifications']);
//if mail is a method type -fire mail!!
if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]);
$mail_obj = (new AutoBillingFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build();
$mail_obj->from = [config('mail.from.address'), config('mail.from.name')];
//send email
try {
Mail::to($company_user->user->email)
->send(new EntityNotificationMailer($mail_obj));
} catch (\Exception $e) {
//$this->failed($e);
$this->logMailError($e->getMessage(), $this->client);
}
}
});
}
}

View File

@ -18,6 +18,7 @@ use App\Models\SystemLog;
use App\Models\User; use App\Models\User;
use App\Providers\MailServiceProvider; use App\Providers\MailServiceProvider;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
@ -28,11 +29,14 @@ use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\Lang;
use Turbo124\Beacon\Facades\LightLogs; use Turbo124\Beacon\Facades\LightLogs;
/*Multi Mailer implemented*/ /*
Multi Mailer implemented
@Deprecated 14/02/2021
*/
class BaseMailerJob implements ShouldQueue class BaseMailerJob implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
public $tries = 5; //number of retries public $tries = 5; //number of retries
@ -42,7 +46,11 @@ class BaseMailerJob implements ShouldQueue
public function setMailDriver() public function setMailDriver()
{ {
/* Singletons need to be rebooted each time just in case our Locale is changing*/
App::forgetInstance('translator'); App::forgetInstance('translator');
App::forgetInstance('mail.manager'); //singletons must be destroyed!
/* Inject custom translations if any exist */
Lang::replace(Ninja::transformTranslations($this->settings)); Lang::replace(Ninja::transformTranslations($this->settings));
switch ($this->settings->email_sending_method) { switch ($this->settings->email_sending_method) {
@ -60,7 +68,7 @@ class BaseMailerJob implements ShouldQueue
{ {
$sending_user = $this->settings->gmail_sending_user_id; $sending_user = $this->settings->gmail_sending_user_id;
$user = User::find($sending_user); $user = User::find($this->decodePrimaryKey($sending_user));
$google = (new Google())->init(); $google = (new Google())->init();
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token)); $google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
@ -75,12 +83,15 @@ class BaseMailerJob implements ShouldQueue
* just for this request. * just for this request.
*/ */
Config::set('mail.driver', 'gmail'); // config(['mail.driver' => 'gmail']);
Config::set('services.gmail.token', $user->oauth_user_token->access_token); // config(['services.gmail.token' => $user->oauth_user_token->access_token]);
Config::set('mail.from.address', $user->email); // config(['mail.from.address' => $user->email]);
Config::set('mail.from.name', $user->present()->name()); // config(['mail.from.name' => $user->present()->name()]);
(new MailServiceProvider(app()))->register(); //(new MailServiceProvider(app()))->register();
nlog("after registering mail service provider");
nlog(config('services.gmail.token'));
} }
public function logMailError($errors, $recipient_object) public function logMailError($errors, $recipient_object)
@ -96,7 +107,7 @@ class BaseMailerJob implements ShouldQueue
public function failed($exception = null) public function failed($exception = null)
{ {
nlog('the job failed'); nlog('mailer job failed');
nlog($exception->getMessage()); nlog($exception->getMessage());
$job_failure = new EmailFailure(); $job_failure = new EmailFailure();

View File

@ -1,111 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use App\Libraries\MultiDB;
use App\Mail\Admin\ClientPaymentFailureObject;
use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\PaymentFailureObject;
use App\Models\Invoice;
use App\Models\User;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Notifications\UserNotifies;
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\Mail;
/*Multi Mailer implemented*/
class ClientPaymentFailureMailer extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UserNotifies, MakesHash;
public $client;
public $error;
public $company;
public $payment_hash;
public $settings;
/**
* Create a new job instance.
*
* @param $client
* @param $message
* @param $company
* @param $amount
*/
public function __construct($client, $error, $company, $payment_hash)
{
$this->company = $company;
$this->error = $error;
$this->client = $client;
$this->payment_hash = $payment_hash;
$this->company = $company;
$this->settings = $client->getMergedSettings();
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
/*If we are migrating data we don't want to fire these notification*/
if ($this->company->is_disabled) {
return true;
}
//Set DB
MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
$this->invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->get();
$this->invoices->first()->invitations->each(function ($invitation) {
if ($invitation->contact->send_email && $invitation->contact->email) {
$mail_obj = (new ClientPaymentFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build();
$mail_obj->from = [config('mail.from.address'), config('mail.from.name')];
//send email
try {
Mail::to($invitation->contact->email)
->send(new EntityNotificationMailer($mail_obj));
} catch (\Exception $e) {
$this->logMailError($e->getMessage(), $this->client);
}
}
});
}
}

View File

@ -1,103 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use App\Libraries\MultiDB;
use App\Mail\Admin\EntityFailedSendObject;
use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\EntitySentObject;
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\Mail;
/*Multi Mailer implemented*/
class EntityFailedSendMailer extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $company;
public $user;
public $invitation;
public $entity_type;
public $entity;
public $settings;
public $template;
public $message;
/**
* Create a new job instance.
*
* @param $invitation
* @param $entity_type
* @param $user
* @param $company
*/
public function __construct($invitation, $entity_type, $user, $company, $template, $message)
{
$this->company = $company;
$this->user = $user;
$this->invitation = $invitation;
$this->entity = $invitation->{$entity_type};
$this->entity_type = $entity_type;
$this->settings = $invitation->contact->client->getMergedSettings();
$this->template = $template;
$this->message = $message;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
/*If we are migrating data we don't want to fire these notification*/
if ($this->company->is_disabled)
return true;
//Set DB
MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
$mail_obj = (new EntityFailedSendObject($this->invitation, $this->entity_type, $this->template, $this->message))->build();
$mail_obj->from = [config('mail.from.address'), config('mail.from.name')];
try {
Mail::to($this->user->email)
->send(new EntityNotificationMailer($mail_obj));
} catch (\Exception $e) {
nlog("failing in EntityFailedSendMailer");
//$this->failed($e);
$this->logMailError($e->getMessage(), $this->entity->client);
}
}
}

View File

@ -1,90 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use App\Libraries\MultiDB;
use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\EntityPaidObject;
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\Mail;
/*Multi Mailer implemented*/
class EntityPaidMailer extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $company;
public $user;
public $payment;
public $entity_type;
public $entity;
public $settings;
/**
* Create a new job instance.
*
* @param $payment
* @param $user
* @param $company
*/
public function __construct($payment, $company, $user)
{
$this->company = $company;
$this->user = $user;
$this->payment = $payment;
$this->settings = $payment->client->getMergedSettings();
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
/*If we are migrating data we don't want to fire these notification*/
if ($this->company->is_disabled) {
return true;
}
//Set DB
MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
try {
$mail_obj = (new EntityPaidObject($this->payment))->build();
$mail_obj->from = [config('mail.from.address'), config('mail.from.name')];
//send email
Mail::to($this->user->email)
->send(new EntityNotificationMailer($mail_obj));
} catch (\Exception $e) {
// //$this->failed($e);
$this->logMailError($e->getMessage(), $this->payment->client);
}
}
}

View File

@ -1,97 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use App\Libraries\MultiDB;
use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\EntitySentObject;
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\Mail;
/*Multi Mailer implemented*/
class EntitySentMailer extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $company;
public $user;
public $invitation;
public $entity_type;
public $entity;
public $settings;
public $template;
/**
* Create a new job instance.
*
* @param $invitation
* @param $entity_type
* @param $user
* @param $company
*/
public function __construct($invitation, $entity_type, $user, $company, $template)
{
$this->company = $company;
$this->user = $user;
$this->invitation = $invitation;
$this->entity = $invitation->{$entity_type};
$this->entity_type = $entity_type;
$this->settings = $invitation->contact->client->getMergedSettings();
$this->template = $template;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
/*If we are migrating data we don't want to fire these notification*/
if ($this->company->is_disabled)
return true;
//Set DB
MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
$mail_obj = (new EntitySentObject($this->invitation, $this->entity_type, $this->template))->build();
$mail_obj->from = [config('mail.from.address'), config('mail.from.name')];
try {
Mail::to($this->user->email)
->send(new EntityNotificationMailer($mail_obj));
} catch (\Exception $e) {
// //$this->failed($e);
$this->logMailError($e->getMessage(), $this->entity->client);
}
}
}

View File

@ -1,95 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use App\Libraries\MultiDB;
use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\EntityViewedObject;
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\Mail;
/*Multi Mailer implemented*/
class EntityViewedMailer extends BaseMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $company;
public $user;
public $invitation;
public $entity_type;
public $entity;
public $settings;
/**
* Create a new job instance.
*
* @param $invitation
* @param $entity_type
* @param $user
* @param $company
*/
public function __construct($invitation, $entity_type, $user, $company)
{
$this->company = $company;
$this->user = $user;
$this->invitation = $invitation;
$this->entity = $invitation->{$entity_type};
$this->entity_type = $entity_type;
$this->settings = $invitation->contact->client->getMergedSettings();
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
/*If we are migrating data we don't want to fire these notification*/
if ($this->company->is_disabled) {
return true;
}
//Set DB
MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
$mail_obj = (new EntityViewedObject($this->invitation, $this->entity_type))->build();
$mail_obj->from = [config('mail.from.address'), config('mail.from.name')];
//send email
try {
Mail::to($this->user->email)
->send(new EntityNotificationMailer($mail_obj));
} catch (\Exception $e) {
//$this->failed($e);
$this->logMailError($e->getMessage(), $this->entity->client);
}
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use Illuminate\Mail\Mailable;
class NinjaMailer extends Mailable
{
public $mail_obj;
/**
* Create a new message instance.
*
* @param $mail_obj
*/
public function __construct($mail_obj)
{
$this->mail_obj = $mail_obj;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->from(config('mail.from.address'), config('mail.from.name'))
->subject($this->mail_obj->subject)
->markdown($this->mail_obj->markdown, $this->mail_obj->data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->mail_obj->tag);
});
}
}

View File

@ -0,0 +1,189 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
use App\DataMapper\Analytics\EmailFailure;
use App\Events\Invoice\InvoiceWasEmailedAndFailed;
use App\Jobs\Mail\NinjaMailerObject;
use App\Jobs\Util\SystemLogger;
use App\Libraries\Google\Google;
use App\Libraries\MultiDB;
use App\Mail\TemplateEmail;
use App\Models\ClientContact;
use App\Models\SystemLog;
use App\Models\User;
use App\Providers\MailServiceProvider;
use App\Utils\Ninja;
use App\Utils\Traits\MakesHash;
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\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Mail;
use Turbo124\Beacon\Facades\LightLogs;
use Dacastro4\LaravelGmail\Facade\LaravelGmail;
/*Multi Mailer implemented*/
class NinjaMailerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MakesHash;
public $tries = 5; //number of retries
public $backoff = 5; //seconds to wait until retry
public $deleteWhenMissingModels = true;
public $nmo;
public function __construct(NinjaMailerObject $nmo)
{
$this->nmo = $nmo;
}
public function handle()
{
/*If we are migrating data we don't want to fire any emails*/
if ($this->nmo->company->is_disabled)
return true;
/*Set the correct database*/
MultiDB::setDb($this->nmo->company->db);
/* Set the email driver */
$this->setMailDriver();
//send email
try {
nlog("trying to send");
Mail::to($this->nmo->to_user->email)
->send($this->nmo->mailable);
} catch (\Exception $e) {
nlog("error failed with {$e->getMessage()}");
if ($this->nmo->to_user instanceof ClientContact)
$this->logMailError($e->getMessage(), $this->nmo->to_user->client);
if($this->nmo->entity_string)
$this->entityEmailFailed($e->getMessage());
}
}
/* Switch statement to handle failure notifications */
private function entityEmailFailed($message)
{
switch ($this->nmo->entity_string) {
case 'invoice':
event(new InvoiceWasEmailedAndFailed($this->nmo->invitation, $this->nmo->company, $message, $this->nmo->reminder_template, Ninja::eventVars()));
break;
default:
# code...
break;
}
}
private function setMailDriver()
{
/* Singletons need to be rebooted each time just in case our Locale is changing*/
App::forgetInstance('translator');
App::forgetInstance('mail.manager'); //singletons must be destroyed!
App::forgetInstance('mailer');
App::forgetInstance('laravelgmail');
/* Inject custom translations if any exist */
Lang::replace(Ninja::transformTranslations($this->nmo->settings));
switch ($this->nmo->settings->email_sending_method) {
case 'default':
config(['mail.driver' => config('mail.default')]);
break;
case 'gmail':
$this->setGmailMailer();
break;
default:
break;
}
(new MailServiceProvider(app()))->register();
}
private function setGmailMailer()
{
if(LaravelGmail::check())
LaravelGmail::logout();
$sending_user = $this->nmo->settings->gmail_sending_user_id;
$user = User::find($this->decodePrimaryKey($sending_user));
nlog("Sending via {$user->present()->name()}");
$google = (new Google())->init();
$google->getClient()->setAccessToken(json_encode($user->oauth_user_token));
if ($google->getClient()->isAccessTokenExpired()) {
$google->refreshToken($user);
}
/*
* Now that our token is refreshed and valid we can boot the
* mail driver at runtime and also set the token which will persist
* just for this request.
*/
config(['mail.driver' => 'gmail']);
(new MailServiceProvider(app()))->register();
$token = $user->oauth_user_token->access_token;
$this->nmo
->mailable
->from($user->email, $user->present()->name())
->withSwiftMessage(function ($message) use($token) {
$message->getHeaders()->addTextHeader('GmailToken', $token);
});
}
private function logMailError($errors, $recipient_object)
{
SystemLogger::dispatch(
$errors,
SystemLog::CATEGORY_MAIL,
SystemLog::EVENT_MAIL_SEND,
SystemLog::TYPE_FAILURE,
$recipient_object
);
}
public function failed($exception = null)
{
nlog('mailer job failed');
nlog($exception->getMessage());
$job_failure = new EmailFailure();
$job_failure->string_metric5 = get_parent_class($this);
$job_failure->string_metric6 = $exception->getMessage();
LightLogs::create($job_failure)
->batch();
}
}

View File

@ -0,0 +1,38 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Jobs\Mail;
/**
* NinjaMailerObject.
*/
class NinjaMailerObject
{
public $mailable;
public $company;
public $from_user; //not yet used
public $to_user;
public $settings;
public $transport; //not yet used
/* Variable for cascading notifications */
public $entity_string = FALSE;
public $invitation = FALSE;
public $template = FALSE;
}

View File

@ -11,9 +11,12 @@
namespace App\Jobs\Mail; namespace App\Jobs\Mail;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntityNotificationMailer; use App\Mail\Admin\EntityNotificationMailer;
use App\Mail\Admin\PaymentFailureObject; use App\Mail\Admin\PaymentFailureObject;
use App\Mail\NinjaMailer;
use App\Models\User; use App\Models\User;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -70,39 +73,29 @@ class PaymentFailureMailer extends BaseMailerJob implements ShouldQueue
public function handle() public function handle()
{ {
/*If we are migrating data we don't want to fire these notification*/
if ($this->company->is_disabled) {
return true;
}
//Set DB //Set DB
MultiDB::setDb($this->company->db); MultiDB::setDb($this->company->db);
//if we need to set an email driver do it now
$this->setMailDriver();
//iterate through company_users //iterate through company_users
$this->company->company_users->each(function ($company_user) { $this->company->company_users->each(function ($company_user) {
//determine if this user has the right permissions //determine if this user has the right permissions
$methods = $this->findCompanyUserNotificationType($company_user, ['payment_failure','all_notifications']); $methods = $this->findCompanyUserNotificationType($company_user, ['payment_failure','all_notifications']);
//if mail is a method type -fire mail!! //if mail is a method type -fire mail!!
if (($key = array_search('mail', $methods)) !== false) { if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]); unset($methods[$key]);
$mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build(); $mail_obj = (new PaymentFailureObject($this->client, $this->error, $this->company, $this->payment_hash))->build();
$mail_obj->from = [config('mail.from.address'), config('mail.from.name')];
//send email $nmo = new NinjaMailerObject;
try { $nmo->mailable = new NinjaMailer($mail_obj);
Mail::to($company_user->user->email) $nmo->company = $this->company;
->send(new EntityNotificationMailer($mail_obj)); $nmo->to_user = $company_user->user;
} catch (\Exception $e) { $nmo->settings = $this->settings;
//$this->failed($e);
$this->logMailError($e->getMessage(), $this->client); NinjaMailerJob::dispatch($nmo);
}
} }
}); });
} }

View File

@ -25,6 +25,7 @@ use Illuminate\Queue\Middleware\RateLimited;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Turbo124\Beacon\Jobs\Database\MySQL\DbStatus; use Turbo124\Beacon\Jobs\Database\MySQL\DbStatus;
use Illuminate\Support\Facades\Cache;
class CheckCompanyData implements ShouldQueue class CheckCompanyData implements ShouldQueue
{ {
@ -36,12 +37,13 @@ class CheckCompanyData implements ShouldQueue
public $company_data = []; public $company_data = [];
public $is_valid;
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @return void * @return void
*/ */
public function __construct(Company $company, string $hash) public function __construct(Company $company, string $hash = '')
{ {
$this->company = $company; $this->company = $company;
$this->hash = $hash; $this->hash = $hash;
@ -54,6 +56,7 @@ class CheckCompanyData implements ShouldQueue
*/ */
public function handle() public function handle()
{ {
$this->is_valid = true;
$this->checkInvoiceBalances(); $this->checkInvoiceBalances();
$this->checkInvoicePayments(); $this->checkInvoicePayments();
@ -68,12 +71,19 @@ class CheckCompanyData implements ShouldQueue
else else
$cache_instance = Cache::add($this->hash, ''); $cache_instance = Cache::add($this->hash, '');
$cache_instance[$this->company->company_hash] = $this->company_data; $this->company_data['company_hash'] = $this->company->company_hash;
Cache::put($this->hash, $cache_instance, now()->addMinutes(30)); Cache::put($this->hash, $cache_instance, now()->addMinutes(30));
nlog(Cache::get($this->hash)); nlog(Cache::get($this->hash));
nlog($this->company_data);
if(!$this->is_valid)
$this->company_data['status'] = 'errors';
else
$this->company_data['status'] = 'success';
return $this->company_data;
} }
@ -88,25 +98,26 @@ class CheckCompanyData implements ShouldQueue
$wrong_balances = 0; $wrong_balances = 0;
$wrong_paid_to_dates = 0; $wrong_paid_to_dates = 0;
foreach ($this->company->clients->where('is_deleted', 0)->cursor() as $client) { foreach ($this->company->clients->where('is_deleted', 0) as $client) {
$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance'); $invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
$credit_balance = $client->credits->where('is_deleted', false)->sum('balance'); //$credit_balance = $client->credits->where('is_deleted', false)->sum('balance');
if($client->balance != $invoice_balance) // if($client->balance != $invoice_balance)
$invoice_balance -= $credit_balance;//doesn't make sense to remove the credit amount // $invoice_balance -= $credit_balance;//doesn't make sense to remove the credit amount
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first(); $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
if ($ledger && number_format($invoice_balance, 4) != number_format($client->balance, 4)) { if ($ledger && number_format($invoice_balance, 4) != number_format($client->balance, 4)) {
$wrong_balances++; $wrong_balances++;
$this->company_data[] = "# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger->balance}"; $this->company_data[] = "# {$client->id} " . $client->present()->name.' - '.$client->number." - Balance Failure - Invoice Balances = {$invoice_balance} Client Balance = {$client->balance} Ledger Balance = {$ledger->balance} ";
$this->isValid = false; $this->is_valid = false;
} }
} }
$this->logMessage("{$wrong_balances} clients with incorrect balances"); $this->company_data[] = "{$wrong_balances} clients with incorrect balances";
} }
private function checkInvoicePayments() private function checkInvoicePayments()
@ -114,7 +125,7 @@ class CheckCompanyData implements ShouldQueue
$wrong_balances = 0; $wrong_balances = 0;
$wrong_paid_to_dates = 0; $wrong_paid_to_dates = 0;
$this->company->clients->where('is_deleted', 0)->cursor()->each(function ($client) use ($wrong_balances) { $this->company->clients->where('is_deleted', 0)->each(function ($client) use ($wrong_balances) {
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) { $client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) {
$total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount'); $total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
$total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded'); $total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
@ -128,7 +139,7 @@ class CheckCompanyData implements ShouldQueue
$this->company_data[] = $client->present()->name.' - '.$client->id." - Total Amount = {$total_amount} != Calculated Total = {$calculated_paid_amount} - Total Refund = {$total_refund} Total credit = {$total_credit}"; $this->company_data[] = $client->present()->name.' - '.$client->id." - Total Amount = {$total_amount} != Calculated Total = {$calculated_paid_amount} - Total Refund = {$total_refund} Total credit = {$total_credit}";
$this->isValid = false; $this->is_valid = false;
} }
}); });
}); });
@ -141,12 +152,11 @@ class CheckCompanyData implements ShouldQueue
$wrong_paid_to_dates = 0; $wrong_paid_to_dates = 0;
$credit_total_applied = 0; $credit_total_applied = 0;
$this->company->clients->where('is_deleted', 0)->cursor()->each(function ($client) use ($wrong_paid_to_dates, $credit_total_applied) { $this->company->clients->where('is_deleted', 0)->each(function ($client) use ($wrong_paid_to_dates, $credit_total_applied) {
$total_invoice_payments = 0; $total_invoice_payments = 0;
foreach ($client->invoices->where('is_deleted', false)->where('status_id', '>', 1) as $invoice) { foreach ($client->invoices->where('is_deleted', false)->where('status_id', '>', 1) as $invoice) {
// $total_amount = $invoice->payments->whereNull('deleted_at')->sum('pivot.amount');
// $total_refund = $invoice->payments->whereNull('deleted_at')->sum('pivot.refunded');
$total_amount = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.amount'); $total_amount = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.amount');
$total_refund = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.refunded'); $total_refund = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])->sum('pivot.refunded');
@ -154,13 +164,14 @@ class CheckCompanyData implements ShouldQueue
$total_invoice_payments += ($total_amount - $total_refund); $total_invoice_payments += ($total_amount - $total_refund);
} }
foreach ($client->payments as $payment) { //10/02/21
$credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(DB::raw('amount')); // foreach ($client->payments as $payment) {
} // $credit_total_applied += $payment->paymentables->where('paymentable_type', App\Models\Credit::class)->sum(DB::raw('amount'));
// }
if ($credit_total_applied < 0) { // if ($credit_total_applied < 0) {
$total_invoice_payments += $credit_total_applied; // $total_invoice_payments += $credit_total_applied;
} //todo this is contentious // } //todo this is contentious
// nlog("total invoice payments = {$total_invoice_payments} with client paid to date of of {$client->paid_to_date}"); // nlog("total invoice payments = {$total_invoice_payments} with client paid to date of of {$client->paid_to_date}");
@ -169,7 +180,7 @@ class CheckCompanyData implements ShouldQueue
$this->company_data[] = $client->present()->name.'id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}"; $this->company_data[] = $client->present()->name.'id = # '.$client->id." - Paid to date does not match Client Paid To Date = {$client->paid_to_date} - Invoice Payments = {$total_invoice_payments}";
$this->isValid = false; $this->is_valid = false;
} }
}); });
@ -182,23 +193,24 @@ class CheckCompanyData implements ShouldQueue
$wrong_balances = 0; $wrong_balances = 0;
$wrong_paid_to_dates = 0; $wrong_paid_to_dates = 0;
foreach ($this->company->clients->where('is_deleted', 0)->cursor() as $client) { foreach ($this->company->clients->where('is_deleted', 0) as $client) {
//$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance'); //$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
$invoice_balance = Invoice::where('client_id', $client->id)->where('is_deleted', false)->where('status_id', '>', 1)->withTrashed()->sum('balance'); $invoice_balance = Invoice::where('client_id', $client->id)->where('is_deleted', false)->where('status_id', '>', 1)->withTrashed()->sum('balance');
$credit_balance = Credit::where('client_id', $client->id)->where('is_deleted', false)->withTrashed()->sum('balance'); $credit_balance = Credit::where('client_id', $client->id)->where('is_deleted', false)->withTrashed()->sum('balance');
/*Legacy - V4 will add credits to the balance - we may need to reverse engineer this and remove the credits from the client balance otherwise we need this hack here and in the invoice balance check.*/ //10/02/21
if($client->balance != $invoice_balance) // Legacy - V4 will add credits to the balance - we may need to reverse engineer this and remove the credits from the client balance otherwise we need this hack here and in the invoice balance check.
$invoice_balance -= $credit_balance; // if($client->balance != $invoice_balance)
// $invoice_balance -= $credit_balance;
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first(); $ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
if ($ledger && (string) $invoice_balance != (string) $client->balance) { if ($ledger && (string) $invoice_balance != (string) $client->balance) {
$wrong_paid_to_dates++; $wrong_paid_to_dates++;
$this->company_data[] = $client->present()->name.' - '.$client->id." - calculated client balances do not match {$invoice_balance} - ".rtrim($client->balance, '0'); $this->company_data[] = $client->present()->name.' - '.$client->id." - calculated client balances do not match {$invoice_balance} - ".rtrim($client->balance, '0')."";
$this->isValid = false; $this->is_valid = false;
} }
} }
@ -209,7 +221,7 @@ class CheckCompanyData implements ShouldQueue
{ {
// check for contacts with the contact_key value set // check for contacts with the contact_key value set
$contacts = DB::table('client_contacts') $contacts = DB::table('client_contacts')
->where('company_id', $this->company_id) ->where('company_id', $this->company->id)
->whereNull('contact_key') ->whereNull('contact_key')
->orderBy('id') ->orderBy('id')
->get(['id']); ->get(['id']);
@ -217,24 +229,24 @@ class CheckCompanyData implements ShouldQueue
$this->company_data[] = $contacts->count().' contacts without a contact_key'; $this->company_data[] = $contacts->count().' contacts without a contact_key';
if ($contacts->count() > 0) { if ($contacts->count() > 0) {
$this->isValid = false; $this->is_valid = false;
} }
if ($this->option('fix') == 'true') { // if ($this->option('fix') == 'true') {
foreach ($contacts as $contact) { // foreach ($contacts as $contact) {
DB::table('client_contacts') // DB::table('client_contacts')
->where('company_id', $this->company->id) // ->where('company_id', $this->company->id)
->where('id', '=', $contact->id) // ->where('id', '=', $contact->id)
->whereNull('contact_key') // ->whereNull('contact_key')
->update([ // ->update([
'contact_key' => str_random(config('ninja.key_length')), // 'contact_key' => str_random(config('ninja.key_length')),
]); // ]);
} // }
} // }
// check for missing contacts // check for missing contacts
$clients = DB::table('clients') $clients = DB::table('clients')
->where('company_id', $this->company->id) ->where('clients.company_id', $this->company->id)
->leftJoin('client_contacts', function ($join) { ->leftJoin('client_contacts', function ($join) {
$join->on('client_contacts.client_id', '=', 'clients.id') $join->on('client_contacts.client_id', '=', 'clients.id')
->whereNull('client_contacts.deleted_at'); ->whereNull('client_contacts.deleted_at');
@ -242,34 +254,34 @@ class CheckCompanyData implements ShouldQueue
->groupBy('clients.id', 'clients.user_id', 'clients.company_id') ->groupBy('clients.id', 'clients.user_id', 'clients.company_id')
->havingRaw('count(client_contacts.id) = 0'); ->havingRaw('count(client_contacts.id) = 0');
if ($this->option('client_id')) { // if ($this->option('client_id')) {
$clients->where('clients.id', '=', $this->option('client_id')); // $clients->where('clients.id', '=', $this->option('client_id'));
} // }
$clients = $clients->get(['clients.id', 'clients.user_id', 'clients.company_id']); $clients = $clients->get(['clients.id', 'clients.user_id', 'clients.company_id']);
$this->company_data[] = $clients->count().' clients without any contacts'; $this->company_data[] = $clients->count().' clients without any contacts';
if ($clients->count() > 0) { if ($clients->count() > 0) {
$this->isValid = false; $this->is_valid = false;
} }
if ($this->option('fix') == 'true') { // if ($this->option('fix') == 'true') {
foreach ($clients as $client) { // foreach ($clients as $client) {
$contact = new ClientContact(); // $contact = new ClientContact();
$contact->company_id = $client->company_id; // $contact->company_id = $client->company_id;
$contact->user_id = $client->user_id; // $contact->user_id = $client->user_id;
$contact->client_id = $client->id; // $contact->client_id = $client->id;
$contact->is_primary = true; // $contact->is_primary = true;
$contact->send_invoice = true; // $contact->send_invoice = true;
$contact->contact_key = str_random(config('ninja.key_length')); // $contact->contact_key = str_random(config('ninja.key_length'));
$contact->save(); // $contact->save();
} // }
} // }
// check for more than one primary contact // check for more than one primary contact
$clients = DB::table('clients') $clients = DB::table('clients')
->where('company_id', $this->company->id) ->where('clients.company_id', $this->company->id)
->leftJoin('client_contacts', function ($join) { ->leftJoin('client_contacts', function ($join) {
$join->on('client_contacts.client_id', '=', 'clients.id') $join->on('client_contacts.client_id', '=', 'clients.id')
->where('client_contacts.is_primary', '=', true) ->where('client_contacts.is_primary', '=', true)
@ -278,15 +290,15 @@ class CheckCompanyData implements ShouldQueue
->groupBy('clients.id') ->groupBy('clients.id')
->havingRaw('count(client_contacts.id) != 1'); ->havingRaw('count(client_contacts.id) != 1');
if ($this->option('client_id')) { // if ($this->option('client_id')) {
$clients->where('clients.id', '=', $this->option('client_id')); // $clients->where('clients.id', '=', $this->option('client_id'));
} // }
$clients = $clients->get(['clients.id', DB::raw('count(client_contacts.id)')]); $clients = $clients->get(['clients.id', DB::raw('count(client_contacts.id)')]);
$this->company_data[] = $clients->count().' clients without a single primary contact'; $this->company_data[] = $clients->count().' clients without a single primary contact';
if ($clients->count() > 0) { if ($clients->count() > 0) {
$this->isValid = false; $this->is_valid = false;
} }
} }
@ -325,11 +337,20 @@ class CheckCompanyData implements ShouldQueue
->get(["{$table}.id"]); ->get(["{$table}.id"]);
if ($records->count()) { if ($records->count()) {
$this->isValid = false; $this->is_valid = false;
$this->company_data[] = $records->count()." {$table} records with incorrect {$entityType} company id"; $this->company_data[] = $records->count()." {$table} records with incorrect {$entityType} company id";
} }
} }
} }
} }
public function pluralizeEntityType($type)
{
if ($type === 'company') {
return 'companies';
}
return $type.'s';
}
} }

View File

@ -14,6 +14,7 @@ namespace App\Jobs\Util;
use App\DataMapper\Analytics\MigrationFailure; use App\DataMapper\Analytics\MigrationFailure;
use App\DataMapper\CompanySettings; use App\DataMapper\CompanySettings;
use App\Exceptions\MigrationValidatorFailed; use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\ProcessingMigrationArchiveFailed;
use App\Exceptions\ResourceDependencyMissing; use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ResourceNotAvailableForMigration; use App\Exceptions\ResourceNotAvailableForMigration;
use App\Factory\ClientFactory; use App\Factory\ClientFactory;
@ -31,7 +32,9 @@ use App\Http\Requests\Company\UpdateCompanyRequest;
use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule; use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule;
use App\Http\ValidationRules\ValidUserForCompany; use App\Http\ValidationRules\ValidUserForCompany;
use App\Jobs\Company\CreateCompanyToken; use App\Jobs\Company\CreateCompanyToken;
use App\Jobs\Ninja\CheckCompanyData;
use App\Jobs\Ninja\CompanySizeCheck; use App\Jobs\Ninja\CompanySizeCheck;
use App\Jobs\Util\VersionCheck;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\MigrationCompleted; use App\Mail\MigrationCompleted;
use App\Models\Activity; use App\Models\Activity;
@ -79,10 +82,10 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Turbo124\Beacon\Facades\LightLogs; use Turbo124\Beacon\Facades\LightLogs;
use Illuminate\Support\Facades\Mail;
class Import implements ShouldQueue class Import implements ShouldQueue
{ {
@ -206,9 +209,13 @@ class Import implements ShouldQueue
$this->setInitialCompanyLedgerBalances(); $this->setInitialCompanyLedgerBalances();
// $this->fixClientBalances(); // $this->fixClientBalances();
$check_data = CheckCompanyData::dispatchNow($this->company, md5(time()));
// if($check_data['status'] == 'errors')
// throw new ProcessingMigrationArchiveFailed(implode("\n", $check_data));
Mail::to($this->user) Mail::to($this->user)
->send(new MigrationCompleted($this->company)); ->send(new MigrationCompleted($this->company, implode("<br>",$check_data)));
/*After a migration first some basic jobs to ensure the system is up to date*/ /*After a migration first some basic jobs to ensure the system is up to date*/
VersionCheck::dispatch(); VersionCheck::dispatch();
@ -220,15 +227,22 @@ class Import implements ShouldQueue
private function setInitialCompanyLedgerBalances() private function setInitialCompanyLedgerBalances()
{ {
Client::cursor()->each(function ($client) { Client::cursor()->each(function ($client) {
$invoice_balances = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
$company_ledger = CompanyLedgerFactory::create($client->company_id, $client->user_id); $company_ledger = CompanyLedgerFactory::create($client->company_id, $client->user_id);
$company_ledger->client_id = $client->id; $company_ledger->client_id = $client->id;
$company_ledger->adjustment = $client->balance; $company_ledger->adjustment = $invoice_balances;
$company_ledger->notes = 'Migrated Client Balance'; $company_ledger->notes = 'Migrated Client Balance';
$company_ledger->balance = $client->balance; $company_ledger->balance = $invoice_balances;
$company_ledger->activity_id = Activity::CREATE_CLIENT; $company_ledger->activity_id = Activity::CREATE_CLIENT;
$company_ledger->save(); $company_ledger->save();
$client->company_ledger()->save($company_ledger); $client->company_ledger()->save($company_ledger);
$client->balance = $invoice_balances;
$client->save();
}); });
} }
@ -1023,10 +1037,36 @@ class Import implements ShouldQueue
} }
if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && array_key_exists('invoices', $this->ids)) { if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && array_key_exists('invoices', $this->ids)) {
$invoice_id = $this->transformId('invoices', $resource['invoice_id']);
$entity = Invoice::where('id', $invoice_id)->withTrashed()->first(); $try_quote = false;
$exception = false;
try{
$invoice_id = $this->transformId('invoices', $resource['invoice_id']);
$entity = Invoice::where('id', $invoice_id)->withTrashed()->first();
}
catch(\Exception $e){
nlog("i couldn't find the invoice document {$resource['invoice_id']}, perhaps it is a quote?");
nlog($e->getMessage());
$try_quote = true;
}
if($try_quote && array_key_exists('quotes', $this->ids) ) {
$quote_id = $this->transformId('quotes', $resource['invoice_id']);
$entity = Quote::where('id', $quote_id)->withTrashed()->first();
$exception = $e;
}
if(!$entity)
throw new Exception("Resource invoice/quote document not available.");
} }
if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && array_key_exists('expenses', $this->ids)) { if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && array_key_exists('expenses', $this->ids)) {
$expense_id = $this->transformId('expenses', $resource['expense_id']); $expense_id = $this->transformId('expenses', $resource['expense_id']);
$entity = Expense::where('id', $expense_id)->withTrashed()->first(); $entity = Expense::where('id', $expense_id)->withTrashed()->first();

View File

@ -108,15 +108,16 @@ class StartMigration implements ShouldQueue
throw new NonExistingMigrationFile('Migration file does not exist, or it is corrupted.'); throw new NonExistingMigrationFile('Migration file does not exist, or it is corrupted.');
} }
//$data = json_decode(file_get_contents($file), 1);
//Import::dispatchNow($data['data'], $this->company, $this->user);
Import::dispatchNow($file, $this->company, $this->user); Import::dispatchNow($file, $this->company, $this->user);
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) { } catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage())); Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));
if (app()->environment() !== 'production') { if (app()->environment() !== 'production') {
info($e->getMessage()); info($e->getMessage());
} }
} }
//always make sure we unset the migration as running //always make sure we unset the migration as running

View File

@ -78,7 +78,7 @@ class UploadFile implements ShouldQueue
$instance = Storage::disk($this->disk)->putFileAs( $instance = Storage::disk($this->disk)->putFileAs(
$path, $path,
$this->file, $this->file,
$this->file->hashName() $this->file->hashName()
); );
if (in_array($this->file->extension(), ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'psd'])) { if (in_array($this->file->extension(), ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tiff', 'psd'])) {

View File

@ -142,6 +142,31 @@ class MultiDB
return null; return null;
} }
/**
* @param array $data
* @return User|null
*/
public static function hasContact(array $data) : ?ClientContact
{
if (! config('ninja.db.multi_db_enabled')) {
return ClientContact::where($data)->withTrashed()->first();
}
foreach (self::$dbs as $db) {
self::setDB($db);
$user = ClientContacts::where($data)->withTrashed()->first();
if ($user) {
return $user;
}
}
self::setDefaultDatabase();
return null;
}
public static function contactFindAndSetDb($token) :bool public static function contactFindAndSetDb($token) :bool
{ {
foreach (self::$dbs as $db) { foreach (self::$dbs as $db) {
@ -160,7 +185,7 @@ class MultiDB
public static function userFindAndSetDb($email) : bool public static function userFindAndSetDb($email) : bool
{ {
//multi-db active //multi-db active
foreach (self::$dbs as $db) { foreach (self::$dbs as $db) {
if (User::on($db)->where(['email' => $email])->get()->count() >= 1) { // if user already exists, validation will fail if (User::on($db)->where(['email' => $email])->get()->count() >= 1) { // if user already exists, validation will fail
return true; return true;

View File

@ -11,8 +11,11 @@
namespace App\Listeners\Credit; namespace App\Listeners\Credit;
use App\Jobs\Mail\EntitySentMailer; use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntitySentObject;
use App\Notifications\Admin\EntitySentNotification; use App\Notifications\Admin\EntitySentNotification;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -41,6 +44,11 @@ class CreditEmailedNotification implements ShouldQueue
$credit->last_sent_date = now(); $credit->last_sent_date = now();
$credit->save(); $credit->save();
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new EntitySentObject($event->invitation, 'credit', $event->template))->build() );
$nmo->company = $credit->company;
$nmo->settings = $credit->company->settings;
foreach ($event->invitation->company->company_users as $company_user) { foreach ($event->invitation->company->company_users as $company_user) {
$user = $company_user->user; $user = $company_user->user;
@ -51,7 +59,10 @@ class CreditEmailedNotification implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) { if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) {
unset($methods[$key]); unset($methods[$key]);
EntitySentMailer::dispatch($event->invitation, 'credit', $user, $event->invitation->company, $event->template); $nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
$first_notification_sent = false; $first_notification_sent = false;
} }

View File

@ -11,8 +11,11 @@
namespace App\Listeners\Invoice; namespace App\Listeners\Invoice;
use App\Jobs\Mail\EntitySentMailer; use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntitySentObject;
use App\Notifications\Admin\EntitySentNotification; use App\Notifications\Admin\EntitySentNotification;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -41,6 +44,12 @@ class InvoiceEmailedNotification implements ShouldQueue
$invoice->last_sent_date = now(); $invoice->last_sent_date = now();
$invoice->save(); $invoice->save();
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new EntitySentObject($event->invitation, 'invoice', $event->template))->build() );
$nmo->company = $invoice->company;
$nmo->settings = $invoice->company->settings;
/* We loop through each user and determine whether they need to be notified */ /* We loop through each user and determine whether they need to be notified */
foreach ($event->invitation->company->company_users as $company_user) { foreach ($event->invitation->company->company_users as $company_user) {
@ -57,8 +66,11 @@ class InvoiceEmailedNotification implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) { if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) {
unset($methods[$key]); unset($methods[$key]);
EntitySentMailer::dispatch($event->invitation, 'invoice', $user, $event->invitation->company, $event->template);
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
/* This prevents more than one notification being sent */ /* This prevents more than one notification being sent */
$first_notification_sent = false; $first_notification_sent = false;
} }

View File

@ -11,9 +11,11 @@
namespace App\Listeners\Invoice; namespace App\Listeners\Invoice;
use App\Jobs\Mail\EntityFailedSendMailer; use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\EntitySentMailer; use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntityFailedSendObject;
use App\Notifications\Admin\EntitySentNotification; use App\Notifications\Admin\EntitySentNotification;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -44,6 +46,11 @@ class InvoiceFailedEmailNotification implements ShouldQueue
$invoice->last_sent_date = now(); $invoice->last_sent_date = now();
$invoice->save(); $invoice->save();
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new EntityFailedSendObject($event->invitation, 'invoice', $event->template, $event->message))->build() );
$nmo->company = $invoice->company;
$nmo->settings = $invoice->company->settings;
foreach ($event->invitation->company->company_users as $company_user) { foreach ($event->invitation->company->company_users as $company_user) {
$user = $company_user->user; $user = $company_user->user;
@ -54,7 +61,10 @@ class InvoiceFailedEmailNotification implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) { if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) {
unset($methods[$key]); unset($methods[$key]);
EntityFailedSendMailer::dispatch($event->invitation, 'invoice', $user, $event->invitation->company, $event->template, $event->message); $nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
$first_notification_sent = false; $first_notification_sent = false;
} }

View File

@ -11,8 +11,11 @@
namespace App\Listeners\Misc; namespace App\Listeners\Misc;
use App\Jobs\Mail\EntityViewedMailer; use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntityViewedObject;
use App\Notifications\Admin\EntityViewedNotification; use App\Notifications\Admin\EntityViewedNotification;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -46,6 +49,12 @@ class InvitationViewedListener implements ShouldQueue
$notification = new EntityViewedNotification($invitation, $entity_name); $notification = new EntityViewedNotification($invitation, $entity_name);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new EntityViewedObject($invitation, $entity_name))->build() );
$nmo->company = $invitation->company;
$nmo->settings = $invitation->company->settings;
foreach ($invitation->company->company_users as $company_user) { foreach ($invitation->company->company_users as $company_user) {
$entity_viewed = "{$entity_name}_viewed"; $entity_viewed = "{$entity_name}_viewed";
@ -54,7 +63,9 @@ class InvitationViewedListener implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false) { if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]); unset($methods[$key]);
EntityViewedMailer::dispatch($invitation, $entity_name, $company_user->user, $invitation->company); $nmo->to_user = $company_user->user;
NinjaMailerJob::dispatch($nmo);
} }
$notification->method = $methods; $notification->method = $methods;
@ -65,8 +76,8 @@ class InvitationViewedListener implements ShouldQueue
if (isset($invitation->company->slack_webhook_url)) { if (isset($invitation->company->slack_webhook_url)) {
$notification->method = ['slack']; $notification->method = ['slack'];
Notification::route('slack', $invitation->company->slack_webhook_url) // Notification::route('slack', $invitation->company->slack_webhook_url)
->notify($notification); // ->notify($notification);
} }
} }
} }

View File

@ -11,8 +11,11 @@
namespace App\Listeners\Payment; namespace App\Listeners\Payment;
use App\Jobs\Mail\EntityPaidMailer; use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntityPaidObject;
use App\Notifications\Admin\NewPaymentNotification; use App\Notifications\Admin\NewPaymentNotification;
use App\Utils\Ninja; use App\Utils\Ninja;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
@ -41,12 +44,16 @@ class PaymentNotification implements ShouldQueue
public function handle($event) public function handle($event)
{ {
MultiDB::setDb($event->company->db); MultiDB::setDb($event->company->db);
if ($event->company->is_disabled)
return true;
$payment = $event->payment; $payment = $event->payment;
if ($event->company->is_disabled) { $nmo = new NinjaMailerObject;
return true; $nmo->mailable = new NinjaMailer( (new EntityPaidObject($payment))->build() );
} $nmo->company = $event->company;
$nmo->settings = $event->company->settings;
/*User notifications*/ /*User notifications*/
foreach ($payment->company->company_users as $company_user) { foreach ($payment->company->company_users as $company_user) {
@ -57,7 +64,9 @@ class PaymentNotification implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false) { if (($key = array_search('mail', $methods)) !== false) {
unset($methods[$key]); unset($methods[$key]);
EntityPaidMailer::dispatch($payment, $payment->company, $user); $nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
} }
$notification = new NewPaymentNotification($payment, $payment->company); $notification = new NewPaymentNotification($payment, $payment->company);

View File

@ -11,8 +11,11 @@
namespace App\Listeners\Quote; namespace App\Listeners\Quote;
use App\Jobs\Mail\EntitySentMailer; use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\EntitySentObject;
use App\Notifications\Admin\EntitySentNotification; use App\Notifications\Admin\EntitySentNotification;
use App\Utils\Traits\Notifications\UserNotifies; use App\Utils\Traits\Notifications\UserNotifies;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
@ -41,6 +44,12 @@ class QuoteEmailedNotification implements ShouldQueue
$quote->last_sent_date = now(); $quote->last_sent_date = now();
$quote->save(); $quote->save();
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new EntitySentObject($event->invitation, 'quote', $event->template))->build() );
$nmo->company = $quote->company;
$nmo->settings = $quote->company->settings;
foreach ($event->invitation->company->company_users as $company_user) { foreach ($event->invitation->company->company_users as $company_user) {
$user = $company_user->user; $user = $company_user->user;
@ -51,7 +60,11 @@ class QuoteEmailedNotification implements ShouldQueue
if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) { if (($key = array_search('mail', $methods)) !== false && $first_notification_sent === true) {
unset($methods[$key]); unset($methods[$key]);
EntitySentMailer::dispatch($event->invitation, 'quote', $user, $event->invitation->company, $event->template);
$nmo->to_user = $user;
NinjaMailerJob::dispatch($nmo);
$first_notification_sent = false; $first_notification_sent = false;
} }

View File

@ -11,7 +11,11 @@
namespace App\Listeners; namespace App\Listeners;
use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Libraries\MultiDB; use App\Libraries\MultiDB;
use App\Mail\Admin\VerifyUserObject;
use App\Notifications\Ninja\VerifyUser; use App\Notifications\Ninja\VerifyUser;
use App\Utils\Ninja; use App\Utils\Ninja;
use Exception; use Exception;
@ -45,9 +49,19 @@ class SendVerificationNotification implements ShouldQueue
MultiDB::setDB($event->company->db); MultiDB::setDB($event->company->db);
try { try {
$event->user->notify(new VerifyUser($event->user, $event->company));
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new VerifyUserObject($event->user, $event->company))->build());
$nmo->company = $event->company;
$nmo->to_user = $event->user;
$nmo->settings = $event->company->settings;
NinjaMailerJob::dispatch($nmo);
// $event->user->notify(new VerifyUser($event->user, $event->company));
Ninja::registerNinjaUser($event->user); Ninja::registerNinjaUser($event->user);
} catch (Exception $e) { } catch (Exception $e) {
nlog("I couldn't send the email " . $e->getMessage()); nlog("I couldn't send the email " . $e->getMessage());
} }

View File

@ -0,0 +1,52 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Mail\Admin;
class AccountCreatedObject
{
public $user;
public $company;
/**
*
*/
public function __construct($user, $company)
{
$this->user = $user;
$this->company = $company;
}
public function build()
{
$data = [
'title' => ctrans('texts.new_signup'),
'message' => ctrans('texts.new_signup_text', ['user' => $this->user->present()->name(), 'email' => $this->user->email, 'ip' => $this->user->ip]),
'url' => config('ninja.web_url'),
'button' => ctrans('texts.account_login'),
'signature' => $this->company->settings->email_signature,
'settings' => $this->company->settings,
'logo' => $this->company->present()->logo(),
];
$mail_obj = new \stdClass;
$mail_obj->subject = ctrans('texts.new_signup');
$mail_obj->data = $data;
$mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key;
return $mail_obj;
}
}

View File

@ -0,0 +1,53 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Mail\Admin;
class ResetPasswordObject
{
public $user;
public $token;
public $company;
/**
*
*/
public function __construct($token, $user, $company)
{
$this->token = $token;
$this->user = $user;
$this->company = $company;
}
public function build()
{
$data = [
'title' => ctrans('texts.your_password_reset_link'),
'message' => ctrans('texts.reset_password'),
'url' => route('password.reset', ['token' => $this->token, 'email' => $this->user->email]),
'button' => ctrans('texts.reset'),
'signature' => $this->company->settings->email_signature,
'settings' => $this->company->settings,
'logo' => $this->company->present()->logo(),
];
$mail_obj = new \stdClass;
$mail_obj->subject = ctrans('texts.your_password_reset_link');
$mail_obj->data = $data;
$mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key;
return $mail_obj;
}
}

View File

@ -0,0 +1,58 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Mail\Admin;
use App\Utils\Traits\MakesHash;
class VerifyUserObject
{
use MakesHash;
public $user;
public $company;
/**
*
*/
public function __construct($user, $company)
{
$this->user = $user;
$this->company = $company;
}
public function build()
{
$this->user->confirmation_code = $this->createDbHash($this->company->db);
$this->user->save();
$data = [
'title' => ctrans('texts.confirmation_subject'),
'message' => ctrans('texts.confirmation_message'),
'url' => url("/user/confirm/{$this->user->confirmation_code}"),
'button' => ctrans('texts.button_confirmation_message'),
'settings' => $this->company->settings,
'logo' => $this->company->present()->logo(),
'signature' => $this->company->settings->email_signature,
];
$mail_obj = new \stdClass;
$mail_obj->subject = ctrans('texts.confirmation_subject');
$mail_obj->data = $data;
$mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key;
return $mail_obj;
}
}

View File

@ -17,9 +17,10 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class BouncedEmail extends Mailable implements ShouldQueue // class BouncedEmail extends Mailable implements ShouldQueue
class BouncedEmail extends Mailable
{ {
use Queueable, SerializesModels; //use Queueable, SerializesModels;
public $invitation; public $invitation;

View File

@ -0,0 +1,54 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Mail\ClientContact;
class ClientContactResetPasswordObject
{
public $client_contact;
public $token;
private $company;
/**
*
*/
public function __construct($token, $client_contact)
{
$this->token = $token;
$this->client_contact = $client_contact;
$this->company = $client_contact->company;
}
public function build()
{
$data = [
'title' => ctrans('texts.your_password_reset_link'),
'message' => ctrans('texts.reset_password'),
'url' => route('client.password.reset', ['token' => $this->token, 'email' => $this->client_contact->email]),
'button' => ctrans('texts.reset'),
'signature' => $this->company->settings->email_signature,
'settings' => $this->company->settings,
'logo' => $this->company->present()->logo(),
];
$mail_obj = new \stdClass;
$mail_obj->subject = ctrans('texts.your_password_reset_link');
$mail_obj->data = $data;
$mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key;
return $mail_obj;
}
}

View File

@ -9,7 +9,7 @@ use Illuminate\Queue\SerializesModels;
class DownloadInvoices extends Mailable class DownloadInvoices extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
public $file_path; public $file_path;

View File

@ -8,7 +8,7 @@ use Illuminate\Queue\SerializesModels;
class ExistingMigration extends Mailable class ExistingMigration extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
/** /**
* Create a new message instance. * Create a new message instance.

View File

@ -8,7 +8,7 @@ use Illuminate\Queue\SerializesModels;
class ImportCompleted extends Mailable class ImportCompleted extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
/** /**
* Create a new message instance. * Create a new message instance.

View File

@ -8,7 +8,7 @@ use Illuminate\Queue\SerializesModels;
class InvoiceWasPaid extends Mailable class InvoiceWasPaid extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
/** /**
* Create a new message instance. * Create a new message instance.

View File

@ -9,18 +9,21 @@ use Illuminate\Queue\SerializesModels;
class MigrationCompleted extends Mailable class MigrationCompleted extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
public $company; public $company;
public $check_data;
/** /**
* Create a new message instance. * Create a new message instance.
* *
* @return void * @return void
*/ */
public function __construct(Company $company) public function __construct(Company $company, $check_data = '')
{ {
$this->company = $company; $this->company = $company;
$this->check_data = $check_data;
} }
/** /**
@ -31,9 +34,10 @@ class MigrationCompleted extends Mailable
public function build() public function build()
{ {
$data['settings'] = $this->company->settings; $data['settings'] = $this->company->settings;
$data['company'] = $this->company; $data['company'] = $this->company->fresh();
$data['whitelabel'] = $this->company->account->isPaid() ? true : false; $data['whitelabel'] = $this->company->account->isPaid() ? true : false;
$data['check_data'] = $this->check_data;
$result = $this->from(config('mail.from.address'), config('mail.from.name')) $result = $this->from(config('mail.from.address'), config('mail.from.name'))
->view('email.import.completed', $data); ->view('email.import.completed', $data);

View File

@ -8,7 +8,7 @@ use Illuminate\Queue\SerializesModels;
class MigrationFailed extends Mailable class MigrationFailed extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
public $exception; public $exception;
public $content; public $content;
@ -22,6 +22,7 @@ class MigrationFailed extends Mailable
public function __construct($exception, $content = null) public function __construct($exception, $content = null)
{ {
$this->exception = $exception; $this->exception = $exception;
$this->content = $content;
} }
/** /**

View File

@ -8,7 +8,7 @@ use Illuminate\Queue\SerializesModels;
class QuoteWasApproved extends Mailable class QuoteWasApproved extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
/** /**
* Create a new message instance. * Create a new message instance.

View File

@ -0,0 +1,55 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Mail\RecurringInvoice;
class ClientContactRequestCancellationObject
{
public $recurring_invoice;
public $client_contact;
private $company;
/**
*
*/
public function __construct($recurring_invoice, $client_contact)
{
$this->recurring_invoice = $recurring_invoice;
$this->client_contact = $client_contact;
$this->company = $recurring_invoice->company;
}
public function build()
{
$data = [
'title' => ctrans('texts.recurring_cancellation_request', ['contact' => $this->client_contact->present()->name()]),
'message' => ctrans('texts.recurring_cancellation_request_body', ['contact' => $this->client_contact->present()->name(), 'client' => $this->client_contact->client->present()->name(), 'invoice' => $this->recurring_invoice->number]),
'url' => config('ninja.web_url'),
'button' => ctrans('texts.account_login'),
'signature' => $this->company->settings->email_signature,
'settings' => $this->company->settings,
'logo' => $this->company->present()->logo(),
];
$mail_obj = new \stdClass;
$mail_obj->subject = ctrans('texts.recurring_cancellation_request', ['contact' => $this->client_contact->present()->name()]);
$mail_obj->data = $data;
$mail_obj->markdown = 'email.admin.generic';
$mail_obj->tag = $this->company->company_key;
return $mail_obj;
}
}

View File

@ -11,7 +11,7 @@ use SplFileObject;
class SupportMessageSent extends Mailable class SupportMessageSent extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
public $message; public $message;

View File

@ -20,7 +20,6 @@ use Illuminate\Queue\SerializesModels;
class TemplateEmail extends Mailable class TemplateEmail extends Mailable
{ {
use Queueable, SerializesModels;
private $build_email; private $build_email;
@ -37,12 +36,6 @@ class TemplateEmail extends Mailable
$this->client = $contact->client; $this->client = $contact->client;
} }
/**
* Build the message.
*
* @return $this
* @throws \Laracasts\Presenter\Exceptions\PresenterException
*/
public function build() public function build()
{ {
$template_name = 'email.template.'.$this->build_email->getTemplate(); $template_name = 'email.template.'.$this->build_email->getTemplate();
@ -79,7 +72,10 @@ class TemplateEmail extends Mailable
'settings' => $settings, 'settings' => $settings,
'company' => $company, 'company' => $company,
'whitelabel' => $this->client->user->account->isPaid() ? true : false, 'whitelabel' => $this->client->user->account->isPaid() ? true : false,
]); ])
->withSwiftMessage(function ($message) use($company){
$message->getHeaders()->addTextHeader('Tag', $company->company_key);
});
//conditionally attach files //conditionally attach files
if ($settings->pdf_email_attachment !== false && ! empty($this->build_email->getAttachments())) { if ($settings->pdf_email_attachment !== false && ! empty($this->build_email->getAttachments())) {

View File

@ -17,7 +17,7 @@ use Illuminate\Queue\SerializesModels;
class TestMailServer extends Mailable class TestMailServer extends Mailable
{ {
use Queueable, SerializesModels; // use Queueable, SerializesModels;
public $message; public $message;

View File

@ -11,6 +11,10 @@
namespace App\Models; namespace App\Models;
use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\ClientContact\ClientContactResetPasswordObject;
use App\Models\Presenters\ClientContactPresenter; use App\Models\Presenters\ClientContactPresenter;
use App\Notifications\ClientContactResetPassword; use App\Notifications\ClientContactResetPassword;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -151,7 +155,15 @@ class ClientContact extends Authenticatable implements HasLocalePreference
public function sendPasswordResetNotification($token) public function sendPasswordResetNotification($token)
{ {
$this->notify(new ClientContactResetPassword($token)); $nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer((new ClientContactResetPasswordObject($token, $this))->build());
$nmo->to_user = $this;
$nmo->company = $this->company;
$nmo->settings = $this->company->settings;
NinjaMailerJob::dispatch($nmo);
//$this->notify(new ClientContactResetPassword($token));
} }
public function preferredLocale() public function preferredLocale()

View File

@ -83,6 +83,8 @@ class Company extends BaseModel
'default_task_is_date_based', 'default_task_is_date_based',
'enable_product_discount', 'enable_product_discount',
'expense_inclusive_taxes', 'expense_inclusive_taxes',
'session_timeout',
'oauth_password_required',
]; ];
protected $hidden = [ protected $hidden = [

View File

@ -67,6 +67,10 @@ class Project extends BaseModel
return $this->morphMany(Document::class, 'documentable'); return $this->morphMany(Document::class, 'documentable');
} }
public function user()
{
return $this->belongsTo(User::class)->withTrashed();
}
// /** // /**
// * @return \Illuminate\Database\Eloquent\Relations\HasMany // * @return \Illuminate\Database\Eloquent\Relations\HasMany
// */ // */

View File

@ -11,6 +11,10 @@
namespace App\Models; namespace App\Models;
use App\Jobs\Mail\NinjaMailer;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\Admin\ResetPasswordObject;
use App\Models\Presenters\UserPresenter; use App\Models\Presenters\UserPresenter;
use App\Notifications\ResetPasswordNotification; use App\Notifications\ResetPasswordNotification;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
@ -371,6 +375,15 @@ class User extends Authenticatable implements MustVerifyEmail
*/ */
public function sendPasswordResetNotification($token) public function sendPasswordResetNotification($token)
{ {
$this->notify(new ResetPasswordNotification($token));
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new ResetPasswordObject($token, $this, $this->account->default_company))->build());
$nmo->to_user = $this;
$nmo->settings = $this->account->default_company->settings;
$nmo->company = $this->account->default_company;
NinjaMailerJob::dispatch($nmo);
//$this->notify(new ResetPasswordNotification($token));
} }
} }

View File

@ -99,4 +99,9 @@ class Vendor extends BaseModel
{ {
return $this->belongsTo(Company::class); return $this->belongsTo(Company::class);
} }
public function user()
{
return $this->belongsTo(User::class)->withTrashed();
}
} }

View File

@ -21,9 +21,10 @@ use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
//@deprecated
class EntitySentNotification extends Notification implements ShouldQueue class EntitySentNotification extends Notification implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; //use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -77,39 +78,6 @@ class EntitySentNotification extends Notification implements ShouldQueue
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
$amount = Number::formatMoney($this->entity->amount, $this->entity->client);
$subject = ctrans(
"texts.notification_{$this->entity_name}_sent_subject",
[
'client' => $this->contact->present()->name(),
'invoice' => $this->entity->number,
]
);
$data = [
'title' => $subject,
'message' => ctrans(
"texts.notification_{$this->entity_name}_sent",
[
'amount' => $amount,
'client' => $this->contact->present()->name(),
'invoice' => $this->entity->number,
]
),
'url' => $this->invitation->getAdminLink(),
'button' => ctrans("texts.view_{$this->entity_name}"),
'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->settings,
];
return (new MailMessage)
->subject($subject)
->markdown('email.admin.generic', $data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
});
} }
/** /**
@ -120,9 +88,7 @@ class EntitySentNotification extends Notification implements ShouldQueue
*/ */
public function toArray($notifiable) public function toArray($notifiable)
{ {
return [ return [];
//
];
} }
public function toSlack($notifiable) public function toSlack($notifiable)

View File

@ -23,7 +23,7 @@ use Illuminate\Queue\SerializesModels;
class EntityViewedNotification extends Notification implements ShouldQueue class EntityViewedNotification extends Notification implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; //use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -76,17 +76,6 @@ class EntityViewedNotification extends Notification implements ShouldQueue
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
$data = $this->buildDataArray();
$subject = $this->buildSubject();
return (new MailMessage)
->subject($subject)
->markdown('email.admin.generic', $data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
});
} }
/** /**

View File

@ -1,149 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Notifications\Admin;
use App\Utils\Number;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class InvoiceSentNotification extends Notification implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new notification instance.
*
* @return void
*/
protected $invitation;
protected $invoice;
protected $company;
protected $settings;
public $is_system;
protected $contact;
public function __construct($invitation, $company, $is_system = false, $settings = null)
{
$this->invitation = $invitation;
$this->invoice = $invitation->invoice;
$this->contact = $invitation->contact;
$this->company = $company;
$this->settings = $this->invoice->client->getMergedSettings();
$this->is_system = $is_system;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return $this->is_system ? ['slack'] : ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
$amount = Number::formatMoney($this->invoice->amount, $this->invoice->client);
$subject = ctrans(
'texts.notification_invoice_sent_subject',
[
'client' => $this->contact->present()->name(),
'invoice' => $this->invoice->number,
]
);
$data = [
'title' => $subject,
'message' => ctrans(
'texts.notification_invoice_sent',
[
'amount' => $amount,
'client' => $this->contact->present()->name(),
'invoice' => $this->invoice->number,
]
),
'url' => config('ninja.app_url').'/invoices/'.$this->invoice->hashed_id,
'button' => ctrans('texts.view_invoice'),
'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->settings,
];
return (new MailMessage)
->subject($subject)
->markdown('email.admin.generic', $data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
});
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
public function toSlack($notifiable)
{
$logo = $this->company->present()->logo();
$amount = Number::formatMoney($this->invoice->amount, $this->invoice->client);
return (new SlackMessage)
->from(ctrans('texts.notification_bot'))
->success()
->image('https://app.invoiceninja.com/favicon-v2.png')
->content(trans(
'texts.notification_invoice_sent_subject',
[
'amount' => $amount,
'client' => $this->contact->present()->name(),
'invoice' => $this->invoice->number,
]
))
->attachment(function ($attachment) use ($amount) {
$attachment->title(ctrans('texts.invoice_number_placeholder', ['invoice' => $this->invoice->number]), $this->invitation->getAdminLink())
->fields([
ctrans('texts.client') => $this->contact->present()->name(),
ctrans('texts.amount') => $amount,
]);
});
}
}

View File

@ -1,142 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Notifications\Admin;
use App\Utils\Number;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class InvoiceViewedNotification extends Notification implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new notification instance.
*
* @return void
*/
protected $invitation;
protected $invoice;
protected $company;
protected $settings;
public $is_system;
protected $contact;
public function __construct($invitation, $company, $is_system = false, $settings = null)
{
$this->invoice = $invitation->invoice;
$this->contact = $invitation->contact;
$this->company = $company;
$this->settings = $this->invoice->client->getMergedSettings();
$this->is_system = $is_system;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return $this->is_system ? ['slack'] : ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
$amount = Number::formatMoney($this->invoice->amount, $this->invoice->client);
$subject = ctrans(
'texts.notification_invoice_viewed_subject',
[
'client' => $this->contact->present()->name(),
'invoice' => $this->invoice->number,
]
);
$data = [
'title' => $subject,
'message' => ctrans(
'texts.notification_invoice_viewed',
[
'amount' => $amount,
'client' => $this->contact->present()->name(),
'invoice' => $this->invoice->number,
]
),
'url' => config('ninja.app_url').'/invoices/'.$this->invoice->hashed_id,
'button' => ctrans('texts.view_invoice'),
'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->settings,
];
return (new MailMessage)
->subject($subject)
->markdown('email.admin.generic', $data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
});
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
public function toSlack($notifiable)
{
$logo = $this->company->present()->logo();
$amount = Number::formatMoney($this->invoice->amount, $this->invoice->client);
return (new SlackMessage)
->success()
->from(ctrans('texts.notification_bot'))
->image($logo)
->content(ctrans(
'texts.notification_invoice_viewed',
[
'amount' => $amount,
'client' => $this->contact->present()->name(),
'invoice' => $this->invoice->number,
]
));
}
}

View File

@ -1,148 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Notifications\Admin;
use App\Utils\Number;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class NewPartialPaymentNotification extends Notification implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new notification instance.
*
* @return void
*/
protected $payment;
protected $company;
protected $settings;
protected $is_system;
public function __construct($payment, $company, $is_system = false, $settings = null)
{
$this->payment = $payment;
$this->company = $company;
$this->settings = $payment->client->getMergedSettings();
$this->is_system = $is_system;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return $this->is_system ? ['slack'] : ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
$amount = Number::formatMoney($this->payment->amount, $this->payment->client);
$invoice_texts = ctrans('texts.invoice_number_short');
foreach ($this->payment->invoices as $invoice) {
$invoice_texts .= $invoice->number.',';
}
$invoice_texts = substr($invoice_texts, 0, -1);
$data = [
'title' => ctrans(
'texts.notification_partial_payment_paid_subject',
['client' => $this->payment->client->present()->name()]
),
'message' => ctrans(
'texts.notification_partial_payment_paid',
['amount' => $amount,
'client' => $this->payment->client->present()->name(),
'invoice' => $invoice_texts,
]
),
'url' => config('ninja.app_url').'/payments/'.$this->payment->hashed_id,
'button' => ctrans('texts.view_payment'),
'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->settings,
];
return (new MailMessage)
->subject(
ctrans(
'texts.notification_partial_payment_paid_subject',
['client' => $this->payment->client->present()->name()]
)
)->markdown('email.admin.generic', $data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
});
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
public function toSlack($notifiable)
{
$logo = $this->company->present()->logo();
$amount = Number::formatMoney($this->payment->amount, $this->payment->client);
$invoice_texts = ctrans('texts.invoice_number_short');
foreach ($this->payment->invoices as $invoice) {
$invoice_texts .= $invoice->number.',';
}
$invoice_texts = substr($invoice_texts, 0, -1);
return (new SlackMessage)
->success()
//->to("#devv2")
->from('System')
->image($logo)
->content(ctrans(
'texts.notification_payment_paid',
['amount' => $amount,
'client' => $this->payment->client->present()->name(),
'invoice' => $invoice_texts, ]
));
}
}

View File

@ -23,7 +23,7 @@ use Illuminate\Queue\SerializesModels;
class NewPaymentNotification extends Notification implements ShouldQueue class NewPaymentNotification extends Notification implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -68,47 +68,6 @@ class NewPaymentNotification extends Notification implements ShouldQueue
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
$amount = Number::formatMoney($this->payment->amount, $this->payment->client);
$invoice_texts = ctrans('texts.invoice_number_short');
foreach ($this->payment->invoices as $invoice) {
$invoice_texts .= $invoice->number.',';
}
$invoice_texts = substr($invoice_texts, 0, -1);
$data = [
'title' => ctrans(
'texts.notification_payment_paid_subject',
['client' => $this->payment->client->present()->name()]
),
'message' => ctrans(
'texts.notification_payment_paid',
['amount' => $amount,
'client' => $this->payment->client->present()->name(),
'invoice' => $invoice_texts,
]
),
'url' => config('ninja.app_url').'/payments/'.$this->payment->hashed_id,
'button' => ctrans('texts.view_payment'),
'signature' => $this->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->settings,
];
return (new MailMessage)
->subject(
ctrans(
'texts.notification_payment_paid_subject',
['client' => $this->payment->client->present()->name()]
)
)->markdown('email.admin.generic', $data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
});
} }
/** /**

View File

@ -18,9 +18,9 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
class BaseNotification extends Notification implements ShouldQueue class BaseNotification extends Notification
{ {
use Queueable; // use Queueable;
use MakesInvoiceHtml; use MakesInvoiceHtml;
/** /**

View File

@ -22,9 +22,10 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
class ClientContactRequestCancellation extends Notification implements ShouldQueue //@deprecated for mail
class ClientContactRequestCancellation extends Notification
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -56,7 +57,7 @@ class ClientContactRequestCancellation extends Notification implements ShouldQue
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail', 'slack']; return ['slack'];
} }
/** /**
@ -67,19 +68,6 @@ class ClientContactRequestCancellation extends Notification implements ShouldQue
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $this->client_contact);
}
$client_contact_name = $this->client_contact->present()->name();
$client_name = $this->client_contact->client->present()->name();
$recurring_invoice_number = $this->recurring_invoice->number;
return (new MailMessage)
->subject('Request for recurring invoice cancellation from '.$client_contact_name)
->markdown('email.support.cancellation', [
'message' => "Contact [{$client_contact_name}] from Client [{$client_name}] requested to cancel Recurring Invoice [#{$recurring_invoice_number}]",
]);
} }
/** /**

View File

@ -19,9 +19,10 @@ use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
//@deprecated
class ClientContactResetPassword extends Notification class ClientContactResetPassword extends Notification
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* The password reset token. * The password reset token.
* *
@ -55,7 +56,7 @@ class ClientContactResetPassword extends Notification
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail']; return [];
} }
/** /**
@ -66,16 +67,6 @@ class ClientContactResetPassword extends Notification
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $this->token);
}
return (new MailMessage)
->subject('Reset Password Notification')
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url(config('app.url').route('client.password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')])
->line('If you did not request a password reset, no further action is required.');
} }
/** /**

View File

@ -1,73 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class GmailTestNotification extends Notification implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
}

View File

@ -20,9 +20,10 @@ use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class NewAccountCreated extends Notification implements ShouldQueue //@deprecated
class NewAccountCreated extends Notification
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; //use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -50,7 +51,7 @@ class NewAccountCreated extends Notification implements ShouldQueue
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['slack', 'mail']; return ['slack'];
} }
/** /**
@ -61,26 +62,6 @@ class NewAccountCreated extends Notification implements ShouldQueue
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
$user_name = $this->user->first_name.' '.$this->user->last_name;
$email = $this->user->email;
$ip = $this->user->ip;
$data = [
'title' => ctrans('texts.new_signup'),
'message' => ctrans('texts.new_signup_text', ['user' => $user_name, 'email' => $email, 'ip' => $ip]),
'url' => config('ninja.web_url'),
'button' => ctrans('texts.account_login'),
'signature' => $this->company->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->company->settings,
];
return (new MailMessage)
->subject(ctrans('texts.new_signup'))
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
})
->markdown('email.admin.generic', $data);
} }
/** /**

View File

@ -20,9 +20,11 @@ use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class NewAccountCreated extends Notification implements ShouldQueue
//@deprecated
class NewAccountCreated extends Notification
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -50,7 +52,7 @@ class NewAccountCreated extends Notification implements ShouldQueue
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['slack', 'mail']; return ['slack'];
} }
/** /**
@ -61,26 +63,6 @@ class NewAccountCreated extends Notification implements ShouldQueue
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
$user_name = $this->user->first_name.' '.$this->user->last_name;
$email = $this->user->email;
$ip = $this->user->ip;
$data = [
'title' => ctrans('texts.new_signup'),
'message' => ctrans('texts.new_signup_text', ['user' => $user_name, 'email' => $email, 'ip' => $ip]),
'url' => config('ninja.web_url'),
'button' => ctrans('texts.account_login'),
'signature' => $this->company->settings->email_signature,
'logo' => $this->company->present()->logo(),
'settings' => $this->company->settings,
];
return (new MailMessage)
->subject(ctrans('texts.new_signup'))
->markdown('email.admin.generic', $data)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->company->company_key);
});
} }
/** /**

View File

@ -19,9 +19,10 @@ use Illuminate\Notifications\Notification;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
class VerifyUser extends Notification implements ShouldQueue //@deprecated
class VerifyUser extends Notification
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; // use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/** /**
* Create a new notification instance. * Create a new notification instance.
@ -46,7 +47,7 @@ class VerifyUser extends Notification implements ShouldQueue
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail']; return [''];
} }
/** /**
@ -57,19 +58,6 @@ class VerifyUser extends Notification implements ShouldQueue
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
$data = [
'title' => ctrans('texts.confirmation_subject'),
'message' => ctrans('texts.confirmation_message'),
'url' => url("/user/confirm/{$this->user->confirmation_code}"),
'button' => ctrans('texts.button_confirmation_message'),
'signature' => '',
'logo' => 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png',
'settings' => $this->company->settings,
];
return (new MailMessage)
->subject(ctrans('texts.confirmation_subject'))
->markdown('email.admin.generic', $data);
} }
/** /**

View File

@ -6,9 +6,10 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
//@deprecated
class ResetPasswordNotification extends Notification class ResetPasswordNotification extends Notification
{ {
use Queueable; // use Queueable;
public $token; public $token;
@ -30,7 +31,7 @@ class ResetPasswordNotification extends Notification
*/ */
public function via($notifiable) public function via($notifiable)
{ {
return ['mail']; return [];
} }
/** /**
@ -41,8 +42,6 @@ class ResetPasswordNotification extends Notification
*/ */
public function toMail($notifiable) public function toMail($notifiable)
{ {
return (new MailMessage)
->view('email.auth.password-reset', ['link' => route('password.reset', $this->token)]);
} }
/** /**

View File

@ -1,124 +0,0 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Notifications;
use App\Models\Invoice;
use App\Utils\Number;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Queue\SerializesModels;
class SendGenericNotification extends BaseNotification implements ShouldQueue
{
use Queueable;
use Dispatchable;
use SerializesModels;
/**
* Create a new notification instance.
*
* @return void
*/
protected $invitation;
protected $entity;
protected $contact;
protected $entity_string;
protected $settings;
public $is_system;
protected $body;
protected $subject;
public function __construct($invitation, $entity_string, $subject, $body)
{
$this->entity = $invitation->{$entity_string};
$this->contact = $invitation->contact;
$this->settings = $this->entity->client->getMergedSettings();
$this->subject = $subject;
$this->body = $body;
$this->invitation = $invitation;
$this->entity_string = $entity_string;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return MailMessage
*/
public function toMail($notifiable)
{
$mail_message = (new MailMessage)
->withSwiftMessage(function ($message) {
$message->getHeaders()->addTextHeader('Tag', $this->invitation->company->company_key);
})->markdown($this->getTemplateView(), $this->buildMailMessageData());
//})->markdown('email.template.plain', $this->buildMailMessageData());
$mail_message = $this->buildMailMessageSettings($mail_message);
return $mail_message;
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
public function toSlack($notifiable)
{
return '';
// $logo = $this->company->present()->logo();
// $amount = Number::formatMoney($this->invoice->amount, $this->invoice->client);
// return (new SlackMessage)
// ->success()
// ->from(ctrans('texts.notification_bot'))
// ->image($logo)
// ->content(ctrans(
// 'texts.notification_invoice_viewed',
// [
// 'amount' => $amount,
// 'client' => $this->contact->present()->name(),
// 'invoice' => $this->invoice->number
// ]
// ));
}
}

View File

@ -0,0 +1,75 @@
<?php
/**
* Credit Ninja (https://creditninja.com).
*
* @link https://github.com/creditninja/creditninja source repository
*
* @copyright Copyright (c) 2021. Credit Ninja LLC (https://creditninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Observers;
use App\Jobs\Util\UnlinkFile;
use App\Jobs\Util\WebhookHandler;
use App\Models\Client;
use App\Models\Credit;
use App\Models\Webhook;
class CreditObserver
{
/**
* Handle the client "created" event.
*
* @param Credit $credit
* @return void
*/
public function created(Credit $credit)
{
}
/**
* Handle the client "updated" event.
*
* @param Credit $credit
* @return void
*/
public function updated(Credit $credit)
{
UnlinkFile::dispatchNow(config('filesystems.default'), $credit->client->credit_filepath() . $credit->number.'.pdf');
}
/**
* Handle the client "deleted" event.
*
* @param Credit $credit
* @return void
*/
public function deleted(Credit $credit)
{
}
/**
* Handle the client "restored" event.
*
* @param Credit $credit
* @return void
*/
public function restored(Credit $credit)
{
//
}
/**
* Handle the client "force deleted" event.
*
* @param Credit $credit
* @return void
*/
public function forceDeleted(Credit $credit)
{
//
}
}

Some files were not shown because too many files have changed in this diff Show More