Merge remote-tracking branch 'upstream/v5-develop' into 1314-subscriptions-v3

This commit is contained in:
Benjamin Beganović 2024-03-13 19:25:37 +01:00
commit 428b416de3
44 changed files with 7307 additions and 6618 deletions

View File

@ -1 +1 @@
5.8.34
5.8.35

View File

@ -127,7 +127,7 @@ class ClientExport extends BaseExport
$query = Client::query()->with('contacts')
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -103,7 +103,7 @@ class CreditExport extends BaseExport
->withTrashed()
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -83,7 +83,7 @@ class ExpenseExport extends BaseExport
->with('client')
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -58,7 +58,7 @@ class InvoiceExport extends BaseExport
->withTrashed()
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -71,7 +71,7 @@ class InvoiceItemExport extends BaseExport
->withTrashed()
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -59,7 +59,7 @@ class PurchaseOrderExport extends BaseExport
->withTrashed()
->with('vendor')
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -63,7 +63,7 @@ class PurchaseOrderItemExport extends BaseExport
$query = PurchaseOrder::query()
->withTrashed()
->with('vendor')->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -65,7 +65,7 @@ class QuoteExport extends BaseExport
->withTrashed()
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -66,7 +66,7 @@ class QuoteItemExport extends BaseExport
$query = Quote::query()
->withTrashed()
->with('client')->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -57,7 +57,7 @@ class RecurringInvoiceExport extends BaseExport
->withTrashed()
->with('client')
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -68,7 +68,7 @@ class TaskExport extends BaseExport
$query = Task::query()
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -62,7 +62,7 @@ class VendorExport extends BaseExport
$query = Vendor::query()->with('contacts')
->withTrashed()
->where('company_id', $this->company->id)
->where('is_deleted', $this->input['include_deleted']);
->where('is_deleted', $this->input['include_deleted'] ?? false);
$query = $this->addDateRange($query);

View File

@ -97,11 +97,11 @@ class Nordigen
return $it->transform($out);
} catch (\Exception $e) {
if (strpos($e->getMessage(), "Invalid Account ID") !== false) {
return false;
}
throw $e;
nlog("Nordigen getAccount() failed => {$account_id} => " . $e->getMessage());
return false;
}
}

View File

@ -25,6 +25,10 @@ class SubscriptionPurchaseController extends Controller
{
App::setLocale($subscription->company->locale());
if ($subscription->trashed()) {
return $this->render('generic.not_available', ['account' => $subscription->company->account, 'company' => $subscription->company]);
}
/* Make sure the contact is logged into the correct company for this subscription */
if (auth()->guard('contact')->user() && auth()->guard('contact')->user()->company_id != $subscription->company_id) {
auth()->guard('contact')->logout();

View File

@ -21,7 +21,7 @@ class ProtectedDownloadController extends BaseController
public function index(Request $request, string $hash)
{
/** @var string $hashed_path */
$hashed_path = Cache::pull($hash);
$hashed_path = Cache::get($hash);
if (!$hashed_path) {
throw new SystemError('File no longer available', 404);

View File

@ -44,6 +44,7 @@ class StoreProjectRequest extends Request
$rules['name'] = 'required';
$rules['client_id'] = 'required|exists:clients,id,company_id,'.$user->company()->id;
$rules['budgeted_hours'] = 'sometimes|numeric';
if (isset($this->number)) {
$rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id);
@ -74,6 +75,9 @@ class StoreProjectRequest extends Request
$input['color'] = '';
}
if(array_key_exists('budgeted_hours', $input) && empty($input['budgeted_hours']))
$input['budgeted_hours'] = 0;
$this->replace($input);
}

View File

@ -45,6 +45,8 @@ class UpdateProjectRequest extends Request
$rules['number'] = Rule::unique('projects')->where('company_id', $user->company()->id)->ignore($this->project->id);
}
$rules['budgeted_hours'] = 'sometimes|numeric';
if ($this->file('documents') && is_array($this->file('documents'))) {
$rules['documents.*'] = $this->file_validation;
} elseif ($this->file('documents')) {
@ -73,6 +75,10 @@ class UpdateProjectRequest extends Request
if (array_key_exists('color', $input) && is_null($input['color'])) {
$input['color'] = '';
}
if(array_key_exists('budgeted_hours', $input) && empty($input['budgeted_hours'])) {
$input['budgeted_hours'] = 0;
}
$this->replace($input);
}

View File

@ -66,7 +66,7 @@ class StoreSchedulerRequest extends Request
'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'],
'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'],
'parameters.entity_id' => ['bail', 'sometimes', 'string'],
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,client,contact,client_contact,credit,document,expense,invoice,invoice_item,quote,quote_item,recurring_invoice,payment,product,task'],
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,activities,client,clients,client_contact,client_contacts,credit,credits,document,documents,expense,expenses,invoice,invoices,invoice_item,invoice_items,quote,quotes,quote_item,quote_items,recurring_invoice,recurring_invoices,payment,payments,product,products,task,tasks'],
'parameters.date_key' => ['bail','sometimes', 'string'],
'parameters.status' => ['bail','sometimes', 'nullable', 'string'],
];

