feat(accounting): add Shayegan as fixed boursecu provider

Wire outbound Invoice/Document sync after confirmed ops, store sync status with timeout lock, and keep SQL credentials out of frontend responses.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-05 17:12:43 +03:30
co-authored by Cursor
parent 698fbed805
commit a42261c06e
20 changed files with 1485 additions and 18 deletions
@@ -81,7 +81,7 @@ class SettingsController extends Controller
->orderBy('key')
->get();
return response()->json($rows);
return response()->json($this->maskSecretSettingValues($rows));
}
/**
@@ -162,7 +162,19 @@ class SettingsController extends Controller
$previousValue = $setting->exists ? (string) ($setting->value ?? '') : null;
$setting->value = $request->value;
$incomingValue = $request->value;
if (
in_array($key, ['shayegan_db_password', 'shayegan_auth_password'], true)
&& is_string($incomingValue)
&& ($incomingValue === '********' || $incomingValue === '')
&& $previousValue !== null
&& $previousValue !== ''
) {
// ماسک frontend را به‌جای رمز واقعی ذخیره نکن
$incomingValue = $previousValue;
}
$setting->value = $incomingValue;
$setting->description = $request->description;
if ($request->has('is_active')) {
$setting->is_active = filter_var($request->is_active, FILTER_VALIDATE_BOOLEAN) ? 1 : 0;
@@ -327,7 +339,7 @@ class SettingsController extends Controller
$overlay = app(SuperAdminProgramSettingsOverlay::class);
$saMap = $overlay->catalogMap();
if ($saMap === null) {
return $localRows;
return $this->maskSecretSettingValues($localRows);
}
$localByKey = [];
@@ -380,7 +392,53 @@ class SettingsController extends Controller
}
}
return collect(array_values($byKey))->sortBy(fn ($row) => (string) ($row->key ?? ''))->values();
return $this->maskSecretSettingValues(
collect(array_values($byKey))->sortBy(fn ($row) => (string) ($row->key ?? ''))->values()
);
}
/**
* رمزهای شایگان و مشابه در پاسخ frontend برنگردند (فقط پر/خالی بودن).
*
* @param mixed $rows
* @return mixed
*/
private function maskSecretSettingValues($rows)
{
$secretKeys = [
'shayegan_db_password',
'shayegan_auth_password',
];
$maskOne = function ($row) use ($secretKeys) {
if (is_array($row)) {
$key = (string) ($row['key'] ?? '');
if (in_array($key, $secretKeys, true)) {
$val = (string) ($row['value'] ?? '');
$row['value'] = $val !== '' ? '********' : '';
}
return $row;
}
if (is_object($row) && isset($row->key)) {
$key = (string) $row->key;
if (in_array($key, $secretKeys, true)) {
$val = (string) ($row->value ?? '');
$row->value = $val !== '' ? '********' : '';
}
}
return $row;
};
if ($rows instanceof \Illuminate\Support\Collection) {
return $rows->map($maskOne)->values();
}
if (is_array($rows)) {
return array_map($maskOne, $rows);
}
return $rows;
}
private function isEditableCanEdit(mixed $canEdit): bool