Files
BarangaySystem/.claude/plans/1321cb985147e2ce3a341d045288e5f5-complete.md
2026-06-06 18:43:00 +08:00

6.2 KiB

task, cycles, context, private, started, finished
task cycles context private started finished
enable store owner to delete and edit stores he owns in manage stores admin page 5 true false 2026-05-16T00:00:00Z 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:

    $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:

    $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):

    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):

    {
        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)

$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)

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)

->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