View File

@ -66,9 +66,9 @@ class UpdateSchedulerRequest extends Request
'parameters.end_date' => ['bail', 'sometimes', 'date:Y-m-d', 'required_if:parameters.date_rate,custom', 'after_or_equal:parameters.start_date'],
'parameters.entity' => ['bail', 'sometimes', 'string', 'in:invoice,credit,quote,purchase_order'],
'parameters.entity_id' => ['bail', 'sometimes', 'string'],
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,client,contact,client_contact,credit,document,expense,invoice,invoice_item,quote,quote_item,recurring_invoice,payment,product,task'],
'parameters.report_name' => ['bail','sometimes', 'string', 'required_if:template,email_report','in:vendor,purchase_order_item,purchase_order,ar_detailed,ar_summary,client_balance,tax_summary,profitloss,client_sales,user_sales,product_sales,activity,activities,client,clients,client_contact,client_contacts,credit,credits,document,documents,expense,expenses,invoice,invoices,invoice_item,invoice_items,quote,quotes,quote_item,quote_items,recurring_invoice,recurring_invoices,payment,payments,product,products,task,tasks'],
'parameters.date_key' => ['bail','sometimes', 'string'],
'parameters.status' => ['bail','sometimes', 'string'],
'parameters.status' => ['bail','sometimes', 'nullable', 'string'],
];
return $rules;

View File

