Merge branch 'master' into v3.0

This commit is contained in:
Darko Gjorgjijoski
2026-04-03 14:36:24 +02:00
15 changed files with 94 additions and 17 deletions

View File

@@ -61,10 +61,10 @@ class CompaniesController extends Controller
$company = Company::find($request->header('company')); $company = Company::find($request->header('company'));
$this->authorize('transfer company ownership', $company); $this->authorize('transfer company ownership', $company);
if ($user->hasCompany($company->id)) { if (! $user->hasCompany($company->id)) {
return response()->json([ return response()->json([
'success' => false, 'success' => false,
'message' => 'User does not belongs to this company.', 'message' => 'User does not belong to this company.',
]); ]);
} }

View File

@@ -92,7 +92,11 @@ class CustomersController extends Controller
{ {
$this->authorize('delete multiple customers'); $this->authorize('delete multiple customers');
Customer::deleteCustomers($request->ids); $ids = Customer::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
Customer::deleteCustomers($ids);
return response()->json([ return response()->json([
'success' => true, 'success' => true,

View File

@@ -21,6 +21,7 @@ class CloneEstimateController extends Controller
*/ */
public function __invoke(Request $request, Estimate $estimate) public function __invoke(Request $request, Estimate $estimate)
{ {
$this->authorize('view', $estimate);
$this->authorize('create', Estimate::class); $this->authorize('create', Estimate::class);
$date = Carbon::now(); $date = Carbon::now();

View File

@@ -68,7 +68,11 @@ class EstimatesController extends Controller
{ {
$this->authorize('delete multiple estimates'); $this->authorize('delete multiple estimates');
Estimate::destroy($request->ids); $ids = Estimate::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
Estimate::destroy($ids);
return response()->json([ return response()->json([
'success' => true, 'success' => true,

View File

@@ -81,7 +81,11 @@ class ExpensesController extends Controller
{ {
$this->authorize('delete multiple expenses'); $this->authorize('delete multiple expenses');
Expense::destroy($request->ids); $ids = Expense::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
Expense::destroy($ids);
return response()->json([ return response()->json([
'success' => true, 'success' => true,

View File

@@ -25,7 +25,8 @@ class SearchController extends Controller
->paginate(10); ->paginate(10);
if ($user->isOwner()) { if ($user->isOwner()) {
$users = User::applyFilters($request->only(['search'])) $users = User::whereCompany()
->applyFilters($request->only(['search']))
->latest() ->latest()
->paginate(10); ->paginate(10);
} }

View File

@@ -21,6 +21,7 @@ class CloneInvoiceController extends Controller
*/ */
public function __invoke(Request $request, Invoice $invoice) public function __invoke(Request $request, Invoice $invoice)
{ {
$this->authorize('view', $invoice);
$this->authorize('create', Invoice::class); $this->authorize('create', Invoice::class);
$date = Carbon::now(); $date = Carbon::now();

View File

@@ -100,7 +100,11 @@ class InvoicesController extends Controller
{ {
$this->authorize('delete multiple invoices'); $this->authorize('delete multiple invoices');
Invoice::deleteInvoices($request->ids); $ids = Invoice::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
Invoice::deleteInvoices($ids);
return response()->json([ return response()->json([
'success' => true, 'success' => true,

View File

@@ -90,7 +90,11 @@ class ItemsController extends Controller
{ {
$this->authorize('delete multiple items'); $this->authorize('delete multiple items');
Item::destroy($request->ids); $ids = Item::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
Item::destroy($ids);
return response()->json([ return response()->json([
'success' => true, 'success' => true,

View File

@@ -73,7 +73,11 @@ class PaymentsController extends Controller
{ {
$this->authorize('delete multiple payments'); $this->authorize('delete multiple payments');
Payment::deletePayments($request->ids); $ids = Payment::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
Payment::deletePayments($ids);
return response()->json([ return response()->json([
'success' => true, 'success' => true,

View File

@@ -84,7 +84,11 @@ class RecurringInvoiceController extends Controller
{ {
$this->authorize('delete multiple recurring invoices'); $this->authorize('delete multiple recurring invoices');
RecurringInvoice::deleteRecurringInvoice($request->ids); $ids = RecurringInvoice::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
RecurringInvoice::deleteRecurringInvoice($ids);
return response()->json([ return response()->json([
'success' => true, 'success' => true,

View File

@@ -25,14 +25,15 @@ class UsersController extends Controller
$user = $request->user(); $user = $request->user();
$users = User::applyFilters($request->all()) $users = User::whereCompany()
->applyFilters($request->all())
->where('id', '<>', $user->id) ->where('id', '<>', $user->id)
->latest() ->latest()
->paginate($limit); ->paginate($limit);
return UserResource::collection($users) return UserResource::collection($users)
->additional(['meta' => [ ->additional(['meta' => [
'user_total_count' => User::count(), 'user_total_count' => User::whereCompany()->count(),
]]); ]]);
} }

View File

@@ -218,6 +218,13 @@ class User extends Authenticatable implements HasMedia
return $query->where('email', 'LIKE', '%'.$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) public function scopePaginateData($query, $limit)
{ {
if ($limit == 'all') { if ($limit == 'all') {

View File

@@ -32,7 +32,7 @@ class CustomerPolicy
*/ */
public function view(User $user, Customer $customer): bool public function view(User $user, Customer $customer): bool
{ {
if (BouncerFacade::can('view-customer', $customer)) { if (BouncerFacade::can('view-customer', $customer) && $user->hasCompany($customer->company_id)) {
return true; return true;
} }
@@ -60,7 +60,7 @@ class CustomerPolicy
*/ */
public function update(User $user, Customer $customer): bool public function update(User $user, Customer $customer): bool
{ {
if (BouncerFacade::can('edit-customer', $customer)) { if (BouncerFacade::can('edit-customer', $customer) && $user->hasCompany($customer->company_id)) {
return true; return true;
} }
@@ -74,7 +74,7 @@ class CustomerPolicy
*/ */
public function delete(User $user, Customer $customer): bool public function delete(User $user, Customer $customer): bool
{ {
if (BouncerFacade::can('delete-customer', $customer)) { if (BouncerFacade::can('delete-customer', $customer) && $user->hasCompany($customer->company_id)) {
return true; return true;
} }
@@ -88,7 +88,7 @@ class CustomerPolicy
*/ */
public function restore(User $user, Customer $customer): bool public function restore(User $user, Customer $customer): bool
{ {
if (BouncerFacade::can('delete-customer', $customer)) { if (BouncerFacade::can('delete-customer', $customer) && $user->hasCompany($customer->company_id)) {
return true; return true;
} }
@@ -102,7 +102,7 @@ class CustomerPolicy
*/ */
public function forceDelete(User $user, Customer $customer): bool public function forceDelete(User $user, Customer $customer): bool
{ {
if (BouncerFacade::can('delete-customer', $customer)) { if (BouncerFacade::can('delete-customer', $customer) && $user->hasCompany($customer->company_id)) {
return true; return true;
} }

View File

@@ -2,6 +2,7 @@
use App\Http\Controllers\V1\Admin\Customer\CustomersController; use App\Http\Controllers\V1\Admin\Customer\CustomersController;
use App\Http\Requests\CustomerRequest; use App\Http\Requests\CustomerRequest;
use App\Models\Company;
use App\Models\Customer; use App\Models\Customer;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\User; use App\Models\User;
@@ -157,3 +158,40 @@ test('delete multiple customer', function () {
'success' => true, 'success' => true,
]); ]);
}); });
test('cannot view customer from another company', function () {
$otherCompany = Company::factory()->create();
$otherCustomer = Customer::factory()->create([
'company_id' => $otherCompany->id,
]);
getJson("api/v1/customers/{$otherCustomer->id}")
->assertForbidden();
});
test('cannot update customer from another company', function () {
$otherCompany = Company::factory()->create();
$otherCustomer = Customer::factory()->create([
'company_id' => $otherCompany->id,
]);
putJson("api/v1/customers/{$otherCustomer->id}", [
'name' => 'Hacked Name',
'email' => 'hacked@example.com',
])->assertForbidden();
});
test('cannot bulk delete customer from another company', function () {
$otherCompany = Company::factory()->create();
$otherCustomer = Customer::factory()->create([
'company_id' => $otherCompany->id,
]);
postJson('api/v1/customers/delete', [
'ids' => [$otherCustomer->id],
])->assertOk();
$this->assertDatabaseHas('customers', [
'id' => $otherCustomer->id,
]);
});