﻿﻿<template>
  <!-- ===========================================================
       🧾 ORDER ENTRY PAGE (BSSApp)
       v23 — Sticky-stack + Drawer uyumlu yapı
  ============================================================ -->
  <q-page
    v-if="canReadOrder"
    class="order-page"
    :style="{ '--grid-header-h': gridHeaderHeight + 'px' }"
  >
    <!-- 🔄 SAYFA LOADERI -->
    <q-inner-loading :showing="isPageBlocking" color="primary">
      <q-spinner size="50px" />
    </q-inner-loading>

    <template v-if="!isPageBlocking">
      <!-- =======================================================
           🔹 STICKY STACK (Filter + Save + Header)
      ======================================================== -->
      <div class="sticky-stack">

        <!-- 🔸 1. Satır: Filtre Bar -->
        <div class="filter-bar row q-col-gutter-md q-mb-sm">

          <!-- 🧾 Cari Seçimi -->
          <div class="col-5">
            <q-select
              v-model="form.CurrAccCode"
              :options="filteredCariOptions"
              label="Cari Seçimi"
              filled
              dense
              use-input
              input-debounce="300"
              emit-value
              map-options
              option-value="Cari_Kod"
              :option-label="opt => `${opt.Cari_Kod} - ${opt.Cari_Ad}`"
              @filter="filterCari"
              @update:model-value="onCariChange"
              :loading="loadingCari"
              :disable="isEditMode || isClosedOrder || isViewOnly"
              :readonly="isViewOnly"
              clearable

            >

              <template #option="scope">
                <q-item v-bind="scope.itemProps">
                  <q-item-section>
                    <q-item-label>{{ scope.opt.Cari_Ad }}</q-item-label>
                    <q-item-label caption>{{ scope.opt.Cari_Kod }}</q-item-label>
                  </q-item-section>
                </q-item>
              </template>
            </q-select>
          </div>

          <!-- 🔢 Sipariş No -->
          <div class="col-2">
            <q-input
              v-model="form.OrderNumber"
              label="Sipariş No"
              filled
              dense
              :disable="isEditMode || isClosedOrder || isViewOnly"

              :readonly="isViewOnly"
            />
          </div>

          <!-- 📅 Oluşturulma Tarihi -->
          <div class="col-2">
            <q-input
              :model-value="formatDateInput(form.OrderDate)"
              label="Oluşturulma Tarihi"
              type="date"
              filled
              dense
              @update:model-value="v => form.OrderDate = v"
              :disable="isEditMode || isClosedOrder || isViewOnly"
              :readonly="isViewOnly"

            />
          </div>

          <!-- 📅 Tahmini Termin Tarihi (AverageDueDate kilitlenmeyecek) -->
          <div class="col-2">
            <q-input
              :model-value="formatDateInput(form.AverageDueDate)"
              label="Tahmini Termin Tarihi"
              type="date"
              filled
              dense
              @update:model-value="v => form.AverageDueDate = v"
              :readonly="isViewOnly"
              :disable="isViewOnly"

            />
          </div>

          <!-- 🔹 Cari Bilgi Satırı (2. Satır) -->
          <div
            v-if="cariInfo"
            v-show="!filterBarCollapsed"
            class="col-12 row q-col-gutter-md q-mt-xs cari-info-bar"
          >
            <div class="col-3">
              <q-input
                :model-value="cariInfo.Musteri_Temsilcisi || '-'"
                label="Müşteri Temsilcisi"
                filled dense readonly
              />
            </div>
            <div class="col-3">
              <q-input
                :model-value="cariInfo.Musteri_Ana_Grubu || '-'"
                label="Ana Grup"
                filled dense readonly
              />
            </div>
            <div class="col-3">
              <q-input
                :model-value="cariInfo.Piyasa || '-'"
                label="Piyasa"
                filled dense readonly
              />
            </div>
            <div class="col-3">
              <q-input
                :model-value="cariInfo.Ulke || '-'"
                label="Ülke"
                filled dense readonly
              />
            </div>
          </div>

          <!-- 💰 TOPLAM TUTAR + KDV -->
          <div v-show="!filterBarCollapsed" class="col-12 row q-col-gutter-sm q-mt-xs items-center">
            <!-- 💰 Toplam Tutar -->
            <div class="col-3">
              <q-input
                dense
                filled
                :model-value="Number(orderStore.totalAmount || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 })"
                label="Toplam Tutar"
                readonly
              >
                <template #append>{{ form.pb }}</template>
              </q-input>
            </div>

            <!-- 🔘 KDV Checkbox -->
            <div class="col-auto flex items-center">
              <q-checkbox
                v-model="form.includeVat"
                label="KDV Dahil"
                color="primary"
                @update:model-value="onVatToggle"
                :disable="isClosedRow||isViewOnly"
                :readonly="isViewOnly"
              />
            </div>

            <!-- ⚙️ KDV ALANLARI: sadece tikliyken görünür -->
            <template v-if="form.includeVat">
              <!-- % oran sadece bilgi -->
              <div class="col-1">
                <q-input
                  dense
                  filled
                  :model-value="form.vatRate"
                  label="%"
                  readonly
                >
                  <template #append>%</template>
                </q-input>
              </div>

              <!-- 🧮 KDV Tutarı (manuel düzenlenebilir) -->
              <div class="col-2">
                <q-input
                  dense
                  filled
                  v-model="form.vatAmountInput"
                  label="KDV Tutarı"
                  @update:model-value="onVatAmountChange"
                  input-class="text-right"
                  :disable="isClosedRow || isViewOnly"
                  :readonly="isViewOnly"
                >
                  <template #append>{{ form.pb }}</template>
                </q-input>
              </div>
              <!-- 🧾 KDV Dahil Toplam -->
              <div class="col-2">
                <q-input
                  dense
                  filled
                  :model-value="Number(form.totalWithVat || 0).toLocaleString('tr-TR',{minimumFractionDigits:2})"
                  label="KDV Dahil Toplam"
                  readonly
                >
                  <template #append>{{ form.pb }}</template>
                </q-input>
              </div>
            </template>
          </div>

        </div>
        <!-- 📝 Sipariş Genel Açıklaması (filter bar altında) -->
        <div v-show="!filterBarCollapsed" class="filter-bar-desc q-mt-sm">
          <q-input
            v-model="form.Description"
            type="textarea"
            label="Sipariş Genel Açıklaması"
            filled
            dense
            autogrow
            maxlength="1500"
            counter
            placeholder="Siparişe genel açıklama giriniz (örn. teslimat, üretim notu, müşteri isteği...)"
            :disable="isClosedRow"
            :readonly="isViewOnly"
          />
        </div>
        <!-- 🔹 Save Toolbar -->
        <div class="save-toolbar">
          <div class="text-subtitle2 text-weight-bold">Sipariş Formu</div>
          <div>
            <q-btn
              flat
              color="grey-7"
              class="q-ml-sm"
              :label="filterBarCollapsed ? 'FİLTREYİ GENİŞLET' : 'FİLTREYİ DARALT'"
              :icon="filterBarCollapsed ? 'expand_more' : 'expand_less'"
              @click="toggleFilterBarCollapsed"
            />
            <q-btn
              flat
              color="grey-7"
              class="q-ml-sm"
              :label="compactGridHeader ? 'BAŞLIK GENİŞLET' : 'BAŞLIK DARALT'"
              :icon="compactGridHeader ? 'unfold_more' : 'unfold_less'"
              @click="compactGridHeader = !compactGridHeader"
            />
            <q-btn
              v-if="isViewOnly && canExportOrder"
              label="🖨 SİPARİŞİ YAZDIR"
              color="primary"
              icon="print"
              class="q-ml-sm"
              @click="onPrintOrder"
            />

            <q-btn
              v-if="canMutateRows"
              label="SATIR EKLE"
              color="secondary"
              icon="add"
              class="q-ml-sm"
              @click="openNewRowEditor"
              :disable="isClosedRow || isViewOnly || !canMutateRows"
            />
            <q-btn
              v-if="isEditMode && canBulkUpdateLineDueDates"
              label="SATIR TERMINLERINI TOPLU GUNCELLE"
              color="warning"
              icon="event"
              class="q-ml-sm"
              :loading="orderStore.loading"
              :disable="orderStore.loading || !canBulkUpdateLineDueDates"
              @click="openBulkDueDateDialog"
            />
            <q-btn
              v-if="canSubmitOrder"
              :label="isEditMode ? 'TÜMÜNÜ GÜNCELLE' : 'TÜMÜNÜ KAYDET'"
              color="primary"
              icon="save"
              class="q-ml-sm"
              :loading="orderStore.loading || isSubmitAllInFlight"
              :disable="!canSubmitOrder || orderStore.loading || isSubmitAllInFlight"
              @click="confirmAndSubmit"
            />
          </div>
        </div>

        <!-- 🔹 Grid Header -->
        <div class="order-grid-header" :class="{ compact: compactGridHeader }">
          <div class="col-fixed model">MODEL</div>
          <div class="col-fixed renk">RENK</div>
          <div class="col-fixed ana">ÜRÜN ANA GRUBU</div>
          <div class="col-fixed alt">ÜRÜN ALT GRUBU</div>
          <div class="col-fixed aciklama-col">AÇIKLAMA</div>

          <div class="beden-block">
            <div
              v-for="grp in (
                Object.keys(orderStore?.schemaMap || {}).length
                  ? Object.values(orderStore.schemaMap)
                  : Object.values(storeSchemaByKey)
              )"
              :key="grp.key"
              class="grp-row"
              :class="{ 'hl-pan': grp.key === 'pan' && highlightPantolon }"
            >

              <div class="grp-title">{{ grp.title }}</div>
              <div class="grp-body">
                <div
                  v-for="v in (grp.values || [])"
                  :key="'b-' + grp.key + '-' + v"
                  class="grp-cell hdr"
                >
                  {{ v }}
                </div>
              </div>
            </div>

          </div>

          <div class="total-row">
            <div class="total-cell">ADET</div>
            <div class="total-cell">FİYAT</div>
            <div class="total-cell">PB</div>
            <div class="total-cell">TUTAR</div>
            <div class="total-cell">Tahmini Gönderim Tarihi</div>
          </div>
        </div>
      </div>
      <!-- =======================================================
           🔹 GRID BODY (Final Stabil) + EDITOR aynı scroll’da
      ======================================================== -->
      <div class="order-scroll-y" :class="{ 'compact-grid-header': compactGridHeader }"> <!-- ✅ YENİ: Grid + Editor ortak dikey scroll -->
        <div class="order-grid-body">
          <template v-for="grp in groupedRows" :key="grp.groupKey">
            <div :class="['summary-group', grp.open ? 'open' : 'closed']">

              <!-- 🟡 Sub-header -->
              <div class="order-sub-header" @click="toggleGroup(grp.groupKey)">
                <div class="sub-left">{{ grp.displayName }}</div>

                <div class="sub-center">
                  <div
                    v-for="v in (
                          orderStore.schemaMap?.[grp.grpKey]?.values
                          || storeSchemaByKey?.[grp.grpKey]?.values
                          || []
                        )"
                    :key="'hdr-' + grp.grpKey + '-' + v"
                    class="beden-cell"
                  >
                    {{ v }}
                  </div>
                </div>

                <div class="sub-right">


                  <div class="order-text-caption">
                    Toplam {{ grp.displayName }} Adet: {{ grp.toplamAdet }}
                  </div>
                  <div class="order-text-caption">
                    Toplam {{ grp.displayName }} Tutar:
                    {{ Number(grp.toplamTutar || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) }}
                    {{ form.pb || aktifPB }}
                  </div>
                  <q-icon
                    :name="grp.open ? 'expand_less' : 'expand_more'"
                    size="20px"
                    class="cursor-pointer text-grey-8 q-ml-sm"
                  />
                </div>
              </div>

              <!-- 🧩 Grup satırları -->
              <template v-if="grp.open">
                <div
                  v-for="row in grp.rows"
                  :key="rowKey(row)"
                  class="summary-row"
                  :data-clientkey="row.clientKey"
                  :class="{
                      active: orderStore.editingKey === rowKey(row),
                      'is-editing': orderStore.editingKey === rowKey(row),
                      'row-closed': row.isClosed,
                      'row-error': row._error
                    }"
                  @click="!row.isClosed && !isViewOnly && editRow(row)"
                >

                  <!-- 🔴 HATA İKONU (SADECE HATALI SATIRDA) -->
                  <q-icon
                    v-if="row._error"
                    name="error"
                    color="negative"
                    size="18px"
                    class="q-mr-sm row-error-icon"
                  >
                    <q-tooltip>
                      {{ row._error.message }}
                    </q-tooltip>
                  </q-icon>

                  <!-- Sol kolonlar -->
                  <div class="cell model">{{ row.model }}</div>
                  <div class="cell renk">
                    {{ row.renk }}{{ row.renk2 ? '-' + row.renk2 : '' }}
                  </div>
                  <div class="cell ana">{{ row.urunAnaGrubu }}</div>
                  <div class="cell alt">{{ row.urunAltGrubu }}</div>
                  <div class="cell aciklama">{{ row.aciklama }}</div>

                  <!-- Beden kolonları -->
                  <div class="grp-area">
                    <div class="grp-row">
                      <div
                        v-for="v in (
                  (orderStore.schemaMap?.[row.grpKey]?.values) ||
                  (storeSchemaByKey[row.grpKey]?.values) ||
                  (storeSchemaByKey.tak.values)
                )"

                        :key="'val-' + v"
                        class="cell beden"
                      >
                        {{ resolveBedenValue(row.bedenMap, row.grpKey, v) }}
                      </div>

                      <div
                        v-for="i2 in Math.max(0, 16 - (
                    (orderStore.schemaMap?.[row.grpKey]?.values?.length) ||
                    (storeSchemaByKey[row.grpKey]?.values?.length) ||
                    (storeSchemaByKey.tak.values.length)
                  ))"

                        :key="'empty-' + i2"
                        class="cell beden ghost"
                      ></div>

                    </div>
                  </div>

                  <!-- Sağ kolonlar -->
                  <div class="cell adet">{{ row.adet }}</div>
                  <div class="cell fiyat">{{ row.fiyat }}</div>
                  <div class="cell pb">{{ row.pb }}</div>
                  <div class="cell tutar">
                    {{ Number(row.tutar || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) }}
                  </div>

                  <!-- 🗓 Termin Tarihi -->
                  <div class="cell termin">
                    <div class="termin-label text-center">
                      {{ formatDate(row.terminTarihi) }}
                    </div>
                  </div>
                </div>
              </template>
            </div>
          </template>
        </div>
        <!-- =======================================================
             🔹 TOPLU TERMIN GUNCELLEME
        ======================================================== -->
        <q-dialog v-model="showBulkDueDateDialog" persistent>
          <q-card style="min-width: 420px; max-width: 90vw;">
            <q-card-section class="text-subtitle1 text-weight-bold">
              Satir Terminlerini Toplu Guncelle
            </q-card-section>

            <q-card-section class="q-pt-none">
              <div class="q-mb-md">
                Tum siparis satiri terminlerini sectiginiz tarihi koyarak guncellemek istediginize emin misiniz?
              </div>
              <q-input
                v-model="bulkDueDateValue"
                type="date"
                label="Yeni Termin Tarihi"
                filled
                dense
                autofocus
              />
            </q-card-section>

            <q-card-actions align="right">
              <q-btn flat label="Iptal" v-close-popup />
              <q-btn
                color="primary"
                label="Evet"
                :loading="orderStore.loading"
                @click="confirmBulkDueDateUpdate"
              />
            </q-card-actions>
          </q-card>
        </q-dialog>

        <!-- =======================================================
             🔹 SATIR DÜZENLEYİCİ FORM (EDITOR)
        ======================================================== -->
        <q-dialog
          v-model="showEditor"
          class="order-editor-dialog"
          :maximized="$q.screen.lt.md"
          full-width
          transition-show="jump-down"
          transition-hide="jump-up"
          persistent
        >
          <q-card class="order-editor-card">
            <q-card-section class="row items-center justify-between">
              <div class="text-subtitle1 text-weight-bold">
                {{ isEditing ? 'Satır Düzenle' : 'Yeni Satır' }}
              </div>
              <q-btn flat round icon="close" @click="showEditor = false" />
            </q-card-section>
            <q-separator />
            <q-card-section class="editor q-pa-sm">

              <!-- 🔸 1. Satır: Model ve Ürün Bilgileri -->
              <div class="row q-col-gutter-sm q-mb-sm">
                <div class="col-3">
                  <!-- 🔹 Model Seçimi -->
                  <q-select
                    v-model="form.model"
                    :options="filteredModelOptions"
                    label="Model"
                    filled dense
                    use-input input-debounce="250"
                    emit-value map-options
                    option-value="value"
                    option-label="label"
                    clearable behavior="menu"
                    hint="Model kodu ile arayabilirsiniz"
                    :loading="loadingModels"
                    :disable=" isClosedRow||isViewOnly"
                    :readonly="isViewOnly"
                    @filter="filterModel"
                    @update:model-value="(val) => useComboWatcher('model', onModelChange)(val)"
                  />

                  <!-- 🔹 1. Renk Seçimi -->
                  <div class="q-mt-sm">
                    <q-select
                      ref="renkSelect"
                      v-model="form.renk"
                      :options="renkOptions"
                      label="Renk"
                      filled dense clearable
                      emit-value map-options
                      option-value="value"
                      option-label="label"
                      :disable="isClosedRow||isViewOnly"
                      :readonly="isViewOnly"
                      @update:model-value="(val) => useComboWatcher('renk', onColorChange)(val)"
                    />
                  </div>

                  <!-- 🔹 2. Renk Seçimi -->
                  <div class="q-mt-sm">
                    <q-select
                      ref="renk2Select"
                      v-model="form.renk2"
                      :options="renkOptions2"
                      label="2. Renk"
                      filled dense clearable
                      emit-value map-options
                      option-value="value"
                      option-label="label"
                      :disable="!renkOptions2.length || isEditing || isClosedRow"
                      @update:model-value="(val) => useComboWatcher('renk2', onColor2Change)(val)"
                    />
                  </div>
                </div>

                <!-- Ürün teknik alanları -->
                <div class="col-2">
                  <q-input v-model="form.urunAnaGrubu" label="Ürün Ana Grubu" filled dense readonly />
                </div>
                <div class="col-1">
                  <q-input v-model="form.urunAltGrubu" label="Alt Grup" filled dense readonly />
                </div>
                <div class="col-1">
                  <q-input v-model="form.fit" label="Fit" filled dense readonly />
                </div>
                <div class="col-2">
                  <q-input v-model="form.urunIcerik" label="İçerik" filled dense readonly />
                </div>
                <div class="col-1">
                  <q-input v-model="form.drop" label="Drop" filled dense readonly />
                </div>
                <div class="col-1">
                  <q-input v-model="form.askiliyan" label="ASKILI/YAN" filled dense readonly />
                </div>
                <div class="col-1">
                  <q-input v-model="form.kategori" label="Kategori" filled dense readonly />
                </div>
              </div>

              <!-- 🔸 2. Satır: Seri Seçimi -->
              <div class="row q-col-gutter-sm q-mt-xs">
                <div class="col-3">
                  <q-select
                    ref="seriSelect"
                    v-show="Array.isArray(activeSeriesOptions) && activeSeriesOptions.length > 0"
                    v-model="selectedSeriSet"
                    :options="activeSeriesOptions"
                    label="Beden Seti Seç"
                    filled dense
                    emit-value map-options
                    option-value="value"
                    option-label="label"
                    :disable="isClosedRow"
                  />
                </div>

                <div class="col-2 q-mt-sm">
                  <q-input
                    v-if="selectedSeriSet"
                    v-model.number="seriMultiplier"
                    type="number"
                    label="Çarpan"
                    min="1"
                    filled dense
                    :disable="isClosedRow"
                  />
                </div>

                <div class="col-2 q-mt-sm">
                  <q-btn
                    v-if="selectedSeriSet && canMutateRows"
                    color="primary"
                    icon="add"
                    label="Seri Ekle"
                    @click="applySeriSet"
                    :disable="isClosedRow || isViewOnly || !canMutateRows"
                    :readonly="isViewOnly"
                  />
                </div>

              </div>

              <!-- =======================================================
                   🔹 BEDEN GİRİŞ ALANI + STOK ETİKETİ GÖRÜNÜMÜ
              ======================================================== -->
              <div class="row q-mt-sm q-col-gutter-xs beden-grid">
                <div
                  v-for="(lbl, i) in form.bedenLabels || []"
                  :key="'beden-'+i"
                  class="col-auto beden-wrap"
                >
                  <div class="beden-label">{{ lbl }}</div>

                  <q-input
                    v-model.number="form.bedenler[i]"
                    dense outlined type="number" min="0"
                    style="width:60px"
                    @focus="activeBeden = i"
                    @blur="activeBeden = null"
                    @update:model-value="updateTotals(form)"
                    :class="{ 'beden-active': activeBeden === i }"
                    :disable="isClosedRow||isViewOnly"
                    :readonly="isViewOnly"
                  />

                  <div
                    v-if="getStockFor(lbl) !== null"
                    class="stok-label text-caption text-center q-mt-xs"
                    :class="stockColorClass(getStockFor(lbl))"
                  >
                    Stok: {{ getStockFor(lbl) }}
                  </div>
                </div>
              </div>
              <!-- 🔹 Aktif beden için küçük stok etiketi -->
              <div
                v-if="form.model && activeBeden !== null && getStockFor(form.bedenLabels[activeBeden]) !== null"
                class="stok-label-sm"
                :class="stockColorClass(getStockFor(form.bedenLabels[activeBeden]))"
              >
                Stok: {{ getStockFor(form.bedenLabels[activeBeden]) }}
              </div>

              <!-- =======================================================
                   🔹 ADET / FİYAT / PB / TUTAR
              ======================================================== -->
              <div class="row q-mt-sm q-col-gutter-sm">
                <div class="col-2">
                  <q-input
                    v-model.number="form.adet"
                    label="Adet"
                    dense
                    filled
                    readonly
                    :disable="isClosedRow"
                  />
                </div>
                <div class="col-2">
                  <q-input
                    v-model.number="form.fiyat"
                    label="Fiyat"
                    dense
                    filled
                    type="number"
                    min="0"
                    @update:model-value="() => updateTotals(form)"
                    :disable="isClosedRow||isViewOnly"
                    :readonly="isViewOnly"
                  />
                </div>
                <div class="col-2">
                  <q-select
                    v-model="form.pb"
                    :options="paraBirimOptions"
                    label="PB"
                    dense
                    filled
                    :disable="isClosedRow"
                  />
                </div>
                <div class="col-3">
                  <q-input
                    v-model="form.tutar"
                    label="Tutar"
                    dense
                    filled
                    readonly
                    :disable="isClosedRow"
                  />
                </div>
              </div>

              <!-- =======================================================
                   🔹 SATIR BAZINDA TAHMİNİ TERMİN TARİHİ
              ======================================================== -->
              <div class="row q-mt-sm">
                <div class="col-4">
                  <q-input
                    v-model="form.terminTarihi"
                    type="date"
                    label="Tahmini Termin Tarihi"
                    filled
                    dense
                    :disable="isClosedRow"
                  />
                </div>
              </div>

              <!-- =======================================================
                   🔹 AÇIKLAMA ALANI
              ======================================================== -->
              <div class="row q-mt-sm">
                <div class="col-12">
                  <q-input
                    v-model="form.aciklama"
                    label="Açıklama"
                    type="textarea"
                    filled
                    dense
                    autogrow
                    maxlength="1500"
                    counter
                    :disable="isClosedRow"
                  />
                </div>
              </div>
              <!-- =======================================================
                   🔹 BUTONLAR (Kaydet / Güncelle / Sil / Temizle)
              ======================================================== -->
              <div class="row justify-between items-center q-mt-md">
                <div class="row q-gutter-sm">
                  <q-btn
                    v-if="canMutateRows"
                    :color="isEditing ? 'positive' : 'primary'"
                    :label="isEditing ? 'Güncelle' : 'Kaydet'"
                    :loading="isRowSaveInFlight"
                    @click="onSaveOrUpdateRow"
                    :disable="isClosedRow || isViewOnly || !canMutateRows || isRowSaveInFlight"

                  />
                  <q-btn
                    v-if="canMutateRows"
                    color="secondary"
                    label="Kaydet ve Diğer Renge Geç"
                    :loading="isRowSaveInFlight"
                    @click="onSaveAndNextColor"
                    :disable="isClosedRow || isViewOnly || !canMutateRows || isRowSaveInFlight"
                  />
                  <q-btn
                    v-if="isEditing && canMutateRows"
                    color="negative"
                    flat
                    label="Satırı Sil"
                    @click="removeSelected"
                    :disable="isClosedRow || isViewOnly || !canMutateRows"
                  />
                  <q-btn
                    v-if="canMutateRows"
                    flat
                    color="warning"
                    label="Bedenleri Sıfırla"
                    @click="onZeroBedenClick"
                    :disable="isClosedRow || isViewOnly || !canMutateRows"
                  />
                  <q-btn
                    v-if="canMutateRows"
                    flat
                    color="grey-8"
                    label="Formu Temizle"
                    @click="onResetEditorClick"
                    :disable="isClosedRow||isViewOnly || !canMutateRows"

                  />
                </div>
              </div>

              <!-- =======================================================
                   🔹 ALT BİLGİLENDİRME ALANI
              ======================================================== -->
              <div class="q-mt-md text-caption text-grey-7 text-center">
                <q-icon name="info" size="16px" class="q-mr-xs" />
                Bu sayfada yapılan siparişler henüz gönderilmemiştir.
                <br />
                <span class="text-negative">"Tümünü Kaydet (Toplu Gönder)"</span>
                butonuna basarak işlemleri kaydedebilirsiniz.
              </div>

            </q-card-section>
          </q-card>
        </q-dialog>
      </div> <!-- ✅ order-scroll-y -->
    </template>

  </q-page>
  <q-page
    v-else
    class="order-page flex flex-center"
  >
    <div class="text-negative text-subtitle1">
      Bu module erisim yetkiniz yok.
    </div>
  </q-page>
