import sys
import os
import uuid
import requests
import json
import logging
import unicodedata
from datetime import datetime, date, timedelta
from pathlib import Path
from typing import List, Dict, Any, Optional, Tuple
from collections import defaultdict
from PyQt6.QtWidgets import (
    QDialog, QVBoxLayout, QGroupBox, QFormLayout, QComboBox,
    QLineEdit, QHBoxLayout, QPushButton, QDialogButtonBox, QFrame,
    QWidget, QCheckBox, QDateEdit, QLabel, QMessageBox, QTableWidget,
    QTableWidgetItem, QHeaderView, QInputDialog, QApplication, QGridLayout
)
from PyQt6.QtGui import QDoubleValidator, QIcon, QKeyEvent, QShortcut, QKeySequence
from PyQt6.QtCore import Qt, QDate, QLocale, QTimer, QEvent
from core.config import SUPPORTED_CURRENCIES, TRUNCGIL_API_URL, BASE_CURRENCY, format_number, logger, get_app_data_directory
from core.db import BaseManager, DataManager
from core.db import FinancialTransaction, CashAccount, CardAccount, ScheduledTransaction, CustomerHistoryEvent
try:
    from modules.stock import AdvancedGoldEntryDialog
except ImportError:
    pass
class CurrencyManager:
    """
    Döviz ve Altın kurlarını yönetir.
    "Ne Girdiysem O" Prensibi korunmuştur. Veritabanı kayıtlarında dönüşüm yapılmaz.
    Sadece bilgilendirme amaçlı (Zekat, Hesap Makinesi) API verisi çeker.
    """
    def __init__(self):
        self.rates_cache: Dict[str, Any] = {}
        self.last_fetch_time: Optional[datetime] = None
        self.cache_duration = timedelta(minutes=15)
        self.cache_file = Path(get_app_data_directory()) / "rates_cache.json"
        self._load_rates_from_disk()
    def _load_rates_from_disk(self):
        """Diskteki son başarılı kurları yükler."""
        if self.cache_file.exists():
            try:
                with open(self.cache_file, 'r', encoding='utf-8') as f:
                    self.rates_cache = json.load(f)
                logger.info("Kurlar disk önbelleğinden yüklendi.")
            except Exception as e:
                logger.error(f"Önbellek okuma hatası: {e}")
    def _save_rates_to_disk(self):
        """Başarılı çekilen kurları diske kaydeder."""
        try:
            with open(self.cache_file, 'w', encoding='utf-8') as f:
                json.dump(self.rates_cache, f, ensure_ascii=False, indent=4)
        except Exception as e:
            logger.error(f"Önbellek yazma hatası: {e}")
    def _parse_rate(self, value: Any) -> float:
        try:
            if value is None:
                return 0.0
            if isinstance(value, (int, float)):
                return float(value)

            cleaned = str(value).strip()
            if not cleaned:
                return 0.0

            cleaned = cleaned.replace('%', '').replace('−', '-').replace(' ', '')

            if ',' in cleaned and '.' in cleaned:
                if cleaned.rfind(',') > cleaned.rfind('.'):
                    cleaned = cleaned.replace('.', '').replace(',', '.')
                else:
                    cleaned = cleaned.replace(',', '')
            elif ',' in cleaned:
                cleaned = cleaned.replace(',', '.')

            cleaned = ''.join(ch for ch in cleaned if ch in '0123456789.-')
            if cleaned in {'', '-', '.', '-.'}:
                return 0.0
            return float(cleaned)
        except (ValueError, TypeError):
            return 0.0

    def _normalize_rate_key(self, key: Any) -> str:
        normalized = str(key or "").strip().lower()
        tr_map = {
            "ı": "i", "İ": "i", "ş": "s", "Ş": "s",
            "ğ": "g", "Ğ": "g", "ü": "u", "Ü": "u",
            "ö": "o", "Ö": "o", "ç": "c", "Ç": "c",
        }
        for src, dst in tr_map.items():
            normalized = normalized.replace(src, dst)

        normalized = unicodedata.normalize("NFKD", normalized)
        normalized = "".join(ch for ch in normalized if not unicodedata.combining(ch))
        normalized = normalized.encode("ascii", "ignore").decode("ascii")
        normalized = "".join(ch for ch in normalized if ch.isalnum())
        return normalized

    def _extract_buy_sell(self, rate_item: Any) -> Tuple[float, float]:
        if not isinstance(rate_item, dict):
            return 0.0, 0.0

        normalized_items: List[Tuple[str, Any]] = []
        for key, value in rate_item.items():
            nk = self._normalize_rate_key(key)
            if nk:
                normalized_items.append((nk, value))

        buy_raw = None
        sell_raw = None

        for nk, value in normalized_items:
            if buy_raw is None and nk in {"alis", "buy", "buying", "bid"}:
                buy_raw = value
            if sell_raw is None and nk in {"satis", "sell", "selling", "ask"}:
                sell_raw = value

        if buy_raw is None:
            for nk, value in normalized_items:
                if nk.startswith("al") or nk.startswith("buy") or nk.startswith("bid"):
                    buy_raw = value
                    break

        if sell_raw is None:
            for nk, value in normalized_items:
                if nk.startswith("sat") or nk.startswith("sel") or nk.startswith("ask"):
                    sell_raw = value
                    break

        return self._parse_rate(buy_raw), self._parse_rate(sell_raw)
    def _fetch_all_rates(self) -> bool:
        """
        API'den güncel kurları çeker.
        Bu kurlar SADECE BİLGİ AMAÇLI (Dashboard, Hesap Makinesi, Zekat) kullanılır.
        """
        current_time = datetime.now()
        
        # Eğer cache doluysa ve son çekimden bu yana 15 dakika geçmediyse çekme
        if self.rates_cache and self.last_fetch_time and (current_time - self.last_fetch_time) < self.cache_duration:
            return True

        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept': 'application/json',
            'Connection': 'keep-alive'
        }
        source_urls = [
            TRUNCGIL_API_URL,
            "https://finans.truncgil.com/today.json",
        ]
        for source_url in source_urls:
            try:
                response = requests.get(source_url, timeout=10, headers=headers)
                response.raise_for_status()
                data = response.json()
                if data and isinstance(data, dict):
                    self.rates_cache = data
                    self.last_fetch_time = current_time
                    self._save_rates_to_disk()
                    logger.info(f"Kurlar kaynaktan güncellendi: {source_url}")
                    return True
                logger.warning(f"Kur kaynagi bos yanit döndürdü: {source_url}")
            except Exception as e:
                logger.error(f"Kur kaynagi hatasi ({source_url}): {e}")
                continue
        return False
        # --- DEÄİŞİKLİK SONU ---
    def get_exchange_rate(self, from_currency: str, to_currency: str) -> Optional[float]:
        """Sadece bilgilendirme amaçlı kur döndürür."""
        if from_currency == to_currency:
            return 1.0
        self._fetch_all_rates()
        if not self.rates_cache:
            return 1.0
        if from_currency == "TRY":
            from_rate_try = 1.0
        else:
            from_data = self.rates_cache.get(from_currency)
            if not from_data: return 1.0
            from_buy, from_sell = self._extract_buy_sell(from_data)
            from_rate_try = from_buy or from_sell
        if to_currency == "TRY":
            to_rate_try = 1.0
        else:
            to_data = self.rates_cache.get(to_currency)
            if not to_data: return 1.0
            to_buy, to_sell = self._extract_buy_sell(to_data)
            to_rate_try = to_sell or to_buy
        if to_rate_try == 0: return 0.0
        rate = from_rate_try / to_rate_try
        return rate
    def format_currency(self, amount: float, target_currency: Optional[str] = None, include_symbol: bool = True) -> str:
        target_currency = target_currency or BASE_CURRENCY
        symbol = f" {target_currency}" if include_symbol else ""
        try:
            return f"{format_number(amount)}{symbol}"
        except (ValueError, TypeError):
            return f"0{symbol}"
    def _convert_to_display_currency(self, amount_base: float, display_currency: str) -> float:
        """İPTAL EDİLDİ: 1:1 dönüşüm."""
        return amount_base
    def _convert_to_base_currency(self, amount: float, from_currency: str) -> float:
        """İPTAL EDİLDİ: 1:1 dönüşüm."""
        return amount
