---
task: enable store owner to delete and edit stores he owns in manage stores admin page
cycles: 5
context: true
private: false
started: 2026-05-16T00:00:00Z
finished: 2026-05-16T00:01:00Z
---

## files
- resources/js/Pages/ManageStoresAdmin.vue — frontend page; already has canModifyStore checks and edit/delete buttons conditioned on `store.user_can_manage`; no changes needed unless navigation from service grid is added
- resources/js/Pages/Fragments/Home/HomeStoreOwner.vue [lines 34-60] — store owner home page services grid; has "My Stores" in `balanceFooterItems` → ManageStoresAdmin but NOT in the services grid
- app/Http/Controllers/Market/StoreController.php [lines 626-732] — `update()` method; allows edit if `$store->owner_id === $user->id` but does NOT check store_managers pivot table, causing a mismatch with `user_can_manage` flag
- app/Http/Controllers/Market/StoreController.php [lines 1403-1435] — `deleteStore_Admin()` method; allows owner to delete but uses `catch (Exception $e)` without backslash (namespace bug) and does NOT check store_managers pivot
- app/Http/Controllers/Market/StoreController.php [lines 1437-1475] — `toggleStoreStatus_Admin()` method; same `catch (Exception $e)` bug
- app/Http/Controllers/Market/StoreController.php [lines 1297-1353] — `listStores_Admin()`: sets `user_can_manage = true` when user is in `allowedUserIds` (includes owner, manager_id, and managers pivot) — but backend edit/delete endpoints don't fully mirror this logic

## steps
1. In `StoreController.php` `deleteStore_Admin()` (line ~1425): fix `catch (Exception $e)` → `catch (\Exception $e)` (namespace issue; unqualified `Exception` in a namespace resolves to `App\Http\Controllers\Market\Exception` which doesn't exist)

2. In `StoreController.php` `toggleStoreStatus_Admin()` (line ~1468): same fix — `catch (Exception $e)` → `catch (\Exception $e)`

3. In `StoreController.php` `deleteStore_Admin()` (line ~1423): after `$allowedIds` is defined, add a check for managers pivot so any user listed in `store_managers` table can also delete (currently only checks `owner_id` and `manager_id` columns). Add:
   ```php
   $isManagerViaPivot = $store->managers()->whereIn('user_id', $allowedIds)->exists();
   if (!in_array($store->owner_id, $allowedIds) && !in_array($store->manager_id, $allowedIds) && !$isManagerViaPivot) {
       return ResponseHelper::returnUnauthorized();
   }
   ```

4. In `StoreController.php` `update()` (line ~648): the permission check only allows: isBig3, isParentOfOwner, or `$store->owner_id === $user->id`. Add store managers pivot check so managers assigned via pivot can also edit:
   ```php
   $isStoreOwner = $acctType === UserTypes::STORE_OWNER;
   $isManagerViaPivot = $store->managers()->where('user_id', $user->id)->exists();
   $isDirectManager = $store->manager_id === $user->id;
   if (!$isBig3 && !$isParentOfOwner && $store->owner_id !== $user->id && !$isDirectManager && !$isManagerViaPivot) {
       return response()->json(['error' => 'Unauthorized to modify this store'], 403);
   }
   ```
   Also guard `owner_id`/`manager_id` changes for STORE_OWNER in update — they should not be able to reassign ownership away from themselves (mirror the restriction in `store()` create method):
   ```php
   if ($isStoreOwner) {
       $ownerId = $store->owner_id; // lock owner to self
   }
   ```

5. In `HomeStoreOwner.vue` `services` computed (line ~34): add a "Manage Stores" service button so store owners can navigate directly from their service grid (currently only accessible via balance box footer):
   ```js
   {
       icon: 'https://cdn.jsdelivr.net/gh/telemagnadon/obj-vault-3a@v2026.05.14-vendor-2/a/85605eacd4c8.bin',
       title: 'Manage Stores',
       pagename: 'ManageStoresAdmin',
   },
   ```

## context
### StoreController.php — update() permission block (lines 644-651)
```php
$acctType = $user->acct_type instanceof UserTypes ? $user->acct_type : UserTypes::tryFrom($user->acct_type);
$isBig3 = in_array($acctType, [UserTypes::ULTIMATE, UserTypes::SUPER_OPERATOR, UserTypes::OPERATOR]);

// Permission check: Big 3 or Parent of Owner/Manager
$isParentOfOwner = UserPermissions::isDescendantOfCurrentUser($store->owner_id);
if (!$isBig3 && !$isParentOfOwner && $store->owner_id !== $user->id) {
     return response()->json(['error' => 'Unauthorized to modify this store'], 403);
}
```

### StoreController.php — deleteStore_Admin() (lines 1403-1435)
```php
public function deleteStore_Admin(Request $request)
{
    ...
    $isUltimate = $user->acct_type === UserTypes::ULTIMATE;
    if (!$isUltimate) {
        $descendants = $user->getAllDescendants();
        $descendantIds = $descendants->pluck('id')->toArray();
        $allowedIds = array_merge([$user->id], $descendantIds);
        if (!in_array($store->owner_id, $allowedIds) && !in_array($store->manager_id, $allowedIds)) {
            return ResponseHelper::returnUnauthorized();
        }
    }
    $store->delete();
    ...
    } catch (Exception $e) {   // BUG: missing backslash
```

### StoreController.php — listStores_Admin() user_can_manage logic (lines 1330-1334)
```php
->each(function ($s) use ($allowedUserIds) {
    $s->user_can_manage = in_array($s->owner_id, $allowedUserIds)
        || in_array($s->manager_id, $allowedUserIds)
        || $s->managers->pluck('user_id')->intersect($allowedUserIds)->isNotEmpty();
    unset($s->managers);
});
```
The `user_can_manage` flag includes managers-pivot check, but `update()` and `deleteStore_Admin()` do not — this is the root mismatch that causes store managers to see buttons but get 403.

### HomeStoreOwner.vue — services array (lines 34-60)
Currently has: Create Store, Import Products, New Product, My Products, POS Keys.
"My Stores" exists only in `balanceFooterItems`. Adding it to `services` makes it more discoverable.

### Store model relations
`$store->managers()` returns `StoreManager` records (pivot model at `app/Models/Market/StoreManager.php`).
`$store->managerUsers()` returns `User` models via the pivot.

## notes
- dictionary: /home/josh/development/personal/BukidBountyApp/ai-docs/dictionary.md
- linters: none detected
- constraints: Hypervel (not Laravel) — use `Hypervel\Support\Facades\*` not `Illuminate\*`; table name for stores is `str` (abbreviated); `declare(strict_types=1)` is in effect