</template>

<script setup>
/* ===========================================================
   🧩 ORDER ENTRY (v22–Final) — Setup Başlangıcı
   =========================================================== */

import { ref, reactive, computed, watch, onMounted, onUnmounted, nextTick, toRaw } from 'vue'
import { useQuasar } from 'quasar'
import { useRoute, useRouter, onBeforeRouteLeave} from 'vue-router'
import { useOrderEntryStore,schemaByKey as storeSchemaByKey,detectBedenGroup,normalizeBedenLabel} from 'src/stores/orderentryStore'
import dayjs from 'dayjs'
import api from 'src/services/api.js'
import { useAuthStore } from 'src/stores/authStore'
import { formatDateInput, formatDateDisplay } from 'src/utils/formatters'
import { normalizeSearchText } from 'src/utils/searchText'
import { usePermission } from 'src/composables/usePermission'

const { canRead, canWrite, canUpdate, canExport } = usePermission()

const canReadOrder   = canRead('order')
const canWriteOrder  = canWrite('order')
const canUpdateOrder = canUpdate('order')
const canExportOrder = canExport('order')

// script setup içinde
const formatDate = formatDateDisplay


const showEditor = ref(false)
const compactGridHeader = ref(false)
const filterBarCollapsed = ref(false)

function toggleFilterBarCollapsed () {
  filterBarCollapsed.value = !filterBarCollapsed.value
  nextTick(() => {
    updateStickyVars()
    measureHeaderGap()
  })
}



/* ===========================================================
   🔹 GLOBAL TANIMLAR VE ROUTE BİLGİLERİ
   =========================================================== */
const $q = useQuasar()
const orderStore = useOrderEntryStore()
const orderentryStore = useOrderEntryStore()
orderStore.initSchemaMap()
void orderStore.ensureProductSizeMatchRules()