class FinancialTransactionManager(BaseManager):
    """
    Finansal işlemleri (Gelir/Gider) yönetir.
    Doğrudan SQL sorguları kullanarak performans optimizasyonu sağlar.
    """
    def __init__(self, data_manager: DataManager, data_key: str):
        self.account_id = None
        if "_" in data_key:
            self.account_id = data_key.split("_")[-1]
        super().__init__(data_manager, data_key, FinancialTransaction)
        cols = set()
        try:
            cols = self.db.get_table_columns(self.table_name)
        except Exception:
            cols = set()
        if "amount" in cols:
            self._amount_col = "amount"
        elif "original_amount" in cols:
            self._amount_col = "original_amount"
        elif "transaction_amount" in cols:
            self._amount_col = "transaction_amount"
        else:
            self._amount_col = "amount"
        if "related_person_id" in cols:
            self._person_col = "related_person_id"
        elif "person_full_name" in cols:
            self._person_col = "person_full_name"
        else:
            self._person_col = None
        if "related_record_id" in cols:
            self._related_record_col = "related_record_id"
        elif "related_id" in cols:
            self._related_record_col = "related_id"
        else:
            self._related_record_col = None
        self._supports_soft_delete = "is_deleted" in cols
    def _custom_map_to_sql(self, item: Any, sql_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        if "original_amount" in sql_data:
            amount_col = getattr(self, "_amount_col", "amount")
            sql_data[amount_col] = sql_data.pop("original_amount")
        person_name = sql_data.pop("person_full_name", None)
        if getattr(self, "_person_col", None) == "related_person_id":
            if person_name:
                p_row = self.db.fetch_one(
                    "SELECT id FROM persons WHERE full_name=%s AND profile_id=%s LIMIT 1",
                    (person_name, self.profile_id),
                )
                sql_data["related_person_id"] = p_row.get("id") if p_row else None
            else:
                sql_data["related_person_id"] = None
        elif getattr(self, "_person_col", None) == "person_full_name":
            sql_data["person_full_name"] = (str(person_name).strip() if person_name else None)
        related_val = sql_data.pop("related_id", None)
        if getattr(self, "_related_record_col", None):
            sql_data[self._related_record_col] = related_val
        if self.account_id and not sql_data.get("account_id"):
            sql_data["account_id"] = self.account_id
        return sql_data
    def _custom_map_from_sql(self, row: Dict[str, Any]) -> Optional[Any]:
        def fmt_date(val):
            if not val:
                return ""
            if isinstance(val, (datetime, date)):
                return val.strftime("%d.%m.%Y")
            return str(val)
        def to_float(val, default=0.0):
            try:
                if val is None:
                    return default
                return float(val)
            except Exception:
                return default
        person_full_name = row.get("person_full_name") or None
        rel_person_id = row.get("related_person_id")
        if rel_person_id:
            p_row = self.db.fetch_one(
                "SELECT full_name FROM persons WHERE id=%s AND profile_id=%s LIMIT 1",
                (rel_person_id, self.profile_id),
            )
            if p_row and p_row.get("full_name"):
                person_full_name = p_row["full_name"]
        amount_val = None
        for col in (getattr(self, "_amount_col", None), "amount", "original_amount", "transaction_amount"):
            if col and col in row:
                amount_val = row.get(col)
                break
        return self.data_class(
            transaction_date=fmt_date(row.get("transaction_date")),
            transaction_type=row.get("transaction_type", "") or "",
            original_amount=to_float(amount_val),
            currency=row.get("currency", "") or "",
            base_amount=to_float(row.get("base_amount")),
            description=row.get("description", "") or "",
            person_full_name=person_full_name,
            related_id=row.get("related_record_id") or row.get("related_id"),
            account_id=row.get("account_id", "") or "",
            id=row.get("id") or "",
            created_at=str(row.get("created_at") or ""),
            updated_at=str(row.get("updated_at") or ""),
            profile_id=row.get("profile_id") or "",
        )
    def calculate_balance(self, cursor=None) -> Dict[str, float]:
        if not self.account_id:
            return {}
        amount_col = getattr(self, "_amount_col", "amount")
        if not amount_col.isidentifier():
            amount_col = "amount"
        if getattr(self.data_manager, "is_aggregate", False):
            if not getattr(self.data_manager, "profile_ids", None):
                return {}
            placeholders = ", ".join(["%s"] * len(self.data_manager.profile_ids))
            where = f"WHERE account_id = %s AND profile_id IN ({placeholders})"
            params = [self.account_id] + list(self.data_manager.profile_ids)
        else:
            where = "WHERE account_id = %s AND profile_id = %s"
            params = [self.account_id, self.profile_id]
        if getattr(self, "_supports_soft_delete", False):
            where += " AND is_deleted = 0"
        sql = f"""
            SELECT currency,
                   SUM(CASE WHEN transaction_type = 'Income' THEN {amount_col} ELSE -{amount_col} END) as net_balance
            FROM financial_transactions
            {where}
            GROUP BY currency
        """
        try:
            if cursor:
                cursor.execute(sql, tuple(params))
                rows = cursor.fetchall()
            else:
                rows = self.db.fetch_all(sql, tuple(params))
            balances = {}
            for row in rows:
                currency = row['currency']
                balance = float(row['net_balance'] or 0)
                if abs(balance) > 0.001: # 0 olanları gösterme
                    balances[currency] = balance
            return balances
        except Exception as e:
            logger.error(f"SQL Bakiye hesaplama hatası ({self.account_id}): {e}")
            return {}
    def get_filtered_transactions(self, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None) -> List[FinancialTransaction]:
        """
        Tarih aralığına göre işlemleri SQL 'WHERE' ile filtreler.
        """
        if not self.account_id:
            return []
        if getattr(self.data_manager, "is_aggregate", False):
            if not getattr(self.data_manager, "profile_ids", None):
                return []
            placeholders = ", ".join(["%s"] * len(self.data_manager.profile_ids))
            sql = f"SELECT * FROM financial_transactions WHERE account_id = %s AND profile_id IN ({placeholders})"
            params = [self.account_id] + list(self.data_manager.profile_ids)
        else:
            sql = "SELECT * FROM financial_transactions WHERE account_id = %s AND profile_id = %s"
            params = [self.account_id, self.profile_id]
        if start_date:
            sql += " AND transaction_date >= %s"
            params.append(start_date.strftime("%Y-%m-%d"))
        if end_date:
            sql += " AND transaction_date <= %s"
            params.append(end_date.strftime("%Y-%m-%d"))
        sql += " ORDER BY transaction_date DESC, created_at DESC" # Sıralama
        try:
            rows = self.db.fetch_all(sql, tuple(params))
            results = []
            for row in rows:
                obj = self._map_sql_to_dataclass(row) # BaseManager metodu
                if obj:
                    results.append(obj)
            return results
        except Exception as e:
            logger.error(f"SQL Filtreleme hatası ({self.account_id}): {e}")
            return []
class CashAccountManager(BaseManager):
    """Nakit Kasa tanımlarını yönetir."""
    def __init__(self, data_manager: DataManager):
        super().__init__(data_manager, "cash_accounts", CashAccount)
    def _map_sql_to_dataclass(self, row: Dict[str, Any]) -> Optional[Any]:
        account_type = (row.get("type") or "").lower()
        if account_type and account_type != "cash":
            return None
        return super()._map_sql_to_dataclass(row)
class CardAccountManager(BaseManager):
    """Banka/Kart Hesap tanımlarını yönetir."""
    def __init__(self, data_manager: DataManager):
        super().__init__(data_manager, "card_accounts", CardAccount)
    def _map_sql_to_dataclass(self, row: Dict[str, Any]) -> Optional[Any]:
        account_type = (row.get("type") or "").lower()
        if account_type != "card":
            return None
        return super()._map_sql_to_dataclass(row)
class ScheduledTransactionManager(BaseManager):
    """Vadeli işlemleri yönetir."""
    def __init__(self, data_manager: DataManager):
        super().__init__(data_manager, "scheduled_transactions", ScheduledTransaction)
    def get_pending_transactions(self) -> List[ScheduledTransaction]:
        """Bekleyen işlemleri SQL ile çeker."""
        sql = "SELECT * FROM scheduled_transactions WHERE status = 'Pending' AND profile_id = %s AND is_deleted = 0"
        rows = self.db.fetch_all(sql, (self.profile_id,))
        results = []
        for row in rows:
            obj = self._map_sql_to_dataclass(row)
            if obj: results.append(obj)
        return results
class FinanceManager:
    """
    Finansal işlemlerin mantığını yöneten kontrolcü sınıf.
    UI ve Veritabanı arasında köprü kurar.
    """
    def __init__(self, main_app):
        self.main_app = main_app
        self.cash_account_manager = main_app.cash_account_manager
        self.card_account_manager = main_app.card_account_manager
        self.currency_manager = main_app.currency_manager
        self.data_manager = main_app.data_manager
    def _add_cash_account(self):
        name, ok = QInputDialog.getText(self.main_app, self.main_app.lang["add_cash_account"], self.main_app.lang["cash_account_name_placeholder"])
        if ok and name.strip():
            name = name.strip()
            if self.cash_account_manager.find_by_attribute("name", name):
                QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["existing_item"])
                return
            new_account = CashAccount(name=name)
            self.cash_account_manager.add_item(new_account)
            data_key = f"cash_transactions_{new_account.id}"
            self.main_app.cash_managers[new_account.id] = FinancialTransactionManager(self.data_manager, data_key)
            self.main_app.refresh_cash_table()
            self.main_app.reload_related_combos()
            self.main_app._show_status_message(f"Cash account '{name}' added.")
    def _edit_cash_account(self):
        account_id = self.main_app.cash_account_selector_combo.currentData()
        if not account_id: return
        account = self.cash_account_manager.find_by_id(account_id)
        if not account: return
        new_name, ok = QInputDialog.getText(self.main_app, self.main_app.lang["edit_cash_account"], self.main_app.lang["cash_account_name_placeholder"], text=account.name)
        if ok and new_name.strip() and new_name.strip() != account.name:
            self.cash_account_manager.update_item(account.id, {"name": new_name.strip()})
            self.main_app.refresh_cash_table()
            self.main_app.reload_related_combos()
            self.main_app._show_status_message(f"Cash account updated to '{new_name.strip()}'.")
    def _delete_cash_account(self):
        account_id = self.main_app.cash_account_selector_combo.currentData()
        if not account_id: return
        account = self.cash_account_manager.find_by_id(account_id)
        if not account: return
        all_accs = self.cash_account_manager.get_all()
        if len(all_accs) <= 1:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], "You must have at least one cash account.")
            return
        if self.main_app._show_confirm_dialog(self.main_app.lang["delete_cash_account"], self.main_app.lang["confirm_delete_cash_account"].format(name=account.name)):
            self.cash_account_manager.delete_item(account.id)
            if account.id in self.main_app.cash_managers:
                del self.main_app.cash_managers[account.id]
            self.main_app.refresh_cash_table()
            self.main_app.reload_related_combos()
            self.main_app._show_status_message(f"Cash account '{account.name}' deleted.")
    def _update_cash_account_button_states(self):
        has_selection = len(self.main_app.cash_account_selector_combo.currentText()) > 0
        self.main_app.edit_cash_account_btn.setEnabled(has_selection)
        self.main_app.delete_cash_account_btn.setEnabled(has_selection)
    def update_cash_transaction_display(self):
        """Seçili nakit kasa için tabloyu ve özeti günceller (SQL tabanlı)."""
        if not hasattr(self.main_app, 'cash_account_selector_combo') or not hasattr(self.main_app, 'cash_transaction_table'):
            return
        account_id = self.main_app.cash_account_selector_combo.currentData()
        if account_id and account_id in self.main_app.cash_managers:
            manager = self.main_app.cash_managers[account_id]
            self.main_app.update_transaction_list_display(self.main_app.cash_transaction_table, manager)
            new_summary_widget = QWidget()
            summary_layout = QFormLayout(new_summary_widget)
            balances = manager.calculate_balance() # SQL sorgusu çalışır
            if not balances:
                summary_layout.addRow(QLabel(f"<b>{self.main_app.lang['net']}:</b>"), QLabel("0.00"))
            else:
                for currency, amount in sorted(balances.items()):
                    label = QLabel(f"<b>{currency} {self.main_app.lang['net']}:</b>")
                    value = QLabel(format_number(amount))
                    summary_layout.addRow(label, value)
            if hasattr(self.main_app, 'cash_summary_widget'):
                old_widget = self.main_app.cash_summary_widget
                if old_widget.parent():
                    old_widget.parent().layout().replaceWidget(old_widget, new_summary_widget)
                self.main_app.cash_summary_widget = new_summary_widget
                old_widget.deleteLater()
        else:
            self.main_app.cash_transaction_table.setRowCount(0)
            new_summary_widget = QWidget()
            QFormLayout(new_summary_widget).addRow(QLabel(f"<b>{self.main_app.lang['net']}:</b>"), QLabel("0.00"))
            if hasattr(self.main_app, 'cash_summary_widget'):
                old_widget = self.main_app.cash_summary_widget
                if old_widget.parent():
                    old_widget.parent().layout().replaceWidget(old_widget, new_summary_widget)
                self.main_app.cash_summary_widget = new_summary_widget
                old_widget.deleteLater()
    def _edit_cash_transaction(self):
        account_id = self.main_app.cash_account_selector_combo.currentData()
        if account_id and account_id in self.main_app.cash_managers:
            self._edit_transaction(self.main_app.cash_managers[account_id], "cash_transaction_table")
    def _delete_cash_transaction(self):
        account_id = self.main_app.cash_account_selector_combo.currentData()
        if account_id and account_id in self.main_app.cash_managers:
            self._delete_transaction(self.main_app.cash_managers[account_id], "cash_transaction_table")
    def _add_card_account(self):
        name, ok = QInputDialog.getText(self.main_app, self.main_app.lang["add_card_account"], self.main_app.lang["card_account_name"])
        if ok and name:
            if self.card_account_manager.find_by_attribute("name", name):
                QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["existing_item"])
                return
            new_account = CardAccount(name=name)
            self.card_account_manager.add_item(new_account)
            data_key = f"card_transactions_{new_account.id}"
            self.main_app.card_managers[new_account.id] = FinancialTransactionManager(self.data_manager, data_key)
            self.main_app.refresh_cash_table()
            self.main_app.reload_related_combos()
            self.main_app._show_status_message(f"Card account '{name}' added.")
    def _edit_card_account(self):
        account_id = self.main_app.card_account_selector_combo.currentData()
        if not account_id: return
        account = self.card_account_manager.find_by_id(account_id)
        if not account: return
        new_name, ok = QInputDialog.getText(self.main_app, self.main_app.lang["edit_card_account"], self.main_app.lang["card_account_name"], text=account.name)
        if ok and new_name:
            self.card_account_manager.update_item(account_id, {"name": new_name})
            self.main_app.refresh_cash_table()
            self.main_app.reload_related_combos()
            self.main_app._show_status_message(f"Card account '{new_name}' updated.")
    def _delete_card_account(self):
        account_id = self.main_app.card_account_selector_combo.currentData()
        if not account_id: return
        account = self.card_account_manager.find_by_id(account_id)
        if not account: return
        if self.main_app._show_confirm_dialog(self.main_app.lang["delete_card_account"], f"'{account.name}' - {self.main_app.lang['confirm_delete_prompt']}"):
            self.card_account_manager.delete_item(account_id)
            if account_id in self.main_app.card_managers:
                del self.main_app.card_managers[account_id]
            self.main_app.refresh_cash_table()
            self.main_app.reload_related_combos()
            self.main_app._show_status_message(f"Card account '{account.name}' deleted.")
    def _update_card_account_button_states(self):
        has_selection = len(self.main_app.card_account_selector_combo.currentText()) > 0
        self.main_app.edit_card_account_btn.setEnabled(has_selection)
        self.main_app.delete_card_account_btn.setEnabled(has_selection)
    def update_card_transaction_display(self):
        """Seçili kart hesabı için tabloyu ve özeti günceller (SQL tabanlı)."""
        if not hasattr(self.main_app, 'card_account_selector_combo') or not hasattr(self.main_app, 'card_transaction_table'):
            return
        account_id = self.main_app.card_account_selector_combo.currentData()
        if account_id and account_id in self.main_app.card_managers:
            manager = self.main_app.card_managers[account_id]
            self.main_app.update_transaction_list_display(self.main_app.card_transaction_table, manager)
            new_summary_widget = QWidget()
            summary_layout = QFormLayout(new_summary_widget)
            balances = manager.calculate_balance()
            if not balances:
                summary_layout.addRow(QLabel(f"<b>{self.main_app.lang['net']}:</b>"), QLabel("0.00"))
            else:
                for currency, amount in sorted(balances.items()):
                    label = QLabel(f"<b>{currency} {self.main_app.lang['net']}:</b>")
                    value = QLabel(format_number(amount))
                    summary_layout.addRow(label, value)
            if hasattr(self.main_app, 'card_summary_widget'):
                old_widget = self.main_app.card_summary_widget
                if old_widget.parent():
                    old_widget.parent().layout().replaceWidget(old_widget, new_summary_widget)
                self.main_app.card_summary_widget = new_summary_widget
                old_widget.deleteLater()
        else:
            self.main_app.card_transaction_table.setRowCount(0)
    def _edit_card_transaction(self):
        account_id = self.main_app.card_account_selector_combo.currentData()
        if account_id and account_id in self.main_app.card_managers:
            self._edit_transaction(self.main_app.card_managers[account_id], "card_transaction_table")
    def _delete_card_transaction(self):
        account_id = self.main_app.card_account_selector_combo.currentData()
        if account_id and account_id in self.main_app.card_managers:
            self._delete_transaction(self.main_app.card_managers[account_id], "card_transaction_table")
    def update_all_account_combos(self):
        """Tüm hesap seçim kutularını (Combobox) yetki kontrolüyle günceller."""
        cash_accounts = sorted(self.main_app._get_accessible_cash_accounts(), key=lambda x: x.name)
        card_accounts = sorted(self.main_app._get_accessible_card_accounts(), key=lambda x: x.name)
        for combo_name in ['cash_account_selector_combo', 'card_account_selector_combo']:
            if hasattr(self.main_app, combo_name):
                try:
                    combo = getattr(self.main_app, combo_name)
                    if not isinstance(combo, QComboBox): continue
                    current_id = combo.currentData()
                    combo.blockSignals(True)
                    combo.clear()
                    accounts = cash_accounts if 'cash' in combo_name else card_accounts
                    for acc in accounts:
                        combo.addItem(acc.name, acc.id)
                    if current_id:
                        idx = combo.findData(current_id)
                        if idx != -1: combo.setCurrentIndex(idx)
                    combo.blockSignals(False)
                except RuntimeError:
                    pass
                except Exception as e:
                    logger.error(f"Account combo error ({combo_name}): {e}")
        potential_combos = [
            'daily_income_dest_account_combo',
            'daily_expense_source_account_combo',
            'st_target_account_combo',
            'exchange_source_account_combo',
            'exchange_dest_account_combo',
            'transfer_source_combo',
            'transfer_dest_combo',
            'quick_acc_account_combo'
        ]
        for name in potential_combos:
            if hasattr(self.main_app, name):
                try:
                    combo = getattr(self.main_app, name)
                    if not isinstance(combo, QComboBox): continue
                    current_data = combo.currentData()
                    combo.blockSignals(True)
                    combo.clear()
                    for acc in cash_accounts:
                        combo.addItem(f"{self.main_app.lang['cash']} - {acc.name}", ("cash", acc.id))
                    for acc in card_accounts:
                        combo.addItem(f"{self.main_app.lang['card']} - {acc.name}", ("card", acc.id))
                    if current_data:
                        index = combo.findData(current_data)
                        if index != -1: combo.setCurrentIndex(index)
                    combo.blockSignals(False)
                except RuntimeError:
                    pass
                except Exception as e:
                    logger.error(f"Combo update error in loop ({name}): {e}")
    def _add_daily_transaction(self, trans_type: str):
        """
        Günlük İşlemler sekmesinden gelir veya gider ekler.
        Refactor: Sihirli stringler (Income/Expense) yerine Constant kullanımı.
        """
        from core.constants import TransactionType
        is_income = (trans_type == TransactionType.INCOME)
        if is_income:
            amount_widget, currency_widget = self.main_app.daily_income_amount, self.main_app.daily_income_currency
            desc_widget = self.main_app.daily_income_desc
            account_combo = self.main_app.daily_income_dest_account_combo
        else:
            amount_widget, currency_widget = self.main_app.daily_expense_amount, self.main_app.daily_expense_currency
            desc_widget = self.main_app.daily_expense_desc
            account_combo = self.main_app.daily_expense_source_account_combo
        amount = self.main_app.get_float_from_entry(amount_widget, self.main_app.lang['amount'], allow_zero=False)
        if amount is None: return
        account_data = account_combo.currentData()
        if not account_data:
            QMessageBox.warning(self.main_app, self.main_app.lang['warning'], self.main_app.lang['no_selection'].format(item_type=self.main_app.lang['source_account'].lower()))
            return
        account_type, account_id = account_data
        manager = self.main_app.cash_managers.get(account_id) if account_type == 'cash' else self.main_app.card_managers.get(account_id)
        if not manager: return
        currency = currency_widget.currentText()
        description = desc_widget.text().strip() or self.main_app.lang["manual_transaction_desc"].format(transaction_type=self.main_app.lang.get(trans_type.lower(), trans_type))
        transaction_date = datetime.now().strftime("%d.%m.%Y")
        base_amount = self.currency_manager._convert_to_base_currency(amount, currency)
        db = self.main_app.data_manager.db
        try:
            with db.transaction_scope() as cursor:
                lock_sql = "SELECT id FROM financial_accounts WHERE id = %s FOR UPDATE"
                cursor.execute(lock_sql, (account_id,))
                if not is_income:
                    balances = manager.calculate_balance(cursor=cursor)
                    current_balance = balances.get(currency, 0.0)
                    if current_balance < amount:
                        raise ValueError(f"{self.main_app.lang['insufficient_balance']}!\n\nMevcut: {format_number(current_balance)} {currency}\nİstenen: {format_number(amount)} {currency}")
                manager.add_item(FinancialTransaction(
                    transaction_date, trans_type, amount, currency, base_amount, description
                ), cursor=cursor)
            amount_widget.clear()
            desc_widget.clear()
            self.main_app.refresh_cash_table()
            self.main_app.refresh_current_account_table()
            self.main_app.refresh_dashboard_cards()
            self.main_app._show_status_message(f"{self.main_app.lang[trans_type.lower()]} added: {self.currency_manager.format_currency(amount, currency)}")
        except ValueError as ve:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], str(ve))
        except Exception as e:
            logger.error(f"Transaction Error: {e}")
            QMessageBox.critical(self.main_app, "Hata", f"İşlem kaydedilemedi: {e}")
    def _execute_account_transfer(self):
        """İki hesap arasında para transferi yapar."""
        source_data = self.main_app.transfer_source_combo.currentData()
        dest_data = self.main_app.transfer_dest_combo.currentData()
        if not source_data or not dest_data:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["no_selection"].format(item_type=self.main_app.lang['source_account'].lower()))
            return
        if source_data == dest_data:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["accounts_must_be_different"])
            return
        amount = self.main_app.get_float_from_entry(self.main_app.transfer_amount_entry, self.main_app.lang['amount'], allow_zero=False)
        if amount is None: return
        currency = self.main_app.transfer_currency_combo.currentText()
        source_type, source_id = source_data
        dest_type, dest_id = dest_data
        source_manager = self.main_app.cash_managers.get(source_id) if source_type == 'cash' else self.main_app.card_managers.get(source_id)
        dest_manager = self.main_app.cash_managers.get(dest_id) if dest_type == 'cash' else self.main_app.card_managers.get(dest_id)
        source_acc = (self.cash_account_manager.find_by_id(source_id) or self.card_account_manager.find_by_id(source_id))
        dest_acc = (self.cash_account_manager.find_by_id(dest_id) or self.card_account_manager.find_by_id(dest_id))
        if not all([source_manager, dest_manager, source_acc, dest_acc]):
            QMessageBox.critical(self.main_app, self.main_app.lang["error"], "Account information could not be retrieved.")
            return
        transaction_date = datetime.now().strftime("%d.%m.%Y")
        base_amount = self.currency_manager._convert_to_base_currency(amount, currency)
        transfer_id = str(uuid.uuid4())
        custom_desc = self.main_app.transfer_desc_entry.text().strip()
        expense_desc = custom_desc or self.main_app.lang["transfer_to_desc"].format(dest_name=dest_acc.name, source_name=source_acc.name)
        income_desc = custom_desc or self.main_app.lang["transfer_from_desc"].format(source_name=source_acc.name, dest_name=dest_acc.name)
        db = self.data_manager.db
        profile_id = self.data_manager.profile_id
        try:
            with db.transaction_scope() as cursor:
                for lock_id in sorted([source_id, dest_id]):
                    cursor.execute(
                        "SELECT id FROM financial_accounts WHERE id = %s AND profile_id = %s FOR UPDATE",
                        (lock_id, profile_id),
                    )
                    if not cursor.fetchone():
                        raise ValueError("Hesap bulunamadı veya bu profile ait değil.")
                if source_manager.calculate_balance(cursor=cursor).get(currency, 0) < amount:
                    raise ValueError(f"{self.main_app.lang['insufficient_balance']} for '{source_acc.name}' in {currency}!")
                source_manager.add_item(
                    FinancialTransaction(
                        transaction_date,
                        "Expense",
                        amount,
                        currency,
                        base_amount,
                        expense_desc,
                        related_id=transfer_id,
                    ),
                    cursor=cursor,
                )
                dest_manager.add_item(
                    FinancialTransaction(
                        transaction_date,
                        "Income",
                        amount,
                        currency,
                        base_amount,
                        income_desc,
                        related_id=transfer_id,
                    ),
                    cursor=cursor,
                )
        except ValueError as ve:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], str(ve))
            return
        except Exception as e:
            logger.error(f"Transfer Error: {e}")
            QMessageBox.critical(self.main_app, "Hata", f"Transfer tamamlanamadı: {e}")
            return
        self.main_app.transfer_amount_entry.clear()
        self.main_app.transfer_desc_entry.clear()
        self.main_app.update_all_displays() # Tüm ekranları eşzamanlı yenile (Dashboard dahil)
        self.main_app._show_status_message(f"'{source_acc.name}' hesabından '{dest_acc.name}' hesabına transfer tamamlandı.")
    def _execute_exchange_transaction_from_crm(self, data: Dict[str, Any], cursor=None) -> None:
        """
        CRM'den gelen veri ile çeviri işlemini gerçekleştirir.
        """
        out_type = (data.get("out_type") or "").strip()
        in_type = (data.get("in_type") or "").strip()
        out_amount = float(data.get("out_amount") or 0)
        in_amount = float(data.get("in_amount") or 0)
        out_gram = float(data.get("out_gram") or 0)
        in_gram = float(data.get("in_gram") or 0)

        if not out_type or not in_type or out_amount <= 0 or in_amount <= 0:
            raise ValueError("Eksik veri: Miktar veya tür girilmedi.")

        # --- YARDIMCI FONKSİYON: HESAP ÇÖZÜMLEME ---
        def resolve_account_and_manager(account_data):
            if not isinstance(account_data, (list, tuple)) or len(account_data) < 2:
                return None, None, None

            # Veri yapısı: ('type', 'id_or_name')
            acc_type, acc_id = account_data[0], account_data[1]
            acc_role = str(acc_type or "").strip().lower()

            # Eğer Depo ise (Altın Çevirisi) - Manager None döner
            if acc_role == "depot":
                return acc_id, None, acc_role

            if acc_role not in {"cash", "card"}:
                return None, None, acc_role

            # Kasa veya Kart ise
            manager = (
                self.main_app.cash_managers.get(acc_id)
                if acc_role == "cash"
                else self.main_app.card_managers.get(acc_id)
            )
            account = (
                self.cash_account_manager.find_by_id(acc_id)
                or self.card_account_manager.find_by_id(acc_id)
            )
            return account, manager, acc_role

        # Hesapları Çözümle
        out_acc, out_mgr, out_role = resolve_account_and_manager(data.get("out_account"))
        in_acc, in_mgr, in_role = resolve_account_and_manager(data.get("in_account"))

        from core.config import SUPPORTED_CURRENCIES

        # --- İŞLEMİ UYGULA ---
        transaction_date = data.get("date") or datetime.now().strftime("%d.%m.%Y")
        description_base = (data.get("description") or "").strip() or "Çeviri"
        exchange_id = str(uuid.uuid4())

        # 1. ÇIKIŞ İŞLEMİ (KAYNAK)
        if out_type in SUPPORTED_CURRENCIES and out_role in {"cash", "card"} and out_mgr:
            # Nakit Çıkışı (Expense)
            base_amount = self.currency_manager._convert_to_base_currency(out_amount, out_type)
            expense_desc = f"{description_base} (Çıkan: {format_number(out_amount)} {out_type})"

            out_mgr.add_item(
                FinancialTransaction(
                    transaction_date, "Expense", out_amount, out_type,
                    base_amount, expense_desc, related_id=exchange_id
                ), cursor=cursor
            )
        elif out_type in SUPPORTED_CURRENCIES:
            logger.info("Exchange source currency leg skipped (no cash/card account selected).")
        elif out_role == "depot" and out_acc:
            # Stok Çıkışı (Remove Stock)
            depot_name = out_acc

            # Hangi stok yöneticisi?
            sm = self.main_app.stock_manager
            if self.main_app.ziynet_manager.find_by_attribute('product_name', out_type):
                sm = self.main_app.ziynet_stock_manager
            elif self.main_app.scrap_manager.find_by_attribute('product_name', out_type):
                sm = self.main_app.scrap_stock_manager

            sm.remove_stock(out_type, out_amount, depot_name, cursor=cursor)
        else:
            logger.warning("Exchange source stock leg skipped (no depot selected).")

        # 2. GİRİŞ İŞLEMİ (HEDEF)
        if in_type in SUPPORTED_CURRENCIES and in_role in {"cash", "card"} and in_mgr:
            # Nakit Girişi (Income)
            base_amount = self.currency_manager._convert_to_base_currency(in_amount, in_type)
            income_desc = f"{description_base} (Giren: {format_number(in_amount)} {in_type})"

            in_mgr.add_item(
                FinancialTransaction(
                    transaction_date, "Income", in_amount, in_type,
                    base_amount, income_desc, related_id=exchange_id
                ), cursor=cursor
            )
        elif in_type in SUPPORTED_CURRENCIES:
            logger.info("Exchange destination currency leg skipped (no cash/card account selected).")
        elif in_role == "depot" and in_acc:
            # Stok Girişi (Add Stock)
            depot_name = in_acc

            # Hangi stok yöneticisi ve fiyat?
            sm = self.main_app.stock_manager
            p_def = self.main_app.product_manager.find_by_attribute('product_name', in_type)

            if not p_def:
                p_def = self.main_app.ziynet_manager.find_by_attribute('product_name', in_type)
                sm = self.main_app.ziynet_stock_manager
            if not p_def:
                p_def = self.main_app.scrap_manager.find_by_attribute('product_name', in_type)
                sm = self.main_app.scrap_stock_manager

            if p_def:
                price_base = self.currency_manager._convert_to_base_currency(
                    p_def.default_purchase_price, p_def.purchase_currency
                )
                if in_gram > 0:
                    from core.db import StockItem
                    sm.add_item(StockItem(
                        item_type=in_type,
                        quantity=in_amount,
                        quantity_gr=in_gram,
                        unique_barcode="",
                        depot_name=depot_name,
                        purchase_price_base=price_base,
                    ), cursor=cursor)
                else:
                    sm.add_or_update_stock(in_type, in_amount, price_base, depot_name, cursor=cursor)
        else:
            logger.warning("Exchange destination stock leg skipped (no depot selected).")
    def _execute_exchange_transaction(self):
        """
        Para birimi çevirme işlemi.
        FAZ 3: Kur makuliyet kontrolü (Sanity Check) eklendi.
        """
        source_data = self.main_app.exchange_source_account_combo.currentData()
        dest_data = self.main_app.exchange_dest_account_combo.currentData()
        if not source_data or not dest_data:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["no_selection"].format(item_type=self.main_app.lang['account'].lower()))
            return
        source_type, source_id = source_data
        dest_type, dest_id = dest_data
        source_manager = self.main_app.cash_managers.get(source_id) if source_type == 'cash' else self.main_app.card_managers.get(source_id)
        dest_manager = self.main_app.cash_managers.get(dest_id) if dest_type == 'cash' else self.main_app.card_managers.get(dest_id)
        source_acc = self.cash_account_manager.find_by_id(source_id) or self.card_account_manager.find_by_id(source_id)
        dest_acc = self.cash_account_manager.find_by_id(dest_id) or self.card_account_manager.find_by_id(dest_id)
        if not source_manager or not dest_manager or not source_acc or not dest_acc:
             QMessageBox.critical(self.main_app, self.main_app.lang["error"], "Hesap bilgileri alınamadı.")
             return
        sold_amount = self.main_app.get_float_from_entry(self.main_app.exchange_sold_amount, self.main_app.lang["sold_amount"], allow_zero=False)
        if sold_amount is None: return
        bought_amount = self.main_app.get_float_from_entry(self.main_app.exchange_bought_amount, self.main_app.lang["bought_amount"], allow_zero=False)
        if bought_amount is None: return
        sold_currency = self.main_app.exchange_sold_currency.currentText()
        bought_currency = self.main_app.exchange_bought_currency.currentText()
        if source_manager.calculate_balance().get(sold_currency, 0) < sold_amount:
            if not self.main_app._show_confirm_dialog("Yetersiz Bakiye", f"'{source_acc.name}' hesabında yeterli {sold_currency} yok. Yine de devam edilsin mi?"):
                return
        market_rate = self.currency_manager.get_exchange_rate(sold_currency, bought_currency)
        if market_rate and market_rate > 0:
            user_rate = bought_amount / sold_amount
            diff_ratio = abs(user_rate - market_rate) / market_rate
            if diff_ratio > 0.10: # %10'dan fazla sapma
                msg = (f"DİKKAT: Girdiğiniz kur piyasa değerinden %{int(diff_ratio*100)} farklı!\n\n"
                       f"Piyasa Kuru: 1 {sold_currency} = {market_rate:.4f} {bought_currency}\n"
                       f"Sizin İşleminiz: 1 {sold_currency} = {user_rate:.4f} {bought_currency}\n\n"
                       "İşlemi onaylıyor musunuz?")
                if not self.main_app._show_confirm_dialog("Kur Uyarısı", msg):
                    return
        transaction_date = datetime.now().strftime("%d.%m.%Y")
        exchange_id = str(uuid.uuid4())
        db = self.data_manager.db
        profile_id = self.data_manager.profile_id
        sold_base_amount = self.currency_manager._convert_to_base_currency(sold_amount, sold_currency)
        bought_base_amount = self.currency_manager._convert_to_base_currency(bought_amount, bought_currency)
        try:
            with db.transaction_scope() as cursor:
                for lock_id in sorted([source_id, dest_id]):
                    cursor.execute(
                        "SELECT id FROM financial_accounts WHERE id = %s AND profile_id = %s FOR UPDATE",
                        (lock_id, profile_id),
                    )
                    if not cursor.fetchone():
                        raise ValueError("Hesap bulunamadı veya bu profile ait değil.")
                expense_desc = (
                    f"Çeviri Çıkışı: {format_number(sold_amount)} {sold_currency} -> {dest_acc.name} "
                    f"({format_number(bought_amount)} {bought_currency})"
                )
                source_manager.add_item(
                    FinancialTransaction(
                        transaction_date,
                        "Expense",
                        sold_amount,
                        sold_currency,
                        sold_base_amount,
                        expense_desc,
                        related_id=exchange_id,
                    ),
                    cursor=cursor,
                )
                income_desc = (
                    f"Çeviri Girişi: {format_number(bought_amount)} {bought_currency} <- {source_acc.name} "
                    f"({format_number(sold_amount)} {sold_currency})"
                )
                dest_manager.add_item(
                    FinancialTransaction(
                        transaction_date,
                        "Income",
                        bought_amount,
                        bought_currency,
                        bought_base_amount,
                        income_desc,
                        related_id=exchange_id,
                    ),
                    cursor=cursor,
                )
        except ValueError as ve:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], str(ve))
            return
        except Exception as e:
            logger.error(f"Exchange Error: {e}")
            QMessageBox.critical(self.main_app, "Hata", f"Döviz çeviri işlemi kaydedilemedi: {e}")
            return
        self.main_app.exchange_sold_amount.clear()
        self.main_app.exchange_bought_amount.clear()
        self.main_app.refresh_cash_table()
        self.main_app.refresh_current_account_table()
        self.main_app.refresh_account_balances()
        self.main_app._show_status_message("Döviz çeviri işlemi başarıyla tamamlandı.")
    def _update_exchange_rates_display(self):
        """Canlı kurları günceller ve tabloya yazar."""
        self.main_app._show_status_message(self.main_app.lang["updating_rates"])
        QApplication.processEvents()
        success = self.currency_manager._fetch_all_rates()
        if not success:
            self.main_app._show_status_message(self.main_app.lang["rates_update_failed"], 5000)
            return
        self.main_app.rates_table.setRowCount(0)
        data = self.currency_manager.rates_cache
        target_currency = self.main_app.display_currency
        target_rate = 1.0
        if target_currency != "TRY":
            target_data = data.get(target_currency)
            if target_data:
                try:
                    target_buy, target_sell = self.currency_manager._extract_buy_sell(target_data)
                    target_rate = target_buy or target_sell or 1.0
                except Exception:
                    target_rate = 1.0
        if target_rate == 0:
            target_rate = 1.0
        self.main_app.rates_table.setHorizontalHeaderLabels([f"{self.main_app.lang.get('unit_col', 'Unit')} ({target_currency})", self.main_app.lang.get("buy_rate", "Buy"), self.main_app.lang.get("sell_rate", "Sell")])
        display_keys = [
            "USD", "EUR", "GBP",
            "gram-altin", "ceyrek-altin", "yarim-altin",
            "tam-altin", "cumhuriyet-altini", "22-ayar-bilezik"
        ]
        for key in display_keys:
            if key == target_currency: continue
            if key in data and isinstance(data[key], dict):
                item = data[key]
                name = key.replace("-", " ").title().replace("Usd", "USD").replace("Eur", "EUR").replace("Gbp", "GBP")
                try:
                    raw_buy, raw_sell = self.currency_manager._extract_buy_sell(item)
                    conv_buy = raw_buy / target_rate
                    conv_sell = raw_sell / target_rate
                    row = self.main_app.rates_table.rowCount()
                    self.main_app.rates_table.insertRow(row)
                    self.main_app.rates_table.setItem(row, 0, QTableWidgetItem(name))
                    self.main_app.rates_table.setItem(row, 1, QTableWidgetItem(format_number(conv_buy)))
                    self.main_app.rates_table.setItem(row, 2, QTableWidgetItem(format_number(conv_sell)))
                except ValueError:
                    continue
        update_time = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
        self.main_app.rates_last_updated_label.setText(f"{self.main_app.lang.get('last_update', 'Last Update')}: {update_time}")
        self.main_app._show_status_message(self.main_app.lang["rates_updated"], 3000)
    def _edit_transaction(self, manager: FinancialTransactionManager, table_attr_name: str) -> None:
        """Herhangi bir finansal işlemi (Nakit/Kart) düzenler."""
        table: QTableWidget = getattr(self.main_app, table_attr_name)
        selected_items = table.selectedItems()
        if not selected_items:
            QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["no_selection"].format(item_type=self.main_app.lang["financial_transaction"].lower()))
            return
        transaction_id = table.item(selected_items[0].row(), 1).data(Qt.ItemDataRole.UserRole)
        original_transaction = manager.find_by_id(transaction_id)
        if not original_transaction:
            QMessageBox.critical(self.main_app, self.main_app.lang["data_error"], self.main_app.lang["data_error"].format(item_type=self.main_app.lang["financial_transaction"].lower()))
            return
        dialog = EditTransactionDialog(original_transaction, self.main_app.lang, SUPPORTED_CURRENCIES, self.main_app)
        if dialog.exec():
            new_data_raw = dialog.get_data()
            try:
                new_original_amount = float(new_data_raw["original_amount"].replace(',', '.'))
                new_currency = new_data_raw["currency"]
                new_base_amount = self.currency_manager._convert_to_base_currency(new_original_amount, new_currency)
                new_data = {
                    "transaction_date": new_data_raw["transaction_date"],
                    "transaction_type": new_data_raw["transaction_type"],
                    "original_amount": new_original_amount,
                    "currency": new_currency,
                    "base_amount": new_base_amount,
                    "description": new_data_raw["description"]
                }
                manager.update_item(transaction_id, new_data)
                self.main_app.refresh_cash_table()
                self.main_app.refresh_current_account_table()
                self.main_app.refresh_dashboard_cards()
                self.main_app._show_status_message(f"{self.main_app.lang['financial_transaction']} updated.")
            except (ValueError, Exception) as e:
                QMessageBox.critical(self.main_app, self.main_app.lang["process_error"], f"{self.main_app.lang['process_error']}: {e}")
    def _delete_transaction(self, manager: FinancialTransactionManager, table_attr_name: str) -> None:
        """Herhangi bir finansal işlemi siler."""
        table: QTableWidget = getattr(self.main_app, table_attr_name)
        ids_to_delete = []
        for row in range(table.rowCount()):
            item = table.item(row, 0)
            if item and item.checkState() == Qt.CheckState.Checked:
                ids_to_delete.append(table.item(row, 1).data(Qt.ItemDataRole.UserRole))
        if not ids_to_delete:
            selected_rows = set(item.row() for item in table.selectedItems())
            if not selected_rows:
                QMessageBox.warning(self.main_app, self.main_app.lang["warning"], self.main_app.lang["no_selection"].format(item_type=self.main_app.lang["financial_transaction"].lower()))
                return
            for row in selected_rows:
                ids_to_delete.append(table.item(row, 1).data(Qt.ItemDataRole.UserRole))
        if not ids_to_delete: return
        if self.main_app._show_confirm_dialog(self.main_app.lang["delete"], self.main_app.lang["confirm_delete_transactions"].format(count=len(ids_to_delete))):
            for item_id in ids_to_delete:
                manager.delete_item(item_id)
            self.main_app.refresh_cash_table()
            self.main_app.refresh_current_account_table()
            self.main_app.refresh_dashboard_cards()
            self.main_app._show_status_message(f"{len(ids_to_delete)} transactions deleted.")
