6.2 KiB
6.2 KiB
task, cycles, context, private, started, finished
| task | cycles | context | private | started | finished |
|---|---|---|---|---|---|
| Fix accounting theme switch+apply — switching theme should auto-seed accounts, apply should not silently fail | 5 | true | false | 2026-05-29T00:00:00Z | 2026-05-29T00:00:30Z |
files
resources/js/Pages/ManageAccounts.vue[lines 457-491] — switchTheme() and reapplyTheme() JS functionsapp/Http/Controllers/Accounting/AccountingController.php[lines 677-715] — setTheme() and applyTheme() controllers; also getAccountsTree() lines 78-109 has a scoping bugapp/Support/AccountingTheme.php[lines 201-276] — static apply() / applyNode() logic
steps
Bug 1 — "Switch" does not seed accounts (primary UX failure)
-
In
ManageAccounts.vueswitchTheme()(line ~457), afterPOST /admin/accounting/theme/setsucceeds, immediately callPOST /admin/accounting/theme/apply(reusereapplyTheme()logic inline or call it).
Concrete change: afterawait loadThemes(); await fetchAll();inside theif (res.data?.success)block, also callawait reapplyTheme()— OR refactor: makeswitchTheme()call a combined backend endpoint.Simplest safe fix: after confirming
setThemesuccess, callreapplyTheme()directly:if (res.data?.success) { await setThemeKey(selectThemeKey.value); // already done await reapplyTheme(); // NEW: seed accounts for the newly-active theme }Since
reapplyTheme()reads the current theme from the backend (not a parameter), andsetThemealready saved it, the order is correct.
Bug 2 — getAccountsTree() discards scope on grandchildren (silent data loss)
- In
AccountingController.phpgetAccountsTree()(lines 100-102), the innermostwith()closure does:Fix: capture the return value:$this->scopeAccounts($q2, $storeIds, $isBig3); // return value DISCARDEDLine 102 — change$q2 = $this->scopeAccounts($q2, $storeIds, $isBig3);$this->scopeAccounts($q2, $storeIds, $isBig3);→$q2 = $this->scopeAccounts($q2, $storeIds, $isBig3);
Bug 3 — applyTheme() has no error handling; DB failures are silent 500s
- In
AccountingController.phpapplyTheme()(line ~696), wrap theAccountingTheme::apply()call in try/catch:try { $stats = \App\Support\AccountingTheme::apply(); } catch (\Throwable $e) { return response()->json([ 'success' => false, 'message' => 'Failed to apply theme: ' . $e->getMessage(), ], 422); }
Bug 4 — switchTheme UX: don't allow re-clicking Switch on already-active theme after apply
- After
reapplyTheme()completes insideswitchTheme(), callloadThemes()once more sothemeInfo.currentis refreshed and the Switch button re-disables correctly. (IfreapplyTheme()already callsloadThemes(), this is automatic — just verify no double-call confusion.)
context
ManageAccounts.vue switchTheme() (lines 457-473):
async function switchTheme() {
if (!selectThemeKey.value || selectThemeKey.value === themeInfo.value?.current) return;
switchingTheme.value = true;
try {
const res = await axios.post('/admin/accounting/theme/set', { key: selectThemeKey.value });
if (res.data?.success) {
await loadThemes();
await fetchAll();
// BUG: no apply call here — accounts never seeded
} else {
showNotice(res.data?.message || 'Could not switch theme.', { variant: 'danger' });
}
} catch (e) {
showNotice(e.response?.data?.message || 'Could not switch theme.', { variant: 'danger' });
} finally {
switchingTheme.value = false;
}
}
ManageAccounts.vue reapplyTheme() (lines 475-491):
async function reapplyTheme() {
applyingTheme.value = true;
try {
const res = await axios.post('/admin/accounting/theme/apply', {});
if (res.data?.success) {
showNotice(res.data.message || 'Theme applied.', { variant: 'success', title: 'Theme Applied' });
await loadThemes();
await fetchAll();
} else {
showNotice(res.data?.message || 'Apply failed.', { variant: 'danger' });
}
} catch (e) {
showNotice(e.response?.data?.message || 'Apply failed.', { variant: 'danger' });
} finally {
applyingTheme.value = false;
}
}
AccountingController.php getAccountsTree() children scope (lines 96-105):
->with(['children' => function ($q) use ($storeIds, $isBig3) {
$q = $q->where('is_active', true);
$q = $this->scopeAccounts($q, $storeIds, $isBig3); // ✓ captured
$q->with(['children' => function ($q2) use ($storeIds, $isBig3) {
$q2 = $q2->where('is_active', true);
$this->scopeAccounts($q2, $storeIds, $isBig3); // ✗ BUG: return discarded
}]);
}])
AccountingController.php applyTheme() (lines 696-715):
public function applyTheme(Request $request)
{
if (!UserPermissions::isActionPermitted(...)) {
return ResponseHelper::returnUnauthorized();
}
$stats = \App\Support\AccountingTheme::apply(); // no try-catch
return response()->json(['success' => true, 'message' => ..., 'data' => $stats]);
}
AccountingTheme::apply() tree source:
- Reads
Config::get('accounting.themes', [])fromconfig/accounting/themes.php banana_tradingkey hastreewith 4 root nodes (Sales, Supplier Purchases, Delivery & Logistics, Operating Expenses) + leaf childrenblankkey has'tree' => []— ifcurrent()falls back toblank, apply creates 0 accounts
Scope for Big3 vs store-level:
scopeAccounts($query, $storeIds, $isBig3): Big3 →whereNull('store_id'), store-level →whereIn('store_id', $storeIds)applyNode()creates accounts withstore_idabsent (NULL) → correct for Big3 global chart- Root query:
whereNull('parent_id') AND is_active = 1 AND store_id IS NULL→ should match theme-applied accounts
notes
- dictionary: ai-docs/dictionary.md
- linters: none detected
- constraints: Do NOT delete existing accounts; apply is idempotent/additive. Do NOT change the
setThemebackend — it is correct as-is (just saves key). The frontend fix inswitchTheme()is the primary fix. WrapreapplyTheme()call insideswitchTheme()withswitchingTheme.value = truestill active so the button stays disabled during the full switch+apply operation.