const schemaSource = computed(() =>
  Object.keys(orderStore?.schemaMap || {}).length
    ? orderStore.schemaMap
    : storeSchemaByKey
)

const gridHeaderHeight = computed(() => {
  const count = Object.keys(schemaSource.value || {}).length
  const bedenH = 28
  return Math.max(1, count) * bedenH + 4
})


const route = useRoute()
const router = useRouter()
const isClosedOrder = computed(() => !!orderStore.hasClosedLines)

// 🔹 Param: Artık sadece :orderHeaderID kullanıyoruz
const orderHeaderID = computed(() => route.params.orderHeaderID || null)
console.log('🧩 Route parametresi alındı (orderHeaderID):', orderHeaderID.value)



const routeMode = computed(() => resolveMode())



// ✅ Pinia store: siparişler, localStorage, API çağrıları
const auth = useAuthStore()
const isViewOnly = computed(() => orderStore.mode === 'view')


console.log('🧩 Route parametresi alındı (setup başında):', orderHeaderID.value)

// 🔹 Genel reaktif değişkenler
const aktifPB = ref('USD')                 // Varsayılan para birimi (Cari seçimiyle değişebilir)
// 🔹 Model detayları cache (product-detail API verilerini tutar)
const productCache = reactive({})
const showBulkDueDateDialog = ref(false)
const bulkDueDateValue = ref('')
const isSubmitAllInFlight = ref(false)
const isRowSaveInFlight = ref(false)

function showEditorQtyPriceBlockingDialog(message, details = '') {
  const detailHtml = details ? `<br><br><b>Detay:</b><br>${details}` : ''
  $q.dialog({
    title: 'Kayit Engellendi',
    message: `${message}${detailHtml}`,
    html: true,
    ok: { label: 'Tamam', color: 'negative' }
  })
}

function validateEditorRowBeforeSave() {
  const adet = Number(form.adet || 0)
  const fiyatRaw = String(form.fiyat ?? '').trim()
  const fiyat = Number(form.fiyat || 0)

  if (adet <= 0) {
    showEditorQtyPriceBlockingDialog('Siparis adeti toplam 0 olamaz.')
    return false
  }
  if (!fiyatRaw || !Number.isFinite(fiyat) || fiyat <= 0) {
    showEditorQtyPriceBlockingDialog('Urun fiyati girmeden ilerleyemezsiniz.')
    return false
  }
  return true
}

function validateSummaryRowsBeforeSubmit() {
  const rows = Array.isArray(orderStore.summaryRows) ? orderStore.summaryRows : []
  const invalidRows = rows.filter(r => {
    const adet = Number(r?.adet || 0)
    const fiyatRaw = String(r?.fiyat ?? '').trim()
    const fiyat = Number(r?.fiyat || 0)
    return adet <= 0 || !fiyatRaw || !Number.isFinite(fiyat) || fiyat <= 0
  })

  if (!invalidRows.length) return true

  const preview = invalidRows
    .slice(0, 8)
    .map(r => `${String(r?.model || '').trim() || '-'} / ${String(r?.renk || '').trim() || '-'} (adet=${Number(r?.adet || 0)}, fiyat=${String(r?.fiyat ?? '')})`)
    .join('<br>')

  showEditorQtyPriceBlockingDialog(
    'Urun fiyati girmeden ilerleyemezsiniz.',
    preview
  )
  return false
}

const confirmAndSubmit = async () => {
  if (orderStore.loading || isSubmitAllInFlight.value) return

  if (!hasSubmitPermission()) {
    notifyNoPermission(
      isEditMode.value
        ? 'Siparis guncelleme yetkiniz yok'
        : 'Siparis kaydetme yetkiniz yok'
    )
    return
  }

  // Grid boşsa
  if (!orderStore.summaryRows?.length) {
    $q.notify({
      type: 'warning',
      message: 'Kaydedilecek satır yok'
    })
    return
  }

  if (!validateSummaryRowsBeforeSubmit()) {
    return
  }

  isSubmitAllInFlight.value = true
  try {
    // NEW veya EDIT ayrımı store.mode üzerinden
    await orderStore.submitAllReal(
      $q,
      router,
      form,
      summaryRows,
      productCache
    )
  } catch (err) {
    console.error('❌ confirmAndSubmit hata:', err)
  } finally {
    isSubmitAllInFlight.value = false
  }
}

function openBulkDueDateDialog() {
  if (!canBulkUpdateLineDueDates.value) return

  const firstRowDate = summaryRows.value?.find?.(row => !!row?.terminTarihi)?.terminTarihi || ''
  bulkDueDateValue.value = toDateOnly(form.AverageDueDate || firstRowDate || dayjs().format('YYYY-MM-DD'))
  showBulkDueDateDialog.value = true
}

async function confirmBulkDueDateUpdate() {
  const dueDate = toDateOnly(bulkDueDateValue.value)
  if (!dueDate) {
    $q.notify({
      type: 'warning',
      message: 'Lutfen bir termin tarihi seciniz.'
    })
    return
  }

  try {
    const result = await orderStore.bulkUpdateOrderLineDueDate(orderHeaderID.value, dueDate)
    orderStore.applyBulkLineDueDateLocally(dueDate)
    form.AverageDueDate = dueDate
    showBulkDueDateDialog.value = false

    $q.notify({
      type: 'positive',
      message: `Tum siparis satiri terminleri guncellendi (${Number(result?.updatedLines || 0)} satir).`
    })
  } catch (err) {
    console.error('❌ confirmBulkDueDateUpdate hata:', err)
    $q.notify({
      type: 'negative',
      message: err?.message || 'Satir terminleri guncellenemedi.'
    })
  }
}


/* ===========================================================
   🗓️ SİPARİŞ TARİHLERİ — Varsayılan Değerler
   Oluşturulma tarihi = bugünün tarihi
   Tahmini termin tarihi = bugünden + 5 hafta (35 gün)
=========================================================== */
const today = new Date()
const Termindate = new Date(today)
Termindate.setDate(Termindate.getDate() + 35) // +5 hafta
const defaultOlusturmaTarihi = today.toISOString().substring(0, 10)
const defaultTerminTarihi = Termindate.toISOString().substring(0, 10)

const isEditMode = computed(() => orderStore.mode === 'edit')
const canSubmitOrder = computed(() => {
  if (isViewOnly.value) return false
  return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
})
const canMutateRows = computed(() => {
  if (isViewOnly.value) return false
  return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
})
const canBulkUpdateLineDueDates = computed(() => {
  if (!isEditMode.value) return false
  if (isViewOnly.value) return false
  if (isClosedOrder.value) return false
  if (!canUpdateOrder.value) return false
  if (!orderHeaderID.value) return false
  return Array.isArray(orderStore.summaryRows) && orderStore.summaryRows.length > 0
})

function notifyNoPermission(message) {
  $q.notify({
    type: 'negative',
    message
  })
}

function hasSubmitPermission() {
  if (isViewOnly.value) return false
  return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
}

function hasRowMutationPermission() {
  if (isViewOnly.value) return false
  return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
}

function onPrintOrder() {
  if (!canExportOrder.value) {
    notifyNoPermission('Siparisi yazdirma yetkiniz yok')
    return
  }
  orderStore.downloadOrderPdf()
}

async function onResetEditorClick() {
  if (!canWriteOrder.value) {
    notifyNoPermission('Yeni siparis baslatma yetkiniz yok')
    return
  }
  await resetEditor()
}

/* ===========================================================
   🔹 FORM NESNESİ — TEMEL ALANLAR
   =========================================================== */
const form = reactive({
  // ----------------------------------------------------------
  // 🔸 TEMEL ALANLAR
  // ----------------------------------------------------------
  OrderHeaderID: '',                     // string (GUID)
  OrderTypeCode: 1,                      // int8
  ProcessCode: 'WS',                     // string
  OrderNumber: '',                       // string
  OrderTime: dayjs().format('HH:mm:ss'),
  IsCancelOrder: false,

  // ----------------------------------------------------------
  // 🔸 ADRES / REFERANS
  // ----------------------------------------------------------
  BillingPostalAddressID: '',
  GuarantorContactID: '',
  ApplicationCode: '',
  ApplicationID: '',

  // ----------------------------------------------------------
  // 🔸 TARİH / AÇIKLAMA
  // ----------------------------------------------------------
  OrderDate: dayjs().format('YYYY-MM-DD'),
  AverageDueDate: dayjs().add(30, 'day').format('YYYY-MM-DD'),
  Description: '',
  InternalDescription: '',

  // ----------------------------------------------------------
  // 🔸 CARİ BİLGİLERİ
  // ----------------------------------------------------------
  CurrAccTypeCode: 1,

  CurrAccCode: '',
  CurrAccDescription: '',

  // ----------------------------------------------------------
  // 🔸 PARA BİRİMİ
  // ----------------------------------------------------------
  DocCurrencyCode: 'USD',
  LocalCurrencyCode: 'TRY',
  ExchangeRate: 1,

  // ----------------------------------------------------------
  // 🔸 DURUM ALANLARI
  // ----------------------------------------------------------
  IsCreditSale: true,
  IsCreditableConfirmed: false,
  IsSalesViaInternet: false,
  IsSuspended: false,
  IsCompleted: true,
  IsPrinted: false,
  IsLocked: false,
  IsClosed: false,

  // ----------------------------------------------------------
  // 🔸 KULLANICI VE TARİH
  // ----------------------------------------------------------
  CreatedUserName: '',
  CreatedDate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  LastUpdatedUserName: '',
  LastUpdatedDate: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  CreditableConfirmedUser: '',
  CreditableConfirmedDate: '',

  // ----------------------------------------------------------
  // 🔸 SABİT / EK ALANLAR
  // ----------------------------------------------------------
  DocumentNumber: '',
  PaymentTerm: '',
  SubCurrAccID: '',
  ShipmentMethodCode: '',
  ContactID: '',
  ShippingPostalAddressID: '',
  GuarantorContactID2: '',
  RoundsmanCode: '',
  DeliveryCompanyCode: '',
  TaxTypeCode: '',
  WithHoldingTaxTypeCode: '',
  DOVCode: '',
  TaxExemptionCode: 0,
  CompanyCode: 1,
  OfficeCode: 101,
  StoreTypeCode: 5,
  StoreCode: 0,
  POSTerminalID: 0,
  WarehouseCode: '1-0-12',
  ToWarehouseCode: '',
  OrdererCompanyCode: 1,
  OrdererOfficeCode: 101,
  OrdererStoreCode: '',
  GLTypeCode: '',
  TDisRate1: 0,
  TDisRate2: 0,
  TDisRate3: 0,
  TDisRate4: 0,
  TDisRate5: 0,
  DiscountReasonCode: 0,
  SurplusOrderQtyToleranceRate: 0,
  ImportFileNumber: '',
  ExportFileNumber: '',
  IncotermCode1: '',
  IncotermCode2: '',
  LettersOfCreditNumber: '',
  PaymentMethodCode: '',
  IsIncludedVat: 0,
  UserLocked: 0,
  IsProposalBased: 0,

  // ----------------------------------------------------------
  // 🔸 ÜRÜN / RENK / BEDEN
  // ----------------------------------------------------------
  model: '',
  renk: '',
  renk2: '',
  urunAnaGrubu: '',
  urunAltGrubu: '',
  fit: '',
  urunIcerik: '',
  drop: '',
  kategori: '',
  askiliyan: '',
  yetiskinGarson: '',
  yasPayloadMap: {},
  seri: '',
  bedenLabels: [],
  bedenler: [],
  adet: 0,
  fiyat: 0,
  pb: aktifPB.value,
  tutar: 0,
  aciklama: '',
  minFiyat: 0,
  kur: 1,
  minFiyatTRY: 0,

  // ----------------------------------------------------------
  // 🔸 TARİHLER
  // ----------------------------------------------------------
  olusturmaTarihi: defaultOlusturmaTarihi,
  tahminiTerminTarihi: defaultTerminTarihi,
  terminTarihi: defaultTerminTarihi,
  includeVat: false,   // 🔹 KDV dahil mi (q-toggle)
  vatRate: 10,          // 🔹 yüzde (gösterge + manuel değiştirilebilir)
  subtotal: 0,          // 🔹 KDV hariç tutar
  vatAmount: 0,         // 🔹 hesaplanan KDV tutarı
  totalWithVat: 0  ,     // 🔹 toplam (KDV dahil)
  vatAmountInput: '',   // 🟢 KDV manuel giriş buffer

})


/* ===========================================================
   🔹 REFS & COMPUTEDS (Grid + Edit Mode)
   =========================================================== */
const summaryRows = computed(() => orderStore.summaryRows)
// ✅ Tek kaynak: editingKey
const isEditing = computed(() => !!orderStore.editingKey)
const rowKey = (row) => row?.clientKey || row?.id || row?.OrderLineID






const activeBeden = ref(null)
const isClosedRow = computed(() => {
  const row = selectedRow.value
  return row?.isClosed === true
})


const selectedCari = ref(null)

/* ===========================================================
   🔹 EDITOR SEÇİMLERİ — Seri / Renk / Kategori setleri
   =========================================================== */
const selectedSeriSet = ref(null)
const seriMultiplier = ref(1)
/** ----------------------------------------------------------
 *  REQUIRED REFS (Mutlaka Tanımlı Olmalı)
 * ---------------------------------------------------------- */

/* ===========================================================
   🔹 CARI / MODEL / RENK YÜKLEYİCİ REFLERİ — DOĞRU SIRA
   =========================================================== */
const loadingHeader = ref(true)
const loadingCari = ref(true)
const loadingModels = ref(true)
const isPageBlocking = computed(() => {
  if (!isEditMode.value) return false
  const headerReady = !!orderStore.header
  return (
    loadingHeader.value ||
    loadingCari.value ||
    loadingModels.value ||
    !headerReady
  )
})
/* ===========================================================
   🔹 CARİ INFO STATE
=========================================================== */
const cariInfo = ref(null)