class EditTransactionDialog(QDialog):
    """Mevcut bir finansal işlemi düzenlemek için diyalog."""
    def __init__(self, transaction: FinancialTransaction, lang: Dict[str, str], supported_currencies: List[str], parent=None):
        super().__init__(parent)
        self.lang = lang
        self.setWindowTitle(self.lang["edit_transaction"])
        self.layout = QFormLayout(self)
        self.date_entry = QLineEdit(transaction.transaction_date)
        self.layout.addRow(f"{self.lang['date']} (DD.MM.YYYY):", self.date_entry)
        self.transaction_type_combo = QComboBox()
        self.transaction_type_combo.addItems([self.lang["income"], self.lang["expense"]])
        self.transaction_type_combo.setCurrentText(self.lang["income"] if transaction.transaction_type == "Income" else self.lang["expense"])
        self.layout.addRow(self.lang["transaction_type"], self.transaction_type_combo)
        amount_layout = QHBoxLayout()
        self.amount_entry = QLineEdit(f"{transaction.original_amount:.2f}")
        self.currency_combo = QComboBox()
        self.currency_combo.addItems(supported_currencies)
        self.currency_combo.setCurrentText(transaction.currency)
        amount_layout.addWidget(self.amount_entry)
        amount_layout.addWidget(self.currency_combo)
        self.layout.addRow(f"{self.lang['amount']}:", amount_layout)
        self.description_entry = QLineEdit(transaction.description)
        self.layout.addRow(self.lang["description"], self.description_entry)
        self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)
        ok_btn = self.buttons.button(QDialogButtonBox.StandardButton.Ok)
        if ok_btn:
            ok_btn.setDefault(True)
            ok_btn.setAutoDefault(True)
        self.date_entry.returnPressed.connect(self.accept)
        self.amount_entry.returnPressed.connect(self.accept)
        self.description_entry.returnPressed.connect(self.accept)
        self.layout.addRow(self.buttons)
    def get_data(self) -> Dict[str, Any]:
        """Düzenlenen verileri döndürür."""
        return {
            "transaction_date": self.date_entry.text(),
            "transaction_type": "Income" if self.transaction_type_combo.currentText() == self.lang["income"] else "Expense",
            "original_amount": self.amount_entry.text(),
            "currency": self.currency_combo.currentText(),
            "description": self.description_entry.text()
        }
