mirror of
https://github.com/InvoiceShelf/InvoiceShelf.git
synced 2026-09-09 00:24:09 +00:00
chore(accounts): remove legacy-era accounts sources
This commit is contained in:
@@ -1,121 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Application;
|
||||
|
||||
use App\Domains\Accounts\Contracts\CompanyDataPurger;
|
||||
use App\Domains\Accounts\Contracts\CompanyDefaultsProvisioner;
|
||||
use App\Domains\Accounts\Models\Company;
|
||||
use App\Domains\Accounts\Models\CompanySetting;
|
||||
use Silber\Bouncer\BouncerFacade;
|
||||
use Silber\Bouncer\Database\Role;
|
||||
|
||||
class CompanyService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CompanyDefaultsProvisioner $companyDefaultsProvisioner,
|
||||
private readonly CompanyDataPurger $companyDataPurger,
|
||||
) {}
|
||||
|
||||
public function setupDefaults(Company $company, int $currencyId = 13): bool
|
||||
{
|
||||
$this->setupRoles($company);
|
||||
$this->companyDefaultsProvisioner->provision($company);
|
||||
$this->setupDefaultSettings($company, $currencyId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setupRoles(Company $company): void
|
||||
{
|
||||
BouncerFacade::scope()->to($company->id);
|
||||
|
||||
$owner = BouncerFacade::role()->firstOrCreate([
|
||||
'name' => 'owner',
|
||||
'title' => 'Owner',
|
||||
'scope' => $company->id,
|
||||
]);
|
||||
|
||||
foreach (config('abilities.abilities') as $ability) {
|
||||
BouncerFacade::allow($owner)->to($ability['ability'], $ability['model']);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(Company $company): bool
|
||||
{
|
||||
$this->companyDataPurger->purge($company);
|
||||
|
||||
$roles = Role::when($company->id, function ($query) use ($company) {
|
||||
return $query->where('scope', $company->id);
|
||||
})->get();
|
||||
|
||||
if ($roles) {
|
||||
$roles->map(function ($role) {
|
||||
$role->delete();
|
||||
});
|
||||
}
|
||||
|
||||
$company->users()->detach();
|
||||
|
||||
$company->settings()->delete();
|
||||
$company->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function setupDefaultSettings(Company $company, int $currencyId): void
|
||||
{
|
||||
$defaultInvoiceEmailBody = 'You have received a new invoice from <b>{COMPANY_NAME}</b>.</br> Please download using the button below:';
|
||||
$defaultEstimateEmailBody = 'You have received a new estimate from <b>{COMPANY_NAME}</b>.</br> Please download using the button below:';
|
||||
$defaultPaymentEmailBody = 'Thank you for the payment.</b></br> Please download your payment receipt using the button below:';
|
||||
$billingAddressFormat = '<h3>{BILLING_ADDRESS_NAME}</h3><p>{BILLING_ADDRESS_STREET_1}</p><p>{BILLING_ADDRESS_STREET_2}</p><p>{BILLING_CITY} {BILLING_STATE}</p><p>{BILLING_COUNTRY} {BILLING_ZIP_CODE}</p><p>{BILLING_PHONE}</p>';
|
||||
$shippingAddressFormat = '<h3>{SHIPPING_ADDRESS_NAME}</h3><p>{SHIPPING_ADDRESS_STREET_1}</p><p>{SHIPPING_ADDRESS_STREET_2}</p><p>{SHIPPING_CITY} {SHIPPING_STATE}</p><p>{SHIPPING_COUNTRY} {SHIPPING_ZIP_CODE}</p><p>{SHIPPING_PHONE}</p>';
|
||||
$companyAddressFormat = '<h3><strong>{COMPANY_NAME}</strong></h3><p>{COMPANY_ADDRESS_STREET_1}</p><p>{COMPANY_ADDRESS_STREET_2}</p><p>{COMPANY_CITY} {COMPANY_STATE}</p><p>{COMPANY_COUNTRY} {COMPANY_ZIP_CODE}</p><p>{COMPANY_PHONE}</p>';
|
||||
$paymentFromCustomerAddress = '<h3>{BILLING_ADDRESS_NAME}</h3><p>{BILLING_ADDRESS_STREET_1}</p><p>{BILLING_ADDRESS_STREET_2}</p><p>{BILLING_CITY} {BILLING_STATE} {BILLING_ZIP_CODE}</p><p>{BILLING_COUNTRY}</p><p>{BILLING_PHONE}</p>';
|
||||
|
||||
$settings = [
|
||||
'invoice_mail_body' => $defaultInvoiceEmailBody,
|
||||
'estimate_mail_body' => $defaultEstimateEmailBody,
|
||||
'payment_mail_body' => $defaultPaymentEmailBody,
|
||||
'invoice_company_address_format' => $companyAddressFormat,
|
||||
'invoice_shipping_address_format' => $shippingAddressFormat,
|
||||
'invoice_billing_address_format' => $billingAddressFormat,
|
||||
'estimate_company_address_format' => $companyAddressFormat,
|
||||
'estimate_shipping_address_format' => $shippingAddressFormat,
|
||||
'estimate_billing_address_format' => $billingAddressFormat,
|
||||
'payment_company_address_format' => $companyAddressFormat,
|
||||
'payment_from_customer_address_format' => $paymentFromCustomerAddress,
|
||||
'currency' => $currencyId,
|
||||
'time_zone' => 'Asia/Kolkata',
|
||||
'language' => 'en',
|
||||
'fiscal_year' => '1-12',
|
||||
'carbon_date_format' => 'Y/m/d',
|
||||
'moment_date_format' => 'YYYY/MM/DD',
|
||||
'carbon_time_format' => 'H:i',
|
||||
'moment_time_format' => 'HH:mm',
|
||||
'invoice_use_time' => 'NO',
|
||||
'notification_email' => 'noreply@invoiceshelf.com',
|
||||
'notify_invoice_viewed' => 'NO',
|
||||
'notify_estimate_viewed' => 'NO',
|
||||
'tax_per_item' => 'NO',
|
||||
'discount_per_item' => 'NO',
|
||||
'invoice_email_attachment' => 'NO',
|
||||
'estimate_email_attachment' => 'NO',
|
||||
'payment_email_attachment' => 'NO',
|
||||
'retrospective_edits' => 'allow',
|
||||
'invoice_number_format' => '{{SERIES:INV}}{{DELIMITER:-}}{{SEQUENCE:6}}',
|
||||
'credit_note_number_format' => '{{SERIES:CN}}{{DELIMITER:-}}{{SEQUENCE:6}}',
|
||||
'estimate_number_format' => '{{SERIES:EST}}{{DELIMITER:-}}{{SEQUENCE:6}}',
|
||||
'payment_number_format' => '{{SERIES:PAY}}{{DELIMITER:-}}{{SEQUENCE:6}}',
|
||||
'estimate_set_expiry_date_automatically' => 'YES',
|
||||
'estimate_expiry_date_days' => 7,
|
||||
'invoice_set_due_date_automatically' => 'YES',
|
||||
'invoice_due_date_days' => 7,
|
||||
'bulk_exchange_rate_configured' => 'YES',
|
||||
'estimate_convert_action' => 'no_action',
|
||||
'automatically_expire_public_links' => 'YES',
|
||||
'link_expiry_days' => 7,
|
||||
];
|
||||
|
||||
CompanySetting::setSettings($settings, $company->id);
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Application;
|
||||
|
||||
use App\Domains\Accounts\Contracts\MemberReferencesCleaner;
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use Silber\Bouncer\BouncerFacade;
|
||||
|
||||
class MemberService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MemberReferencesCleaner $memberReferencesCleaner,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $attributes
|
||||
* @param iterable<int, array{id: int, role: string}> $companies
|
||||
*/
|
||||
public function create(array $attributes, iterable $companies): User
|
||||
{
|
||||
$user = User::create($attributes);
|
||||
|
||||
$user->setSettings([
|
||||
'language' => 'default',
|
||||
]);
|
||||
|
||||
$companies = collect($companies);
|
||||
$user->companies()->sync($companies->pluck('id'));
|
||||
|
||||
foreach ($companies as $company) {
|
||||
BouncerFacade::scope()->to($company['id']);
|
||||
|
||||
BouncerFacade::sync($user)->roles([$company['role']]);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $attributes
|
||||
* @param iterable<int, array{id: int, role: string}> $companies
|
||||
*/
|
||||
public function update(User $user, array $attributes, iterable $companies): User
|
||||
{
|
||||
$user->update($attributes);
|
||||
|
||||
$companies = collect($companies);
|
||||
$user->companies()->sync($companies->pluck('id'));
|
||||
|
||||
foreach ($companies as $company) {
|
||||
BouncerFacade::scope()->to($company['id']);
|
||||
|
||||
BouncerFacade::sync($user)->roles([$company['role']]);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function delete(array $ids): bool
|
||||
{
|
||||
foreach ($ids as $id) {
|
||||
$user = User::find($id);
|
||||
|
||||
if (! $user) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->memberReferencesCleaner->clear($user);
|
||||
|
||||
if ($user->settings()->exists()) {
|
||||
$user->settings()->delete();
|
||||
}
|
||||
|
||||
$user->delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Auth;
|
||||
|
||||
use App\Domains\Accounts\Application\InvitationService;
|
||||
use App\Domains\Accounts\Http\Requests\LoginRequest;
|
||||
use App\Domains\Accounts\Models\CompanyInvitation;
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use App\Platform\Http\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
public function login(LoginRequest $request)
|
||||
{
|
||||
$user = User::whereRaw('LOWER(email) = ?', [strtolower($request->username)])->first();
|
||||
|
||||
if (! $user || ! Hash::check($request->password, $user->password)) {
|
||||
throw ValidationException::withMessages([
|
||||
'email' => ['The provided credentials are incorrect.'],
|
||||
]);
|
||||
}
|
||||
|
||||
// Auto-accept invitation if token is provided
|
||||
if ($request->has('invitation_token') && $request->invitation_token) {
|
||||
$invitation = CompanyInvitation::where('token', $request->invitation_token)
|
||||
->pending()
|
||||
->first();
|
||||
|
||||
if ($invitation) {
|
||||
app(InvitationService::class)->accept($invitation, $user);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'type' => 'Bearer',
|
||||
'token' => $user->createToken($request->device_name)->plainTextToken,
|
||||
]);
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$request->user()->currentAccessToken()->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
return Auth::check();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Auth;
|
||||
|
||||
use App\Platform\Http\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset emails and
|
||||
| includes a trait which assists in sending these notifications from
|
||||
| your application to your users. Feel free to explore this trait.
|
||||
|
|
||||
*/
|
||||
|
||||
use SendsPasswordResetEmails;
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset link.
|
||||
*
|
||||
* @param string $response
|
||||
* @return RedirectResponse|JsonResponse
|
||||
*/
|
||||
protected function sendResetLinkResponse(Request $request, $response)
|
||||
{
|
||||
return response()->json([
|
||||
'message' => 'Password reset email sent.',
|
||||
'data' => $response,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a failed password reset link.
|
||||
*
|
||||
* @param string $response
|
||||
* @return RedirectResponse|JsonResponse
|
||||
*/
|
||||
protected function sendResetLinkFailedResponse(Request $request, $response)
|
||||
{
|
||||
return response()->json([
|
||||
'error' => 'Email could not be sent to this email address.',
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Auth;
|
||||
|
||||
use App\Platform\Http\Controller;
|
||||
use App\Providers\AppServiceProvider;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Login Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller handles authenticating users for the application and
|
||||
| redirecting them to your home screen. The controller uses a trait
|
||||
| to conveniently provide its functionality to your applications.
|
||||
|
|
||||
*/
|
||||
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* Where to redirect users after login.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = AppServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest')->except('logout');
|
||||
}
|
||||
|
||||
public function logout(Request $request): void
|
||||
{
|
||||
auth()->guard('web')->logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Auth;
|
||||
|
||||
use App\Platform\Http\Controller;
|
||||
use App\Providers\AppServiceProvider;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Contracts\Auth\CanResetPassword;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
/**
|
||||
* Where to redirect users after resetting their password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = AppServiceProvider::HOME;
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset.
|
||||
*
|
||||
* @param string $response
|
||||
* @return RedirectResponse|JsonResponse
|
||||
*/
|
||||
protected function sendResetResponse(Request $request, $response)
|
||||
{
|
||||
return response()->json([
|
||||
'message' => 'Password reset successfully.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the given user's password.
|
||||
*
|
||||
* @param CanResetPassword $user
|
||||
* @param string $password
|
||||
* @return void
|
||||
*/
|
||||
protected function resetPassword($user, $password)
|
||||
{
|
||||
$user->password = $password;
|
||||
|
||||
$user->setRememberToken(Str::random(60));
|
||||
|
||||
$user->save();
|
||||
|
||||
event(new PasswordReset($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a failed password reset.
|
||||
*
|
||||
* @param string $response
|
||||
* @return RedirectResponse|JsonResponse
|
||||
*/
|
||||
protected function sendResetFailedResponse(Request $request, $response)
|
||||
{
|
||||
return response('Failed, Invalid Token.', 403);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Company;
|
||||
|
||||
use App\Platform\Http\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
|
||||
class AbilitiesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Handle the incoming request.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
return response()->json(['abilities' => config('abilities.abilities')]);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Company;
|
||||
|
||||
use App\Domains\Accounts\Contracts\CompanyAddressWriter;
|
||||
use App\Domains\Accounts\Contracts\CompanyLogoManager;
|
||||
use App\Domains\Accounts\Http\Requests\CompanyLogoRequest;
|
||||
use App\Domains\Accounts\Http\Requests\CompanyRequest;
|
||||
use App\Domains\Accounts\Http\Resources\CompanyResource;
|
||||
use App\Domains\Accounts\Models\Company;
|
||||
use App\Platform\Http\Controller;
|
||||
|
||||
class CompanyController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CompanyAddressWriter $companyAddressWriter,
|
||||
private readonly CompanyLogoManager $companyLogoManager,
|
||||
) {}
|
||||
|
||||
public function updateCompany(CompanyRequest $request)
|
||||
{
|
||||
$company = Company::find($request->header('company'));
|
||||
|
||||
$this->authorize('manage company', $company);
|
||||
|
||||
$company->update($request->getCompanyPayload());
|
||||
|
||||
$this->companyAddressWriter->upsert($company, (array) $request->input('address'));
|
||||
|
||||
return new CompanyResource($company);
|
||||
}
|
||||
|
||||
public function uploadCompanyLogo(CompanyLogoRequest $request)
|
||||
{
|
||||
$company = Company::find($request->header('company'));
|
||||
|
||||
$this->authorize('manage company', $company);
|
||||
|
||||
$data = json_decode($request->company_logo);
|
||||
|
||||
if (isset($request->is_company_logo_removed) && (bool) $request->is_company_logo_removed) {
|
||||
$this->companyLogoManager->clear($company);
|
||||
}
|
||||
if ($data) {
|
||||
$this->companyLogoManager->replaceBase64($company, $data->data, $data->name);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Company;
|
||||
|
||||
use App\Domains\Accounts\Http\Requests\GetSettingsRequest;
|
||||
use App\Domains\Accounts\Http\Requests\UpdateSettingsRequest;
|
||||
use App\Domains\Accounts\Models\Company;
|
||||
use App\Domains\Accounts\Models\CompanySetting;
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use App\Platform\Http\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Arr;
|
||||
use Silber\Bouncer\BouncerFacade;
|
||||
|
||||
class CompanySettingsController extends Controller
|
||||
{
|
||||
public function show(GetSettingsRequest $request): JsonResponse
|
||||
{
|
||||
$settings = CompanySetting::getSettings((array) $request->settings, $request->header('company'));
|
||||
|
||||
return response()->json($settings);
|
||||
}
|
||||
|
||||
public function update(UpdateSettingsRequest $request): JsonResponse
|
||||
{
|
||||
$company = Company::find($request->header('company'));
|
||||
$this->authorize('manage company', $company);
|
||||
|
||||
$data = $request->settings;
|
||||
|
||||
if (
|
||||
Arr::exists($data, 'currency') &&
|
||||
(CompanySetting::getSetting('currency', $company->id) !== $data['currency']) &&
|
||||
$company->hasTransactions()
|
||||
) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'Cannot update company currency after transactions are created.',
|
||||
]);
|
||||
}
|
||||
|
||||
CompanySetting::setSettings($data, $request->header('company'));
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function checkTransactions(Request $request): JsonResponse
|
||||
{
|
||||
$company = Company::find($request->header('company'));
|
||||
|
||||
$this->authorize('manage company', $company);
|
||||
|
||||
return response()->json([
|
||||
'has_transactions' => $company->hasTransactions(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function transferOwnership(Request $request, User $user): JsonResponse
|
||||
{
|
||||
$company = Company::find($request->header('company'));
|
||||
$this->authorize('transfer company ownership', $company);
|
||||
|
||||
if (! $user->hasCompany($company->id)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'User does not belong to this company.',
|
||||
]);
|
||||
}
|
||||
|
||||
$company->update(['owner_id' => $user->id]);
|
||||
BouncerFacade::scope()->to($company->id);
|
||||
BouncerFacade::sync($user)->roles(['owner']);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Company;
|
||||
|
||||
use App\Domains\Accounts\Application\MemberService;
|
||||
use App\Domains\Accounts\Http\Requests\DeleteMemberRequest;
|
||||
use App\Domains\Accounts\Http\Requests\MemberRequest;
|
||||
use App\Domains\Accounts\Http\Resources\UserResource;
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use App\Platform\Http\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MembersController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MemberService $memberService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('viewAny', User::class);
|
||||
|
||||
$limit = $request->has('limit') ? $request->limit : 10;
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
$users = User::whereCompany()
|
||||
->applyFilters($request->all())
|
||||
->where('id', '<>', $user->id)
|
||||
->latest()
|
||||
->paginate($limit);
|
||||
|
||||
return UserResource::collection($users)
|
||||
->additional(['meta' => [
|
||||
'user_total_count' => User::whereCompany()->count(),
|
||||
]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function store(MemberRequest $request)
|
||||
{
|
||||
$this->authorize('create', User::class);
|
||||
|
||||
$user = $this->memberService->create(
|
||||
$request->getUserPayload(),
|
||||
$request->validated('companies'),
|
||||
);
|
||||
|
||||
return new UserResource($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function show(User $member)
|
||||
{
|
||||
$this->authorize('view', $member);
|
||||
|
||||
return new UserResource($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function update(MemberRequest $request, User $member)
|
||||
{
|
||||
$this->authorize('update', $member);
|
||||
|
||||
$this->memberService->update(
|
||||
$member,
|
||||
$request->getUserPayload(),
|
||||
$request->validated('companies'),
|
||||
);
|
||||
|
||||
return new UserResource($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function delete(DeleteMemberRequest $request)
|
||||
{
|
||||
$this->authorize('delete multiple users', User::class);
|
||||
|
||||
if ($request->users) {
|
||||
// Scope the candidate ids to members of the acting company so a user
|
||||
// from one company cannot delete accounts belonging to another.
|
||||
$ids = User::whereCompany()
|
||||
->whereIn('id', $request->users)
|
||||
->pluck('id')
|
||||
->toArray();
|
||||
|
||||
if ($ids) {
|
||||
$this->memberService->delete($ids);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Controllers\Company;
|
||||
|
||||
use App\Domains\Accounts\Http\Requests\RoleRequest;
|
||||
use App\Domains\Accounts\Http\Resources\RoleResource;
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use App\Platform\Http\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Silber\Bouncer\BouncerFacade;
|
||||
use Silber\Bouncer\Database\Role;
|
||||
|
||||
class RolesController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('viewAny', Role::class);
|
||||
|
||||
$roles = Role::when($request->has('orderByField'), function ($query) use ($request) {
|
||||
return $query->orderBy($request['orderByField'], $request['orderBy']);
|
||||
})
|
||||
->when($request->company_id, function ($query) use ($request) {
|
||||
return $query->where('scope', $request->company_id);
|
||||
})
|
||||
->get();
|
||||
|
||||
return RoleResource::collection($roles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function store(RoleRequest $request)
|
||||
{
|
||||
$this->authorize('create', Role::class);
|
||||
|
||||
$role = Role::create($request->getRolePayload());
|
||||
|
||||
$this->syncAbilities($request, $role);
|
||||
|
||||
return new RoleResource($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param \Spatie\Permission\Models\Role $role
|
||||
* @return Response
|
||||
*/
|
||||
public function show(Role $role)
|
||||
{
|
||||
$this->authorize('view', $role);
|
||||
|
||||
return new RoleResource($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Spatie\Permission\Models\Role $role
|
||||
* @return Response
|
||||
*/
|
||||
public function update(RoleRequest $request, Role $role)
|
||||
{
|
||||
$this->authorize('update', $role);
|
||||
|
||||
$role->update($request->getRolePayload());
|
||||
|
||||
$this->syncAbilities($request, $role);
|
||||
|
||||
return new RoleResource($role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \Spatie\Permission\Models\Role $role
|
||||
* @return Response
|
||||
*/
|
||||
public function destroy(Role $role)
|
||||
{
|
||||
$this->authorize('delete', $role);
|
||||
|
||||
$users = User::whereIs($role->name)->get()->toArray();
|
||||
|
||||
if (! empty($users)) {
|
||||
return respondJson('role_attached_to_users', 'Roles Attached to user');
|
||||
}
|
||||
|
||||
$role->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
private function syncAbilities(RoleRequest $request, $role)
|
||||
{
|
||||
foreach (config('abilities.abilities') as $ability) {
|
||||
$check = array_search($ability['ability'], array_column($request->abilities, 'ability'));
|
||||
if ($check !== false) {
|
||||
BouncerFacade::allow($role)->to($ability['ability'], $ability['model']);
|
||||
} else {
|
||||
BouncerFacade::disallow($role)->to($ability['ability'], $ability['model']);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Middleware;
|
||||
|
||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the path the user should be redirected to when they are not authenticated.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return string
|
||||
*/
|
||||
protected function redirectTo($request)
|
||||
{
|
||||
if (! $request->expectsJson()) {
|
||||
return route('login');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CompanyMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (Schema::hasTable('user_company')) {
|
||||
$user = $request->user();
|
||||
|
||||
if (! $user) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
$firstCompany = $user->companies()->first();
|
||||
|
||||
// User has no companies — allow request through without company header
|
||||
if (! $firstCompany) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Super admin without company header — allow pass-through (admin mode)
|
||||
if ($user->isSuperAdmin() && ! $request->header('company')) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if (! $request->header('company') || ! $user->hasCompany($request->header('company'))) {
|
||||
$request->headers->set('company', $firstCompany->id);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string|null $guard
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
{
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class RedirectIfUnauthorized
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, $guard = null): Response
|
||||
{
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return redirect('/login');
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Silber\Bouncer\Bouncer;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ScopeBouncer
|
||||
{
|
||||
/**
|
||||
* The Bouncer instance.
|
||||
*
|
||||
* @var Bouncer
|
||||
*/
|
||||
protected $bouncer;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct(Bouncer $bouncer)
|
||||
{
|
||||
$this->bouncer = $bouncer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the proper Bouncer scope for the incoming request.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
$company = $request->header('company');
|
||||
|
||||
if (! $company) {
|
||||
$firstCompany = $user->companies()->first();
|
||||
if (! $firstCompany) {
|
||||
return $next($request);
|
||||
}
|
||||
$company = $firstCompany->id;
|
||||
}
|
||||
|
||||
$this->bouncer->scope()->to($company);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use App\Rules\Base64Mime;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class AvatarRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'admin_avatar' => [
|
||||
'nullable',
|
||||
'file',
|
||||
'mimes:gif,jpg,png',
|
||||
'max:20000',
|
||||
],
|
||||
'avatar' => [
|
||||
'nullable',
|
||||
new Base64Mime(['gif', 'jpg', 'png']),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CompaniesRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
Rule::unique('companies'),
|
||||
'string',
|
||||
],
|
||||
'currency' => [
|
||||
'required',
|
||||
],
|
||||
'address.name' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.address_street_1' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.address_street_2' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.city' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.state' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.country_id' => [
|
||||
'required',
|
||||
],
|
||||
'address.zip' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.phone' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.fax' => [
|
||||
'nullable',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getCompanyPayload()
|
||||
{
|
||||
return collect($this->validated())
|
||||
->only([
|
||||
'name',
|
||||
'vat_id',
|
||||
'tax_id',
|
||||
])
|
||||
->merge([
|
||||
'owner_id' => $this->user()->id,
|
||||
'slug' => Str::slug($this->name),
|
||||
])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use App\Rules\Base64Mime;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CompanyLogoRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'company_logo' => [
|
||||
'nullable',
|
||||
new Base64Mime(['gif', 'jpg', 'png']),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CompanyRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
Rule::unique('companies')->ignore($this->header('company'), 'id'),
|
||||
],
|
||||
'vat_id' => [
|
||||
'nullable',
|
||||
],
|
||||
'tax_id' => [
|
||||
'nullable',
|
||||
],
|
||||
'address.country_id' => [
|
||||
'required',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getCompanyPayload()
|
||||
{
|
||||
return collect($this->validated())
|
||||
->only([
|
||||
'name',
|
||||
'vat_id',
|
||||
'tax_id',
|
||||
])
|
||||
->merge([
|
||||
'slug' => Str::slug($this->name),
|
||||
])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class DeleteMemberRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'users' => [
|
||||
'required',
|
||||
],
|
||||
'users.*' => [
|
||||
'required',
|
||||
Rule::exists('users', 'id'),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class GetSettingsRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'settings' => [
|
||||
'required',
|
||||
],
|
||||
'settings.*' => [
|
||||
'required',
|
||||
'string',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'username' => [
|
||||
'required',
|
||||
],
|
||||
'password' => [
|
||||
'required',
|
||||
],
|
||||
'device_name' => [
|
||||
'required',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use App\Rules\IdnEmail;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class MemberRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
],
|
||||
'email' => [
|
||||
'required',
|
||||
new IdnEmail,
|
||||
Rule::unique('users'),
|
||||
],
|
||||
'phone' => [
|
||||
'nullable',
|
||||
],
|
||||
'password' => [
|
||||
'required',
|
||||
'min:8',
|
||||
],
|
||||
'companies' => [
|
||||
'required',
|
||||
],
|
||||
'companies.*.id' => [
|
||||
'required',
|
||||
],
|
||||
'companies.*.role' => [
|
||||
'required',
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->getMethod() == 'PUT') {
|
||||
$rules['email'] = [
|
||||
'required',
|
||||
new IdnEmail,
|
||||
Rule::unique('users')->ignore($this->member),
|
||||
];
|
||||
$rules['password'] = [
|
||||
'nullable',
|
||||
'min:8',
|
||||
];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function getUserPayload()
|
||||
{
|
||||
return collect($this->validated())
|
||||
->only([
|
||||
'name',
|
||||
'email',
|
||||
'phone',
|
||||
'password',
|
||||
])
|
||||
->merge([
|
||||
'creator_id' => $this->user()->id,
|
||||
])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use App\Rules\IdnEmail;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ProfileRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => [
|
||||
'required',
|
||||
],
|
||||
'password' => [
|
||||
'nullable',
|
||||
'min:8',
|
||||
],
|
||||
'email' => [
|
||||
'required',
|
||||
new IdnEmail,
|
||||
Rule::unique('users')->ignore(Auth::id(), 'id'),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class RoleRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
Rule::unique('roles')->where('scope', $this->header('company')),
|
||||
],
|
||||
'abilities' => [
|
||||
'required',
|
||||
],
|
||||
'abilities.*' => [
|
||||
'required',
|
||||
],
|
||||
];
|
||||
|
||||
if ($this->getMethod() == 'PUT') {
|
||||
$rules['name'] = [
|
||||
'required',
|
||||
'string',
|
||||
Rule::unique('roles')
|
||||
->ignore($this->route('role')->id, 'id')
|
||||
->where('scope', $this->header('company')),
|
||||
];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function getRolePayload()
|
||||
{
|
||||
return collect($this->except('abilities'))
|
||||
->merge([
|
||||
'scope' => $this->header('company'),
|
||||
])
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateSettingsRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'settings' => [
|
||||
'required',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Resources;
|
||||
|
||||
use App\Domains\Contacts\Http\Resources\AddressResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CompanyResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'vat_id' => $this->vat_id,
|
||||
'tax_id' => $this->tax_id,
|
||||
'logo' => $this->logo,
|
||||
'logo_path' => $this->logo_path,
|
||||
'unique_hash' => $this->unique_hash,
|
||||
'owner_id' => $this->owner_id,
|
||||
'slug' => $this->slug,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
'address' => $this->when($this->address()->exists(), function () {
|
||||
return new AddressResource($this->address);
|
||||
}),
|
||||
'owner' => $this->when($this->relationLoaded('owner'), function () {
|
||||
return new UserResource($this->owner);
|
||||
}),
|
||||
'roles' => RoleResource::collection($this->roles),
|
||||
'user_role' => $this->getUserRoleTitle(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getUserRoleTitle(): ?string
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if (! $user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return DB::table('assigned_roles')
|
||||
->join('roles', 'roles.id', '=', 'assigned_roles.role_id')
|
||||
->where('assigned_roles.entity_id', $user->id)
|
||||
->where('assigned_roles.entity_type', $user->getMorphClass())
|
||||
->where('assigned_roles.scope', $this->id)
|
||||
->value('roles.title');
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Resources\CustomerPortal;
|
||||
|
||||
use App\Domains\Contacts\Http\Resources\CustomerPortal\AddressResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class CompanyResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'slug' => $this->slug,
|
||||
'logo' => $this->logo,
|
||||
'logo_path' => $this->logo_path,
|
||||
'unique_hash' => $this->unique_hash,
|
||||
'owner_id' => $this->owner_id,
|
||||
'address' => $this->when($this->address()->exists(), function () {
|
||||
return new AddressResource($this->address);
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Resources\CustomerPortal;
|
||||
|
||||
use App\Domains\Money\Http\Resources\CustomerPortal\CurrencyResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'phone' => $this->phone,
|
||||
'role' => $this->role,
|
||||
'contact_name' => $this->contact_name,
|
||||
'company_name' => $this->company_name,
|
||||
'website' => $this->website,
|
||||
'enable_portal' => $this->enable_portal,
|
||||
'currency_id' => $this->currency_id,
|
||||
'facebook_id' => $this->facebook_id,
|
||||
'google_id' => $this->google_id,
|
||||
'github_id' => $this->github_id,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
'avatar' => $this->avatar,
|
||||
'is_owner' => $this->isOwner(),
|
||||
'roles' => $this->roles,
|
||||
'formatted_created_at' => $this->formattedCreatedAt,
|
||||
'currency' => $this->when($this->currency()->exists(), function () {
|
||||
return new CurrencyResource($this->currency);
|
||||
}),
|
||||
'companies' => $this->when($this->companies()->exists(), function () {
|
||||
return CompanyResource::collection($this->companies);
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Resources;
|
||||
|
||||
use App\Domains\Accounts\Models\CompanySetting;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class RoleResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'title' => $this->title,
|
||||
'level' => $this->level,
|
||||
'formatted_created_at' => $this->getFormattedAt(),
|
||||
'abilities' => $this->getAbilities(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getFormattedAt()
|
||||
{
|
||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $this->scope);
|
||||
|
||||
return Carbon::parse($this->created_at)->translatedFormat($dateFormat);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Http\Resources;
|
||||
|
||||
use App\Domains\Money\Http\Resources\CurrencyResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param Request $request
|
||||
*/
|
||||
public function toArray($request): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'phone' => $this->phone,
|
||||
'role' => $this->role,
|
||||
'contact_name' => $this->contact_name,
|
||||
'company_name' => $this->company_name,
|
||||
'website' => $this->website,
|
||||
'enable_portal' => $this->enable_portal,
|
||||
'currency_id' => $this->currency_id,
|
||||
'facebook_id' => $this->facebook_id,
|
||||
'google_id' => $this->google_id,
|
||||
'github_id' => $this->github_id,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
'avatar' => $this->avatar,
|
||||
'is_owner' => $this->isOwner(),
|
||||
'is_super_admin' => $this->isSuperAdmin(),
|
||||
'roles' => $this->roles,
|
||||
'formatted_created_at' => $this->formattedCreatedAt,
|
||||
'currency' => $this->when($this->currency()->exists(), function () {
|
||||
return new CurrencyResource($this->currency);
|
||||
}),
|
||||
'companies' => $this->when($this->companies()->exists(), function () {
|
||||
return CompanyResource::collection($this->companies);
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\Item;
|
||||
use App\Domains\Catalog\Models\Unit;
|
||||
use App\Domains\Contacts\Models\Address;
|
||||
use App\Domains\Contacts\Models\Customer;
|
||||
use App\Domains\Metadata\Models\CustomField;
|
||||
use App\Domains\Metadata\Models\CustomFieldValue;
|
||||
use App\Domains\Money\Models\ExchangeRateLog;
|
||||
use App\Domains\Money\Models\ExchangeRateProvider;
|
||||
use App\Domains\Purchases\Models\Expense;
|
||||
use App\Domains\Purchases\Models\ExpenseCategory;
|
||||
use App\Domains\Receivables\Models\Payment;
|
||||
use App\Domains\Receivables\Models\PaymentMethod;
|
||||
use App\Domains\Sales\Models\Estimate;
|
||||
use App\Domains\Sales\Models\Invoice;
|
||||
use App\Domains\Sales\Models\RecurringInvoice;
|
||||
use App\Domains\Taxation\Models\TaxType;
|
||||
use App\Platform\Storage\Models\FileDisk;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Silber\Bouncer\Database\Role;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
class Company extends Model implements HasMedia
|
||||
{
|
||||
protected $table = 'companies';
|
||||
|
||||
use HasFactory;
|
||||
use InteractsWithMedia;
|
||||
|
||||
public function registerMediaCollections(): void
|
||||
{
|
||||
$this->addMediaCollection('logo')
|
||||
->useDisk('public')
|
||||
->singleFile();
|
||||
}
|
||||
|
||||
protected $guarded = [
|
||||
'id',
|
||||
];
|
||||
|
||||
protected $appends = ['logo', 'logo_path'];
|
||||
|
||||
public function getRolesAttribute()
|
||||
{
|
||||
return Role::where('scope', $this->id)
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getLogoPathAttribute()
|
||||
{
|
||||
$logo = $this->getMedia('logo')->first();
|
||||
|
||||
$isSystem = FileDisk::whereSetAsDefault(true)->first()->isSystem();
|
||||
|
||||
if ($logo) {
|
||||
if ($isSystem) {
|
||||
return $logo->getPath();
|
||||
} else {
|
||||
return $logo->getFullUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getLogoAttribute()
|
||||
{
|
||||
$logo = $this->getMedia('logo')->first();
|
||||
|
||||
if ($logo) {
|
||||
return $logo->getFullUrl();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function customers(): HasMany
|
||||
{
|
||||
return $this->hasMany(Customer::class);
|
||||
}
|
||||
|
||||
public function owner(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'owner_id');
|
||||
}
|
||||
|
||||
public function settings(): HasMany
|
||||
{
|
||||
return $this->hasMany(CompanySetting::class);
|
||||
}
|
||||
|
||||
public function recurringInvoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(RecurringInvoice::class);
|
||||
}
|
||||
|
||||
public function customFields(): HasMany
|
||||
{
|
||||
return $this->hasMany(CustomField::class);
|
||||
}
|
||||
|
||||
public function customFieldValues(): HasMany
|
||||
{
|
||||
return $this->hasMany(CustomFieldValue::class);
|
||||
}
|
||||
|
||||
public function exchangeRateLogs(): HasMany
|
||||
{
|
||||
return $this->hasMany(ExchangeRateLog::class);
|
||||
}
|
||||
|
||||
public function exchangeRateProviders(): HasMany
|
||||
{
|
||||
return $this->hasMany(ExchangeRateProvider::class);
|
||||
}
|
||||
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
|
||||
public function expenses(): HasMany
|
||||
{
|
||||
return $this->hasMany(Expense::class);
|
||||
}
|
||||
|
||||
public function units(): HasMany
|
||||
{
|
||||
return $this->hasMany(Unit::class);
|
||||
}
|
||||
|
||||
public function expenseCategories(): HasMany
|
||||
{
|
||||
return $this->hasMany(ExpenseCategory::class);
|
||||
}
|
||||
|
||||
public function taxTypes(): HasMany
|
||||
{
|
||||
return $this->hasMany(TaxType::class);
|
||||
}
|
||||
|
||||
public function items(): HasMany
|
||||
{
|
||||
return $this->hasMany(Item::class);
|
||||
}
|
||||
|
||||
public function payments(): HasMany
|
||||
{
|
||||
return $this->hasMany(Payment::class);
|
||||
}
|
||||
|
||||
public function paymentMethods(): HasMany
|
||||
{
|
||||
return $this->hasMany(PaymentMethod::class);
|
||||
}
|
||||
|
||||
public function estimates(): HasMany
|
||||
{
|
||||
return $this->hasMany(Estimate::class);
|
||||
}
|
||||
|
||||
public function address(): HasOne
|
||||
{
|
||||
return $this->hasOne(Address::class);
|
||||
}
|
||||
|
||||
public function users(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'user_company', 'company_id', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the company has any business data such as customers,
|
||||
* items, invoices, estimates, expenses, payments, or recurring invoices.
|
||||
*/
|
||||
public function hasTransactions(): bool
|
||||
{
|
||||
if (
|
||||
$this->customers()->exists() ||
|
||||
$this->items()->exists() ||
|
||||
$this->invoices()->exists() ||
|
||||
$this->estimates()->exists() ||
|
||||
$this->expenses()->exists() ||
|
||||
$this->payments()->exists() ||
|
||||
$this->recurringInvoices()->exists()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class CompanySetting extends Model
|
||||
{
|
||||
protected $table = 'company_settings';
|
||||
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['company_id', 'option', 'value'];
|
||||
|
||||
public function company(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function scopeWhereCompany($query, $company_id)
|
||||
{
|
||||
$query->where('company_id', $company_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk create or update settings for a specific company.
|
||||
*/
|
||||
public static function setSettings(array $settings, mixed $company_id): void
|
||||
{
|
||||
foreach ($settings as $key => $value) {
|
||||
self::updateOrCreate(
|
||||
[
|
||||
'option' => $key,
|
||||
'company_id' => $company_id,
|
||||
],
|
||||
[
|
||||
'option' => $key,
|
||||
'company_id' => $company_id,
|
||||
'value' => $value,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all settings for a company as a key-value collection.
|
||||
*/
|
||||
public static function getAllSettings(mixed $company_id): Collection
|
||||
{
|
||||
return static::whereCompany($company_id)->get()->mapWithKeys(function ($item) {
|
||||
return [$item['option'] => $item['value']];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve specific settings for a company as a key-value collection.
|
||||
*/
|
||||
public static function getSettings(array $settings, mixed $company_id): Collection
|
||||
{
|
||||
return static::whereIn('option', $settings)->whereCompany($company_id)
|
||||
->get()->mapWithKeys(function ($item) {
|
||||
return [$item['option'] => $item['value']];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a single company setting value by key, or null if not found.
|
||||
*/
|
||||
public static function getSetting(string $key, mixed $company_id): mixed
|
||||
{
|
||||
$setting = static::whereOption($key)->whereCompany($company_id)->first();
|
||||
|
||||
if ($setting) {
|
||||
return $setting->value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,414 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Models;
|
||||
|
||||
use App\Domains\Accounts\Notifications\MailResetPasswordNotification;
|
||||
use App\Domains\Catalog\Models\Item;
|
||||
use App\Domains\Contacts\Models\Address;
|
||||
use App\Domains\Contacts\Models\Customer;
|
||||
use App\Domains\Metadata\Concerns\HasCustomFields;
|
||||
use App\Domains\Money\Models\Currency;
|
||||
use App\Domains\Purchases\Models\Expense;
|
||||
use App\Domains\Receivables\Models\Payment;
|
||||
use App\Domains\Sales\Models\Estimate;
|
||||
use App\Domains\Sales\Models\Invoice;
|
||||
use App\Domains\Sales\Models\RecurringInvoice;
|
||||
use App\Support\SafeOrderBy;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Silber\Bouncer\Database\HasRolesAndAbilities;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
class User extends Authenticatable implements HasMedia
|
||||
{
|
||||
protected $table = 'users';
|
||||
|
||||
use HasApiTokens;
|
||||
use HasCustomFields;
|
||||
use HasFactory;
|
||||
use HasRolesAndAbilities;
|
||||
use InteractsWithMedia;
|
||||
use Notifiable;
|
||||
|
||||
public function registerMediaCollections(): void
|
||||
{
|
||||
$this->addMediaCollection('admin_avatar')
|
||||
->useDisk('public')
|
||||
->singleFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = [
|
||||
'id',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for arrays.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
protected $with = [
|
||||
'currency',
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'formattedCreatedAt',
|
||||
'avatar',
|
||||
];
|
||||
|
||||
/**
|
||||
* Find the user instance for the given username.
|
||||
*/
|
||||
public function findForPassport(string $username): ?self
|
||||
{
|
||||
return $this->where('email', $username)->first();
|
||||
}
|
||||
|
||||
public function setPasswordAttribute(string $value): void
|
||||
{
|
||||
if ($value != null) {
|
||||
$this->attributes['password'] = bcrypt($value);
|
||||
}
|
||||
}
|
||||
|
||||
public function isSuperAdmin(): bool
|
||||
{
|
||||
return $this->role === 'super admin';
|
||||
}
|
||||
|
||||
public function isSuperAdminOrAdmin(): bool
|
||||
{
|
||||
return ($this->role == 'super admin') || ($this->role == 'admin');
|
||||
}
|
||||
|
||||
public static function login(object $request): bool
|
||||
{
|
||||
$remember = $request->remember;
|
||||
$email = $request->email;
|
||||
$password = $request->password;
|
||||
|
||||
return \Auth::attempt(['email' => $email, 'password' => $password], $remember);
|
||||
}
|
||||
|
||||
public function getFormattedCreatedAtAttribute($value)
|
||||
{
|
||||
$companyId = request()->header('company');
|
||||
|
||||
if (! $companyId || ! CompanySetting::where('company_id', $companyId)->exists()) {
|
||||
$firstCompany = $this->companies()->first();
|
||||
if (! $firstCompany) {
|
||||
return Carbon::parse($this->created_at)->format('Y-m-d');
|
||||
}
|
||||
$companyId = $firstCompany->id;
|
||||
}
|
||||
|
||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $companyId);
|
||||
|
||||
return Carbon::parse($this->created_at)->format($dateFormat);
|
||||
}
|
||||
|
||||
public function estimates(): HasMany
|
||||
{
|
||||
return $this->hasMany(Estimate::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function customers(): HasMany
|
||||
{
|
||||
return $this->hasMany(Customer::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function recurringInvoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(RecurringInvoice::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function currency(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Currency::class, 'currency_id');
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function companies(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Company::class, 'user_company', 'user_id', 'company_id');
|
||||
}
|
||||
|
||||
public function expenses(): HasMany
|
||||
{
|
||||
return $this->hasMany(Expense::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function payments(): HasMany
|
||||
{
|
||||
return $this->hasMany(Payment::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function invoices(): HasMany
|
||||
{
|
||||
return $this->hasMany(Invoice::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function items(): HasMany
|
||||
{
|
||||
return $this->hasMany(Item::class, 'creator_id');
|
||||
}
|
||||
|
||||
public function settings(): HasMany
|
||||
{
|
||||
return $this->hasMany(UserSetting::class, 'user_id');
|
||||
}
|
||||
|
||||
public function addresses(): HasMany
|
||||
{
|
||||
return $this->hasMany(Address::class);
|
||||
}
|
||||
|
||||
public function billingAddress(): HasOne
|
||||
{
|
||||
return $this->hasOne(Address::class)->where('type', Address::BILLING_TYPE);
|
||||
}
|
||||
|
||||
public function shippingAddress(): HasOne
|
||||
{
|
||||
return $this->hasOne(Address::class)->where('type', Address::SHIPPING_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the mail body for reset password notification mail.
|
||||
*/
|
||||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
$this->notify(new MailResetPasswordNotification($token));
|
||||
}
|
||||
|
||||
public function scopeWhereOrder($query, $orderByField, $orderBy)
|
||||
{
|
||||
SafeOrderBy::apply($query, $orderByField, $orderBy);
|
||||
}
|
||||
|
||||
public function scopeWhereSearch($query, $search)
|
||||
{
|
||||
foreach (explode(' ', $search) as $term) {
|
||||
$query->where(function ($query) use ($term) {
|
||||
$query->where('name', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('email', 'LIKE', '%'.$term.'%')
|
||||
->orWhere('phone', 'LIKE', '%'.$term.'%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function scopeWhereContactName($query, $contactName)
|
||||
{
|
||||
return $query->where('contact_name', 'LIKE', '%'.$contactName.'%');
|
||||
}
|
||||
|
||||
public function scopeWhereDisplayName($query, $displayName)
|
||||
{
|
||||
return $query->where('name', 'LIKE', '%'.$displayName.'%');
|
||||
}
|
||||
|
||||
public function scopeWherePhone($query, $phone)
|
||||
{
|
||||
return $query->where('phone', 'LIKE', '%'.$phone.'%');
|
||||
}
|
||||
|
||||
public function scopeWhereEmail($query, $email)
|
||||
{
|
||||
return $query->where('email', 'LIKE', '%'.$email.'%');
|
||||
}
|
||||
|
||||
public function scopeWhereCompany($query)
|
||||
{
|
||||
return $query->whereHas('companies', function ($q) {
|
||||
$q->where('company_id', request()->header('company'));
|
||||
});
|
||||
}
|
||||
|
||||
public function scopePaginateData($query, $limit)
|
||||
{
|
||||
if ($limit == 'all') {
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
return $query->paginate($limit);
|
||||
}
|
||||
|
||||
public function scopeApplyFilters($query, array $filters)
|
||||
{
|
||||
$filters = collect($filters);
|
||||
|
||||
if ($filters->get('search')) {
|
||||
$query->whereSearch($filters->get('search'));
|
||||
}
|
||||
|
||||
if ($filters->get('display_name')) {
|
||||
$query->whereDisplayName($filters->get('display_name'));
|
||||
}
|
||||
|
||||
if ($filters->get('email')) {
|
||||
$query->whereEmail($filters->get('email'));
|
||||
}
|
||||
|
||||
if ($filters->get('phone')) {
|
||||
$query->wherePhone($filters->get('phone'));
|
||||
}
|
||||
|
||||
if ($filters->get('role')) {
|
||||
$query->whereHas('roles', function ($q) use ($filters) {
|
||||
$q->where('roles.id', $filters->get('role'));
|
||||
});
|
||||
}
|
||||
|
||||
if ($filters->get('orderByField') || $filters->get('orderBy')) {
|
||||
$field = $filters->get('orderByField') ? $filters->get('orderByField') : 'name';
|
||||
$orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc';
|
||||
$query->whereOrder($field, $orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
public function scopeWhereSuperAdmin($query)
|
||||
{
|
||||
$query->orWhere('role', 'super admin');
|
||||
}
|
||||
|
||||
public function scopeApplyInvoiceFilters($query, array $filters)
|
||||
{
|
||||
$filters = collect($filters);
|
||||
|
||||
if ($filters->get('from_date') && $filters->get('to_date')) {
|
||||
$start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date'));
|
||||
$end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date'));
|
||||
$query->invoicesBetween($start, $end);
|
||||
}
|
||||
}
|
||||
|
||||
public function scopeInvoicesBetween($query, $start, $end)
|
||||
{
|
||||
$query->whereHas('invoices', function ($query) use ($start, $end) {
|
||||
$query->whereBetween(
|
||||
'invoice_date',
|
||||
[$start->format('Y-m-d'), $end->format('Y-m-d')]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public function getAvatarAttribute()
|
||||
{
|
||||
$avatar = $this->getMedia('admin_avatar')->first();
|
||||
|
||||
if ($avatar) {
|
||||
return asset($avatar->getUrl());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk upsert user settings, creating or updating each key-value pair.
|
||||
*/
|
||||
public function setSettings(array $settings): void
|
||||
{
|
||||
foreach ($settings as $key => $value) {
|
||||
$this->settings()->updateOrCreate(
|
||||
[
|
||||
'key' => $key,
|
||||
],
|
||||
[
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasCompany(int $company_id): bool
|
||||
{
|
||||
$companies = $this->companies()->pluck('company_id')->toArray();
|
||||
|
||||
return in_array($company_id, $companies);
|
||||
}
|
||||
|
||||
public function getAllSettings(): Collection
|
||||
{
|
||||
return $this->settings()->get()->mapWithKeys(function ($item) {
|
||||
return [$item['key'] => $item['value']];
|
||||
});
|
||||
}
|
||||
|
||||
public function getSettings(array $settings): Collection
|
||||
{
|
||||
return $this->settings()->whereIn('key', $settings)->get()->mapWithKeys(function ($item) {
|
||||
return [$item['key'] => $item['value']];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user is the owner of the current company.
|
||||
*/
|
||||
public function isOwner(): bool
|
||||
{
|
||||
if (Schema::hasColumn('companies', 'owner_id')) {
|
||||
$company = Company::find(request()->header('company'));
|
||||
|
||||
if ($company && $this->id == $company->owner_id) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return $this->role == 'super admin' || $this->role == 'admin';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the user has the required permissions based on ability data,
|
||||
* considering super-admin status, company ownership, and Bouncer abilities.
|
||||
*/
|
||||
public function checkAccess(object $data): bool
|
||||
{
|
||||
if (! empty($data->data['super_admin_only']) && $data->data['super_admin_only']) {
|
||||
return $this->isSuperAdmin();
|
||||
}
|
||||
|
||||
if ($this->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((! $data->data['owner_only']) && empty($data->data['ability'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((! $data->data['owner_only']) && (! empty($data->data['ability'])) && (! empty($data->data['model'])) && $this->can($data->data['ability'], $data->data['model'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((! $data->data['owner_only']) && $this->can($data->data['ability'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class UserSetting extends Model
|
||||
{
|
||||
protected $table = 'user_settings';
|
||||
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Notifications;
|
||||
|
||||
use Illuminate\Auth\Notifications\ResetPassword;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class MailResetPasswordNotification extends ResetPassword
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($token)
|
||||
{
|
||||
parent::__construct($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
*/
|
||||
public function via($notifiable): array
|
||||
{
|
||||
return ['mail'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
*/
|
||||
public function toMail($notifiable): MailMessage
|
||||
{
|
||||
$link = url('/reset-password/'.$this->token);
|
||||
|
||||
return (new MailMessage)
|
||||
->subject('Reset Password Notification')
|
||||
->line('Hello! You are receiving this email because we received a password reset request for your account.')
|
||||
->action('Reset Password', $link)
|
||||
->line('This password reset link will expire in '.config('auth.passwords.users.expire').' minutes')
|
||||
->line('If you did not request a password reset, no further action is required.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
*/
|
||||
public function toArray($notifiable): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Policies;
|
||||
|
||||
use App\Domains\Accounts\Models\Company;
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class CompanyPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function create(User $user): bool
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function delete(User $user, Company $company): bool
|
||||
{
|
||||
if ($user->id == $company->owner_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function transferOwnership(User $user, Company $company)
|
||||
{
|
||||
if ($user->id == $company->owner_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Policies;
|
||||
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class OwnerPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function managedByOwner(User $user)
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Policies;
|
||||
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Silber\Bouncer\Database\Role;
|
||||
|
||||
class RolePolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->isOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, Role $role): bool
|
||||
{
|
||||
return $user->isOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->isOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, Role $role): bool
|
||||
{
|
||||
return $user->isOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, Role $role): bool
|
||||
{
|
||||
return $user->isOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, Role $role): bool
|
||||
{
|
||||
return $user->isOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, Role $role): bool
|
||||
{
|
||||
return $user->isOwner();
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Policies;
|
||||
|
||||
use App\Domains\Accounts\Models\Company;
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class SettingsPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
public function manageCompany(User $user, Company $company)
|
||||
{
|
||||
if ($user->id == $company->owner_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domains\Accounts\Policies;
|
||||
|
||||
use App\Domains\Accounts\Models\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class UserPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function view(User $user, User $model): bool
|
||||
{
|
||||
return $user->isOwner() && $this->sharesActiveCompany($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, User $model): bool
|
||||
{
|
||||
return $user->isOwner() && $this->sharesActiveCompany($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(User $user, User $model): bool
|
||||
{
|
||||
return $user->isOwner() && $this->sharesActiveCompany($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function restore(User $user, User $model): bool
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function forceDelete(User $user, User $model): bool
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can invite the model.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function invite(User $user, User $model)
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete models.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function deleteMultiple(User $user)
|
||||
{
|
||||
if ($user->isOwner()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A company owner may only act on members of the company set in the
|
||||
* `company` request header. Without this, view/update/delete resolve the
|
||||
* target user by global id, which would let an owner of one company read
|
||||
* or overwrite users belonging to another company (cross-tenant IDOR).
|
||||
*/
|
||||
private function sharesActiveCompany(User $model): bool
|
||||
{
|
||||
$companyId = request()->header('company');
|
||||
|
||||
return $companyId
|
||||
&& $model->companies()->wherePivot('company_id', $companyId)->exists();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user