// Cari listeleri
const cariOptions = ref([])
const filteredCariOptions = ref([])


// Model listeleri
const modelOptions = ref([])
const filteredModelOptions = ref([])


// RENK SELECTLER — 🔥 MUTLAKA BURADA OLMALI
const renkSelect = ref(null)     // <= BUNLAR EKSİKSE hata alırsın
const renk2Select = ref(null)

const renkOptions = ref([])
const renkOptions2 = ref([])

// Mode senkronu
orderStore.mode = routeMode.value
function resolveBedenValue(bedenMap, grpKey, v) {
  if (!bedenMap) return ''

  const detectedGrpKey = Object.keys(bedenMap || {})
    .find(k => bedenMap?.[k] && typeof bedenMap[k] === 'object')
  const effectiveGrpKey =
    (grpKey != null && String(grpKey) !== '')
      ? String(grpKey)
      : (detectedGrpKey !== undefined ? detectedGrpKey : '')

  const map =
    (Object.prototype.hasOwnProperty.call(bedenMap, effectiveGrpKey) &&
      bedenMap[effectiveGrpKey] &&
      typeof bedenMap[effectiveGrpKey] === 'object')
      ? bedenMap[effectiveGrpKey]
      : bedenMap
  if (!map) return ''

  // 🔴 AKSBİR / boş beden KESİNLİKLE normalize edilmez
  if (v === ' ') {
    return map[' '] ?? ''
  }

  // 🔹 Diğer bedenler normal akış
  return map[v] ?? map[normalizeBedenLabel(String(v))] ?? ''
}


async function resetEditor(force = false) {
  console.log('🧹 resetEditor', { force, editingKey: orderStore.editingKey })

  // 🔒 Edit varken reset yok
  if (!force && orderStore.editingKey) {
    console.log('⛔ resetEditor iptal (edit mode)')
    return
  }

  // ============================
  // 🔓 EDIT STATE RESET
  // ============================
  orderStore.editingKey = null
  orderStore.selected = null

  // ============================
  // 🧼 FORM — TAM TEMİZ
  // ============================
  Object.assign(form, {
    model: '',
    renk: '',
    renk2: '',
    urunAnaGrubu: '',
    urunAltGrubu: '',
    kategori: '',
    aciklama: '',
    fit: '',
    urunIcerik: '',
    drop: '',
    askiliyan: '',
    yetiskinGarson: '',
    yasPayloadMap: {},

    adet: 0,
    fiyat: 0,
    tutar: 0,

    // ❌ BEDEN ŞEMASI TAMAMEN SIFIR
    grpKey: null,
    bedenLabels: [],
    bedenler: []
  })

  // ============================
  // 🧹 UI STATE TEMİZLİĞİ
  // ============================
  selectedSeriSet.value = null
  seriMultiplier.value = 1

  bedenStock.value = []
  stockMap.value = {}

  renkOptions.value = []
  renkOptions2.value = []

  await nextTick()

  console.log('✅ resetEditor tamamlandı (BEDEN ŞEMASI YOK)')
}


/* ===========================================================
   🔴 ROW ERROR — COMPONENT SCOPE (FIXED)
   - summaryRows computed yazımı yok
   - tek kaynak: store action
=========================================================== */
function applyRowError(err) {
  const key = err?.clientKey
  if (!key) return

  // ✅ store üzerinden set et (tek kaynak)
  if (typeof orderStore.setRowErrorByClientKey === 'function') {
    orderStore.setRowErrorByClientKey(key, {
      code: err?.code,
      message: err?.message
    })
  } else {
    // fallback (action yoksa) — yine de computed'a yazmıyoruz, store array mutate ediyoruz
    const row = orderStore.summaryRows?.find(r => r?.clientKey === key)
    if (row) {
      row._error = { code: err?.code, message: err?.message }
    }
  }

  scrollToRow(key)
}

/* ===========================================================
   🔹 applyTerminToRows (FIXED)
   - store.summaryRows reassign YOK
   - tek kaynak: store action
=========================================================== */
function applyTerminToRows(dateStr) {
  if (!dateStr) return

  if (typeof orderStore.applyTerminToRowsIfEmpty === 'function') {
    orderStore.applyTerminToRowsIfEmpty(dateStr)
    return
  }

  // fallback (action yoksa) — reassign yok, sadece mutate
  const rows = orderStore.summaryRows
  if (!Array.isArray(rows)) return
  for (const r of rows) {
    if (!r?.terminTarihi || r.terminTarihi === '') {
      r.terminTarihi = dateStr
    }
  }
}

/* ===========================================================
   ✅ selectedRow (FIXED)
   - editingIndex yok
   - tek kaynak: orderStore.editingKey
=========================================================== */
const selectedRow = computed(() => {
  const key = orderStore.editingKey
  if (!key) return null

  const rows = orderStore.summaryRows
  if (!Array.isArray(rows)) return null

  // 🔑 store.getRowKey varsa onu kullan
  if (typeof orderStore.getRowKey === 'function') {
    return rows.find(r => orderStore.getRowKey(r) === key) || null
  }

  // fallback: clientKey || OrderLineID
  return rows.find(r => (r?.clientKey || r?.OrderLineID) === key) || null
})

/* ===========================================================
   🧩 GROUPED ROWS — FIXED (bedenValues bug fixed)
   - schemaMap tek kaynak
   - en geniş beden seti kazanır
   - aksbir özel kural korunur
=========================================================== */
/* ===========================================================
   🧩 GROUPED ROWS — FINAL & SAFE
   -----------------------------------------------------------
   ✔ grpKey SADECE row.grpKey
   ✔ schemaMap tek kaynak
   ✔ detectBedenGroup YOK
=========================================================== */
const groupOpen = reactive({})

function normalizeSubHeaderText (v, fallback = 'GENEL') {
  const s = String(v || '')
    .replace(/\([^)]*\)/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
  return s || fallback
}

function resolveKategoriForSubHeader (row) {
  return normalizeSubHeaderText(
    row?.kategori ||
      row?.Kategori ||
      row?.ProductAtt44Desc ||
      row?.YETISKIN_GARSON ||
      row?.yetiskinGarson ||
      row?.YetiskinGarson ||
      '',
    'GENEL'
  )
}

function resolveUrunAnaGrubuForSubHeader (row) {
  return normalizeSubHeaderText(
    row?.urunAnaGrubu ||
      row?.UrunAnaGrubu ||
      row?.ProductAtt01Desc ||
      '',
    'GENEL'
  ).toUpperCase()
}

const groupedRows = computed(() => {
  const rows = Array.isArray(summaryRows.value) ? summaryRows.value : []
  const buckets = {}
  const order = []

  const schemaMap =
    orderStore.schemaMap && typeof orderStore.schemaMap === 'object'
      ? orderStore.schemaMap
      : storeSchemaByKey

  for (const row of rows) {
    const ana = resolveUrunAnaGrubuForSubHeader(row)
    const kategori = resolveKategoriForSubHeader(row)
    const grpKey = String(row?.grpKey || 'tak').trim() || 'tak'
    const bucketKey = `${kategori}::${ana}`

    if (!buckets[bucketKey]) {
      buckets[bucketKey] = {
        name: ana,
        kategori,
        displayName: `${kategori} ${ana}`,
        rows: [],
        toplamAdet: 0,
        toplamTutar: 0,
        open: groupOpen[bucketKey] ?? true,

        // 🔑 TEK KAYNAK
        grpKey
      }
      order.push(bucketKey)
    }

    const bucket = buckets[bucketKey]
    bucket.rows.push(row)
    bucket.toplamAdet += Number(row.adet || 0)
    bucket.toplamTutar += Number(row.tutar || 0)
  }

  return order.map(bucketKey => {
    const grp = buckets[bucketKey]
    const schema = schemaMap?.[grp.grpKey]
    const displayName = `${grp.kategori} ${grp.name}`.trim()

    return {
      ...grp,
      displayName,
      groupKey: bucketKey,
      bedenValues: schema?.values || []
    }
  })
})

function toggleGroup(name) {
  if (!name) return
  groupOpen[name] = !(groupOpen[name] ?? true)
}







/* ===========================================================
   ✏️ GRID SATIR DÜZENLEME — editRow (Final v4 — IsClosed Safe)
   -----------------------------------------------------------
   - Kapalı satır (row.isClosed === true) → düzenlemeye izin yok
   - UI’da row.isClosed class ile gri görünür
   - Kullanıcı tıklasa bile edit mode açılmaz
=========================================================== */
function toDateOnly(v) {
  if (!v) return ''
  // '2025-10-27 00:00:00' → '2025-10-27'
  if (typeof v === 'string' && v.includes(' ')) {
    return v.split(' ')[0]
  }
  return v
}






// 🧮 KDV Hesaplama Fonksiyonları

/* ===========================================================
   🧮 KDV HESAPLAMA — FINAL (X3)
   - Tek kaynak: orderStore.totalAmount
   - Tek hesap fonksiyonu: recalcVat
=========================================================== */

const subtotal = computed(() => Number(orderStore.totalAmount || 0))
/* ===========================================================
   🔹 KDV TOGGLE HANDLER (SAFE)
=========================================================== */
const onVatToggle = (val) => {
  form.includeVat = !!val
  recalcVat()
}
/* ===========================================================
   🧮 recalcVat — FINAL
   - Tek kaynak: orderStore.totalAmount
   - Manuel KDV girişini destekler
=========================================================== */
function recalcVat() {
  const baseTotal = Number(orderStore.totalAmount || 0)
  const rate = Number(form.vatRate || 0) / 100

  // 🔹 KDV dahil değilse
  if (!form.includeVat) {
    form.subtotal = baseTotal
    form.vatAmount = 0
    form.vatAmountInput = ''
    form.totalWithVat = baseTotal
    return
  }

  /* ---------------------------------------------------------
     🔹 MANUEL KDV VAR MI?
  --------------------------------------------------------- */
  let vatAmt = 0

  if (form.vatAmountInput !== '' && form.vatAmountInput != null) {
    vatAmt = Number(
      String(form.vatAmountInput).replace(',', '.')
    )
    vatAmt = isNaN(vatAmt) ? 0 : vatAmt
  } else {
    vatAmt = Number((baseTotal * rate).toFixed(2))
  }

  const totalWithVat = Number((baseTotal + vatAmt).toFixed(2))

  form.subtotal = baseTotal
  form.vatAmount = vatAmt
  form.totalWithVat = totalWithVat
}


// Kullanıcı KDV tutarını manuel girdiyse true
const vatManualMode = ref(false)

/* -----------------------------------------------------------
   Yardımcılar
----------------------------------------------------------- */
function toNumberTR(val) {
  const cleaned = String(val ?? '').replace(',', '.').trim()
  const n = parseFloat(cleaned)
  return isNaN(n) ? 0 : n
}

function clampRate(val) {
  const n = Number(val)
  if (isNaN(n) || n < 0) return 0
  if (n > 100) return 100
  return n
}

/* ===========================================================
   ✅ ROUTE FLOW — SINGLE SOURCE OF TRUTH (FINAL)
=========================================================== */

/* -------------------- MODE HELPERS -------------------- */
function isInvalidId(id) {
  return !id || ["new", "0", "null", "undefined"].includes(id)
}

function isGuid(id) {
  return typeof id === "string" &&
    /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i.test(id)
}

function resolveMode() {
  const qm = String(route.query.mode || "").toLowerCase()
  const id = String(orderHeaderID.value || "")
  if (["edit", "view", "new"].includes(qm)) return qm
  if (!isInvalidId(id) && isGuid(id)) return "edit"
  return "new"
}

/* -------------------- INTERNAL STATE -------------------- */
const routeBusy = ref(false)
const lastRouteSignature = ref("")
let autosaveTimer = null
let beforeUnloadHandler = null
let resizeHandler = null
/* ===========================================================
   🚫 BEFOREUNLOAD (Browser Close / Refresh)
   -----------------------------------------------------------
   ✔ NEW  → taslak KORUNUR, uyarı var
   ✔ EDIT → snapshot KORUNUR, uyarı var
   ✔ VIEW → ASLA uyarı yok
   ✔ isControlledSubmit → ASLA uyarı yok
   ✔ allowRouteLeaveOnce → ASLA uyarı yok
   ❌ burada snapshot SİLİNMEZ (kritik)
=========================================================== */

function installBeforeUnloadGuard() {
  clearBeforeUnload()

  // 👁 VIEW MODE → hiç guard kurma
  if (orderStore.mode === 'view') return

  beforeUnloadHandler = (e) => {

    /* -------------------------------------------------------
       🔒 Kontrollü submit (kaydet / gönder)
       → browser close uyarısı YOK
    -------------------------------------------------------- */
    if (orderStore.isControlledSubmit) return

    /* -------------------------------------------------------
       ✅ Programatik yönlendirme sonrası 1 kere bypass
       (listeye dön, replace vs.)
    -------------------------------------------------------- */
    if (orderStore.allowRouteLeaveOnce) return

    /* -------------------------------------------------------
       💾 Değişiklik yok → sessiz çık
    -------------------------------------------------------- */
    if (!orderStore.hasUnsavedChanges) return

    /* -------------------------------------------------------
       ⚠️ UYARI (NEW + EDIT için ortak)
       ❗ Snapshot SİLME YOK
       ❗ LocalStorage'a DOKUNMA YOK
    -------------------------------------------------------- */
    e.preventDefault()
    e.returnValue = ''
  }

  window.addEventListener('beforeunload', beforeUnloadHandler)
}

/* -------------------- CLEANUP -------------------- */
function clearAutosave() {
  if (autosaveTimer) {
    clearInterval(autosaveTimer)
    autosaveTimer = null
  }
}

function clearBeforeUnload() {
  if (beforeUnloadHandler) {
    window.removeEventListener("beforeunload", beforeUnloadHandler)
    beforeUnloadHandler = null
  }
}

function installGuards() {
  clearBeforeUnload()
  clearAutosave()

  // 👁 View ise hiçbir guard yok
  if (orderStore.mode === 'view') return

  /* ---------------- BEFOREUNLOAD ---------------- */
  installBeforeUnloadGuard()

  /* ---------------- AUTOSAVE ---------------- */
  autosaveTimer = setInterval(() => {
    orderStore.persistLocalStorage?.()
  }, 30000)
}




