Master data review: spec updates and C1/C2/M3-M6 fixes

Spec (docs/reviewing/master-data.md):
- M1: Remove margin from stored product fields (it's a derived frontend value)
- M2: Clarify posting window only restricts transaction dates, not master data
- M5: Document AccountFormulaManager::delete() as archive, not physical delete

Code:
- C1: Fix CompanySettingManager property typo (company_id → companyId) —
  prevented PHP 8.4 dynamic property fatal on all posting window operations
- C2: Fix WarehouseManager::deleteWarehouse() guard — was comparing
  warehouse_name (string) against warehouse id column (no-op); now correctly
  blocks on storage rows and active stock rows
- M3: Remove hard-delete of td_rack_log in deleteStorage() — retain rack
  history consistent with soft-delete philosophy elsewhere
- M4: Add reference guards to ChartOfAccounts::delete() (blocks on GL items,
  formula items, product account mappings) and DepartmentManager::delete()
  (blocks on GL items)
- M6: Fix CompanySettingManager::handle() partial update — only upsert keys
  present in the request, not all allowed keys defaulted

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Thanakorn S
2026-05-26 17:33:58 +07:00
co-authored by Claude Sonnet 4.6
parent cb36d3b8fd
commit dfeb57533a
5 changed files with 230 additions and 35 deletions
@@ -106,10 +106,45 @@ class ChartOfAccounts
public function delete(int $id): void
{
$sth = $this->pdo->prepare(
"UPDATE md_account SET status = 0
WHERE company_id = :cid AND id = :id"
"SELECT account_code FROM md_account
WHERE company_id = :cid AND id = :id LIMIT 1"
);
$sth->execute([':cid' => $this->companyId, ':id' => $id]);
$row = $sth->fetch(PDO::FETCH_ASSOC);
if (!$row) throw new Exception('Account not found.');
$code = $row['account_code'];
$sth = $this->pdo->prepare(
"SELECT COUNT(*) FROM td_gl_item WHERE account_code = :code LIMIT 1"
);
$sth->execute([':code' => $code]);
if ((int)$sth->fetchColumn() > 0) {
throw new Exception("Cannot deactivate — account {$code} has GL journal entries.");
}
$sth = $this->pdo->prepare(
"SELECT COUNT(*) FROM md_account_formula_item
WHERE company_id = :cid AND account_code = :code LIMIT 1"
);
$sth->execute([':cid' => $this->companyId, ':code' => $code]);
if ((int)$sth->fetchColumn() > 0) {
throw new Exception("Cannot deactivate — account {$code} is used in one or more account formulas.");
}
$sth = $this->pdo->prepare(
"SELECT COUNT(*) FROM md_product
WHERE company_id = :cid
AND (sales_account_code = :code OR purchase_account_code = :code) LIMIT 1"
);
$sth->execute([':cid' => $this->companyId, ':code' => $code]);
if ((int)$sth->fetchColumn() > 0) {
throw new Exception("Cannot deactivate — account {$code} is mapped to one or more products.");
}
$this->pdo->prepare(
"UPDATE md_account SET status = 0
WHERE company_id = :cid AND id = :id"
)->execute([':cid' => $this->companyId, ':id' => $id]);
}
public function isPostingAccount(string $account_code): bool