class EditScheduledTransactionDialog(QDialog):
    """Vadeli işlemi düzenlemek için diyalog."""
    def __init__(self, transaction: ScheduledTransaction, lang: dict, accounts: List[Tuple[str, str, str]], parent=None):
        super().__init__(parent)
        self.lang = lang
        self.setWindowTitle(self.lang["edit_scheduled_transaction"])
        layout = QFormLayout(self)
        self.st_due_date = QDateEdit()
        self.st_due_date.setCalendarPopup(True)
        self.st_due_date.setDisplayFormat("dd.MM.yyyy")
        self.st_due_date.setDate(QDate.fromString(transaction.due_date, "dd.MM.yyyy"))
        layout.addRow(f"{self.lang['due_date']}:", self.st_due_date)
        self.st_type_combo = QComboBox()
        self.st_type_combo.addItems([self.lang["income"], self.lang["expense"]])
        self.st_type_combo.setCurrentText(self.lang["income"] if transaction.transaction_type == "Income" else self.lang["expense"])
        layout.addRow(f"{self.lang['transaction_type']}:", self.st_type_combo)
        amount_layout = QHBoxLayout()
        self.st_amount_entry = QLineEdit(f"{transaction.amount:.2f}")
        self.st_currency_combo = QComboBox()
        self.st_currency_combo.addItems(SUPPORTED_CURRENCIES)
        self.st_currency_combo.setCurrentText(transaction.currency)
        amount_layout.addWidget(self.st_amount_entry)
        amount_layout.addWidget(self.st_currency_combo)
        layout.addRow(f"{self.lang['amount']}:", amount_layout)
        self.st_description_entry = QLineEdit(transaction.description)
        layout.addRow(f"{self.lang['description']}:", self.st_description_entry)
        self.st_target_account_combo = QComboBox()
        for display_name, acc_type, acc_id in accounts:
            self.st_target_account_combo.addItem(display_name, (acc_type, acc_id))
        target_index = self.st_target_account_combo.findData(next((d for n, t, d in accounts if d == transaction.target), None), role=Qt.ItemDataRole.UserRole)
        if target_index > -1:
             self.st_target_account_combo.setCurrentIndex(target_index)
        layout.addRow(f"{self.lang['source_account']}:", self.st_target_account_combo)
        self.buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)
        ok_btn = self.buttons.button(QDialogButtonBox.StandardButton.Ok)
        if ok_btn:
            ok_btn.setDefault(True)
            ok_btn.setAutoDefault(True)
        self.st_amount_entry.returnPressed.connect(self.accept)
        self.st_description_entry.returnPressed.connect(self.accept)
        layout.addRow(self.buttons)
    def get_data(self) -> Optional[Dict[str, Any]]:
        try:
            amount = float(self.st_amount_entry.text().replace(',', '.'))
            target_data = self.st_target_account_combo.currentData()
            if not target_data: return None
            return {
                "due_date": self.st_due_date.date().toString("dd.MM.yyyy"),
                "transaction_type": "Income" if self.st_type_combo.currentText() == self.lang["income"] else "Expense",
                "amount": amount,
                "currency": self.st_currency_combo.currentText(),
                "description": self.st_description_entry.text().strip(),
                "target": target_data[1]
            }
        except (ValueError, IndexError):
            return None
class ExcelStyleTransactionDialog(QDialog):
    """
    Excel Tarzı Hızlı İşlem Penceresi.
    Cari Hesaplar sekmesindeki "Hızlı İşlem" butonları tarafından kullanılır.
    Verileri toplar ve CRMManager'a iletir.
    """
    def __init__(self, mode, person_name, managers, lang, parent=None):
        super().__init__(parent)
        self.mode = mode  # Örn: 'CashIn', 'ProductOut', 'ExchangePayable'
        self.person_name = person_name
        self.managers = managers
        self.lang = lang
        self.main_app = parent
        self.setWindowTitle(self._get_title())
        self.setMinimumWidth(750)
        self._init_ui()
        self._setup_tab_order()
    def _get_title(self):
        titles = {
            'CashIn': "NAKİT GİRİŞİ (TAHSİLAT)", 'CashOut': "NAKİT ÇIKIŞI (ÖDEME)",
            'CashPaidIn': "BEDELLİ NAKİT GİRİŞİ", 'CashPaidOut': "BEDELLİ NAKİT ÇIKIŞI",
            'ProductIn': "ALTIN GİRİŞİ (ALACAK)", 'ProductOut': "ALTIN ÇIKIŞI (BORÇ)",
            'ProductPaidIn': "BEDELLİ ALTIN GİRİŞİ", 'ProductPaidOut': "BEDELLİ ALTIN ÇIKIŞI",
            'ZiynetIn': "ZİYNET GİRİŞİ (ALACAK)", 'ZiynetOut': "ZİYNET ÇIKIŞI (BORÇ)",
            'ZiynetPaidIn': "BEDELLİ ZİYNET GİRİŞİ", 'ZiynetPaidOut': "BEDELLİ ZİYNET ÇIKIŞI",
            'ScrapIn': "HURDA GİRİŞİ (ALACAK)", 'ScrapOut': "HURDA ÇIKIŞI (BORÇ)",
            'ScrapPaidIn': "BEDELLİ HURDA GİRİŞİ", 'ScrapPaidOut': "BEDELLİ HURDA ÇIKIŞI",
            'VirmanPayable': "VİRMAN - BORÇ (Borcumu Aktar)",
            'VirmanReceivable': "VİRMAN - ALACAK (Alacağımı Aktar)",
            'ExchangePayable': "ÇEVİRİ - BORÇ (Benim Borcum)",
            'ExchangeReceivable': "ÇEVİRİ - ALACAK (Benim Alacağım)"
        }
        return f"{self.person_name} - {titles.get(self.mode, 'İŞLEM')}"
    def _init_ui(self):
        layout = QVBoxLayout(self)
        top_group = QGroupBox(f"{self.person_name} - {self.lang.get('transaction', 'TRANSACTION')}")
        top_group.setStyleSheet("QGroupBox { font-weight: bold; font-size: 14px; color: #555; }")
        top_form = QFormLayout(top_group)
        self.date_edit = QDateEdit(datetime.now())
        self.date_edit.setCalendarPopup(True)
        self.date_edit.setDisplayFormat("dd.MM.yyyy")
        self.date_edit.setMinimumHeight(30)
        top_form.addRow(f"{self.lang.get('date_upper', 'DATE:')}", self.date_edit)
        self.desc_edit = QLineEdit()
        self.desc_edit.setPlaceholderText(self.lang.get("transaction_desc_ph", "Transaction description..."))
        self.desc_edit.setMinimumHeight(30)
        top_form.addRow(f"{self.lang.get('desc_upper', 'DESCRIPTION:')}", self.desc_edit)
        layout.addWidget(top_group)
        if 'Exchange' in self.mode:
            self._init_exchange_ui(layout)
        elif 'Virman' in self.mode:
            self._init_virman_ui(layout)
        else:
            self._init_standard_ui(layout)
        btn_layout = QHBoxLayout()
        is_paid_mode = 'Paid' in self.mode
        is_stock_trade = ('Product' in self.mode or 'Ziynet' in self.mode or 'Scrap' in self.mode)
        self.btn_save = None
        self.btn_payment = None
        self.btn_print_doc = None
        if is_paid_mode or not is_stock_trade:
            save_label = self.lang.get("save_paid_debt", "SAVE (Paid/Debt Transaction)") if is_paid_mode else self.lang.get("save_upper", "SAVE")
            self.btn_save = QPushButton(f"{save_label} (Ctrl+Enter)")
            self.btn_save.setIcon(QIcon.fromTheme("document-save"))
            self.btn_save.setMinimumHeight(40)
            self.btn_save.clicked.connect(self.accept_save)
            QShortcut(QKeySequence("Ctrl+Return"), self).activated.connect(self.accept_save)
            QShortcut(QKeySequence("Ctrl+Enter"), self).activated.connect(self.accept_save)
            btn_layout.addWidget(self.btn_save)
        if is_stock_trade and not is_paid_mode:
            self.btn_payment = QPushButton(self.lang.get("make_payment_receive", "MAKE PAYMENT / RECEIVE"))
            self.btn_payment.setIcon(QIcon.fromTheme("emblem-money"))
            self.btn_payment.setMinimumHeight(40)
            self.btn_payment.setStyleSheet("font-weight: bold; font-size: 13px;")
            self.btn_payment.clicked.connect(self.accept_payment)
            btn_layout.addWidget(self.btn_payment)
        if 'Exchange' not in self.mode and 'Virman' not in self.mode:
            self.btn_print_doc = QPushButton(self.lang.get("print_invoice_receipt", "Print Invoice/Receipt (Ctrl+P)"))
            self.btn_print_doc.setIcon(QIcon.fromTheme("document-print"))
            self.btn_print_doc.setMinimumHeight(40)
            self.btn_print_doc.clicked.connect(self._print_document)
            QShortcut(QKeySequence("Ctrl+P"), self).activated.connect(self._print_document)
            btn_layout.addWidget(self.btn_print_doc)
        self.btn_cancel = QPushButton(self.lang.get("cancel", "Cancel"))
        self.btn_cancel.setMinimumHeight(40)
        self.btn_cancel.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_cancel)
        primary_btn = self.btn_payment or self.btn_save
        if primary_btn:
            primary_btn.setDefault(True)
            primary_btn.setAutoDefault(True)
        submit_handler = self.accept_payment if self.btn_payment else (self.accept_save if self.btn_save else self.accept)
        for edit in self.findChildren(QLineEdit):
            edit.returnPressed.connect(submit_handler)
        layout.addLayout(btn_layout)
        self.action_type = "save"
    def _print_document(self) -> None:
        """
        Mevcut işlem formundaki verileri alıp Belge Servisine gönderir.
        Çoklu vergi seçimi desteklenir.
        """
        doc_service = getattr(self.main_app, "document_service", None) if self.main_app else None
        if not doc_service:
            QMessageBox.critical(self, "Hata", "Belge servisi bulunamadı.")
            return

        if 'Exchange' in self.mode or 'Virman' in self.mode:
            QMessageBox.information(self, "Bilgi", "Bu işlem türü için belge yazdırma desteklenmiyor.")
            return

        options = ["Fatura (A4)", "Perakende Fişi (A4)"]
        choice, ok = QInputDialog.getItem(self, "Belge Türü", "Yazdırılacak belge türünü seçin:", options, 0, False)
        if not ok:
            return
        doc_type = "invoice" if choice.startswith("Fatura") else "receipt"

        desc = self.desc_edit.text().strip()
        date_txt = self.date_edit.date().toString("dd.MM.yyyy")
        
        transaction_data: Dict[str, Any] = {
            "source": "cari",
            "customer": {"name": self.person_name} if self.person_name else {},
            "currency": "TRY",
            "payment_method": "",
            "note": desc or date_txt,
        }

        # --- KALEMLERİ OLUŞTUR ---
        tax_items: List[Dict[str, Any]] = []
        
        # 1. Nakit İşlem Durumu
        if hasattr(self, 'amount_edit') and hasattr(self, 'currency_combo'):
            try:
                amount = float(self.amount_edit.text().replace(",", ".") or 0)
            except Exception: amount = 0.0
            
            if amount <= 0:
                QMessageBox.warning(self, "Uyarı", "Tutar 0'dan büyük olmalıdır.")
                return
            
            currency = self.currency_combo.currentText() or "TRY"
            transaction_data["currency"] = currency
            if hasattr(self, "type_combo"):
                transaction_data["payment_method"] = self.type_combo.currentText() or ""
            
            mode_label_map = {
                "CashIn": "Nakit Giriş (Tahsilat)",
                "CashOut": "Nakit Çıkış (Ödeme)",
                "CashPaidIn": "Bedelli Nakit Giriş",
                "CashPaidOut": "Bedelli Nakit Çıkış",
            }
            line_name = mode_label_map.get(self.mode, "Cari Nakit İşlemi")
            tax_items = [{"name": line_name, "qty": 1, "unit_price": amount}]
            transaction_data["note"] = " | ".join([line_name, date_txt] + ([desc] if desc else []))

        # 2. Ürün/Stok İşlem Durumu
        elif hasattr(self, 'product_combo') and hasattr(self, 'quantity_edit') and hasattr(self, 'price_edit') and hasattr(self, 'currency_combo'):
            product_name = (self.product_combo.currentText() or "").strip()
            try:
                qty = float(self.quantity_edit.text().replace(",", ".") or 0)
            except Exception: qty = 0.0
            try:
                price = float(self.price_edit.text().replace(",", ".") or 0)
            except Exception: price = 0.0
            
            if not product_name:
                QMessageBox.warning(self, "Uyarı", "Lütfen ürün seçin.")
                return
            if qty <= 0:
                QMessageBox.warning(self, "Uyarı", "Miktar 0'dan büyük olmalıdır.")
                return
            
            currency = self.currency_combo.currentText() or "TRY"
            transaction_data["currency"] = currency
            tax_items = [{"name": product_name, "qty": qty, "unit_price": price}]
            
            op_title = self.windowTitle() 
            note_parts = [op_title]
            if hasattr(self, "depot_combo"):
                depot_name = (self.depot_combo.currentText() or "").strip()
                if depot_name:
                    note_parts.append(f"Depo: {depot_name}")
            note_parts.append(date_txt)
            if desc:
                note_parts.append(desc)
            transaction_data["note"] = " | ".join(note_parts)
        else:
            QMessageBox.warning(self, "Uyarı", "Bu ekranda belge için yeterli veri bulunamadı.")
            return

        # --- VERGİ HESAPLAMA ---
        apply_taxes = False
        selected_taxes_list = []
        
        try:
            if hasattr(self, "apply_taxes_checkbox"):
                apply_taxes = self.apply_taxes_checkbox.isChecked()
            
            if hasattr(self, "selected_taxes") and self.selected_taxes:
                selected_taxes_list = self.selected_taxes
        except Exception:
            apply_taxes = False
            selected_taxes_list = []

        tax_snapshot = {}
        try:
            tax_engine = getattr(self.main_app, "tax_engine", None) if self.main_app else None
            if not tax_engine:
                raise RuntimeError("tax_engine servisi bulunamadı.")
            
            # Çoklu vergi listesini gönderiyoruz
            tax_snapshot = tax_engine.compute_snapshot(
                tax_items,
                currency=transaction_data.get("currency") or "TRY",
                selected_taxes=selected_taxes_list,
                apply_taxes=apply_taxes,
                customer_name=self.person_name or "",
                note=transaction_data.get("note") or "",
            )
        except Exception as e:
            logger.error(f"[TAX] Belge yazdırma hatası: {e}")
            # Hata durumunda vergisiz (fallback) hesapla
            subtotal = sum(float(i.get("qty") or 0) * float(i.get("unit_price") or 0) for i in tax_items)
            lines = []
            for i in tax_items:
                q = float(i.get("qty") or 0)
                up = float(i.get("unit_price") or 0)
                lines.append({
                    "name": str(i.get("name") or ""),
                    "qty": q,
                    "unit_price": up,
                    "line_subtotal": q * up,
                    "line_tax_total": 0.0,
                    "line_total": q * up,
                    "tax_items": [],
                })
            tax_snapshot = {
                "version": 1,
                "apply_taxes": False,
                "currency": transaction_data.get("currency") or "TRY",
                "subtotal": subtotal,
                "tax_total": 0.0,
                "grand_total": subtotal,
                "taxes": [],
                "lines": lines,
            }

        transaction_data["tax_snapshot"] = tax_snapshot
        doc_service.create_document_from_transaction(transaction_data, doc_type)

    def _validate_stock_inputs(self) -> Optional[Dict[str, Any]]:
        """Stok işlem formundaki zorunlu alanları doğrular."""
        if not ('Product' in self.mode or 'Ziynet' in self.mode or 'Scrap' in self.mode):
            return {}

        product_name = (self.product_combo.currentText() or "").strip() if hasattr(self, "product_combo") else ""
        depot_name = (self.depot_combo.currentText() or "").strip() if hasattr(self, "depot_combo") else ""

        if not product_name:
            QMessageBox.warning(self, "Hata", "Lütfen ürün/ziynet/hurda seçiniz.")
            return None
        if not depot_name:
            QMessageBox.warning(self, "Hata", "Lütfen depo seçiniz.")
            return None

        try:
            qty_text = (self.quantity_edit.text() if hasattr(self, "quantity_edit") else "0").replace(',', '.')
            qty = float(qty_text)
        except Exception:
            qty = 0.0

        if qty <= 0:
            QMessageBox.warning(self, "Hata", "Lütfen 0'dan büyük geçerli bir miktar giriniz.")
            return None

        qty_int = int(qty)
        if ('Product' in self.mode or 'Ziynet' in self.mode) and qty_int != qty:
            QMessageBox.warning(self, "Hata", "Altın/Ziynet işlemlerinde miktar tam sayı (adet) olmalıdır.")
            return None

        return {"qty": qty, "qty_int": qty_int, "product_name": product_name, "depot_name": depot_name}

    def accept_save(self):
        """Kaydet butonu: Altın işlemi ise detay tablosunu açar, değilse direkt kaydeder."""
        stock_form = self._validate_stock_inputs()
        if stock_form is None:
            return

        if 'Product' in self.mode:
            qty_int = int(stock_form["qty_int"])
            product_name = str(stock_form["product_name"])
            stock_mgr = self.managers.get('product_stock')
            prod_mgr = self.managers.get('product')
            is_out = 'Out' in self.mode or 'Sale' in self.mode
            dialog_mode = "Out" if is_out else "In"
            from modules.stock import AdvancedGoldEntryDialog
            dialog = AdvancedGoldEntryDialog(dialog_mode, product_name, qty_int, stock_mgr, prod_mgr, self)
            if dialog.exec():
                self.gold_details = dialog.get_details()
                self.action_type = "save"
                super().accept()
            else:
                return
        else:
            self.action_type = "save"
            super().accept()
    def accept_payment(self):
        """
        ÖDEME YAP butonu.
        Önce Barkod Tablosunu (10 satır) açar, veriyi alır, sonra ödemeye geçer.
        """
        stock_form = self._validate_stock_inputs()
        if stock_form is None:
            return

        if 'Product' in self.mode:
            qty_int = int(stock_form["qty_int"])
            product_name = str(stock_form["product_name"])
            stock_mgr = self.managers.get('product_stock')
            prod_mgr = self.managers.get('product')
            is_out = 'Out' in self.mode or 'Sale' in self.mode
            dialog_mode = "Out" if is_out else "In"
            from modules.stock import AdvancedGoldEntryDialog
            dialog = AdvancedGoldEntryDialog(dialog_mode, product_name, qty_int, stock_mgr, prod_mgr, self)
            if dialog.exec():
                self.gold_details = dialog.get_details()
                self.action_type = "payment"
                self.accept() # Şimdi diyaloğu kapat ve ödeme ekranına geç
            else:
                return # İptal edilirse hiçbir şey yapma
        else:
            self.action_type = "payment"
            self.accept()

    def _is_gold_product_type(self, type_str: str) -> bool:
        txt = str(type_str or "").strip()
        if not txt:
            return False
        product_mgr = self.managers.get('product')
        if not product_mgr:
            return False
        try:
            names = {str(n).strip() for n in (product_mgr.get_product_names() or []) if str(n).strip()}
        except Exception:
            names = set()
        return txt in names

    def _prompt_gold_meta_for_exchange_row(self, row_no: int, type_str: str, amount_widget: QLineEdit) -> bool:
        current_gram = str(amount_widget.property("gram") or "").strip()
        current_karat = str(amount_widget.property("karat") or "").strip()

        gram_txt, ok = QInputDialog.getText(
            self,
            "Bedel Detayı",
            f"{row_no}. satır - '{type_str}' için toplam gram girin:",
            text=current_gram or "0.000"
        )
        if not ok:
            return False
        try:
            gram_val = float(str(gram_txt).replace(",", "."))
        except Exception:
            QMessageBox.warning(self, "Uyarı", "Geçersiz gramaj değeri.")
            return False
        if gram_val <= 0:
            QMessageBox.warning(self, "Uyarı", "Gramaj 0'dan büyük olmalıdır.")
            return False

        karat_txt, ok = QInputDialog.getText(
            self,
            "Bedel Detayı",
            f"{row_no}. satır - '{type_str}' için ayar girin:",
            text=current_karat or ""
        )
        if not ok:
            return False
        karat_val = str(karat_txt or "").strip()
        if not karat_val:
            QMessageBox.warning(self, "Uyarı", "Ayar bilgisi boş bırakılamaz.")
            return False

        amount_widget.setProperty("gram", gram_val)
        amount_widget.setProperty("karat", karat_val)
        amount_widget.setProperty("meta_type", type_str)
        return True

    def _bind_exchange_row_meta(self, amount_widget: QLineEdit, type_combo: QComboBox, row_index: int) -> None:
        def _on_type_changed():
            selected_type = str(type_combo.currentText() or "").strip()
            if not self._is_gold_product_type(selected_type):
                amount_widget.setProperty("gram", None)
                amount_widget.setProperty("karat", "")
                amount_widget.setProperty("meta_type", "")
                return

            prev_type = str(amount_widget.property("meta_type") or "").strip()
            prev_gram = amount_widget.property("gram")
            prev_karat = str(amount_widget.property("karat") or "").strip()
            has_meta = prev_gram not in (None, "", 0, 0.0) and bool(prev_karat)
            if has_meta and prev_type == selected_type:
                return

            row_no = int(row_index) + 1
            ok = self._prompt_gold_meta_for_exchange_row(row_no=row_no, type_str=selected_type, amount_widget=amount_widget)
            if not ok:
                amount_widget.setProperty("gram", None)
                amount_widget.setProperty("karat", "")
                amount_widget.setProperty("meta_type", "")

        type_combo.currentTextChanged.connect(lambda _: _on_type_changed())

    def _prompt_gold_meta_for_type_combo(self, combo: QComboBox, type_str: str, field_label: str) -> bool:
        current_gram = str(combo.property("gram") or "").strip()
        current_karat = str(combo.property("karat") or "").strip()

        gram_txt, ok = QInputDialog.getText(
            self,
            "Ürün Detayı",
            f"{field_label} - '{type_str}' için toplam gram girin:",
            text=current_gram or "0.000",
        )
        if not ok:
            return False
        try:
            gram_val = float(str(gram_txt).replace(",", "."))
        except Exception:
            QMessageBox.warning(self, "Uyarı", "Geçersiz gramaj değeri.")
            return False
        if gram_val <= 0:
            QMessageBox.warning(self, "Uyarı", "Gramaj 0'dan büyük olmalıdır.")
            return False

        karat_txt, ok = QInputDialog.getText(
            self,
            "Ürün Detayı",
            f"{field_label} - '{type_str}' için ayar girin:",
            text=current_karat or "",
        )
        if not ok:
            return False
        karat_val = str(karat_txt or "").strip()
        if not karat_val:
            QMessageBox.warning(self, "Uyarı", "Ayar bilgisi boş bırakılamaz.")
            return False

        combo.setProperty("gram", gram_val)
        combo.setProperty("karat", karat_val)
        combo.setProperty("meta_type", type_str)
        return True

    def _bind_type_combo_meta(self, combo: QComboBox, field_label: str) -> None:
        if not hasattr(self, "_type_combo_meta_guard"):
            self._type_combo_meta_guard = False

        def _on_type_changed():
            if self._type_combo_meta_guard:
                return

            selected_type = str(combo.currentText() or "").strip()
            prev_text = str(combo.property("last_valid_text") or "").strip()

            if not self._is_gold_product_type(selected_type):
                combo.setProperty("gram", None)
                combo.setProperty("karat", "")
                combo.setProperty("meta_type", "")
                combo.setProperty("last_valid_text", selected_type)
                return

            prev_type = str(combo.property("meta_type") or "").strip()
            prev_gram = combo.property("gram")
            prev_karat = str(combo.property("karat") or "").strip()
            has_meta = prev_gram not in (None, "", 0, 0.0) and bool(prev_karat)
            if has_meta and prev_type == selected_type:
                combo.setProperty("last_valid_text", selected_type)
                return

            ok = self._prompt_gold_meta_for_type_combo(combo, selected_type, field_label)
            if ok:
                combo.setProperty("last_valid_text", selected_type)
                return

            combo.setProperty("gram", None)
            combo.setProperty("karat", "")
            combo.setProperty("meta_type", "")

            self._type_combo_meta_guard = True
            combo.blockSignals(True)
            combo.setCurrentText(prev_text if prev_text else "")
            combo.blockSignals(False)
            self._type_combo_meta_guard = False

        combo.currentTextChanged.connect(lambda _: _on_type_changed())

    def get_exchange_items(self) -> Optional[List[Dict[str, Any]]]:
        items = []
        for row in range(self.exchange_table.rowCount()):
            amount_widget = self.exchange_table.cellWidget(row, 0)
            type_widget = self.exchange_table.cellWidget(row, 1)
            if not amount_widget or not type_widget: continue
            try:
                txt_amount = amount_widget.text().replace(',', '.')
                if not txt_amount: continue
                amount = float(txt_amount)
                if amount <= 0: continue
                type_str = type_widget.currentText().strip()
                if not type_str: continue
                item_data = {'amount': amount, 'type': type_str}
                if self._is_gold_product_type(type_str):
                    gram_val = amount_widget.property("gram")
                    karat_val = str(amount_widget.property("karat") or "").strip()
                    try:
                        gram_val_f = float(gram_val)
                    except Exception:
                        gram_val_f = 0.0
                    if gram_val_f <= 0 or not karat_val:
                        QMessageBox.warning(
                            self,
                            "Eksik Bedel Detayı",
                            f"{row + 1}. satırdaki altın bedeli için gram ve ayar bilgisi zorunludur."
                        )
                        return None
                    item_data["gram"] = gram_val_f
                    item_data["karat"] = karat_val
                items.append(item_data)
            except ValueError:
                continue
        return items
    def get_data(self):
        """Formdaki verileri toplar ve döndürür."""
        data = {
            "date": self.date_edit.date().toString("dd.MM.yyyy"),
            "description": self.desc_edit.text().strip(),
            "mode": self.mode,
            "action_type": getattr(self, 'action_type', 'save')
        }
        
        if hasattr(self, 'gold_details'):
            data['gold_details'] = self.gold_details
        # Bedel satırları tabloda tutuluyor; attribute adı exchange_table.
        if hasattr(self, 'exchange_table'):
            exchange_items = self.get_exchange_items()
            if exchange_items is None:
                return None
            data['exchange_items'] = exchange_items
        
        # --- ÇEVİRİ İŞLEMİ VERİLERİ ---
        if 'Exchange' in self.mode:
            out_type = self.ex_out_type.currentText()
            in_type = self.ex_in_type.currentText()
            out_gram = self.ex_out_type.property("gram")
            out_karat = str(self.ex_out_type.property("karat") or "").strip()
            in_gram = self.ex_in_type.property("gram")
            in_karat = str(self.ex_in_type.property("karat") or "").strip()

            if self._is_gold_product_type(out_type):
                try:
                    out_gram_f = float(out_gram)
                except Exception:
                    out_gram_f = 0.0
                if out_gram_f <= 0 or not out_karat:
                    QMessageBox.warning(self, "Eksik Detay", "Kaynak türü için gram ve ayar bilgisi zorunludur.")
                    return None
            else:
                out_gram_f = 0.0
                out_karat = ""

            if self._is_gold_product_type(in_type):
                try:
                    in_gram_f = float(in_gram)
                except Exception:
                    in_gram_f = 0.0
                if in_gram_f <= 0 or not in_karat:
                    QMessageBox.warning(self, "Eksik Detay", "Hedef türü için gram ve ayar bilgisi zorunludur.")
                    return None
            else:
                in_gram_f = 0.0
                in_karat = ""

            data.update({
                "out_type": out_type,
                "out_amount": float(self.ex_out_amount.text().replace(',','.') or 0),
                "out_account": None,
                "out_gram": out_gram_f,
                "out_karat": out_karat,
                "in_type": in_type,
                "in_amount": float(self.ex_in_amount.text().replace(',','.') or 0),
                "in_account": None,
                "in_gram": in_gram_f,
                "in_karat": in_karat,
                "affect_real": False
            })
            
        elif 'Virman' in self.mode:
            source_asset = self.source_asset_combo.currentText()
            source_gram = self.source_asset_combo.property("gram")
            source_karat = str(self.source_asset_combo.property("karat") or "").strip()
            if self._is_gold_product_type(source_asset):
                try:
                    source_gram_f = float(source_gram)
                except Exception:
                    source_gram_f = 0.0
                if source_gram_f <= 0 or not source_karat:
                    QMessageBox.warning(self, "Eksik Detay", "Virman türü için gram ve ayar bilgisi zorunludur.")
                    return None
            else:
                source_gram_f = 0.0
                source_karat = ""

            data.update({
                "target_person": self.target_person_combo.currentText(),
                "source_asset": source_asset,
                "asset_type": "Mixed",
                "amount": float(self.amount_edit.text().replace(',','.') or 0),
                "source_gram": source_gram_f,
                "source_karat": source_karat,
            })
            
        elif self.mode in ['CashIn', 'CashOut', 'CashPaidIn', 'CashPaidOut']:
            affect_val = True
            if hasattr(self, 'affect_real_checkbox'):
                affect_val = self.affect_real_checkbox.isChecked()
            data.update({
                "debt_type": self.type_combo.currentText(),
                "account_data": self.source_combo.currentData(),
                "amount": float(self.amount_edit.text().replace(',','.') or 0),
                "currency": self.currency_combo.currentText(),
                "affect_real": affect_val,
                "affect_cash": affect_val,
                # VERGİ VERİLERİ (NAKİT İÇİN DE EKLENDİ)
                "apply_taxes": hasattr(self, 'apply_taxes_checkbox') and self.apply_taxes_checkbox.isChecked(),
                "selected_taxes": getattr(self, 'selected_taxes', [])
            })
            
        elif 'Product' in self.mode or 'Ziynet' in self.mode or 'Scrap' in self.mode:
            affect_val = True
            if hasattr(self, 'affect_real_checkbox'):
                affect_val = self.affect_real_checkbox.isChecked()
            
            try: qty = float(self.quantity_edit.text().replace(',','.') or 0)
            except: qty = 0
            try: prc = float(self.price_edit.text().replace(',','.') or 0)
            except: prc = 0
                
            data.update({
                "product": self.product_combo.currentText(),
                "depot": self.depot_combo.currentText(),
                "quantity": qty,
                "affect_real": affect_val,
                "price": prc,
                "currency": self.currency_combo.currentText(),

                # VERGİ VERİLERİ (GÜNCELLENDİ)
                "apply_taxes": hasattr(self, 'apply_taxes_checkbox') and self.apply_taxes_checkbox.isChecked(),
                # Tek ID yerine tüm listeyi gönderiyoruz
                "selected_taxes": getattr(self, 'selected_taxes', [])
            })
            
        return data
    def _auto_populate_payment(self):
        """
        Miktar girildiğinde alt listeye otomatik olarak:
        Miktar -> Girilen Miktar
        Tür -> Seçilen Ürün
        olarak ekler (Bedelli işlemde ürünün kendisi borçlanılıyorsa).
        """
        try:
            qty = float(self.quantity_edit.text().replace(',', '.') or 0)
            if qty <= 0: return
            product_name = self.product_combo.currentText()
            if self.exchange_table.rowCount() <= 1:
                self.exchange_table.setRowCount(0)
                self.exchange_table.insertRow(0)
                qty_display = f"{int(qty)}" if qty.is_integer() else f"{qty:.3f}".replace('.', ',')
                amount_item = QLineEdit(qty_display)
                self.exchange_table.setCellWidget(0, 0, amount_item)
                type_combo = QComboBox()
                type_combo.setEditable(True)
                self._fill_all_types(type_combo)
                self._bind_exchange_row_meta(amount_widget=amount_item, type_combo=type_combo, row_index=0)
                type_combo.setCurrentText(product_name)
                self.exchange_table.setCellWidget(0, 1, type_combo)
        except ValueError:
            pass
    def _add_exchange_row(self):
        row = self.exchange_table.rowCount()
        self.exchange_table.insertRow(row)
        amount_item = QLineEdit()
        amount_item.setValidator(QDoubleValidator(0, 999999999, 3))
        amount_item.setPlaceholderText("0.00")
        self.exchange_table.setCellWidget(row, 0, amount_item)
        type_combo = QComboBox()
        type_combo.setEditable(True)
        self._fill_all_types(type_combo)
        self.exchange_table.setCellWidget(row, 1, type_combo)
        self._bind_exchange_row_meta(amount_widget=amount_item, type_combo=type_combo, row_index=row)
        amount_item.setFocus()
    def _remove_exchange_row(self):
        curr_row = self.exchange_table.currentRow()
        if curr_row >= 0:
            self.exchange_table.removeRow(curr_row)
        else:
            rc = self.exchange_table.rowCount()
            if rc > 0: self.exchange_table.removeRow(rc - 1)
    def _init_exchange_ui(self, layout):
        exchange_group = QGroupBox(self.lang.get("exchange_details", "Exchange Details"))
        grid = QGridLayout(exchange_group)
        grid.setSpacing(10)
        
        if self.mode == 'ExchangePayable':
            txt_src = self.lang.get("source_deduct_debt", "SOURCE (Deduct from Debt / +Value)")
            color_src = "#388E3C"
            txt_dst = self.lang.get("target_add_debt", "TARGET (Add to Debt / -Value)")
            color_dst = "#D32F2F"
        else:
            txt_src = self.lang.get("source_deduct_credit", "SOURCE (Deduct from Credit / -Value)")
            color_src = "#D32F2F"
            txt_dst = self.lang.get("target_add_credit", "TARGET (Add to Credit / +Value)")
            color_dst = "#388E3C"
            
        lbl_src = QLabel(txt_src)
        lbl_src.setAlignment(Qt.AlignmentFlag.AlignCenter)
        lbl_src.setStyleSheet(f"font-weight: bold; color: {color_src}; font-size: 13px; border-bottom: 2px solid {color_src};")
        grid.addWidget(lbl_src, 0, 0, 1, 3)
        
        line = QFrame()
        line.setFrameShape(QFrame.Shape.VLine)
        line.setFrameShadow(QFrame.Shadow.Sunken)
        grid.addWidget(line, 0, 3, 3, 1)
        
        lbl_dst = QLabel(txt_dst)
        lbl_dst.setAlignment(Qt.AlignmentFlag.AlignCenter)
        lbl_dst.setStyleSheet(f"font-weight: bold; color: {color_dst}; font-size: 13px; border-bottom: 2px solid {color_dst};")
        grid.addWidget(lbl_dst, 0, 3, 1, 2)
        
        # Başlıklar
        grid.addWidget(QLabel(self.lang.get("amount_upper", "AMOUNT")), 1, 0)
        grid.addWidget(QLabel(self.lang.get("type_upper", "TYPE")), 1, 1)
        
        grid.addWidget(QLabel(self.lang.get("amount_upper", "AMOUNT")), 1, 3)
        grid.addWidget(QLabel(self.lang.get("type_upper", "TYPE")), 1, 4)
        
        # --- KAYNAK (Sol Taraf) ---
        self.ex_out_amount = QLineEdit()
        self.ex_out_amount.setValidator(QDoubleValidator(0, 999999999, 3))
        
        self.ex_out_type = QComboBox()
        self.ex_out_type.setEditable(True)
        self._fill_all_types(self.ex_out_type)
        self.ex_out_type.setProperty("last_valid_text", str(self.ex_out_type.currentText() or "").strip())
        self._bind_type_combo_meta(self.ex_out_type, "Kaynak Türü")

        grid.addWidget(self.ex_out_amount, 2, 0)
        grid.addWidget(self.ex_out_type, 2, 1)
        
        # --- HEDEF (Sağ Taraf) ---
        self.ex_in_amount = QLineEdit()
        self.ex_in_amount.setValidator(QDoubleValidator(0, 999999999, 3))
        
        self.ex_in_type = QComboBox()
        self.ex_in_type.setEditable(True)
        self._fill_all_types(self.ex_in_type)
        self.ex_in_type.setProperty("last_valid_text", str(self.ex_in_type.currentText() or "").strip())
        self._bind_type_combo_meta(self.ex_in_type, "Hedef Türü")

        grid.addWidget(self.ex_in_amount, 2, 3)
        grid.addWidget(self.ex_in_type, 2, 4)
        
        layout.addWidget(exchange_group)
    def _update_exchange_depot_combo(self, type_combo, account_combo):
        """
        Seçilen türe göre (Para Birimi veya Altın) kasa/depo listesini doldurur.
        """
        selected_type = str(type_combo.currentText() or "").strip()
        prev_data = account_combo.currentData()
        account_combo.clear()

        if not selected_type or "---" in selected_type:
            account_combo.addItem("Tür seçin", None)
            return

        # Para Birimleri Listesi
        from core.config import SUPPORTED_CURRENCIES

        # 1. Eğer Para Birimi ise -> Kasa/Banka Listele
        if selected_type.upper() in SUPPORTED_CURRENCIES:
            # self._fill_accounts zaten bu işi yapıyor (cash/card tuple olarak ekler)
            self._fill_accounts(account_combo)

        # 2. Eğer Ürün ise -> Depo Listele
        else:
            # Depo listesini al
            depots = []
            if hasattr(self.main_app, '_get_accessible_depots'):
                depots = self.main_app._get_accessible_depots()
            elif 'depot' in self.managers:
                depots = self.managers['depot'].get_depot_names()
                
            for d in depots:
                # Formatı FinanceManager ile uyumlu yapıyoruz: ('depot', 'DepoAdi')
                account_combo.addItem(f"Depo: {d}", ('depot', d))
            if account_combo.count() == 0:
                account_combo.addItem("Depo bulunamadı", None)

        if prev_data is not None:
            for idx in range(account_combo.count()):
                if account_combo.itemData(idx) == prev_data:
                    account_combo.setCurrentIndex(idx)
                    break
    def _init_virman_ui(self, layout):
        virman_group = QGroupBox(self.lang.get("transfer_details", "Transfer Details"))
        grid = QGridLayout(virman_group)
        grid.setSpacing(12)
        self.target_person_combo = QComboBox(); self.target_person_combo.setEditable(True)
        self.target_person_combo.addItems(self.managers['person'].get_person_names())
        self.amount_edit = QLineEdit(); self.amount_edit.setPlaceholderText("0.00"); self.amount_edit.setValidator(QDoubleValidator(0, 999999999, 3))
        self.source_asset_combo = QComboBox(); self.source_asset_combo.setEditable(True); self._fill_all_types(self.source_asset_combo)
        self.source_asset_combo.setProperty("last_valid_text", str(self.source_asset_combo.currentText() or "").strip())
        self._bind_type_combo_meta(self.source_asset_combo, "Virman Türü")
        grid.addWidget(QLabel(self.lang.get("recipient_target_person", "Recipient/Target Person:")), 0, 0); grid.addWidget(QLabel(self.lang.get("amount_colon", "Amount:")), 0, 1); grid.addWidget(QLabel(self.lang.get("type_currency", "Type / Currency:")), 0, 2)
        grid.addWidget(self.target_person_combo, 1, 0); grid.addWidget(self.amount_edit, 1, 1); grid.addWidget(self.source_asset_combo, 1, 2)
        layout.addWidget(virman_group)
    def _init_standard_ui(self, layout):
        """
        Standart işlem arayüzünü (Nakit, Altın, Hurda vb.) oluşturur.
        Çoklu vergi seçimi için buton yapısına geçildi.
        """
        std_group = QGroupBox(self.lang.get("transaction_details", "Transaction Details"))
        grid = QGridLayout(std_group)
        grid.setSpacing(12)

        # --- MOD A: NAKİT İŞLEMLERİ ---
        if self.mode in ['CashIn', 'CashOut', 'CashPaidIn', 'CashPaidOut']:
            self.type_combo = QComboBox()
            self.type_combo.addItems([self.lang.get("cash_upper", "CASH"), self.lang.get("card_upper", "CARD"), self.lang.get("transfer_eft", "TRANSFER/EFT")])
            
            self.source_combo = QComboBox()
            self._fill_accounts(self.source_combo, include_cash=True, include_card=False)
            
            self.amount_edit = QLineEdit()
            self.amount_edit.setValidator(QDoubleValidator(0, 999999999, 2))
            
            self.currency_combo = QComboBox()
            self.currency_combo.addItems(SUPPORTED_CURRENCIES)
            
            grid.addWidget(QLabel(self.lang.get("transaction_type_colon", "Transaction Type:")), 0, 0)
            grid.addWidget(self.type_combo, 1, 0)
            grid.addWidget(QLabel(self.lang.get("cash_bank_colon", "Cash / Bank:")), 0, 1)
            grid.addWidget(self.source_combo, 1, 1)
            grid.addWidget(QLabel(self.lang.get("amount_colon", "Amount:")), 0, 2)
            grid.addWidget(self.amount_edit, 1, 2)
            grid.addWidget(QLabel(self.lang.get("currency_colon", "Currency:")), 0, 3)
            grid.addWidget(self.currency_combo, 1, 3)
            
            self.type_combo.currentIndexChanged.connect(self._on_transaction_type_changed)
            self._on_transaction_type_changed()

        # --- MOD B: STOK İŞLEMLERİ (Altın, Ziynet, Hurda) ---
        elif 'Product' in self.mode or 'Ziynet' in self.mode or 'Scrap' in self.mode:
            mgr_key = 'product' if 'Product' in self.mode else ('ziynet' if 'Ziynet' in self.mode else 'scrap')
            lbl_stock = "Ürün:" if 'Product' in self.mode else ("Ziynet:" if 'Ziynet' in self.mode else "Hurda:")
            
            self.product_combo = QComboBox()
            self.product_combo.setEditable(True)
            self.product_combo.addItems(self.managers[mgr_key].get_product_names())
            
            self.depot_combo = QComboBox()
            self.depot_combo.addItems(self.managers['depot'].get_depot_names())
            
            qty_precision = 0 if ('Product' in self.mode or 'Ziynet' in self.mode) else 3
            self.quantity_edit = QLineEdit()
            self.quantity_edit.setValidator(QDoubleValidator(0, 999999999, qty_precision))
            
            self.price_edit = QLineEdit()
            self.price_edit.setValidator(QDoubleValidator(0, 999999999, 2))
            
            self.currency_combo = QComboBox()
            self.currency_combo.addItems(SUPPORTED_CURRENCIES)
            
            grid.addWidget(QLabel(lbl_stock), 0, 0)
            grid.addWidget(self.product_combo, 1, 0)
            grid.addWidget(QLabel(self.lang.get("depot_colon", "Depot:")), 0, 1)
            grid.addWidget(self.depot_combo, 1, 1)
            grid.addWidget(QLabel("Miktar:"), 0, 2)
            grid.addWidget(self.quantity_edit, 1, 2)
            grid.addWidget(QLabel(self.lang.get("unit_price_colon", "Unit Price:")), 0, 3)
            grid.addWidget(self.price_edit, 1, 3)
            grid.addWidget(QLabel(self.lang.get("currency_short_colon", "Cur.:")), 0, 4)
            grid.addWidget(self.currency_combo, 1, 4)
            
            if 'Paid' in self.mode:
                self.quantity_edit.textChanged.connect(self._auto_populate_payment)
                self.product_combo.currentTextChanged.connect(self._auto_populate_payment)

        layout.addWidget(std_group)

        # --- BEDEL TABLOSU (Sadece Bedelli İşlemlerde) ---
        if 'Paid' in self.mode:
            bedel_group = QGroupBox(self.lang.get("equivalent_payment_list", "Equivalent/Payment List (Optional - Trade/Payment)"))
            bedel_layout = QVBoxLayout(bedel_group)
            
            self.exchange_table = QTableWidget(0, 2)
            self.exchange_table.setHorizontalHeaderLabels([self.lang.get("amount_col", "Amount"), self.lang.get("type_tl_usd_has", "Type (TRY, USD, Pure...)")])
            self.exchange_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
            self.exchange_table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
            self.exchange_table.installEventFilter(self)
            bedel_layout.addWidget(self.exchange_table)
            
            btn_layout_bedel = QHBoxLayout()
            self.btn_add_bedel = QPushButton(self.lang.get("add_row", "Add Row"))
            self.btn_add_bedel.clicked.connect(self._add_exchange_row)
            self.btn_remove_bedel = QPushButton(self.lang.get("delete_row", "Delete Row"))
            self.btn_remove_bedel.clicked.connect(self._remove_exchange_row)
            
            btn_layout_bedel.addWidget(self.btn_add_bedel)
            btn_layout_bedel.addWidget(self.btn_remove_bedel)
            btn_layout_bedel.addStretch()
            
            bedel_layout.addLayout(btn_layout_bedel)
            layout.addWidget(bedel_group)

        # --- ALT KISIM: STOK VE VERGİ ---
        if 'Virman' not in self.mode and 'Cash' not in self.mode:
            chk_text = self.lang.get("affect_stock", "Affect Stock")
            self.affect_real_checkbox = QCheckBox(chk_text)
            self.affect_real_checkbox.setChecked(True)
            self.affect_real_checkbox.setStyleSheet("color: green; font-weight: bold;")
            layout.addWidget(self.affect_real_checkbox)
            
            # --- VERGİ ALANI (ÇOKLU SEÇİM İÇİN GÜNCELLENDİ) ---
            self.apply_taxes_checkbox = QCheckBox(self.lang.get("calculate_tax", "Calculate Tax"))
            self.apply_taxes_checkbox.setChecked(False)
            
            # Seçilen vergileri tutacak liste
            self.selected_taxes = [] 
            
            # Seçili vergileri gösteren etiket
            self.tax_info_label = QLabel(self.main_app.lang.get("selected_none", "Selected: None"))
            self.tax_info_label.setStyleSheet("color: #1976D2; font-style: italic;")
            
            # Vergi Seçim Butonu
            self.select_tax_btn = QPushButton(self.lang.get("select_tax", "Select Tax"))
            self.select_tax_btn.setEnabled(False)
            self.select_tax_btn.clicked.connect(self._on_select_tax_clicked)
            
            # Checkbox açılınca butonu aktif et
            self.apply_taxes_checkbox.toggled.connect(self.select_tax_btn.setEnabled)
            self.apply_taxes_checkbox.toggled.connect(self._toggle_tax_logic)

            tax_row = QHBoxLayout()
            tax_row.addWidget(self.apply_taxes_checkbox)
            tax_row.addWidget(self.tax_info_label)
            tax_row.addWidget(self.select_tax_btn)
            tax_row.addStretch()
            layout.addLayout(tax_row)
    def _toggle_tax_logic(self, checked):
        """Vergi kutusu kapandığında seçimi temizler."""
        if not checked:
            self.selected_taxes = []
            self.tax_info_label.setText("Seçili: Yok")

    def _on_select_tax_clicked(self):
        """Çoklu vergi seçim pencresini açar."""
        if not self.main_app or not hasattr(self.main_app, "tax_definition_manager"):
            QMessageBox.warning(self, "Hata", "Vergi yöneticisi bulunamadı.")
            return

        all_taxes = self.main_app.tax_definition_manager.get_all()
        if not all_taxes:
            QMessageBox.warning(self, "Uyarı", "Tanımlı vergi yok. Ayarlar > Vergiler menüsünden ekleyin.")
            return

        # Mevcut seçili ID'leri bul
        current_ids = [t['id'] for t in self.selected_taxes]

        from ui.tax_dialogs import TaxSelectionDialog
        dlg = TaxSelectionDialog(all_taxes, current_ids, self)
        if dlg.exec():
            self.selected_taxes = dlg.get_selected_taxes() # [{'id':..., 'name':..., 'rate':...}, ...] döner
            if self.selected_taxes:
                names = ", ".join([t['name'] for t in self.selected_taxes])
                self.tax_info_label.setText(f"Seçili: {names}")
            else:
                self.tax_info_label.setText("Seçili: Yok")
    def _setup_tab_order(self):
        """Basit Tab sıralaması."""
        self.setTabOrder(self.date_edit, self.desc_edit)
    def _get_main_affect_cash_value(self) -> bool:
        main_app = self.main_app or self.parent()
        checkbox = getattr(main_app, "affect_cash_checkbox", None)
        return checkbox.isChecked() if checkbox else True
    def eventFilter(self, source, event):
        if (event.type() == QEvent.Type.KeyPress and source is getattr(self, 'exchange_table', None) and event.key() == Qt.Key.Key_Tab):
            rows = self.exchange_table.rowCount()
            if self.exchange_table.currentRow() == rows - 1 and self.exchange_table.currentColumn() == 1:
                self.btn_add_bedel.setFocus()
                return True
        return super().eventFilter(source, event)
    def _fill_accounts(self, combo, include_cash: bool = True, include_card: bool = True):
        combo.clear()
        if hasattr(self.parent(), '_get_accessible_cash_accounts'):
            cash_accs = self.parent()._get_accessible_cash_accounts()
            card_accs = self.parent()._get_accessible_card_accounts()
        else:
            cash_accs = self.managers['cash_acc'].get_all()
            card_accs = self.managers['card_acc'].get_all()
        if include_cash:
            for acc in cash_accs:
                combo.addItem(f"Kasa: {acc.name}", ('cash', acc.id))
        if include_card:
            for acc in card_accs:
                combo.addItem(f"Banka/Kart: {acc.name}", ('card', acc.id))
        if combo.count() == 0:
            combo.addItem("Hesap bulunamadı", None)
    def _fill_all_types(self, combo):
        combo.clear()
        combo.addItem("--- PARA BİRİMLERİ ---")
        combo.addItems(SUPPORTED_CURRENCIES)
        if 'product' in self.managers:
            combo.addItem("--- ALTIN ÜRÜNLER ---")
            combo.addItems(self.managers['product'].get_product_names())
        if 'ziynet' in self.managers:
            combo.addItem("--- ZİYNETLER ---")
            combo.addItems(self.managers['ziynet'].get_product_names())
        if 'scrap' in self.managers:
            combo.addItem("--- HURDALAR ---")
            combo.addItems(self.managers['scrap'].get_product_names())
    def _on_transaction_type_changed(self):
        if not hasattr(self, 'type_combo') or not hasattr(self, 'source_combo'):
            return
        selected = self.type_combo.currentText().upper()
        if "NAK" in selected:
            self._fill_accounts(self.source_combo, include_cash=True, include_card=False)
        elif "KART" in selected or "HAVALE" in selected or "EFT" in selected:
            self._fill_accounts(self.source_combo, include_cash=False, include_card=True)
        else:
            self._fill_accounts(self.source_combo, include_cash=True, include_card=True)


    

    
    
class AdvancedEditTransactionDialog(QDialog):
    """Geçmiş bir işlemi güvenli şekilde düzenlemek için basit edit penceresi."""

    def __init__(self, event_data, managers, lang, parent=None):
        super().__init__(parent)
        self.managers = managers
        self.lang = lang
        self.event_data = event_data
        self.person_name = str(getattr(event_data, "person_full_name", "") or "")
        self.mode = self._detect_mode_from_event(event_data)
        self.action_type = "save"
        self.setWindowTitle(self.lang.get("edit_transaction_title", "Edit Transaction"))
        self.setMinimumWidth(520)
        self._init_ui()
        self._pre_fill_data()

    def _detect_mode_from_event(self, event_data) -> str:
        desc_raw = str(getattr(event_data, "description", "") or "").strip()
        if desc_raw.startswith("{"):
            try:
                parsed = json.loads(desc_raw)
                mode = str((parsed or {}).get("mode") or "").strip()
                if mode:
                    return mode
            except Exception:
                pass

        event_type = str(getattr(event_data, "event_type", "") or "").upper()
        currency = str(getattr(event_data, "currency", "") or "").strip()
        is_incoming = float(getattr(event_data, "credit_original", 0) or 0) > 0

        if currency in SUPPORTED_CURRENCIES:
            return "CashIn" if is_incoming else "CashOut"
        if "ZIYNET" in event_type or "ZİYNET" in event_type:
            return "ZiynetIn" if is_incoming else "ZiynetOut"
        if "HURDA" in event_type or "SCRAP" in event_type:
            return "ScrapIn" if is_incoming else "ScrapOut"
        return "ProductIn" if is_incoming else "ProductOut"

    @staticmethod
    def _safe_to_float(value: Any, default: float = 0.0) -> float:
        if isinstance(value, (int, float)):
            return float(value)
        text = str(value or "").strip()
        if not text:
            return default
        if "," in text and "." in text:
            if text.rfind(",") > text.rfind("."):
                text = text.replace(".", "").replace(",", ".")
            else:
                text = text.replace(",", "")
        elif "," in text:
            text = text.replace(",", ".")
        try:
            return float(text)
        except Exception:
            return default

    def _set_combo_text_or_add(self, combo: QComboBox, text: str) -> None:
        text = str(text or "").strip()
        if not text:
            return
        idx = combo.findText(text)
        if idx >= 0:
            combo.setCurrentIndex(idx)
        else:
            combo.addItem(text)
            combo.setCurrentIndex(combo.count() - 1)

    def _build_cash_account_combo(self) -> None:
        self.account_combo.clear()
        cash_mgr = self.managers.get("cash_acc")
        card_mgr = self.managers.get("card_acc")
        cash_accs = cash_mgr.get_all() if cash_mgr else []
        card_accs = card_mgr.get_all() if card_mgr else []

        for acc in cash_accs:
            self.account_combo.addItem(f"Kasa: {acc.name}", ("cash", acc.id))
        for acc in card_accs:
            self.account_combo.addItem(f"Banka/Kart: {acc.name}", ("card", acc.id))
        if self.account_combo.count() == 0:
            self.account_combo.addItem("Hesap bulunamadı", None)

    def _build_product_combo(self) -> None:
        self.prod_combo.clear()
        if "Ziynet" in self.mode:
            mgr = self.managers.get("ziynet")
        elif "Scrap" in self.mode:
            mgr = self.managers.get("scrap")
        else:
            mgr = self.managers.get("product")
        names = mgr.get_product_names() if mgr else []
        if names:
            self.prod_combo.addItems(names)

    def _init_ui(self):
        layout = QVBoxLayout(self)

        top_group = QGroupBox(f"{self.person_name} - {self.lang.get('transaction', 'TRANSACTION')}")
        top_group.setStyleSheet("QGroupBox { font-weight: bold; font-size: 14px; color: #555; }")
        top_form = QFormLayout(top_group)

        self.date_edit = QDateEdit(datetime.now())
        self.date_edit.setCalendarPopup(True)
        self.date_edit.setDisplayFormat("dd.MM.yyyy")
        self.date_edit.setMinimumHeight(30)
        top_form.addRow(f"{self.lang.get('date_upper', 'DATE:')}", self.date_edit)

        self.desc_edit = QLineEdit()
        self.desc_edit.setPlaceholderText(self.lang.get("transaction_desc_ph", "Transaction description..."))
        self.desc_edit.setMinimumHeight(30)
        top_form.addRow(f"{self.lang.get('desc_upper', 'DESCRIPTION:')}", self.desc_edit)

        self.type_combo = QComboBox()
        self.type_combo.addItems([self.lang.get("cash", "Cash"), self.lang.get("stock", "Stock")])
        self.type_combo.currentIndexChanged.connect(self._toggle_inputs)
        top_form.addRow(f"{self.lang.get('type_upper', 'TYPE:')}", self.type_combo)
        layout.addWidget(top_group)

        self.cash_widget = QGroupBox(self.lang.get("cash_details", "Cash Details"))
        cash_form = QFormLayout(self.cash_widget)
        self.account_combo = QComboBox()
        self._build_cash_account_combo()
        self.cash_amount = QLineEdit()
        self.cash_amount.setValidator(QDoubleValidator(0, 999999999, 2))
        self.cash_curr = QComboBox()
        self.cash_curr.addItems(SUPPORTED_CURRENCIES)
        cash_form.addRow(f"{self.lang.get('account_colon', 'Account:')}", self.account_combo)
        cash_form.addRow(f"{self.lang.get('amount_colon', 'Amount:')}", self.cash_amount)
        cash_form.addRow(f"{self.lang.get('currency_colon', 'Currency:')}", self.cash_curr)
        layout.addWidget(self.cash_widget)

        self.prod_widget = QGroupBox(self.lang.get("stock_details", "Stock Details"))
        prod_form = QFormLayout(self.prod_widget)
        self.depot_combo = QComboBox()
        depot_mgr = self.managers.get("depot")
        if depot_mgr:
            self.depot_combo.addItems(depot_mgr.get_depot_names())
        self.prod_combo = QComboBox()
        self.prod_combo.setEditable(True)
        self._build_product_combo()
        self.qty_edit = QLineEdit()
        qty_precision = 3 if "Scrap" in self.mode else 0
        self.qty_edit.setValidator(QDoubleValidator(0, 999999999, qty_precision))
        prod_form.addRow(f"{self.lang.get('depot_colon', 'Depot:')}", self.depot_combo)
        prod_form.addRow(f"{self.lang.get('product_colon', 'Product:')}", self.prod_combo)
        prod_form.addRow(f"{self.lang.get('quantity_colon', 'Quantity:')}", self.qty_edit)
        layout.addWidget(self.prod_widget)

        btn_layout = QHBoxLayout()
        self.btn_save = QPushButton(self.lang.get("save_upper", "SAVE"))
        self.btn_save.setIcon(QIcon.fromTheme("document-save"))
        self.btn_save.setMinimumHeight(40)
        self.btn_save.clicked.connect(self.accept_save)
        btn_layout.addWidget(self.btn_save)

        self.btn_cancel = QPushButton(self.lang.get("cancel", "Cancel"))
        self.btn_cancel.setMinimumHeight(40)
        self.btn_cancel.clicked.connect(self.reject)
        btn_layout.addWidget(self.btn_cancel)
        layout.addLayout(btn_layout)

    def _toggle_inputs(self):
        is_cash = (self.type_combo.currentIndex() == 0)
        self.cash_widget.setVisible(is_cash)
        self.prod_widget.setVisible(not is_cash)

    def accept_save(self):
        self.action_type = "save"
        self.accept()

    def accept_payment(self):
        self.action_type = "payment"
        self.accept()

    def _pre_fill_data(self):
        e = self.event_data
        try:
            dt = datetime.fromisoformat(str(e.event_date))
            self.date_edit.setDate(QDate(dt.year, dt.month, dt.day))
        except Exception:
            pass

        desc_raw = str(getattr(e, "description", "") or "")
        desc_display = desc_raw
        parsed = {}
        if desc_raw.strip().startswith("{"):
            try:
                parsed = json.loads(desc_raw)
                desc_display = str(parsed.get("main_desc") or desc_raw)
            except Exception:
                parsed = {}
        self.desc_edit.setText(desc_display)

        amount_value = self._safe_to_float(getattr(e, "transaction_amount", 0), 0.0)
        if amount_value <= 0:
            amount_value = self._safe_to_float(getattr(e, "debt_original", 0), 0.0) or self._safe_to_float(getattr(e, "credit_original", 0), 0.0)

        event_currency = str(getattr(e, "currency", "") or "").strip()
        if event_currency in SUPPORTED_CURRENCIES:
            self.type_combo.setCurrentIndex(0)
            self.cash_amount.setText(f"{amount_value:.2f}".replace(".", ","))
            self.cash_curr.setCurrentText(event_currency)
        else:
            self.type_combo.setCurrentIndex(1)
            qty = self._safe_to_float(parsed.get("main_qty"), 0.0)
            if qty <= 0:
                qty = amount_value
            self.qty_edit.setText(f"{qty:.3f}".replace(".", ","))

            product_name = str(parsed.get("main_product") or event_currency or "").strip()
            if "," in product_name:
                product_name = product_name.split(",", 1)[0].strip()
            self._set_combo_text_or_add(self.prod_combo, product_name)

            depot_name = str(parsed.get("main_depot") or "").strip()
            if not depot_name and " - " in desc_display:
                depot_name = desc_display.split(" - ", 1)[0].strip()
            self._set_combo_text_or_add(self.depot_combo, depot_name or "Merkez")

        self._toggle_inputs()

    def get_data(self):
        is_cash = (self.type_combo.currentIndex() == 0)
        data = {
            "date": self.date_edit.date().toString("dd.MM.yyyy"),
            "description": self.desc_edit.text().strip(),
            "type": "Cash" if is_cash else "Product",
        }
        if is_cash:
            data.update(
                {
                    "account_data": self.account_combo.currentData(),
                    "amount": self._safe_to_float(self.cash_amount.text(), 0.0),
                    "currency": self.cash_curr.currentText(),
                }
            )
        else:
            data.update(
                {
                    "depot": self.depot_combo.currentText(),
                    "product": self.prod_combo.currentText(),
                    "quantity": self._safe_to_float(self.qty_edit.text(), 0.0),
                }
            )
        return data