/* -------------------- MAIN ROUTE ENGINE -------------------- */
/* -------------------- MAIN ROUTE ENGINE -------------------- */

async function runRouteFlow() {
  const id   = String(orderHeaderID.value || "")
  const mode = resolveMode()

  /* =======================================================
     🔥 NEW MODE — signature BYPASS (KRİTİK)
  ======================================================= */
  if (mode === 'new') {
    lastRouteSignature.value = ''
  }

  const sig = `${mode}:${id}:${route.query.source || ""}`

  if (routeBusy.value || lastRouteSignature.value === sig) return
  lastRouteSignature.value = sig

  routeBusy.value = true
  loadingHeader.value = true

  try {
    orderStore.mode = mode

    // Ortak lookup’lar
    if (!cariOptions.value.length)  await loadCariList($q)
    if (!modelOptions.value.length) await loadModels($q)

    /* =======================================================
       🟢 NEW MODE — FINAL & SAFE
       -------------------------------------------------------
       ✔ route param boş / "new" ise → aktif NEW header’a fix
       ✔ draft varsa → hydrate
       ✔ draft yoksa → startNewOrder (LOCAL UUID üretir)
       ✔ URL her zaman GERÇEK OrderHeaderID taşır
    ======================================================= */
    if (mode === "new") {
      const routeId  = String(orderHeaderID.value || "")
      const activeId = orderStore.getActiveNewHeaderId?.()

      /* 1️⃣ Route ID FIX (kritik) */
      if (!routeId || routeId === "new") {
        if (activeId) {
          orderStore.allowRouteLeaveOnce = true
          await router.replace({
            name: "order-entry",
            params: { orderHeaderID: activeId },
            query: {
              ...route.query,
              mode: "new",
              source: route.query.source || "local"
            }
          })
          return
        }
      }

      /* 2️⃣ EDIT snapshot temizle */
      orderStore.clearEditSnapshotIfExists?.()

      /* 3️⃣ Draft hydrate → yoksa YENİ oluştur */
      const resumed = orderStore.hydrateFromLocalStorageIfExists?.()

      if (!resumed) {
        const header = await orderStore.startNewOrder({ $q, form, productCache })
        const newId  = header?.OrderHeaderID

        if (newId && newId !== routeId) {
          orderStore.allowRouteLeaveOnce = true
          await router.replace({
            name: "order-entry",
            params: { orderHeaderID: newId },
            query: { mode: "new", source: "new" }
          })
          return
        }
      }

      /* 4️⃣ Form sync */
      if (orderStore.header) {
        Object.assign(form, orderStore.header)
        syncCurrencyFromHeader()
      }

      return
    }

    /* =======================================================
       🔵 EDIT / 👁 VIEW MODE
    ======================================================= */
    if (isInvalidId(id)) {
      await router.replace({ name: "order-list" })
      return
    }

    let ok = false
    try {
      ok = await orderStore.openExistingForEdit(id, {
        $q,
        form,
        productCache
      })
    } catch {}

    if (!ok) {
      $q.notify({ type: "negative", message: "Sipariş açılamadı" })
      await router.replace({ name: "order-list" })
      return
    }

    if (orderStore.header) {
      Object.assign(form, orderStore.header)
      syncCurrencyFromHeader()
    }

  } finally {
    /* =======================================================
       🔒 GUARDS — TEK MERKEZ
    ======================================================= */
    installGuards()
    loadingHeader.value = false
    routeBusy.value = false
  }
}




/* -------------------- ROUTE WATCH -------------------- */
watch(
  () => [orderHeaderID.value, route.query.mode, route.query.source],
  runRouteFlow,
  { immediate: true }
)

/* -------------------- SIGNAL WATCHERS -------------------- */
watch(() => orderStore.newOrderSignal, async (v) => {
  if (!v) return

  // NEW header’ı üret (persistLocalStorage içinde draft + activeNewHeader yazılacak)
  const header = await orderStore.startNewOrder({ $q, form, productCache })

  const id = header?.OrderHeaderID || orderStore.getActiveNewHeaderId?.()
  if (!id) return

  await router.replace({
    name: "order-entry",
    params: { orderHeaderID: id },
    query: { mode: "new", source: "new" }
  })
})


watch(() => orderStore.replaceRouteSignal, async (id) => {
  if (!id) return
  await router.replace({
    name: "order-entry",
    params: { orderHeaderID: id },
    query: { mode: "edit", source: "backend" }
  })
})

/* -------------------- LIFECYCLE -------------------- */
onMounted(async () => {
  await orderStore.ensureProductSizeMatchRules()
  await nextTick()

  /* ---------------- UI ---------------- */
  updateStickyVars()
  measureHeaderGap()

  resizeHandler = () => updateStickyVars()
  window.addEventListener('resize', resizeHandler)

  /* ------------- HYDRATE DECISION ------------- */
  const mode = route.query.mode || 'new'
  const source = route.query.source || ''
  const id = orderHeaderID.value

  console.log('🧩 hydrate decision', { mode, source, id })

  if (mode === 'new' && source === 'draft' && id) {
    await orderentryStore.hydrateFromLocalStorage(id)
    return
  }

  if (mode === 'edit' && id) {
    await orderentryStore.hydrateFromLocalStorage(id)
    return
  }

  await orderentryStore.startNewOrder({ $q })
})



onUnmounted(() => {
  if (resizeHandler) window.removeEventListener("resize", resizeHandler)
  clearAutosave()
  clearBeforeUnload()
})

/* ===========================================================
   🚫 ROUTE LEAVE GUARD — FINAL (NEW DRAFT GUARANTEE)
   -----------------------------------------------------------
   ✔ View mode           → asla bloklama yok
   ✔ isControlledSubmit  → sessiz geç
   ✔ allowRouteLeaveOnce → 1 kez bypass
   ✔ hasUnsavedChanges=false → sessiz geç
   ✔ EDIT mode           → onayda edit snapshot temizlenir (+ optional reset)
   ✔ NEW mode            → onayda hiçbir şey silinmez
   ✔ NEW mode            → onayla çıkmadan önce DRAFT ZORLA PERSIST edilir
=========================================================== */
onBeforeRouteLeave((to, from, next) => {

  // 1) Kontrollü submit (kaydet / gönder akışı) → guard devre dışı
  if (orderStore.isControlledSubmit) {
    next()
    return
  }

  // 2) Programatik geçiş → 1 kere bypass
  if (orderStore.allowRouteLeaveOnce) {
    orderStore.allowRouteLeaveOnce = false
    next()
    return
  }

  // 3) VIEW → serbest
  if (orderStore.mode === 'view') {
    next()
    return
  }

  // 4) Değişiklik yok → serbest
  if (!orderStore.hasUnsavedChanges) {
    next()
    return
  }

  // 5) Kullanıcı onayı
  $q.dialog({
    title: 'Sayfadan ayrılıyorsunuz',
    message:
      orderStore.mode === 'edit'
        ? 'Değişiklikler kaybolacak. Devam edilsin mi?'
        : 'Taslak korunacak. Sayfadan çıkmak istiyor musunuz?',
    ok: { label: 'Evet', color: 'negative' },
    cancel: { label: 'Hayır' },
    persistent: true
  })
    .onOk(() => {

      /* ===================================================
         ✅ NEW MODE — DRAFT GARANTİSİ
         ---------------------------------------------------
         Amaç: gateway’e dönünce taslak kartı %100 görünsün.
         Çözüm: çıkmadan hemen önce snapshot’ı tek kaynağa yaz.
      =================================================== */
      if (orderStore.mode === 'new') {
        try {
          // NEW taslak tek kaynağa yazılmalı: orderStore.getDraftKey
          // persistLocalStorage bununla uyumlu olmalı.
          orderStore.persistLocalStorage?.()
        } catch (e) {
          // persist başarısız olsa bile kullanıcı çıkmayı onayladıysa çıkışa engel olmayalım
          console.warn('⚠️ NEW draft persist edilemedi (route leave):', e)
        }

        next()
        return
      }

      /* ===================================================
         🔥 EDIT MODE — TEMİZLİK
         ---------------------------------------------------
         ✔ local edit snapshot silinir
         ✔ (opsiyonel) state reset
      =================================================== */
      if (orderStore.mode === 'edit') {
        try {
          orderStore.clearEditSnapshotIfExists?.()
        } catch (e) {
          console.warn('⚠️ edit snapshot temizlenemedi:', e)
        }

        next()
        return
      }

      // diğer modlar için default
      next()
    })
    .onCancel(() => next(false))
})




// -----------------------------------------------------------
// 🔸 Cari Listesi
// -----------------------------------------------------------
// =======================================================
// 📦 Cari Listesini Yükle (Sadece new modda çağrılır)
// =======================================================


async function loadCariList($q) {
  loadingCari.value = true
  try {

    const res = await api.get('/customer-list')
    const data = res?.data

    if (Array.isArray(data)) {
      cariOptions.value = data
    } else if (Array.isArray(data?.data)) {
      cariOptions.value = data.data
    } else {
      cariOptions.value = []
    }

    filteredCariOptions.value = [...cariOptions.value]
    console.log(`🧾 Cari listesi yüklendi: ${cariOptions.value.length} kayıt.`)
  } catch (err) {
    console.error('❌ Cari listesi alınamadı:', err)
    $q.notify({
      type: 'negative',
      message: 'Cari listesi yüklenemedi ❌',
      position: 'top'
    })
  } finally {
    loadingCari.value = false
  }
}

/* ===========================================================
   🔍 Cari Arama Filtresi (QSelect @filter)
=========================================================== */
function filterCari(val, update) {
  if (!val) {
    update(() => {
      filteredCariOptions.value = [...cariOptions.value]
    })
    return
  }

  const needle = normalizeSearchText(val)

  update(() => {
    filteredCariOptions.value = cariOptions.value.filter(opt => {
      const kod = normalizeSearchText(opt.Cari_Kod)
      const ad = normalizeSearchText(opt.Cari_Ad)
      const unvan = normalizeSearchText(opt.Unvan)
      return `${kod} ${ad} ${unvan}`.includes(needle)
    })
  })
}
const highlightPantolon = computed(() =>
  (summaryRows.value || []).some(r =>
    (r.urunAnaGrubu || '').toLowerCase().includes('pantolon')
  )
)



// -----------------------------------------------------------
// 🔸 Model Listesi
// -----------------------------------------------------------
async function loadModels($q) {
  loadingModels.value = true
  try {
    const res = await api.get('/products')
    const arr = res?.data || []

    modelOptions.value = arr.map(x => ({
      label: x.ProductCode,
      value: x.ProductCode
    }))
    filteredModelOptions.value = modelOptions.value
    console.log('✅ Model listesi yüklendi:', modelOptions.value.length)
  } catch (err) {
    console.error('❌ Model listesi alınamadı:', err)
    $q.notify({
      type: 'negative',
      message: 'Model listesi alınamadı ❌',
      position: 'top-right'
    })
  } finally {
    loadingModels.value = false
  }
}

function syncCurrencyFromHeader() {
  const hdrPB = orderStore.header?.DocCurrencyCode || orderStore.header?.CurrencyCode
  if (!hdrPB) return

  form.pb = hdrPB
  form.DocCurrencyCode = hdrPB

  orderStore.setHeaderFields?.(
    { DocCurrencyCode: hdrPB, PriceCurrencyCode: hdrPB },
    { applyCurrencyToLines: true, immediatePersist: false }
  )
}


/* ===========================================================
   🔹 MODEL + PB Bazlı Minimum Fiyat
   =========================================================== */
async function fetchMinPrice() {
  if (!form.model || !form.pb) return

  try {
    const res = await api.get('/min-price', {
      params: { model: form.model, currency: form.pb }
    })
    const data = res.data

    form.minFiyat = Number(data.price || 0)
    form.kur = Number(data.rateToTRY || 1)
    form.minFiyatTRY = Number(data.priceTRY || 0)

    console.log(`💰 Min Fiyat: ${form.minFiyat} ${form.pb} (${form.minFiyatTRY} TRY)`)
  } catch (err) {
    console.error('❌ Min fiyat alınamadı:', err)
    form.minFiyat = 0
  }
}




/* ===========================================================
   🔽 SCROLL TO ROW — DOM SAFE
=========================================================== */
function scrollToRow(clientKey) {
  requestAnimationFrame(() => {
    const el = document.querySelector(
      `[data-client-key="${clientKey}"]`
    )
    if (!el) return

    el.scrollIntoView({
      behavior: 'smooth',
      block: 'center'
    })

    el.classList.add('row-error-flash')
    setTimeout(() => {
      el.classList.remove('row-error-flash')
    }, 1500)
  })
}

/* ===========================================================
   🔹 SERİ SETİ UYGULAMA (FINAL — grpKey SAFE)
=========================================================== */
function applySeriSet() {
  if (!hasRowMutationPermission()) {
    notifyNoPermission(
      isEditMode.value
        ? 'Siparis satiri guncelleme yetkiniz yok'
        : 'Siparis satiri ekleme yetkiniz yok'
    )
    return
  }

  if (!selectedSeriSet.value) return

  /* 🔑 TEK KAYNAK */
  const grpKey = activeGrpKey.value
  if (!grpKey) {
    console.warn('⚠️ applySeriSet: grpKey bulunamadı')
    return
  }

  const setKey =
    typeof selectedSeriSet.value === 'object'
      ? selectedSeriSet.value.value
      : selectedSeriSet.value

  const pattern = seriMatrix?.[grpKey]?.[setKey]
  if (!pattern) {
    console.warn(`⚠️ Seri seti bulunamadı → grpKey:${grpKey}, set:${setKey}`)
    return
  }

  const mult = Number(seriMultiplier.value) || 1

  /* =======================================================
     🔹 BEDENLER — LABEL BAZLI
  ======================================================= */
  form.bedenler = form.bedenLabels.map((lbl, i) => {
    const base = Number(form.bedenler?.[i] || 0)
    const inc  = Number(pattern[lbl] || 0) * mult
    return base + inc
  })

  updateTotals(form)

  $q.notify({
    type: 'positive',
    message: `Seri "${setKey}" uygulandı (${grpKey})`,
    position: 'top-right'
  })
}




