diff --git a/.travis.yml b/.travis.yml index a3d2432b86dc..46a6fd8f81b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ group: deprecated-2017Q4 php: - 7.3 -# - 7.4snapshot +# - 7.4 # - nightly addons: diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php index 159b8433dcab..61b6ab39edd0 100644 --- a/app/Console/Commands/CreateTestData.php +++ b/app/Console/Commands/CreateTestData.php @@ -19,6 +19,7 @@ use App\Models\PaymentType; use App\Models\User; use App\Repositories\InvoiceRepository; use App\Utils\Traits\MakesHash; +use Faker\Factory; use Illuminate\Console\Command; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Schema; @@ -47,6 +48,8 @@ class CreateTestData extends Command parent::__construct(); $this->invoice_repo = $invoice_repo; + + } /** @@ -274,9 +277,12 @@ class CreateTestData extends Command private function createInvoice($client) { + $faker = \Faker\Factory::create(); + $invoice = InvoiceFactory::create($client->company->id,$client->user->id);//stub the company and user_id $invoice->client_id = $client->id; - + $invoice->date = $faker->date(); + $invoice->line_items = $this->buildLineItems(); $invoice->uses_inclusive_Taxes = false; diff --git a/app/Factory/CompanyFactory.php b/app/Factory/CompanyFactory.php index a2b789e9d60c..b9a1a53467af 100644 --- a/app/Factory/CompanyFactory.php +++ b/app/Factory/CompanyFactory.php @@ -28,7 +28,7 @@ class CompanyFactory $company->company_key = $this->createHash(); $company->settings = CompanySettings::defaults(); $company->db = config('database.default'); - $company->custom_fields = (object) ['custom1' => '1', 'custom2' => '2', 'custom3'=>3]; + $company->custom_fields = (object) ['custom1' => '1', 'custom2' => '2', 'custom3'=>'3']; $company->domain = ''; return $company; diff --git a/app/Http/Controllers/ActivityController.php b/app/Http/Controllers/ActivityController.php index 1fef5a8df7ba..641a98564e8e 100644 --- a/app/Http/Controllers/ActivityController.php +++ b/app/Http/Controllers/ActivityController.php @@ -40,6 +40,16 @@ class ActivityController extends BaseController * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), * @OA\Parameter(ref="#/components/parameters/include"), * @OA\Parameter(ref="#/components/parameters/index"), + * @OA\Parameter( + * name="rows", + * in="path", + * description="The number of activities to return", + * example="50", + * @OA\Schema( + * type="number", + * format="integer", + * ), + * ), * @OA\Response( * response=200, * description="A list of actvities", @@ -62,12 +72,12 @@ class ActivityController extends BaseController * ) * */ - public function index() + public function index(Request $request) { + $default_activities = isset($request->input('rows')) ? $request->input('rows') : 50; $activities = Activity::orderBy('created_at', 'DESC')->company() - ->take(50); - + ->take($default_activities); return $this->listResponse($activities); diff --git a/app/Http/Controllers/TemplateController.php b/app/Http/Controllers/TemplateController.php index f8b2d15b0074..3c1c494179a1 100644 --- a/app/Http/Controllers/TemplateController.php +++ b/app/Http/Controllers/TemplateController.php @@ -11,6 +11,7 @@ namespace App\Http\Controllers; +use Parsedown; class TemplateController extends BaseController { @@ -107,6 +108,26 @@ class TemplateController extends BaseController * format="string", * ), * ), + * @OA\RequestBody( + * description="The template subject and body", + * required=true, + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * type="object", + * @OA\Property( + * property="subject", + * description="The email template subject", + * type="string", + * ), + * @OA\Property( + * property="body", + * description="The email template body", + * type="string", + * ), + * ) + * ) + * ), * @OA\Response( * response=200, * description="The template response", @@ -135,9 +156,13 @@ class TemplateController extends BaseController $entity_obj = $class::find($entity_id)->company(); - $markdown = request()->input('text'); + $subject = request()->input('subject'); + $body = ; - return response()->json($markdown, 200); + $body = Parsedown::instance()->text(request()->input('body')); + $subject = Parsedown::instance()->text(request()->input('subject')); + + return response()->json($body, 200); } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 27202baf9dba..f029fa9006df 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -11,18 +11,19 @@ namespace App\Http\Controllers; +use App\DataMapper\DefaultSettings; use App\Factory\UserFactory; use App\Filters\UserFilters; use App\Http\Controllers\Traits\VerifiesUserEmail; use App\Http\Requests\User\CreateUserRequest; use App\Http\Requests\User\DestroyUserRequest; +use App\Http\Requests\User\DetachCompanyUserRequest; use App\Http\Requests\User\EditUserRequest; use App\Http\Requests\User\ShowUserRequest; use App\Http\Requests\User\StoreUserRequest; use App\Http\Requests\User\UpdateUserRequest; use App\Jobs\Company\CreateCompanyToken; use App\Models\User; -use App\DataMapper\DefaultSettings; use App\Repositories\UserRepository; use App\Transformers\UserTransformer; use App\Utils\Traits\MakesHash; @@ -528,4 +529,100 @@ class UserController extends BaseController } + + + /** + * Attach an existing user to a company. + * + * @OA\Post( + * path="/api/v1/users/{user}/attachToCompany", + * operationId="attachUser", + * tags={"users"}, + * summary="Attach an existing user to a company", + * description="Attach an existing user to a company", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\RequestBody(ref="#/components/schemas/CompanyUser"), + * @OA\Response( + * response=200, + * description="Returns the saved User object", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * @OA\JsonContent(ref="#/components/schemas/CompanyUser"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function attach(AttachCompanyUserRequest $request, User $user) + { + + $company = auth()->user()->company(); + + $user->companies()->attach($company->id, $request->all()); + + $ct = CreateCompanyToken::dispatchNow($company, $user, 'User token created by'.auth()->user()->present()->user()); + + return $this->itemResponse($user->fresh()); + + } + + /** + * Detach an existing user to a company. + * + * @OA\Delete( + * path="/api/v1/users/{user}/detachFromCompany", + * operationId="detachUser", + * tags={"users"}, + * summary="Detach an existing user to a company", + * description="Detach an existing user from a company", + * @OA\Parameter(ref="#/components/parameters/X-Api-Secret"), + * @OA\Parameter(ref="#/components/parameters/X-Api-Token"), + * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), + * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Response( + * response=200, + * description="Success response", + * @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"), + * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), + * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), + * ), + * @OA\Response( + * response=422, + * description="Validation error", + * @OA\JsonContent(ref="#/components/schemas/ValidationError"), + * + * ), + * @OA\Response( + * response="default", + * description="Unexpected Error", + * @OA\JsonContent(ref="#/components/schemas/Error"), + * ), + * ) + * + */ + public function detach(DetachCompanyUserRequest $request, User $user) + { + $company_user = CompanyUser::whereUserId($user->id) + ->whereCompanyId(auth()->user()->id)->first(); + + $company_user->token->delete(); + $company_user->delete(); + + return response()->json(['message' => 'User detached from company'], 200); + + } } diff --git a/app/Http/Requests/User/AttachCompanyUserRequest.php b/app/Http/Requests/User/AttachCompanyUserRequest.php new file mode 100644 index 000000000000..b2d3123163cf --- /dev/null +++ b/app/Http/Requests/User/AttachCompanyUserRequest.php @@ -0,0 +1,45 @@ +user()->isAdmin(); + } + + protected function prepareForValidation() + { + + $this->replace([ + 'is_admin' => isset($request->input('is_admin')) ? $request->input('is_admin') : false, + 'permissions' => isset($request->input('permissions')) ? $request->input('permissions') : '', + 'settings' => isset($request->input('settings')) ? $request->input('settings') : json_encode(DefaultSettings::userSettings()), + 'is_locked' => isset($request->input('is_locked')) ? $request->input('is_locked') : false, + 'is_owner' => false, + ]); + } + + +} \ No newline at end of file diff --git a/app/Http/Requests/User/DetachCompanyUserRequest.php b/app/Http/Requests/User/DetachCompanyUserRequest.php new file mode 100644 index 000000000000..7b315ff684fb --- /dev/null +++ b/app/Http/Requests/User/DetachCompanyUserRequest.php @@ -0,0 +1,32 @@ +user()->isAdmin(); + } + +} \ No newline at end of file diff --git a/app/Models/ClientGatewayToken.php b/app/Models/ClientGatewayToken.php index fe463c589fea..43a1dfa82908 100644 --- a/app/Models/ClientGatewayToken.php +++ b/app/Models/ClientGatewayToken.php @@ -51,5 +51,16 @@ class ClientGatewayToken extends BaseModel { return $this->hasOne(User::class)->withTrashed(); } - + + /** + * Retrieve the model for a bound value. + * + * @param mixed $value + * @return \Illuminate\Database\Eloquent\Model|null + */ + public function resolveRouteBinding($value) + { + return $this + ->where('id', $this->decodePrimaryKey($value))->firstOrFail(); + } } \ No newline at end of file diff --git a/app/Models/Presenters/UserPresenter.php b/app/Models/Presenters/UserPresenter.php index 01feb27f8193..3fb1ab612e4b 100644 --- a/app/Models/Presenters/UserPresenter.php +++ b/app/Models/Presenters/UserPresenter.php @@ -23,7 +23,10 @@ class UserPresenter extends EntityPresenter */ public function name() { - return $this->entity->first_name . ' ' . $this->entity->last_name; + $first_name = isset($this->entity->first_name) ? $this->entity->first_name : ''; + $last_name = isset($this->entity->last_name) ? $this->entity->last_name : ''; + + return $first_name . ' ' . $last_name; } } diff --git a/app/Transformers/GatewayTransformer.php b/app/Transformers/GatewayTransformer.php index ac684df1e158..f15164a2c185 100644 --- a/app/Transformers/GatewayTransformer.php +++ b/app/Transformers/GatewayTransformer.php @@ -45,7 +45,7 @@ class GatewayTransformer extends EntityTransformer 'provider' => (string)$gateway->provider ?: '', 'visible' => (bool)$gateway->visible, 'sort_order' => (int)$gateway->sort_order, - 'default_gateway_type_id' => (bool)$gateway->default_gateway_type_id, + 'default_gateway_type_id' => (int)$gateway->default_gateway_type_id, 'site_url' => (string)$gateway->site_url ?: '', 'is_offsite' => (bool)$gateway->is_offsite, 'is_secure' => (bool)$gateway->is_secure, diff --git a/resources/views/portal/default/invoices/index.blade.php b/resources/views/portal/default/invoices/index.blade.php index ff7c4693d927..ec669c3581ae 100644 --- a/resources/views/portal/default/invoices/index.blade.php +++ b/resources/views/portal/default/invoices/index.blade.php @@ -115,8 +115,8 @@ $(function() { columns: [ {data: 'checkbox', name: 'checkbox', title: '', searchable: false, orderable: false}, - {data: 'invoice_number', name: 'invoice_number', title: '{{ctrans('texts.invoice_number')}}', visible: true}, - {data: 'invoice_date', name: 'invoice_date', title: '{{ctrans('texts.invoice_date')}}', visible: true}, + {data: 'number', name: 'number', title: '{{ctrans('texts.invoice_number')}}', visible: true}, + {data: 'date', name: 'date', title: '{{ctrans('texts.invoice_date')}}', visible: true}, {data: 'amount', name: 'amount', title: '{{ctrans('texts.total')}}', visible: true}, {data: 'balance', name: 'balance', title: '{{ctrans('texts.balance')}}', visible: true}, {data: 'due_date', name: 'due_date', title: '{{ctrans('texts.due_date')}}', visible: true}, diff --git a/routes/api.php b/routes/api.php index 97d6419544b0..0e3662470a20 100644 --- a/routes/api.php +++ b/routes/api.php @@ -70,6 +70,9 @@ Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefi // Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit Route::resource('users', 'UserController'); // name = (users. index / create / show / update / destroy / edit + Route::post('users/{user}/attachToCompany', 'UserController@attach'); + Route::delete('users/{user}/detachFromCompany','UserController@detach'); + Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected');