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

111 lines
6.2 KiB
Markdown

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