/* ===========================================================
   🔹 TOPLAM HESAPLAMA
   =========================================================== */
function updateTotals(f) {
  f.adet = (f.bedenler || []).reduce((a, b) => a + Number(b || 0), 0)
  const fiyat = Number(f.fiyat) || 0
  f.tutar = Number((f.adet * fiyat).toFixed(2))

}


function removeSelected() {
  if (!hasRowMutationPermission()) {
    notifyNoPermission('Siparis satiri silme/guncelleme yetkiniz yok')
    return
  }

  const row = selectedRow.value
  if (!row) {
    $q.notify({ type: 'warning', message: 'Silmek için önce bir satır seçmelisiniz.' })
    return
  }

  // 🔒 Kapalı satır koruması
  if (row.isClosed === true) {
    $q.notify({ type: 'warning', message: 'Kapalı satır silinemez.', position: 'top-right' })
    return
  }

  $q.dialog({
    title: 'Satırı Sil',
    message: `<b>${row.model} / ${row.renk}</b> satırı silinsin mi?`,
    html: true,
    ok: { label: 'Sil', color: 'negative' },
    cancel: { label: 'Vazgeç', flat: true }
  }).onOk(() => {
    orderStore.removeRowInternal(row)

    // ✅ edit state temizle
    orderStore.editingKey = null
    orderStore.selected = null

    resetEditor()

    $q.notify({
      type: 'positive',
      message: 'Satır silindi (DELETE ops oluşturuldu)',
      position: 'top-right'
    })
  })
}

// ===========================================================
// ✅ hydrateEditorFromRow — FINAL FIX (EDITOR BEDEN MAP)
// ===========================================================
async function hydrateEditorFromRow(row, opts = {}) {
  const {
    allowClosed = false,
    notify = true,
    message = 'Düzenleme moduna alındı',
    loadSizes = true,
    source = 'hydrate'
  } = opts

  if (!row) return false

  /* -------------------------------------------------------
     🔒 KAPALI SATIR KONTROLÜ
  ------------------------------------------------------- */
  if (!allowClosed && row.isClosed === true) {
    notify && $q.notify({
      type: 'warning',
      message: 'Bu satır kapalıdır ve düzenlenemez.',
      position: 'top-right'
    })
    return false
  }

  /* -------------------------------------------------------
     🔑 editingKey
  ------------------------------------------------------- */
  const key =
    typeof orderStore.getRowKey === 'function'
      ? orderStore.getRowKey(row)
      : (row.clientKey || row.OrderLineID)

  if (!key) return false

  orderStore.editingKey = key
  orderStore.selected = { ...row }

  const ensureModelDetail = async (modelCode) => {
    const code = String(modelCode || '').trim()
    if (!code) return null

    if (productCache?.[code]) {
      return productCache[code]
    }

    try {
      const d = (await api.get('/product-detail', { params: { code } }))?.data || {}
      const normalized = {
        ...d,
        UrunAnaGrubu: d.UrunAnaGrubu || d.ProductGroup || d.ProductAtt01Desc || '',
        UrunAltGrubu: d.UrunAltGrubu || d.ProductSubGroup || d.ProductAtt02Desc || '',
        Kategori: d.Kategori || d.ProductAtt44Desc || d.YETISKIN_GARSON || d.YetiskinGarson || '',
        ProductAtt01Desc: d.ProductAtt01Desc || d.UrunAnaGrubu || d.ProductGroup || '',
        ProductAtt44Desc: d.ProductAtt44Desc || d.Kategori || d.YETISKIN_GARSON || d.YetiskinGarson || '',
        AskiliYan: d.AskiliYan || '',
        YETISKIN_GARSON: d.YETISKIN_GARSON || d.YetiskinGarson || d.AskiliYan || '',
        YetiskinGarson: d.YetiskinGarson || d.YETISKIN_GARSON || d.AskiliYan || '',
        Fit: d.Fit || d.Fit1 || '',
        UrunIcerik: d.UrunIcerik || d.Fabric || '',
        Drop: d.Drop || ''
      }
      productCache[code] = normalized
      orderStore.productCache[code] = normalized
      return normalized
    } catch (e) {
      console.warn(`⚠ model detail alınamadı (${code})`, e)
      return null
    }
  }

  const modelMeta = await ensureModelDetail(row.model)
  const rowUrunAna = row.urunAnaGrubu || row.UrunAnaGrubu || row.ProductAtt01Desc || modelMeta?.UrunAnaGrubu || modelMeta?.ProductAtt01Desc || ''
  const rowUrunAlt = row.urunAltGrubu || row.UrunAltGrubu || modelMeta?.UrunAltGrubu || ''
  const rowAskili = row.askiliyan || row.AskiliYan || modelMeta?.AskiliYan || ''
  const rowYetiskinGarson =
    row.yetiskinGarson ||
    row.YETISKIN_GARSON ||
    row.YetiskinGarson ||
    modelMeta?.YETISKIN_GARSON ||
    modelMeta?.YetiskinGarson ||
    rowAskili ||
    ''
  const rowKategori =
    row.kategori ||
    row.Kategori ||
    row.ProductAtt44Desc ||
    modelMeta?.Kategori ||
    modelMeta?.ProductAtt44Desc ||
    ''

  /* -------------------------------------------------------
     🧩 FORM BASIC
  ------------------------------------------------------- */
  const firstGroupedKey = Object.keys(row?.bedenMap || {})
    .find(k => row?.bedenMap?.[k] && typeof row.bedenMap[k] === 'object')
  const firstGroupedMap =
    firstGroupedKey !== undefined
      ? row?.bedenMap?.[firstGroupedKey]
      : null
  const rowGrpKey =
    row.grpKey ||
    detectBedenGroup(
      null,
      rowUrunAna,
      rowKategori,
      rowYetiskinGarson,
      rowUrunAlt
    )

  if (!rowGrpKey) {
    $q.notify({
      type: 'negative',
      message: 'Beden grubu SQL kural tablosundan belirlenemedi.'
    })
    return false
  }

  Object.assign(form, {
    model: row.model,
    renk: row.renk,
    renk2: row.renk2,
    urunAnaGrubu: rowUrunAna,
    urunAltGrubu: rowUrunAlt,
    fit: row.fit || row.Fit || row.Fit1 || modelMeta?.Fit || '',
    urunIcerik: row.urunIcerik || row.UrunIcerik || modelMeta?.UrunIcerik || '',
    drop: row.drop || row.Drop || modelMeta?.Drop || '',
    askiliyan: rowAskili,
    yetiskinGarson: rowYetiskinGarson,
    yasPayloadMap: { ...(row.yasPayloadMap || {}) },
    kategori: rowKategori,
    aciklama: row.aciklama,
    fiyat: Number(row.fiyat || 0),
    pb: row.pb || aktifPB.value || 'USD',
    terminTarihi: toDateOnly(row.terminTarihi || ''),
    grpKey: rowGrpKey
  })

  /* =======================================================
     🔑 BEDEN EDITOR — TEK DOĞRU KAYNAK (GARANTİLİ)
     -------------------------------------------------------
     ✔ schemaMap hazır değilse init edilir
     ✔ labels = schemaMap[grpKey].values
     ✔ values = row.bedenMap[grpKey] || 0
  ======================================================= */
  const grpKey = form.grpKey

  // 🔒 GARANTİ: schemaMap + grpKey
  if (!orderStore.schemaMap || !orderStore.schemaMap[grpKey]) {
    orderStore.initSchemaMap()
  }

  const schema = orderStore.schemaMap?.[grpKey]

  if (schema?.values?.length) {
    const rowMap = row?.bedenMap?.[grpKey] || firstGroupedMap || {}

    form.bedenLabels = [...schema.values]
    form.bedenler = form.bedenLabels.map(lbl =>
      Number(rowMap[lbl] || 0)
    )
  } else {
    console.warn('⛔ schema bulunamadı:', grpKey)
    form.bedenLabels = []
    form.bedenler = []
  }

  /* -------------------------------------------------------
     🧮 SATIR TOPLAM
  ------------------------------------------------------- */
  updateTotals(form)

  /* -------------------------------------------------------
     ⚙️ STOK / BEDEN ENVANTERİ (LABEL DOKUNMAZ)
  ------------------------------------------------------- */
  if (loadSizes && form.model) {
    await nextTick()

    await orderStore.loadProductSizes(
      form,
      true,
      $q
    )

    // backend’den gelen snapshot stokları varsa
    if (row.stokMap && typeof row.stokMap === 'object') {
      stockMap.value = { ...row.stokMap }
    }

    await loadOrderInventory(true)
  }

  notify && $q.notify({
    type: 'info',
    message: `${message} → ${row.model}`,
    position: 'top-right'
  })

  console.log('✅ hydrateEditorFromRow OK', {
    source,
    grpKey,
    labels: form.bedenLabels,
    values: form.bedenler
  })

  return true
}



/* ===========================================================
   🔹 handleNewCombination (v6.3 — FINAL & STABLE)
   - Model varsa ÇALIŞIR (renk opsiyonel)
   - UI bedenleri → loadProductSizes
   - Gerçek stok → loadOrderInventory
   - Formu ASLA resetlemez
=========================================================== */
async function handleNewCombination() {
  if (!form.model) {
    console.warn('⚠️ handleNewCombination: model yok')
    return
  }

  console.log('🆕 handleNewCombination', {
    model: form.model,
    renk: form.renk,
    renk2: form.renk2
  })

  try {
    /* -------------------------------------------------------
       1️⃣ Reaktivite sakinleşsin
    ------------------------------------------------------- */
    await nextTick()
    await new Promise(r => setTimeout(r, 200))
    await nextTick()

    /* -------------------------------------------------------
       2️⃣ UI için beden / grpKey hazırlığı
    ------------------------------------------------------- */
    await orderStore.loadProductSizes(
      form,
      true,
      $q,
      productCache
    )

    /* -------------------------------------------------------
       3️⃣ GERÇEK STOK (MSSQL)
       - merge=true → editor bozulmaz
    ------------------------------------------------------- */
    await loadOrderInventory(true)

    /* -------------------------------------------------------
       4️⃣ Stok bilgilendirme (opsiyonel)
    ------------------------------------------------------- */
    const stoklar = Object.values(stockMap.value || {})
    if (stoklar.length && stoklar.every(v => Number(v) === 0)) {
      $q.notify({
        type: 'warning',
        message: '⚠️ Bu kombinasyon için stok bulunamadı (0)',
        position: 'top-right'
      })
    } else {
      console.log(`✅ Stok yüklendi (${stoklar.length} beden)`)
    }

    /* -------------------------------------------------------
       5️⃣ Gridde aynı kombinasyon varsa → edit moduna al
    ------------------------------------------------------- */
    await openExistingCombination()

  } catch (err) {
    console.error('❌ handleNewCombination hata:', err)
    $q.notify({
      type: 'negative',
      message: 'Stok bilgisi alınamadı ❌',
      position: 'top-right'
    })
  }
}

/* ===========================================================
   ✅ openExistingCombination (X3 FINAL — helper kullanır)
   - sadece eşleşen satırı bulur
   - hydrate işini helper yapar
=========================================================== */
async function openExistingCombination() {
  if (!form.model) return

  const row = (orderStore.summaryRows || []).find(r =>
    r.model === form.model &&
    (r.renk || '') === (form.renk || '') &&
    (r.renk2 || '') === (form.renk2 || '') &&
    r.grpKey === form.grpKey

  )

  if (!row) {

    return
  }

  // Kapalıysa uyar + çık
  if (row.isClosed === true) {
    $q.notify({
      type: 'warning',
      message: 'Bu satır kapalıdır.',
      position: 'top-right'
    })
    return
  }

  await hydrateEditorFromRow(row, {
    source: 'openExistingCombination',
    message: 'Düzenleme moduna alındı',
    notify: true,
    loadSizes: true
  })
}

const editRow = async (row) => {
  try {
    await hydrateEditorFromRow(row, {
      source: 'editRow',
      message: 'Düzenleme moduna geçildi',
      notify: true,
      loadSizes: true
    })
    showEditor.value = true
  } catch (err) {
    console.error('❌ editRow hata:', err)
  }
}

const openNewRowEditor = async () => {
  if (!hasRowMutationPermission()) {
    notifyNoPermission('Siparis satiri ekleme/guncelleme yetkiniz yok')
    return
  }

  await resetEditor(true)
  showEditor.value = true
}





/* ===========================================================
   🔹 STOK YARDIMCI FONKSİYONLARI
   =========================================================== */
function getStockFor(lbl) {
  if (!lbl || !stockMap.value) return 0
  const val = stockMap.value[lbl]
  const num = Number(val)
  return isNaN(num) ? 0 : num
}

function getStockForRow(row, beden) {
  if (!row || !beden) return 0

  // 🔹 Eğer formda seçili model ve renk bu satıra aitse, bedenStock'tan getir
  if (row.model === form.model && row.renk === form.renk) {
    const f = bedenStock.value.find(x => x.beden === beden)
    if (f) return Number(f.stok) || 0
  }

  // 🔹 Satırın kendi stokMap’inde varsa oradan getir
  if (row.stokMap && row.stokMap[beden] != null) {
    const num = Number(row.stokMap[beden])
    return isNaN(num) ? 0 : num
  }

  return 0
}

function stockColorClass(qty) {
  const n = Number(qty)
  if (isNaN(n)) return ''
  if (n === 0) return 'stok-red'
  if (n > 0 && n <= 2) return 'stok-yellow'
  return 'stok-green'
}
const getKey =
  typeof orderStore.getRowKey === 'function'
    ? orderStore.getRowKey
    : (r => r?.clientKey || r?.id || r?.OrderLineID)

const activeGrpKey = computed(() => {
  // 1️⃣ Edit edilen satır varsa
  if (orderStore.editingKey) {
    const row = (summaryRows.value || []).find(
      r => getKey(r) === orderStore.editingKey
    )
    if (row?.grpKey) return row.grpKey
  }

  // 2️⃣ Editor formdan
  if (form.grpKey) return form.grpKey

  // 3️⃣ Güvenli fallback
  return 'tak'
})


const editingRow = computed(() => {
  const key = orderStore.editingKey
  if (!key) return null
  const getKey = typeof orderStore.getRowKey === 'function'
    ? orderStore.getRowKey
    : (r => r?.clientKey || r?.OrderLineID)
  return (summaryRows.value || []).find(r => getKey(r) === key) || null
})


/* ===========================================================
   🔹 Seri Matrix — Excel benzeri çarpan tabloları
   Her ürün tipi için (takım, gömlek, pantolon vs.)
   önceden tanımlanmış seri setlerini tutar.
   Örneğin “46–58 seri” seçilirse 46=1, 48=1, … şeklinde çarpanlar oluşur.
=========================================================== */
const seriMatrix = {
  tak: {
    '46-58 seri':     { 46:1, 48:1, 50:1, 52:1, 54:1, 56:1, 58:1 },
    '46-58 ara çift': { 46:1, 48:2, 50:2, 52:2, 54:1, 56:1, 58:1 },
    '44-58 seri':     { 44:1, 46:1, 48:1, 50:1, 52:1, 54:1, 56:1, 58:1 },
    '44-58 ara çift': { 44:1, 46:1, 48:2, 50:2, 52:2, 54:1, 56:1, 58:1 },
    '60-64 seri':     { 60:1, 62:1, 64:1 },
    '66-70 seri':     { 66:1, 68:1, 70:1 },
    '48-58 seri':     { 48:1, 50:1, 52:1, 54:1, 56:1, 58:1 }
  },
  gom: {
    'XS-XXL':          { XS:1, S:1, M:1, L:1, XL:1, XXL:1 },
    'XS-XXL ara çift': { XS:1, S:1, M:2, L:2, XL:2, XXL:1 },
    '3XL-5XL':         { '3XL':1, '4XL':1, '5XL':1 }
  },
  ayk: {
    '10\'lu seri': { 39:1, 40:2, 41:2, 42:2, 43:2, 44:1 },
    '39-44':        { 39:1, 40:1, 41:1, 42:1, 43:1, 44:1 },
    '45-47':        { 45:1, 46:1, 47:1 }
  },
  yas: {
    '2-14Y': { 2:1, 4:1, 6:1, 8:1, 10:1, 12:1, 14:1 }
  },
  pan: {
    '38-50 seri':     { 38:1, 40:1, 42:1, 44:1, 46:1, 48:1, 50:1 },
    '38-50 ara çift': { 38:1, 40:1, 42:2, 44:2, 46:2, 48:1, 50:1 },
    '52-56 seri':     { 52:1, 54:1, 56:1 },
    '58-62 seri':     { 58:1, 60:1, 62:1 }
  }
}
const activeSeriesOptions = computed(() => {
  const grpKey = activeGrpKey.value
  const sets = seriMatrix[grpKey]
  if (!sets) return []

  return Object.keys(sets).map(k => ({
    label: k,
    value: k
  }))
})

/* ===========================================================
   🔹 Para Birimi ve Toplam Tutar Hesaplaması
   Sipariş toplamları ve para birimi seçimi burada yönetilir.
=========================================================== */
const paraBirimOptions = ['USD', 'EUR', 'TRY','GBP']   // Kullanıcıya sunulacak döviz seçenekleri



// 🔸 3. Ana veri yüklemeleri (cari + modeller)



/* ===========================================================
   🔹 Model Arama (QSelect içinde)
   Kullanıcının yazdığı harflerle model kodlarını filtreler.
=========================================================== */
function filterModel(val, update) {
  if (val === '') {
    update(() => (filteredModelOptions.value = modelOptions.value))
    return
  }
  update(() => {
    const needle = normalizeSearchText(val)
    filteredModelOptions.value = modelOptions.value.filter(v =>
      normalizeSearchText(v.label).includes(needle)
    )
  })
}

/* ===========================================================
   🔹 MODEL SEÇİMİ (onModelChangeV2) — FINAL (grpKey FIXED)
   -----------------------------------------------------------
   ✔ grpKey SADECE burada set edilir
   ✔ grpKey, urunAnaGrubu/kategori’ye göre DETERMINISTIC
   ✔ Editor / loadProductSizes tahmin yapmaz (form.grpKey kesin)
   ✔ Pantolon gibi durumlarda 'tak' fallback engellenir
=========================================================== */
async function onModelChange(modelCode) {
  // 🧹 önceki renkleri tamamen sıfırla
  form.renk = ''
  form.renk2 = ''
  renkOptions.value = []
  renkOptions2.value = []

  if (renkSelect.value?.reset) renkSelect.value.reset()
  if (renk2Select.value?.reset) renk2Select.value.reset()

  if (!modelCode) {
    console.warn('⚠️ Model kodu boş, sorgu yapılmadı.')
    return
  }

  // 🧩 Önceki değerleri yedekle (korunacak alanlar)
  const keep = {
    aciklama: form.aciklama,
    bedenler: Array.isArray(form.bedenler) ? [...form.bedenler] : [],
    bedenLabels: Array.isArray(form.bedenLabels) ? [...form.bedenLabels] : [],
    fiyat: form.fiyat,
    adet: form.adet,
    tutar: form.tutar
  }

  try {
    /* -------------------------------------------------------
       🎨 1️⃣ Renk listesi
    ------------------------------------------------------- */
    const resColors = await api.get('/product-colors', {
      params: { code: modelCode }
    })
    renkOptions.value = (resColors?.data || []).map(x => ({
      label: `${x.color_code || x.ColorCode} – ${x.color_description || x.ColorDesc || ''}`,
      value: x.color_code || x.ColorCode
    }))

    /* -------------------------------------------------------
       🧱 2️⃣ Ürün detayları
    ------------------------------------------------------- */
    const resDetail = await api.get('/product-detail', {
      params: { code: modelCode }
    })
    const d = resDetail?.data || {}

    // ✅ Cache
    if (modelCode && d) {
      orderStore.productCache[modelCode] = productCache[modelCode]
      productCache[modelCode] = {
        ...d,
        ProductGroup: d.ProductGroup || d.UrunAnaGrubu || d.ProductAtt01Desc || '',
        ProductSubGroup: d.ProductSubGroup || d.UrunAltGrubu || d.ProductAtt02Desc || '',
        URUN_ANA_GRUBU: d.UrunAnaGrubu || d.ProductAtt01Desc || '',
        URUN_ALT_GRUBU: d.UrunAltGrubu || d.ProductAtt02Desc || '',
        ProductAtt01Desc: d.ProductAtt01Desc || d.UrunAnaGrubu || d.ProductGroup || '',
        ProductAtt44Desc: d.ProductAtt44Desc || d.Kategori || d.YETISKIN_GARSON || d.YetiskinGarson || ''
      }
      console.log('🗂️ Cache eklendi:', modelCode, Object.keys(productCache[modelCode]))
    }

    /* -------------------------------------------------------
       🧩 Form temel alanları
    ------------------------------------------------------- */
    Object.assign(form, {
      model: modelCode,
      urunAnaGrubu: d.UrunAnaGrubu || d.ProductGroup || d.ProductAtt01Desc || '',
      urunAltGrubu: d.UrunAltGrubu || d.ProductSubGroup || d.ProductAtt02Desc || '',
      fit: d.Fit1 || d.Fit || '',
      urunIcerik: d.UrunIcerik || d.Fabric || '',
      drop: d.Drop || '',
      kategori: d.Kategori || d.ProductAtt44Desc || d.YETISKIN_GARSON || d.YetiskinGarson || '',
      askiliyan: d.AskiliYan || '',
      yetiskinGarson: d.YETISKIN_GARSON || d.YetiskinGarson || d.AskiliYan || '',
      yasPayloadMap: {},
      aciklama: keep.aciklama,
      fiyat: keep.fiyat,
      adet: keep.adet,
      tutar: keep.tutar,
    })

    /* =======================================================
       🔑 BEDEN GRUBU — TEK VE KESİN KARAR (SQL kural tabanı)
    ======================================================= */
    await orderStore.ensureProductSizeMatchRules($q)

    let bedenGrpKey = null
    try {
      bedenGrpKey = detectBedenGroup(
        null,
        form.urunAnaGrubu,
        form.kategori || '',
        form.yetiskinGarson || form.askiliyan,
        form.urunAltGrubu || ''
      )
    } catch (e) {
      console.warn('⚠️ detectBedenGroup hata:', e)
      bedenGrpKey = null
    }

    if (!bedenGrpKey) {
      $q.notify({
        type: 'negative',
        message: 'Beden grubu eşleşmesi bulunamadı (kategori/ana grup/alt grup).'
      })
      return
    }

    form.grpKey = bedenGrpKey
    console.log('🧭 Editor grpKey set edildi →', bedenGrpKey)
    // ✅ Editor bedenleri hemen aç (UI seed) — schemaMap tek kaynak
    const schema =
      orderStore.schemaMap?.[form.grpKey] ||
      storeSchemaByKey?.[form.grpKey]

    if (Array.isArray(schema?.values) && schema.values.length) {
      // önceki adetleri label bazlı koru
      const prevMap = {}
      ;(keep.bedenLabels || []).forEach((lbl, i) => {
        prevMap[lbl] = Number(keep.bedenler?.[i] || 0)
      })

      form.bedenLabels = [...schema.values]
      form.bedenler = form.bedenLabels.map(lbl => Number(prevMap[lbl] || 0))
    } else {
      form.bedenLabels = []
      form.bedenler = []
    }

    console.log('📦 Model detayları yüklendi:', form.urunAnaGrubu, form.fit)

    /* -------------------------------------------------------
       💰 3️⃣ Min fiyat
    ------------------------------------------------------- */
    await fetchMinPrice()

    /* -------------------------------------------------------
       ⚙️ 4️⃣ Renk yoksa direkt beden/stok
    ------------------------------------------------------- */
    if (!renkOptions.value.length) {
      await orderStore.loadProductSizes(form, true, $q, productCache)
      await loadOrderInventory(true)
    }

    /* -------------------------------------------------------
       🧮 5️⃣ Gridde varsa → edit mod
    ------------------------------------------------------- */
    await openExistingCombination()

    $q.notify({
      type: 'info',
      message: `Model "${modelCode}" yüklendi ✅`,
      position: 'top-right'
    })
  } catch (err) {
    console.error('❌ Model verileri alınamadı:', err)
    $q.notify({
      type: 'negative',
      message: 'Model bilgileri alınamadı ❌',
      position: 'top-right'
    })
  }
}

/* ===========================================================
   🔹 RENK SEÇİMİ (1. Renk Değişimi)
=========================================================== */
async function onColorChange(colorCode) {
  form.renk = colorCode || ''
  renkOptions2.value = []
  form.renk2 = ''

  // 2. renk QSelect bileşenini sıfırla
  if (renk2Select.value) renk2Select.value.reset?.()

  // ⚠️ Renk seçilmediyse işlemi iptal et
  if (!form.renk) {
    console.warn('⚠️ Renk seçilmedi, işlemler durduruldu.')
    return
  }

  try {


    // 🎨 2️⃣ İkinci renk listesini yükle
    const res = await api.get('/product-secondcolor', {
      params: { code: form.model, color: colorCode }
    })

    const data = res?.data || []
    if (Array.isArray(data) && data.length > 0) {
      renkOptions2.value = data.map(x => ({
        label: x.item_dim2_code,
        value: x.item_dim2_code
      }))
      console.log('🎨 2. renk listesi yüklendi:', renkOptions2.value.length)
    } else {
      // 2. renk yoksa doğrudan beden/stok yükle
      console.log('⚪ 2. renk yok, doğrudan beden/stok yükleniyor...')

    }

    await handleNewCombination()

  } catch (err) {
    console.error('❌ 1. renk sonrası hata:', err)
  }
}

/* ===========================================================
   🔹 2. RENK SEÇİMİ (onColor2Change)
=========================================================== */
async function onColor2Change(colorCode2) {
  if (typeof colorCode2 === 'object' && colorCode2?.value) {
    colorCode2 = colorCode2.value
  }
  form.renk2 = colorCode2 || ''

  try {

    await handleNewCombination()



  } catch (err) {
    console.error('❌ 2. renk sonrası hata:', err)
  }
}

/* ===========================================================
   🔹 Beden / Stok Yükleyici — Değişkenler
=========================================================== */
const bedenStock = ref([])    // Görsel tablo için stok listesi
const stockMap = ref({})      // { "48": 12, "50": 7, ... } şeklinde key-value map

function warnIfSecondColorMissing() {
  const hasSecondColorOptions = Array.isArray(renkOptions2.value) && renkOptions2.value.length > 0
  const secondColorEmpty = !String(form.renk2 || '').trim()

  if (hasSecondColorOptions && secondColorEmpty) {
    $q.notify({
      type: 'warning',
      message: 'Bu model/renk için 2. renk seçimi önerilir.',
      position: 'top-right'
    })
  }
}

const onSaveOrUpdateRow = async () => {
  if (isRowSaveInFlight.value) return

  if (!hasRowMutationPermission()) {
    notifyNoPermission(
      isEditMode.value
        ? 'Siparis satiri guncelleme yetkiniz yok'
        : 'Siparis satiri kaydetme yetkiniz yok'
    )
    return
  }

  if (!validateEditorRowBeforeSave()) return

  warnIfSecondColorMissing()

  isRowSaveInFlight.value = true
  try {
    const ok = await orderStore.saveOrUpdateRowUnified({
      form,

      recalcVat: typeof recalcVat === 'function' ? recalcVat : null,
      resetEditor: typeof resetEditor === 'function' ? resetEditor : null,
      loadProductSizes: async () => {
        await orderStore.loadProductSizes(form, true, $q, productCache)
        await loadOrderInventory(true)
      },

      // gerekiyorsa pass edebilirsin (store tarafında zaten optional)
      stockMap,
      $q
    })
    if (ok !== false) {
      showEditor.value = false
    }
  } finally {
    isRowSaveInFlight.value = false
  }
}

function normalizeColorValue(val) {
  return String(val || '').trim().toUpperCase()
}

function getNextColorValue() {
  const options = Array.isArray(renkOptions.value) ? renkOptions.value : []
  if (!options.length) return null

  const current = normalizeColorValue(form.renk)
  const idx = options.findIndex(o => normalizeColorValue(o.value) === current)
  if (idx === -1) return null

  const next = options[idx + 1]
  return next ? next.value : null
}

const onSaveAndNextColor = async () => {
  if (isRowSaveInFlight.value) return

  if (!hasRowMutationPermission()) {
    notifyNoPermission(
      isEditMode.value
        ? 'Siparis satiri guncelleme yetkiniz yok'
        : 'Siparis satiri kaydetme yetkiniz yok'
    )
    return
  }

  if (!form.model) {
    $q.notify({ type: 'warning', message: 'Model seçiniz' })
    return
  }

  if (!form.renk) {
    $q.notify({ type: 'warning', message: 'Renk seçiniz' })
    return
  }

  if (!validateEditorRowBeforeSave()) return

  warnIfSecondColorMissing()

  isRowSaveInFlight.value = true
  let ok = false
  try {
    ok = await orderStore.saveOrUpdateRowUnified({
      form,
      recalcVat: typeof recalcVat === 'function' ? recalcVat : null,
      resetEditor: () => {},
      loadProductSizes: async () => {
        await orderStore.loadProductSizes(form, true, $q, productCache)
        await loadOrderInventory(true)
      },
      stockMap,
      $q
    })
  } finally {
    isRowSaveInFlight.value = false
  }

  if (!ok) return

  // Edit state temizle: renk değişimi combo delete tetiklemesin
  orderStore.editingKey = null
  orderStore.selected = null

  const nextColor = getNextColorValue()
  if (!nextColor) {
    $q.notify({
      type: 'warning',
      message: 'Son renktesiniz. Lütfen farklı bir renk seçin.',
      position: 'top-right'
    })
    return
  }

  form.renk2 = ''
  await onColorChange(nextColor)

  $q.notify({
    type: 'info',
    message: 'Satır kaydedildi. Bir sonraki renge geçildi.',
    position: 'top-right'
  })
}

function onZeroBedenClick () {
  if (!Array.isArray(form.bedenLabels) || !form.bedenLabels.length) {
    $q.notify({
      type: 'warning',
      message: 'Sıfırlanacak beden alanı bulunamadı.'
    })
    return
  }

  form.bedenler = form.bedenLabels.map(() => 0)
  updateTotals(form)

  $q.notify({
    type: 'info',
    message: 'Beden adetleri sıfırlandı.',
    position: 'top-right'
  })
}





/* ===========================================================
   🔹 loadOrderInventory (GÜNCELLENMİŞ)
   MSSQL stok sorgusu — artık boş değerleri 0 yapmıyor.
   merge=true ise sadece dolu değerleri günceller.
=========================================================== */
async function loadOrderInventory(merge = false) {
  if (!form.model) {
    console.warn('⚠️ Stok yüklenemedi: model seçilmemiş.')
    return
  }

  try {
    const params = { code: form.model }
    if (form.renk?.trim()) params.color = form.renk.trim()
    if (form.renk2?.trim()) params.color2 = form.renk2.trim()

    console.log('📦 MSSQL stok sorgusu gönderiliyor:', params)
    const res = await api.get('/order-inventory', { params })
    const data = res?.data || []

    console.log(`📦 MSSQL stok verisi geldi: ${data.length}`)
    console.table(data)

    const pickPreferredYasPayloadLabel = (currentRaw, nextRaw) => {
      const cur = String(currentRaw || '').trim().toUpperCase()
      const nxt = String(nextRaw || '').trim().toUpperCase()
      if (!nxt) return cur
      if (!cur) return nxt
      const curYas = /YAS$|YAŞ$/.test(cur)
      const nxtYas = /YAS$|YAŞ$/.test(nxt)
      if (!curYas && nxtYas) return nxt
      return cur
    }

    // 1️⃣ Normalize (gelen büyük harfli)
    const invMap = {}
    const invYasPayloadMap = {}
    for (const x of data) {
      const rawBeden = String(x.Beden || '').trim()
      const beden = normalizeBedenLabel(rawBeden)
      const stokDeger = Number(x.KullanilabilirAdet ?? 0)
      invMap[beden] = stokDeger

      const rawUpper = rawBeden.toUpperCase()
      if (/^(\d+)\s*(Y|YAS|YAŞ)$/.test(rawUpper)) {
        const canonical = normalizeBedenLabel(rawUpper)
        invYasPayloadMap[canonical] = pickPreferredYasPayloadLabel(
          invYasPayloadMap[canonical],
          rawUpper
        )
      }
    }

    // 2️⃣ Form bedenlerine göre map oluştur
    const newMap = {}
    for (const lbl of form.bedenLabels || []) {
      const key = lbl?.trim() === '' ? ' ' : normalizeBedenLabel(lbl)
      newMap[lbl] = invMap[key] ?? invMap[lbl] ?? 0
    }

    // 3️⃣ Merge veya replace
    if (merge && stockMap.value) {
      for (const lbl of Object.keys(newMap)) {
        stockMap.value[lbl] = newMap[lbl]
      }
    } else {
      stockMap.value = { ...newMap }
    }

    // 4️⃣ Görsel listeyi güncelle
    bedenStock.value = Object.entries(stockMap.value).map(([beden, stok]) => ({
      beden,
      stok
    }))
    form.yasPayloadMap = { ...(form.yasPayloadMap || {}), ...invYasPayloadMap }

    console.log('✅ Stok haritası güncellendi:', stockMap.value)
  } catch (err) {
    console.error('❌ Order inventory yüklenemedi:', err)
    $q.notify({
      type: 'negative',
      message: 'Stok verisi alınamadı ❌',
      position: 'top-right'
    })
  }
}



// 🔹 Üst formdaki tahmini termin değişince:
watch(
  () => form.AverageDueDate,
  (yeni) => {
    if (!yeni) return
    applyTerminToRows(yeni)
  }
)

// 📝 Sipariş genel açıklaması draft'ta kalmalı:
// form -> store.header sync + debounce persist
watch(
  () => [form.Description, form.InternalDescription],
  ([description, internalDescription]) => {
    orderStore.setHeaderFields?.(
      {
        Description: description ?? '',
        InternalDescription: internalDescription ?? ''
      },
      { immediatePersist: false }
    )
    orderStore._safePersistDebounced?.()
  }
)

/* ===========================================================
   🔹 useComboWatcher (v6.3 — MUTATION AWARE & CLEAN)
   - Edit modda combo değişirse:
     DELETE → temiz edit state
   - Guard KURMAZ
   - Persist SADECE gerçek mutation varsa
=========================================================== */
function useComboWatcher(type, handler) {
  return async (val) => {
    const prevBusy = !!orderStore._uiBusy
    const prevPrevent = !!orderStore.preventPersist
    let mutated = false   // 🔥 SADECE gerçekten değiştiyse true

    try {
      const currentRow = selectedRow.value
      const isEditingNow = !!currentRow

      /* =====================================================
         1️⃣ EDIT MODE → combo değiştiyse DELETE
      ====================================================== */
      if (isEditingNow && currentRow) {
        const nextCombo = {
          model: type === 'model' ? val : form.model,
          renk : type === 'renk'  ? val : form.renk,
          renk2: type === 'renk2' ? val : form.renk2
        }

        const comboChanged =
          (currentRow.model || '') !== (nextCombo.model || '') ||
          (currentRow.renk  || '') !== (nextCombo.renk  || '') ||
          (currentRow.renk2 || '') !== (nextCombo.renk2 || '')

        if (comboChanged) {
          console.warn('🟥 Combo değişti → DELETE')

          mutated = true   // 🔥 GERÇEK DEĞİŞİKLİK

          orderStore._uiBusy = true
          orderStore.preventPersist = true

          orderStore.removeRowInternal(currentRow)

          // 🔑 Tek kaynak edit state
          orderStore.editingKey = null
          orderStore.selected = null

          await nextTick()
        }
      }

      /* =====================================================
         2️⃣ ASIL HANDLER
      ====================================================== */
      if (typeof handler === 'function') {
        await handler(val)
      }

    } catch (err) {
      console.error('❌ useComboWatcher hata:', err)
    } finally {
      /* =====================================================
         3️⃣ STATE GERİ AL + ŞARTLI PERSIST
      ====================================================== */
      orderStore._uiBusy = prevBusy
      orderStore.preventPersist = prevPrevent

      // ✅ SADECE mutation olduysa snapshot al
      if (mutated) {
        orderStore.persistLocalStorage?.()
      }
    }
  }
}





// ======================================================
// 👁‍🗨 GROUPED ROWS WATCHER
// ======================================================
watch(groupedRows, (val) => {
  if (!Array.isArray(val)) return
  console.log(
    '👀 groupedRows değişti:',
    val.map(g => ({
      name: g.name,
      count: g.rows?.length || 0
    }))
  )
})




// =============================================
// ✅ onCariChange — %100 SAFE + CurrAccTypeCode Entegre
// =============================================
// =============================================
// ✅ onCariChange — FINAL (pb scope FIX)
// =============================================
async function onCariChange(kod) {
  let pb = 'USD' // ✅ dış scope: try/catch/finally hepsinde erişilebilir

  try {
    if (!kod) return

    // 🔹 Cari kaydını bul
    const cari = cariOptions.value.find(c => c.Cari_Kod === kod)
    if (!cari) {
      console.warn('⚠️ Cari bulunamadı:', kod)
      return
    }

    selectedCari.value = kod
    cariInfo.value = cari

    // 🔹 Para birimi (fallback’li)
    pb =
      cari.Doviz_Cinsi ||
      cari.ParaBirimi ||
      cari.DocCurrencyCode ||
      'USD'

    const postalAddressId = String(cari.PostalAddressID || '').trim()

    // 🔹 FORM sync (UI için)
    form.CurrAccTypeCode = cari.CurrAccTypeCode || 1
    form.CurrAccCode = kod
    form.DocCurrencyCode = pb
    form.pb = pb
    form.ShippingPostalAddressID = postalAddressId
    form.BillingPostalAddressID = postalAddressId
    aktifPB.value = pb

    /* =====================================================
       🔥 STORE HEADER SYNC
    ===================================================== */
    orderStore.setHeaderFields(
      {
        CurrAccTypeCode: form.CurrAccTypeCode,
        CurrAccCode: kod,
        DocCurrencyCode: pb,
        PriceCurrencyCode: pb,
        ShippingPostalAddressID: postalAddressId,
        BillingPostalAddressID: postalAddressId
      },
      {
        applyCurrencyToLines: true,
        immediatePersist: true
      }
    )

    /* =====================================================
       💱 Kur (opsiyonel)
    ===================================================== */
    if (orderStore.getTodayRate) {
      try {
        const rate = await orderStore.getTodayRate(pb, 'TRY')
        if (!isNaN(rate)) {
          orderStore.setHeaderFields({ ExchangeRate: Number(rate) })
        }
      } catch (e) {
        console.warn('⚠️ Kur alınamadı:', e)
      }
    }

    // 🔁 Toplamları yenile
    recalcVat()

    $q.notify({
      type: 'positive',
      message: `Cari değiştirildi → ${kod} (${pb})`,
      position: 'top-right'
    })
  } catch (err) {
    console.error('❌ onCariChange hata:', err)
    $q.notify({
      type: 'negative',
      message: 'Cari değiştirilemedi',
      position: 'top-right'
    })
  } finally {
    // 🔥 X3: para birimini satırlara yay (varsa)
    if (orderStore.applyCurrencyToLines) {
      orderStore.applyCurrencyToLines(pb)
    }
    // 💾 tek persist
    orderStore.persistLocalStorage?.()
  }
}

// ===========================================================
// 🔹 STICKY VARIABLE GÜNCELLEYİCİ (Eksik Olan Fonksiyon)
// CSS değişkenlerini DOM üzerinden yeniden hesaplar
// ===========================================================
// 🔹 Sticky değişkenleri güncelle
function updateStickyVars() {
  try {
    const root = document.documentElement
    const headerH = document.querySelector('.q-header')?.offsetHeight || 56
    const filterMainH = document.querySelector('.filter-bar')?.offsetHeight || 0
    const filterDescH = document.querySelector('.filter-bar-desc')?.offsetHeight || 0
    const cariInfoH = document.querySelector('.cari-info-bar')?.offsetHeight || 0
    const filterH = filterMainH + filterDescH + cariInfoH
    const saveH = document.querySelector('.save-toolbar')?.offsetHeight || 52
    const totalSticky = headerH + filterH + saveH

    root.style.setProperty('--header-h', `${headerH}px`)
    root.style.setProperty('--filter-h', `${filterH}px`)
    root.style.setProperty('--save-h', `${saveH}px`)
    root.style.setProperty('--sticky-total', `${totalSticky}px`)

    console.log(`📐 Sticky vars → header:${headerH}, filterMain:${filterMainH}, filterDesc:${filterDescH}, cariInfo:${cariInfoH}, filterTotal:${filterH}, save:${saveH}`)
  } catch (err) {
    console.warn('⚠️ updateStickyVars hata:', err)
  }
}

// 🔹 Header ile grid arasındaki boşluğu ölç
function measureHeaderGap() {
  try {
    const hdr = document.querySelector('.order-grid-header')
    if (!hdr) return
    const height = hdr.getBoundingClientRect().height || 0
    const gap = -height
    document.documentElement.style.setProperty('--header-body-gap', `${gap}px`)
    console.log('📏 Header boşluğu ölçüldü:', height, 'gap:', gap)
  } catch (err) {
    console.warn('⚠️ measureHeaderGap hata:', err)
  }
}




</script>