@ -114,23 +114,26 @@ class ProcessBankTransactionsNordigen implements ShouldQueue
private function updateAccount()
{
if (!$this->nordigen->isAccountActive($this->bank_integration->nordigen_account_id)) {
$is_account_active = $this->nordigen->isAccountActive($this->bank_integration->nordigen_account_id);
$account = $this->nordigen->getAccount($this->bank_integration->nordigen_account_id);
if (!$is_account_active || !$account) {
$this->bank_integration->disabled_upstream = true;
$this->bank_integration->save();
$this->stop_loop = false;
nlog("Nordigen: account inactive: " . $this->bank_integration->nordigen_account_id);
// @turbo124 @todo send email for expired account
$this->nordigen->disabledAccountEmail($this->bank_integration);
return;
}
$this->nordigen_account = $this->nordigen->getAccount($this->bank_integration->nordigen_account_id);
$this->nordigen_account = $account;
$this->bank_integration->disabled_upstream = false;
$this->bank_integration->bank_account_status = $this->nordigen_account['account_status'];
$this->bank_integration->balance = $this->nordigen_account['current_balance'];
$this->bank_integration->bank_account_status = $account['account_status'];
$this->bank_integration->balance = $account['current_balance'];
$this->bank_integration->save();
}

View File

@ -60,6 +60,7 @@ class TaskScheduler implements ShouldQueue
nlog("Doing job {$scheduler->name}");
try {
//@var \App\Models\Schedule $scheduler
$scheduler->service()->runTask();
} catch(\Exception $e) {
nlog($e->getMessage());

View File

@ -123,7 +123,7 @@ class WebhookSingle implements ShouldQueue
]);
(new SystemLogger(
array_merge((array) $response, $data),
['message' => $response->getBody()->getHeaders(), 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_SUCCESS,
SystemLog::TYPE_WEBHOOK_RESPONSE,
@ -136,7 +136,7 @@ class WebhookSingle implements ShouldQueue
nlog($e->getMessage());
(new SystemLogger(
['message' => "Error connecting to ". $subscription->target_url],
['message' => "Error connecting to ". $subscription->target_url, 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_FAILURE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
@ -152,7 +152,7 @@ class WebhookSingle implements ShouldQueue
$message = "There was a problem when connecting to {$subscription->target_url} => status code ". $e->getResponse()->getStatusCode(). " This webhook call will be suspended until further action is taken.";
(new SystemLogger(
['message' => $message],
['message' => $message, 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_FAILURE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
@ -170,7 +170,7 @@ class WebhookSingle implements ShouldQueue
nlog($message);
(new SystemLogger(
['message' => $message],
['message' => $message, 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_FAILURE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
@ -192,7 +192,7 @@ class WebhookSingle implements ShouldQueue
$message = "There was a problem when connecting to {$subscription->target_url} => status code ". $e->getResponse()->getStatusCode(). " no retry attempted.";
(new SystemLogger(
['message' => $message],
['message' => $message, 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_FAILURE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
@ -208,7 +208,7 @@ class WebhookSingle implements ShouldQueue
$error = json_decode($e->getResponse()->getBody()->getContents());
(new SystemLogger(
['message' => $error],
['message' => $error, 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_FAILURE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
@ -220,7 +220,7 @@ class WebhookSingle implements ShouldQueue
$error = json_decode($e->getResponse()->getBody()->getContents());
(new SystemLogger(
['message' => $error],
['message' => $error, 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_FAILURE,
SystemLog::TYPE_WEBHOOK_RESPONSE,
@ -232,7 +232,7 @@ class WebhookSingle implements ShouldQueue
nlog($e->getCode());
(new SystemLogger(
$e->getMessage(),
['message' => $e->getMessage(), 'body' => $data],
SystemLog::CATEGORY_WEBHOOK,
SystemLog::EVENT_WEBHOOK_FAILURE,
SystemLog::TYPE_WEBHOOK_RESPONSE,

View File

@ -11,34 +11,35 @@
namespace App\Services\Scheduler;
use App\Export\CSV\ClientExport;
use App\Export\CSV\ContactExport;
use App\Export\CSV\CreditExport;
use App\Export\CSV\DocumentExport;
use App\Export\CSV\ExpenseExport;
use App\Export\CSV\InvoiceExport;
use App\Export\CSV\InvoiceItemExport;
use App\Export\CSV\PaymentExport;
use App\Export\CSV\ProductExport;
use App\Export\CSV\ProductSalesExport;
use App\Export\CSV\QuoteExport;
use App\Export\CSV\QuoteItemExport;
use App\Export\CSV\RecurringInvoiceExport;
use App\Export\CSV\TaskExport;
use App\Jobs\Mail\NinjaMailerJob;
use App\Jobs\Mail\NinjaMailerObject;
use App\Mail\DownloadReport;
use App\Models\Client;
use App\Models\Scheduler;
use App\Mail\DownloadReport;
use App\Export\CSV\TaskExport;
use App\Export\CSV\QuoteExport;
use App\Utils\Traits\MakesHash;
use App\Export\CSV\ClientExport;
use App\Export\CSV\CreditExport;
use App\Utils\Traits\MakesDates;
use App\Export\CSV\ContactExport;
use App\Export\CSV\ExpenseExport;
use App\Export\CSV\InvoiceExport;
use App\Export\CSV\PaymentExport;
use App\Export\CSV\ProductExport;
use App\Jobs\Mail\NinjaMailerJob;
use App\Export\CSV\ActivityExport;
use App\Export\CSV\DocumentExport;
use App\Export\CSV\QuoteItemExport;
use App\Services\Report\ProfitLoss;
use App\Jobs\Mail\NinjaMailerObject;
use App\Export\CSV\InvoiceItemExport;
use App\Export\CSV\ProductSalesExport;
use App\Services\Report\ARDetailReport;
use App\Services\Report\ARSummaryReport;
use App\Services\Report\ClientBalanceReport;
use App\Services\Report\ClientSalesReport;
use App\Services\Report\ProfitLoss;
use App\Services\Report\TaxSummaryReport;
use App\Services\Report\UserSalesReport;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
use App\Services\Report\TaxSummaryReport;
use App\Export\CSV\RecurringInvoiceExport;
use App\Services\Report\ClientSalesReport;
use App\Services\Report\ClientBalanceReport;
class EmailReport
{
@ -77,19 +78,33 @@ class EmailReport
'client_sales' => $export = (new ClientSalesReport($this->scheduler->company, $data)),
'user_sales' => $export = (new UserSalesReport($this->scheduler->company, $data)),
'profitloss' => $export = (new ProfitLoss($this->scheduler->company, $data)),
'activity' => $export = (new ActivityExport($this->scheduler->company, $data)),
'activities' => $export = (new ActivityExport($this->scheduler->company, $data)),
'client' => $export = (new ClientExport($this->scheduler->company, $data)),
'clients' => $export = (new ClientExport($this->scheduler->company, $data)),
'client_contact' => $export = (new ContactExport($this->scheduler->company, $data)),
'client_contacts' => $export = (new ContactExport($this->scheduler->company, $data)),
'credit' => $export = (new CreditExport($this->scheduler->company, $data)),
'credits' => $export = (new CreditExport($this->scheduler->company, $data)),
'document' => $export = (new DocumentExport($this->scheduler->company, $data)),
'documents' => $export = (new DocumentExport($this->scheduler->company, $data)),
'expense' => $export = (new ExpenseExport($this->scheduler->company, $data)),
'expenses' => $export = (new ExpenseExport($this->scheduler->company, $data)),
'invoice' => $export = (new InvoiceExport($this->scheduler->company, $data)),
'invoices' => $export = (new InvoiceExport($this->scheduler->company, $data)),
'invoice_item' => $export = (new InvoiceItemExport($this->scheduler->company, $data)),
'invoice_items' => $export = (new InvoiceItemExport($this->scheduler->company, $data)),
'quote' => $export = (new QuoteExport($this->scheduler->company, $data)),
'quotes' => $export = (new QuoteExport($this->scheduler->company, $data)),
'quote_item' => $export = (new QuoteItemExport($this->scheduler->company, $data)),
'quote_items' => $export = (new QuoteItemExport($this->scheduler->company, $data)),
'recurring_invoice' => $export = (new RecurringInvoiceExport($this->scheduler->company, $data)),
'recurring_invoices' => $export = (new RecurringInvoiceExport($this->scheduler->company, $data)),
'payment' => $export = (new PaymentExport($this->scheduler->company, $data)),
'payments' => $export = (new PaymentExport($this->scheduler->company, $data)),
'product' => $export = (new ProductExport($this->scheduler->company, $data)),
'task' => $export = (new TaskExport($this->scheduler->company, $data)),
'products' => $export = (new ProductExport($this->scheduler->company, $data)),
'tasks' => $export = (new TaskExport($this->scheduler->company, $data)),
default => $export = false,
};

View File

@ -49,6 +49,12 @@ class ProjectTransformer extends EntityTransformer
public function includeClient(Project $project): \League\Fractal\Resource\Item
{
if (!$project->client) {
nlog("Project {$project->hashed_id} does not have a client attached - this project is in a bad state");
return null;
}
$transformer = new ClientTransformer($this->serializer);
return $this->includeItem($project->client, $transformer, Client::class);

View File

@ -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.34'),
'app_tag' => env('APP_TAG', '5.8.34'),
'app_version' => env('APP_VERSION', '5.8.35'),
'app_tag' => env('APP_TAG', '5.8.35'),
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),

View File

@ -460,7 +460,7 @@ $lang = array(
'edit_token' => 'Edit Token',
'delete_token' => 'Delete Token',
'token' => 'Token',
'add_gateway' => 'Add Gateway',
'add_gateway' => 'Add Payment Gateway',
'delete_gateway' => 'Delete Gateway',
'edit_gateway' => 'Edit Gateway',
'updated_gateway' => 'Successfully updated gateway',
@ -5263,8 +5263,11 @@ $lang = array(
'use_unapplied_payments_help' => 'Apply any payment balances prior to charging a payment method',
'payment_terms_help' => 'The number of days after the invoice date that payment is due',
'payment_type_help' => 'The default payment type to be used for payments',
'quote_valid_until' => 'The number of days that the quote is valid for',
'expense_payment_type' => 'The default expense payment type to be used',
'quote_valid_until_help' => 'The number of days that the quote is valid for',
'expense_payment_type_help' => 'The default expense payment type to be used',
'paylater' => 'Pay in 4',
'payment_provider' => 'Payment Provider',
);
return $lang;

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +1,32 @@
CompanyUser:
properties:
permissions:
description: 'The company user permissions'
description: 'The user permissionsfor this company in a comma separated list'
type: string
example: '[create_invoice]'
example: 'create_invoice,create_client,view_client'
settings:
description: 'Settings that are used for the frontend applications to store user preferences / metadata'
description: 'Settings that are used for the flutter applications to store user preferences / metadata'
type: object
readOnly: true
react_settings:
description: 'Dedicated settings object for the react web application'
type: object
readOnly: true
is_owner:
description: 'Determines whether the user owns this company'
type: boolean
example: true
readOnly: true
is_admin:
description: 'Determines whether the user is the admin of this company'
type: boolean
example: true
readOnly: true
is_locked:
description: 'Determines whether the users access to this company has been locked'
type: boolean
example: true
readOnly: true
updated_at:
description: 'The last time the record was modified, format Unix Timestamp'
type: integer

View File

@ -1,6 +1,6 @@
openapi: 3.0.0
info:
title: 'Invoice Ninja API Reference - Where self host invoicing lives.'
title: 'Invoice Ninja API Reference.'
description: |
---
<br>
@ -15,7 +15,7 @@ info:
license:
name: 'Elastic License'
url: 'https://www.elastic.co/licensing/elastic-license'
version: 5.5.70
version: 5.8.34
servers:
-
url: 'https://demo.invoiceninja.com'

View File

@ -89,7 +89,14 @@ paths:
tags:
- login
summary: "Attempts authentication"
description: "Returns a CompanyUser object on success"
description: |
After authenticating with the API, the returned object is a CompanyUser object which is a bridge linking the user to the company.
The company user object itself contains the users permissions (admin/owner or fine grained permissions) You will most likely want to
also include in the response of this object both the company and the user object, this can be done by using the include parameter.
/api/v1/login?include=company,user
operationId: postLogin
parameters:
- $ref: "#/components/parameters/X-API-SECRET"
@ -109,10 +116,19 @@ paths:
description: "The users email address."
type: string
example: "demo@invoiceninja.com"
required: true
password:
description: "The user password. Must meet minimum criteria ~ > 6 characters"
type: string
example: "Password0"
required: true
one_time_password:
description: "The one time password if 2FA is enabled"
type: string
example: "123456"
required:
- email
- password
type: object
responses:
200:
@ -127,13 +143,15 @@ paths:
content:
application/json:
schema:
$ref: "#/components/schemas/Client"
$ref: "#/components/schemas/CompanyUser"
401:
$ref: "#/components/responses/401"
403:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
@ -146,7 +164,8 @@ paths:
description: |
Refreshes the dataset.
This endpoint can be used if you only need to access the most recent data from a certain point in time.
This endpoint can be used if you only need to access the most recent data from a certain point in time. For example, if you only want to retrieve The
most recent data from the last time you accessed the system, you would pass the query parameter ?updated_at=1676173763. (unix timestamp)
operationId: refresh
parameters:
- name: updated_at
@ -182,6 +201,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/yodlee/refresh:
@ -214,6 +237,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_integrations:
@ -264,6 +291,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
post:
@ -296,6 +327,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/bank_integrations/{id}":
@ -337,6 +372,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
put:
@ -377,6 +416,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
delete:
@ -413,6 +456,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/bank_integrations/{id}/edit":
@ -454,6 +501,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_integrations/create:
@ -487,6 +538,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_integrations/bulk:
@ -527,6 +582,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_integrations/refresh_accounts:
@ -560,6 +619,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_integrations/remove_account/account_id:
@ -593,6 +656,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_integrations/get_transactions/account_id:
@ -626,6 +693,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_transactions:
@ -676,6 +747,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
post:
@ -708,6 +783,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/bank_transactions/{id}":
@ -749,6 +828,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
put:
@ -789,6 +872,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
delete:
@ -866,6 +953,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_transactions/create:
@ -899,6 +990,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_transations/bulk:
@ -939,6 +1034,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_transations/match:
@ -979,6 +1078,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_transaction_rules:
@ -1029,6 +1132,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
post:
@ -1061,6 +1168,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/bank_transaction_rules/{id}":
@ -1102,6 +1213,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
put:
@ -1142,6 +1257,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
delete:
@ -1178,6 +1297,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/bank_transaction_rules/{id}/edit":
@ -1219,6 +1342,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_transaction_rules/create:
@ -1252,6 +1379,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/bank_transation_rules/bulk:
@ -1292,6 +1423,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/charts/totals:
@ -1326,6 +1461,10 @@ paths:
$ref: "#/components/headers/X-RateLimit-Limit"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
@ -1369,6 +1508,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
post:
@ -1401,6 +1544,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/client_gateway_tokens/{id}":
@ -1442,6 +1589,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
put:
@ -1482,6 +1633,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
delete:
@ -1518,6 +1673,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/client_gateway_tokens/{id}/edit":
@ -1559,6 +1718,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/client_gateway_tokens/create:
@ -1592,6 +1755,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
@ -1634,6 +1801,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
post:
@ -1666,6 +1837,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/companies/create:
@ -1699,6 +1874,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/companies/{id}":
@ -1740,6 +1919,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
put:
@ -1778,9 +1961,12 @@ paths:
$ref: "#/components/responses/401"
403:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
delete:
@ -1815,9 +2001,12 @@ paths:
$ref: "#/components/responses/401"
403:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/companies/{id}/edit":
@ -1857,9 +2046,12 @@ paths:
$ref: "#/components/responses/401"
403:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/companies/{id}/upload":
@ -1916,9 +2108,12 @@ paths:
$ref: "#/components/responses/401"
403:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
"/api/v1/companies/{company}/default":
@ -1958,9 +2153,12 @@ paths:
$ref: "#/components/responses/401"
403:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/company_gateways:
@ -2000,9 +2198,12 @@ paths:
$ref: "#/components/responses/401"
403:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
post:
@ -2035,6 +2236,10 @@ paths:
$ref: "#/components/responses/403"
422:
$ref: "#/components/responses/422"
429:
$ref: "#/components/responses/429"
5XX:
description: 'Server error'
default:
$ref: "#/components/responses/default"
/api/v1/company_gateways/create:

1
public/build/assets/app-17bd8d2c.css vendored Normal file

File diff suppressed because one or more lines are too long

109
public/build/assets/app-b98bbdda.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@
]
},
"resources/js/app.js": {
"file": "assets/app-c80ec97e.js",
"file": "assets/app-b98bbdda.js",
"imports": [
"_index-08e160a7.js",
"__commonjsHelpers-725317a4.js"
@ -240,7 +240,7 @@
"src": "resources/js/setup/setup.js"
},
"resources/sass/app.scss": {
"file": "assets/app-8c56d3d4.css",
"file": "assets/app-17bd8d2c.css",
"isEntry": true,
"src": "resources/sass/app.scss"
}

View File

@ -82,7 +82,7 @@
<option value="none"></option>
@foreach(App\Utils\TranslationHelper::getCountries() as $country)
<option
{{ $country == isset(auth()->user()->client->shipping_country->id) ? 'selected' : null }} value="{{ $country->id }}">
{{ $country = isset(auth()->guard('contact')->user()->client->shipping_country->id) ? 'selected' : null }} value="{{ $country->id }}">
{{ $country->iso_3166_2 }}
({{ $country->name }})
</option>

View File

@ -80,7 +80,7 @@
@component('portal.ninja2020.components.general.card-element-single')
<input type="checkbox" class="form-checkbox mr-1" name="accept_terms" id="accept-terms" required>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth('contact')->user()->client->company->settings->email]) }}</label>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->guard('contact')->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
@endcomponent
<div class="bg-white px-4 py-5 flex justify-end">

View File

@ -82,7 +82,7 @@
@component('portal.ninja2020.components.general.card-element-single')
<input type="checkbox" class="form-checkbox mr-1" id="accept-terms" required>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->guard('contact')->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
@endcomponent
@component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'save-button'])

View File

@ -59,7 +59,7 @@
@component('portal.ninja2020.components.general.card-element-single')
<input type="checkbox" class="form-checkbox mr-1" id="accept-terms" required>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->guard('contact')->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
@endcomponent
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.account_holder_name')])

View File

@ -70,7 +70,7 @@
@component('portal.ninja2020.components.general.card-element-single')
<input type="checkbox" class="form-checkbox mr-1" id="accept-terms" required>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->guard('contact')->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
@endcomponent
@component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'save-button'])

View File

@ -70,7 +70,7 @@
@component('portal.ninja2020.components.general.card-element-single')
<input type="checkbox" class="form-checkbox mr-1" id="accept-terms" required>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
<label for="accept-terms" class="cursor-pointer">{{ ctrans('texts.ach_authorization', ['company' => auth()->guard('contact')->user()->company->present()->name, 'email' => auth()->guard('contact')->user()->client->company->settings->email]) }}</label>
@endcomponent
@component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'save-button'])

View File

@ -17,7 +17,7 @@
@csrf
<button type="submit" onclick="setTimeout(() => this.disabled = true, 0); setTimeout(() => this.disabled = false, 5000); return true;" class="button button-primary bg-primary" name="action" value="download">{{ ctrans('texts.download') }}</button>
@csrf
@if(!empty(auth()->user()->client->service()->getPaymentMethods(0)))
@if(!empty(auth()->guard('contact')->user()->client->service()->getPaymentMethods(0)))
<button onclick="setTimeout(() => this.disabled = true, 0); return true;" type="submit" class="button button-primary bg-primary" name="action" value="payment">{{ ctrans('texts.pay_now') }}</button>
@endif
</form>

View File

@ -54,7 +54,7 @@
{{ ctrans('texts.date_created') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{{ $payment_method->formatDateTimestamp($payment_method->created_at, auth()->user()->client->date_format()) }}
{{ $payment_method->formatDateTimestamp($payment_method->created_at, auth()->guard('contact')->user()->client->date_format()) }}
</dd>
</div>
@endif

View File

@ -29,6 +29,8 @@ class ProjectApiTest extends TestCase
use DatabaseTransactions;
use MockAccountData;
protected $faker;
protected function setUp() :void
{
parent::setUp();
@ -42,6 +44,110 @@ class ProjectApiTest extends TestCase
Model::reguard();
}
public function testProjectValidationForBudgetedHoursPut()
{
$data = $this->project->toArray();
$data['budgeted_hours'] = "aa";
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/projects/{$this->project->hashed_id}", $data);
$response->assertStatus(422);
}
public function testProjectValidationForBudgetedHoursPutNull()
{
$data = $this->project->toArray();
$data['budgeted_hours'] = null;
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/projects/{$this->project->hashed_id}", $data);
$response->assertStatus(200);
}
public function testProjectValidationForBudgetedHoursPutEmpty()
{
$data = $this->project->toArray();
$data['budgeted_hours'] = "";
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/projects/{$this->project->hashed_id}", $data);
$response->assertStatus(200);
}
public function testProjectValidationForBudgetedHours()
{
$data = [
'name' => $this->faker->firstName(),
'client_id' => $this->client->hashed_id,
'number' => 'duplicate',
'budgeted_hours' => null
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/projects', $data);
$response->assertStatus(200);
}
public function testProjectValidationForBudgetedHours2()
{
$data = [
'name' => $this->faker->firstName(),
'client_id' => $this->client->hashed_id,
'number' => 'duplicate',
'budgeted_hours' => "a"
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/projects', $data);
$response->assertStatus(422);
}
public function testProjectValidationForBudgetedHours3()
{
$data = [
'name' => $this->faker->firstName(),
'client_id' => $this->client->hashed_id,
'number' => 'duplicate',
'budgeted_hours' => ""
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson('/api/v1/projects', $data);
$response->assertStatus(200);
}
public function testProjectGetFilter()
{
$response = $this->withHeaders([

View File

@ -72,6 +72,11 @@ trait MockAccountData
use MakesHash;
use GeneratesCounter;
/**
* @var
*/
public $project;
/**
* @var
*/