if get delete error move to processed

This commit is contained in:
Raykov-MS 2026-07-10 15:26:42 +03:00
parent d454cf1577
commit 9f55eeeecb
3 changed files with 55 additions and 2 deletions

View File

@ -41,6 +41,12 @@ class InputFileScanResult:
skipped_files: list[RemoteFile] skipped_files: list[RemoteFile]
class ProcessedCopySucceededDeleteFailedError(RuntimeError):
def __init__(self, message: str, target_path: str) -> None:
super().__init__(message)
self.target_path = target_path
class SMBClientConnection: class SMBClientConnection:
def __init__( def __init__(
self, smb_base_path: str, user_params: dict[str, str] | None = None self, smb_base_path: str, user_params: dict[str, str] | None = None
@ -455,11 +461,12 @@ def _samba_move_file(path_from: str, path_to: str, samba_conn) -> None:
samba_conn.delete(path_from) samba_conn.delete(path_from)
except Exception as exc: # noqa: BLE001 except Exception as exc: # noqa: BLE001
target_exists = _safe_path_exists(path_to, samba_conn) target_exists = _safe_path_exists(path_to, samba_conn)
raise RuntimeError( raise ProcessedCopySucceededDeleteFailedError(
"SMB move failed at delete stage after successful copy: " "SMB move failed at delete stage after successful copy: "
f"source='{path_from}', target='{path_to}', " f"source='{path_from}', target='{path_to}', "
f"target_exists={target_exists}, {_format_smb_exception(exc)}" f"target_exists={target_exists}, {_format_smb_exception(exc)}"
f"{_format_rename_attempt(rename_error)}" f"{_format_rename_attempt(rename_error)}",
target_path=path_to,
) from exc ) from exc

View File

@ -13,6 +13,7 @@ from .config import (
join_path, join_path,
) )
from .file_manager import ( from .file_manager import (
ProcessedCopySucceededDeleteFailedError,
check_dir_write_access, check_dir_write_access,
check_input_dir_access, check_input_dir_access,
connect_samba, connect_samba,
@ -331,6 +332,16 @@ def _process_files(
log_lines.append( log_lines.append(
f"{_now()} | success -> processed: {remote_file.path} => {moved_to}" f"{_now()} | success -> processed: {remote_file.path} => {moved_to}"
) )
except ProcessedCopySucceededDeleteFailedError as exc:
moved_to = exc.target_path
message = (
f"{message} Файл скопирован в Processed, "
"но исходник не удален из входной папки из-за ограничений доступа."
)
log_lines.append(
f"{_now()} | success with delete warning: "
f"{remote_file.path} => {moved_to}. {exc}"
)
except Exception as exc: # noqa: BLE001 except Exception as exc: # noqa: BLE001
status = "partial" status = "partial"
message = f"{message} Не удалось переместить в Processed: {exc}" message = f"{message} Не удалось переместить в Processed: {exc}"

View File

@ -62,6 +62,14 @@ class FakeSamba:
shutil.copy2(source, target) shutil.copy2(source, target)
class FakeSambaDeleteDeniedForXml(FakeSamba):
def delete(self, path: str) -> None:
normalized = path.replace("/", "\\").lower()
if normalized.endswith(".xml") and "\\test\\" in normalized:
raise PermissionError("STATUS_ACCESS_DENIED")
super().delete(path)
def _valid_xml() -> str: def _valid_xml() -> str:
return """<?xml version="1.0" encoding="UTF-8"?> return """<?xml version="1.0" encoding="UTF-8"?>
<СообщОперКО xmlns="urn:test"> <СообщОперКО xmlns="urn:test">
@ -113,3 +121,30 @@ def test_run_batch_smoke_and_idempotency(tmp_path: Path, monkeypatch) -> None:
) )
assert second.status == "success" assert second.status == "success"
assert second.processed == 0 assert second.processed == 0
def test_run_batch_success_when_delete_denied_after_copy(
tmp_path: Path, monkeypatch
) -> None:
samba = FakeSambaDeleteDeniedForXml(tmp_path)
in_dir_local = tmp_path / "Exchange" / "SMBDEMO" / "test"
in_dir_local.mkdir(parents=True)
file_name = "SKO115FZ_01_123456789_20260616_X00001.xml"
(in_dir_local / file_name).write_text(_valid_xml(), encoding="utf-8")
from app.pipeline import service
monkeypatch.setattr(
service, "connect_samba", lambda smb_base_path, user_params: samba
)
result = run_batch(
smb_base_path="//test-server/share",
input_dir="Exchange\\SMBDEMO\\test",
samba_user="user",
samba_password="pass",
launcher="tester",
)
assert result.status == "success"
assert result.processed == 1