mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
commit
54cdb8c91d
6
.github/workflows/phpunit.yml
vendored
6
.github/workflows/phpunit.yml
vendored
@ -81,9 +81,9 @@ jobs:
|
|||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
extensions: mysql, mysqlnd, sqlite3, bcmath, gmp, gd, curl, zip, openssl, mbstring, xml, redis
|
extensions: mysql, mysqlnd, sqlite3, bcmath, gmp, gd, curl, zip, openssl, mbstring, xml, redis, :psr
|
||||||
|
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: v5-develop
|
ref: v5-develop
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
@ -96,7 +96,7 @@ jobs:
|
|||||||
id: composer-cache
|
id: composer-cache
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.composer-cache.outputs.dir }}
|
path: ${{ steps.composer-cache.outputs.dir }}
|
||||||
key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
|
key: ${{ runner.os }}-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||||
|
@ -1 +1 @@
|
|||||||
5.7.57
|
5.7.58
|
@ -127,6 +127,7 @@ class CheckData extends Command
|
|||||||
$this->checkCompanyTokens();
|
$this->checkCompanyTokens();
|
||||||
$this->checkUserState();
|
$this->checkUserState();
|
||||||
$this->checkContactEmailAndSendEmailStatus();
|
$this->checkContactEmailAndSendEmailStatus();
|
||||||
|
$this->checkPaymentCurrency();
|
||||||
|
|
||||||
if (Ninja::isHosted()) {
|
if (Ninja::isHosted()) {
|
||||||
$this->checkAccountStatuses();
|
$this->checkAccountStatuses();
|
||||||
@ -325,6 +326,7 @@ class CheckData extends Command
|
|||||||
->whereNull('contact_key')
|
->whereNull('contact_key')
|
||||||
->orderBy('id')
|
->orderBy('id')
|
||||||
->get(['id']);
|
->get(['id']);
|
||||||
|
|
||||||
$this->logMessage($contacts->count().' contacts without a contact_key');
|
$this->logMessage($contacts->count().' contacts without a contact_key');
|
||||||
|
|
||||||
if ($contacts->count() > 0) {
|
if ($contacts->count() > 0) {
|
||||||
@ -341,21 +343,9 @@ class CheckData extends Command
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for missing contacts
|
$vendors = Vendor::withTrashed()->doesntHave('contacts');
|
||||||
$vendors = DB::table('vendors')
|
|
||||||
->leftJoin('vendor_contacts', function ($join) {
|
|
||||||
$join->on('vendor_contacts.vendor_id', '=', 'vendors.id')
|
|
||||||
->whereNull('vendor_contacts.deleted_at');
|
|
||||||
})
|
|
||||||
->groupBy('vendors.id', 'vendors.user_id', 'vendors.company_id')
|
|
||||||
->havingRaw('count(vendor_contacts.id) = 0');
|
|
||||||
|
|
||||||
if ($this->option('vendor_id')) {
|
|
||||||
$vendors->where('vendors.id', '=', $this->option('vendor_id'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$vendors = $vendors->get(['vendors.id', 'vendors.user_id', 'vendors.company_id']);
|
|
||||||
$this->logMessage($vendors->count().' vendors without any contacts');
|
$this->logMessage($vendors->count().' vendors without any contacts');
|
||||||
|
|
||||||
if ($vendors->count() > 0) {
|
if ($vendors->count() > 0) {
|
||||||
@ -377,23 +367,6 @@ class CheckData extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function checkFailedJobs()
|
|
||||||
{
|
|
||||||
if (config('ninja.testvars.travis')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$queueDB = config('queue.connections.database.connection');
|
|
||||||
$count = DB::connection($queueDB)->table('failed_jobs')->count();
|
|
||||||
|
|
||||||
if ($count > 25) {
|
|
||||||
$this->isValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->logMessage($count.' failed jobs');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkInvitations()
|
private function checkInvitations()
|
||||||
{
|
{
|
||||||
$invoices = DB::table('invoices')
|
$invoices = DB::table('invoices')
|
||||||
@ -683,6 +656,8 @@ class CheckData extends Command
|
|||||||
->count();
|
->count();
|
||||||
|
|
||||||
if ($count == 0) {
|
if ($count == 0) {
|
||||||
|
$this->isValid = false;
|
||||||
|
|
||||||
//factor in over payments to the client balance
|
//factor in over payments to the client balance
|
||||||
$over_payment = Payment::where('client_id', $client->id)
|
$over_payment = Payment::where('client_id', $client->id)
|
||||||
->where('is_deleted', 0)
|
->where('is_deleted', 0)
|
||||||
@ -745,6 +720,8 @@ class CheckData extends Command
|
|||||||
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
|
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
|
||||||
|
|
||||||
if (number_format($invoice_balance, 4) != number_format($client->balance, 4)) {
|
if (number_format($invoice_balance, 4) != number_format($client->balance, 4)) {
|
||||||
|
$this->isValid = false;
|
||||||
|
|
||||||
$this->wrong_balances++;
|
$this->wrong_balances++;
|
||||||
$ledger_balance = $ledger ? $ledger->balance : 0;
|
$ledger_balance = $ledger ? $ledger->balance : 0;
|
||||||
|
|
||||||
@ -893,6 +870,8 @@ class CheckData extends Command
|
|||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
if ($payment) {
|
if ($payment) {
|
||||||
|
$this->isValid = false;
|
||||||
|
|
||||||
$this->logMessage("I found a payment for {$account->key}");
|
$this->logMessage("I found a payment for {$account->key}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1008,6 +987,7 @@ class CheckData extends Command
|
|||||||
BankTransaction::with('payment')->withTrashed()->where('invoice_ids', ',,,,,,,,')->cursor()->each(function ($bt) {
|
BankTransaction::with('payment')->withTrashed()->where('invoice_ids', ',,,,,,,,')->cursor()->each(function ($bt) {
|
||||||
|
|
||||||
if($bt->payment->exists()) {
|
if($bt->payment->exists()) {
|
||||||
|
$this->isValid = false;
|
||||||
|
|
||||||
$bt->invoice_ids = collect($bt->payment->invoices)->pluck('hashed_id')->implode(',');
|
$bt->invoice_ids = collect($bt->payment->invoices)->pluck('hashed_id')->implode(',');
|
||||||
$bt->save();
|
$bt->save();
|
||||||
@ -1038,4 +1018,30 @@ class CheckData extends Command
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkPaymentCurrency()
|
||||||
|
{
|
||||||
|
$p = Payment::with('company','client')
|
||||||
|
->withTrashed()
|
||||||
|
->where('currency_id', '')
|
||||||
|
->orWhereNull('currency_id');
|
||||||
|
|
||||||
|
$this->logMessage($p->count() . " Payments with No currency set");
|
||||||
|
|
||||||
|
if($p->count() != 0)
|
||||||
|
$this->isValid = false;
|
||||||
|
|
||||||
|
if ($this->option('fix') == 'true') {
|
||||||
|
|
||||||
|
$p->cursor()->each(function ($c) {
|
||||||
|
$c->currency_id = $c->client->settings->currency_id ?? $c->company->settings->currency_id;
|
||||||
|
$c->saveQuietly();
|
||||||
|
|
||||||
|
$this->logMessage("Fixing - {$c->id}");
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ class CreditFilters extends QueryFilters
|
|||||||
* @param string $value The credit status as seen by the client
|
* @param string $value The credit status as seen by the client
|
||||||
* @return Builder
|
* @return Builder
|
||||||
*/
|
*/
|
||||||
public function credit_status(string $value = ''): Builder
|
public function client_status(string $value = ''): Builder
|
||||||
{
|
{
|
||||||
if (strlen($value) == 0) {
|
if (strlen($value) == 0) {
|
||||||
return $this->builder;
|
return $this->builder;
|
||||||
|
@ -147,11 +147,15 @@ class InvoiceFilters extends QueryFilters
|
|||||||
public function upcoming(): Builder
|
public function upcoming(): Builder
|
||||||
{
|
{
|
||||||
return $this->builder->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
|
return $this->builder->whereIn('status_id', [Invoice::STATUS_PARTIAL, Invoice::STATUS_SENT])
|
||||||
->where(function ($query) {
|
->whereNull('due_date')
|
||||||
$query->whereNull('due_date')
|
->orWhere(function ($q) {
|
||||||
->orWhere('due_date', '>', now());
|
$q->where('due_date', '>=', now()->startOfDay()->subSecond())->where('partial', 0);
|
||||||
})
|
})
|
||||||
->orderBy('due_date', 'ASC');
|
->orWhere(function ($q) {
|
||||||
|
$q->where('partial_due_date', '>=', now()->startOfDay()->subSecond())->where('partial', '>', 0);
|
||||||
|
})
|
||||||
|
->orderByRaw('ISNULL(due_date), due_date '. 'desc')
|
||||||
|
->orderByRaw('ISNULL(partial_due_date), partial_due_date '. 'desc');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,10 +55,18 @@ class PaymentResponseRequest extends FormRequest
|
|||||||
'pay_with_token' => ($this->pay_with_token === 'true' || $this->pay_with_token === true) ? true : false,
|
'pay_with_token' => ($this->pay_with_token === 'true' || $this->pay_with_token === true) ? true : false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($this->has('payment_method_id')) {
|
||||||
|
|
||||||
|
$this->merge([
|
||||||
|
'payment_method_id' => preg_replace('~\D~', '', $this->payment_method_id),
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function shouldUseToken(): bool
|
public function shouldUseToken(): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->token;
|
return (bool) $this->token;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -60,9 +60,9 @@ class UpdateCreditRequest extends Request
|
|||||||
$rules['file'] = $this->file_validation;
|
$rules['file'] = $this->file_validation;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->number) {
|
$rules['number'] = ['bail', 'sometimes', Rule::unique('credits')->where('company_id', $user->company()->id)->ignore($this->credit->id)];
|
||||||
$rules['number'] = Rule::unique('credits')->where('company_id', $user->company()->id)->ignore($this->credit->id);
|
|
||||||
}
|
$rules['client_id'] = ['bail', 'sometimes',Rule::in([$this->credit->client_id])];
|
||||||
|
|
||||||
$rules['line_items'] = 'array';
|
$rules['line_items'] = 'array';
|
||||||
$rules['discount'] = 'sometimes|numeric';
|
$rules['discount'] = 'sometimes|numeric';
|
||||||
|
@ -37,6 +37,7 @@ class ImportRequest extends Request
|
|||||||
'column_map' => 'required_with:hash|array',
|
'column_map' => 'required_with:hash|array',
|
||||||
'skip_header' => 'required_with:hash|boolean',
|
'skip_header' => 'required_with:hash|boolean',
|
||||||
'files.*' => 'file|mimes:csv,txt',
|
'files.*' => 'file|mimes:csv,txt',
|
||||||
|
'bank_integration_id' => 'bail|required_if:column_map,bank_transaction|min:2'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,10 @@ class PreImportRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function authorize() : bool
|
public function authorize() : bool
|
||||||
{
|
{
|
||||||
return auth()->user()->isAdmin();
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $user->isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
|
@ -74,7 +74,7 @@ class StoreInvoiceRequest extends Request
|
|||||||
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
|
$rules['tax_name3'] = 'bail|sometimes|string|nullable';
|
||||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||||
$rules['partial'] = 'bail|sometimes|nullable|numeric|gte:0';
|
$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'];
|
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
@ -89,6 +89,10 @@ class StoreInvoiceRequest extends Request
|
|||||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isset($input['partial']) && $input['partial'] == 0 && isset($input['partial_due_date'])) {
|
||||||
|
$input['partial_due_date'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
$input['amount'] = 0;
|
$input['amount'] = 0;
|
||||||
$input['balance'] = 0;
|
$input['balance'] = 0;
|
||||||
|
|
||||||
|
@ -59,12 +59,11 @@ class UpdateInvoiceRequest extends Request
|
|||||||
|
|
||||||
$rules['id'] = new LockedInvoiceRule($this->invoice);
|
$rules['id'] = new LockedInvoiceRule($this->invoice);
|
||||||
|
|
||||||
if ($this->number) {
|
$rules['number'] = ['bail', 'sometimes', Rule::unique('invoices')->where('company_id', $user->company()->id)->ignore($this->invoice->id)];
|
||||||
$rules['number'] = Rule::unique('invoices')->where('company_id', $user->company()->id)->ignore($this->invoice->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$rules['is_amount_discount'] = ['boolean'];
|
$rules['is_amount_discount'] = ['boolean'];
|
||||||
|
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->invoice->client_id])];
|
||||||
$rules['line_items'] = 'array';
|
$rules['line_items'] = 'array';
|
||||||
$rules['discount'] = 'sometimes|numeric';
|
$rules['discount'] = 'sometimes|numeric';
|
||||||
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
|
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
|
||||||
@ -77,7 +76,7 @@ class UpdateInvoiceRequest extends Request
|
|||||||
$rules['status_id'] = 'bail|sometimes|not_in:5'; //do not allow cancelled invoices to be modfified.
|
$rules['status_id'] = 'bail|sometimes|not_in:5'; //do not allow cancelled invoices to be modfified.
|
||||||
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
$rules['exchange_rate'] = 'bail|sometimes|numeric';
|
||||||
$rules['partial'] = 'bail|sometimes|nullable|numeric';
|
$rules['partial'] = 'bail|sometimes|nullable|numeric';
|
||||||
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date'];
|
$rules['partial_due_date'] = ['bail', 'sometimes', 'exclude_if:partial,0', Rule::requiredIf(fn () => $this->partial > 0), 'date', 'before:due_date'];
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
}
|
}
|
||||||
@ -90,6 +89,10 @@ class UpdateInvoiceRequest extends Request
|
|||||||
|
|
||||||
$input['id'] = $this->invoice->id;
|
$input['id'] = $this->invoice->id;
|
||||||
|
|
||||||
|
if(isset($input['partial']) && $input['partial'] == 0 && isset($input['partial_due_date'])) {
|
||||||
|
$input['partial_due_date'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($input['line_items']) && is_array($input['line_items'])) {
|
if (isset($input['line_items']) && is_array($input['line_items'])) {
|
||||||
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
$input['line_items'] = isset($input['line_items']) ? $this->cleanItems($input['line_items']) : [];
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,10 @@ class UpdatePurchaseOrderRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function authorize() : bool
|
public function authorize() : bool
|
||||||
{
|
{
|
||||||
return auth()->user()->can('edit', $this->purchase_order);
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $user->can('edit', $this->purchase_order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,11 +43,13 @@ class UpdatePurchaseOrderRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
if ($this->number) {
|
$rules['number'] = ['bail', 'sometimes', Rule::unique('purchase_orders')->where('company_id', $user->company()->id)->ignore($this->purchase_order->id)];
|
||||||
$rules['number'] = Rule::unique('purchase_orders')->where('company_id', auth()->user()->company()->id)->ignore($this->purchase_order->id);
|
$rules['vendor_id'] = ['bail', 'sometimes', Rule::in([$this->purchase_order->vendor_id])];
|
||||||
}
|
|
||||||
|
|
||||||
$rules['line_items'] = 'array';
|
$rules['line_items'] = 'array';
|
||||||
$rules['discount'] = 'sometimes|numeric';
|
$rules['discount'] = 'sometimes|numeric';
|
||||||
|
@ -30,11 +30,16 @@ class UpdateQuoteRequest extends Request
|
|||||||
*/
|
*/
|
||||||
public function authorize() : bool
|
public function authorize() : bool
|
||||||
{
|
{
|
||||||
return auth()->user()->can('edit', $this->quote);
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
return $user->can('edit', $this->quote);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rules()
|
public function rules()
|
||||||
{
|
{
|
||||||
|
/** @var \App\Models\User $user */
|
||||||
|
$user = auth()->user();
|
||||||
$rules = [];
|
$rules = [];
|
||||||
|
|
||||||
if ($this->file('documents') && is_array($this->file('documents'))) {
|
if ($this->file('documents') && is_array($this->file('documents'))) {
|
||||||
@ -50,9 +55,9 @@ class UpdateQuoteRequest extends Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ($this->number) {
|
$rules['number'] = ['bail', 'sometimes', Rule::unique('quotes')->where('company_id', $user->company()->id)->ignore($this->quote->id)];
|
||||||
$rules['number'] = Rule::unique('quotes')->where('company_id', auth()->user()->company()->id)->ignore($this->quote->id);
|
|
||||||
}
|
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->quote->client_id])];
|
||||||
|
|
||||||
$rules['line_items'] = 'array';
|
$rules['line_items'] = 'array';
|
||||||
$rules['discount'] = 'sometimes|numeric';
|
$rules['discount'] = 'sometimes|numeric';
|
||||||
|
@ -56,9 +56,10 @@ class UpdateRecurringInvoiceRequest extends Request
|
|||||||
$rules['file'] = $this->file_validation;
|
$rules['file'] = $this->file_validation;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->number) {
|
$rules['number'] = ['bail', 'sometimes', Rule::unique('recurring_invoices')->where('company_id', $user->company()->id)->ignore($this->recurring_invoice->id)];
|
||||||
$rules['number'] = Rule::unique('recurring_invoices')->where('company_id', $user->company()->id)->ignore($this->recurring_invoice->id);
|
|
||||||
}
|
|
||||||
|
$rules['client_id'] = ['bail', 'sometimes', Rule::in([$this->recurring_invoice->client_id])];
|
||||||
|
|
||||||
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
|
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
|
||||||
$rules['tax_rate1'] = 'bail|sometimes|numeric';
|
$rules['tax_rate1'] = 'bail|sometimes|numeric';
|
||||||
|
@ -49,6 +49,7 @@ class StoreSchedulerRequest extends Request
|
|||||||
'parameters.entity_id' => ['bail', 'sometimes', 'string'],
|
'parameters.entity_id' => ['bail', 'sometimes', 'string'],
|
||||||
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,clients,client_contacts,credits,documents,expenses,invoices,invoice_items,quotes,quote_items,recurring_invoices,payments,products,tasks'],
|
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,clients,client_contacts,credits,documents,expenses,invoices,invoice_items,quotes,quote_items,recurring_invoices,payments,products,tasks'],
|
||||||
'parameters.date_key' => ['bail','sometimes', 'string'],
|
'parameters.date_key' => ['bail','sometimes', 'string'],
|
||||||
|
'parameters.status' => ['bail','sometimes', 'string', 'in:all,draft,paid,unpaid,overdue'],
|
||||||
];
|
];
|
||||||
|
|
||||||
return $rules;
|
return $rules;
|
||||||
|
@ -353,6 +353,10 @@ class Activity extends StaticModel
|
|||||||
return $this->belongsTo(Expense::class)->withTrashed();
|
return $this->belongsTo(Expense::class)->withTrashed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function account(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Account::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function recurring_expense(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
public function recurring_expense(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
{
|
{
|
||||||
|
@ -139,9 +139,8 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
|||||||
public function init(): self
|
public function init(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
// $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';
|
|
||||||
$this->api_endpoint_url = 'https://api-m.paypal.com';
|
$this->api_endpoint_url = 'https://api-m.paypal.com';
|
||||||
|
// $this->api_endpoint_url = 'https://api-m.sandbox.paypal.com';
|
||||||
$secret = config('ninja.paypal.secret');
|
$secret = config('ninja.paypal.secret');
|
||||||
$client_id = config('ninja.paypal.client_id');
|
$client_id = config('ninja.paypal.client_id');
|
||||||
|
|
||||||
@ -238,30 +237,23 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getClientToken(): string
|
|
||||||
{
|
|
||||||
|
|
||||||
$r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']);
|
|
||||||
|
|
||||||
if($r->successful()) {
|
|
||||||
return $r->json()['client_token'];
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function processPaymentResponse($request)
|
public function processPaymentResponse($request)
|
||||||
{
|
{
|
||||||
|
|
||||||
$request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']);
|
$request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']);
|
||||||
$response = json_decode($request['gateway_response'], true);
|
$response = json_decode($request['gateway_response'], true);
|
||||||
|
|
||||||
|
//capture
|
||||||
|
$orderID = $response['orderID'];
|
||||||
|
$r = $this->gatewayRequest("/v2/checkout/orders/{$orderID}/capture", 'post', ['body' => '']);
|
||||||
|
|
||||||
|
$response = $r;
|
||||||
|
|
||||||
if(isset($response['status']) && $response['status'] == 'COMPLETED' && isset($response['purchase_units'])) {
|
if(isset($response['status']) && $response['status'] == 'COMPLETED' && isset($response['purchase_units'])) {
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'payment_type' => $this->getPaymentMethod($request->gateway_type_id),
|
'payment_type' => $this->getPaymentMethod($request->gateway_type_id),
|
||||||
'amount' => $response['purchase_units'][0]['amount']['value'],
|
'amount' => $response['purchase_units'][0]['payments']['captures'][0]['amount']['value'],
|
||||||
'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'],
|
'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'],
|
||||||
'gateway_type_id' => GatewayType::PAYPAL,
|
'gateway_type_id' => GatewayType::PAYPAL,
|
||||||
];
|
];
|
||||||
@ -300,6 +292,21 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private function getClientToken(): string
|
||||||
|
{
|
||||||
|
|
||||||
|
$r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']);
|
||||||
|
|
||||||
|
if($r->successful()) {
|
||||||
|
return $r->json()['client_token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private function paymentSource(): array
|
private function paymentSource(): array
|
||||||
{
|
{
|
||||||
/** we only need to support paypal as payment source until as we are only using hosted payment buttons */
|
/** we only need to support paypal as payment source until as we are only using hosted payment buttons */
|
||||||
@ -307,36 +314,6 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**@deprecated v5.7.54 */
|
|
||||||
private function injectVenmoPaymentSource(): array
|
|
||||||
{
|
|
||||||
|
|
||||||
return [
|
|
||||||
"venmo" => [
|
|
||||||
"email_address" => $this->client->present()->email(),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**@deprecated v5.7.54 */
|
|
||||||
private function injectCardPaymentSource(): array
|
|
||||||
{
|
|
||||||
|
|
||||||
return [
|
|
||||||
"card" => [
|
|
||||||
|
|
||||||
"name" => $this->client->present()->name(),
|
|
||||||
"email_address" => $this->client->present()->email(),
|
|
||||||
"billing_address" => $this->getBillingAddress(),
|
|
||||||
"experience_context"=> [
|
|
||||||
"user_action" => "PAY_NOW"
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private function injectPayPalPaymentSource(): array
|
private function injectPayPalPaymentSource(): array
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -462,10 +439,23 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
|||||||
->withHeaders($this->getHeaders($headers))
|
->withHeaders($this->getHeaders($headers))
|
||||||
->{$verb}("{$this->api_endpoint_url}{$uri}", $data);
|
->{$verb}("{$this->api_endpoint_url}{$uri}", $data);
|
||||||
|
|
||||||
|
nlog($r);
|
||||||
|
nlog($r->json());
|
||||||
|
|
||||||
if($r->successful()) {
|
if($r->successful()) {
|
||||||
return $r;
|
return $r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SystemLogger::dispatch(
|
||||||
|
['response' => $r->body()],
|
||||||
|
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||||
|
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||||
|
SystemLog::TYPE_PAYPAL,
|
||||||
|
$this->client,
|
||||||
|
$this->client->company,
|
||||||
|
);
|
||||||
|
|
||||||
throw new PaymentFailed("Gateway failure - {$r->body()}", 401);
|
throw new PaymentFailed("Gateway failure - {$r->body()}", 401);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -288,7 +288,7 @@ class EmailDefaults
|
|||||||
$documents = [];
|
$documents = [];
|
||||||
|
|
||||||
/* Return early if the user cannot attach documents */
|
/* Return early if the user cannot attach documents */
|
||||||
if (!$this->email->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)) {
|
if (!$this->email->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT) || $this->email->email_object->email_template_subject == 'email_subject_statement') {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,10 +715,8 @@ class SubscriptionService
|
|||||||
nlog($response);
|
nlog($response);
|
||||||
|
|
||||||
if ($credit) {
|
if ($credit) {
|
||||||
// return $this->handleRedirect('/client/credits/'.$credit->hashed_id);
|
|
||||||
return '/client/credits/'.$credit->hashed_id;
|
return '/client/credits/'.$credit->hashed_id;
|
||||||
} else {
|
} else {
|
||||||
// return $this->handleRedirect('/client/credits');
|
|
||||||
return '/client/credits';
|
return '/client/credits';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1492,12 +1490,12 @@ class SubscriptionService
|
|||||||
* Handles redirecting the user
|
* Handles redirecting the user
|
||||||
*/
|
*/
|
||||||
private function handleRedirect($default_redirect)
|
private function handleRedirect($default_redirect)
|
||||||
{
|
{
|
||||||
if (array_key_exists('return_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['return_url']) >=1) {
|
if (array_key_exists('return_url', $this->subscription->webhook_configuration) && strlen($this->subscription->webhook_configuration['return_url']) >=1) {
|
||||||
return redirect($this->subscription->webhook_configuration['return_url']);
|
return redirect($this->subscription->webhook_configuration['return_url'])->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect($default_redirect);
|
return redirect($default_redirect)->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
"setasign/fpdf": "^1.8",
|
"setasign/fpdf": "^1.8",
|
||||||
"setasign/fpdi": "^2.3",
|
"setasign/fpdi": "^2.3",
|
||||||
"shopify/shopify-api": "^4.3",
|
"shopify/shopify-api": "^4.3",
|
||||||
"socialiteproviders/apple": "^5.2",
|
"socialiteproviders/apple": "dev-master",
|
||||||
"socialiteproviders/microsoft": "^4.1",
|
"socialiteproviders/microsoft": "^4.1",
|
||||||
"spatie/laravel-data": "^3.5",
|
"spatie/laravel-data": "^3.5",
|
||||||
"sprain/swiss-qr-bill": "^4.3",
|
"sprain/swiss-qr-bill": "^4.3",
|
||||||
@ -113,7 +113,7 @@
|
|||||||
"laracasts/cypress": "^3.0",
|
"laracasts/cypress": "^3.0",
|
||||||
"mockery/mockery": "^1.4.4",
|
"mockery/mockery": "^1.4.4",
|
||||||
"nunomaduro/collision": "^7.0",
|
"nunomaduro/collision": "^7.0",
|
||||||
"nunomaduro/larastan": "^2.0",
|
"larastan/larastan": "^2",
|
||||||
"phpstan/phpstan": "^1.9",
|
"phpstan/phpstan": "^1.9",
|
||||||
"phpunit/phpunit": "^10.0",
|
"phpunit/phpunit": "^10.0",
|
||||||
"spatie/laravel-ignition": "^2.0",
|
"spatie/laravel-ignition": "^2.0",
|
||||||
@ -171,6 +171,12 @@
|
|||||||
"php-http/discovery": true
|
"php-http/discovery": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "vcs",
|
||||||
|
"url": "https://github.com/turbo124/apple"
|
||||||
|
}
|
||||||
|
],
|
||||||
"minimum-stability": "dev",
|
"minimum-stability": "dev",
|
||||||
"prefer-stable": true
|
"prefer-stable": true
|
||||||
}
|
}
|
1202
composer.lock
generated
1202
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -17,8 +17,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => env('APP_VERSION', '5.7.57'),
|
'app_version' => env('APP_VERSION', '5.7.58'),
|
||||||
'app_tag' => env('APP_TAG', '5.7.57'),
|
'app_tag' => env('APP_TAG', '5.7.58'),
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', false),
|
'api_secret' => env('API_SECRET', false),
|
||||||
|
@ -3372,7 +3372,7 @@ $lang = array(
|
|||||||
'credit_number_counter' => 'Credit Number Counter',
|
'credit_number_counter' => 'Credit Number Counter',
|
||||||
'reset_counter_date' => 'Reset Counter Date',
|
'reset_counter_date' => 'Reset Counter Date',
|
||||||
'counter_padding' => 'Counter Padding',
|
'counter_padding' => 'Counter Padding',
|
||||||
'shared_invoice_quote_counter' => 'Share Invoice Quote Counter',
|
'shared_invoice_quote_counter' => 'Share Invoice/Quote Counter',
|
||||||
'default_tax_name_1' => 'Default Tax Name 1',
|
'default_tax_name_1' => 'Default Tax Name 1',
|
||||||
'default_tax_rate_1' => 'Default Tax Rate 1',
|
'default_tax_rate_1' => 'Default Tax Rate 1',
|
||||||
'default_tax_name_2' => 'Default Tax Name 2',
|
'default_tax_name_2' => 'Default Tax Name 2',
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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
|
|
||||||
*/class t{constructor(){this.button=document.querySelector("#pay-button")}init(){this.frames=Frames.init(document.querySelector("meta[name=public-key]").content)}handle(){this.init(),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,e=>{this.button.disabled=!Frames.isCardValid()}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,e=>{document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e),document.getElementById("server_response").submit()}),document.querySelector("#authorization-form").addEventListener("submit",e=>{this.button.disabled=!0,e.preventDefault(),Frames.submitCard()})}}new t().handle();
|
|
@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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
|
|
||||||
*/class o{constructor(){this.tokens=[]}mountFrames(){console.log("Mount checkout frames..")}handlePaymentUsingToken(t){document.getElementById("checkout--container").classList.add("hidden"),document.getElementById("pay-now-with-token--container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=t.target.dataset.token}handlePaymentUsingCreditCard(t){document.getElementById("checkout--container").classList.remove("hidden"),document.getElementById("pay-now-with-token--container").classList.add("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value="";const e=document.getElementById("pay-button"),a=document.querySelector('meta[name="public-key"]').content??"",d=document.getElementById("payment-form");Frames.init(a),Frames.addEventHandler(Frames.Events.CARD_VALIDATION_CHANGED,function(n){e.disabled=!Frames.isCardValid()}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED,function(n){pay.button.disabled=!1}),Frames.addEventHandler(Frames.Events.CARD_TOKENIZED,function(n){e.disabled=!0,document.querySelector('input[name="gateway_response"]').value=JSON.stringify(n),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()}),d.addEventListener("submit",function(n){n.preventDefault(),Frames.submitCard()})}completePaymentUsingToken(t){let e=document.getElementById("pay-now-with-token");e.disabled=!0,e.querySelector("svg").classList.remove("hidden"),e.querySelector("span").classList.add("hidden"),document.getElementById("server-response").submit()}handle(){this.handlePaymentUsingCreditCard(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach(t=>t.addEventListener("click",this.handlePaymentUsingToken)),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",this.handlePaymentUsingCreditCard),document.getElementById("pay-now-with-token").addEventListener("click",this.completePaymentUsingToken)}}new o().handle();
|
|
@ -41,7 +41,7 @@
|
|||||||
"src": "resources/js/clients/payment_methods/authorize-authorize-card.js"
|
"src": "resources/js/clients/payment_methods/authorize-authorize-card.js"
|
||||||
},
|
},
|
||||||
"resources/js/clients/payment_methods/authorize-checkout-card.js": {
|
"resources/js/clients/payment_methods/authorize-checkout-card.js": {
|
||||||
"file": "assets/authorize-checkout-card-9d660182.js",
|
"file": "assets/authorize-checkout-card-d561d355.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/js/clients/payment_methods/authorize-checkout-card.js"
|
"src": "resources/js/clients/payment_methods/authorize-checkout-card.js"
|
||||||
},
|
},
|
||||||
@ -71,7 +71,7 @@
|
|||||||
"src": "resources/js/clients/payments/braintree-paypal.js"
|
"src": "resources/js/clients/payments/braintree-paypal.js"
|
||||||
},
|
},
|
||||||
"resources/js/clients/payments/checkout-credit-card.js": {
|
"resources/js/clients/payments/checkout-credit-card.js": {
|
||||||
"file": "assets/checkout-credit-card-906a30cd.js",
|
"file": "assets/checkout-credit-card-8a04938c.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/js/clients/payments/checkout-credit-card.js"
|
"src": "resources/js/clients/payments/checkout-credit-card.js"
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,9 @@
|
|||||||
|
|
||||||
class CheckoutCreditCardAuthorization {
|
class CheckoutCreditCardAuthorization {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.button = document.querySelector('#pay-button');
|
// this.button = document.querySelector('#pay-button');
|
||||||
|
this.button = document.getElementById('pay-button');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -37,6 +37,7 @@ class CheckoutCreditCard {
|
|||||||
.value = '';
|
.value = '';
|
||||||
|
|
||||||
const payButton = document.getElementById('pay-button');
|
const payButton = document.getElementById('pay-button');
|
||||||
|
|
||||||
const publicKey = document.querySelector('meta[name="public-key"]').content ?? '';
|
const publicKey = document.querySelector('meta[name="public-key"]').content ?? '';
|
||||||
const form = document.getElementById('payment-form');
|
const form = document.getElementById('payment-form');
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ class CheckoutCreditCard {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED, function (event) {
|
Frames.addEventHandler(Frames.Events.CARD_TOKENIZATION_FAILED, function (event) {
|
||||||
pay.button.disabled = false;
|
payButton.disabled = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
Frames.addEventHandler(Frames.Events.CARD_TOKENIZED, function (event) {
|
Frames.addEventHandler(Frames.Events.CARD_TOKENIZED, function (event) {
|
||||||
@ -68,6 +69,7 @@ class CheckoutCreditCard {
|
|||||||
|
|
||||||
form.addEventListener('submit', function (event) {
|
form.addEventListener('submit', function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
payButton.disabled = true;
|
||||||
Frames.submitCard();
|
Frames.submitCard();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,11 @@
|
|||||||
return actions.restart();
|
return actions.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions.order.capture().then(function(details) {
|
// return actions.order.capture().then(function(details) {
|
||||||
document.getElementById("gateway_response").value =JSON.stringify( details );
|
document.getElementById("gateway_response").value =JSON.stringify( data );
|
||||||
|
// document.getElementById("gateway_response").value =JSON.stringify( details );
|
||||||
document.getElementById("server_response").submit();
|
document.getElementById("server_response").submit();
|
||||||
});
|
// });
|
||||||
},
|
},
|
||||||
onCancel: function() {
|
onCancel: function() {
|
||||||
window.location.href = "/client/invoices/";
|
window.location.href = "/client/invoices/";
|
||||||
|
@ -19,8 +19,18 @@ use Tests\TestCase;
|
|||||||
*/
|
*/
|
||||||
class EvaluateStringTest extends TestCase
|
class EvaluateStringTest extends TestCase
|
||||||
{
|
{
|
||||||
|
public function testNumericCleanup()
|
||||||
|
{
|
||||||
|
$string = '13/favicon.ico';
|
||||||
|
|
||||||
|
$number = preg_replace('~\D~', '', $string);
|
||||||
|
|
||||||
|
$this->assertEquals(13, $number);
|
||||||
|
}
|
||||||
|
|
||||||
public function testClassNameResolution()
|
public function testClassNameResolution()
|
||||||
{
|
{
|
||||||
$this->assertEquals(class_basename(Client::class), 'Client');
|
$this->assertEquals(class_basename(Client::class), 'Client');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user