mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
commit
8e50ae9691
@ -1 +1 @@
|
||||
5.8.25
|
||||
5.8.26
|
@ -495,7 +495,10 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $show_pdfhtml_on_mobile = true;
|
||||
|
||||
public $use_unapplied_payment = 'off'; //always, option, off //@implemented
|
||||
|
||||
public static $casts = [
|
||||
'use_unapplied_payment' => 'string',
|
||||
'show_pdfhtml_on_mobile' => 'bool',
|
||||
'payment_email_all_contacts' => 'bool',
|
||||
'statement_design_id' => 'string',
|
||||
|
@ -49,6 +49,14 @@ class CompanyFactory
|
||||
$company->markdown_enabled = false;
|
||||
$company->tax_data = new TaxModel();
|
||||
$company->first_month_of_year = 1;
|
||||
$company->smtp_encryption = 'tls';
|
||||
$company->smtp_host = '';
|
||||
$company->smtp_local_domain = '';
|
||||
$company->smtp_password = '';
|
||||
$company->smtp_port = '';
|
||||
$company->smtp_username = '';
|
||||
$company->smtp_verify_peer = true;
|
||||
|
||||
return $company;
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,9 @@
|
||||
namespace App\Filters;
|
||||
|
||||
use App\Models\Payment;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* PaymentFilters.
|
||||
@ -163,7 +164,7 @@ class PaymentFilters extends QueryFilters
|
||||
{
|
||||
$sort_col = explode('|', $sort);
|
||||
|
||||
if (!is_array($sort_col) || count($sort_col) != 2) {
|
||||
if (!is_array($sort_col) || count($sort_col) != 2 || !in_array($sort_col, Schema::getColumnListing('payments'))) {
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
|
@ -140,6 +140,7 @@ class BaseController extends Controller
|
||||
'company.quotes.invitations.company',
|
||||
'company.quotes.documents',
|
||||
'company.tasks.documents',
|
||||
// 'company.tasks.project',
|
||||
'company.subscriptions',
|
||||
'company.tax_rates',
|
||||
'company.tokens_hashed',
|
||||
@ -458,7 +459,7 @@ class BaseController extends Controller
|
||||
}
|
||||
},
|
||||
'company.tasks' => function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
$query->where('updated_at', '>=', $updated_at)->with('project','documents');
|
||||
|
||||
if (! $user->hasPermission('view_task')) {
|
||||
$query->whereNested(function ($query) use ($user) {
|
||||
@ -796,7 +797,7 @@ class BaseController extends Controller
|
||||
}
|
||||
},
|
||||
'company.tasks' => function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
$query->where('created_at', '>=', $created_at)->with('project.documents','documents');
|
||||
|
||||
if (! $user->hasPermission('view_task')) {
|
||||
$query->whereNested(function ($query) use ($user) {
|
||||
|
@ -169,13 +169,16 @@ class ConnectedAccountController extends BaseController
|
||||
'email_verified_at' => now(),
|
||||
];
|
||||
|
||||
auth()->user()->update($connected_account);
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->save();
|
||||
/** @var \App\Models\User $logged_in_user */
|
||||
$logged_in_user = auth()->user();
|
||||
|
||||
$this->setLoginCache(auth()->user());
|
||||
$logged_in_user->update($connected_account);
|
||||
$logged_in_user->email_verified_at = now();
|
||||
$logged_in_user->save();
|
||||
|
||||
return $this->itemResponse(auth()->user());
|
||||
$this->setLoginCache($logged_in_user);
|
||||
|
||||
return $this->itemResponse($logged_in_user);
|
||||
}
|
||||
|
||||
return response()
|
||||
@ -214,20 +217,22 @@ class ConnectedAccountController extends BaseController
|
||||
// 'email_verified_at' =>now(),
|
||||
];
|
||||
|
||||
if (auth()->user()->email != $google->harvestEmail($user)) {
|
||||
/** @var \App\Models\User $logged_in_user */
|
||||
$logged_in_user = auth()->user();
|
||||
|
||||
if ($logged_in_user->email != $google->harvestEmail($user)) {
|
||||
return response()->json(['message' => 'Primary Email differs to OAuth email. Emails must match.'], 400);
|
||||
}
|
||||
|
||||
auth()->user()->update($connected_account);
|
||||
auth()->user()->email_verified_at = now();
|
||||
auth()->user()->oauth_user_token = $token;
|
||||
auth()->user()->oauth_user_refresh_token = $refresh_token;
|
||||
$logged_in_user->update($connected_account);
|
||||
$logged_in_user->email_verified_at = now();
|
||||
$logged_in_user->oauth_user_token = $token;
|
||||
$logged_in_user->oauth_user_refresh_token = $refresh_token;
|
||||
$logged_in_user->save();
|
||||
|
||||
auth()->user()->save();
|
||||
$this->activateGmail($logged_in_user);
|
||||
|
||||
$this->activateGmail(auth()->user());
|
||||
|
||||
return $this->itemResponse(auth()->user());
|
||||
return $this->itemResponse($logged_in_user);
|
||||
}
|
||||
|
||||
return response()
|
||||
|
64
app/Http/Controllers/SmtpController.php
Normal file
64
app/Http/Controllers/SmtpController.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Smtp\CheckSmtpRequest;
|
||||
use App\Mail\TestMailServer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SmtpController extends BaseController
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function check(CheckSmtpRequest $request)
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
$company = $user->company();
|
||||
|
||||
|
||||
|
||||
config([
|
||||
'mail.mailers.smtp' => [
|
||||
'transport' => 'smtp',
|
||||
'host' => $request->input('smtp_host', $company->smtp_host),
|
||||
'port' => $request->input('smtp_port', $company->smtp_port),
|
||||
'username' => $request->input('smtp_username', $company->smtp_username),
|
||||
'password' => $request->input('smtp_password', $company->smtp_password),
|
||||
'encryption' => $request->input('smtp_encryption', $company->smtp_encryption ?? 'tls'),
|
||||
'local_domain' => $request->input('smtp_local_domain', strlen($company->smtp_local_domain) > 2 ? $company->smtp_local_domain : null),
|
||||
'verify_peer' => $request->input('verify_peer', $company->smtp_verify_peer ?? true),
|
||||
'timeout' => 5,
|
||||
],
|
||||
]);
|
||||
|
||||
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
|
||||
|
||||
try {
|
||||
Mail::to($user->email, $user->present()->name())->send(new TestMailServer('Email Server Works!', strlen($company->settings->custom_sending_email) > 1 ? $company->settings->custom_sending_email : $user->email));
|
||||
} catch (\Exception $e) {
|
||||
app('mail.manager')->forgetMailers();
|
||||
return response()->json(['message' => $e->getMessage()], 400);
|
||||
}
|
||||
|
||||
app('mail.manager')->forgetMailers();
|
||||
|
||||
return response()->json(['message' => 'Ok'], 200);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -49,6 +49,9 @@ class StoreClientRequest extends Request
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}
|
||||
else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
|
@ -53,6 +53,8 @@ class UpdateClientRequest extends Request
|
||||
$rules['file.*'] = $this->file_validation;
|
||||
} elseif ($this->file('file')) {
|
||||
$rules['file'] = $this->file_validation;
|
||||
} else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
$rules['company_logo'] = 'mimes:jpeg,jpg,png,gif|max:10000';
|
||||
|
@ -56,6 +56,15 @@ class StoreCompanyRequest extends Request
|
||||
}
|
||||
}
|
||||
|
||||
$rules['smtp_host'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_port'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_encryption'] = 'sometimes|string';
|
||||
$rules['smtp_local_domain'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_encryption'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_local_domain'] = 'sometimes|string|nullable';
|
||||
|
||||
// $rules['smtp_verify_peer'] = 'sometimes|in:true,false';
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
@ -67,11 +76,11 @@ class StoreCompanyRequest extends Request
|
||||
$input['name'] = 'Untitled Company';
|
||||
}
|
||||
|
||||
if (array_key_exists('google_analytics_url', $input)) {
|
||||
if (isset($input['google_analytics_url'])) {
|
||||
$input['google_analytics_key'] = $input['google_analytics_url'];
|
||||
}
|
||||
|
||||
if (array_key_exists('portal_domain', $input)) {
|
||||
if (isset($input['portal_domain'])) {
|
||||
$input['portal_domain'] = rtrim(strtolower($input['portal_domain']), "/");
|
||||
}
|
||||
|
||||
@ -79,6 +88,17 @@ class StoreCompanyRequest extends Request
|
||||
$input['subdomain'] = MultiDB::randomSubdomainGenerator();
|
||||
}
|
||||
|
||||
if(isset($input['smtp_username']) && strlen(str_replace("*", "", $input['smtp_username'])) < 2) {
|
||||
unset($input['smtp_username']);
|
||||
}
|
||||
|
||||
if(isset($input['smtp_password']) && strlen(str_replace("*", "", $input['smtp_password'])) < 2) {
|
||||
unset($input['smtp_password']);
|
||||
}
|
||||
|
||||
if(isset($input['smtp_verify_peer']) && is_string($input['smtp_verify_peer']))
|
||||
$input['smtp_verify_peer'] == 'true' ? true : false;
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
||||
|
@ -57,8 +57,14 @@ class UpdateCompanyRequest extends Request
|
||||
$rules['matomo_id'] = 'nullable|integer';
|
||||
$rules['e_invoice_certificate_passphrase'] = 'sometimes|nullable';
|
||||
$rules['e_invoice_certificate'] = 'sometimes|nullable|file|mimes:p12,pfx,pem,cer,crt,der,txt,p7b,spc,bin';
|
||||
// $rules['client_registration_fields'] = 'array';
|
||||
|
||||
$rules['smtp_host'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_port'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_encryption'] = 'sometimes|string|nullable';
|
||||
$rules['smtp_local_domain'] = 'sometimes|string|nullable';
|
||||
// $rules['smtp_verify_peer'] = 'sometimes|string';
|
||||
|
||||
|
||||
if (isset($input['portal_mode']) && ($input['portal_mode'] == 'domain' || $input['portal_mode'] == 'iframe')) {
|
||||
$rules['portal_domain'] = 'bail|nullable|sometimes|url';
|
||||
}
|
||||
@ -74,23 +80,35 @@ class UpdateCompanyRequest extends Request
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
if (array_key_exists('portal_domain', $input) && strlen($input['portal_domain']) > 1) {
|
||||
if (isset($input['portal_domain']) && strlen($input['portal_domain']) > 1) {
|
||||
$input['portal_domain'] = $this->addScheme($input['portal_domain']);
|
||||
$input['portal_domain'] = rtrim(strtolower($input['portal_domain']), "/");
|
||||
}
|
||||
|
||||
if (array_key_exists('settings', $input)) {
|
||||
if (isset($input['settings'])) {
|
||||
$input['settings'] = (array)$this->filterSaveableSettings($input['settings']);
|
||||
}
|
||||
|
||||
if(array_key_exists('subdomain', $input) && $this->company->subdomain == $input['subdomain']) {
|
||||
if(isset($input['subdomain']) && $this->company->subdomain == $input['subdomain']) {
|
||||
unset($input['subdomain']);
|
||||
}
|
||||
|
||||
if(array_key_exists('e_invoice_certificate_passphrase', $input) && empty($input['e_invoice_certificate_passphrase'])) {
|
||||
if(isset($input['e_invoice_certificate_passphrase']) && empty($input['e_invoice_certificate_passphrase'])) {
|
||||
unset($input['e_invoice_certificate_passphrase']);
|
||||
}
|
||||
|
||||
if(isset($input['smtp_username']) && strlen(str_replace("*","", $input['smtp_username'])) < 2) {
|
||||
unset($input['smtp_username']);
|
||||
}
|
||||
|
||||
if(isset($input['smtp_password']) && strlen(str_replace("*", "", $input['smtp_password'])) < 2) {
|
||||
unset($input['smtp_password']);
|
||||
}
|
||||
|
||||
if(isset($input['smtp_verify_peer']) && is_string($input['smtp_verify_peer'])) {
|
||||
$input['smtp_verify_peer'] == 'true' ? true : false;
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,8 @@ class StoreCreditRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -52,6 +52,8 @@ class UpdateCreditRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -52,6 +52,7 @@ class StoreExpenseRequest extends Request
|
||||
$rules['category_id'] = 'bail|nullable|sometimes|exists:expense_categories,id,company_id,'.$user->company()->id.',is_deleted,0';
|
||||
$rules['payment_date'] = 'bail|nullable|sometimes|date:Y-m-d';
|
||||
$rules['date'] = 'bail|sometimes|date:Y-m-d';
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
|
||||
return $this->globalRules($rules);
|
||||
}
|
||||
|
@ -29,25 +29,32 @@ class UpdateExpenseRequest extends Request
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->expense);
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->can('edit', $this->expense);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
$rules = [];
|
||||
|
||||
if (isset($this->number)) {
|
||||
$rules['number'] = Rule::unique('expenses')->where('company_id', auth()->user()->company()->id)->ignore($this->expense->id);
|
||||
$rules['number'] = Rule::unique('expenses')->where('company_id', $user->company()->id)->ignore($this->expense->id);
|
||||
}
|
||||
|
||||
if ($this->client_id) {
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
$rules['client_id'] = 'bail|sometimes|exists:clients,id,company_id,'.$user->company()->id;
|
||||
}
|
||||
|
||||
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.auth()->user()->company()->id.',is_deleted,0';
|
||||
$rules['transaction_id'] = 'bail|sometimes|nullable|exists:bank_transactions,id,company_id,'.auth()->user()->company()->id;
|
||||
$rules['invoice_id'] = 'bail|sometimes|nullable|exists:invoices,id,company_id,'.auth()->user()->company()->id;
|
||||
$rules['category_id'] = 'bail|sometimes|nullable|exists:expense_categories,id,company_id,'.$user->company()->id.',is_deleted,0';
|
||||
$rules['transaction_id'] = 'bail|sometimes|nullable|exists:bank_transactions,id,company_id,'.$user->company()->id;
|
||||
$rules['invoice_id'] = 'bail|sometimes|nullable|exists:invoices,id,company_id,'.$user->company()->id;
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
|
||||
|
||||
return $this->globalRules($rules);
|
||||
@ -55,6 +62,10 @@ class UpdateExpenseRequest extends Request
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
$input = $this->all();
|
||||
|
||||
$input = $this->decodePrimaryKeys($input);
|
||||
@ -64,7 +75,7 @@ class UpdateExpenseRequest extends Request
|
||||
}
|
||||
|
||||
if (! array_key_exists('currency_id', $input) || strlen($input['currency_id']) == 0) {
|
||||
$input['currency_id'] = (string) auth()->user()->company()->settings->currency_id;
|
||||
$input['currency_id'] = (string) $user->company()->settings->currency_id;
|
||||
}
|
||||
|
||||
/* Ensure the project is related */
|
||||
|
@ -47,6 +47,8 @@ class StoreInvoiceRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
@ -76,6 +78,7 @@ class StoreInvoiceRequest extends Request
|
||||
$rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date'];
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,8 @@ class UpdateInvoiceRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
@ -77,6 +79,7 @@ class UpdateInvoiceRequest extends Request
|
||||
$rules['partial'] = 'bail|sometimes|nullable|numeric';
|
||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
@ -126,6 +126,8 @@ class StorePaymentRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -55,6 +55,8 @@ class UpdatePaymentRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -35,6 +35,8 @@ class StoreProductRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -25,7 +25,11 @@ class UpdateProductRequest extends Request
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->product);
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->can('edit', $this->product);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
@ -34,6 +38,8 @@ class UpdateProductRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -53,6 +53,8 @@ class StoreProjectRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -49,6 +49,8 @@ class UpdateProjectRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -57,6 +57,8 @@ class StorePurchaseOrderRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
} else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -59,6 +59,8 @@ class UpdatePurchaseOrderRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -49,6 +49,8 @@ class StoreQuoteRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
@ -59,11 +61,8 @@ class StoreQuoteRequest extends Request
|
||||
|
||||
$rules['number'] = ['nullable', Rule::unique('quotes')->where('company_id', $user->company()->id)];
|
||||
$rules['discount'] = 'sometimes|numeric';
|
||||
|
||||
$rules['is_amount_discount'] = ['boolean'];
|
||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||
|
||||
// $rules['number'] = new UniqueQuoteNumberRule($this->all());
|
||||
$rules['line_items'] = 'array';
|
||||
|
||||
return $rules;
|
||||
|
@ -46,6 +46,8 @@ class UpdateQuoteRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -49,6 +49,8 @@ class StoreRecurringInvoiceRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -48,6 +48,8 @@ class UpdateRecurringInvoiceRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
54
app/Http/Requests/Smtp/CheckSmtpRequest.php
Normal file
54
app/Http/Requests/Smtp/CheckSmtpRequest.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\Smtp;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
|
||||
class CheckSmtpRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
$input = $this->input();
|
||||
|
||||
if(isset($input['smtp_username']) && $input['smtp_username'] == '********')
|
||||
unset($input['smtp_username']);
|
||||
|
||||
if(isset($input['smtp_password'])&& $input['smtp_password'] == '********')
|
||||
unset($input['smtp_password']);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
@ -82,6 +82,8 @@ class StoreTaskRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -88,6 +88,8 @@ class UpdateTaskRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -64,6 +64,8 @@ class StoreVendorRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -65,6 +65,8 @@ class UpdateVendorRequest extends Request
|
||||
$rules['documents.*'] = $this->file_validation;
|
||||
} elseif ($this->file('documents')) {
|
||||
$rules['documents'] = $this->file_validation;
|
||||
}else {
|
||||
$rules['documents'] = 'bail|sometimes|array';
|
||||
}
|
||||
|
||||
if ($this->file('file') && is_array($this->file('file'))) {
|
||||
|
@ -315,14 +315,11 @@ class BaseTransformer
|
||||
public function getFloat($data, $field)
|
||||
{
|
||||
if (array_key_exists($field, $data)) {
|
||||
//$number = preg_replace('/[^0-9-.]+/', '', $data[$field]);
|
||||
return Number::parseFloat($data[$field]);
|
||||
} else {
|
||||
//$number = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return Number::parseFloat($number);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,6 +112,13 @@ use Laracasts\Presenter\PresentableTrait;
|
||||
* @property int $notify_vendor_when_paid
|
||||
* @property int $invoice_task_hours
|
||||
* @property int $deleted_at
|
||||
* @property string $smtp_username
|
||||
* @property string $smtp_password
|
||||
* @property string $smtp_host
|
||||
* @property string $smtp_port
|
||||
* @property string $smtp_encryption
|
||||
* @property string $smtp_local_domain
|
||||
* @property boolean $smtp_verify_peer
|
||||
* @property-read \App\Models\Account $account
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
|
||||
* @property-read int|null $activities_count
|
||||
@ -352,12 +359,19 @@ class Company extends BaseModel
|
||||
'calculate_taxes',
|
||||
'tax_data',
|
||||
'e_invoice_certificate_passphrase',
|
||||
'smtp_host',
|
||||
'smtp_port',
|
||||
'smtp_encryption',
|
||||
'smtp_local_domain',
|
||||
'smtp_verify_peer',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'id',
|
||||
'db',
|
||||
'ip',
|
||||
'smtp_username',
|
||||
'smtp_password',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -372,6 +386,8 @@ class Company extends BaseModel
|
||||
'tax_data' => 'object',
|
||||
'origin_tax_data' => 'object',
|
||||
'e_invoice_certificate_passphrase' => EncryptedCast::class,
|
||||
'smtp_username' => 'encrypted',
|
||||
'smtp_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected $with = [];
|
||||
|
@ -176,7 +176,11 @@ class Document extends BaseModel
|
||||
|
||||
public function generateRoute($absolute = false)
|
||||
{
|
||||
try{
|
||||
return route('api.documents.show', ['document' => $this->hashed_id]).'/download';
|
||||
}catch(\Exception $e){
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteFile()
|
||||
|
@ -17,7 +17,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* App\Models\License
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $created_at
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property int|null $updated_at
|
||||
* @property int|null $deleted_at
|
||||
* @property string|null $first_name
|
||||
@ -28,6 +28,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property string|null $transaction_reference
|
||||
* @property int|null $product_id
|
||||
* @property int|null $recurring_invoice_id
|
||||
* @property-read \App\Models\RecurringInvoice $recurring_invoice
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel company()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|StaticModel exclude($columns)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|License newModelQuery()
|
||||
@ -53,4 +54,24 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
class License extends StaticModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
}
|
||||
|
||||
protected $casts = [
|
||||
'created_at' => 'date',
|
||||
];
|
||||
|
||||
public function expiry(): string
|
||||
{
|
||||
return $this->created_at->addYear()->format('Y-m-d');
|
||||
}
|
||||
|
||||
public function recurring_invoice()
|
||||
{
|
||||
return $this->belongsTo(RecurringInvoice::class);
|
||||
}
|
||||
|
||||
public function url()
|
||||
{
|
||||
$contact = $this->recurring_invoice->client->contacts()->where('email', $this->email)->first();
|
||||
|
||||
}
|
||||
}
|
@ -75,6 +75,10 @@ class Project extends BaseModel
|
||||
'number',
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
'documents',
|
||||
];
|
||||
|
||||
public function getEntityType()
|
||||
{
|
||||
return self::class;
|
||||
|
@ -131,6 +131,10 @@ class Task extends BaseModel
|
||||
'deleted_at' => 'timestamp',
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
// 'project',
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
||||
public function getEntityType()
|
||||
|
@ -39,6 +39,7 @@ class CompanyRepository extends BaseRepository
|
||||
|
||||
$company->fill($data);
|
||||
|
||||
// nlog($data);
|
||||
/** Only required to handle v4 migration workloads */
|
||||
if(Ninja::isHosted() && $company->isDirty('is_disabled') && !$company->is_disabled) {
|
||||
Ninja::triggerForwarding($company->company_key, $company->owner()->email);
|
||||
@ -48,6 +49,14 @@ class CompanyRepository extends BaseRepository
|
||||
$company->saveSettings($data['settings'], $company);
|
||||
}
|
||||
|
||||
if(isset($data['smtp_username'])) {
|
||||
$company->smtp_username = $data['smtp_username'];
|
||||
}
|
||||
|
||||
if(isset($data['smtp_password'])) {
|
||||
$company->smtp_password = $data['smtp_password'];
|
||||
}
|
||||
|
||||
$company->save();
|
||||
|
||||
return $company;
|
||||
|
@ -22,6 +22,8 @@ use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Repositories\CreditRepository;
|
||||
use App\Repositories\PaymentRepository;
|
||||
use App\Services\AbstractService;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Support\Str;
|
||||
@ -31,7 +33,7 @@ class AutoBillInvoice extends AbstractService
|
||||
private Client $client;
|
||||
|
||||
private array $used_credit = [];
|
||||
|
||||
|
||||
/*Specific variable for partial payments */
|
||||
private bool $is_partial_amount = false;
|
||||
|
||||
@ -66,8 +68,12 @@ class AutoBillInvoice extends AbstractService
|
||||
$this->applyCreditPayment();
|
||||
}
|
||||
|
||||
if($this->client->getSetting('use_unapplied_payment') != 'off') {
|
||||
$this->applyUnappliedPayment();
|
||||
}
|
||||
|
||||
//If this returns true, it means a partial invoice amount was paid as a credit and there is no further balance payable
|
||||
if ($this->is_partial_amount && $this->invoice->partial == 0) {
|
||||
if (($this->is_partial_amount && $this->invoice->partial == 0) || (int)$this->invoice->balance == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -176,9 +182,6 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
$payment->amount = 0;
|
||||
$payment->applied = 0;
|
||||
|
||||
// $payment->amount = $amount;
|
||||
// $payment->applied = $amount;
|
||||
$payment->client_id = $this->invoice->client_id;
|
||||
$payment->currency_id = $this->invoice->client->getSetting('currency_id');
|
||||
$payment->date = now()->addSeconds($this->invoice->company->utc_offset())->format('Y-m-d');
|
||||
@ -217,8 +220,6 @@ class AutoBillInvoice extends AbstractService
|
||||
->client
|
||||
->service()
|
||||
->updateBalanceAndPaidToDate($amount * -1, $amount)
|
||||
// ->updateBalance($amount * -1)
|
||||
// ->updatePaidToDate($amount)
|
||||
->adjustCreditBalance($amount * -1)
|
||||
->save();
|
||||
|
||||
@ -233,9 +234,7 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
//if we have paid the invoice in full using credits, then we need to fire the event
|
||||
if($this->invoice->balance == 0) {
|
||||
|
||||
event(new InvoiceWasPaid($this->invoice, $payment, $payment->company, Ninja::eventVars()));
|
||||
|
||||
}
|
||||
|
||||
return $this->invoice
|
||||
@ -243,6 +242,84 @@ class AutoBillInvoice extends AbstractService
|
||||
->setCalculatedStatus()
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the client has unapplied payments on file
|
||||
* we will use these prior to charging a
|
||||
* payment method on file.
|
||||
*
|
||||
* This needs to be wrapped in a transaction.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
private function applyUnappliedPayment(): self
|
||||
{
|
||||
$unapplied_payments = Payment::query()
|
||||
->where('client_id', $this->client->id)
|
||||
->where('status_id', Payment::STATUS_COMPLETED)
|
||||
->where('is_deleted', false)
|
||||
->whereColumn('amount', '>', 'applied')
|
||||
->where('amount', '>', 0)
|
||||
->orderBy('created_at')
|
||||
->get();
|
||||
|
||||
$available_unapplied_balance = $unapplied_payments->sum('amount') - $unapplied_payments->sum('applied');
|
||||
|
||||
nlog("available unapplied balance = {$available_unapplied_balance}");
|
||||
|
||||
if ((int) $available_unapplied_balance == 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->invoice->partial > 0) {
|
||||
$this->is_partial_amount = true;
|
||||
}
|
||||
|
||||
$payment_repo = new PaymentRepository(new CreditRepository());
|
||||
|
||||
foreach ($unapplied_payments as $key => $payment) {
|
||||
$payment_balance = $payment->amount - $payment->applied;
|
||||
|
||||
if ($this->is_partial_amount) {
|
||||
//more than needed
|
||||
if ($payment_balance > $this->invoice->partial) {
|
||||
$payload = ['client_id' => $this->invoice->client_id, 'invoices' => [['invoice_id' => $this->invoice->id,'amount' => $this->invoice->partial]]];
|
||||
$payment_repo->save($payload, $payment);
|
||||
|
||||
$this->invoice = $this->invoice->fresh();
|
||||
|
||||
return $this;
|
||||
} else {
|
||||
$payload = ['client_id' => $this->invoice->client_id, 'invoices' => [['invoice_id' => $this->invoice->id,'amount' => $payment_balance]]];
|
||||
$payment_repo->save($payload, $payment);
|
||||
}
|
||||
} else {
|
||||
//more than needed
|
||||
if ($payment_balance > $this->invoice->balance) {
|
||||
|
||||
$payload = ['client_id' => $this->invoice->client_id, 'invoices' => [['invoice_id' => $this->invoice->id,'amount' => $this->invoice->balance]]];
|
||||
$payment_repo->save($payload, $payment);
|
||||
|
||||
$this->invoice = $this->invoice->fresh();
|
||||
|
||||
return $this;
|
||||
|
||||
} else {
|
||||
|
||||
$payload = ['client_id' => $this->invoice->client_id, 'invoices' => [['invoice_id' => $this->invoice->id,'amount' => $payment_balance]]];
|
||||
$payment_repo->save($payload, $payment);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if((int)$this->invoice->balance == 0) {
|
||||
event(new InvoiceWasPaid($this->invoice, $payment, $payment->company, Ninja::eventVars()));
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies credits to a payment prior to push
|
||||
@ -260,7 +337,7 @@ class AutoBillInvoice extends AbstractService
|
||||
|
||||
$available_credit_balance = $available_credits->sum('balance');
|
||||
|
||||
info("available credit balance = {$available_credit_balance}");
|
||||
nlog("available credit balance = {$available_credit_balance}");
|
||||
|
||||
if ((int) $available_credit_balance == 0) {
|
||||
return $this;
|
||||
@ -332,14 +409,6 @@ class AutoBillInvoice extends AbstractService
|
||||
})->orderBy('is_default', 'DESC')
|
||||
->get();
|
||||
|
||||
// $gateway_tokens = $this->client
|
||||
// ->gateway_tokens()
|
||||
// ->whereHas('gateway', function ($query) {
|
||||
// $query->where('is_deleted', 0)
|
||||
// ->where('deleted_at', null);
|
||||
// })->orderBy('is_default', 'DESC')
|
||||
// ->get();
|
||||
|
||||
$filtered_gateways = $gateway_tokens->filter(function ($gateway_token) use ($amount) {
|
||||
$company_gateway = $gateway_token->gateway;
|
||||
|
||||
|
@ -204,9 +204,9 @@ class PdfMock
|
||||
[
|
||||
'$client.shipping_postal_code' => '46420',
|
||||
'$client.billing_postal_code' => '11243',
|
||||
'$company.city_state_postal' => 'Beveley Hills, CA, 90210',
|
||||
'$company.postal_city_state' => 'CA',
|
||||
'$company.postal_city' => '90210, CA',
|
||||
'$company.city_state_postal' => "{$this->settings->city}, {$this->settings->state}, {$this->settings->postal_code}",
|
||||
'$company.postal_city_state' => "{$this->settings->postal_code}, {$this->settings->city}, {$this->settings->state}",
|
||||
'$company.postal_city' => "{$this->settings->postal_code}, {$this->settings->state}",
|
||||
'$product.gross_line_total' => '100',
|
||||
'$client.classification' => 'Individual',
|
||||
'$company.classification' => 'Business',
|
||||
@ -262,9 +262,9 @@ class PdfMock
|
||||
'$company.id_number' => $this->settings->id_number,
|
||||
'$invoice.po_number' => 'PO12345',
|
||||
'$invoice_total_raw' => 0.0,
|
||||
'$postal_city_state' => '11243 Aufderharchester, North Carolina',
|
||||
'$postal_city_state' => "{$this->settings->postal_code}, {$this->settings->city}, {$this->settings->state}",
|
||||
'$client.vat_number' => '975977515',
|
||||
'$city_state_postal' => 'Aufderharchester, North Carolina 11243',
|
||||
'$city_state_postal' => "{$this->settings->city}, {$this->settings->state}, {$this->settings->postal_code}",
|
||||
'$contact.full_name' => 'Benedict Eichmann',
|
||||
'$contact.last_name' => 'Eichmann',
|
||||
'$company.country_2' => 'US',
|
||||
@ -275,7 +275,7 @@ class PdfMock
|
||||
'$statement_amount' => '',
|
||||
'$task.description' => '',
|
||||
'$product.discount' => '',
|
||||
'$entity_issued_to' => 'Bob JOnes',
|
||||
'$entity_issued_to' => 'Bob Jones',
|
||||
'$assigned_to_user' => '',
|
||||
'$product.quantity' => '',
|
||||
'$total_tax_labels' => '',
|
||||
@ -303,10 +303,10 @@ class PdfMock
|
||||
'$invoice.custom2' => 'custom value',
|
||||
'$invoice.custom3' => 'custom value',
|
||||
'$invoice.custom4' => 'custom value',
|
||||
'$company.custom1' => 'custom value',
|
||||
'$company.custom2' => 'custom value',
|
||||
'$company.custom3' => 'custom value',
|
||||
'$company.custom4' => 'custom value',
|
||||
'$company.custom1' => $this->company->custom_value1,
|
||||
'$company.custom2' => $this->company->custom_value2,
|
||||
'$company.custom3' => $this->company->custom_value3,
|
||||
'$company.custom4' => $this->company->custom_value4,
|
||||
'$quote.po_number' => 'PO12345',
|
||||
'$company.website' => $this->settings->website,
|
||||
'$balance_due_raw' => '0.00',
|
||||
@ -317,8 +317,8 @@ class PdfMock
|
||||
'$user.first_name' => 'Derrick Monahan DDS',
|
||||
'$created_by_user' => 'Derrick Monahan DDS Erna Wunsch',
|
||||
'$client.currency' => 'USD',
|
||||
'$company.country' => 'United States',
|
||||
'$company.address' => 'Christiansen Garden<br/>70218 Lori Station Suite 529<br/>New Loy, Delaware 29359<br/>United States<br/>Phone: 1-240-886-2233<br/>Email: immanuel53@example.net<br/>',
|
||||
'$company.country' => $this->company->country()?->name ?? 'USA',
|
||||
'$company.address' => $this->company->present()->address(),
|
||||
'$tech_hero_image' => 'http://ninja.test:8000/images/pdf-designs/tech-hero-image.jpg',
|
||||
'$task.tax_name1' => '',
|
||||
'$task.tax_name2' => '',
|
||||
@ -450,15 +450,15 @@ class PdfMock
|
||||
'$task.tax' => '',
|
||||
'$discount' => '$0.00',
|
||||
'$subtotal' => '$0.00',
|
||||
'$company1' => 'custom value',
|
||||
'$company2' => 'custom value',
|
||||
'$company3' => 'custom value',
|
||||
'$company4' => 'custom value',
|
||||
'$company1' => $this->company->custom_value1,
|
||||
'$company2' => $this->company->custom_value2,
|
||||
'$company3' => $this->company->custom_value3,
|
||||
'$company4' => $this->company->custom_value4,
|
||||
'$due_date' => '2022-01-01',
|
||||
'$poNumber' => 'PO-123456',
|
||||
'$quote_no' => '0029',
|
||||
'$address2' => '63993 Aiyana View',
|
||||
'$address1' => '8447',
|
||||
'$address2' => $this->settings->address2,
|
||||
'$address1' => $this->settings->address1,
|
||||
'$viewLink' => '<a class="button" href="http://ninja.test:8000/client/invoice/UAUY8vIPuno72igmXbbpldwo5BDDKIqs">View Invoice</a>',
|
||||
'$autoBill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
|
||||
'$view_url' => 'http://ninja.test:8000/client/invoice/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
|
||||
@ -477,7 +477,7 @@ class PdfMock
|
||||
'$country' => 'United States',
|
||||
'$contact' => 'Benedict Eichmann',
|
||||
'$app_url' => 'http://ninja.test:8000',
|
||||
'$website' => 'http://www.parisian.org/',
|
||||
'$website' => $this->settings->website,
|
||||
'$entity' => '',
|
||||
'$thanks' => 'Thanks!',
|
||||
'$amount' => '$30.00',
|
||||
|
@ -47,6 +47,7 @@ use App\Utils\Traits\SubscriptionHooker;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
|
||||
class SubscriptionService
|
||||
{
|
||||
@ -208,7 +209,7 @@ class SubscriptionService
|
||||
$invitation = $invoice->invitations()->first();
|
||||
|
||||
$email_object = new EmailObject();
|
||||
$email_object->to = [$contact->email];
|
||||
$email_object->to = [new Address($contact->email, $contact->present()->name())];
|
||||
$email_object->subject = ctrans('texts.white_label_link') . " " .ctrans('texts.payment_subject');
|
||||
$email_object->body = ctrans('texts.white_label_body', ['license_key' => $license_key]);
|
||||
$email_object->client_id = $invoice->client_id;
|
||||
|
@ -711,7 +711,10 @@ class TemplateService
|
||||
private function getPaymentRefundActivity(Payment $payment): array
|
||||
{
|
||||
|
||||
return collect($payment->refund_meta ?? [])
|
||||
if(!is_array($payment->refund_meta))
|
||||
return [];
|
||||
|
||||
return collect($payment->refund_meta)
|
||||
->map(function ($refund) use ($payment) {
|
||||
|
||||
$date = \Carbon\Carbon::parse($refund['date'])->addSeconds($payment->client->timezone_offset());
|
||||
|
@ -204,6 +204,13 @@ class CompanyTransformer extends EntityTransformer
|
||||
'invoice_task_project_header' => (bool) $company->invoice_task_project_header,
|
||||
'invoice_task_item_description' => (bool) $company->invoice_task_item_description,
|
||||
'origin_tax_data' => $company->origin_tax_data ?: new \stdClass(),
|
||||
'smtp_host' => (string)$company->smtp_host ?? '',
|
||||
'smtp_port' => (int)$company->smtp_port ?? 25,
|
||||
'smtp_encryption' => (string)$company->smtp_encryption ?? 'tls',
|
||||
'smtp_username' => $company->smtp_username ? '********' : '',
|
||||
'smtp_password' => $company->smtp_password ? '********' : '',
|
||||
'smtp_local_domain' => (string)$company->smtp_local_domain ?? '',
|
||||
'smtp_verify_peer' => (bool)$company->smtp_verify_peer,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,10 @@ class ProjectTransformer extends EntityTransformer
|
||||
{
|
||||
$transformer = new DocumentTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($project->documents, $transformer, Document::class);
|
||||
if($project->documents)
|
||||
return $this->includeCollection($project->documents, $transformer, Document::class);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function includeClient(Project $project): \League\Fractal\Resource\Item
|
||||
|
@ -100,11 +100,10 @@ class TaskTransformer extends EntityTransformer
|
||||
{
|
||||
$transformer = new ProjectTransformer($this->serializer);
|
||||
|
||||
if (!$task->project) {
|
||||
return null;
|
||||
}
|
||||
if ($task->project)
|
||||
return $this->includeItem($task->project, $transformer, Project::class);
|
||||
|
||||
return $this->includeItem($task->project, $transformer, Project::class);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function transform(Task $task)
|
||||
|
@ -95,6 +95,14 @@ class Number
|
||||
*/
|
||||
public static function parseFloat($value)
|
||||
{
|
||||
if(!$value)
|
||||
return 0;
|
||||
|
||||
$multiplier = false;
|
||||
|
||||
if(substr($value, 0,1) == '-')
|
||||
$multiplier = -1;
|
||||
|
||||
// convert "," to "."
|
||||
$s = str_replace(',', '.', $value);
|
||||
|
||||
@ -108,7 +116,9 @@ class Number
|
||||
// remove all separators from first part and keep the end
|
||||
$s = str_replace('.', '', substr($s, 0, -3)).substr($s, -3);
|
||||
|
||||
// return float
|
||||
if($multiplier)
|
||||
$s = floatval($s)*-1;
|
||||
|
||||
return (float) $s;
|
||||
}
|
||||
|
||||
|
@ -75,11 +75,11 @@ trait CleanLineItems
|
||||
}
|
||||
|
||||
if(isset($item['notes'])) {
|
||||
$item['notes'] = str_replace("</", "<-", $item['notes']);
|
||||
$item['notes'] = str_replace("</sc", "<-", $item['notes']);
|
||||
}
|
||||
|
||||
if(isset($item['product_key'])) {
|
||||
$item['product_key'] = str_replace("</", "<-", $item['product_key']);
|
||||
$item['product_key'] = str_replace("</sc", "<-", $item['product_key']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -101,7 +101,8 @@
|
||||
"twilio/sdk": "^6.40",
|
||||
"webpatser/laravel-countries": "dev-master#75992ad",
|
||||
"wepay/php-sdk": "^0.3",
|
||||
"wildbit/postmark-php": "^4.0"
|
||||
"wildbit/postmark-php": "^4.0",
|
||||
"hyvor/php-json-exporter": "^0.0.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"php": "^8.1|^8.2",
|
||||
@ -112,7 +113,6 @@
|
||||
"fakerphp/faker": "^1.14",
|
||||
"filp/whoops": "^2.7",
|
||||
"friendsofphp/php-cs-fixer": "^3.14",
|
||||
"hyvor/php-json-exporter": "^0.0.3",
|
||||
"laracasts/cypress": "^3.0",
|
||||
"larastan/larastan": "^2",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
|
284
composer.lock
generated
284
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "7a990a24b596bd1ab52a5f829fcc7e27",
|
||||
"content-hash": "dc9142e4af116b98de0ac6310297dba6",
|
||||
"packages": [
|
||||
{
|
||||
"name": "afosto/yaac",
|
||||
@ -141,16 +141,16 @@
|
||||
},
|
||||
{
|
||||
"name": "amphp/byte-stream",
|
||||
"version": "v2.1.0",
|
||||
"version": "v2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/amphp/byte-stream.git",
|
||||
"reference": "0a4b0e80dad92c75e6131f8ad253919211540338"
|
||||
"reference": "daa00f2efdbd71565bf64ffefa89e37542addf93"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/amphp/byte-stream/zipball/0a4b0e80dad92c75e6131f8ad253919211540338",
|
||||
"reference": "0a4b0e80dad92c75e6131f8ad253919211540338",
|
||||
"url": "https://api.github.com/repos/amphp/byte-stream/zipball/daa00f2efdbd71565bf64ffefa89e37542addf93",
|
||||
"reference": "daa00f2efdbd71565bf64ffefa89e37542addf93",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -166,7 +166,7 @@
|
||||
"amphp/php-cs-fixer-config": "^2",
|
||||
"amphp/phpunit-util": "^3",
|
||||
"phpunit/phpunit": "^9",
|
||||
"psalm/phar": "^5.4"
|
||||
"psalm/phar": "5.22.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -204,7 +204,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/amphp/byte-stream/issues",
|
||||
"source": "https://github.com/amphp/byte-stream/tree/v2.1.0"
|
||||
"source": "https://github.com/amphp/byte-stream/tree/v2.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -212,7 +212,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-11-19T14:34:16+00:00"
|
||||
"time": "2024-02-17T04:49:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "amphp/cache",
|
||||
@ -581,16 +581,16 @@
|
||||
},
|
||||
{
|
||||
"name": "amphp/process",
|
||||
"version": "v2.0.1",
|
||||
"version": "v2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/amphp/process.git",
|
||||
"reference": "a65d3bc1f36ef12d44df42a68f0f0643183f1052"
|
||||
"reference": "a79dc87100be857db2c4bbfd5369585a6d1e658c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/amphp/process/zipball/a65d3bc1f36ef12d44df42a68f0f0643183f1052",
|
||||
"reference": "a65d3bc1f36ef12d44df42a68f0f0643183f1052",
|
||||
"url": "https://api.github.com/repos/amphp/process/zipball/a79dc87100be857db2c4bbfd5369585a6d1e658c",
|
||||
"reference": "a79dc87100be857db2c4bbfd5369585a6d1e658c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -637,7 +637,7 @@
|
||||
"homepage": "https://amphp.org/process",
|
||||
"support": {
|
||||
"issues": "https://github.com/amphp/process/issues",
|
||||
"source": "https://github.com/amphp/process/tree/v2.0.1"
|
||||
"source": "https://github.com/amphp/process/tree/v2.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -645,7 +645,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-01-15T16:00:57+00:00"
|
||||
"time": "2024-02-13T20:38:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "amphp/serialization",
|
||||
@ -1343,16 +1343,16 @@
|
||||
},
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
"version": "3.298.9",
|
||||
"version": "3.299.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||
"reference": "db225c3a1c5dabfbb5071349cfb7e4c396c3d9ec"
|
||||
"reference": "a0f87b8e8bfb9afd0ffd702fcda556b465eee457"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/db225c3a1c5dabfbb5071349cfb7e4c396c3d9ec",
|
||||
"reference": "db225c3a1c5dabfbb5071349cfb7e4c396c3d9ec",
|
||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a0f87b8e8bfb9afd0ffd702fcda556b465eee457",
|
||||
"reference": "a0f87b8e8bfb9afd0ffd702fcda556b465eee457",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1432,9 +1432,9 @@
|
||||
"support": {
|
||||
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
|
||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.298.9"
|
||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.299.1"
|
||||
},
|
||||
"time": "2024-02-13T19:08:16+00:00"
|
||||
"time": "2024-02-16T19:08:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@ -2265,16 +2265,16 @@
|
||||
},
|
||||
{
|
||||
"name": "doctrine/dbal",
|
||||
"version": "3.8.1",
|
||||
"version": "3.8.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/dbal.git",
|
||||
"reference": "c9ea252cdce4da324ede3d6c5913dd89f769afd2"
|
||||
"reference": "a19a1d05ca211f41089dffcc387733a6875196cb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/c9ea252cdce4da324ede3d6c5913dd89f769afd2",
|
||||
"reference": "c9ea252cdce4da324ede3d6c5913dd89f769afd2",
|
||||
"url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb",
|
||||
"reference": "a19a1d05ca211f41089dffcc387733a6875196cb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2358,7 +2358,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/dbal/issues",
|
||||
"source": "https://github.com/doctrine/dbal/tree/3.8.1"
|
||||
"source": "https://github.com/doctrine/dbal/tree/3.8.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -2374,7 +2374,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-02-03T17:33:49+00:00"
|
||||
"time": "2024-02-12T18:36:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
@ -4500,6 +4500,50 @@
|
||||
},
|
||||
"time": "2021-07-21T13:50:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hyvor/php-json-exporter",
|
||||
"version": "0.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/hyvor/php-json-exporter.git",
|
||||
"reference": "9fade1856135deaa2d0fc9b3065949019703471d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/hyvor/php-json-exporter/zipball/9fade1856135deaa2d0fc9b3065949019703471d",
|
||||
"reference": "9fade1856135deaa2d0fc9b3065949019703471d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"pestphp/pest": "^1.22",
|
||||
"phpstan/phpstan": "^1.8"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Hyvor\\JsonExporter\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Supun",
|
||||
"email": "supun@hyvor.com"
|
||||
}
|
||||
],
|
||||
"description": "Export large datasets to a JSON file without memory exhaustion",
|
||||
"support": {
|
||||
"issues": "https://github.com/hyvor/php-json-exporter/issues",
|
||||
"source": "https://github.com/hyvor/php-json-exporter/tree/0.0.3"
|
||||
},
|
||||
"time": "2023-04-11T15:12:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "imdhemy/appstore-iap",
|
||||
"version": "1.6.0",
|
||||
@ -4606,16 +4650,16 @@
|
||||
},
|
||||
{
|
||||
"name": "imdhemy/laravel-purchases",
|
||||
"version": "1.9.1",
|
||||
"version": "1.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/imdhemy/laravel-in-app-purchases.git",
|
||||
"reference": "b74e09b78fb3e0f1b1630dbcfd23d9f6fe251b90"
|
||||
"reference": "014c189795704bea88260a4054b02d80b9bbc3ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/imdhemy/laravel-in-app-purchases/zipball/b74e09b78fb3e0f1b1630dbcfd23d9f6fe251b90",
|
||||
"reference": "b74e09b78fb3e0f1b1630dbcfd23d9f6fe251b90",
|
||||
"url": "https://api.github.com/repos/imdhemy/laravel-in-app-purchases/zipball/014c189795704bea88260a4054b02d80b9bbc3ac",
|
||||
"reference": "014c189795704bea88260a4054b02d80b9bbc3ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -4671,7 +4715,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/imdhemy/laravel-in-app-purchases/issues",
|
||||
"source": "https://github.com/imdhemy/laravel-in-app-purchases/tree/1.9.1"
|
||||
"source": "https://github.com/imdhemy/laravel-in-app-purchases/tree/1.10.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -4679,7 +4723,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-15T10:35:56+00:00"
|
||||
"time": "2024-02-17T05:58:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "intervention/image",
|
||||
@ -4829,7 +4873,7 @@
|
||||
},
|
||||
{
|
||||
"name": "invoiceninja/ubl_invoice",
|
||||
"version": "v2.0.1",
|
||||
"version": "v2.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/invoiceninja/UBL_invoice.git",
|
||||
@ -4887,7 +4931,7 @@
|
||||
"xml invoice"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/invoiceninja/UBL_invoice/tree/v2.0.1"
|
||||
"source": "https://github.com/invoiceninja/UBL_invoice/tree/v2.0.2"
|
||||
},
|
||||
"time": "2024-02-05T02:16:14+00:00"
|
||||
},
|
||||
@ -5710,16 +5754,16 @@
|
||||
},
|
||||
{
|
||||
"name": "laravel/socialite",
|
||||
"version": "v5.12.0",
|
||||
"version": "v5.12.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/socialite.git",
|
||||
"reference": "ffeeb2cdf723b4c88b25479968e2d3a61a83dbe5"
|
||||
"reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/socialite/zipball/ffeeb2cdf723b4c88b25479968e2d3a61a83dbe5",
|
||||
"reference": "ffeeb2cdf723b4c88b25479968e2d3a61a83dbe5",
|
||||
"url": "https://api.github.com/repos/laravel/socialite/zipball/7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
|
||||
"reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -5776,7 +5820,7 @@
|
||||
"issues": "https://github.com/laravel/socialite/issues",
|
||||
"source": "https://github.com/laravel/socialite"
|
||||
},
|
||||
"time": "2024-02-11T18:29:55+00:00"
|
||||
"time": "2024-02-16T08:58:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/tinker",
|
||||
@ -7384,41 +7428,41 @@
|
||||
},
|
||||
{
|
||||
"name": "moneyphp/money",
|
||||
"version": "v4.4.0",
|
||||
"version": "v4.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/moneyphp/money.git",
|
||||
"reference": "5e60aebf09f709dd4ea16bf85e66d65301c0d172"
|
||||
"reference": "a1daa7daf159b4044e3d0c34c41fe2be5860e850"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/moneyphp/money/zipball/5e60aebf09f709dd4ea16bf85e66d65301c0d172",
|
||||
"reference": "5e60aebf09f709dd4ea16bf85e66d65301c0d172",
|
||||
"url": "https://api.github.com/repos/moneyphp/money/zipball/a1daa7daf159b4044e3d0c34c41fe2be5860e850",
|
||||
"reference": "a1daa7daf159b4044e3d0c34c41fe2be5860e850",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-bcmath": "*",
|
||||
"ext-filter": "*",
|
||||
"ext-json": "*",
|
||||
"php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cache/taggable-cache": "^1.1.0",
|
||||
"doctrine/coding-standard": "^9.0",
|
||||
"doctrine/instantiator": "^1.4.0",
|
||||
"doctrine/coding-standard": "^12.0",
|
||||
"doctrine/instantiator": "^1.5.0 || ^2.0",
|
||||
"ext-gmp": "*",
|
||||
"ext-intl": "*",
|
||||
"florianv/exchanger": "^2.6.3",
|
||||
"florianv/exchanger": "^2.8.1",
|
||||
"florianv/swap": "^4.3.0",
|
||||
"moneyphp/crypto-currencies": "^1.0.0",
|
||||
"moneyphp/iso-currencies": "^3.2.1",
|
||||
"php-http/message": "^1.11.0",
|
||||
"php-http/mock-client": "^1.4.1",
|
||||
"moneyphp/crypto-currencies": "^1.1.0",
|
||||
"moneyphp/iso-currencies": "^3.4",
|
||||
"php-http/message": "^1.16.0",
|
||||
"php-http/mock-client": "^1.6.0",
|
||||
"phpbench/phpbench": "^1.2.5",
|
||||
"phpunit/phpunit": "^9.5.4",
|
||||
"phpunit/phpunit": "^10.5.9",
|
||||
"psalm/plugin-phpunit": "^0.18.4",
|
||||
"psr/cache": "^1.0.1 || ^2.0 || ^3.0",
|
||||
"vimeo/psalm": "~5.15.0"
|
||||
"vimeo/psalm": "~5.20.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gmp": "Calculate without integer limits",
|
||||
@ -7466,9 +7510,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/moneyphp/money/issues",
|
||||
"source": "https://github.com/moneyphp/money/tree/v4.4.0"
|
||||
"source": "https://github.com/moneyphp/money/tree/v4.5.0"
|
||||
},
|
||||
"time": "2024-01-24T08:29:16+00:00"
|
||||
"time": "2024-02-15T19:47:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "monolog/monolog",
|
||||
@ -11801,38 +11845,38 @@
|
||||
},
|
||||
{
|
||||
"name": "spatie/php-structure-discoverer",
|
||||
"version": "2.0.1",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/php-structure-discoverer.git",
|
||||
"reference": "d2e4e6cba962ce2a058ea415a123dd84b37765ac"
|
||||
"reference": "f5b3c935dda89d6c382b27e3caf348fa80bcfa88"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/php-structure-discoverer/zipball/d2e4e6cba962ce2a058ea415a123dd84b37765ac",
|
||||
"reference": "d2e4e6cba962ce2a058ea415a123dd84b37765ac",
|
||||
"url": "https://api.github.com/repos/spatie/php-structure-discoverer/zipball/f5b3c935dda89d6c382b27e3caf348fa80bcfa88",
|
||||
"reference": "f5b3c935dda89d6c382b27e3caf348fa80bcfa88",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"amphp/amp": "^v3.0",
|
||||
"amphp/parallel": "^2.2",
|
||||
"illuminate/collections": "^9.30|^10.0",
|
||||
"illuminate/collections": "^10.0|^11.0",
|
||||
"php": "^8.1",
|
||||
"spatie/laravel-package-tools": "^1.4.3",
|
||||
"symfony/finder": "^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"illuminate/console": "^9.30|^10.0",
|
||||
"illuminate/console": "^10.0|^11.0",
|
||||
"laravel/pint": "^1.0",
|
||||
"nunomaduro/collision": "^6.0",
|
||||
"nunomaduro/collision": "^7.0|^8.0",
|
||||
"nunomaduro/larastan": "^2.0.1",
|
||||
"orchestra/testbench": "^7.0|^8.0",
|
||||
"pestphp/pest": "^1.21",
|
||||
"pestphp/pest-plugin-laravel": "^1.1",
|
||||
"orchestra/testbench": "^7.0|^8.0|^9.0",
|
||||
"pestphp/pest": "^2.0",
|
||||
"pestphp/pest-plugin-laravel": "^2.0",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan-deprecation-rules": "^1.0",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"phpunit/phpunit": "^9.5|^10.0",
|
||||
"spatie/laravel-ray": "^1.26"
|
||||
},
|
||||
"type": "library",
|
||||
@ -11869,7 +11913,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/php-structure-discoverer/issues",
|
||||
"source": "https://github.com/spatie/php-structure-discoverer/tree/2.0.1"
|
||||
"source": "https://github.com/spatie/php-structure-discoverer/tree/2.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -11877,7 +11921,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-01-08T21:01:26+00:00"
|
||||
"time": "2024-02-16T12:42:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sprain/swiss-qr-bill",
|
||||
@ -15832,16 +15876,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "barryvdh/laravel-debugbar",
|
||||
"version": "v3.10.4",
|
||||
"version": "v3.10.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-debugbar.git",
|
||||
"reference": "09d3dc77d7dc1b063e3728a6029c39ee0fbebf1d"
|
||||
"reference": "d1a48965f2b25a6cec2eea07d719b568a37c9a88"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/09d3dc77d7dc1b063e3728a6029c39ee0fbebf1d",
|
||||
"reference": "09d3dc77d7dc1b063e3728a6029c39ee0fbebf1d",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/d1a48965f2b25a6cec2eea07d719b568a37c9a88",
|
||||
"reference": "d1a48965f2b25a6cec2eea07d719b568a37c9a88",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -15900,7 +15944,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-debugbar/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.10.4"
|
||||
"source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.10.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -15912,43 +15956,43 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-02-14T08:52:12+00:00"
|
||||
"time": "2024-02-15T10:45:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/laravel-ide-helper",
|
||||
"version": "v2.14.0",
|
||||
"version": "v2.15.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
|
||||
"reference": "485c756f6cff408d6b273274c5e86112c3973d98"
|
||||
"reference": "77831852bb7bc54f287246d32eb91274eaf87f8b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/485c756f6cff408d6b273274c5e86112c3973d98",
|
||||
"reference": "485c756f6cff408d6b273274c5e86112c3973d98",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/77831852bb7bc54f287246d32eb91274eaf87f8b",
|
||||
"reference": "77831852bb7bc54f287246d32eb91274eaf87f8b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"barryvdh/reflection-docblock": "^2.0.6",
|
||||
"composer/class-map-generator": "^1.0",
|
||||
"doctrine/dbal": "^2.6 || ^3",
|
||||
"doctrine/dbal": "^2.6 || ^3.1.4",
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^8 || ^9 || ^10",
|
||||
"illuminate/filesystem": "^8 || ^9 || ^10",
|
||||
"illuminate/support": "^8 || ^9 || ^10",
|
||||
"illuminate/console": "^9 || ^10",
|
||||
"illuminate/filesystem": "^9 || ^10",
|
||||
"illuminate/support": "^9 || ^10",
|
||||
"nikic/php-parser": "^4.18 || ^5",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"php": "^8.0",
|
||||
"phpdocumentor/type-resolver": "^1.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo_sqlite": "*",
|
||||
"friendsofphp/php-cs-fixer": "^2",
|
||||
"illuminate/config": "^8 || ^9 || ^10",
|
||||
"illuminate/view": "^8 || ^9 || ^10",
|
||||
"friendsofphp/php-cs-fixer": "^3",
|
||||
"illuminate/config": "^9 || ^10",
|
||||
"illuminate/view": "^9 || ^10",
|
||||
"mockery/mockery": "^1.4",
|
||||
"orchestra/testbench": "^6 || ^7 || ^8",
|
||||
"phpunit/phpunit": "^8.5 || ^9",
|
||||
"spatie/phpunit-snapshot-assertions": "^3 || ^4",
|
||||
"orchestra/testbench": "^7 || ^8",
|
||||
"phpunit/phpunit": "^9",
|
||||
"spatie/phpunit-snapshot-assertions": "^4",
|
||||
"vimeo/psalm": "^5.4"
|
||||
},
|
||||
"suggest": {
|
||||
@ -15957,7 +16001,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.14-dev"
|
||||
"dev-master": "2.15-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
@ -15994,7 +16038,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.14.0"
|
||||
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v2.15.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@ -16006,7 +16050,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-02-05T08:16:36+00:00"
|
||||
"time": "2024-02-15T14:23:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/reflection-docblock",
|
||||
@ -16780,50 +16824,6 @@
|
||||
},
|
||||
"time": "2020-07-09T08:09:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hyvor/php-json-exporter",
|
||||
"version": "0.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/hyvor/php-json-exporter.git",
|
||||
"reference": "9fade1856135deaa2d0fc9b3065949019703471d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/hyvor/php-json-exporter/zipball/9fade1856135deaa2d0fc9b3065949019703471d",
|
||||
"reference": "9fade1856135deaa2d0fc9b3065949019703471d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"pestphp/pest": "^1.22",
|
||||
"phpstan/phpstan": "^1.8"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Hyvor\\JsonExporter\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Supun",
|
||||
"email": "supun@hyvor.com"
|
||||
}
|
||||
],
|
||||
"description": "Export large datasets to a JSON file without memory exhaustion",
|
||||
"support": {
|
||||
"issues": "https://github.com/hyvor/php-json-exporter/issues",
|
||||
"source": "https://github.com/hyvor/php-json-exporter/tree/0.0.3"
|
||||
},
|
||||
"time": "2023-04-11T15:12:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laracasts/cypress",
|
||||
"version": "3.0.1",
|
||||
@ -16986,16 +16986,16 @@
|
||||
},
|
||||
{
|
||||
"name": "maximebf/debugbar",
|
||||
"version": "v1.20.1",
|
||||
"version": "v1.20.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/maximebf/php-debugbar.git",
|
||||
"reference": "06ebf922ccedfa4cc43015825697ee8c1fb80f7e"
|
||||
"reference": "484625c23a4fa4f303617f29fcacd42951c9c01d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/06ebf922ccedfa4cc43015825697ee8c1fb80f7e",
|
||||
"reference": "06ebf922ccedfa4cc43015825697ee8c1fb80f7e",
|
||||
"url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/484625c23a4fa4f303617f29fcacd42951c9c01d",
|
||||
"reference": "484625c23a4fa4f303617f29fcacd42951c9c01d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -17046,9 +17046,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/maximebf/php-debugbar/issues",
|
||||
"source": "https://github.com/maximebf/php-debugbar/tree/v1.20.1"
|
||||
"source": "https://github.com/maximebf/php-debugbar/tree/v1.20.2"
|
||||
},
|
||||
"time": "2024-02-13T19:03:14+00:00"
|
||||
"time": "2024-02-15T10:49:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
|
@ -17,8 +17,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => env('APP_VERSION', '5.8.25'),
|
||||
'app_tag' => env('APP_TAG', '5.8.25'),
|
||||
'app_version' => env('APP_VERSION', '5.8.26'),
|
||||
'app_tag' => env('APP_TAG', '5.8.26'),
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', false),
|
||||
|
34
database/migrations/2024_02_16_011055_smtp_configuration.php
Normal file
34
database/migrations/2024_02_16_011055_smtp_configuration.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
|
||||
Schema::table('companies', function (Blueprint $table) {
|
||||
$table->string('smtp_host')->nullable();
|
||||
$table->unsignedInteger('smtp_port')->nullable();
|
||||
$table->string('smtp_encryption')->nullable();
|
||||
$table->text('smtp_username')->nullable();
|
||||
$table->text('smtp_password')->nullable();
|
||||
$table->string('smtp_local_domain')->nullable();
|
||||
$table->boolean('smtp_verify_peer')->default(0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
@ -4925,7 +4925,7 @@ $lang = array(
|
||||
'no_assigned_tasks' => 'No billable tasks for this project',
|
||||
'authorization_failure' => 'Insufficient permissions to perform this action',
|
||||
'authorization_sms_failure' => 'Please verify your account to send emails.',
|
||||
'white_label_body' => 'Thank you for purchasing a white label license. <br><br> Your license key is: <br><br> :license_key',
|
||||
'white_label_body' => 'Thank you for purchasing a white label license. <br><br> Your license key is: <br><br> :license_key <br><br> You can manage your license here: https://invoiceninja.invoicing.co/client/login',
|
||||
'payment_type_Klarna' => 'Klarna',
|
||||
'payment_type_Interac E Transfer' => 'Interac E Transfer',
|
||||
'xinvoice_payable' => 'Payable within :payeddue days net until :paydate',
|
||||
|
@ -1,4 +1,4 @@
|
||||
@component('email.template.admin', ['settings' => $settings, 'logo' => $logo ?? 'https://www.invoiceninja.com/wp-content/uploads/2015/10/logo-white-horizontal-1.png'])
|
||||
@component('email.template.admin', ['settings' => $settings, 'logo' => $logo ?? 'https://pdf.invoicing.co/favicon-v2.png'])
|
||||
{{-- Body --}}
|
||||
{!! $support_message !!}
|
||||
|
||||
|
163
routes/api.php
163
routes/api.php
@ -10,113 +10,114 @@
|
||||
| is assigned the "api" middleware group. Enjoy building your API!
|
||||
|
|
||||
*/
|
||||
use App\Http\Controllers\AccountController;
|
||||
use App\Http\Controllers\ActivityController;
|
||||
use App\Http\Controllers\Auth\ForgotPasswordController;
|
||||
use App\Http\Controllers\Auth\LoginController;
|
||||
use App\Http\Controllers\Auth\PasswordTimeoutController;
|
||||
use App\Http\Controllers\Bank\NordigenController;
|
||||
use App\Http\Controllers\Bank\YodleeController;
|
||||
use App\Http\Controllers\BankIntegrationController;
|
||||
use App\Http\Controllers\BankTransactionController;
|
||||
use App\Http\Controllers\BankTransactionRuleController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\BaseController;
|
||||
use App\Http\Controllers\PingController;
|
||||
use App\Http\Controllers\SmtpController;
|
||||
use App\Http\Controllers\TaskController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\ChartController;
|
||||
use App\Http\Controllers\EmailController;
|
||||
use App\Http\Controllers\QuoteController;
|
||||
use App\Http\Controllers\TokenController;
|
||||
use App\Http\Controllers\ClientController;
|
||||
use App\Http\Controllers\ClientGatewayTokenController;
|
||||
use App\Http\Controllers\ClientStatementController;
|
||||
use App\Http\Controllers\CompanyController;
|
||||
use App\Http\Controllers\CompanyGatewayController;
|
||||
use App\Http\Controllers\CompanyLedgerController;
|
||||
use App\Http\Controllers\CompanyUserController;
|
||||
use App\Http\Controllers\ConnectedAccountController;
|
||||
use App\Http\Controllers\CreditController;
|
||||
use App\Http\Controllers\DesignController;
|
||||
use App\Http\Controllers\DocumentController;
|
||||
use App\Http\Controllers\EmailController;
|
||||
use App\Http\Controllers\EmailHistoryController;
|
||||
use App\Http\Controllers\ExpenseCategoryController;
|
||||
use App\Http\Controllers\ExpenseController;
|
||||
use App\Http\Controllers\ExportController;
|
||||
use App\Http\Controllers\FilterController;
|
||||
use App\Http\Controllers\GroupSettingController;
|
||||
use App\Http\Controllers\HostedMigrationController;
|
||||
use App\Http\Controllers\ImportController;
|
||||
use App\Http\Controllers\ImportJsonController;
|
||||
use App\Http\Controllers\InAppPurchase\AppleController;
|
||||
use App\Http\Controllers\LogoutController;
|
||||
use App\Http\Controllers\SearchController;
|
||||
use App\Http\Controllers\StaticController;
|
||||
use App\Http\Controllers\StripeController;
|
||||
use App\Http\Controllers\TwilioController;
|
||||
use App\Http\Controllers\VendorController;
|
||||
use App\Http\Controllers\AccountController;
|
||||
use App\Http\Controllers\CompanyController;
|
||||
use App\Http\Controllers\ExpenseController;
|
||||
use App\Http\Controllers\InvoiceController;
|
||||
use App\Http\Controllers\LicenseController;
|
||||
use App\Http\Controllers\LogoutController;
|
||||
use App\Http\Controllers\MailgunWebhookController;
|
||||
use App\Http\Controllers\MigrationController;
|
||||
use App\Http\Controllers\OneTimeTokenController;
|
||||
use App\Http\Controllers\PaymentController;
|
||||
use App\Http\Controllers\PaymentNotificationWebhookController;
|
||||
use App\Http\Controllers\PaymentTermController;
|
||||
use App\Http\Controllers\PaymentWebhookController;
|
||||
use App\Http\Controllers\PingController;
|
||||
use App\Http\Controllers\PostMarkController;
|
||||
use App\Http\Controllers\PreviewController;
|
||||
use App\Http\Controllers\PreviewPurchaseOrderController;
|
||||
use App\Http\Controllers\ProductController;
|
||||
use App\Http\Controllers\ProjectController;
|
||||
use App\Http\Controllers\ProtectedDownloadController;
|
||||
use App\Http\Controllers\TaxRateController;
|
||||
use App\Http\Controllers\WebCronController;
|
||||
use App\Http\Controllers\WebhookController;
|
||||
use App\Http\Controllers\ActivityController;
|
||||
use App\Http\Controllers\DocumentController;
|
||||
use App\Http\Controllers\PostMarkController;
|
||||
use App\Http\Controllers\TemplateController;
|
||||
use App\Http\Controllers\MigrationController;
|
||||
use App\Http\Controllers\SchedulerController;
|
||||
use App\Http\Controllers\SubdomainController;
|
||||
use App\Http\Controllers\SystemLogController;
|
||||
use App\Http\Controllers\TwoFactorController;
|
||||
use App\Http\Controllers\Auth\LoginController;
|
||||
use App\Http\Controllers\ImportJsonController;
|
||||
use App\Http\Controllers\SelfUpdateController;
|
||||
use App\Http\Controllers\TaskStatusController;
|
||||
use App\Http\Controllers\Bank\YodleeController;
|
||||
use App\Http\Controllers\CompanyUserController;
|
||||
use App\Http\Controllers\PaymentTermController;
|
||||
use App\PaymentDrivers\PayPalPPCPPaymentDriver;
|
||||
use App\Http\Controllers\EmailHistoryController;
|
||||
use App\Http\Controllers\GroupSettingController;
|
||||
use App\Http\Controllers\OneTimeTokenController;
|
||||
use App\Http\Controllers\SubscriptionController;
|
||||
use App\Http\Controllers\Bank\NordigenController;
|
||||
use App\Http\Controllers\CompanyLedgerController;
|
||||
use App\Http\Controllers\PurchaseOrderController;
|
||||
use App\Http\Controllers\QuoteController;
|
||||
use App\Http\Controllers\TaskSchedulerController;
|
||||
use App\Http\Controllers\CompanyGatewayController;
|
||||
use App\Http\Controllers\MailgunWebhookController;
|
||||
use App\Http\Controllers\PaymentWebhookController;
|
||||
use App\Http\Controllers\RecurringQuoteController;
|
||||
use App\Http\Controllers\BankIntegrationController;
|
||||
use App\Http\Controllers\BankTransactionController;
|
||||
use App\Http\Controllers\ClientStatementController;
|
||||
use App\Http\Controllers\ExpenseCategoryController;
|
||||
use App\Http\Controllers\HostedMigrationController;
|
||||
use App\Http\Controllers\TemplatePreviewController;
|
||||
use App\Http\Controllers\ConnectedAccountController;
|
||||
use App\Http\Controllers\RecurringExpenseController;
|
||||
use App\Http\Controllers\RecurringInvoiceController;
|
||||
use App\Http\Controllers\RecurringQuoteController;
|
||||
use App\Http\Controllers\Reports\ActivityReportController;
|
||||
use App\Http\Controllers\Reports\ARDetailReportController;
|
||||
use App\Http\Controllers\Reports\ARSummaryReportController;
|
||||
use App\Http\Controllers\Reports\ClientBalanceReportController;
|
||||
use App\Http\Controllers\Reports\ClientContactReportController;
|
||||
use App\Http\Controllers\ProtectedDownloadController;
|
||||
use App\Http\Controllers\ClientGatewayTokenController;
|
||||
use App\Http\Controllers\Reports\TaskReportController;
|
||||
use App\Http\Controllers\Auth\ForgotPasswordController;
|
||||
use App\Http\Controllers\BankTransactionRuleController;
|
||||
use App\Http\Controllers\InAppPurchase\AppleController;
|
||||
use App\Http\Controllers\Reports\QuoteReportController;
|
||||
use App\Http\Controllers\Auth\PasswordTimeoutController;
|
||||
use App\Http\Controllers\PreviewPurchaseOrderController;
|
||||
use App\Http\Controllers\Reports\ClientReportController;
|
||||
use App\Http\Controllers\Reports\ClientSalesReportController;
|
||||
use App\Http\Controllers\Reports\CreditReportController;
|
||||
use App\Http\Controllers\Reports\DocumentReportController;
|
||||
use App\Http\Controllers\Reports\ReportExportController;
|
||||
use App\Http\Controllers\Reports\VendorReportController;
|
||||
use App\Http\Controllers\Reports\ExpenseReportController;
|
||||
use App\Http\Controllers\Reports\InvoiceItemReportController;
|
||||
use App\Http\Controllers\Reports\InvoiceReportController;
|
||||
use App\Http\Controllers\Reports\PaymentReportController;
|
||||
use App\Http\Controllers\Reports\ProductReportController;
|
||||
use App\Http\Controllers\Reports\ProductSalesReportController;
|
||||
use App\Http\Controllers\Reports\ProfitAndLossController;
|
||||
use App\Http\Controllers\Reports\PurchaseOrderItemReportController;
|
||||
use App\Http\Controllers\Reports\PurchaseOrderReportController;
|
||||
use App\Http\Controllers\Reports\QuoteItemReportController;
|
||||
use App\Http\Controllers\Reports\QuoteReportController;
|
||||
use App\Http\Controllers\Reports\RecurringInvoiceReportController;
|
||||
use App\Http\Controllers\Reports\ReportExportController;
|
||||
use App\Http\Controllers\Reports\ReportPreviewController;
|
||||
use App\Http\Controllers\Reports\TaskReportController;
|
||||
use App\Http\Controllers\Reports\TaxSummaryReportController;
|
||||
use App\Http\Controllers\Reports\ActivityReportController;
|
||||
use App\Http\Controllers\Reports\ARDetailReportController;
|
||||
use App\Http\Controllers\Reports\DocumentReportController;
|
||||
use App\Http\Controllers\Reports\ARSummaryReportController;
|
||||
use App\Http\Controllers\Reports\QuoteItemReportController;
|
||||
use App\Http\Controllers\Reports\UserSalesReportController;
|
||||
use App\Http\Controllers\Reports\VendorReportController;
|
||||
use App\Http\Controllers\SchedulerController;
|
||||
use App\Http\Controllers\SearchController;
|
||||
use App\Http\Controllers\SelfUpdateController;
|
||||
use App\Http\Controllers\StaticController;
|
||||
use App\Http\Controllers\StripeController;
|
||||
use App\Http\Controllers\SubdomainController;
|
||||
use App\Http\Controllers\SubscriptionController;
|
||||
use App\Http\Controllers\Reports\TaxSummaryReportController;
|
||||
use App\Http\Controllers\Support\Messages\SendingController;
|
||||
use App\Http\Controllers\SystemLogController;
|
||||
use App\Http\Controllers\TaskController;
|
||||
use App\Http\Controllers\TaskSchedulerController;
|
||||
use App\Http\Controllers\TaskStatusController;
|
||||
use App\Http\Controllers\TaxRateController;
|
||||
use App\Http\Controllers\TemplateController;
|
||||
use App\Http\Controllers\TemplatePreviewController;
|
||||
use App\Http\Controllers\TokenController;
|
||||
use App\Http\Controllers\TwilioController;
|
||||
use App\Http\Controllers\TwoFactorController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\VendorController;
|
||||
use App\Http\Controllers\WebCronController;
|
||||
use App\Http\Controllers\WebhookController;
|
||||
use App\PaymentDrivers\PayPalPPCPPaymentDriver;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\Reports\ClientSalesReportController;
|
||||
use App\Http\Controllers\Reports\InvoiceItemReportController;
|
||||
use App\Http\Controllers\PaymentNotificationWebhookController;
|
||||
use App\Http\Controllers\Reports\ProductSalesReportController;
|
||||
use App\Http\Controllers\Reports\ClientBalanceReportController;
|
||||
use App\Http\Controllers\Reports\ClientContactReportController;
|
||||
use App\Http\Controllers\Reports\PurchaseOrderReportController;
|
||||
use App\Http\Controllers\Reports\RecurringInvoiceReportController;
|
||||
use App\Http\Controllers\Reports\PurchaseOrderItemReportController;
|
||||
|
||||
Route::group(['middleware' => ['throttle:api', 'api_secret_check']], function () {
|
||||
Route::post('api/v1/signup', [AccountController::class, 'store'])->name('signup.submit');
|
||||
@ -392,6 +393,8 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale']
|
||||
// Route::post('hooks', [SubscriptionController::class, 'subscribe'])->name('hooks.subscribe');
|
||||
// Route::delete('hooks/{subscription_id}', [SubscriptionController::class, 'unsubscribe'])->name('hooks.unsubscribe');
|
||||
|
||||
Route::post('smtp/check', [SmtpController::class, 'check'])->name('smtp.check')->middleware('throttle:10,1');
|
||||
|
||||
Route::post('stripe/update_payment_methods', [StripeController::class, 'update'])->middleware('password_protected')->name('stripe.update');
|
||||
Route::post('stripe/import_customers', [StripeController::class, 'import'])->middleware('password_protected')->name('stripe.import');
|
||||
|
||||
|
@ -59,6 +59,78 @@ class ClientApiTest extends TestCase
|
||||
Model::reguard();
|
||||
}
|
||||
|
||||
public function testDocumentValidation()
|
||||
{
|
||||
$data = [
|
||||
'name' => 'name of client',
|
||||
'documents' => [],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson("/api/v1/clients",$data)
|
||||
->assertStatus(200);
|
||||
|
||||
}
|
||||
|
||||
public function testDocumentValidationFails()
|
||||
{
|
||||
$data = [
|
||||
'name' => 'name of client',
|
||||
'documents' => 'wut',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson("/api/v1/clients", $data)
|
||||
->assertStatus(422);
|
||||
|
||||
$data = [
|
||||
'name' => 'name of client',
|
||||
'documents' => null,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->postJson("/api/v1/clients", $data)
|
||||
->assertStatus(422);
|
||||
|
||||
}
|
||||
|
||||
public function testDocumentValidationPutFails()
|
||||
{
|
||||
$data = [
|
||||
'name' => 'name of client',
|
||||
'documents' => 'wut',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson("/api/v1/clients/{$this->client->hashed_id}", $data)
|
||||
->assertStatus(422);
|
||||
|
||||
$data = [
|
||||
'name' => 'name of client',
|
||||
'documents' => null,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson("/api/v1/clients/{$this->client->hashed_id}", $data)
|
||||
->assertStatus(422);
|
||||
|
||||
$data = [
|
||||
'name' => 'name of client',
|
||||
'documents' => [],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->putJson("/api/v1/clients/{$this->client->hashed_id}", $data)
|
||||
->assertStatus(200);
|
||||
|
||||
}
|
||||
|
||||
public function testClientDocumentQuery()
|
||||
{
|
||||
|
||||
|
@ -50,6 +50,15 @@ class CompanyTest extends TestCase
|
||||
$this->makeTestData();
|
||||
}
|
||||
|
||||
public function testEnsureStrReplace()
|
||||
{
|
||||
$x = '**********';
|
||||
|
||||
$new_string = str_replace("*", "", $x);
|
||||
|
||||
$this->assertEquals(0, strlen($new_string));
|
||||
}
|
||||
|
||||
public function testCompanyTaxInit()
|
||||
{
|
||||
TaxRate::query()->delete();
|
||||
|
180
tests/Feature/Payments/AutoUnappliedPaymentTest.php
Normal file
180
tests/Feature/Payments/AutoUnappliedPaymentTest.php
Normal file
@ -0,0 +1,180 @@
|
||||
<?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://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace Tests\Feature\Payments;
|
||||
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\Factory\InvoiceFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Tests\MockUnitData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
class AutoUnappliedPaymentTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
use MockUnitData;
|
||||
|
||||
protected function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Session::start();
|
||||
|
||||
$this->faker = \Faker\Factory::create();
|
||||
|
||||
Model::reguard();
|
||||
|
||||
$this->makeTestData();
|
||||
// $this->withoutExceptionHandling();
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
);
|
||||
}
|
||||
|
||||
public function testUnappliedPaymentsAreEnabled()
|
||||
{
|
||||
|
||||
$settings = ClientSettings::defaults();
|
||||
$settings->use_unapplied_payment = 'always';
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'settings' => $settings,
|
||||
]);
|
||||
|
||||
$this->assertEquals('always', $client->settings->use_unapplied_payment);
|
||||
|
||||
$invoice = Invoice::factory()->for($client)->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'auto_bill_enabled' => true,
|
||||
'client_id' => $client->id,
|
||||
]);
|
||||
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
|
||||
$payment = Payment::factory()->for($client)->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $client->id,
|
||||
'amount' => 100,
|
||||
'applied' => 0,
|
||||
'refunded' => 0,
|
||||
'status_id' => Payment::STATUS_COMPLETED,
|
||||
'is_deleted' => 0,
|
||||
]);
|
||||
|
||||
$invoice->service()->markSent()->save();
|
||||
|
||||
$this->assertGreaterThan(0, $invoice->balance);
|
||||
|
||||
nlog($invoice->balance);
|
||||
|
||||
try{
|
||||
$invoice->service()->autoBill()->save();
|
||||
}
|
||||
catch(\Exception $e){
|
||||
|
||||
}
|
||||
|
||||
$invoice = $invoice->fresh();
|
||||
$payment = $payment->fresh();
|
||||
|
||||
nlog($invoice->toArray());
|
||||
nlog($payment->toArray());
|
||||
|
||||
$this->assertEquals($payment->applied, $invoice->paid_to_date);
|
||||
$this->assertGreaterThan(2, $invoice->status_id);
|
||||
$this->assertGreaterThan(0, $payment->applied);
|
||||
|
||||
// $this->assertEquals(Invoice::STATUS_PAID, $invoice->status_id);
|
||||
// $this->assertEquals(0, $invoice->balance);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testUnappliedPaymentsAreDisabled()
|
||||
{
|
||||
|
||||
$settings = ClientSettings::defaults();
|
||||
$settings->use_unapplied_payment = 'off';
|
||||
|
||||
$client = Client::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'settings' => $settings,
|
||||
]);
|
||||
|
||||
$this->assertEquals('off', $client->settings->use_unapplied_payment);
|
||||
|
||||
$invoice = Invoice::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $client->id,
|
||||
'auto_bill_enabled' => true,
|
||||
'status_id' => 2
|
||||
]);
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
$invoice_balance = $invoice->balance;
|
||||
|
||||
$payment = Payment::factory()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $client->id,
|
||||
'amount' => 100,
|
||||
'applied' => 0,
|
||||
'refunded' => 0,
|
||||
'status_id' => Payment::STATUS_COMPLETED
|
||||
]);
|
||||
|
||||
$invoice->service()->markSent()->save();
|
||||
|
||||
$this->assertGreaterThan(0, $invoice->balance);
|
||||
|
||||
try {
|
||||
$invoice->service()->autoBill()->save();
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
$invoice = $invoice->fresh();
|
||||
$payment = $payment->fresh();
|
||||
|
||||
$this->assertEquals($invoice_balance, $invoice->balance);
|
||||
$this->assertEquals(0, $payment->applied);
|
||||
$this->assertEquals(2, $invoice->status_id);
|
||||
$this->assertEquals(0, $invoice->paid_to_date);
|
||||
$this->assertEquals($invoice->amount, $invoice->balance);
|
||||
|
||||
// $this->assertEquals($payment->applied, $invoice->paid_to_date);
|
||||
// $this->assertEquals(2, $invoice->status_id);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -2,9 +2,19 @@
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use App\Utils\Traits\AppSetup;
|
||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
use CreatesApplication;
|
||||
use AppSetup;
|
||||
|
||||
protected function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->buildCache(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user