<template>
  <section class="page-grid">
    <section class="panel inventory-unified-panel">
      <div class="warehouse-switchboard" role="tablist" aria-label="嘉恒仓库库存类型">
        <button
          v-for="tab in inventoryTabs"
          :key="tab.key"
          class="warehouse-tab-card"
          :class="[tab.iconClass, { 'warehouse-tab-card-active': activeInventoryTab === tab.key }]"
          type="button"
          role="tab"
          :aria-selected="activeInventoryTab === tab.key"
          @click="selectInventoryTab(tab.key)"
        >
          <span class="warehouse-tab-card-icon" aria-hidden="true">
            <AppIcon :name="tab.icon" />
          </span>
          <span class="warehouse-tab-card-copy">
            <strong>{{ tab.label }}</strong>
            <span>{{ tab.count }} 条</span>
          </span>
        </button>
      </div>

      <div class="inventory-section-divider"></div>

      <div class="warehouse-operation-zone" aria-label="出入库业务操作">
        <section class="warehouse-operation-group warehouse-operation-group-in" aria-label="入库">
          <header>
            <span class="warehouse-operation-kicker">入库区</span>
            <strong>入库</strong>
          </header>
          <div class="warehouse-operation-actions">
            <button
              v-for="operation in activeInboundOperations"
              :key="operation.key"
              class="warehouse-operation-button warehouse-operation-button-in"
              type="button"
              :title="operation.title"
              @click="openOperationDrawer(operation)"
            >
              <AppIcon name="plus" />
              <strong>{{ operation.label }}</strong>
            </button>
          </div>
        </section>

        <section class="warehouse-operation-group warehouse-operation-group-out" aria-label="出库">
          <header>
            <span class="warehouse-operation-kicker">出库区</span>
            <strong>出库</strong>
          </header>
          <div class="warehouse-operation-actions">
            <button
              v-for="operation in activeOutboundOperations"
              :key="operation.key"
              class="warehouse-operation-button warehouse-operation-button-out"
              type="button"
              :title="operation.title"
              @click="openOperationDrawer(operation)"
            >
              <AppIcon name="minus" />
              <strong>{{ operation.label }}</strong>
            </button>
          </div>
        </section>

        <section class="warehouse-operation-group warehouse-operation-group-tools" aria-label="工具">
          <header>
            <span class="warehouse-operation-kicker">辅助工具</span>
            <strong>工具</strong>
          </header>
          <div class="warehouse-tool-actions">
            <button
              class="warehouse-tool-button no-auto-icon"
              type="button"
              title="查看当前仓库出入库流水"
              @click="openWarehouseLedgerDrawer"
            >
              <AppIcon name="ledger" />
              <strong>流水</strong>
            </button>
            <button
              class="warehouse-tool-button no-auto-icon"
              type="button"
              title="对当前仓库执行盘库"
              @click="openStocktakeDialog"
            >
              <AppIcon name="stocktake" />
              <strong>盘库</strong>
            </button>
            <button
              class="warehouse-tool-button warehouse-tool-button-primary no-auto-icon"
              type="button"
              title="按在生产的生产台账统一办理成品、余料、废料入库"
              @click="openProductionWorkOrderInboundDrawer"
            >
              <AppIcon name="workOrder" />
              <strong>生产台账入库</strong>
            </button>
          </div>
        </section>
      </div>

      <div v-if="!inventoryOverlayOpen && (feedbackMessage || errorMessage || lastWarehouseArchiveResult)" class="inventory-page-feedback">
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>
        <DocumentArchiveActions
          v-if="lastWarehouseArchiveResult"
          :status="lastWarehouseArchiveResult.archive_status || '未生成'"
          @preview="previewWarehouseLedgerArchive(lastWarehouseArchiveResult)"
          @download="downloadWarehouseLedgerArchive(lastWarehouseArchiveResult)"
          @regenerate="regenerateWarehouseLedgerArchive(lastWarehouseArchiveResult)"
        />
      </div>

      <div class="inventory-tab-panel" role="tabpanel">
        <TableControls
          :key="`inventory-controls-${activeInventoryTab}`"
          v-model:search="balanceControls.search.value"
          v-model:sort-key="balanceControls.sortKey.value"
          v-model:sort-direction="balanceControls.sortDirection.value"
          :sort-options="balanceControls.sortOptions"
          :placeholder="activeSearchPlaceholder"
        />

        <div class="table-wrap">
          <table :key="`inventory-main-table-${activeInventoryTab}`" class="data-table compact-table">
            <thead>
              <tr>
                <th>{{ activeItemColumnLabel }}</th>
                <th v-if="!activeWeightOnly">现存数量</th>
                <th v-if="!activeWeightOnly">可用数量</th>
                <th v-if="activeQuantityOnly">单位</th>
                <th v-if="!activeQuantityOnly">现存重量(kg)</th>
                <th v-if="!activeQuantityOnly">可用重量(kg)</th>
                <th>仓库</th>
                <th>库位</th>
                <th>均价</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="balance in paginatedBalances" :key="balance.stock_balance_id">
                <td>
                  <button class="link-button" type="button" :title="formatItemTypeLabel(balance.item_type)" @click="openStockDetail(balance)">
                    {{ formatInventoryBalanceLabel(balance) }}
                  </button>
                </td>
                <td v-if="!activeWeightOnly">{{ activeInventoryTab === "semi" ? formatSemiMeasureValue(balance, balance.qty_on_hand, "PIECE") : formatQty(balance.qty_on_hand) }}</td>
                <td v-if="!activeWeightOnly">{{ activeInventoryTab === "semi" ? formatSemiMeasureValue(balance, balance.qty_available, "PIECE") : formatQty(balance.qty_available) }}</td>
                <td v-if="activeQuantityOnly">{{ formatInventoryUnitLabel(balance) }}</td>
                <td v-if="!activeQuantityOnly">{{ activeInventoryTab === "semi" ? formatSemiMeasureValue(balance, balance.weight_on_hand_kg, "WEIGHT") : formatWeight(balance.weight_on_hand_kg) }}</td>
                <td v-if="!activeQuantityOnly">{{ activeInventoryTab === "semi" ? formatSemiMeasureValue(balance, balance.weight_available_kg, "WEIGHT") : formatWeight(balance.weight_available_kg) }}</td>
                <td>{{ balance.warehouse_name }}</td>
                <td>{{ balance.location_name || "-" }}</td>
                <td>¥{{ formatAmount(balance.avg_unit_cost) }}</td>
              </tr>
              <tr v-if="!filteredBalances.length">
                <td :colspan="activeWeightOnly ? 6 : activeQuantityOnly ? 7 : 8" class="empty-row">暂无{{ activeInventoryLabel }}库存</td>
              </tr>
            </tbody>
          </table>
        </div>
        <PaginationBar v-model:page="balancePage" v-model:page-size="balancePageSize" :total="filteredBalances.length" />
      </div>
    </section>

    <Teleport to="body">
      <div v-if="detailDrawerOpen" class="workflow-drawer-mask dual-drawer-mask inventory-ledger-detail-mask" @click.self="closeStockDetail">
        <div class="dual-drawer-shell">
          <aside class="workflow-drawer compact-side-drawer">
            <header class="workflow-drawer-head">
              <div>
                <p class="eyebrow">{{ selectedDetailEntityLabel }}总览</p>
                <h3>{{ selectedBalanceLabel }}</h3>
              </div>
              <button class="ghost-button" type="button" @click="closeStockDetail">关闭</button>
            </header>

            <div class="inventory-lot-summary-grid">
              <article>
                <span>{{ selectedPrimaryAvailableLabel }}</span>
                <strong>{{ selectedPrimaryAvailableValue }}</strong>
              </article>
              <article>
                <span>{{ selectedPrimaryOnHandLabel }}</span>
                <strong>{{ selectedPrimaryOnHandValue }}</strong>
              </article>
              <article>
                <span>{{ selectedAuxiliaryMetricLabel }}</span>
                <strong>{{ selectedAuxiliaryMetricValue }}</strong>
              </article>
            </div>

            <div class="inventory-source-lots">
              <span>来自库存批次号</span>
              <div>
                <b v-for="lotNo in selectedSourceLotNos" :key="lotNo">{{ lotNo }}</b>
                <em v-if="!selectedSourceLotNos.length">{{ selectedSourceLotEmptyText }}</em>
              </div>
            </div>

            <DrawerDataTable
              :rows="selectedOverviewLots"
              :columns="overviewLotDrawerColumns"
              row-key="lot_id"
              :empty-text="`暂无该${selectedDetailEntityLabel}库存批次`"
            >
              <template #cell-lot_no="{ row }">
                <span class="drawer-cell-code" :title="formatSelectedDetailLotNo(row)">{{ formatSelectedDetailLotNo(row) }}</span>
              </template>
              <template #cell-status="{ row }">
                <span class="status-pill" :class="lotStatusClass(row)">{{ formatStatusLabel(row.status) }}</span>
              </template>
            </DrawerDataTable>

            <section class="drawer-subsection">
              <div class="subsection-head">
                <strong>库存批次明细</strong>
                <span class="panel-tag">{{ filteredLots.length }} 条</span>
              </div>
              <TableControls
                v-model:search="lotControls.search.value"
                v-model:sort-key="lotControls.sortKey.value"
                v-model:sort-direction="lotControls.sortDirection.value"
                :sort-options="lotControls.sortOptions"
                placeholder="搜索库存批次号、采购单号、入库单号、仓库、状态"
              />

              <DrawerDataTable
                v-model:expanded-rows="expandedLotRows"
                :rows="paginatedLots"
                :columns="lotDrawerColumns"
                row-key="lot_id"
                :empty-text="`暂无该${selectedDetailEntityLabel}库存批次`"
              >
                <template #cell-lot_no="{ row }">
                  <span class="drawer-cell-code" :title="formatSelectedDetailLotNo(row)">{{ formatSelectedDetailLotNo(row) }}</span>
                </template>
                <template #cell-status="{ row }">
                  <span class="status-pill" :class="lotStatusClass(row)">{{ formatStatusLabel(row.status) }}</span>
                </template>
                <template #detail="{ row }">
                  <div class="drawer-row-detail-grid">
                    <article><span>仓库</span><strong>{{ row.warehouse_name || "-" }}</strong></article>
                    <article><span>采购单号</span><strong>{{ row.source_purchase_order_no || "-" }}</strong></article>
                    <article><span>入库单号</span><strong>{{ row.source_receipt_no || "-" }}</strong></article>
                    <article><span>来源</span><strong>{{ formatSourceDocTypeLabel(row.source_doc_type) }}</strong></article>
                    <article><span>来源库存批次号</span><strong>{{ formatLotSourceLots(row) }}</strong></article>
                    <article><span>运单号</span><strong>{{ row.logistics_waybill_no || "-" }}</strong></article>
                    <article><span>运费</span><strong>{{ formatFreight(row.logistics_freight_amount) }}</strong></article>
                    <article>
                      <span>辅助照片</span>
                      <strong>
                        <button v-if="row.logistics_photo_url" class="link-button" type="button" @click="openLogisticsPhoto(row.logistics_photo_url)">查看</button>
                        <span v-else>-</span>
                      </strong>
                    </article>
                    <article><span>质检放行人</span><strong>{{ row.item_type === "RAW_MATERIAL" ? formatLotQualityRelease(row) : "-" }}</strong></article>
                  </div>
                </template>
              </DrawerDataTable>
              <PaginationBar v-model:page="lotPage" v-model:page-size="lotPageSize" :total="filteredLots.length" />
            </section>
          </aside>

          <aside class="workflow-drawer compact-side-drawer">
            <header class="workflow-drawer-head">
              <div>
                <p class="eyebrow">库存流水</p>
                <h3>{{ selectedBalanceLabel }}</h3>
              </div>
            </header>

            <section class="drawer-subsection">
              <div class="subsection-head">
                <strong>库存流水</strong>
                <span class="panel-tag">{{ filteredTransactions.length }} 条</span>
              </div>
              <TableControls
                v-model:search="txnControls.search.value"
                v-model:sort-key="txnControls.sortKey.value"
                v-model:sort-direction="txnControls.sortDirection.value"
                :sort-options="txnControls.sortOptions"
                placeholder="搜索流水类型、库存批次号、来源、备注"
              />
              <DrawerDataTable
                v-model:expanded-rows="expandedTxnRows"
                :rows="paginatedTransactions"
                :columns="txnDrawerColumns"
                row-key="inventory_txn_id"
                :empty-text="`暂无该${selectedDetailEntityLabel}库存流水`"
              >
                <template #cell-lot_no="{ row }">
                  <span class="drawer-cell-code" :title="formatSelectedDetailLotNo(row)">{{ formatSelectedDetailLotNo(row) }}</span>
                </template>
                <template #cell-archive_actions="{ row }">
                  <DocumentArchiveActions
                    :status="row.archive_status || '未生成'"
                    @preview="previewWarehouseLedgerArchive(row)"
                    @download="downloadWarehouseLedgerArchive(row)"
                    @regenerate="regenerateWarehouseLedgerArchive(row)"
                  />
                </template>
                <template #detail="{ row }">
                  <div class="drawer-row-detail-grid">
                    <article><span>来源</span><strong>{{ formatSourceDocTypeLabel(row.source_doc_type) }} #{{ row.source_doc_id || "-" }}</strong></article>
                    <article><span>备注</span><strong>{{ row.remark || "-" }}</strong></article>
                  </div>
                </template>
              </DrawerDataTable>
              <PaginationBar v-model:page="txnPage" v-model:page-size="txnPageSize" :total="filteredTransactions.length" />
            </section>
          </aside>
        </div>
      </div>
    </Teleport>

    <FormDrawer
      :open="genericDrawerOpen"
      :eyebrow="activeOperation?.direction === 'out' ? '出库业务' : '入库业务'"
      :title="activeOperation?.label || '出入库登记'"
      :tag="activeInventoryLabel"
      wide
      @close="closeGenericDrawer"
    >
      <WarehouseDocumentFormShell
        v-if="isCustomerSuppliedInbound"
        class="warehouse-document-operation-form"
        title="客料入库单"
        :document-no="customerSuppliedLotNoPreview || '提交后生成'"
        :business-date="new Date()"
        @submit="submitGenericOperation"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="客料入库信息">
          <div class="warehouse-document-meta-strip" aria-label="单据信息">
            <span>入库业务</span>
            <strong>{{ activeInventoryLabel }}</strong>
            <span>成本按客供材料处理</span>
          </div>

          <div class="warehouse-paper-grid" aria-label="客料入库填写项">
            <label class="warehouse-paper-field warehouse-paper-field-wide">
              <span>{{ genericItemFieldLabel }}</span>
              <select v-model="genericForm.item_option_key" required @change="handleGenericItemChange">
                <option disabled value="">{{ genericItemPlaceholder }}</option>
                <option v-for="item in genericItemOptions" :key="item.option_key || item.item_id" :value="item.option_key || String(item.item_id)">
                  {{ formatGenericItemOption(item) }}
                </option>
              </select>
            </label>

            <label class="warehouse-paper-field">
              <span>仓库</span>
              <select v-model.number="genericForm.warehouse_id" required @change="handleGenericWarehouseChange">
                <option disabled value="">请选择仓库</option>
                <option v-for="warehouse in activeWarehouses" :key="warehouse.id" :value="warehouse.id">
                  {{ warehouse.warehouse_name }}
                </option>
              </select>
            </label>

            <label class="warehouse-paper-field">
              <span>库位</span>
              <select v-model.number="genericForm.location_id">
                <option value="">默认库位</option>
                <option v-for="location in genericLocations" :key="location.id" :value="location.id">
                  {{ location.location_name }}
                </option>
              </select>
            </label>

            <label class="warehouse-paper-field">
              <span>入库重量(kg)</span>
              <input
                v-model.number="genericForm.weight_kg"
                type="number"
                min="0.001"
                step="0.001"
                required
              />
            </label>

            <label class="warehouse-paper-field">
              <span>{{ genericUnitCostLabel }}</span>
              <input v-model.number="genericForm.unit_cost" type="number" min="0" step="0.0001" readonly />
            </label>

            <label class="warehouse-paper-field">
              <span>来源/提供方</span>
              <input v-model.trim="genericForm.provider_name" type="text" placeholder="客户、项目或来源说明" />
            </label>

            <label class="warehouse-paper-field">
              <span>运单号</span>
              <input v-model.trim="genericForm.waybill_no" type="text" placeholder="可填运单号，或上传辅助照片" />
            </label>

            <label class="warehouse-paper-field">
              <span>运费</span>
              <input v-model.number="genericForm.freight_amount" type="number" min="0" step="0.01" required />
            </label>

            <div class="warehouse-paper-field warehouse-paper-upload-field">
              <span>辅助照片</span>
              <label
                class="warehouse-paper-upload"
                :class="{ 'is-uploaded': genericForm.order_photo_url, 'is-busy': logisticsPhotoUploading }"
              >
                <input
                  type="file"
                  accept="image/*"
                  :disabled="logisticsPhotoUploading"
                  @change="handleLogisticsPhotoUpload(genericForm, $event)"
                />
                <b aria-hidden="true">{{ genericForm.order_photo_url ? "✓" : "↑" }}</b>
                <strong>{{ logisticsPhotoUploading ? "上传中..." : genericForm.order_photo_url ? "已上传辅助照片" : "上传辅助照片" }}</strong>
              </label>
            </div>

            <div class="warehouse-paper-field warehouse-paper-field-wide">
              <span>库存批次号</span>
              <p class="warehouse-paper-readonly-value">
                {{ customerSuppliedLotNoPreview ? `提交后自动生成：${customerSuppliedLotNoPreview}` : "选择物料后自动生成库存批次号" }}
              </p>
            </div>

            <label class="warehouse-paper-field warehouse-paper-field-full">
              <span>备注</span>
              <textarea v-model.trim="genericForm.remark" rows="4"></textarea>
            </label>
          </div>
        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation || logisticsPhotoUploading">
            {{ submittingOperation ? "处理中..." : "确认登记客料入库" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>

      <WarehouseDocumentFormShell
        v-else
        class="warehouse-document-operation-form"
        :title="genericOperationDocumentTitle"
        document-no="保存后生成"
        :business-date="new Date()"
        @submit="submitGenericOperation"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="出入库业务信息">
        <div v-if="isProductionWorkOrderControlledInbound" class="warehouse-paper-control-grid warehouse-paper-control-grid-compact">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>{{ isReworkScrapInbound ? "返工工单" : "生产台账" }}</span>
            <select v-model.number="genericForm.work_order_id" required @change="applyGenericWorkOrder">
              <option disabled value="">{{ isReworkScrapInbound ? "请选择返工工单" : "请选择生产台账" }}</option>
              <option v-for="workOrder in genericWorkOrderOptions" :key="workOrder.work_order_id" :value="workOrder.work_order_id">
                {{ formatWorkOrderLedgerOption(workOrder, activeOperation?.bizType) }}
              </option>
            </select>
          </label>

          <template v-if="selectedGenericWorkOrder">
            <article class="warehouse-paper-summary-card warehouse-paper-summary-card-wide">
              <span>产品</span>
              <strong>{{ selectedGenericWorkOrder.product_code }} · {{ selectedGenericWorkOrder.product_name }}</strong>
            </article>
            <article class="warehouse-paper-summary-card">
              <span>材料库存批次号</span>
              <strong>{{ selectedGenericWorkOrder.source_lot_summary || selectedGenericWorkOrder.material_lot_no || "-" }}</strong>
            </article>
            <article v-if="isProductionScrapInbound" class="warehouse-paper-summary-card">
              <span>库外未闭环重量</span>
              <strong>{{ formatWeight(selectedGenericWorkOrder.outside_weight_kg || selectedGenericWorkOrder.limits?.max_scrap_inbound_weight_kg) }}</strong>
            </article>
            <article v-if="isProductionSurplusInbound" class="warehouse-paper-summary-card">
              <span>库外未闭环重量</span>
              <strong>{{ formatWeight(selectedGenericWorkOrder.outside_weight_kg || selectedGenericWorkOrder.limits?.max_surplus_inbound_weight_kg) }}</strong>
            </article>
          </template>
        </div>

        <div class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>{{ genericItemFieldLabel }}</span>
            <select v-model="genericForm.item_option_key" required :disabled="(isProductionScrapInbound || isReworkScrapInbound) && !!selectedGenericWorkOrder" @change="handleGenericItemChange">
              <option disabled value="">{{ genericItemPlaceholder }}</option>
              <option v-for="item in genericItemOptions" :key="item.option_key || item.item_id" :value="item.option_key || String(item.item_id)">
                {{ formatGenericItemOption(item) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>仓库</span>
            <select v-model.number="genericForm.warehouse_id" required @change="handleGenericWarehouseChange">
              <option disabled value="">请选择仓库</option>
              <option v-for="warehouse in activeWarehouses" :key="warehouse.id" :value="warehouse.id">
                {{ warehouse.warehouse_name }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>库位</span>
            <select v-model.number="genericForm.location_id">
              <option value="">默认库位</option>
              <option v-for="location in genericLocations" :key="location.id" :value="location.id">
                {{ location.location_name }}
              </option>
            </select>
          </label>
          <label v-if="isSemiSourceLotTraceInbound" class="warehouse-paper-field">
            <span>入库类型</span>
            <select v-model="genericForm.measure_basis" :disabled="isSemiMeasureBasisLocked" required @change="handleGenericMeasureBasisChange">
              <option disabled value="">请选择入库类型</option>
              <option value="PIECE">按件入库</option>
              <option value="WEIGHT">按重入库</option>
            </select>
          </label>
          <label v-if="showGenericWeightInput" class="warehouse-paper-field">
            <span>{{ activeOperation?.direction === "out" ? "出库重量(kg)" : "入库重量(kg)" }}</span>
            <input
              v-model.number="genericForm.weight_kg"
              type="number"
              :min="isSemiQuantityWeightFlexibleOperation ? 0 : 0.001"
              :max="genericWeightInputMax || undefined"
              step="0.001"
              :required="!isSemiQuantityWeightFlexibleOperation"
            />
          </label>
          <label v-if="showGenericQtyInput" class="warehouse-paper-field">
            <span>{{ activeOperation?.direction === "out" ? "出库数量" : "入库数量" }}</span>
            <input v-model.number="genericForm.qty" type="number" min="0" step="0.001" />
          </label>
          <label v-if="activeOperation?.direction !== 'out' && !isSourceLotReturnInbound && !isSemiSourceLotTraceInbound" class="warehouse-paper-field">
            <span>{{ genericUnitCostLabel }}</span>
            <input v-model.number="genericForm.unit_cost" type="number" min="0" step="0.0001" :readonly="activeOperation?.bizType === 'CUSTOMER_SUPPLIED'" />
          </label>
        </div>

        <div v-if="activeOperation?.direction !== 'out' && !isCustomerSuppliedInbound && !isSourceLotReturnInbound && !isSemiSourceLotTraceInbound && !isFinishedOutsourcingInbound && !isProductionScrapInbound && !isReworkScrapInbound && !isOutsourcingScrapInbound" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field">
            <span>批次号</span>
            <input v-model.trim="genericForm.lot_no" type="text" placeholder="不填则自动生成" />
          </label>
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>来源/提供方</span>
            <input v-model.trim="genericForm.provider_name" type="text" placeholder="客户、项目、委外厂或来源说明" />
          </label>
        </div>
        <div v-else-if="isCustomerSuppliedInbound" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>来源/提供方</span>
            <input v-model.trim="genericForm.provider_name" type="text" placeholder="客户、项目或来源说明" />
          </label>
        </div>
        <div v-else-if="['OUTSOURCING_IN', 'OUTSOURCING_SURPLUS'].includes(activeOperation?.bizType)" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>来源/提供方</span>
            <input v-model.trim="genericForm.provider_name" type="text" placeholder="委外厂或来源说明" />
          </label>
        </div>

        <div v-if="activeOperation?.bizType === 'SCRAP_SALE_OUT'" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field">
            <span>购买方</span>
            <input v-model.trim="genericForm.buyer_name" type="text" placeholder="填写废料购买方" />
          </label>
          <label class="warehouse-paper-field">
            <span>售卖单价(元/kg)</span>
            <input v-model.number="genericForm.sale_unit_price" type="number" min="0.0001" step="0.0001" required />
          </label>
          <label class="warehouse-paper-field">
            <span>预计售卖金额</span>
            <input :value="`¥${formatAmount(estimatedScrapSaleAmount)}`" type="text" disabled />
          </label>
        </div>

        <div v-if="genericRequiresLogistics" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field">
            <span>运单号</span>
            <input v-model.trim="genericForm.waybill_no" type="text" placeholder="可填运单号，或上传辅助照片" />
          </label>
          <label class="warehouse-paper-field">
            <span>运费</span>
            <input v-model.number="genericForm.freight_amount" type="number" min="0" step="0.01" required />
          </label>
          <div class="warehouse-paper-field warehouse-paper-upload-field">
            <span>辅助照片</span>
            <label
              class="warehouse-paper-upload"
              :class="{ 'is-uploaded': genericForm.order_photo_url, 'is-busy': logisticsPhotoUploading }"
            >
              <input
                type="file"
                accept="image/*"
                :disabled="logisticsPhotoUploading"
                @change="handleLogisticsPhotoUpload(genericForm, $event)"
              />
              <b aria-hidden="true">{{ genericForm.order_photo_url ? "✓" : "↑" }}</b>
              <strong>{{ logisticsPhotoUploading ? "上传中..." : genericForm.order_photo_url ? "已上传辅助照片" : "上传辅助照片" }}</strong>
            </label>
          </div>
        </div>

        <div v-if="isCustomerSuppliedInbound" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>库存批次号</span>
            <input :value="customerSuppliedLotNoPreview ? `提交后自动生成：${customerSuppliedLotNoPreview}` : '选择物料后自动生成库存批次号'" type="text" readonly />
          </label>
        </div>

        <div v-else-if="!usesGenericSourceLotSelector && !isSemiAutoSourceOutbound && !isReworkScrapInbound" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>来源库存批次号</span>
            <input v-model.trim="genericForm.source_material_sub_batch_no" type="text" placeholder="有来源库存批次时填写" />
          </label>
        </div>

        <div v-else-if="isSemiAutoSourceOutbound" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>来源库存批次号</span>
            <input :value="semiAutoSourceOutboundSourceLotSummary || '选择半成品后自动带出库存批次号'" type="text" readonly />
          </label>
        </div>

        <div v-if="isSemiAutoSourceOutbound && semiAutoSourceOutboundSourceLots.length" class="table-wrap warehouse-paper-source-lot-table">
          <table class="data-table compact-table">
            <thead>
              <tr>
                <th>来源库存批次号</th>
                <th>半成品</th>
                <th>可用数量</th>
                <th>可用重量(kg)</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="lot in semiAutoSourceOutboundSourceLots" :key="lot.lot_id">
                <td>{{ lot.lot_no }}</td>
                <td>{{ selectedGenericItemOption?.operation_label || selectedGenericItemOption?.source_material_summary || "-" }}</td>
                <td>{{ formatQty(lot.remaining_qty) }}</td>
                <td>{{ formatWeight(lot.remaining_weight_kg) }}</td>
              </tr>
            </tbody>
          </table>
        </div>

        <div v-if="activeOperation?.bizType === 'OUTSOURCING_OUT'" class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>委外方</span>
            <input v-model.trim="genericForm.outsourcing_party_name" type="text" placeholder="填写委外加工方" />
          </label>
        </div>

        <div v-if="usesGenericSourceLotSelector" class="warehouse-paper-source-lot-panel">
          <div class="warehouse-paper-source-lot-heading">
            <span>来源库存批次号</span>
            <strong>{{ genericSourceLotSelectPlaceholder }}</strong>
          </div>
          <StockLotTagSelect
            v-model="selectedGenericSourceLots"
            :options="genericSourceLotOptions"
            :placeholder="genericSourceLotSelectPlaceholder"
            :allow-unavailable="isSourceLotReturnInbound || isSemiSourceLotTraceInbound || isOutsourcingScrapInbound"
            :display-weight-field="isProductionSurplusInbound ? 'issued_weight_kg' : 'remaining_weight_kg'"
            :option-weight-label="isProductionSurplusInbound ? '领料' : '剩余'"
          />
        </div>

        <div v-if="isSourceLotReturnInbound && selectedGenericSourceLots.length" class="table-wrap warehouse-paper-source-lot-table">
          <table class="data-table compact-table">
            <thead>
              <tr>
                <th>来源库存批次号</th>
                <th>原材料</th>
                <th>{{ isProductionSurplusInbound ? "领料重量" : "剩余重量" }}</th>
                <th v-if="isProductionSurplusInbound">已归还重量</th>
                <th v-if="isProductionSurplusInbound">建议归还重量</th>
                <th>归还重量(kg)</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="lot in selectedGenericSourceLots" :key="lot.lot_id">
                <td>{{ lot.lot_no }}</td>
                <td>{{ lot.item_code }} · {{ lot.item_name }}</td>
                <td>{{ formatWeight(isProductionSurplusInbound ? lot.issued_weight_kg : lot.remaining_weight_kg) }}</td>
                <td v-if="isProductionSurplusInbound">{{ formatWeight(lot.returned_weight_kg) }}</td>
                <td v-if="isProductionSurplusInbound">{{ formatWeight(lot.max_return_weight_kg) }}</td>
                <td>
                  <input
                    v-model.number="lot.return_weight_kg"
                    type="number"
                    min="0.001"
                    :max="productionSurplusReturnInputMax(lot)"
                    step="0.001"
                    :disabled="productionSurplusReturnInputDisabled(lot)"
                    :placeholder="productionSurplusReturnInputDisabled(lot) ? '已归还满' : ''"
                    required
                  />
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <div v-else-if="isProductTraceOnlySourceLotInbound && selectedGenericSourceLots.length" class="table-wrap warehouse-paper-source-lot-table">
          <table class="data-table compact-table">
            <thead>
              <tr>
                <th>来源库存批次号</th>
                <th>原材料</th>
                <th>剩余重量</th>
                <th>状态</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="lot in selectedGenericSourceLots" :key="lot.lot_id">
                <td>{{ lot.lot_no }}</td>
                <td>{{ lot.item_code }} · {{ lot.item_name }}</td>
                <td>{{ formatWeight(lot.remaining_weight_kg) }}</td>
                <td><span class="status-pill" :class="lotStatusClass(lot)">{{ formatStatusLabel(lot.status) }}</span></td>
              </tr>
            </tbody>
          </table>
        </div>

        <div v-else-if="isSourceLotSelectedOutbound && selectedGenericSourceLots.length" class="table-wrap warehouse-paper-source-lot-table">
          <table class="data-table compact-table">
            <thead>
              <tr>
                <th>来源库存批次号</th>
                <th>{{ isScrapSaleOutbound ? "废料" : "原材料" }}</th>
                <th>可用重量</th>
                <th>本次出库重量(kg)</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="lot in selectedGenericSourceLots" :key="lot.lot_id">
                <td>{{ lot.lot_no }}</td>
                <td>
                  {{ lot.item_code }} · {{ lot.item_name }}
                  <small v-if="isScrapSaleOutbound && lot.source_stock_lot_no">废料库存批次号：{{ lot.source_stock_lot_no }}</small>
                </td>
                <td>{{ formatWeight(lot.remaining_weight_kg) }}</td>
                <td>
                  <input v-model.number="lot.issued_weight_kg" type="number" min="0.001" :max="Number(lot.remaining_weight_kg || 0)" step="0.001" required />
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <div v-if="usesGenericSourceLotSelector && !isProductTraceOnlySourceLotInbound" class="warehouse-paper-inline-summary">
          {{ isSourceLotSelectedOutbound ? "本次出库总重量" : "本次归还总重量" }}：{{ formatWeight(genericSelectedSourceLotWeight) }}
        </div>
        <div v-else-if="isProductTraceOnlySourceLotInbound && selectedGenericSourceLots.length" class="warehouse-paper-inline-summary">
          已选择来源库存批次号：{{ selectedGenericSourceLots.length }} 个，仅用于来源追溯，不扣减原材料库存
        </div>

        <div class="warehouse-paper-control-grid">
          <label class="warehouse-paper-field warehouse-paper-field-full">
            <span>{{ activeWarehouseType === "AUX" && activeOperation?.bizType === "PRODUCTION_OUT" ? "用途" : "备注" }}</span>
            <textarea
              v-model.trim="genericForm.remark"
              rows="3"
              :required="activeWarehouseType === 'AUX' && activeOperation?.bizType === 'PRODUCTION_OUT'"
            ></textarea>
          </label>
        </div>

        <div v-if="isProductionSurplusInbound || isProductionScrapInbound" class="warehouse-paper-settle-panel">
          <label class="warehouse-paper-settle-toggle">
            <input v-model="genericForm.settle_work_order" type="checkbox" />
            <span class="settle-dot" aria-hidden="true"></span>
            <strong>{{ isProductionScrapInbound ? "该批材料废料结单" : "该批材料余料结单" }}</strong>
          </label>
          <div v-if="genericForm.settle_work_order" class="warehouse-paper-settle-grid">
            <article>
              <span>{{ isProductionScrapInbound ? "标准废料重量" : "标准归还重量" }}</span>
              <strong>{{ formatWeight(isProductionScrapInbound ? productionScrapExpectedWeight : productionSurplusExpectedReturnWeight) }}</strong>
            </article>
            <article>
              <span>{{ isProductionScrapInbound ? "本次后入库废料" : "本次后归还重量" }}</span>
              <strong>{{ formatWeight(isProductionScrapInbound ? productionScrapFinalWeight : productionSurplusFinalReturnWeight) }}</strong>
            </article>
            <article>
              <span>偏差</span>
              <strong :class="{ 'settle-warning': isProductionScrapInbound ? productionScrapSettleNeedsRemark : productionSurplusSettleNeedsRemark }">
                {{ formatPercent(isProductionScrapInbound ? productionScrapSettleDeviationRate : productionSurplusSettleDeviationRate) }}
              </strong>
            </article>
          </div>
          <label
            v-if="genericForm.settle_work_order && (isProductionScrapInbound ? productionScrapSettleNeedsRemark : productionSurplusSettleNeedsRemark)"
            class="warehouse-paper-field warehouse-paper-field-full warehouse-paper-settle-remark"
          >
            <span>{{ isProductionScrapInbound ? "废料结单说明" : "余料结单说明" }}</span>
            <textarea
              v-model.trim="genericForm.settle_remark"
              rows="3"
              required
              :placeholder="isProductionScrapInbound ? '入库废料重量超出标准上下15%时填写现场说明' : '归还重量超出标准上下15%时填写现场说明'"
            ></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation || logisticsPhotoUploading">
            {{ submittingOperation ? "处理中..." : "确认登记" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="specialAdjustmentDrawerOpen"
      :eyebrow="`${activeInventoryLabel} · ${specialAdjustmentDirectionText}`"
      :title="specialAdjustmentDirectionText"
      tag="特殊调整"
      wide
      @close="closeSpecialAdjustmentDrawer"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        :title="`${activeInventoryLabel}${specialAdjustmentDirectionText}单`"
        document-no="保存后生成"
        :business-date="new Date()"
        :tone="specialAdjustmentForm.direction === 'OUT' ? 'red' : 'green'"
        @submit="submitSpecialAdjustment"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="特殊调整信息">
        <div class="document-inline-summary special-adjustment-paper-warning">
          <strong>特殊调整会直接改变库存明细</strong>
          <span>保存后写入库存流水和特殊调整记录，请仅在补录、纠错、线下已处理等特殊场景使用。</span>
        </div>

        <div class="document-control-grid special-adjustment-paper-grid">
          <label class="document-paper-field document-paper-field-wide">
            <span>调整说明</span>
            <textarea
              v-model.trim="specialAdjustmentForm.reason"
              rows="3"
              required
              placeholder="必须填写现场原因、审批依据或补录说明"
            ></textarea>
          </label>
          <label class="document-paper-field">
            <span>仓库</span>
            <select v-model.number="specialAdjustmentForm.warehouse_id" required @change="handleSpecialAdjustmentWarehouseChange">
              <option disabled value="">请选择仓库</option>
              <option v-for="warehouse in activeWarehouses" :key="warehouse.id" :value="warehouse.id">
                {{ warehouse.warehouse_name }}
              </option>
            </select>
          </label>
        </div>

        <div class="document-inline-summary special-adjustment-paper-toolbar">
          <button class="ghost-button" type="button" :disabled="!specialAdjustmentForm.reason.trim()" @click="addSpecialAdjustmentLine">
            添加调整明细
          </button>
          <span v-if="!specialAdjustmentForm.reason.trim()" class="special-adjustment-tip">请先填写调整说明</span>
          <span v-else>每条明细都会写入库存流水，调整说明会同步进入变更记录。</span>
        </div>

        <div class="special-adjustment-paper-lines special-adjustment-lines" :class="{ 'is-empty': !specialAdjustmentForm.lines.length }">
          <article
            v-for="(line, index) in specialAdjustmentForm.lines"
            :key="line.row_key"
            class="special-adjustment-paper-line-card special-adjustment-line-card"
          >
            <header class="special-adjustment-line-head">
              <div>
                <span class="special-adjustment-line-index">明细 {{ index + 1 }}</span>
                <strong>{{ specialAdjustmentItemColumnLabel }}</strong>
              </div>
              <button class="link-button danger" type="button" @click="removeSpecialAdjustmentLine(index)">删除明细</button>
            </header>

            <div class="special-adjustment-card-grid">
              <label v-if="specialAdjustmentForm.direction === 'IN'" class="special-adjustment-card-field">
                <span>入库方式</span>
                <select v-model="line.inbound_mode" required @change="handleSpecialAdjustmentInboundModeChange(line)">
                  <option value="EXISTING">补增已有明细</option>
                  <option value="NEW">新增特殊明细</option>
                </select>
              </label>

              <label
                v-if="specialAdjustmentLineUsesExistingLot(line)"
                class="special-adjustment-card-field special-adjustment-card-field-wide"
              >
                <span>{{ specialAdjustmentForm.direction === "OUT" ? "库存明细" : "补增库存明细" }}</span>
                <select
                  v-model.number="line.lot_id"
                  :required="specialAdjustmentForm.direction === 'OUT' || line.inbound_mode === 'EXISTING'"
                  @change="applySpecialAdjustmentLot(line)"
                >
                  <option disabled value="">{{ specialAdjustmentForm.direction === "OUT" ? "请选择库存明细" : "请选择要补增的库存明细" }}</option>
                  <option v-for="lot in specialAdjustmentLotOptions" :key="lot.lot_id" :value="lot.lot_id">
                    {{ formatSpecialAdjustmentLotOption(lot) }}
                  </option>
                </select>
              </label>

              <label v-else class="special-adjustment-card-field special-adjustment-card-field-wide">
                <span>物料</span>
                <select v-model="line.item_option_key" required @change="applySpecialAdjustmentItem(line)">
                  <option disabled value="">请选择物料</option>
                  <option v-for="item in specialAdjustmentItemOptions" :key="item.option_key || item.item_id" :value="item.option_key || String(item.item_id)">
                    {{ formatSpecialAdjustmentItemOption(item) }}
                  </option>
                </select>
              </label>

              <div v-if="line.item_name" class="special-adjustment-card-field special-adjustment-card-field-wide special-adjustment-selected-note">
                <span>已带入</span>
                <p>
                  {{ line.item_name }}<template v-if="line.lot_no"> · {{ line.lot_no }}</template> · 单价 ¥{{ formatAmount(line.unit_cost) }}
                </p>
              </div>

              <label
                v-if="specialAdjustmentForm.direction === 'IN' && line.inbound_mode === 'NEW'"
                class="special-adjustment-card-field"
              >
                <span>单位成本</span>
                <input v-model.number="line.unit_cost" type="number" min="0" step="0.0001" placeholder="可填写新明细单价" />
              </label>

              <label v-if="specialAdjustmentCanShowMeasureBasis" class="special-adjustment-card-field">
                <span>调整口径</span>
                <select
                  v-if="specialAdjustmentRawMeasureMode(line) === 'SEMI_SELECT'"
                  v-model="line.measure_mode"
                  required
                  @change="handleSpecialAdjustmentMeasureModeChange(line)"
                >
                  <option value="QTY">按数量调整</option>
                  <option value="WEIGHT">按重量调整</option>
                </select>
                <p v-else class="special-adjustment-card-static">{{ formatSpecialAdjustmentMeasureMode(line) }}</p>
              </label>

              <label v-if="specialAdjustmentCanShowQty" class="special-adjustment-card-field">
                <span>调整数量</span>
                <input
                  v-if="specialAdjustmentShowQtyInput(line)"
                  v-model.number="line.adjust_qty"
                  type="number"
                  min="0"
                  step="0.001"
                  :max="specialAdjustmentForm.direction === 'OUT' ? Number(selectedSpecialAdjustmentLot(line)?.remaining_qty || 0) : undefined"
                  required
                  placeholder="请输入数量"
                />
                <p v-else class="special-adjustment-card-static">-</p>
              </label>

              <label v-if="specialAdjustmentCanShowWeight" class="special-adjustment-card-field">
                <span>调整重量</span>
                <input
                  v-if="specialAdjustmentShowWeightInput(line)"
                  v-model.number="line.adjust_weight_kg"
                  type="number"
                  min="0"
                  step="0.001"
                  :max="specialAdjustmentForm.direction === 'OUT' ? Number(selectedSpecialAdjustmentLot(line)?.remaining_weight_kg || 0) : undefined"
                  :required="specialAdjustmentWeightRequired(line)"
                  placeholder="请输入重量"
                />
                <p v-else class="special-adjustment-card-static">-</p>
              </label>

              <label class="special-adjustment-card-field special-adjustment-card-field-wide">
                <span>明细说明</span>
                <input v-model.trim="line.line_reason" type="text" placeholder="可填写该行补充说明" />
              </label>

              <div class="special-adjustment-card-preview">
                <span>调整预览</span>
                <strong>{{ specialAdjustmentLinePreview(line) }}</strong>
              </div>
            </div>
          </article>

          <div v-if="!specialAdjustmentForm.lines.length" class="special-adjustment-empty">
            <strong>还没有调整明细</strong>
            <span>先填写调整说明，再点击“添加调整明细”。每一条明细都会写入库存流水。</span>
          </div>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="specialAdjustmentSubmitting">
            {{ specialAdjustmentSubmitting ? "保存中..." : `确认${specialAdjustmentDirectionText}` }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="rawReturnDrawerOpen"
      eyebrow="原材料库 · 退货出库"
      title="原材料退货出库"
      tag="采购退货"
      wide
      @close="closeRawReturnDrawer"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        title="原材料退货出库单"
        document-no="保存后生成"
        :business-date="new Date()"
        tone="red"
        @submit="submitRawReturnOutbound"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="退货出库信息">
        <div class="warehouse-paper-control-grid raw-return-paper-grid">
          <label class="warehouse-paper-field">
            <span>仓库</span>
            <select v-model.number="rawReturnForm.warehouse_id" required>
              <option disabled value="">请选择仓库</option>
              <option v-for="warehouse in activeWarehouses" :key="warehouse.id" :value="warehouse.id">
                {{ warehouse.warehouse_name }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>库存批次号</span>
            <select v-model.number="rawReturnForm.lot_id" required @change="handleRawReturnLotChange">
              <option disabled value="">请选择可退货批次</option>
              <option v-for="lot in rawReturnLotOptions" :key="lot.lot_id" :value="lot.lot_id">
                {{ lot.lot_no }} · {{ lot.item_code }} · {{ lot.item_name }} · 可退 {{ formatWeight(rawReturnLotAvailableWeight(lot)) }}
              </option>
            </select>
          </label>

          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>采购来源</span>
            <select
              v-model.number="rawReturnForm.purchase_order_item_id"
              required
              :disabled="rawReturnLinkLoading || !rawReturnForm.lot_id"
              @change="handleRawReturnLinkChange"
            >
              <option disabled value="">{{ rawReturnLinkLoading ? "采购来源加载中..." : "请选择采购来源" }}</option>
              <option v-for="link in rawReturnPurchaseLinks" :key="link.purchase_order_item_id" :value="link.purchase_order_item_id">
                {{ formatRawReturnLinkLabel(link) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>退货重量(kg)</span>
            <input
              v-model.number="rawReturnForm.return_weight_kg"
              type="number"
              min="0.001"
              step="0.001"
              required
              :max="rawReturnMaxWeight"
            />
          </label>
        </div>

        <div class="warehouse-paper-control-grid raw-return-logistics-grid">
          <label class="warehouse-paper-field">
            <span>运单号</span>
            <input v-model.trim="rawReturnForm.waybill_no" type="text" placeholder="可填运单号，或上传辅助照片" />
          </label>
          <label class="warehouse-paper-field">
            <span>运费</span>
            <input v-model.number="rawReturnForm.freight_amount" type="number" min="0" step="0.01" placeholder="选填" />
          </label>
          <div class="warehouse-paper-field warehouse-paper-upload-field">
            <span>辅助照片</span>
            <label
              class="warehouse-paper-upload"
              :class="{ 'is-uploaded': rawReturnForm.order_photo_url, 'is-busy': logisticsPhotoUploading }"
            >
              <input
                type="file"
                accept="image/*"
                :disabled="logisticsPhotoUploading"
                @change="handleLogisticsPhotoUpload(rawReturnForm, $event)"
              />
              <b aria-hidden="true">{{ rawReturnForm.order_photo_url ? "✓" : "↑" }}</b>
              <strong>{{ logisticsPhotoUploading ? "上传中..." : rawReturnForm.order_photo_url ? "已上传辅助照片" : "上传辅助照片" }}</strong>
            </label>
          </div>
        </div>

        <div class="warehouse-paper-control-grid raw-return-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>退货原因</span>
            <input v-model.trim="rawReturnForm.reason" type="text" placeholder="例如：质量或性能不合格" />
          </label>

          <label class="warehouse-paper-field warehouse-paper-field-full">
            <span>备注</span>
            <textarea v-model.trim="rawReturnForm.remark" rows="3"></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation || logisticsPhotoUploading || rawReturnLinkLoading">
            {{ submittingOperation ? "出库中..." : "创建退货出库" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="returnInboundDrawerOpen"
      eyebrow="退货库 · 退货入库"
      title="退货入库"
      tag="待返工"
      wide
      @close="closeReturnInboundDrawer"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        title="退货入库单"
        document-no="保存后生成"
        :business-date="new Date()"
        @submit="submitReturnInbound"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="退货入库信息">
        <div class="warehouse-paper-control-grid return-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>发货批次</span>
            <select v-model.number="returnInboundForm.delivery_item_id" required @change="applyReturnInboundDeliveryLine">
              <option disabled value="">请选择发货批次</option>
              <option v-for="line in deliveryLineOptions" :key="line.delivery_item_id" :value="line.delivery_item_id">
                {{ formatReturnDeliveryLineLabel(line) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>退货仓</span>
            <select v-model.number="returnInboundForm.warehouse_id" required>
              <option disabled value="">请选择仓库</option>
              <option v-for="warehouse in activeWarehouses" :key="warehouse.id" :value="warehouse.id">
                {{ warehouse.warehouse_name }}
              </option>
            </select>
          </label>
        </div>

        <div class="document-inline-summary return-trace-summary">
          {{ formatReturnTraceSummary(selectedReturnInboundDeliveryLine) }}
        </div>

        <div class="warehouse-paper-control-grid return-paper-grid">
          <label class="warehouse-paper-field">
            <span>退货数量</span>
            <input
              v-model.number="returnInboundForm.return_qty"
              type="number"
              min="1"
              step="1"
              :max="selectedReturnInboundDeliveryLine ? selectedReturnInboundDeliveryLine.returnable_qty : undefined"
              required
            />
          </label>
          <label class="warehouse-paper-field">
            <span>退货重量(kg)</span>
            <input
              v-model.number="returnInboundForm.return_weight_kg"
              type="number"
              min="0.001"
              step="0.001"
              :max="selectedReturnInboundDeliveryLine ? selectedReturnInboundDeliveryLine.returnable_weight_kg : undefined"
              required
            />
          </label>
          <label class="warehouse-paper-field">
            <span>处理人</span>
            <select v-model.number="returnInboundForm.handler_employee_id">
              <option value="">请选择</option>
              <option v-for="employee in employees" :key="employee.id" :value="employee.id">
                {{ employeeOptionLabel(employee) }}
              </option>
            </select>
          </label>

          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>退货原因</span>
            <input v-model.trim="returnInboundForm.return_reason" type="text" placeholder="例如：客户退回，可返工" />
          </label>
        </div>

        <div class="warehouse-paper-control-grid return-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-full">
            <span>备注</span>
            <textarea v-model.trim="returnInboundForm.remark" rows="3"></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation">
            {{ submittingOperation ? "入库中..." : "创建退货入库" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="returnScrapInboundDrawerOpen"
      eyebrow="废料库 · 退货废料入库"
      title="退货废料入库"
      tag="客户退货报废"
      wide
      @close="closeReturnScrapInboundDrawer"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        title="退货废料入库单"
        document-no="保存后生成"
        :business-date="new Date()"
        @submit="submitReturnScrapInbound"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="退货废料入库信息">
        <div class="warehouse-paper-control-grid return-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>发货批次</span>
            <select v-model.number="returnScrapInboundForm.delivery_item_id" required @change="applyReturnScrapDeliveryLine">
              <option disabled value="">请选择发货批次</option>
              <option v-for="line in deliveryLineOptions" :key="line.delivery_item_id" :value="line.delivery_item_id">
                {{ formatReturnDeliveryLineLabel(line) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>废料仓</span>
            <select v-model.number="returnScrapInboundForm.warehouse_id" required>
              <option disabled value="">请选择仓库</option>
              <option v-for="warehouse in activeWarehouses" :key="warehouse.id" :value="warehouse.id">
                {{ warehouse.warehouse_name }}
              </option>
            </select>
          </label>
        </div>

        <div class="document-inline-summary return-trace-summary">
          {{ formatReturnTraceSummary(selectedReturnScrapDeliveryLine) }}
        </div>

        <div class="warehouse-paper-control-grid return-paper-grid">
          <label class="warehouse-paper-field">
            <span>报废数量</span>
            <input
              v-model.number="returnScrapInboundForm.scrap_qty"
              type="number"
              min="1"
              step="1"
              :max="selectedReturnScrapDeliveryLine ? selectedReturnScrapDeliveryLine.returnable_qty : undefined"
              required
            />
          </label>
          <label class="warehouse-paper-field">
            <span>报废重量(kg)</span>
            <input
              v-model.number="returnScrapInboundForm.scrap_weight_kg"
              type="number"
              min="0.001"
              step="0.001"
              :max="selectedReturnScrapDeliveryLine ? selectedReturnScrapDeliveryLine.returnable_weight_kg : undefined"
              required
            />
          </label>
          <label class="warehouse-paper-field">
            <span>废料单价(元/kg)</span>
            <input v-model.number="returnScrapInboundForm.unit_cost" type="number" min="0" step="0.0001" placeholder="不填则按产品废料单价" />
          </label>

          <label class="warehouse-paper-field">
            <span>处理人</span>
            <select v-model.number="returnScrapInboundForm.handler_employee_id">
              <option value="">请选择</option>
              <option v-for="employee in employees" :key="employee.id" :value="employee.id">
                {{ employeeOptionLabel(employee) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>退货原因</span>
            <input v-model.trim="returnScrapInboundForm.return_reason" type="text" placeholder="例如：客户退回，判定报废" />
          </label>
        </div>

        <div class="warehouse-paper-control-grid return-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-full">
            <span>备注</span>
            <textarea v-model.trim="returnScrapInboundForm.remark" rows="3"></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation">
            {{ submittingOperation ? "入库中..." : "创建退货废料入库" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="openingImportDrawerOpen"
      eyebrow="嘉恒仓库 · 期初导入"
      :title="`${activeInventoryLabel}期初库存导入`"
      tag="库存初始化"
      wide
      @close="closeOpeningImportDrawer"
    >
      <div class="stack-form">
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <div class="import-layout">
          <section class="import-card">
            <strong>导出模版</strong>
            <p>先下载当前仓库类型的期初导入模版，按实际清点库存填写后再导入。</p>
            <button class="primary-button" type="button" :disabled="openingImportBusy" @click="exportOpeningTemplate">
              {{ openingImportBusy ? "处理中..." : `导出${activeInventoryLabel}期初模版` }}
            </button>
          </section>

          <section class="import-card">
            <label class="import-dropzone">
              <input ref="openingImportInputRef" class="hidden-file-input" type="file" accept=".xlsx,.xlsm" @change="importOpeningInventory" />
              <span class="import-icon">XLSX</span>
              <strong>{{ openingImportBusy ? "导入中..." : "上传期初库存 Excel" }}</strong>
              <p>导入会生成期初批次、库存余额和库存流水；批次号重复会被拦截。</p>
            </label>
          </section>
        </div>
      </div>
    </FormDrawer>

    <FormDrawer
      :open="productionDrawerOpen"
      eyebrow="原材料库 · 生产出库"
      title="生产出库"
      description="选择原材料库存批次出库，后续小程序报工按库存批次号推进。"
      tag="生产出库"
      wide
      @close="productionDrawerOpen = false"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        title="生产出库单"
        document-no="保存后生成"
        :business-date="new Date()"
        tone="red"
        novalidate
        @submit="submitProductionOut"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>
        <WarehouseDocumentSection index="01" title="生产出库信息">
        <div class="document-inline-summary production-out-paper-tip">
          选择原材料库存批次出库，后续小程序报工按库存批次号推进。一次生产出库只能选择一个材料库存批次。
        </div>

        <div class="document-control-grid production-out-paper-grid">
          <label class="document-paper-field document-paper-field-wide">
            <span>产品</span>
            <select v-model.number="productionForm.product_item_id" required>
              <option disabled value="">请选择产品</option>
              <option v-for="product in products" :key="product.item_id" :value="product.item_id">
                {{ formatProductLabel(product) }}
              </option>
            </select>
          </label>
        </div>

        <div class="document-selector-panel production-out-source-lot-panel">
          <div class="document-selector-heading">
            <span>库存批次号</span>
            <strong>搜索并选择一个材料库存批次号</strong>
          </div>
          <div class="document-selector-body">
          <StockLotTagSelect
            v-model="selectedProductionStockLots"
            :options="productionStockLotOptions"
            :max-selections="1"
            placeholder="搜索并选择一个材料库存批次号"
          />
          </div>
        </div>

        <div v-if="selectedProductionStockLots.length" class="table-wrap document-source-table production-out-source-lot-table">
          <table class="data-table compact-table">
            <thead>
              <tr>
                <th>{{ activeWarehouseType === "SEMI" ? "来源库存批次号" : "库存批次号" }}</th>
                <th>原材料</th>
                <th>剩余重量</th>
                <th>单位成本</th>
                <th>本次出库重量(kg)</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="lot in selectedProductionStockLots" :key="lot.lot_id">
                <td>{{ lot.lot_no }}</td>
                <td>{{ lot.item_name }}</td>
                <td>{{ formatWeight(lot.remaining_weight_kg) }}</td>
                <td>¥{{ formatAmount(lot.unit_cost) }}/kg</td>
                <td>
                  <input
                    v-model.number="lot.issued_weight_kg"
                    type="number"
                    min="0.001"
                    step="0.001"
                    :max="Number(lot.remaining_weight_kg || 0)"
                    required
                  />
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <div class="document-control-grid production-out-paper-grid">
          <label class="document-paper-field">
            <span>本次生产出库重量(kg)</span>
            <input
              v-model.number="productionForm.issue_weight_kg"
              type="number"
              min="0.001"
              step="0.001"
              required
              :readonly="selectedProductionStockLots.length > 0"
            />
          </label>
          <label class="document-paper-field">
            <span>参考生产数量</span>
            <input :value="formatQty(referenceProductionQty)" type="text" readonly />
          </label>
        </div>

        <div class="feedback-banner">
          单件毛重：{{ selectedBomGrossWeight ? formatWeight(selectedBomGrossWeight) : "未配置" }}；参考生产数量 = 本次生产出库重量 ÷ 单件毛重。
        </div>

        <div class="document-control-grid production-out-paper-grid production-out-paper-remark">
          <label class="document-paper-field document-paper-field-full">
            <span>备注</span>
            <textarea v-model.trim="productionForm.remark" rows="3"></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation">
            {{ submittingOperation ? "出库中..." : "创建生产出库" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="completionDrawerOpen"
      :eyebrow="isReworkCompletionOperation ? '成品库 · 返工入库' : '成品库 · 生产入库'"
      :title="isReworkCompletionOperation ? '新增返工入库' : '新增生产入库'"
      :tag="isReworkCompletionOperation ? '返工入库' : '成品入库'"
      wide
      @close="completionDrawerOpen = false"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        :title="isReworkCompletionOperation ? '返工入库单' : '生产入库单'"
        document-no="保存后生成"
        :business-date="new Date()"
        @submit="submitCompletionInbound"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" :title="isReworkCompletionOperation ? '返工入库信息' : '生产入库信息'">
        <div class="warehouse-paper-control-grid completion-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>{{ isReworkCompletionOperation ? "返工工单" : "生产工单" }}</span>
            <select v-model.number="completionForm.work_order_id" required @change="applyCompletionWorkOrder">
              <option disabled value="">{{ isReworkCompletionOperation ? "请选择返工工单" : "请选择生产工单" }}</option>
              <option v-for="workOrder in completionWorkOrderOptions" :key="workOrder.work_order_id" :value="workOrder.work_order_id">
                {{ workOrder.work_order_no }} · {{ workOrder.product_name }} · 可入库 {{ formatQty(workOrder.limits?.max_finished_inbound_qty) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>成品仓</span>
            <select v-model.number="completionForm.warehouse_id" required>
              <option disabled value="">请选择仓库</option>
              <option v-for="warehouse in finishedWarehouses" :key="warehouse.id" :value="warehouse.id">
                {{ warehouse.warehouse_name }}
              </option>
            </select>
          </label>

          <label class="warehouse-paper-field">
            <span>接收人</span>
            <select v-model.number="completionForm.receiver_employee_id">
              <option value="">请选择</option>
              <option v-for="employee in employees" :key="employee.id" :value="employee.id">
                {{ employeeOptionLabel(employee) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>产品</span>
            <input :value="completionProductLabel" type="text" disabled />
          </label>
          <label class="warehouse-paper-field">
            <span>库位</span>
            <select v-model.number="completionForm.location_id">
              <option value="">默认库位</option>
              <option v-for="location in completionLocations" :key="location.id" :value="location.id">
                {{ location.location_name }}
              </option>
            </select>
          </label>
        </div>

        <div class="warehouse-paper-control-grid completion-paper-grid">
          <label class="warehouse-paper-field">
            <span>计算最大可入库数量</span>
            <input :value="formatQty(completionForm.max_receivable_qty)" type="text" disabled />
          </label>
          <label class="warehouse-paper-field">
            <span>入库数量</span>
            <input
              v-model.number="completionForm.receipt_qty"
              type="number"
              min="1"
              step="1"
              :max="completionForm.tolerance_receivable_qty || undefined"
              required
              @input="recalculateCompletionWeight"
            />
          </label>
          <label class="warehouse-paper-field">
            <span>入库重量(kg)</span>
            <input v-model.number="completionForm.receipt_weight_kg" type="number" min="0" step="0.001" readonly />
          </label>
        </div>

        <div class="warehouse-paper-control-grid completion-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-full">
            <span>备注</span>
            <textarea v-model.trim="completionForm.remark" rows="3"></textarea>
          </label>
        </div>

        <div class="warehouse-paper-settle-panel completion-settle-panel">
          <label
            class="warehouse-paper-settle-toggle"
            :class="{ 'is-disabled': !completionForm.work_order_id }"
            :title="completionForm.work_order_id ? '勾选后保存时结单' : '选择生产工单后可勾选结单'"
          >
            <input v-model="completionForm.settle_work_order" type="checkbox" :disabled="!completionForm.work_order_id" />
            <span class="settle-dot" aria-hidden="true"></span>
            <strong>该工单结单</strong>
          </label>
          <div v-if="completionForm.settle_work_order" class="warehouse-paper-settle-grid">
            <article>
              <span>标准成品数量</span>
              <strong>{{ formatQty(completionExpectedFinishedQty) }}</strong>
            </article>
            <article>
              <span>本次后入库成品</span>
              <strong>{{ formatQty(completionFinalFinishedQty) }}</strong>
            </article>
            <article>
              <span>偏差</span>
              <strong :class="{ 'settle-warning': completionSettleNeedsRemark }">
                {{ formatPercent(completionSettleDeviationRate) }}
              </strong>
            </article>
          </div>
          <label v-if="completionForm.settle_work_order && completionSettleNeedsRemark" class="warehouse-paper-field warehouse-paper-field-full warehouse-paper-settle-remark">
            <span>结单说明</span>
            <textarea
              v-model.trim="completionForm.settle_remark"
              rows="3"
              required
              placeholder="入库成品数量超出标准上下15%时填写现场说明"
            ></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation">
            {{ submittingOperation ? "过账中..." : "创建生产入库单" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="deliveryDrawerOpen"
      eyebrow="成品库 · 销售出库"
      title="新增销售出库"
      tag="销售订单发货"
      wide
      @close="deliveryDrawerOpen = false"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        title="销售出库单"
        document-no="保存后生成"
        :business-date="new Date()"
        tone="red"
        @submit="submitSalesOut"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="销售出库信息">
        <div class="workflow-mode-switch delivery-mode-switch sales-delivery-mode-panel" role="tablist" aria-label="销售出库方式">
          <button
            class="workflow-mode-option"
            :class="{ active: isSalesOrderDelivery }"
            type="button"
            role="tab"
            :aria-selected="isSalesOrderDelivery"
            title="选择已有销售订单，发货后回写订单已发数量。"
            @click='setDeliveryMode("SALES_ORDER")'
          >
            <span class="mode-option-dot" aria-hidden="true"></span>
            <strong>销售订单发货</strong>
            <small>回写订单</small>
          </button>
          <button
            class="workflow-mode-option"
            :class="{ active: isDirectCustomerDelivery }"
            type="button"
            role="tab"
            :aria-selected="isDirectCustomerDelivery"
            title="不绑定销售订单，直接按客户和产品从成品库出库。"
            @click='setDeliveryMode("DIRECT_CUSTOMER")'
          >
            <span class="mode-option-dot" aria-hidden="true"></span>
            <strong>直接客户发货</strong>
            <small>直接出库</small>
          </button>
        </div>

        <div v-if="deliveryForm.delivery_mode === 'SALES_ORDER'" class="warehouse-paper-control-grid sales-delivery-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>关联销售订单</span>
            <select v-model.number="deliveryForm.sales_order_id" required @change="applySalesOrderDefaults">
              <option disabled value="">请选择销售订单</option>
              <option v-for="order in openSalesOrders" :key="order.order_id" :value="order.order_id">
                {{ order.order_no }} · {{ order.customer_name }} · 未发 {{ formatQty(orderPendingQty(order.order_id)) }} · {{ orderPendingProductSummary(order.order_id) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>销售订单明细</span>
            <select v-model.number="deliveryForm.sales_order_item_id" required @change="applySalesOrderItemDefaults">
              <option disabled value="">请选择订单明细</option>
              <option v-for="item in selectableSalesOrderItems" :key="item.sales_order_item_id" :value="item.sales_order_item_id">
                {{ item.product_code }} · {{ item.product_name }} · 未发 {{ formatQty(orderItemPendingQty(item)) }}
              </option>
            </select>
          </label>
        </div>

        <div class="warehouse-paper-control-grid sales-delivery-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>发货产品</span>
            <select v-model.number="deliveryForm.product_item_id" required :disabled="isSalesOrderDelivery && Boolean(deliveryForm.sales_order_item_id)">
              <option disabled value="">请选择产品</option>
              <option v-for="product in products" :key="product.item_id" :value="product.item_id">
                {{ product.item_code }} · {{ product.item_name }}{{ product.version_no ? ` · ${product.version_no}` : "" }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>发货仓</span>
            <select v-model.number="deliveryForm.warehouse_id" required>
              <option disabled value="">请选择仓库</option>
              <option v-for="warehouse in finishedWarehouses" :key="warehouse.id" :value="warehouse.id">
                {{ warehouse.warehouse_name }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>发货数量</span>
            <input
              v-model.number="deliveryForm.delivery_qty"
              type="number"
              min="1"
              step="1"
              required
              :max="isSalesOrderDelivery && selectedSalesOrderItem ? orderItemPendingQty(selectedSalesOrderItem) : undefined"
            />
          </label>

          <label class="warehouse-paper-field">
            <span>收货客户</span>
            <select v-model.number="deliveryForm.customer_id" required :disabled="isSalesOrderDelivery" @change="applyDeliveryCustomerDefaults">
              <option disabled value="">请选择客户</option>
              <option v-for="customer in customers" :key="customer.id" :value="customer.id">
                {{ customer.customer_name }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>发货人</span>
            <select v-model.number="deliveryForm.shipper_employee_id">
              <option value="">请选择</option>
              <option v-for="employee in employees" :key="employee.id" :value="employee.id">
                {{ employeeOptionLabel(employee) }}
              </option>
            </select>
          </label>
          <label class="warehouse-paper-field">
            <span>收货人</span>
            <input v-model.trim="deliveryForm.consignee_name" type="text" />
          </label>

          <label class="warehouse-paper-field">
            <span>联系电话</span>
            <input v-model.trim="deliveryForm.consignee_phone" type="text" />
          </label>
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>送货地址</span>
            <input v-model.trim="deliveryForm.delivery_address" type="text" />
          </label>
        </div>

        <div class="warehouse-paper-control-grid sales-delivery-paper-grid">
          <label class="warehouse-paper-field">
            <span>运单号</span>
            <input v-model.trim="deliveryForm.waybill_no" type="text" placeholder="可填运单号，或上传辅助照片" />
          </label>
          <label class="warehouse-paper-field">
            <span>运费</span>
            <input v-model.number="deliveryForm.freight_amount" type="number" min="0" step="0.01" required />
          </label>
          <div class="warehouse-paper-field warehouse-paper-upload-field">
            <span>辅助照片</span>
            <label
              class="warehouse-paper-upload"
              :class="{ 'is-uploaded': deliveryForm.order_photo_url, 'is-busy': logisticsPhotoUploading }"
            >
              <input
                type="file"
                accept="image/*"
                :disabled="logisticsPhotoUploading"
                @change="handleLogisticsPhotoUpload(deliveryForm, $event)"
              />
              <b aria-hidden="true">{{ deliveryForm.order_photo_url ? "✓" : "↑" }}</b>
              <strong>{{ logisticsPhotoUploading ? "上传中..." : deliveryForm.order_photo_url ? "已上传辅助照片" : "上传辅助照片" }}</strong>
            </label>
          </div>

          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>包装备注</span>
            <input v-model.trim="deliveryForm.remark" type="text" placeholder="有无提供篮子等" />
          </label>
        </div>

        <div class="document-inline-summary sales-delivery-allocation-summary">
          <strong>自动销售出库明细</strong>
          <span class="panel-tag">可发 {{ formatQty(totalAvailableQty) }}，本次拆分 {{ formatQty(totalAllocatedQty) }}</span>
        </div>
        <div class="table-wrap warehouse-paper-source-lot-table sales-delivery-allocation-table">
          <table class="data-table compact-table">
            <thead>
              <tr>
                <th>成品库存批次号</th>
                <th>来源原材料库存批次号</th>
                <th>来源材料摘要</th>
                <th>可发数量</th>
                <th>本次发货</th>
                <th>重量</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="item in allocationRows" :key="item.lot_id">
                <td>{{ item.lot_no }}</td>
                <td>{{ item.source_material_sub_batch_no || item.source_material_lot_no || "-" }}</td>
                <td :title="item.source_material_summary || ''">{{ item.source_material_summary || "-" }}</td>
                <td>{{ formatQty(item.remaining_qty) }}</td>
                <td>{{ formatQty(item.delivery_qty) }}</td>
                <td>{{ formatWeight(item.delivery_weight_kg) }}</td>
              </tr>
              <tr v-if="!allocationRows.length">
                <td colspan="6" class="empty-row">{{ isSalesOrderDelivery ? "选择销售订单和发货数量后显示拆分明细" : "选择客户、产品和发货数量后显示拆分明细" }}</td>
              </tr>
            </tbody>
          </table>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation || logisticsPhotoUploading">
            {{ submittingOperation ? "出库中..." : "创建销售出库单" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="returnReworkDrawerOpen"
      eyebrow="退货库 · 返工出库"
      title="返工出库"
      tag="退货返工"
      wide
      @close="closeReturnReworkDrawer"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        title="返工出库单"
        document-no="保存后生成"
        :business-date="new Date()"
        tone="red"
        @submit="submitReturnReworkOutbound"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <WarehouseDocumentSection index="01" title="返工出库信息">
        <div class="warehouse-paper-control-grid return-rework-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-wide">
            <span>待返工退货明细</span>
            <select v-model.number="returnReworkForm.return_item_id" required>
              <option disabled value="">请选择待返工退货明细</option>
              <option v-for="item in pendingReturnItems" :key="item.return_item_id" :value="item.return_item_id">
                {{ formatReturnReworkItemLabel(item) }}
              </option>
            </select>
          </label>
        </div>

        <div class="document-inline-summary return-rework-trace-summary">
          {{ selectedReturnReworkItem ? formatReturnTraceSummary(selectedReturnReworkItem) : "请选择退货明细，系统会按退货库锁定批次办理返工出库。" }}
        </div>

        <div class="warehouse-paper-control-grid return-rework-paper-grid">
          <label class="warehouse-paper-field">
            <span>额外人工成本</span>
            <input v-model.number="returnReworkForm.extra_cost_amount" type="number" min="0" step="0.01" />
          </label>
          <label class="warehouse-paper-field">
            <span>返工重量(kg)</span>
            <input :value="selectedReturnReworkItem ? formatWeight(selectedReturnReworkItem.return_weight_kg) : '-'" type="text" disabled />
          </label>
        </div>

        <div class="warehouse-paper-control-grid return-rework-paper-grid">
          <label class="warehouse-paper-field warehouse-paper-field-full">
            <span>备注</span>
            <textarea v-model.trim="returnReworkForm.remark" rows="3"></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation || !pendingReturnItems.length">
            {{ submittingOperation ? "出库中..." : "确认返工出库" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <FormDrawer
      :open="warehouseLedgerDrawerOpen"
      eyebrow="嘉恒仓库 · 库级流水"
      :title="`${activeInventoryLabel} · 出入库流水`"
      tag="库存流水"
      wide
      @close="closeWarehouseLedgerDrawer"
    >
      <div class="stack-form warehouse-ledger-drawer">
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>

        <div class="warehouse-ledger-summary">
          <article class="warehouse-ledger-summary-card">
            <span>入库笔数</span>
            <strong>{{ warehouseLedgerSummary.in_count || 0 }}</strong>
          </article>
          <article class="warehouse-ledger-summary-card">
            <span>出库笔数</span>
            <strong>{{ warehouseLedgerSummary.out_count || 0 }}</strong>
          </article>
          <article class="warehouse-ledger-summary-card">
            <span>{{ activeQuantityOnly ? "入库数量" : "入库重量" }}</span>
            <strong>{{ activeQuantityOnly ? formatQty(warehouseLedgerSummary.in_qty || 0) : formatWeight(warehouseLedgerSummary.in_weight_kg || 0) }}</strong>
          </article>
          <article class="warehouse-ledger-summary-card">
            <span>{{ activeQuantityOnly ? "出库数量" : "出库重量" }}</span>
            <strong>{{ activeQuantityOnly ? formatQty(warehouseLedgerSummary.out_qty || 0) : formatWeight(warehouseLedgerSummary.out_weight_kg || 0) }}</strong>
          </article>
          <article class="warehouse-ledger-summary-card">
            <span>最近流水</span>
            <strong>{{ warehouseLedgerSummary.latest_biz_time ? formatDateTime(warehouseLedgerSummary.latest_biz_time) : "-" }}</strong>
          </article>
        </div>

        <div class="warehouse-ledger-filters">
          <div class="warehouse-ledger-filter-fields">
            <label class="form-field">
              <span>搜索</span>
              <input v-model.trim="warehouseLedgerFilters.keyword" type="text" placeholder="搜索物料、批次、流水类型、来源、运单号、备注" @keyup.enter="loadWarehouseLedger" />
            </label>
            <label class="form-field">
              <span>方向</span>
              <select v-model="warehouseLedgerFilters.direction" @change="loadWarehouseLedger">
                <option value="ALL">全部</option>
                <option value="IN">入库</option>
                <option value="OUT">出库</option>
                <option value="ADJUST">调整</option>
              </select>
            </label>
            <label class="form-field">
              <span>业务类型</span>
              <input v-model.trim="warehouseLedgerFilters.txn_type" type="text" placeholder="输入流水类型中文名，如销售出库、退货入库" @keyup.enter="loadWarehouseLedger" />
            </label>
            <label class="form-field">
              <span>开始日期</span>
              <input v-model="warehouseLedgerFilters.start_date" type="date" @change="loadWarehouseLedger" />
            </label>
            <label class="form-field">
              <span>结束日期</span>
              <input v-model="warehouseLedgerFilters.end_date" type="date" @change="loadWarehouseLedger" />
            </label>
          </div>
          <div class="warehouse-ledger-filter-actions">
            <button class="ghost-button warehouse-ledger-reset-button" type="button" :disabled="warehouseLedgerLoading" @click="resetWarehouseLedgerFilters">
              重置
            </button>
            <button class="primary-button warehouse-ledger-query-button" type="button" :disabled="warehouseLedgerLoading" @click="loadWarehouseLedger">
              {{ warehouseLedgerLoading ? "查询中..." : "查询" }}
            </button>
            <button class="secondary-button warehouse-ledger-export-button" type="button" :disabled="warehouseLedgerLoading || warehouseLedgerExporting" @click="exportWarehouseLedgerExcel">
              {{ warehouseLedgerExporting ? "导出中..." : "导出Excel" }}
            </button>
          </div>
        </div>

        <div class="table-wrap warehouse-ledger-table-wrap">
          <table ref="warehouseLedgerTableRef" class="data-table compact-table warehouse-ledger-table">
            <colgroup>
              <col class="ledger-col-time" />
              <col class="ledger-col-direction" />
              <col class="ledger-col-type" />
              <col class="ledger-col-item" />
              <col class="ledger-col-lot" />
              <col v-if="!activeWeightOnly" class="ledger-col-qty" />
              <col v-if="!activeQuantityOnly" class="ledger-col-weight" />
              <col class="ledger-col-price" />
              <col class="ledger-col-amount" />
              <col class="ledger-col-source" />
              <col class="ledger-col-operator" />
              <col class="ledger-col-logistics" />
              <col class="ledger-col-remark" />
              <col class="ledger-col-archive" />
            </colgroup>
            <thead>
              <tr>
                <th>时间</th>
                <th>方向</th>
                <th>类型</th>
                <th>物料</th>
                <th>库存批次号</th>
                <th v-if="!activeWeightOnly">变动数量</th>
                <th v-if="!activeQuantityOnly">变动重量(kg)</th>
                <th>单价</th>
                <th>金额</th>
                <th>来源</th>
                <th>经办人</th>
                <th>运单/照片</th>
                <th>备注</th>
                <th>单据留档</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="row in warehouseLedgerRows" :key="row.inventory_txn_id">
                <td v-bind="warehouseLedgerTooltipAttrs(formatDateTime(row.biz_time))">{{ formatDateTime(row.biz_time) }}</td>
                <td><span class="status-pill" :class="warehouseLedgerDirectionClass(row.direction)">{{ formatWarehouseLedgerDirection(row.direction) }}</span></td>
                <td v-bind="warehouseLedgerTooltipAttrs(formatInventoryTxnTypeLabel(row.txn_type))">{{ formatInventoryTxnTypeLabel(row.txn_type) }}</td>
                <td v-bind="warehouseLedgerTooltipAttrs(formatWarehouseLedgerItemLabel(row))">{{ formatWarehouseLedgerItemLabel(row) }}</td>
                <td v-bind="warehouseLedgerTooltipAttrs(formatWarehouseLedgerLotNo(row))">{{ formatWarehouseLedgerLotNo(row) }}</td>
                <td v-if="!activeWeightOnly">{{ formatQty(row.qty_change) }}</td>
                <td v-if="!activeQuantityOnly">{{ formatWeight(row.weight_change_kg) }}</td>
                <td>¥{{ formatAmount(row.unit_cost) }}</td>
                <td>¥{{ formatAmount(row.amount) }}</td>
                <td v-bind="warehouseLedgerTooltipAttrs(formatWarehouseLedgerSourceLabel(row))">{{ formatWarehouseLedgerSourceLabel(row) }}</td>
                <td v-bind="warehouseLedgerTooltipAttrs(row.operator_name || '-')">{{ row.operator_name || "-" }}</td>
                <td v-bind="warehouseLedgerTooltipAttrs(formatWarehouseLedgerLogisticsLabel(row))">
                  <span>{{ row.logistics_waybill_no || "-" }}</span>
                  <button v-if="row.logistics_photo_url" class="link-button" type="button" @click="openLogisticsPhoto(row.logistics_photo_url)">查看</button>
                </td>
                <td class="warehouse-ledger-remark-cell" v-bind="warehouseLedgerTooltipAttrs(row.remark || '-')">{{ row.remark || "-" }}</td>
                <td class="warehouse-ledger-archive-cell">
                  <DocumentArchiveActions
                    :status="row.archive_status || '未生成'"
                    @preview="previewWarehouseLedgerArchive(row)"
                    @download="downloadWarehouseLedgerArchive(row)"
                    @regenerate="regenerateWarehouseLedgerArchive(row)"
                  />
                </td>
              </tr>
              <tr v-if="!warehouseLedgerRows.length">
                <td :colspan="warehouseLedgerEmptyColspan" class="empty-row">
                  {{ warehouseLedgerLoading ? "流水加载中..." : `暂无${activeInventoryLabel}出入库流水` }}
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <PaginationBar
          v-model:page="warehouseLedgerPage"
          v-model:page-size="warehouseLedgerPageSize"
          :total="warehouseLedgerTotal"
        />
      </div>
    </FormDrawer>

    <FormDrawer
      :open="productionWorkOrderInboundDrawerOpen"
      eyebrow="嘉恒仓库 · 生产台账入库"
      title="生产台账入库"
      description="按在生产的材料库存批次号统一办理成品入库、生产余料入库和生产废料入库。"
      tag="材料批次闭环"
      wide
      @close="closeProductionWorkOrderInboundDrawer"
    >
      <WarehouseDocumentFormShell
        class="warehouse-document-operation-form"
        title="生产台账入库结算单"
        document-no="保存后生成"
        :business-date="new Date()"
        @submit="submitProductionWorkOrderInbound"
      >
        <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
        <div v-if="errorMessage" class="feedback-banner error-banner">{{ errorMessage }}</div>
        <WarehouseDocumentSection index="01" title="生产台账入库结算信息">
        <div class="document-inline-summary production-ledger-inbound-tip">
          选择生产台账后，系统按领料重量、单件毛重和已入库成品数量填入理论余料、理论废料。理论值只是辅助参考，实际称重可修改；偏差超过上下15%时需要填写说明。
        </div>

        <div class="document-control-grid production-ledger-inbound-paper-grid">
          <label class="document-paper-field document-paper-field-wide">
            <span>生产台账</span>
            <select v-model.number="selectedInboundWorkOrderId" required @change="loadProductionWorkOrderInboundPreview">
              <option disabled value="">请选择在生产的生产台账</option>
              <option v-for="workOrder in openProductionWorkOrders" :key="workOrder.work_order_id" :value="workOrder.work_order_id">
                {{ formatProductionWorkOrderInboundOption(workOrder) }}
              </option>
            </select>
          </label>
        </div>

        <div v-if="productionWorkOrderInboundPreview" class="warehouse-paper-summary-grid production-ledger-inbound-summary">
          <article class="warehouse-paper-summary-card warehouse-paper-summary-card-wide"><span>生产台账</span><strong>{{ productionWorkOrderInboundPreview.material_lot_no }} · {{ productionWorkOrderInboundPreview.product_name }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>产品</span><strong>{{ productionWorkOrderInboundPreview.product_name }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>材料库存批次号</span><strong>{{ productionWorkOrderInboundPreview.material_lot_no }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>累计领料重量</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.total_issued_weight_kg) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>库外未闭环重量</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.outside_weight_kg) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>参考成品数量</span><strong>{{ formatQty(productionWorkOrderInboundPreview.reference_finished_qty) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>单件毛重</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.gross_weight_per_piece_kg) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>单件净重</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.net_weight_per_piece_kg) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>已入库成品</span><strong>{{ formatQty(productionWorkOrderInboundPreview.finished_inbound_qty) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>已入库废料</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.scrap_inbound_weight_kg) }}</strong></article>
        </div>

        <div class="document-control-grid production-ledger-inbound-paper-grid">
          <label class="document-paper-field">
            <span>成品入库数量</span>
            <input
              v-model.number="productionWorkOrderInboundForm.finished_qty"
              type="number"
              min="0"
              step="1"
              @input="handleProductionWorkOrderInboundFinishedQtyInput"
            />
          </label>
          <label class="document-paper-field">
            <span>余料入库重量(kg)</span>
            <input v-model.number="productionWorkOrderInboundForm.surplus_weight_kg" type="number" min="0" step="0.001" />
          </label>
          <label class="document-paper-field">
            <span>废料重量(kg)</span>
            <input v-model.number="productionWorkOrderInboundForm.scrap_weight_kg" type="number" min="0" step="0.001" />
          </label>
        </div>

        <div v-if="productionWorkOrderInboundPreview" class="warehouse-paper-summary-grid production-ledger-inbound-metrics">
          <article class="warehouse-paper-summary-card"><span>理论余料</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.theoretical_surplus_weight_kg) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>理论废料</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.theoretical_scrap_weight_kg) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>已归还余料</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.surplus_inbound_weight_kg) }}</strong></article>
          <article class="warehouse-paper-summary-card"><span>库外未闭环重量</span><strong>{{ formatWeight(productionWorkOrderInboundPreview.outside_weight_kg) }}</strong></article>
        </div>

        <div class="warehouse-paper-settle-panel production-ledger-inbound-settle-panel">
          <label class="warehouse-paper-settle-toggle">
            <input v-model="productionWorkOrderInboundForm.settle_work_order" type="checkbox" />
            <span class="settle-dot" aria-hidden="true"></span>
            <strong>本次入库后该批材料结单</strong>
          </label>
        </div>
        <div class="document-control-grid production-ledger-inbound-paper-grid production-ledger-inbound-remark">
          <label class="document-paper-field document-paper-field-full">
            <span>备注</span>
            <textarea v-model.trim="productionWorkOrderInboundForm.remark" rows="3"></textarea>
          </label>
        </div>

        </WarehouseDocumentSection>

        <WarehouseDocumentActionBar>
          <button class="primary-button" type="submit" :disabled="submittingOperation">
            {{ submittingOperation ? "保存中..." : "保存生产台账入库" }}
          </button>
        </WarehouseDocumentActionBar>
      </WarehouseDocumentFormShell>
    </FormDrawer>

    <StocktakeDialog
      :open="stocktakeDialogOpen"
      :warehouses="warehouses"
      :default-warehouse-id="activeWarehouseId"
      @close="closeStocktakeDialog"
      @changed="loadAll"
      @message="handleStocktakeMessage"
      @error="handleStocktakeError"
    />
  </section>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";

import AppIcon from "../components/AppIcon.vue";
import DocumentArchiveActions from "../components/documentForms/DocumentArchiveActions.vue";
import WarehouseDocumentActionBar from "../components/documentForms/WarehouseDocumentActionBar.vue";
import WarehouseDocumentFormShell from "../components/documentForms/WarehouseDocumentFormShell.vue";
import WarehouseDocumentSection from "../components/documentForms/WarehouseDocumentSection.vue";
import DrawerDataTable from "../components/DrawerDataTable.vue";
import FormDrawer from "../components/FormDrawer.vue";
import PaginationBar from "../components/PaginationBar.vue";
import StockLotTagSelect from "../components/StockLotTagSelect.vue";
import StocktakeDialog from "../components/StocktakeDialog.vue";
import TableControls from "../components/TableControls.vue";
import { useBodyScrollLock } from "../composables/useBodyScrollLock";
import { downloadResource, fetchResource, openResource, postResource, requestJson, uploadResource } from "../services/api";
import { PERSONNEL_PERMISSION_CODES, employeeOptionLabel, fetchPermissionEmployeeOptions } from "../services/employeeOptions";
import {
  formatInventoryTxnTypeLabel,
  formatItemTypeLabel,
  formatProductLabel,
  formatQualityStatusLabel,
  formatSourceDocTypeLabel,
  formatStatusLabel,
  formatWarehouseTypeLabel
} from "../utils/dictionaries";
import { formatAmount, formatDateTime, formatQty, formatWeight } from "../utils/formatters";
import { buildGenericItemOptions, buildProductOperationOptions } from "../utils/inventoryOperationOptions";
import { usePagination } from "../utils/pagination";
import { formatReturnDeliveryLineLabel, formatReturnReworkItemLabel, formatReturnTraceSummary } from "../utils/returnTraceFormatters";
import { specialAdjustmentDirectionLabel, specialAdjustmentMeasureMode } from "../utils/specialAdjustmentRules";
import { useTableControls } from "../utils/tableControls";

const WAREHOUSE_TYPE_BY_TAB = {
  raw: "RAW",
  semi: "SEMI",
  finished: "FINISHED",
  auxiliary: "AUX",
  scrap: "SCRAP",
  return: "RETURN"
};

const WAREHOUSE_LABEL_BY_TAB = {
  raw: "原材料库",
  semi: "半成品库",
  finished: "成品库",
  auxiliary: "辅料库",
  scrap: "废料库",
  return: "退货库"
};

const SEMI_MEASURE_BASIS_LABELS = {
  PIECE: "按件",
  WEIGHT: "按重",
  MIXED: "历史混合"
};

const LOGISTICS_REQUIRED_BIZ_TYPES = new Set([
  "CUSTOMER_SUPPLIED",
  "OUTSOURCING_SURPLUS",
  "OUTSOURCING_IN",
  "OUTSOURCING_SCRAP_IN",
  "OUTSOURCING_OUT",
  "SCRAP_SALE_OUT"
]);
const SOURCE_LOT_RETURN_INBOUND_BIZ_TYPES = new Set(["PRODUCTION_SURPLUS", "OUTSOURCING_SURPLUS"]);
const SOURCE_LOT_SELECTED_OUTBOUND_BIZ_TYPES = new Set(["OUTSOURCING_OUT", "SCRAP_OUT", "SCRAP_SALE_OUT"]);
const WAREHOUSE_OPERATION_DOCUMENT_TITLES = {
  OPENING: "期初入库单",
  CUSTOMER_SUPPLIED: "客料入库单",
  PRODUCTION_SURPLUS: "生产余料入库单",
  OUTSOURCING_SURPLUS: "委外余料入库单",
  PRODUCTION_OUT: "生产出库单",
  OUTSOURCING_OUT: "委外出库单",
  PURCHASE_RETURN_OUT: "退货出库单",
  SCRAP_OUT: "报废出库单",
  WIP_IN: "产中入库单",
  WIP_OUT: "产中出库单",
  OUTSOURCING_IN: "委外入库单",
  PRODUCTION_FINISHED: "生产入库单",
  FG_IN: "生产入库单",
  REWORK_FINISHED_IN: "返工入库单",
  SALES_OUT: "销售出库单",
  PRODUCTION_SCRAP_IN: "生产废料入库单",
  REWORK_SCRAP_IN: "返工废料入库单",
  OUTSOURCING_SCRAP_IN: "委外废料入库单",
  RETURN_SCRAP_IN: "退货废料入库单",
  SCRAP_SALE_OUT: "售卖出库单",
  RETURN_IN: "退货入库单",
  RETURN_OUT: "返工出库单",
  SPECIAL_IN: "特殊入库单",
  SPECIAL_OUT: "特殊出库单"
};

const lots = ref([]);
const balances = ref([]);
const transactions = ref([]);
const materials = ref([]);
const products = ref([]);
const boms = ref([]);
const processRoutes = ref([]);
const workOrders = ref([]);
const workOrderLedgerRows = ref([]);
const productionLedgerRows = ref([]);
const productionStockLotOptions = ref([]);
const selectedProductionStockLots = ref([]);
const genericSourceLotOptions = ref([]);
const selectedGenericSourceLots = ref([]);
const rawReturnPurchaseLinks = ref([]);
const warehouses = ref([]);
const locations = ref([]);
const employees = ref([]);
const customers = ref([]);
const salesOrders = ref([]);
const salesOrderItems = ref([]);
const deliveries = ref([]);
const deliveryItems = ref([]);
const returnItems = ref([]);
const selectedItemId = ref(null);
const selectedBalance = ref(null);
const activeInventoryTab = ref("raw");
const activeOperation = ref(null);
const detailDrawerOpen = ref(false);
const expandedLotRows = ref([]);
const expandedTxnRows = ref([]);
const genericDrawerOpen = ref(false);
const specialAdjustmentDrawerOpen = ref(false);
const rawReturnDrawerOpen = ref(false);
const productionDrawerOpen = ref(false);
const completionDrawerOpen = ref(false);
const deliveryDrawerOpen = ref(false);
const returnInboundDrawerOpen = ref(false);
const returnScrapInboundDrawerOpen = ref(false);
const returnReworkDrawerOpen = ref(false);
const stocktakeDialogOpen = ref(false);
const openingImportDrawerOpen = ref(false);
const warehouseLedgerDrawerOpen = ref(false);
const productionWorkOrderInboundDrawerOpen = ref(false);
const warehouseLedgerLoading = ref(false);
const warehouseLedgerExporting = ref(false);
const warehouseLedgerRows = ref([]);
const warehouseLedgerTotal = ref(0);
const warehouseLedgerPage = ref(1);
const warehouseLedgerPageSize = ref(10);
const warehouseLedgerTableRef = ref(null);
const openingImportBusy = ref(false);
const openingImportInputRef = ref(null);
const logisticsPhotoUploading = ref(false);
const rawReturnLinkLoading = ref(false);
const feedbackMessage = ref("");
const errorMessage = ref("");
const lastWarehouseArchiveResult = ref(null);
const submittingOperation = ref(false);
const specialAdjustmentSubmitting = ref(false);
let productionStockLotOptionsRequestSeq = 0;
let genericSourceLotOptionsRequestSeq = 0;
let rawReturnLinksRequestSeq = 0;
let productionWorkOrderInboundPreviewRequestSeq = 0;
let warehouseLedgerTooltipTable = null;
let warehouseLedgerTooltipElement = null;
let warehouseLedgerTooltipCleanupHandlers = [];

const genericForm = reactive(buildEmptyGenericForm());
const specialAdjustmentForm = reactive(buildEmptySpecialAdjustmentForm());
const rawReturnForm = reactive(buildEmptyRawReturnForm());
const productionForm = reactive(buildEmptyProductionForm());
const productionWorkOrderInboundForm = reactive(buildEmptyProductionWorkOrderInboundForm());
const completionForm = reactive(buildEmptyCompletionForm());
const deliveryForm = reactive(buildEmptyDeliveryForm());
const returnInboundForm = reactive(buildEmptyReturnInboundForm());
const returnScrapInboundForm = reactive(buildEmptyReturnScrapInboundForm());
const returnReworkForm = reactive(buildEmptyReturnReworkForm());
const warehouseLedgerSummary = reactive(buildEmptyWarehouseLedgerSummary());
const warehouseLedgerFilters = reactive(buildEmptyWarehouseLedgerFilters());
const selectedInboundWorkOrderId = ref("");
const productionWorkOrderInboundPreview = ref(null);

const rawBalances = computed(() =>
  balances.value.filter((item) => normalizeWarehouseType(item) === "RAW" || (!normalizeWarehouseType(item) && item.item_type === "RAW_MATERIAL"))
);
const auxiliaryBalances = computed(() => balances.value.filter((item) => normalizeWarehouseType(item) === "AUX"));
const scrapBalances = computed(() => balances.value.filter((item) => normalizeWarehouseType(item) === "SCRAP"));
const returnBalances = computed(() => aggregateItemBalances(balances.value.filter((item) => normalizeWarehouseType(item) === "RETURN"), "RETURN"));
const semiFinishedBalances = computed(() => aggregateSemiOperationBalances(lots.value.filter((item) => normalizeWarehouseType(item) === "SEMI")));
const finishedBalances = computed(() =>
  aggregateItemBalances(
    balances.value.filter((item) => normalizeWarehouseType(item) === "FINISHED" || (!normalizeWarehouseType(item) && item.item_type === "FINISHED_GOOD")),
    "FINISHED"
  )
);

const inventoryTabs = computed(() => [
  {
    key: "raw",
    label: "原材料库",
    count: filteredRawCount.value,
    icon: "warehouseRaw",
    iconClass: "inventory-tab-icon-raw",
    iconPaths: ["M4 8.5 12 4l8 4.5-8 4.5-8-4.5Z", "m4 12 8 4.5 8-4.5", "m4 15.5 8 4.5 8-4.5"]
  },
  {
    key: "semi",
    label: "半成品库",
    count: filteredSemiCount.value,
    icon: "warehouseSemi",
    iconClass: "inventory-tab-icon-semi",
    iconPaths: ["M6 7h7.5a3.5 3.5 0 0 1 0 7H11", "M6 7v10h5", "M16 15.5 19 18l-3 2.5", "M19 18h-7"]
  },
  {
    key: "finished",
    label: "成品库",
    count: filteredFinishedCount.value,
    icon: "warehouseFinished",
    iconClass: "inventory-tab-icon-finished",
    iconPaths: ["M4 8.5 12 4l8 4.5v8.8L12 21l-8-3.7V8.5Z", "M4 8.5 12 13l8-4.5", "M12 13v8"]
  },
  {
    key: "auxiliary",
    label: "辅料库",
    count: filteredAuxiliaryCount.value,
    icon: "warehouseAuxiliary",
    iconClass: "inventory-tab-icon-auxiliary",
    iconPaths: ["M5 18.5 14.5 9", "m13 7 4-4 4 4-4 4-4-4Z", "M4 20h7", "M7.5 14.5 10 17", "M15.5 14.5h4", "M17.5 12.5v4"]
  },
  {
    key: "scrap",
    label: "废料库",
    count: filteredScrapCount.value,
    icon: "warehouseScrap",
    iconClass: "inventory-tab-icon-scrap",
    iconPaths: ["M6 7h12l-1 13H7L6 7Z", "M9 7V4h6v3", "M4 7h16", "M9.5 11v5", "M14.5 11v5"]
  },
  {
    key: "return",
    label: "退货库",
    count: filteredReturnCount.value,
    icon: "warehouseReturn",
    iconClass: "inventory-tab-icon-return",
    iconPaths: ["M7 7h10l2 4H5l2-4Z", "M5 11v7h14v-7", "M9 15h6", "M10 13l-2 2 2 2", "M8 15h8"]
  }
]);

const activeInventoryTitle = computed(() => {
  const titleMap = {
    raw: "原材料库存台账与批次",
    semi: "半成品暂存与委外流转",
    finished: "成品库存与销售出库",
    auxiliary: "辅料库存与生产消耗",
    scrap: "废料库存与售卖出库",
    return: "退货待返工库存"
  };
  return titleMap[activeInventoryTab.value] || titleMap.raw;
});
const activeInventoryTag = computed(() => {
  return formatWarehouseTypeLabel(activeWarehouseType.value);
});
const activeInventoryLabel = computed(() => WAREHOUSE_LABEL_BY_TAB[activeInventoryTab.value] || "仓库");
const activeWarehouseType = computed(() => WAREHOUSE_TYPE_BY_TAB[activeInventoryTab.value] || "RAW");
const activeWeightOnly = computed(() => ["raw", "scrap"].includes(activeInventoryTab.value));
const activeQuantityOnly = computed(() => activeInventoryTab.value === "auxiliary");
const warehouseLedgerEmptyColspan = computed(() => (activeWeightOnly.value || activeQuantityOnly.value ? 13 : 14));
const selectedWeightOnly = computed(
  () => ["RAW", "SCRAP"].includes(selectedWarehouseType.value)
);
const selectedQuantityOnly = computed(() => selectedWarehouseType.value === "AUX");
const activeItemColumnLabel = computed(() => {
  if (activeInventoryTab.value === "semi") {
    return "半成品";
  }
  if (["finished", "return"].includes(activeInventoryTab.value)) {
    return "产品";
  }
  return "物料";
});
const activeSearchPlaceholder = computed(() => `搜索${activeInventoryLabel.value}${activeItemColumnLabel.value}编码、名称、规格、单位、仓库、库位`);
const activeWarehouses = computed(() => warehouses.value.filter((item) => normalizeWarehouseType(item) === activeWarehouseType.value));
const activeWarehouseId = computed(() => activeWarehouses.value[0]?.id || 0);
const finishedWarehouses = computed(() => warehouses.value.filter((item) => normalizeWarehouseType(item) === "FINISHED"));
const activeBalances = computed(() => {
  const rowsByTab = {
    raw: rawBalances.value,
    semi: semiFinishedBalances.value,
    finished: finishedBalances.value,
    auxiliary: auxiliaryBalances.value,
    scrap: scrapBalances.value,
    return: returnBalances.value
  };
  return rowsByTab[activeInventoryTab.value] || rowsByTab.raw;
});

function normalizeSemiMeasureBasis(value) {
  const normalized = String(value || "").toUpperCase();
  return ["PIECE", "WEIGHT", "MIXED"].includes(normalized) ? normalized : "";
}

function semiMeasureBasisLabel(value) {
  return SEMI_MEASURE_BASIS_LABELS[normalizeSemiMeasureBasis(value)] || "未确定";
}

function deriveSemiLotMeasureBasis(lot) {
  const hasQty =
    Number(lot?.inbound_qty || 0) > 0 ||
    Number(lot?.remaining_qty || 0) > 0 ||
    Number(lot?.locked_qty || 0) > 0 ||
    Number(lot?.qty_on_hand || 0) > 0 ||
    Number(lot?.qty_available || 0) > 0;
  const hasWeight =
    Number(lot?.inbound_weight_kg || 0) > 0 ||
    Number(lot?.remaining_weight_kg || 0) > 0 ||
    Number(lot?.locked_weight_kg || 0) > 0 ||
    Number(lot?.weight_on_hand_kg || 0) > 0 ||
    Number(lot?.weight_available_kg || 0) > 0;
  if (hasQty && hasWeight) {
    return "MIXED";
  }
  if (hasQty) {
    return "PIECE";
  }
  if (hasWeight) {
    return "WEIGHT";
  }
  return "";
}

function mergeSemiMeasureBasis(current, next) {
  const normalizedCurrent = normalizeSemiMeasureBasis(current);
  const normalizedNext = normalizeSemiMeasureBasis(next);
  if (!normalizedCurrent) {
    return normalizedNext;
  }
  if (!normalizedNext || normalizedCurrent === normalizedNext) {
    return normalizedCurrent;
  }
  return "MIXED";
}

function deriveSemiMeasureBasisFromRows(rows) {
  return rows.reduce((basis, row) => mergeSemiMeasureBasis(basis, row.measure_basis || deriveSemiLotMeasureBasis(row)), "");
}

function formatSemiMeasureValue(row, value, measureType) {
  if (normalizeWarehouseType(row) !== "SEMI") {
    return measureType === "PIECE" ? formatQty(value) : formatWeight(value);
  }
  const basis = normalizeSemiMeasureBasis(row?.measure_basis || deriveSemiLotMeasureBasis(row));
  if (basis === "PIECE" && measureType === "WEIGHT") {
    return "-";
  }
  if (basis === "WEIGHT" && measureType === "PIECE") {
    return "-";
  }
  if (measureType === "PIECE") {
    return formatQty(value);
  }
  return formatWeight(value);
}

function formatSemiAvailability(item) {
  const basis = normalizeSemiMeasureBasis(item?.measure_basis);
  if (basis === "PIECE") {
    return `可用 ${formatQty(item?.qty_available)} 件`;
  }
  if (basis === "WEIGHT") {
    return `可用 ${formatWeight(item?.weight_available_kg)}`;
  }
  if (basis === "MIXED") {
    return `历史混合 · 可用 ${formatQty(item?.qty_available)} 件 / ${formatWeight(item?.weight_available_kg)}`;
  }
  return `可用 ${formatQty(item?.qty_available)} 件 / ${formatWeight(item?.weight_available_kg)}`;
}

const operationMatrix = computed(() => ({
  raw: {
    inbound: [
      makeOperation("raw-opening-in", "in", "OPENING", "期初入库", "仓库初始化时导入的初始材料。"),
      makeOperation("raw-customer-in", "in", "CUSTOMER_SUPPLIED", "客料入库", "客户提供的原材料直接入原材料库，成本按0处理。"),
      makeOperation("raw-production-surplus-in", "in", "PRODUCTION_SURPLUS", "生产余料入库", "生产结束后剩余原材料重新入库。"),
      makeOperation("raw-outsourcing-surplus-in", "in", "OUTSOURCING_SURPLUS", "委外余料入库", "委外加工结束剩余退回的原材料重新入库。"),
      makeOperation("raw-special-in", "in", "SPECIAL_IN", "特殊入库", "强制填写说明后，直接调整原材料库库存明细。", "specialAdjustment")
    ],
    outbound: [
      makeOperation("raw-production-out", "out", "PRODUCTION_OUT", "生产出库", "选择原材料库存批次出库，后续小程序报工按库存批次号推进。", "production"),
      makeOperation("raw-outsourcing-out", "out", "OUTSOURCING_OUT", "委外出库", "原材料出库给第三方加工。"),
      makeOperation("raw-return-out", "out", "PURCHASE_RETURN_OUT", "退货出库", "入仓后发现质量或性能不合格的原材料退回供应商。", "rawReturn"),
      makeOperation("raw-scrap-out", "out", "SCRAP_OUT", "报废出库", "原材料报废出库。"),
      makeOperation("raw-special-out", "out", "SPECIAL_OUT", "特殊出库", "强制填写说明后，直接扣减原材料库库存明细。", "specialAdjustment")
    ]
  },
  semi: {
    inbound: [
      makeOperation("semi-wip-in", "in", "WIP_IN", "产中入库", "生产过程中需要暂时入库的半成品。"),
      makeOperation("semi-opening-in", "in", "OPENING", "期初入库", "仓库初始化时导入的初始半成品。"),
      makeOperation("semi-outsourcing-in", "in", "OUTSOURCING_IN", "委外入库", "委外某道工序完成后入库，等待后续继续加工。"),
      makeOperation("semi-special-in", "in", "SPECIAL_IN", "特殊入库", "强制填写说明后，直接调整半成品库库存明细。", "specialAdjustment")
    ],
    outbound: [
      makeOperation("semi-wip-out", "out", "WIP_OUT", "产中出库", "暂存半成品出库，继续完成后续工序。"),
      makeOperation("semi-outsourcing-out", "out", "OUTSOURCING_OUT", "委外出库", "半成品出库委外加工后续工序。"),
      makeOperation("semi-special-out", "out", "SPECIAL_OUT", "特殊出库", "强制填写说明后，直接扣减半成品库库存明细。", "specialAdjustment")
    ]
  },
  finished: {
    inbound: [
      makeOperation("finished-production-in", "in", "PRODUCTION_FINISHED", "生产入库", "按生产台账办理生产完工成品入库，并可同步办理余料、废料入库。", "productionLedgerInbound"),
      makeOperation("finished-rework-in", "in", "REWORK_FINISHED_IN", "返工入库", "退货返工完成后的成品回库。", "reworkCompletion"),
      makeOperation("finished-outsourcing-in", "in", "OUTSOURCING_IN", "委外入库", "委外加工完成后的成品入库。"),
      makeOperation("finished-opening-in", "in", "OPENING", "期初入库", "仓库初始化时导入的初始成品。"),
      makeOperation("finished-special-in", "in", "SPECIAL_IN", "特殊入库", "强制填写说明后，直接调整成品库库存明细。", "specialAdjustment")
    ],
    outbound: [
      makeOperation("finished-sales-out", "out", "SALES_OUT", "销售出库", "按销售订单进行成品发货出库。", "delivery"),
      makeOperation("finished-special-out", "out", "SPECIAL_OUT", "特殊出库", "强制填写说明后，直接扣减成品库库存明细。", "specialAdjustment")
    ]
  },
  auxiliary: {
    inbound: [
      makeOperation("aux-opening-in", "in", "OPENING", "期初入库", "仓库初始化时导入的初始辅料。"),
      makeOperation("aux-special-in", "in", "SPECIAL_IN", "特殊入库", "强制填写说明后，直接调整辅料库库存明细。", "specialAdjustment")
    ],
    outbound: [
      makeOperation("aux-production-out", "out", "PRODUCTION_OUT", "生产出库", "辅料用于生产消耗的出库。"),
      makeOperation("aux-special-out", "out", "SPECIAL_OUT", "特殊出库", "强制填写说明后，直接扣减辅料库库存明细。", "specialAdjustment")
    ]
  },
  scrap: {
    inbound: [
      makeOperation("scrap-production-in", "in", "PRODUCTION_SCRAP_IN", "生产废料入库", "生产过程产生的废料入库。"),
      makeOperation("scrap-rework-in", "in", "REWORK_SCRAP_IN", "返工废料入库", "退货返工失败产生的废品入废料库。", "reworkScrapInbound"),
      makeOperation("scrap-opening-in", "in", "OPENING", "期初入库", "仓库初始化时导入的初始废料。"),
      makeOperation("scrap-outsourcing-in", "in", "OUTSOURCING_SCRAP_IN", "委外废料入库", "委外加工产生的废料入库。"),
      makeOperation("scrap-return-in", "in", "RETURN_SCRAP_IN", "退货废料入库", "客户退回后判定报废的产品直接进入废料库。", "returnScrapInbound"),
      makeOperation("scrap-special-in", "in", "SPECIAL_IN", "特殊入库", "强制填写说明后，直接调整废料库库存明细。", "specialAdjustment")
    ],
    outbound: [
      makeOperation("scrap-sale-out", "out", "SCRAP_SALE_OUT", "售卖出库", "废料称重售卖出库。"),
      makeOperation("scrap-special-out", "out", "SPECIAL_OUT", "特殊出库", "强制填写说明后，直接扣减废料库库存明细。", "specialAdjustment")
    ]
  },
  return: {
    inbound: [
      makeOperation("return-opening-in", "in", "OPENING", "期初入库", "仓库初始化时导入的历史退货待返工品。"),
      makeOperation("return-customer-in", "in", "RETURN_IN", "退货入库", "客户退回且判定可返工的产品入退货库。", "returnInbound"),
      makeOperation("return-special-in", "in", "SPECIAL_IN", "特殊入库", "强制填写说明后，直接调整退货库库存明细。", "specialAdjustment")
    ],
    outbound: [
      makeOperation("return-rework-out", "out", "RETURN_OUT", "返工出库", "退货库待返工品出库并生成返工处置。", "returnRework"),
      makeOperation("return-special-out", "out", "SPECIAL_OUT", "特殊出库", "强制填写说明后，直接扣减退货库库存明细。", "specialAdjustment")
    ]
  }
}));
const activeInboundOperations = computed(() => operationMatrix.value[activeInventoryTab.value]?.inbound || []);
const activeOutboundOperations = computed(() => operationMatrix.value[activeInventoryTab.value]?.outbound || []);
const productOperationOptions = computed(() => buildProductOperationOptions(processRoutes.value));
const isSemiWarehouseOperation = computed(() => activeWarehouseType.value === "SEMI");
const genericItemFieldLabel = computed(() => {
  if (isSemiWarehouseOperation.value) {
    return "半成品";
  }
  if (isScrapSaleOutbound.value) {
    return "废料";
  }
  if (isOutsourcingScrapInbound.value || isReworkScrapInbound.value || ["finished", "return"].includes(activeInventoryTab.value)) {
    return "产品";
  }
  return "物料";
});
const genericItemPlaceholder = computed(() => `请选择${genericItemFieldLabel.value}`);

const baseGenericItemOptions = computed(() => {
  if (isOutsourcingScrapInbound.value || isReworkScrapInbound.value) {
    return products.value;
  }
  return buildGenericItemOptions({
    direction: activeOperation.value?.direction || "in",
    activeInventoryTab: activeInventoryTab.value,
    activeWarehouseType: activeWarehouseType.value,
    warehouseId: genericForm.warehouse_id,
    balances: activeInventoryTab.value === "semi" ? semiFinishedBalances.value : balances.value,
    materials: materials.value,
    products: products.value,
    productOperations: productOperationOptions.value
  });
});
const genericItemOptions = computed(() => {
  if (!isProductionWorkOrderControlledInbound.value || !selectedGenericWorkOrder.value) {
    return baseGenericItemOptions.value;
  }
  if (isReworkScrapInbound.value) {
    const product = products.value.find((item) => Number(item.item_id) === Number(selectedGenericWorkOrder.value.product_item_id));
    return product
      ? [{ ...product, option_key: String(product.item_id), item_id: Number(product.item_id) }]
      : [{
          option_key: String(selectedGenericWorkOrder.value.product_item_id),
          item_id: Number(selectedGenericWorkOrder.value.product_item_id),
          item_code: selectedGenericWorkOrder.value.product_code || "",
          item_name: selectedGenericWorkOrder.value.product_name || ""
        }];
  }
  const optionsByMaterialId = new Map(
    baseGenericItemOptions.value.map((item) => [Number(item.item_id || 0), item])
  );
  const seen = new Set();
  return (selectedGenericWorkOrder.value.materials || [])
    .map((material) => {
      const materialItemId = Number(material.material_item_id || 0);
      if (!materialItemId || seen.has(materialItemId)) {
        return null;
      }
      seen.add(materialItemId);
      return {
        ...(optionsByMaterialId.get(materialItemId) || {}),
        option_key: String(materialItemId),
        item_id: materialItemId,
        item_code: material.material_code || optionsByMaterialId.get(materialItemId)?.item_code || "",
        item_name: material.material_name || optionsByMaterialId.get(materialItemId)?.item_name || "",
        unit_cost: Number(material.unit_cost || 0)
      };
    })
    .filter(Boolean);
});
const selectedGenericItemOption = computed(() =>
  genericItemOptions.value.find((item) => String(item.option_key || item.item_id) === String(genericForm.item_option_key || "")) || null
);
const selectedSemiExistingMeasureBasis = computed(() => {
  if (activeWarehouseType.value !== "SEMI") {
    return "";
  }
  const itemId = Number(genericForm.item_id || 0);
  const warehouseId = Number(genericForm.warehouse_id || 0);
  const sourceLineId = Number(genericForm.source_line_id || 0);
  if (!itemId || !warehouseId || !sourceLineId) {
    return "";
  }
  return deriveSemiMeasureBasisFromRows(
    lots.value.filter(
      (lot) =>
        Number(lot.item_id || 0) === itemId &&
        Number(lot.warehouse_id || 0) === warehouseId &&
        Number(lot.source_line_id || 0) === sourceLineId &&
        (Number(lot.remaining_qty || 0) > 0 || Number(lot.remaining_weight_kg || 0) > 0)
    )
  );
});
const selectedSemiMeasureBasis = computed(() => {
  if (isSemiSourceLotTraceInbound.value) {
    return normalizeSemiMeasureBasis(genericForm.measure_basis || selectedSemiExistingMeasureBasis.value);
  }
  if (isSemiAutoSourceOutbound.value) {
    return normalizeSemiMeasureBasis(selectedGenericItemOption.value?.measure_basis || selectedSemiExistingMeasureBasis.value);
  }
  return "";
});
const isSemiMeasureBasisLocked = computed(() =>
  isSemiSourceLotTraceInbound.value && ["PIECE", "WEIGHT"].includes(normalizeSemiMeasureBasis(selectedSemiExistingMeasureBasis.value))
);
const showGenericWeightInput = computed(() => {
  if (activeWarehouseType.value === "AUX") {
    return false;
  }
  if (isOutsourcingScrapInbound.value) {
    return true;
  }
  if (isReworkScrapInbound.value) {
    return true;
  }
  if (!isSemiQuantityWeightFlexibleOperation.value) {
    return !usesGenericSourceLotSelector.value;
  }
  return selectedSemiMeasureBasis.value === "WEIGHT";
});
const showGenericQtyInput = computed(() =>
  activeWarehouseType.value === "AUX" ||
  isReworkScrapInbound.value ||
  isFinishedOutsourcingInbound.value ||
  (isSemiQuantityWeightFlexibleOperation.value && selectedSemiMeasureBasis.value === "PIECE")
);
const genericSourceLotMaterialIds = computed(() => {
  if (isScrapSaleOutbound.value) {
    const scrapItemId = Number(genericForm.item_id || 0);
    return scrapItemId ? [scrapItemId] : [];
  }
  if (isProductionSurplusInbound.value && selectedGenericWorkOrder.value) {
    return (selectedGenericWorkOrder.value.materials || []).map((item) => item.material_item_id);
  }
  if (isProductTraceOnlySourceLotInbound.value || isOutsourcingScrapInbound.value) {
    return getProductBomMaterialItemIds(genericForm.item_id);
  }
  const materialItemId = Number(genericForm.item_id || 0);
  return materialItemId ? [materialItemId] : [];
});
const semiAutoSourceOutboundSourceLots = computed(() => {
  if (!isSemiAutoSourceOutbound.value) {
    return [];
  }
  const itemId = Number(genericForm.item_id || 0);
  const warehouseId = Number(genericForm.warehouse_id || 0);
  const sourceLineId = Number(genericForm.source_line_id || 0);
  if (!itemId || !warehouseId || !sourceLineId) {
    return [];
  }
  return lots.value
    .filter(
      (lot) =>
        Number(lot.item_id || 0) === itemId &&
        Number(lot.warehouse_id || 0) === warehouseId &&
        Number(lot.source_line_id || 0) === sourceLineId &&
        String(lot.status || "").toUpperCase() === "AVAILABLE" &&
        (Number(lot.remaining_qty || 0) > 0 || Number(lot.remaining_weight_kg || 0) > 0)
    )
    .sort((left, right) => String(left.lot_no || "").localeCompare(String(right.lot_no || ""), "zh-CN", { numeric: true }));
});
const semiAutoSourceOutboundSourceLotSummary = computed(() =>
  semiAutoSourceOutboundSourceLots.value.map((lot) => lot.lot_no).filter(Boolean).join("、")
);
const semiAutoSourceOutboundAvailableQty = computed(() =>
  semiAutoSourceOutboundSourceLots.value.reduce((total, lot) => total + Number(lot.remaining_qty || 0), 0)
);
const semiAutoSourceOutboundAvailableWeight = computed(() =>
  semiAutoSourceOutboundSourceLots.value.reduce((total, lot) => total + Number(lot.remaining_weight_kg || 0), 0)
);
const genericLocations = computed(() => locations.value.filter((item) => item.warehouse_id === Number(genericForm.warehouse_id)));
const completionLocations = computed(() => locations.value.filter((item) => item.warehouse_id === Number(completionForm.warehouse_id)));
const specialAdjustmentDirectionText = computed(() => specialAdjustmentDirectionLabel(specialAdjustmentForm.direction));
const specialAdjustmentItemColumnLabel = computed(() => (specialAdjustmentForm.direction === "IN" ? "入库方式 / 明细" : "库存明细"));
const specialAdjustmentCanShowMeasureBasis = computed(() => activeWarehouseType.value === "SEMI");
const specialAdjustmentCanShowQty = computed(() => !["RAW", "SCRAP"].includes(activeWarehouseType.value));
const specialAdjustmentCanShowWeight = computed(() => activeWarehouseType.value !== "AUX");
const specialAdjustmentLotOptions = computed(() => {
  const warehouseId = Number(specialAdjustmentForm.warehouse_id || 0);
  if (!warehouseId) {
    return [];
  }
  return lots.value
    .filter((lot) => Number(lot.warehouse_id || 0) === warehouseId && normalizeWarehouseType(lot) === activeWarehouseType.value)
    .sort(compareInventoryLots);
});
const specialAdjustmentItemOptions = computed(() => {
  if (activeWarehouseType.value === "SEMI") {
    return productOperationOptions.value.length ? productOperationOptions.value : products.value;
  }
  return buildGenericItemOptions({
    direction: "in",
    activeInventoryTab: activeInventoryTab.value,
    activeWarehouseType: activeWarehouseType.value,
    warehouseId: specialAdjustmentForm.warehouse_id,
    balances: activeInventoryTab.value === "semi" ? semiFinishedBalances.value : balances.value,
    materials: materials.value,
    products: products.value,
    productOperations: productOperationOptions.value
  });
});
const rawReturnLotOptions = computed(() =>
  lots.value
    .filter(
      (item) =>
        (normalizeWarehouseType(item) === "RAW" || item.item_type === "RAW_MATERIAL") &&
        item.status === "AVAILABLE" &&
        item.quality_status === "PASS" &&
        rawReturnLotAvailableWeight(item) > 0
    )
    .sort((left, right) => {
      const leftKey = left.lot_no || "";
      const rightKey = right.lot_no || "";
      return leftKey.localeCompare(rightKey, "zh-CN", { numeric: true }) || Number(left.lot_id) - Number(right.lot_id);
    })
);
const selectedRawReturnLot = computed(() =>
  rawReturnLotOptions.value.find((item) => Number(item.lot_id) === Number(rawReturnForm.lot_id)) || null
);
const selectedRawReturnLink = computed(() =>
  rawReturnPurchaseLinks.value.find((item) => Number(item.purchase_order_item_id) === Number(rawReturnForm.purchase_order_item_id)) || null
);
const selectedRawReturnLotAvailableWeight = computed(() => rawReturnLotAvailableWeight(selectedRawReturnLot.value));
const rawReturnMaxWeight = computed(() => {
  const lotAvailableWeight = selectedRawReturnLotAvailableWeight.value;
  if (!selectedRawReturnLink.value) {
    return lotAvailableWeight;
  }
  return Math.min(lotAvailableWeight, Math.max(Number(selectedRawReturnLink.value.returnable_weight_kg || 0), 0));
});
const deliveryById = computed(() => new Map(deliveries.value.map((item) => [Number(item.delivery_id), item])));
const returnedTotalsByDeliveryItem = computed(() => {
  const map = new Map();
  returnItems.value.forEach((item) => {
    const deliveryItemId = Number(item.delivery_item_id || 0);
    if (!deliveryItemId) {
      return;
    }
    const current = map.get(deliveryItemId) || { qty: 0, weight: 0 };
    current.qty += Number(item.return_qty || 0);
    current.weight += Number(item.return_weight_kg || 0);
    map.set(deliveryItemId, current);
  });
  return map;
});
const deliveryLineOptions = computed(() =>
  deliveryItems.value
    .map((item) => {
      const delivery = deliveryById.value.get(Number(item.delivery_id)) || {};
      const returned = returnedTotalsByDeliveryItem.value.get(Number(item.delivery_item_id)) || { qty: 0, weight: 0 };
      return {
        ...item,
        customer_id: delivery.customer_id,
        customer_name: delivery.customer_name,
        returnable_qty: Math.max(Number(item.delivery_qty || 0) - returned.qty, 0),
        returnable_weight_kg: Math.max(Number(item.delivery_weight_kg || 0) - returned.weight, 0)
      };
    })
    .filter((item) => String(item.status || "").toUpperCase() === "POSTED" && item.returnable_qty > 0)
);
const selectedReturnInboundDeliveryLine = computed(() =>
  deliveryLineOptions.value.find((item) => Number(item.delivery_item_id) === Number(returnInboundForm.delivery_item_id)) || null
);
const selectedReturnScrapDeliveryLine = computed(() =>
  deliveryLineOptions.value.find((item) => Number(item.delivery_item_id) === Number(returnScrapInboundForm.delivery_item_id)) || null
);
const pendingReturnItems = computed(() =>
  returnItems.value.filter((item) => String(item.disposition_status || "").toUpperCase() === "PENDING")
);
const selectedReturnReworkItem = computed(() =>
  pendingReturnItems.value.find((item) => Number(item.return_item_id) === Number(returnReworkForm.return_item_id)) || null
);

function getProductBomMaterialItemIds(productItemId) {
  const id = Number(productItemId || 0);
  if (!id) {
    return [];
  }
  const activeBoms = boms.value
    .filter((item) => Number(item.product_item_id || 0) === id && String(item.status || "ACTIVE").toUpperCase() === "ACTIVE")
    .sort((left, right) => Number(right.is_default || 0) - Number(left.is_default || 0) || Number(right.bom_id || 0) - Number(left.bom_id || 0));
  const selectedProductBom = activeBoms.find((item) => Number(item.is_default || 0) === 1) || activeBoms[0] || null;
  const seen = new Set();
  return (selectedProductBom?.items || [])
    .map((item) => Number(item.material_item_id || 0))
    .filter((materialItemId) => {
      if (!materialItemId || seen.has(materialItemId)) {
        return false;
      }
      seen.add(materialItemId);
      return true;
    });
}

const selectedBom = computed(() =>
  boms.value.find((item) => item.product_item_id === Number(productionForm.product_item_id) && item.is_default) ||
  boms.value.find((item) => item.product_item_id === Number(productionForm.product_item_id)) ||
  null
);
const selectedProductionBomMaterialRows = computed(() => {
  const seen = new Set();
  return (selectedBom.value?.items || [])
    .filter((item) => Number(item.material_item_id || 0) > 0)
    .filter((item) => {
      const materialItemId = Number(item.material_item_id);
      if (seen.has(materialItemId)) {
        return false;
      }
      seen.add(materialItemId);
      return true;
    });
});
const selectedProductionBomMaterialRow = computed(() => {
  const selectedMaterialId = Number(selectedProductionStockLots.value[0]?.item_id || 0);
  if (selectedMaterialId) {
    return selectedProductionBomMaterialRows.value.find((item) => Number(item.material_item_id || 0) === selectedMaterialId) || null;
  }
  return selectedProductionBomMaterialRows.value[0] || null;
});
const selectedBomGrossWeight = computed(() => Number(selectedProductionBomMaterialRow.value?.gross_weight_per_piece_kg || 0));
const selectedProductionIssueWeight = computed(() =>
  selectedProductionStockLots.value.reduce((total, lot) => {
    const issuedWeight = Number(lot.issued_weight_kg);
    return total + (Number.isFinite(issuedWeight) ? issuedWeight : 0);
  }, 0)
);
const referenceProductionQty = computed(() => {
  const grossWeight = selectedBomGrossWeight.value;
  const issueWeight = Number(selectedProductionIssueWeight.value || 0);
  if (grossWeight <= 0 || issueWeight <= 0) {
    return 0;
  }
  return Math.floor(issueWeight / grossWeight);
});
function hasWorkOrderSettleMarker(workOrder, marker) {
  return String(workOrder?.remark || "").includes(marker);
}

function isWorkOrderSettled(workOrder, bizType = "") {
  if (String(workOrder?.status || "") === "锁单") {
    return true;
  }
  if (bizType === "PRODUCTION_SCRAP_IN" && hasWorkOrderSettleMarker(workOrder, "生产废料已结单")) {
    return true;
  }
  if (bizType === "PRODUCTION_SURPLUS" && hasWorkOrderSettleMarker(workOrder, "生产余料已结单")) {
    return true;
  }
  const status = String(workOrder?.status || "").toUpperCase();
  const label = String(formatStatusLabel(workOrder?.status || "") || "");
  const statusSettled = status === "SETTLED" || label === "已结单";
  if (!statusSettled) {
    return false;
  }
  if (bizType === "PRODUCTION_SCRAP_IN") {
    return Number(workOrder?.limits?.tolerance_scrap_inbound_weight_kg || workOrder?.max_scrap_inbound_weight_kg || 0) <= 0;
  }
  if (bizType === "PRODUCTION_SURPLUS") {
    return Number(workOrder?.limits?.tolerance_surplus_inbound_weight_kg || workOrder?.max_surplus_inbound_weight_kg || 0) <= 0;
  }
  return true;
}

const receivableWorkOrders = computed(() =>
  workOrderLedgerRows.value.filter(
    (item) =>
      String(item.work_order_type || "").toUpperCase() !== "REWORK" &&
      !isWorkOrderSettled(item) &&
      Number(item.limits?.tolerance_finished_inbound_qty || item.max_finished_inbound_qty || 0) > 0
  )
);
const reworkReceivableWorkOrders = computed(() =>
  workOrderLedgerRows.value.filter(
    (item) =>
      String(item.work_order_type || "").toUpperCase() === "REWORK" &&
      !isWorkOrderSettled(item) &&
      Number(item.limits?.max_finished_inbound_qty || item.max_finished_inbound_qty || 0) > 0
  )
);
const isReworkCompletionOperation = computed(() => activeOperation.value?.drawerType === "reworkCompletion");
const completionWorkOrderOptions = computed(() =>
  isReworkCompletionOperation.value ? reworkReceivableWorkOrders.value : receivableWorkOrders.value
);
const selectedCompletionWorkOrder = computed(() =>
  workOrderLedgerRows.value.find((item) => Number(item.work_order_id) === Number(completionForm.work_order_id)) || null
);
const completionProduct = computed(() => products.value.find((item) => item.item_id === Number(completionForm.product_item_id)) || null);
const completionProductLabel = computed(() => formatProductLabel(completionProduct.value));
const completionFinishedBeforeQty = computed(() =>
  Number(selectedCompletionWorkOrder.value?.limits?.finished_received_qty || selectedCompletionWorkOrder.value?.finished_received_qty || 0)
);
const completionExpectedFinishedQty = computed(() =>
  Number((completionFinishedBeforeQty.value + Number(completionForm.max_receivable_qty || 0)).toFixed(3))
);
const completionFinalFinishedQty = computed(() =>
  Number((completionFinishedBeforeQty.value + Number(completionForm.receipt_qty || 0)).toFixed(3))
);
const completionSettleDeviationRate = computed(() => {
  const expected = completionExpectedFinishedQty.value;
  if (expected <= 0) {
    return 0;
  }
  return (completionFinalFinishedQty.value - expected) / expected;
});
const completionSettleNeedsRemark = computed(() => {
  if (!completionForm.settle_work_order) {
    return false;
  }
  const expected = completionExpectedFinishedQty.value;
  if (expected <= 0) {
    return completionFinalFinishedQty.value > 0;
  }
  const finalQty = completionFinalFinishedQty.value;
  return finalQty < expected * 0.85 || finalQty > expected * 1.15;
});
const selectedSalesOrder = computed(() =>
  salesOrders.value.find((item) => Number(item.order_id) === Number(deliveryForm.sales_order_id)) || null
);
const isSalesOrderDelivery = computed(() => deliveryForm.delivery_mode === "SALES_ORDER");
const isDirectCustomerDelivery = computed(() => deliveryForm.delivery_mode === "DIRECT_CUSTOMER");
const selectedSalesOrderItem = computed(() =>
  salesOrderItems.value.find((item) => Number(item.sales_order_item_id) === Number(deliveryForm.sales_order_item_id)) || null
);
const openSalesOrders = computed(() =>
  salesOrders.value.filter((order) => order.status !== "CLOSED" && orderPendingQty(order.order_id) > 0)
);
const selectableSalesOrderItems = computed(() => {
  if (!isSalesOrderDelivery.value) {
    return [];
  }
  return salesOrderItems.value.filter(
    (item) => Number(item.sales_order_id) === Number(deliveryForm.sales_order_id) && orderItemPendingQty(item) > 0
  );
});
const availableFinishedLots = computed(() =>
  lots.value
    .filter(
      (item) =>
        item.item_id === Number(deliveryForm.product_item_id) &&
        item.warehouse_id === Number(deliveryForm.warehouse_id) &&
        item.status === "AVAILABLE" &&
        Number(item.remaining_qty || 0) > 0
    )
    .sort((left, right) => {
      const leftKey = left.source_material_sub_batch_no || left.lot_no || "";
      const rightKey = right.source_material_sub_batch_no || right.lot_no || "";
      return leftKey.localeCompare(rightKey, "zh-CN", { numeric: true }) || Number(left.lot_id) - Number(right.lot_id);
    })
);
const totalAvailableQty = computed(() =>
  availableFinishedLots.value.reduce((total, lot) => total + Number(lot.remaining_qty || 0), 0)
);
const allocationRows = computed(() => {
  let remaining = Number(deliveryForm.delivery_qty || 0);
  const rows = [];
  for (const lot of availableFinishedLots.value) {
    if (remaining <= 0) {
      break;
    }
    const availableQty = Number(lot.remaining_qty || 0);
    const deliveryQty = Math.min(availableQty, remaining);
    const ratio = availableQty > 0 ? deliveryQty / availableQty : 0;
    rows.push({
      ...lot,
      delivery_qty: deliveryQty,
      delivery_weight_kg: Number(lot.remaining_weight_kg || 0) * ratio
    });
    remaining -= deliveryQty;
  }
  return rows;
});
const totalAllocatedQty = computed(() =>
  allocationRows.value.reduce((total, item) => total + Number(item.delivery_qty || 0), 0)
);
const estimatedScrapSaleAmount = computed(() =>
  Number(genericForm.weight_kg || 0) * Number(genericForm.sale_unit_price || 0)
);
const isProductionScrapInbound = computed(() =>
  activeOperation.value?.direction === "in" && activeOperation.value?.bizType === "PRODUCTION_SCRAP_IN"
);
const isReworkScrapInbound = computed(() =>
  activeOperation.value?.direction === "in" && activeOperation.value?.bizType === "REWORK_SCRAP_IN"
);
const isProductionSurplusInbound = computed(() =>
  activeOperation.value?.direction === "in" && activeOperation.value?.bizType === "PRODUCTION_SURPLUS"
);
const isProductionWorkOrderControlledInbound = computed(() => isProductionScrapInbound.value || isProductionSurplusInbound.value || isReworkScrapInbound.value);
const productionLedgerOptionRows = computed(() =>
  productionLedgerRows.value.map((row) => ({
    ...row,
    work_order_id: Number(row.production_ledger_id || row.work_order_id || 0),
    work_order_no: row.material_lot_no || "-",
    source_lot_summary: row.material_lot_no || row.source_lot_summary || "",
    issued_weight_kg: Number(row.total_issued_weight_kg || row.issued_weight_kg || 0),
    returned_material_weight_kg: Number(row.surplus_inbound_weight_kg || row.returned_material_weight_kg || 0),
    materials: [
      {
        source_lot_id: Number(row.material_lot_id || 0),
        source_lot_no: row.material_lot_no || "",
        material_item_id: Number(row.material_item_id || 0),
        material_code: row.material_code || "",
        material_name: row.material_name || "",
        issued_weight_kg: Number(row.total_issued_weight_kg || 0),
        returned_weight_kg: Number(row.surplus_inbound_weight_kg || 0),
        unit_cost: Number(row.unit_cost || 0)
      }
    ],
    limits: {
      issued_weight_kg: Number(row.total_issued_weight_kg || 0),
      returned_material_weight_kg: Number(row.surplus_inbound_weight_kg || 0),
      max_surplus_inbound_weight_kg: Number(row.outside_weight_kg || 0),
      tolerance_surplus_inbound_weight_kg: Number(row.outside_weight_kg || 0) * 1.15,
      scrap_inbound_weight_kg: Number(row.scrap_inbound_weight_kg || 0),
      max_scrap_inbound_weight_kg: Number(row.outside_weight_kg || 0),
      tolerance_scrap_inbound_weight_kg: Number(row.outside_weight_kg || 0) * 1.15
    }
  }))
);
const selectedGenericWorkOrder = computed(() =>
  (isProductionScrapInbound.value || isProductionSurplusInbound.value
    ? productionLedgerOptionRows.value
    : workOrderLedgerRows.value
  ).find((item) => Number(item.work_order_id) === Number(genericForm.work_order_id)) || null
);
const genericWorkOrderOptions = computed(() => {
  if (isProductionScrapInbound.value) {
    return productionLedgerOptionRows.value.filter(
      (item) => !isWorkOrderSettled(item, "PRODUCTION_SCRAP_IN") && Number(item.limits?.tolerance_scrap_inbound_weight_kg || item.max_scrap_inbound_weight_kg || 0) > 0
    );
  }
  if (isReworkScrapInbound.value) {
    return workOrderLedgerRows.value.filter(
      (item) => String(item.work_order_type || "").toUpperCase() === "REWORK" && !isWorkOrderSettled(item)
    );
  }
  if (isProductionSurplusInbound.value) {
    return productionLedgerOptionRows.value.filter(
      (item) => !isWorkOrderSettled(item, "PRODUCTION_SURPLUS") && Number(item.limits?.tolerance_surplus_inbound_weight_kg || item.max_surplus_inbound_weight_kg || 0) > 0
    );
  }
  return workOrderLedgerRows.value.filter((item) => !isWorkOrderSettled(item));
});
const openProductionWorkOrders = computed(() =>
  productionLedgerOptionRows.value.filter(
    (item) =>
      !isWorkOrderSettled(item) &&
      Number(item.work_order_id || 0) > 0
  )
);
const genericWeightInputMax = computed(() => {
  if (isProductionScrapInbound.value) {
    if (genericForm.settle_work_order) {
      return 0;
    }
    return Number(productionScrapToleranceRemainingWeight.value || 0);
  }
  return 0;
});
const genericUnitCostLabel = computed(() => {
  if (isProductionScrapInbound.value) {
    return "原材料成本单价(元/kg)";
  }
  return formatGenericUnitCostLabel(activeWarehouseType.value);
});
function formatGenericUnitCostLabel(activeWarehouseType) {
  if (activeWarehouseType === "FINISHED") {
    return "单价(元/件)";
  }
  return activeWarehouseType === "AUX" ? "单价(元/件)" : "单价(元/kg)";
}
const genericRequiresLogistics = computed(() =>
  LOGISTICS_REQUIRED_BIZ_TYPES.has(activeOperation.value?.bizType)
);
const isSourceLotReturnInbound = computed(() =>
  activeOperation.value?.direction === "in" && SOURCE_LOT_RETURN_INBOUND_BIZ_TYPES.has(activeOperation.value?.bizType)
);
const isCustomerSuppliedInbound = computed(() =>
  activeOperation.value?.direction === "in" && activeOperation.value?.bizType === "CUSTOMER_SUPPLIED"
);
const isSemiSourceLotTraceInbound = computed(() =>
  activeOperation.value?.direction === "in" &&
  activeWarehouseType.value === "SEMI" &&
  ["WIP_IN", "OUTSOURCING_IN"].includes(activeOperation.value?.bizType)
);
const isFinishedOutsourcingInbound = computed(() =>
  activeOperation.value?.direction === "in" &&
  activeWarehouseType.value === "FINISHED" &&
  activeOperation.value?.bizType === "OUTSOURCING_IN"
);
const isOutsourcingScrapInbound = computed(() =>
  activeOperation.value?.direction === "in" &&
  activeWarehouseType.value === "SCRAP" &&
  activeOperation.value?.bizType === "OUTSOURCING_SCRAP_IN"
);
const isScrapSaleOutbound = computed(() =>
  activeOperation.value?.direction === "out" &&
  activeWarehouseType.value === "SCRAP" &&
  activeOperation.value?.bizType === "SCRAP_SALE_OUT"
);
const isProductTraceOnlySourceLotInbound = computed(() =>
  isSemiSourceLotTraceInbound.value || isFinishedOutsourcingInbound.value
);
const isSemiAutoSourceOutbound = computed(() =>
  activeOperation.value?.direction === "out" &&
  activeWarehouseType.value === "SEMI" &&
  ["WIP_OUT", "OUTSOURCING_OUT"].includes(activeOperation.value?.bizType)
);
const isSemiQuantityWeightFlexibleOperation = computed(() => isSemiSourceLotTraceInbound.value || isSemiAutoSourceOutbound.value);
const isSourceLotSelectedOutbound = computed(() =>
  activeOperation.value?.direction === "out" &&
  (activeWarehouseType.value === "RAW" || isScrapSaleOutbound.value) &&
  SOURCE_LOT_SELECTED_OUTBOUND_BIZ_TYPES.has(activeOperation.value?.bizType)
);
const genericOperationDocumentTitle = computed(
  () => WAREHOUSE_OPERATION_DOCUMENT_TITLES[activeOperation.value?.bizType] || `${activeOperation.value?.label || "仓库出入库"}单`
);
const usesGenericSourceLotSelector = computed(() =>
  isSourceLotReturnInbound.value ||
  isSourceLotSelectedOutbound.value ||
  isProductTraceOnlySourceLotInbound.value ||
  isOutsourcingScrapInbound.value ||
  isScrapSaleOutbound.value
);
const genericSourceLotReturnWeight = computed(() =>
  selectedGenericSourceLots.value.reduce((total, lot) => total + Number(lot.return_weight_kg || 0), 0)
);
const genericSourceLotOutboundWeight = computed(() =>
  selectedGenericSourceLots.value.reduce((total, lot) => total + Number(lot.issued_weight_kg || 0), 0)
);
const genericSelectedSourceLotWeight = computed(() =>
  isSourceLotSelectedOutbound.value ? genericSourceLotOutboundWeight.value : genericSourceLotReturnWeight.value
);
const productionSurplusReturnedBeforeWeight = computed(() =>
  Number(selectedGenericWorkOrder.value?.limits?.returned_material_weight_kg || selectedGenericWorkOrder.value?.returned_material_weight_kg || 0)
);
const productionSurplusExpectedReturnWeight = computed(() => {
  const issuedWeight = Number(selectedGenericWorkOrder.value?.limits?.issued_weight_kg || selectedGenericWorkOrder.value?.issued_weight_kg || 0);
  const usedWeight = Number(selectedGenericWorkOrder.value?.limits?.used_material_weight_kg || selectedGenericWorkOrder.value?.used_material_weight_kg || 0);
  const remainingLimit = Number(selectedGenericWorkOrder.value?.limits?.max_surplus_inbound_weight_kg || selectedGenericWorkOrder.value?.max_surplus_inbound_weight_kg || 0);
  const calculated = issuedWeight > 0 ? issuedWeight - usedWeight : productionSurplusReturnedBeforeWeight.value + remainingLimit;
  return Math.max(Number(calculated.toFixed(3)), 0);
});
const productionSurplusFinalReturnWeight = computed(() =>
  Number((productionSurplusReturnedBeforeWeight.value + genericSourceLotReturnWeight.value).toFixed(3))
);
const productionSurplusToleranceRemainingWeight = computed(() => {
  const apiLimit = Number(selectedGenericWorkOrder.value?.limits?.tolerance_surplus_inbound_weight_kg || 0);
  if (apiLimit > 0) {
    return Number(apiLimit.toFixed(3));
  }
  const calculated = productionSurplusExpectedReturnWeight.value * 1.15 - productionSurplusReturnedBeforeWeight.value;
  return Math.max(Number(calculated.toFixed(3)), 0);
});
const productionSurplusSettleDeviationRate = computed(() => {
  const expected = productionSurplusExpectedReturnWeight.value;
  if (expected <= 0) {
    return 0;
  }
  return (productionSurplusFinalReturnWeight.value - expected) / expected;
});
const productionSurplusSettleNeedsRemark = computed(() => {
  if (!isProductionSurplusInbound.value || !genericForm.settle_work_order || !selectedGenericSourceLots.value.length) {
    return false;
  }
  const expected = productionSurplusExpectedReturnWeight.value;
  if (expected <= 0) {
    return productionSurplusFinalReturnWeight.value > 0;
  }
  const finalWeight = productionSurplusFinalReturnWeight.value;
  return finalWeight < expected * 0.85 || finalWeight > expected * 1.15;
});
const productionScrapInboundBeforeWeight = computed(() =>
  Number(selectedGenericWorkOrder.value?.limits?.scrap_inbound_weight_kg || selectedGenericWorkOrder.value?.scrap_inbound_weight_kg || 0)
);
const productionScrapExpectedWeight = computed(() => {
  const standardWeight = Number(selectedGenericWorkOrder.value?.limits?.standard_scrap_weight_kg || 0);
  if (standardWeight > 0) {
    return Math.max(Number(standardWeight.toFixed(3)), 0);
  }
  const remainingStandardWeight = Number(
    selectedGenericWorkOrder.value?.limits?.max_scrap_inbound_weight_kg || selectedGenericWorkOrder.value?.max_scrap_inbound_weight_kg || 0
  );
  const calculated = productionScrapInboundBeforeWeight.value + remainingStandardWeight;
  return Math.max(Number(calculated.toFixed(3)), 0);
});
const productionScrapFinalWeight = computed(() =>
  Number((productionScrapInboundBeforeWeight.value + Number(genericForm.weight_kg || 0)).toFixed(3))
);
const productionScrapToleranceRemainingWeight = computed(() => {
  const apiLimit = Number(selectedGenericWorkOrder.value?.limits?.tolerance_scrap_inbound_weight_kg || 0);
  if (apiLimit > 0) {
    return Number(apiLimit.toFixed(3));
  }
  const calculated = productionScrapExpectedWeight.value * 1.15 - productionScrapInboundBeforeWeight.value;
  return Math.max(Number(calculated.toFixed(3)), 0);
});
const productionScrapSettleDeviationRate = computed(() => {
  const expected = productionScrapExpectedWeight.value;
  if (expected <= 0) {
    return 0;
  }
  return (productionScrapFinalWeight.value - expected) / expected;
});
const productionScrapSettleNeedsRemark = computed(() => {
  if (!isProductionScrapInbound.value || !genericForm.settle_work_order) {
    return false;
  }
  const expected = productionScrapExpectedWeight.value;
  if (expected <= 0) {
    return productionScrapFinalWeight.value > 0;
  }
  const finalWeight = productionScrapFinalWeight.value;
  return finalWeight < expected * 0.85 || finalWeight > expected * 1.15;
});
const genericSourceLotSelectPlaceholder = computed(() =>
  isFinishedOutsourcingInbound.value
    ? "搜索并选择该产品用料的可用来源库存批次号，可多选；委外厂自供原料可不选"
    : isScrapSaleOutbound.value
    ? "搜索并选择该废料对应的原材料来源库存批次号，可多选"
    : isOutsourcingScrapInbound.value
    ? "搜索并选择该产品用料的来源库存批次号，可多选"
    : isSemiSourceLotTraceInbound.value
    ? "搜索并选择该产品用料的来源库存批次号，可多选"
    : isSourceLotSelectedOutbound.value
      ? "搜索并选择出库来源库存批次号，可多选"
      : "搜索并选择归还到的来源库存批次号，可多选"
);
const customerSuppliedLotNoPreview = computed(() => {
  if (!isCustomerSuppliedInbound.value) {
    return "";
  }
  return buildInventoryLotNoPreview(selectedGenericItemOption.value, lots.value);
});

const selectedBalanceLabel = computed(() =>
  selectedBalance.value ? formatSelectedBalanceTitle(selectedBalance.value) : "库存明细"
);
const selectedWarehouseType = computed(() => normalizeWarehouseType(selectedBalance.value));
const selectedLots = computed(() => {
  const rows = lots.value.filter(
    (item) =>
      item.item_id === selectedItemId.value &&
      (!selectedWarehouseType.value || normalizeWarehouseType(item) === selectedWarehouseType.value)
  );
  if (selectedWarehouseType.value === "SEMI" && selectedBalance.value && Object.hasOwn(selectedBalance.value, "source_line_id")) {
    const selectedOperationId = Number(selectedBalance.value.source_line_id || 0);
    return rows.filter((lot) => Number(lot.source_line_id || 0) === selectedOperationId);
  }
  if (selectedBalance.value?.item_type !== "FINISHED_GOOD" || selectedWarehouseType.value !== "FINISHED") {
    return rows;
  }
  const map = new Map();
  rows.forEach((lot) => {
    const key = lot.source_material_sub_batch_no || lot.lot_no;
    if (!map.has(key)) {
      map.set(key, {
        ...lot,
        lot_id: `FG-SRC-${key}`,
        lot_no: key,
        warehouse_name: "",
        inbound_qty: 0,
        inbound_weight_kg: 0,
        remaining_qty: 0,
        remaining_weight_kg: 0,
        locked_qty: 0,
        locked_weight_kg: 0,
        source_material_sub_batch_no: key,
        source_material_summary: lot.source_material_summary,
        status: "AVAILABLE",
        _warehouses: new Set()
      });
    }
    const row = map.get(key);
    row.inbound_qty += Number(lot.inbound_qty || 0);
    row.inbound_weight_kg += Number(lot.inbound_weight_kg || 0);
    row.remaining_qty += Number(lot.remaining_qty || 0);
    row.remaining_weight_kg += Number(lot.remaining_weight_kg || 0);
    row.locked_qty += Number(lot.locked_qty || 0);
    row.locked_weight_kg += Number(lot.locked_weight_kg || 0);
    row._warehouses.add(lot.warehouse_name);
    if (row.remaining_qty <= 0 && row.remaining_weight_kg <= 0) {
      row.status = "DEPLETED";
    }
  });
  return Array.from(map.values()).map((item) => ({
    ...item,
    warehouse_name: Array.from(item._warehouses).join(" / ") || "-"
  }));
});

function lotAvailableWeight(lot) {
  return Math.max(Number(lot?.remaining_weight_kg || 0) - Number(lot?.locked_weight_kg || 0), 0);
}

function lotAvailableQty(lot) {
  return Math.max(Number(lot?.remaining_qty || 0) - Number(lot?.locked_qty || 0), 0);
}

function isInventoryLotUsable(lot) {
  if (String(lot?.status || "").toUpperCase() !== "AVAILABLE") {
    return false;
  }
  if (normalizeWarehouseType(lot) === "AUX") {
    return lotAvailableQty(lot) > 0;
  }
  if (normalizeWarehouseType(lot) === "SEMI") {
    const basis = normalizeSemiMeasureBasis(lot?.measure_basis || deriveSemiLotMeasureBasis(lot));
    if (basis === "PIECE") {
      return lotAvailableQty(lot) > 0;
    }
    if (basis === "WEIGHT") {
      return lotAvailableWeight(lot) > 0;
    }
    return lotAvailableQty(lot) > 0 || lotAvailableWeight(lot) > 0;
  }
  return lotAvailableWeight(lot) > 0;
}

function compareInventoryLots(left, right) {
  const leftRank = isInventoryLotUsable(left) ? 0 : 1;
  const rightRank = isInventoryLotUsable(right) ? 0 : 1;
  if (leftRank !== rightRank) {
    return leftRank - rightRank;
  }
  return String(left.lot_no || "").localeCompare(String(right.lot_no || ""), "zh-CN", { numeric: true }) || Number(left.lot_id) - Number(right.lot_id);
}

const sortedSelectedLots = computed(() => [...selectedLots.value].sort(compareInventoryLots));
const selectedOverviewLots = computed(() => sortedSelectedLots.value.slice(0, 8));
const selectedSemiDetailMeasureBasis = computed(() =>
  selectedWarehouseType.value === "SEMI" ? normalizeSemiMeasureBasis(selectedBalance.value?.measure_basis || deriveSemiMeasureBasisFromRows(selectedLots.value)) : ""
);
const isSelectedFinishedWarehouse = computed(() => selectedWarehouseType.value === "FINISHED");
const selectedLotNoColumnLabel = computed(() =>
  selectedWarehouseType.value === "SEMI" ? "来源库存批次号" : "库存批次号"
);
const selectedDetailEntityLabel = computed(() => {
  if (selectedWarehouseType.value === "SEMI") {
    return "半成品";
  }
  if (isSelectedFinishedWarehouse.value || selectedWarehouseType.value === "RETURN") {
    return "产品";
  }
  return "物料";
});
const selectedSourceLotNos = computed(() =>
  Array.from(
    new Set(
      sortedSelectedLots.value
        .filter((lot) => shouldDisplaySourceMaterialLotNo(selectedWarehouseType.value) || isInventoryLotUsable(lot))
        .map((lot) => (shouldDisplaySourceMaterialLotNo(selectedWarehouseType.value) ? formatLotSourceLots(lot) : lot.lot_no))
        .filter((lotNo) => lotNo && lotNo !== "-")
    )
  )
);
const selectedSourceLotEmptyText = computed(() =>
  shouldDisplaySourceMaterialLotNo(selectedWarehouseType.value) ? "暂无来源库存批次号" : "暂无可用库存批次"
);
const selectedSemiOperationLabel = computed(() => formatSemiCompletedOperation(selectedBalance.value));
const selectedLotSummary = computed(() => {
  const totalCount = selectedLots.value.length;
  const availableRows = selectedLots.value.filter((lot) => isInventoryLotUsable(lot));
  return {
    totalCount,
    availableCount: availableRows.length,
    remainingQty: selectedLots.value.reduce((total, lot) => total + Number(lot.remaining_qty || 0), 0),
    availableQty: availableRows.reduce((total, lot) => total + lotAvailableQty(lot), 0),
    remainingWeight: selectedLots.value.reduce((total, lot) => total + Number(lot.remaining_weight_kg || 0), 0),
    availableWeight: availableRows.reduce((total, lot) => total + lotAvailableWeight(lot), 0)
  };
});
const selectedPrimaryAvailableLabel = computed(() => {
  if (isSelectedFinishedWarehouse.value || selectedQuantityOnly.value || (selectedWarehouseType.value === "SEMI" && selectedSemiDetailMeasureBasis.value === "PIECE")) {
    return "可用数量";
  }
  return "可用重量";
});
const selectedPrimaryAvailableValue = computed(() => {
  if (isSelectedFinishedWarehouse.value || selectedQuantityOnly.value || (selectedWarehouseType.value === "SEMI" && selectedSemiDetailMeasureBasis.value === "PIECE")) {
    return formatQty(selectedLotSummary.value.availableQty);
  }
  return formatWeight(selectedLotSummary.value.availableWeight);
});
const selectedPrimaryOnHandLabel = computed(() => {
  if (isSelectedFinishedWarehouse.value || selectedQuantityOnly.value || (selectedWarehouseType.value === "SEMI" && selectedSemiDetailMeasureBasis.value === "PIECE")) {
    return "现存数量";
  }
  return "现存重量";
});
const selectedPrimaryOnHandValue = computed(() => {
  if (isSelectedFinishedWarehouse.value || selectedQuantityOnly.value || (selectedWarehouseType.value === "SEMI" && selectedSemiDetailMeasureBasis.value === "PIECE")) {
    return formatQty(selectedLotSummary.value.remainingQty);
  }
  return formatWeight(selectedLotSummary.value.remainingWeight);
});
const selectedAuxiliaryMetricLabel = computed(() => {
  if (isSelectedFinishedWarehouse.value) {
    return "辅助重量";
  }
  if (selectedWarehouseType.value === "SEMI") {
    return "已完成工序";
  }
  return "可用批次";
});
const selectedAuxiliaryMetricValue = computed(() =>
  isSelectedFinishedWarehouse.value
    ? formatWeight(selectedLotSummary.value.remainingWeight)
    : selectedWarehouseType.value === "SEMI"
      ? selectedSemiOperationLabel.value
      : `${selectedLotSummary.value.availableCount} / ${selectedLotSummary.value.totalCount}`
);
const selectedOverviewAvailableLabel = computed(() => {
  if (isSelectedFinishedWarehouse.value || selectedWarehouseType.value === "AUX" || (selectedWarehouseType.value === "SEMI" && selectedSemiDetailMeasureBasis.value === "PIECE")) {
    return "可用数量";
  }
  return "可用重量(kg)";
});
const selectedUnitCostSuffix = computed(() => {
  if (selectedWarehouseType.value === "FINISHED" || selectedWarehouseType.value === "AUX") {
    return "/件";
  }
  if (selectedWarehouseType.value === "SEMI" && selectedSemiDetailMeasureBasis.value === "PIECE") {
    return "/件";
  }
  return "/kg";
});
const selectedUnitCostColumnLabel = computed(() => {
  if (selectedWarehouseType.value === "FINISHED") {
    return "入库材料成本(毛重；元/件)";
  }
  return selectedUnitCostSuffix.value === "/件" ? "入库单价(元/件)" : "入库单价(元/kg)";
});
const overviewLotDrawerColumns = computed(() => [
  { key: "lot_no", label: selectedLotNoColumnLabel.value, compactLabel: selectedLotNoColumnLabel.value, type: "code", width: "220px" },
  { key: "available", label: selectedOverviewAvailableLabel.value, compactLabel: isSelectedFinishedWarehouse.value || selectedQuantityOnly.value ? "可用数" : "可用重量", type: "number", width: "108px", format: (_value, row) => formatLotAvailableByBasis(row) },
  ...(isSelectedFinishedWarehouse.value
    ? [{ key: "auxiliary_weight", label: "辅助重量(kg)", compactLabel: "辅助重量", type: "number", width: "110px", format: (_value, row) => formatWeight(lotAvailableWeight(row)) }]
    : []),
  { key: "unit_cost", label: selectedUnitCostColumnLabel.value, compactLabel: isSelectedFinishedWarehouse.value ? "材料成本" : "单价", type: "number", width: "116px", format: (_value, row) => formatSelectedUnitCost(row.unit_cost) },
  { key: "status", label: "状态", type: "status", width: "88px" }
]);
const lotDrawerColumns = computed(() => [
  { key: "lot_no", label: selectedLotNoColumnLabel.value, compactLabel: selectedLotNoColumnLabel.value, type: "code", width: "220px" },
  ...(isSelectedFinishedWarehouse.value || selectedQuantityOnly.value ? [{ key: "inbound_qty", label: "入库数量", compactLabel: "入库数", type: "number", width: "96px", format: (_value, row) => formatQty(row.inbound_qty) }] : []),
  ...(selectedWeightOnly.value ? [] : [{ key: "remaining_qty", label: isSelectedFinishedWarehouse.value || selectedQuantityOnly.value ? "剩余数量" : "变动数量", compactLabel: isSelectedFinishedWarehouse.value || selectedQuantityOnly.value ? "剩余数" : "变动数", type: "number", width: "96px", format: (_value, row) => selectedWarehouseType.value === "SEMI" ? formatSemiMeasureValue(row, row.remaining_qty, "PIECE") : formatQty(row.remaining_qty) }]),
  ...(!selectedQuantityOnly.value ? [{ key: "inbound_weight_kg", label: "入库重量(kg)", compactLabel: "入库重量", type: "number", width: "110px", format: (_value, row) => selectedWarehouseType.value === "SEMI" ? formatSemiMeasureValue(row, row.inbound_weight_kg, "WEIGHT") : formatWeight(row.inbound_weight_kg) }] : []),
  ...(!selectedQuantityOnly.value ? [{ key: "remaining_weight_kg", label: "剩余重量(kg)", compactLabel: "剩余重量", type: "number", width: "110px", format: (_value, row) => selectedWarehouseType.value === "SEMI" ? formatSemiMeasureValue(row, row.remaining_weight_kg, "WEIGHT") : formatWeight(row.remaining_weight_kg) }] : []),
  { key: "unit_cost", label: selectedUnitCostColumnLabel.value, compactLabel: isSelectedFinishedWarehouse.value ? "材料成本" : "单价", type: "number", width: "116px", format: (_value, row) => formatSelectedUnitCost(row.unit_cost) },
  { key: "status", label: "状态", type: "status", width: "88px" },
  { key: "warehouse_name", label: "仓库", detailOnly: true },
  { key: "source_purchase_order_no", label: "采购单号", detailOnly: true },
  { key: "source_receipt_no", label: "入库单号", detailOnly: true },
  { key: "source_material_summary", label: "来源库存批次", detailOnly: true },
  { key: "source_doc_type", label: "来源", detailOnly: true, format: (_value, row) => formatSourceDocTypeLabel(row.source_doc_type) },
  { key: "logistics_waybill_no", label: "运单号", detailOnly: true },
  { key: "logistics_freight_amount", label: "运费", detailOnly: true, format: (_value, row) => formatFreight(row.logistics_freight_amount) },
  { key: "quality_release", label: "质检放行人", detailOnly: true, format: (_value, row) => row.item_type === "RAW_MATERIAL" ? formatLotQualityRelease(row) : "-" }
]);
const txnDrawerColumns = computed(() => [
  { key: "biz_time", label: "时间", type: "longText", width: "128px", format: (_value, row) => formatDateTime(row.biz_time) },
  { key: "txn_type", label: "类型", width: "94px", format: (_value, row) => formatInventoryTxnTypeLabel(row.txn_type) },
  { key: "lot_no", label: selectedLotNoColumnLabel.value, compactLabel: selectedLotNoColumnLabel.value, type: "code", width: "220px" },
  ...(selectedWeightOnly.value ? [] : [{ key: "qty_change", label: "变动数量", compactLabel: "变动数", type: "number", width: "96px", format: (_value, row) => formatQty(row.qty_change) }]),
  ...(!selectedQuantityOnly.value ? [{ key: "weight_change_kg", label: "变动重量(kg)", compactLabel: "变动重量", type: "number", width: "110px", format: (_value, row) => formatWeight(row.weight_change_kg) }] : []),
  { key: "unit_cost", label: isSelectedFinishedWarehouse.value ? "材料成本" : "单价", type: "number", width: "116px", format: (_value, row) => formatSelectedUnitCost(row.unit_cost) },
  { key: "amount", label: "金额", type: "number", width: "104px", format: (_value, row) => `¥${formatAmount(row.amount)}` },
  { key: "archive_actions", label: "单据留档", width: "210px" },
  { key: "source", label: "来源", detailOnly: true, format: (_value, row) => `${formatSourceDocTypeLabel(row.source_doc_type)} #${row.source_doc_id || "-"}` },
  { key: "remark", label: "备注", detailOnly: true }
]);
const selectedLotTableColumnCount = computed(() => {
  if (selectedWeightOnly.value) {
    return 13;
  }
  return isSelectedFinishedWarehouse.value ? 16 : 15;
});

function formatLotAvailableByBasis(lot) {
  if (normalizeWarehouseType(lot) === "FINISHED") {
    return formatQty(lotAvailableQty(lot));
  }
  if (normalizeWarehouseType(lot) === "AUX") {
    return formatQty(lotAvailableQty(lot));
  }
  if (normalizeWarehouseType(lot) === "SEMI") {
    const basis = normalizeSemiMeasureBasis(lot?.measure_basis || selectedSemiDetailMeasureBasis.value || deriveSemiLotMeasureBasis(lot));
    if (basis === "PIECE") {
      return formatQty(lotAvailableQty(lot));
    }
    if (basis === "WEIGHT") {
      return formatWeight(lotAvailableWeight(lot));
    }
  }
  return formatWeight(lotAvailableWeight(lot));
}

function formatSelectedUnitCost(value) {
  return `¥${formatAmount(value)}${selectedUnitCostSuffix.value}`;
}

function formatSelectedBalanceTitle(balance) {
  if (normalizeWarehouseType(balance) === "SEMI") {
    return balance?.item_name || balance?.item_code || "半成品明细";
  }
  return formatInventoryBalanceLabel(balance);
}

function extractSummaryPart(summary, label) {
  const escapedLabel = String(label).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const match = String(summary || "").match(new RegExp(`${escapedLabel}[:：]\\s*([^；;]+)`));
  return match?.[1]?.trim() || "";
}

function formatSemiCompletedOperation(row) {
  const summary = row?.operation_label || row?.source_material_summary || "";
  const operation = extractSummaryPart(summary, "已完成工序");
  if (operation) {
    return operation;
  }
  const fallback = String(summary || "")
    .split(/[；;]/)
    .map((part) => part.trim())
    .find((part) => part.includes("工序") && !part.includes("来源库存批次号"));
  return fallback || "未绑定工序";
}

function formatLotSourceLots(lot) {
  const explicitLotNo = lot?.source_material_lot_no || lot?.source_material_sub_batch_no || lot?.material_sub_batch_no;
  if (explicitLotNo) {
    return explicitLotNo;
  }
  return extractSummaryPart(lot?.source_material_summary, "来源库存批次号") || "-";
}

function shouldDisplaySourceMaterialLotNo(warehouseType) {
  return ["SEMI", "FINISHED", "SCRAP", "RETURN"].includes(String(warehouseType || "").toUpperCase());
}

function formatSelectedDetailLotNo(row) {
  if (shouldDisplaySourceMaterialLotNo(selectedWarehouseType.value)) {
    return formatLotSourceLots(row);
  }
  return row?.lot_no || "-";
}

function formatWarehouseLedgerLotNo(row) {
  if (shouldDisplaySourceMaterialLotNo(activeWarehouseType.value)) {
    return formatLotSourceLots(row);
  }
  return row?.lot_no || "-";
}

function formatWarehouseLedgerItemLabel(row) {
  const code = String(row?.item_code || "").trim();
  const name = String(row?.item_name || "").trim();
  if (code && name) {
    return `${code} · ${name}`;
  }
  return code || name || "-";
}

function formatWarehouseLedgerSourceLabel(row) {
  const sourceLabel = formatSourceDocTypeLabel(row?.source_doc_type);
  const sourceId = row?.source_doc_id ? ` #${row.source_doc_id}` : "";
  return `${sourceLabel || ""}${sourceId}`.trim() || "-";
}

function formatWarehouseLedgerLogisticsLabel(row) {
  const waybillNo = String(row?.logistics_waybill_no || "").trim();
  if (waybillNo) {
    return waybillNo;
  }
  return row?.logistics_photo_url ? "有辅助照片" : "-";
}

function normalizeWarehouseLedgerTooltipValue(value) {
  const text = String(value ?? "").replace(/\s+/g, " ").trim();
  return text && text !== "-" ? text : "";
}

function warehouseLedgerTooltipAttrs(value) {
  const fullValue = normalizeWarehouseLedgerTooltipValue(value);
  if (!fullValue) {
    return {};
  }
  return {
    "data-table-tooltip": "auto",
    "data-table-full-value": fullValue,
    "aria-label": fullValue
  };
}

function lotStatusClass(lot) {
  if (isInventoryLotUsable(lot)) {
    return "status-success";
  }
  const status = String(lot?.status || "").toUpperCase();
  if (status === "LOCKED" || status === "PENDING_QC") {
    return "status-warning";
  }
  return "status-muted";
}

const selectedTransactions = computed(() =>
  transactions.value.filter((item) => {
    if (item.item_id !== selectedItemId.value) {
      return false;
    }
    if (selectedWarehouseType.value && normalizeWarehouseType(item) !== selectedWarehouseType.value) {
      return false;
    }
    if (selectedWarehouseType.value === "SEMI" && selectedBalance.value && Object.hasOwn(selectedBalance.value, "source_line_id")) {
      return Number(item.source_line_id || 0) === Number(selectedBalance.value.source_line_id || 0);
    }
    return true;
  })
);

const balanceControls = useTableControls(activeBalances, {
  searchFields: ["item_code", "item_name", "specification", "stock_unit_name", "purchase_unit_name", "sales_unit_name", "warehouse_name", "location_name", "operation_label", "source_material_summary"],
  sortOptions: [
    { key: "item_code", label: "编码" },
    { key: "item_name", label: "名称" },
    { key: "operation_label", label: "半成品" },
    { key: "warehouse_name", label: "仓库" },
    { key: "weight_available_kg", label: "可用重量" },
    { key: "qty_available", label: "可用数量" },
    { key: "avg_unit_cost", label: "均价" }
  ],
  defaultSortKey: "item_code",
  defaultSortDirection: "asc"
});
const lotControls = useTableControls(sortedSelectedLots, {
  searchFields: [
    "lot_no",
    "warehouse_name",
    "status",
    "quality_user_name",
    "source_purchase_order_no",
    "source_receipt_no",
    "source_material_lot_no",
    "source_material_sub_batch_no",
    "source_material_summary",
    (row) => formatLotSourceLots(row),
    "logistics_waybill_no",
    (row) => formatStatusLabel(row.status),
    (row) => formatQualityStatusLabel(row.quality_status),
    (row) => formatItemTypeLabel(row.item_type)
  ],
  sortOptions: [
    { key: "availability", label: "可用优先", field: (row) => (isInventoryLotUsable(row) ? 0 : 1) },
    { key: "lot_no", label: selectedLotNoColumnLabel.value, field: (row) => formatSelectedDetailLotNo(row) },
    { key: "remaining_weight_kg", label: "剩余重量(kg)" },
    { key: "status", label: "状态" }
  ],
  defaultSortKey: "availability",
  defaultSortDirection: "asc"
});
const txnControls = useTableControls(selectedTransactions, {
  searchFields: [
    "txn_type",
    "lot_no",
    "source_material_lot_no",
    "source_material_sub_batch_no",
    "source_material_summary",
    "source_doc_type",
    "logistics_waybill_no",
    (row) => formatLotSourceLots(row),
    (row) => formatInventoryTxnTypeLabel(row.txn_type),
    (row) => formatSourceDocTypeLabel(row.source_doc_type)
  ],
  sortOptions: [
    { key: "biz_time", label: "时间" },
    { key: "txn_type", label: "类型" },
    { key: "lot_no", label: selectedLotNoColumnLabel.value, field: (row) => formatSelectedDetailLotNo(row) },
    { key: "weight_change_kg", label: "变动重量(kg)" }
  ],
  defaultSortKey: "biz_time",
  defaultSortDirection: "desc"
});
const filteredBalances = balanceControls.rows;
const filteredLots = lotControls.rows;
const filteredTransactions = txnControls.rows;
const filteredRawCount = computed(() => rawBalances.value.length);
const filteredSemiCount = computed(() => semiFinishedBalances.value.length);
const filteredFinishedCount = computed(() => finishedBalances.value.length);
const filteredAuxiliaryCount = computed(() => auxiliaryBalances.value.length);
const filteredScrapCount = computed(() => scrapBalances.value.length);
const filteredReturnCount = computed(() => returnBalances.value.length);

const {
  page: balancePage,
  pageSize: balancePageSize,
  rows: paginatedBalances
} = usePagination(filteredBalances);
const {
  page: lotPage,
  pageSize: lotPageSize,
  rows: paginatedLots
} = usePagination(filteredLots);
const {
  page: txnPage,
  pageSize: txnPageSize,
  rows: paginatedTransactions
} = usePagination(filteredTransactions);

const inventoryOverlayOpen = computed(
  () =>
    detailDrawerOpen.value ||
    genericDrawerOpen.value ||
    specialAdjustmentDrawerOpen.value ||
    rawReturnDrawerOpen.value ||
    productionDrawerOpen.value ||
    completionDrawerOpen.value ||
    deliveryDrawerOpen.value ||
    returnInboundDrawerOpen.value ||
    returnScrapInboundDrawerOpen.value ||
    returnReworkDrawerOpen.value ||
    warehouseLedgerDrawerOpen.value ||
    productionWorkOrderInboundDrawerOpen.value ||
    openingImportDrawerOpen.value ||
    stocktakeDialogOpen.value
);

useBodyScrollLock(inventoryOverlayOpen);

function makeOperation(key, direction, bizType, label, title, drawerType = "generic") {
  return { key, direction, bizType, label, title, drawerType };
}

function buildEmptyWarehouseLedgerSummary() {
  return {
    in_count: 0,
    out_count: 0,
    adjust_count: 0,
    in_qty: 0,
    out_qty: 0,
    in_weight_kg: 0,
    out_weight_kg: 0,
    latest_biz_time: null
  };
}

function buildEmptyWarehouseLedgerFilters() {
  return {
    keyword: "",
    direction: "ALL",
    txn_type: "",
    start_date: "",
    end_date: "",
    sort_key: "biz_time",
    sort_direction: "desc"
  };
}

function buildEmptyGenericForm() {
  return {
    work_order_id: "",
    item_option_key: "",
    item_id: "",
    warehouse_id: "",
    location_id: "",
    weight_kg: 0,
    qty: 0,
    measure_basis: "",
    unit_cost: 0,
    sale_unit_price: 0,
    buyer_name: "",
    lot_no: "",
    provider_name: "",
    source_doc_id: null,
    source_line_id: null,
    source_material_sub_batch_no: "",
    sale_remark: "",
    waybill_no: "",
    order_photo_url: "",
    freight_amount: "",
    outsourcing_party_name: "",
    remark: "",
    settle_work_order: false,
    settle_remark: ""
  };
}

function buildEmptySpecialAdjustmentForm() {
  return {
    direction: "IN",
    warehouse_id: "",
    reason: "",
    lines: []
  };
}

function buildEmptyRawReturnForm() {
  return {
    warehouse_id: "",
    lot_id: "",
    purchase_order_item_id: "",
    return_weight_kg: 0,
    waybill_no: "",
    order_photo_url: "",
    freight_amount: "",
    reason: "",
    remark: ""
  };
}

function buildEmptyProductionForm() {
  return {
    product_item_id: "",
    issue_weight_kg: 0,
    remark: ""
  };
}

function buildEmptyProductionWorkOrderInboundForm() {
  return {
    finished_qty: 0,
    surplus_weight_kg: 0,
    scrap_weight_kg: 0,
    deviation_remark: "",
    remark: "",
    settle_work_order: false
  };
}

function buildEmptyCompletionForm() {
  return {
    work_order_id: "",
    warehouse_id: "",
    receiver_employee_id: "",
    product_item_id: "",
    location_id: "",
    max_receivable_qty: 0,
    tolerance_receivable_qty: 0,
    unit_weight_kg: 0,
    receipt_qty: 0,
    receipt_weight_kg: 0,
    remark: "",
    settle_work_order: false,
    settle_remark: ""
  };
}

function buildEmptyDeliveryForm() {
  return {
    delivery_mode: "SALES_ORDER",
    sales_order_id: "",
    sales_order_item_id: "",
    product_item_id: "",
    warehouse_id: "",
    customer_id: "",
    shipper_employee_id: "",
    consignee_name: "",
    consignee_phone: "",
    delivery_address: "",
    delivery_qty: 0,
    waybill_no: "",
    order_photo_url: "",
    freight_amount: "",
    remark: ""
  };
}

function buildEmptyReturnInboundForm() {
  return {
    warehouse_id: "",
    delivery_item_id: "",
    return_qty: 0,
    return_weight_kg: 0,
    handler_employee_id: "",
    return_reason: "",
    remark: ""
  };
}

function buildEmptyReturnScrapInboundForm() {
  return {
    warehouse_id: "",
    delivery_item_id: "",
    scrap_qty: 0,
    scrap_weight_kg: 0,
    unit_cost: "",
    handler_employee_id: "",
    return_reason: "",
    remark: ""
  };
}

function buildEmptyReturnReworkForm() {
  return {
    return_item_id: "",
    extra_cost_amount: 0,
    remark: ""
  };
}

function isFreightBlank(value) {
  return value === "" || value === null || value === undefined || Number.isNaN(Number(value));
}

function validateLogisticsForm(form) {
  if (!String(form.waybill_no || "").trim() && !String(form.order_photo_url || "").trim()) {
    return "请填写运单号或上传辅助照片";
  }
  if (isFreightBlank(form.freight_amount)) {
    return "请填写运费";
  }
  if (Number(form.freight_amount) < 0) {
    return "运费不能小于0";
  }
  return "";
}

async function handleLogisticsPhotoUpload(form, event) {
  const file = event.target.files?.[0];
  if (!file) {
    return;
  }
  const formData = new FormData();
  formData.append("file", file);
  logisticsPhotoUploading.value = true;
  try {
    const result = await uploadResource("/inventory/logistics-photos", formData);
    form.order_photo_url = result.url;
    feedbackMessage.value = "辅助照片已上传";
    errorMessage.value = "";
  } catch (error) {
    form.order_photo_url = "";
    errorMessage.value = error.message || "辅助照片上传失败";
  } finally {
    logisticsPhotoUploading.value = false;
    event.target.value = "";
  }
}

async function openLogisticsPhoto(url) {
  if (!url) {
    return;
  }
  try {
    await openResource(url);
  } catch (error) {
    errorMessage.value = error.message || "辅助照片打开失败";
  }
}

function formatFreight(value) {
  if (value === null || value === undefined || value === "") {
    return "-";
  }
  return `¥${formatAmount(value)}`;
}

function formatPercent(value) {
  const numeric = Number(value || 0);
  if (!Number.isFinite(numeric)) {
    return "-";
  }
  return `${(numeric * 100).toFixed(2)}%`;
}

function resetFeedback() {
  feedbackMessage.value = "";
  errorMessage.value = "";
  lastWarehouseArchiveResult.value = null;
}

function formatArchiveFeedback(result) {
  if (!result?.archive_status) {
    return "";
  }
  if (result.archive_status === "已归档") {
    return "，PDF归档已生成";
  }
  if (result.archive_status === "归档失败") {
    return `，PDF归档失败${result.archive_error_message ? `：${result.archive_error_message}` : ""}`;
  }
  return `，PDF归档状态：${result.archive_status}`;
}

function rememberWarehouseArchiveResult(result, fallbackLabel = "仓库出入库单") {
  if (!result?.archive_business_id) {
    lastWarehouseArchiveResult.value = null;
    return;
  }
  lastWarehouseArchiveResult.value = {
    archive_status: result.archive_status || "未生成",
    archive_business_id: result.archive_business_id,
    archive_document_type: result.archive_document_type || "仓库出入库单",
    archive_error_message: result.archive_error_message || "",
    txn_no: result.lot_no || result.material_lot_no || fallbackLabel
  };
}

function resetGenericForm() {
  Object.assign(genericForm, buildEmptyGenericForm());
  genericForm.warehouse_id = activeWarehouses.value[0]?.id || "";
  genericForm.unit_cost = activeOperation.value?.bizType === "CUSTOMER_SUPPLIED" ? 0 : 0;
  genericSourceLotOptionsRequestSeq += 1;
  selectedGenericSourceLots.value = [];
  genericSourceLotOptions.value = [];
}

function resetSpecialAdjustmentForm() {
  Object.assign(specialAdjustmentForm, buildEmptySpecialAdjustmentForm());
  specialAdjustmentForm.direction = activeOperation.value?.direction === "out" ? "OUT" : "IN";
  specialAdjustmentForm.warehouse_id = activeWarehouses.value[0]?.id || "";
}

function resetRawReturnForm() {
  rawReturnLinksRequestSeq += 1;
  Object.assign(rawReturnForm, buildEmptyRawReturnForm());
  rawReturnForm.warehouse_id = activeWarehouses.value[0]?.id || "";
  rawReturnPurchaseLinks.value = [];
  rawReturnLinkLoading.value = false;
}

function buildInventoryLotNoPreview(material, lotRows) {
  const materialItemId = Number(material?.item_id || material?.id || 0);
  if (!materialItemId) {
    return "";
  }
  const materialCode = String(material?.item_code || "");
  const digits = materialCode.replace(/\D/g, "");
  const codeTail = digits ? digits.slice(-5).padStart(5, "0") : String(materialItemId % 100000).padStart(5, "0");
  const rawName = String(material?.item_name || "物料");
  const materialName = rawName.replace(/\s+/g, "").trim() || "物料";
  const maxNameLength = 50 - "JH_".length - codeTail.length - "_0001".length;
  const prefix = `JH_${materialName.slice(0, maxNameLength)}${codeTail}_`;
  const maxSequence = lotRows
    .filter((lot) => Number(lot.item_id || 0) === materialItemId)
    .reduce((currentMax, lot) => {
      const match = String(lot.lot_no || "").match(/_(\d{4})$/);
      return match ? Math.max(currentMax, Number(match[1])) : currentMax;
    }, 0);
  return `${prefix}${String(maxSequence + 1).padStart(4, "0")}`;
}

function formatGenericItemOption(item) {
  if (activeWarehouseType.value === "SEMI") {
    const operationLabel = item.operation_label || item.source_material_summary || "";
    const specificationLabel = item.specification ? ` · ${item.specification}` : "";
    const unitLabel = formatInventoryUnitLabel(item);
    const baseLabel = `${item.item_code} · ${item.item_name}${specificationLabel}${unitLabel !== "-" ? ` · ${unitLabel}` : ""}${item.version_no ? ` · ${item.version_no}` : ""}`;
    const stockLabel = activeOperation.value?.direction === "out" ? ` · ${formatSemiAvailability(item)}` : "";
    return `${baseLabel}${operationLabel ? ` · ${operationLabel}` : ""}${stockLabel}`;
  }
  const specificationLabel = item.specification ? ` · ${item.specification}` : "";
  const unitLabel = formatInventoryUnitLabel(item);
  const baseLabel = `${item.item_code} · ${item.item_name}${specificationLabel}${unitLabel !== "-" ? ` · ${unitLabel}` : ""}${item.version_no ? ` · ${item.version_no}` : ""}`;
  if (activeOperation.value?.direction !== "out") {
    return baseLabel;
  }
  if (activeWarehouseType.value === "AUX") {
    return `${baseLabel} · 可用 ${formatQty(item.qty_available)}`;
  }
  if (activeWeightOnly.value) {
    return `${baseLabel} · 可用 ${formatWeight(item.weight_available_kg)}`;
  }
  return `${baseLabel} · 可用 ${formatQty(item.qty_available)} / ${formatWeight(item.weight_available_kg)}`;
}

function formatWorkOrderLedgerOption(workOrder, bizType) {
  const baseLabel = `${workOrder.work_order_no || "-"} · ${workOrder.product_name || "-"}`;
  if (bizType === "PRODUCTION_SCRAP_IN") {
    return `${baseLabel} · 库外未闭环 ${formatWeight(workOrder.outside_weight_kg || workOrder.limits?.max_scrap_inbound_weight_kg)}`;
  }
  if (bizType === "REWORK_SCRAP_IN") {
    return `${baseLabel} · 返工废料入库`;
  }
  if (bizType === "PRODUCTION_SURPLUS") {
    return `${baseLabel} · 可归还余料 ${formatWeight(workOrder.outside_weight_kg || workOrder.limits?.max_surplus_inbound_weight_kg)}`;
  }
  return `${baseLabel} · 可入库 ${formatQty(workOrder.limits?.max_finished_inbound_qty)}`;
}

function formatProductionWorkOrderInboundOption(workOrder) {
  return [
    workOrder.material_lot_no || workOrder.source_lot_summary || workOrder.work_order_no || "-",
    workOrder.product_name || "-",
    `累计领料 ${formatWeight(workOrder.total_issued_weight_kg || workOrder.issued_weight_kg)}`,
    `库外未闭环 ${formatWeight(workOrder.outside_weight_kg)}`
  ].join(" · ");
}

function buildWorkOrderSourceLotOptions(workOrder) {
  if (!workOrder) {
    return [];
  }
  const stockLotById = new Map(lots.value.map((lot) => [Number(lot.lot_id || lot.id || 0), lot]));
  return (workOrder.materials || [])
    .map((material) => {
      const sourceLotId = Number(material.source_lot_id || 0);
      const stockLot = stockLotById.get(sourceLotId) || {};
      const issuedWeight = Number(material.issued_weight_kg || 0);
      const returnedWeight = Number(material.returned_weight_kg || 0);
      const returnLimit = computeWorkOrderSourceLotReturnLimit(workOrder, material);
      return {
        ...stockLot,
        lot_id: sourceLotId,
        lot_no: material.source_lot_no || stockLot.lot_no || "",
        item_id: Number(material.material_item_id || stockLot.item_id || 0),
        item_code: material.material_code || stockLot.item_code || "",
        item_name: material.material_name || stockLot.item_name || "",
        warehouse_id: stockLot.warehouse_id || genericForm.warehouse_id,
        warehouse_type: stockLot.warehouse_type || "RAW",
        remaining_weight_kg: Number(stockLot.remaining_weight_kg || 0),
        issued_weight_kg: issuedWeight,
        returned_weight_kg: returnedWeight,
        source_remaining_weight_kg: Math.max(Number((issuedWeight - returnedWeight).toFixed(3)), 0),
        max_return_weight_kg: returnLimit.normal,
        tolerance_return_weight_kg: returnLimit.tolerance,
        unit_cost: Number(material.unit_cost ?? stockLot.unit_cost ?? 0)
      };
    })
    .filter((lot) => lot.lot_id && lot.item_id);
}

function buildScrapSaleSourceLotOptions(scrapItemId, warehouseId) {
  const itemId = Number(scrapItemId || 0);
  const selectedWarehouseId = Number(warehouseId || 0);
  if (!itemId || !selectedWarehouseId) {
    return [];
  }
  return lots.value
    .filter(
      (lot) =>
        Number(lot.item_id || 0) === itemId &&
        Number(lot.warehouse_id || 0) === selectedWarehouseId &&
        normalizeWarehouseType(lot) === "SCRAP"
    )
    .map((lot) => {
      const sourceLotNo =
        lot.source_material_lot_no ||
        lot.source_material_sub_batch_no ||
        extractSummaryPart(lot.source_material_summary, "来源库存批次号") ||
        lot.lot_no ||
        "";
      const availableWeight = Math.max(Number(lot.remaining_weight_kg || 0) - Number(lot.locked_weight_kg || 0), 0);
      const isAvailable =
        String(lot.status || "").toUpperCase() === "AVAILABLE" &&
        String(lot.quality_status || "").toUpperCase() === "PASS" &&
        availableWeight > 0;
      return {
        ...lot,
        lot_id: Number(lot.lot_id || lot.id || 0),
        lot_no: sourceLotNo,
        source_stock_lot_no: lot.lot_no,
        source_material_sub_batch_no: sourceLotNo,
        remaining_weight_kg: Number(availableWeight.toFixed(3)),
        is_available_for_issue: isAvailable,
        disabled_reason: isAvailable ? null : "该废料批次不可用或无剩余重量"
      };
    })
    .filter((lot) => lot.lot_id && lot.lot_no)
    .sort((left, right) => {
      const leftRank = left.is_available_for_issue ? 0 : 1;
      const rightRank = right.is_available_for_issue ? 0 : 1;
      if (leftRank !== rightRank) {
        return leftRank - rightRank;
      }
      return String(left.lot_no || "").localeCompare(String(right.lot_no || ""), "zh-CN", { numeric: true }) || Number(left.lot_id) - Number(right.lot_id);
    });
}

function computeWorkOrderSourceLotReturnLimit(workOrder, material) {
  const issuedWeight = Number(material?.issued_weight_kg || 0);
  const returnedWeight = Number(material?.returned_weight_kg || 0);
  const issuedWorkOrderWeight = Number(workOrder?.limits?.issued_weight_kg || workOrder?.issued_weight_kg || 0);
  const usedWorkOrderWeight = Number(workOrder?.limits?.used_material_weight_kg || workOrder?.used_material_weight_kg || 0);
  const sourceUsedWeight = issuedWorkOrderWeight > 0 ? (usedWorkOrderWeight * issuedWeight) / issuedWorkOrderWeight : 0;
  const sourceExpectedReturnWeight = Math.max(issuedWeight - sourceUsedWeight, 0);
  const sourceLotRemaining = Math.max(sourceExpectedReturnWeight - returnedWeight, 0);
  const sourceIssuedRemaining = Math.max(issuedWeight - returnedWeight, 0);
  const normalWorkOrderLimit = Number(workOrder?.limits?.max_surplus_inbound_weight_kg || workOrder?.max_surplus_inbound_weight_kg || 0);
  const workOrderReturnedWeight = Number(workOrder?.limits?.returned_material_weight_kg || workOrder?.returned_material_weight_kg || 0);
  const expectedWorkOrderReturnWeight = issuedWorkOrderWeight > 0 ? issuedWorkOrderWeight - usedWorkOrderWeight : workOrderReturnedWeight + normalWorkOrderLimit;
  const toleranceWorkOrderLimit = Number(
    workOrder?.limits?.tolerance_surplus_inbound_weight_kg ??
      Math.max(expectedWorkOrderReturnWeight * 1.15 - workOrderReturnedWeight, 0)
  );
  const sourceToleranceLimit = Math.max(sourceExpectedReturnWeight * 1.15 - returnedWeight, 0);
  return {
    normal: Number(Math.min(sourceLotRemaining, Math.max(normalWorkOrderLimit, 0)).toFixed(3)),
    tolerance: Number(Math.min(sourceIssuedRemaining, sourceToleranceLimit, Math.max(toleranceWorkOrderLimit, 0)).toFixed(3))
  };
}

function productionSurplusReturnInputMax(lot) {
  if (!isProductionSurplusInbound.value) {
    return lot?.tolerance_return_weight_kg ?? lot?.max_return_weight_kg ?? undefined;
  }
  return lot?.source_remaining_weight_kg ?? undefined;
}

function productionSurplusReturnInputDisabled(lot) {
  return isProductionSurplusInbound.value && Number(lot?.source_remaining_weight_kg || 0) <= 0;
}

function applyGenericWorkOrder() {
  selectedGenericSourceLots.value = [];
  genericSourceLotOptions.value = [];
  genericForm.settle_work_order = false;
  genericForm.settle_remark = "";
  const workOrder = selectedGenericWorkOrder.value;
  if (!workOrder) {
    genericForm.item_option_key = "";
    genericForm.item_id = "";
    genericForm.source_doc_id = null;
    genericForm.source_line_id = null;
    genericForm.source_material_sub_batch_no = "";
    genericForm.weight_kg = 0;
    return;
  }
  const firstMaterial = (workOrder.materials || [])[0] || null;
  if (isReworkScrapInbound.value) {
    genericForm.item_option_key = String(workOrder.product_item_id || "");
    genericForm.item_id = Number(workOrder.product_item_id || 0);
    genericForm.unit_cost = 0;
  } else if (firstMaterial) {
    genericForm.item_option_key = String(firstMaterial.material_item_id);
    genericForm.item_id = Number(firstMaterial.material_item_id);
    genericForm.unit_cost = Number(firstMaterial.unit_cost || 0);
  }
  genericForm.source_doc_id = Number(workOrder.work_order_id);
  genericForm.source_line_id = null;
  genericForm.source_material_sub_batch_no = workOrder.source_lot_summary || "";
  genericForm.provider_name = workOrder.work_order_no || "";
  if (isProductionScrapInbound.value) {
    genericForm.weight_kg = Number(workOrder.limits?.max_scrap_inbound_weight_kg || 0);
    genericForm.qty = 0;
  } else if (isReworkScrapInbound.value) {
    genericForm.weight_kg = 0;
    genericForm.qty = Number(workOrder.limits?.max_rework_scrap_inbound_qty || workOrder.max_rework_scrap_inbound_qty || 0);
  } else if (isProductionSurplusInbound.value) {
    genericForm.weight_kg = 0;
  }
  if (isProductionSurplusInbound.value) {
    loadGenericSourceLotOptions(genericSourceLotMaterialIds.value);
  }
}

function handleGenericMeasureBasisChange() {
  if (genericForm.measure_basis === "PIECE") {
    genericForm.weight_kg = 0;
  } else if (genericForm.measure_basis === "WEIGHT") {
    genericForm.qty = 0;
  }
}

function syncGenericSemiMeasureBasis() {
  if (isSemiSourceLotTraceInbound.value) {
    const existingBasis = normalizeSemiMeasureBasis(selectedSemiExistingMeasureBasis.value);
    if (["PIECE", "WEIGHT"].includes(existingBasis)) {
      genericForm.measure_basis = existingBasis;
    } else if (!["PIECE", "WEIGHT"].includes(normalizeSemiMeasureBasis(genericForm.measure_basis))) {
      genericForm.measure_basis = "";
    }
    handleGenericMeasureBasisChange();
    return;
  }
  if (isSemiAutoSourceOutbound.value) {
    genericForm.measure_basis = normalizeSemiMeasureBasis(selectedGenericItemOption.value?.measure_basis || selectedSemiExistingMeasureBasis.value);
    handleGenericMeasureBasisChange();
  }
}

function handleGenericItemChange() {
  const selectedOption = selectedGenericItemOption.value;
  genericForm.item_id = selectedOption?.item_id || "";
  genericForm.source_doc_id = selectedOption?.route_id || selectedOption?.source_doc_id || null;
  genericForm.source_line_id = selectedOption?.route_operation_id || selectedOption?.source_line_id || null;
  syncGenericSemiMeasureBasis();
}

function formatLotQualityRelease(lot) {
  const qualityStatus = String(lot?.quality_status || "").toUpperCase();
  if (qualityStatus === "PENDING_QC") {
    return "待放行";
  }
  return lot?.quality_user_name || formatQualityStatusLabel(qualityStatus);
}

function handleGenericWarehouseChange() {
  genericForm.location_id = "";
  if (!genericItemOptions.value.some((item) => String(item.option_key || item.item_id) === String(genericForm.item_option_key || ""))) {
    genericForm.item_option_key = "";
    genericForm.item_id = "";
    genericForm.source_doc_id = null;
    genericForm.source_line_id = null;
    genericForm.measure_basis = "";
    genericForm.qty = 0;
    genericForm.weight_kg = 0;
  }
  syncGenericSemiMeasureBasis();
}

function buildSpecialAdjustmentEmptyLine() {
  return {
    row_key: `special-adjustment-${Date.now()}-${Math.random().toString(36).slice(2)}`,
    inbound_mode: "EXISTING",
    lot_id: "",
    item_option_key: "",
    item_id: "",
    item_name: "",
    lot_no: "",
    location_id: "",
    measure_mode: activeWarehouseType.value === "SEMI" ? "QTY" : specialAdjustmentMeasureMode(activeWarehouseType.value),
    adjust_qty: "",
    adjust_weight_kg: "",
    unit_cost: "",
    line_reason: ""
  };
}

function resetSpecialAdjustmentLineSelection(line, inboundMode = line?.inbound_mode || "EXISTING") {
  const rowKey = line?.row_key || `special-adjustment-${Date.now()}-${Math.random().toString(36).slice(2)}`;
  Object.assign(line, {
    ...buildSpecialAdjustmentEmptyLine(),
    row_key: rowKey,
    inbound_mode: inboundMode,
    measure_mode: activeWarehouseType.value === "SEMI" ? "QTY" : specialAdjustmentMeasureMode(activeWarehouseType.value)
  });
}

function handleSpecialAdjustmentWarehouseChange() {
  specialAdjustmentForm.lines = [];
}

function addSpecialAdjustmentLine() {
  resetFeedback();
  if (!String(specialAdjustmentForm.reason || "").trim()) {
    errorMessage.value = `${specialAdjustmentDirectionText.value}必须先填写调整说明`;
    return;
  }
  specialAdjustmentForm.lines.push(buildSpecialAdjustmentEmptyLine());
}

function removeSpecialAdjustmentLine(index) {
  specialAdjustmentForm.lines.splice(index, 1);
}

function selectedSpecialAdjustmentLot(line) {
  return specialAdjustmentLotOptions.value.find((lot) => Number(lot.lot_id || 0) === Number(line?.lot_id || 0)) || null;
}

function selectedSpecialAdjustmentItem(line) {
  return specialAdjustmentItemOptions.value.find((item) => String(item.option_key || item.item_id) === String(line?.item_option_key || "")) || null;
}

function specialAdjustmentLineUsesExistingLot(line) {
  return specialAdjustmentForm.direction === "OUT" || line?.inbound_mode !== "NEW";
}

function specialAdjustmentRawMeasureMode(line) {
  return specialAdjustmentMeasureMode(activeWarehouseType.value, selectedSpecialAdjustmentLot(line) || {});
}

function specialAdjustmentEffectiveMeasureMode(line) {
  const rawMode = specialAdjustmentRawMeasureMode(line);
  if (rawMode === "SEMI_SELECT") {
    return line.measure_mode === "WEIGHT" ? "WEIGHT" : "QTY";
  }
  return rawMode;
}

function handleSpecialAdjustmentMeasureModeChange(line) {
  if (line.measure_mode === "WEIGHT") {
    line.adjust_qty = "";
    return;
  }
  if (line.measure_mode === "QTY") {
    line.adjust_weight_kg = "";
  }
}

function handleSpecialAdjustmentInboundModeChange(line) {
  resetSpecialAdjustmentLineSelection(line, line.inbound_mode === "NEW" ? "NEW" : "EXISTING");
}

function applySpecialAdjustmentLot(line) {
  const lot = selectedSpecialAdjustmentLot(line);
  if (!lot) {
    resetSpecialAdjustmentLineSelection(line, line.inbound_mode || "EXISTING");
    return;
  }
  line.inbound_mode = "EXISTING";
  line.item_option_key = "";
  line.item_id = lot.item_id;
  line.item_name = lot.item_name || lot.item_code || "";
  line.lot_no = lot.lot_no || "";
  line.location_id = lot.location_id || "";
  line.unit_cost = Number(lot.unit_cost || 0);
  const measureMode = specialAdjustmentRawMeasureMode(line);
  line.measure_mode = measureMode === "SEMI_SELECT" ? "QTY" : measureMode;
  handleSpecialAdjustmentMeasureModeChange(line);
}

function applySpecialAdjustmentItem(line) {
  const item = selectedSpecialAdjustmentItem(line);
  if (!item) {
    resetSpecialAdjustmentLineSelection(line, "NEW");
    return;
  }
  line.inbound_mode = "NEW";
  line.lot_id = "";
  line.item_id = Number(item.item_id || 0);
  line.item_name = [item.item_code, item.item_name].filter(Boolean).join(" · ") || item.item_name || item.item_code || "";
  line.lot_no = "";
  line.location_id = "";
  line.unit_cost = Number(item.unit_cost ?? item.avg_unit_cost ?? 0);
  const measureMode = specialAdjustmentMeasureMode(activeWarehouseType.value, item || {});
  line.measure_mode = measureMode === "SEMI_SELECT" ? "QTY" : measureMode;
  handleSpecialAdjustmentMeasureModeChange(line);
}

function formatSpecialAdjustmentMeasureMode(line) {
  const mode = specialAdjustmentEffectiveMeasureMode(line);
  if (mode === "WEIGHT") {
    return "按重量调整";
  }
  if (mode === "QTY") {
    return "按数量调整";
  }
  return "按数量并记录辅助重量";
}

function specialAdjustmentShowQtyInput(line) {
  return ["QTY", "QTY_WITH_WEIGHT"].includes(specialAdjustmentEffectiveMeasureMode(line));
}

function specialAdjustmentShowWeightInput(line) {
  return ["WEIGHT", "QTY_WITH_WEIGHT"].includes(specialAdjustmentEffectiveMeasureMode(line));
}

function specialAdjustmentWeightRequired(line) {
  return specialAdjustmentEffectiveMeasureMode(line) === "WEIGHT";
}

function formatSpecialAdjustmentLotOption(lot) {
  const itemLabel = [lot.item_code, lot.item_name].filter(Boolean).join(" · ") || "库存物料";
  const lotLabel = lot.lot_no || "-";
  const qtyLabel = formatQty(lot.remaining_qty);
  const weightLabel = formatWeight(lot.remaining_weight_kg);
  return `${itemLabel} · ${lotLabel} · 当前数量 ${qtyLabel} · 当前重量 ${weightLabel}`;
}

function formatSpecialAdjustmentItemOption(item) {
  return formatGenericItemOption(item);
}

function specialAdjustmentLinePreview(line) {
  const lot = selectedSpecialAdjustmentLot(line);
  if (!lot) {
    if (specialAdjustmentForm.direction === "IN" && line?.inbound_mode === "NEW") {
      const itemLabel = line.item_name || "请选择物料";
      const qtyChange = specialAdjustmentShowQtyInput(line) ? Number(line.adjust_qty || 0) : 0;
      const weightChange = specialAdjustmentShowWeightInput(line) ? Number(line.adjust_weight_kg || 0) : 0;
      if (specialAdjustmentEffectiveMeasureMode(line) === "WEIGHT") {
        return `新增特殊明细：${itemLabel}；入库后重量 ${formatWeight(weightChange)}`;
      }
      if (specialAdjustmentEffectiveMeasureMode(line) === "QTY") {
        return `新增特殊明细：${itemLabel}；入库后数量 ${formatQty(qtyChange)}`;
      }
      return `新增特殊明细：${itemLabel}；入库后数量 ${formatQty(qtyChange)} / 重量 ${formatWeight(weightChange)}`;
    }
    return "请选择库存明细";
  }
  const multiplier = specialAdjustmentForm.direction === "OUT" ? -1 : 1;
  const qtyChange = specialAdjustmentShowQtyInput(line) ? Number(line.adjust_qty || 0) * multiplier : 0;
  const weightChange = specialAdjustmentShowWeightInput(line) ? Number(line.adjust_weight_kg || 0) * multiplier : 0;
  const beforeQty = Number(lot.remaining_qty || 0);
  const beforeWeight = Number(lot.remaining_weight_kg || 0);
  const afterQty = Math.max(beforeQty + qtyChange, 0);
  const afterWeight = Math.max(beforeWeight + weightChange, 0);
  if (specialAdjustmentEffectiveMeasureMode(line) === "WEIGHT") {
    return `调整前：${formatWeight(beforeWeight)}；调整后：${formatWeight(afterWeight)}`;
  }
  if (specialAdjustmentEffectiveMeasureMode(line) === "QTY") {
    return `调整前：${formatQty(beforeQty)}；调整后：${formatQty(afterQty)}`;
  }
  return `调整前：${formatQty(beforeQty)} / ${formatWeight(beforeWeight)}；调整后：${formatQty(afterQty)} / ${formatWeight(afterWeight)}`;
}

function resetProductionForm() {
  Object.assign(productionForm, buildEmptyProductionForm());
  productionStockLotOptionsRequestSeq += 1;
  selectedProductionStockLots.value = [];
  productionStockLotOptions.value = [];
}

function resetProductionWorkOrderInboundForm() {
  selectedInboundWorkOrderId.value = "";
  productionWorkOrderInboundPreview.value = null;
  productionWorkOrderInboundPreviewRequestSeq += 1;
  Object.assign(productionWorkOrderInboundForm, buildEmptyProductionWorkOrderInboundForm());
}

function resetCompletionForm() {
  Object.assign(completionForm, buildEmptyCompletionForm());
  completionForm.warehouse_id = finishedWarehouses.value[0]?.id || "";
}

function resetDeliveryForm() {
  Object.assign(deliveryForm, buildEmptyDeliveryForm());
  deliveryForm.warehouse_id = finishedWarehouses.value[0]?.id || "";
}

function setDeliveryMode(mode) {
  deliveryForm.delivery_mode = mode === "DIRECT_CUSTOMER" ? "DIRECT_CUSTOMER" : "SALES_ORDER";
  deliveryForm.sales_order_id = "";
  deliveryForm.sales_order_item_id = "";
  deliveryForm.product_item_id = "";
  deliveryForm.customer_id = "";
  deliveryForm.consignee_name = "";
  deliveryForm.consignee_phone = "";
  deliveryForm.delivery_address = "";
  deliveryForm.delivery_qty = 0;
}

function resetReturnInboundForm() {
  Object.assign(returnInboundForm, buildEmptyReturnInboundForm());
  returnInboundForm.warehouse_id = activeWarehouses.value[0]?.id || "";
}

function resetReturnScrapInboundForm() {
  Object.assign(returnScrapInboundForm, buildEmptyReturnScrapInboundForm());
  returnScrapInboundForm.warehouse_id = activeWarehouses.value[0]?.id || "";
}

function resetReturnReworkForm() {
  Object.assign(returnReworkForm, buildEmptyReturnReworkForm());
}

function selectInventoryTab(tabKey) {
  activeInventoryTab.value = tabKey;
  balancePage.value = 1;
  if (tabKey !== "semi" && balanceControls.sortKey.value === "operation_label") {
    balanceControls.sortKey.value = "item_code";
    balanceControls.sortDirection.value = "asc";
  }
}

function openWarehouseLedgerDrawer() {
  resetFeedback();
  warehouseLedgerDrawerOpen.value = true;
  warehouseLedgerPage.value = 1;
  Object.assign(warehouseLedgerFilters, buildEmptyWarehouseLedgerFilters());
  loadWarehouseLedger();
}

function openProductionWorkOrderInboundDrawer() {
  resetFeedback();
  resetProductionWorkOrderInboundForm();
  productionWorkOrderInboundDrawerOpen.value = true;
}

function closeProductionWorkOrderInboundDrawer() {
  productionWorkOrderInboundDrawerOpen.value = false;
}

function closeWarehouseLedgerDrawer() {
  warehouseLedgerDrawerOpen.value = false;
  clearWarehouseLedgerTooltipListeners();
}

async function loadProductionWorkOrderInboundPreview(useCurrentFinishedQty = false) {
  const requestSeq = ++productionWorkOrderInboundPreviewRequestSeq;
  if (!Number(selectedInboundWorkOrderId.value || 0)) {
    productionWorkOrderInboundPreview.value = null;
    return;
  }
  try {
    const params = new URLSearchParams({
      production_ledger_id: String(selectedInboundWorkOrderId.value)
    });
    if (useCurrentFinishedQty) {
      params.set("finished_qty", String(Number(productionWorkOrderInboundForm.finished_qty || 0)));
    }
    let result = await requestJson(`/production/production-ledger-inbounds/preview?${params.toString()}`, { method: "GET" });
    if (requestSeq !== productionWorkOrderInboundPreviewRequestSeq) {
      return;
    }
    if (!result?.production_ledger_id || !result?.material_lot_no) {
      throw new Error("生产台账入库预览数据不完整，请刷新后重试");
    }
    if (!useCurrentFinishedQty) {
      const defaultFinishedQty = Math.max(Number(result.reference_finished_qty || 0) - Number(result.finished_inbound_qty || 0), 0);
      productionWorkOrderInboundForm.finished_qty = Number(defaultFinishedQty.toFixed(3));
      if (defaultFinishedQty > 0) {
        params.set("finished_qty", String(productionWorkOrderInboundForm.finished_qty));
        result = await requestJson(`/production/production-ledger-inbounds/preview?${params.toString()}`, { method: "GET" });
        if (requestSeq !== productionWorkOrderInboundPreviewRequestSeq) {
          return;
        }
      }
    }
    productionWorkOrderInboundPreview.value = result;
    productionWorkOrderInboundForm.surplus_weight_kg = Number(result.theoretical_surplus_weight_kg || 0);
    productionWorkOrderInboundForm.scrap_weight_kg = Number(result.theoretical_scrap_weight_kg || 0);
    errorMessage.value = "";
  } catch (error) {
    if (requestSeq !== productionWorkOrderInboundPreviewRequestSeq) {
      return;
    }
    productionWorkOrderInboundPreview.value = null;
    errorMessage.value = error.message || "生产台账入库预览加载失败";
  }
}

function handleProductionWorkOrderInboundFinishedQtyInput(event) {
  const value = Number(event?.target?.value || 0);
  productionWorkOrderInboundForm.finished_qty = Number.isFinite(value) ? value : 0;
  loadProductionWorkOrderInboundPreview(true);
}

function resetWarehouseLedgerFilters() {
  Object.assign(warehouseLedgerFilters, buildEmptyWarehouseLedgerFilters());
  warehouseLedgerPage.value = 1;
  loadWarehouseLedger();
}

function formatWarehouseLedgerDirection(direction) {
  return {
    IN: "入库",
    OUT: "出库",
    ADJUST: "调整"
  }[String(direction || "").toUpperCase()] || "-";
}

function warehouseLedgerDirectionClass(direction) {
  return {
    IN: "status-success",
    OUT: "status-warning",
    ADJUST: "status-info"
  }[String(direction || "").toUpperCase()] || "status-muted";
}

function hideWarehouseLedgerCellTooltip() {
  warehouseLedgerTooltipElement?.classList.remove("table-cell-hover-tooltip-visible");
}

function ensureWarehouseLedgerTooltipElement() {
  if (typeof document === "undefined") {
    return null;
  }

  warehouseLedgerTooltipElement = document.querySelector(".table-cell-hover-tooltip");
  if (!warehouseLedgerTooltipElement) {
    warehouseLedgerTooltipElement = document.createElement("div");
    warehouseLedgerTooltipElement.className = "table-cell-hover-tooltip";
    warehouseLedgerTooltipElement.setAttribute("role", "tooltip");
    document.body.appendChild(warehouseLedgerTooltipElement);
  }

  return warehouseLedgerTooltipElement;
}

function positionWarehouseLedgerCellTooltip(event) {
  if (!warehouseLedgerTooltipElement || typeof window === "undefined") {
    return;
  }

  const padding = 14;
  const offset = 16;
  const rect = warehouseLedgerTooltipElement.getBoundingClientRect();
  const nextLeft = Math.min(event.clientX + offset, window.innerWidth - rect.width - padding);
  const nextTop = Math.min(event.clientY + offset, window.innerHeight - rect.height - padding);

  warehouseLedgerTooltipElement.style.left = `${Math.max(padding, nextLeft)}px`;
  warehouseLedgerTooltipElement.style.top = `${Math.max(padding, nextTop)}px`;
}

function getWarehouseLedgerTooltipCell(event) {
  const cell = event.target?.closest?.('td[data-table-tooltip="auto"]');
  const table = warehouseLedgerTableRef.value;
  if (!cell || !table?.contains(cell)) {
    return null;
  }
  return cell;
}

function showWarehouseLedgerCellTooltip(cell, event) {
  const fullValue = cell?.dataset?.tableFullValue || "";
  if (!fullValue) {
    hideWarehouseLedgerCellTooltip();
    return;
  }

  const tooltip = ensureWarehouseLedgerTooltipElement();
  if (!tooltip) {
    return;
  }

  tooltip.textContent = fullValue;
  tooltip.classList.add("table-cell-hover-tooltip-visible");
  positionWarehouseLedgerCellTooltip(event);
}

function clearWarehouseLedgerTooltipListeners() {
  warehouseLedgerTooltipCleanupHandlers.forEach((cleanup) => cleanup());
  warehouseLedgerTooltipCleanupHandlers = [];
  if (warehouseLedgerTooltipTable) {
    delete warehouseLedgerTooltipTable.dataset.warehouseLedgerTooltipBound;
    warehouseLedgerTooltipTable = null;
  }
  hideWarehouseLedgerCellTooltip();
}

function bindWarehouseLedgerCellTooltip() {
  const table = warehouseLedgerTableRef.value;
  if (!table || warehouseLedgerTooltipTable === table) {
    return;
  }

  clearWarehouseLedgerTooltipListeners();
  warehouseLedgerTooltipTable = table;
  table.dataset.warehouseLedgerTooltipBound = "true";

  const handleMouseOver = (event) => {
    const cell = getWarehouseLedgerTooltipCell(event);
    if (cell) {
      showWarehouseLedgerCellTooltip(cell, event);
    }
  };
  const handleMouseMove = (event) => {
    const cell = getWarehouseLedgerTooltipCell(event);
    if (cell) {
      showWarehouseLedgerCellTooltip(cell, event);
    }
  };
  const handleMouseOut = (event) => {
    const cell = getWarehouseLedgerTooltipCell(event);
    if (cell && !cell.contains(event.relatedTarget)) {
      hideWarehouseLedgerCellTooltip();
    }
  };
  const handleDocumentMouseMove = (event) => {
    if (!getWarehouseLedgerTooltipCell(event)) {
      hideWarehouseLedgerCellTooltip();
    }
  };

  table.addEventListener("mouseover", handleMouseOver);
  table.addEventListener("mousemove", handleMouseMove);
  table.addEventListener("mouseout", handleMouseOut);
  document.addEventListener("mousemove", handleDocumentMouseMove, true);
  window.addEventListener("scroll", hideWarehouseLedgerCellTooltip, true);
  window.addEventListener("blur", hideWarehouseLedgerCellTooltip);
  warehouseLedgerTooltipCleanupHandlers.push(() => table.removeEventListener("mouseover", handleMouseOver));
  warehouseLedgerTooltipCleanupHandlers.push(() => table.removeEventListener("mousemove", handleMouseMove));
  warehouseLedgerTooltipCleanupHandlers.push(() => table.removeEventListener("mouseout", handleMouseOut));
  warehouseLedgerTooltipCleanupHandlers.push(() => document.removeEventListener("mousemove", handleDocumentMouseMove, true));
  warehouseLedgerTooltipCleanupHandlers.push(() => window.removeEventListener("scroll", hideWarehouseLedgerCellTooltip, true));
  warehouseLedgerTooltipCleanupHandlers.push(() => window.removeEventListener("blur", hideWarehouseLedgerCellTooltip));
}

function buildWarehouseLedgerParams({ includePagination = true } = {}) {
  const params = new URLSearchParams({
    warehouse_type: activeWarehouseType.value,
    direction: warehouseLedgerFilters.direction || "ALL",
    sort_key: warehouseLedgerFilters.sort_key || "biz_time",
    sort_direction: warehouseLedgerFilters.sort_direction || "desc"
  });
  if (includePagination) {
    params.set("page", String(warehouseLedgerPage.value));
    params.set("page_size", String(warehouseLedgerPageSize.value));
  }
  if (warehouseLedgerFilters.keyword) {
    params.set("keyword", warehouseLedgerFilters.keyword);
  }
  if (warehouseLedgerFilters.txn_type) {
    params.set("txn_type", warehouseLedgerFilters.txn_type);
  }
  if (warehouseLedgerFilters.start_date) {
    params.set("start_date", warehouseLedgerFilters.start_date);
  }
  if (warehouseLedgerFilters.end_date) {
    params.set("end_date", warehouseLedgerFilters.end_date);
  }
  return params;
}

function warehouseLedgerArchiveTypeKey(row) {
  const documentType = String(row?.archive_document_type || "");
  if (documentType === "生产领料出库单") {
    return "production-material-out";
  }
  if (documentType === "生产入库结算单") {
    return "production-inbound-settlement";
  }
  return "warehouse-operation";
}

function warehouseLedgerArchiveBusinessId(row) {
  return Number(row?.archive_business_id || row?.inventory_txn_id || 0);
}

async function previewWarehouseLedgerArchive(row) {
  feedbackMessage.value = "";
  errorMessage.value = "";
  const businessId = warehouseLedgerArchiveBusinessId(row);
  if (!businessId) {
    errorMessage.value = "该流水缺少归档业务ID，无法预览PDF";
    return;
  }
  try {
    await openResource(`/document-archives/${warehouseLedgerArchiveTypeKey(row)}/${businessId}/latest/preview`);
  } catch (error) {
    errorMessage.value = error.message || "PDF归档预览失败";
  }
}

async function downloadWarehouseLedgerArchive(row) {
  feedbackMessage.value = "";
  errorMessage.value = "";
  const businessId = warehouseLedgerArchiveBusinessId(row);
  if (!businessId) {
    errorMessage.value = "该流水缺少归档业务ID，无法下载PDF";
    return;
  }
  try {
    const filename = `${row?.txn_no || row?.archive_document_type || "仓库出入库单"}归档.pdf`;
    await downloadResource(`/document-archives/${warehouseLedgerArchiveTypeKey(row)}/${businessId}/latest/download`, filename);
    feedbackMessage.value = "PDF归档已下载";
  } catch (error) {
    errorMessage.value = error.message || "PDF归档下载失败";
  }
}

async function regenerateWarehouseLedgerArchive(row) {
  feedbackMessage.value = "";
  errorMessage.value = "";
  const businessId = warehouseLedgerArchiveBusinessId(row);
  if (!businessId) {
    errorMessage.value = "该流水缺少归档业务ID，无法重新生成PDF";
    return;
  }
  try {
    await postResource(`/document-archives/${warehouseLedgerArchiveTypeKey(row)}/${businessId}/generate`, {});
    feedbackMessage.value = "PDF归档已重新生成";
    await loadWarehouseLedger();
  } catch (error) {
    errorMessage.value = error.message || "PDF归档重新生成失败";
  }
}

function warehouseLedgerExportFilename() {
  const start = warehouseLedgerFilters.start_date || "全部";
  const end = warehouseLedgerFilters.end_date || "全部";
  return `${activeInventoryLabel.value}出入库流水_${start}_${end}.xlsx`;
}

async function exportWarehouseLedgerExcel() {
  if (!warehouseLedgerFilters.start_date || !warehouseLedgerFilters.end_date) {
    feedbackMessage.value = "当前未选择完整时间范围，将导出全部匹配流水";
  }
  warehouseLedgerExporting.value = true;
  try {
    const params = buildWarehouseLedgerParams({ includePagination: false });
    await downloadResource(`/inventory/transaction-ledger/export?${params.toString()}`, warehouseLedgerExportFilename());
    if (warehouseLedgerFilters.start_date && warehouseLedgerFilters.end_date) {
      feedbackMessage.value = "流水Excel已导出";
    }
  } catch (error) {
    errorMessage.value = error.message || "流水Excel导出失败";
  } finally {
    warehouseLedgerExporting.value = false;
  }
}

async function loadWarehouseLedger() {
  warehouseLedgerLoading.value = true;
  try {
    const params = buildWarehouseLedgerParams();
    const result = await fetchResource(`/inventory/transaction-ledger?${params.toString()}`, {
      items: [],
      total: 0,
      summary: buildEmptyWarehouseLedgerSummary()
    });
    warehouseLedgerRows.value = Array.isArray(result.items) ? result.items : [];
    warehouseLedgerTotal.value = Number(result.total || 0);
    Object.assign(warehouseLedgerSummary, buildEmptyWarehouseLedgerSummary(), result.summary || {});
    errorMessage.value = "";
  } catch (error) {
    warehouseLedgerRows.value = [];
    warehouseLedgerTotal.value = 0;
    Object.assign(warehouseLedgerSummary, buildEmptyWarehouseLedgerSummary());
    errorMessage.value = error.message || "仓库出入库流水加载失败";
  } finally {
    warehouseLedgerLoading.value = false;
    await nextTick();
    bindWarehouseLedgerCellTooltip();
  }
}

function openOperationDrawer(operation) {
  activeOperation.value = operation;
  resetFeedback();
  if (operation.bizType === "OPENING" && operation.direction === "in") {
    openingImportDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "production") {
    resetProductionForm();
    productionDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "rawReturn") {
    resetRawReturnForm();
    rawReturnDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "completion" || operation.drawerType === "reworkCompletion") {
    resetCompletionForm();
    completionDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "productionLedgerInbound") {
    openProductionWorkOrderInboundDrawer();
    return;
  }
  if (operation.drawerType === "delivery") {
    resetDeliveryForm();
    deliveryDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "returnInbound") {
    resetReturnInboundForm();
    returnInboundDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "returnScrapInbound") {
    resetReturnScrapInboundForm();
    returnScrapInboundDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "returnRework") {
    resetReturnReworkForm();
    returnReworkDrawerOpen.value = true;
    return;
  }
  if (operation.drawerType === "specialAdjustment") {
    resetSpecialAdjustmentForm();
    specialAdjustmentDrawerOpen.value = true;
    return;
  }
  resetGenericForm();
  genericDrawerOpen.value = true;
}

function closeGenericDrawer() {
  genericDrawerOpen.value = false;
}

function closeSpecialAdjustmentDrawer() {
  specialAdjustmentDrawerOpen.value = false;
}

function closeRawReturnDrawer() {
  rawReturnDrawerOpen.value = false;
  resetRawReturnForm();
}

function closeReturnInboundDrawer() {
  returnInboundDrawerOpen.value = false;
  resetReturnInboundForm();
}

function closeReturnScrapInboundDrawer() {
  returnScrapInboundDrawerOpen.value = false;
  resetReturnScrapInboundForm();
}

function closeReturnReworkDrawer() {
  returnReworkDrawerOpen.value = false;
  resetReturnReworkForm();
}

function closeOpeningImportDrawer() {
  openingImportDrawerOpen.value = false;
  if (openingImportInputRef.value) {
    openingImportInputRef.value.value = "";
  }
}

function openStockDetail(balance) {
  selectedItemId.value = balance.item_id;
  selectedBalance.value = balance;
  lotPage.value = 1;
  txnPage.value = 1;
  expandedLotRows.value = [];
  expandedTxnRows.value = [];
  lotControls.search.value = "";
  txnControls.search.value = "";
  detailDrawerOpen.value = true;
}

function closeStockDetail() {
  detailDrawerOpen.value = false;
}

function openStocktakeDialog() {
  resetFeedback();
  stocktakeDialogOpen.value = true;
}

function closeStocktakeDialog() {
  stocktakeDialogOpen.value = false;
}

function handleStocktakeMessage(message) {
  feedbackMessage.value = message;
  errorMessage.value = "";
}

function handleStocktakeError(message) {
  errorMessage.value = message;
  feedbackMessage.value = "";
}

function normalizeWarehouseType(item) {
  return String(item?.warehouse_type || "").toUpperCase();
}

function aggregateItemBalances(rows, warehouseType) {
  const map = new Map();
  rows.forEach((item) => {
    const key = Number(item.item_id);
    if (!map.has(key)) {
      map.set(key, {
        ...item,
        stock_balance_id: `${warehouseType}-${key}`,
        warehouse_type: warehouseType,
        warehouse_name: "",
        location_name: "",
        qty_on_hand: 0,
        weight_on_hand_kg: 0,
        qty_available: 0,
        weight_available_kg: 0,
        qty_allocated: 0,
        weight_allocated_kg: 0,
        measure_basis: "",
        avg_unit_cost: 0,
        _cost_amount: 0,
        _weight_for_cost: 0,
        _warehouses: new Set(),
        _locations: new Set()
      });
    }
    const row = map.get(key);
    row.qty_on_hand += Number(item.qty_on_hand || 0);
    row.weight_on_hand_kg += Number(item.weight_on_hand_kg || 0);
    row.qty_available += Number(item.qty_available || 0);
    row.weight_available_kg += Number(item.weight_available_kg || 0);
    row.qty_allocated += Number(item.qty_allocated || 0);
    row.weight_allocated_kg += Number(item.weight_allocated_kg || 0);
    row._cost_amount += Number(item.avg_unit_cost || 0) * Number(item.weight_on_hand_kg || 0);
    row._weight_for_cost += Number(item.weight_on_hand_kg || 0);
    row._warehouses.add(item.warehouse_name);
    if (item.location_name) {
      row._locations.add(item.location_name);
    }
  });
  return Array.from(map.values()).map((item) => ({
    ...item,
    warehouse_name: Array.from(item._warehouses).join(" / ") || "-",
    location_name: Array.from(item._locations).join(" / ") || "-",
    avg_unit_cost: item._weight_for_cost > 0 ? item._cost_amount / item._weight_for_cost : 0
  }));
}

function aggregateSemiOperationBalances(rows) {
  const map = new Map();
  rows.forEach((lot) => {
    const itemId = Number(lot.item_id || 0);
    if (!itemId) {
      return;
    }
    const sourceLineId = Number(lot.source_line_id || 0);
    const key = `${itemId}::${sourceLineId}`;
    if (!map.has(key)) {
      map.set(key, {
        ...lot,
        stock_balance_id: `SEMI-${key}`,
        option_key: key,
        source_line_id: sourceLineId || null,
        route_operation_id: sourceLineId || null,
        operation_label: sourceLineId ? lot.source_material_summary || "半成品未说明" : "未绑定半成品",
        qty_on_hand: 0,
        weight_on_hand_kg: 0,
        qty_available: 0,
        weight_available_kg: 0,
        qty_allocated: 0,
        weight_allocated_kg: 0,
        avg_unit_cost: 0,
        _cost_amount: 0,
        _weight_for_cost: 0,
        _warehouses: new Set(),
        _locations: new Set()
      });
    }
    const row = map.get(key);
    const remainingWeight = Number(lot.remaining_weight_kg || 0);
    const lockedWeight = Number(lot.locked_weight_kg || 0);
    const availableWeight = String(lot.status || "").toUpperCase() === "AVAILABLE" ? Math.max(remainingWeight - lockedWeight, 0) : 0;
    const remainingQty = Number(lot.remaining_qty || 0);
    const lockedQty = Number(lot.locked_qty || 0);
    const availableQty = String(lot.status || "").toUpperCase() === "AVAILABLE" ? Math.max(remainingQty - lockedQty, 0) : 0;
    row.measure_basis = mergeSemiMeasureBasis(row.measure_basis, deriveSemiLotMeasureBasis(lot));
    row.qty_on_hand += remainingQty;
    row.qty_available += availableQty;
    row.qty_allocated += lockedQty;
    row.weight_on_hand_kg += remainingWeight;
    row.weight_available_kg += availableWeight;
    row.weight_allocated_kg += lockedWeight;
    row._cost_amount += Number(lot.unit_cost || 0) * remainingWeight;
    row._weight_for_cost += remainingWeight;
    row._warehouses.add(lot.warehouse_name);
    if (lot.location_name) {
      row._locations.add(lot.location_name);
    }
  });
  return Array.from(map.values()).map((item) => ({
    ...item,
    warehouse_name: Array.from(item._warehouses).join(" / ") || "-",
    location_name: Array.from(item._locations).join(" / ") || "-",
    avg_unit_cost: item._weight_for_cost > 0 ? item._cost_amount / item._weight_for_cost : 0
  }));
}

function formatInventoryBalanceLabel(balance) {
  const specificationLabel = balance.specification ? ` · ${balance.specification}` : "";
  const baseLabel = `${balance.item_code} · ${balance.item_name}${specificationLabel}`;
  if (normalizeWarehouseType(balance) !== "SEMI") {
    return baseLabel;
  }
  return `${baseLabel} · ${balance.operation_label || balance.source_material_summary || "未绑定工序"}`;
}

function formatInventoryUnitLabel(item) {
  return item?.stock_unit_name || item?.purchase_unit_name || item?.sales_unit_name || extractUnitFromRemark(item?.item_remark) || "-";
}

function extractUnitFromRemark(remark) {
  const match = String(remark || "").match(/计量单位：([^；;]+)/);
  return match?.[1]?.trim() || "";
}

function orderItemPendingQty(item) {
  return Math.max(Number(item.order_qty || 0) - Number(item.delivered_qty || 0), 0);
}

function orderPendingQty(orderId) {
  return salesOrderItems.value
    .filter((item) => Number(item.sales_order_id) === Number(orderId))
    .reduce((total, item) => total + orderItemPendingQty(item), 0);
}

function orderPendingProductSummary(orderId) {
  const rows = salesOrderItems.value
    .filter((item) => Number(item.sales_order_id) === Number(orderId) && orderItemPendingQty(item) > 0)
    .map((item) => {
      const productName = item.product_name || item.product_code || "产品";
      return `${productName} ${formatQty(orderItemPendingQty(item))}`;
    });
  return rows.length ? rows.join("；") : "无未发产品";
}

function deliveryStockShortageMessage(requestedQty, availableQty) {
  const requested = Math.max(Number(requestedQty || 0), 0);
  const available = Math.max(Number(availableQty || 0), 0);
  const shortage = Math.max(requested - available, 0);
  return `成品库存不足：本次要发 ${formatQty(requested)}，可发 ${formatQty(available)}，缺 ${formatQty(shortage)}`;
}

function applyCompletionWorkOrder() {
  const workOrder = selectedCompletionWorkOrder.value;
  if (!workOrder) {
    return;
  }
  completionForm.settle_work_order = false;
  completionForm.settle_remark = "";
  const product = products.value.find((item) => item.item_id === Number(workOrder.product_item_id));
  completionForm.product_item_id = workOrder.product_item_id;
  completionForm.max_receivable_qty = Number(workOrder.limits?.max_finished_inbound_qty || workOrder.max_finished_inbound_qty || 0);
  completionForm.tolerance_receivable_qty = Number(workOrder.limits?.tolerance_finished_inbound_qty || completionForm.max_receivable_qty || 0);
  completionForm.unit_weight_kg = Number(product?.unit_weight_kg || 0);
  completionForm.receipt_qty = Number(completionForm.max_receivable_qty || 0);
  recalculateCompletionWeight();
}

function recalculateCompletionWeight() {
  const toleranceMax = Number(completionForm.tolerance_receivable_qty || completionForm.max_receivable_qty || 0);
  if (Number(completionForm.receipt_qty || 0) > toleranceMax) {
    completionForm.receipt_qty = toleranceMax;
  }
  completionForm.receipt_weight_kg = Number((Number(completionForm.receipt_qty || 0) * Number(completionForm.unit_weight_kg || 0)).toFixed(3));
}

function applySalesOrderDefaults() {
  deliveryForm.sales_order_item_id = "";
  if (!isSalesOrderDelivery.value) {
    return;
  }
  const order = selectedSalesOrder.value;
  if (!order) {
    return;
  }
  deliveryForm.customer_id = order.customer_id;
  deliveryForm.delivery_address = order.delivery_address || "";
  const customer = customers.value.find((item) => item.id === Number(order.customer_id));
  deliveryForm.consignee_name = customer?.contact_name || "";
  deliveryForm.consignee_phone = customer?.contact_phone || "";
  deliveryForm.delivery_address = order.delivery_address || customer?.address || "";
  const firstOpenItem = selectableSalesOrderItems.value[0];
  if (firstOpenItem) {
    deliveryForm.sales_order_item_id = firstOpenItem.sales_order_item_id;
    applySalesOrderItemDefaults();
  }
}

function applySalesOrderItemDefaults() {
  const item = selectedSalesOrderItem.value;
  if (!item || !isSalesOrderDelivery.value) {
    return;
  }
  deliveryForm.product_item_id = item.product_item_id;
  deliveryForm.delivery_qty = orderItemPendingQty(item);
}

function applyDeliveryCustomerDefaults() {
  if (isSalesOrderDelivery.value) {
    return;
  }
  const customer = customers.value.find((item) => item.id === Number(deliveryForm.customer_id));
  deliveryForm.consignee_name = customer?.contact_name || "";
  deliveryForm.consignee_phone = customer?.contact_phone || "";
  deliveryForm.delivery_address = customer?.address || "";
}

function formatRawReturnLinkLabel(link) {
  const orderNo = link.purchase_order_no || link.order_no || "采购单";
  const supplierName = link.supplier_name || "供应商";
  const returnableWeight = formatWeight(link.returnable_weight_kg);
  return `${orderNo} · ${supplierName} · 可退 ${returnableWeight}`;
}

function rawReturnLotAvailableWeight(lot) {
  if (!lot) {
    return 0;
  }
  const remainingWeight = Number(lot.remaining_weight_kg || 0);
  const lockedWeight = Number(lot.locked_weight_kg || 0);
  return Math.max(remainingWeight - lockedWeight, 0);
}

function applyRawReturnLinkWeight() {
  const linkReturnableWeight = Number(selectedRawReturnLink.value?.returnable_weight_kg || 0);
  const maxReturnWeight = selectedRawReturnLink.value
    ? Math.min(Math.max(linkReturnableWeight, 0), selectedRawReturnLotAvailableWeight.value)
    : selectedRawReturnLotAvailableWeight.value;
  rawReturnForm.return_weight_kg = Number(maxReturnWeight.toFixed(3));
}

function handleRawReturnLinkChange() {
  if (!selectedRawReturnLink.value) {
    rawReturnForm.return_weight_kg = 0;
    return;
  }
  applyRawReturnLinkWeight();
}

function applyReturnInboundDeliveryLine() {
  const line = selectedReturnInboundDeliveryLine.value;
  if (!line) {
    returnInboundForm.return_qty = 0;
    returnInboundForm.return_weight_kg = 0;
    return;
  }
  returnInboundForm.return_qty = Number(line.returnable_qty || 0);
  returnInboundForm.return_weight_kg = Number(Number(line.returnable_weight_kg || 0).toFixed(3));
}

function applyReturnScrapDeliveryLine() {
  const line = selectedReturnScrapDeliveryLine.value;
  if (!line) {
    returnScrapInboundForm.scrap_qty = 0;
    returnScrapInboundForm.scrap_weight_kg = 0;
    returnScrapInboundForm.unit_cost = "";
    return;
  }
  const product = products.value.find((item) => Number(item.item_id) === Number(line.product_item_id));
  returnScrapInboundForm.scrap_qty = Number(line.returnable_qty || 0);
  returnScrapInboundForm.scrap_weight_kg = Number(Number(line.returnable_weight_kg || 0).toFixed(3));
  returnScrapInboundForm.unit_cost = product?.scrap_sale_price ?? "";
}

async function handleRawReturnLotChange() {
  rawReturnLinksRequestSeq += 1;
  const requestSeq = rawReturnLinksRequestSeq;
  const lotId = Number(rawReturnForm.lot_id);
  rawReturnForm.purchase_order_item_id = "";
  rawReturnForm.return_weight_kg = 0;
  rawReturnForm.waybill_no = "";
  rawReturnForm.order_photo_url = "";
  rawReturnForm.freight_amount = "";
  rawReturnPurchaseLinks.value = [];
  const lot = selectedRawReturnLot.value;
  if (!lot) {
    return;
  }
  rawReturnForm.warehouse_id = lot.warehouse_id || rawReturnForm.warehouse_id;
  rawReturnLinkLoading.value = true;
  try {
    const result = await fetchResource(`/inventory/stock-lots/${lotId}/purchase-links`, []);
    if (requestSeq !== rawReturnLinksRequestSeq || Number(rawReturnForm.lot_id) !== lotId) {
      return;
    }
    rawReturnPurchaseLinks.value = Array.isArray(result) ? result : result?.items || result?.links || [];
    if (rawReturnPurchaseLinks.value.length === 1) {
      rawReturnForm.purchase_order_item_id = rawReturnPurchaseLinks.value[0].purchase_order_item_id;
      applyRawReturnLinkWeight();
    }
    errorMessage.value = "";
  } catch (error) {
    if (requestSeq !== rawReturnLinksRequestSeq || Number(rawReturnForm.lot_id) !== lotId) {
      return;
    }
    errorMessage.value = error.message || "采购来源加载失败";
  } finally {
    if (requestSeq === rawReturnLinksRequestSeq && Number(rawReturnForm.lot_id) === lotId) {
      rawReturnLinkLoading.value = false;
    }
  }
}

function validateRawReturnForm() {
  if (!Number(rawReturnForm.lot_id)) {
    return "请选择库存批次号";
  }
  if (!Number(rawReturnForm.purchase_order_item_id)) {
    return "请选择采购来源";
  }
  const returnWeight = Number(rawReturnForm.return_weight_kg || 0);
  if (!Number.isFinite(returnWeight) || returnWeight <= 0) {
    return "退货重量必须大于0";
  }
  const lotAvailableWeight = selectedRawReturnLotAvailableWeight.value;
  if (lotAvailableWeight > 0 && returnWeight > lotAvailableWeight) {
    return `退货重量不能超过批次可用重量 ${formatWeight(lotAvailableWeight)}`;
  }
  const linkReturnableWeight = Number(selectedRawReturnLink.value?.returnable_weight_kg || 0);
  if (selectedRawReturnLink.value && returnWeight > Math.max(linkReturnableWeight, 0)) {
    return `退货重量不能超过采购来源可退重量 ${formatWeight(linkReturnableWeight)}`;
  }
  if (!String(rawReturnForm.waybill_no || "").trim() && !String(rawReturnForm.order_photo_url || "").trim()) {
    return "请填写运单号或上传辅助照片";
  }
  if (!isFreightBlank(rawReturnForm.freight_amount) && Number(rawReturnForm.freight_amount) < 0) {
    return "运费不能小于0";
  }
  return "";
}

function validateReturnQuantityWeight(line, qty, weight, qtyLabel, weightLabel) {
  if (!line) {
    return "请选择发货批次";
  }
  if (!Number.isFinite(qty) || qty <= 0) {
    return `${qtyLabel}必须大于0`;
  }
  if (!Number.isFinite(weight) || weight <= 0) {
    return `${weightLabel}必须大于0`;
  }
  if (qty > Number(line.returnable_qty || 0)) {
    return `${qtyLabel}不能超过发货批次可退数量 ${formatQty(line.returnable_qty)}`;
  }
  if (weight > Number(line.returnable_weight_kg || 0)) {
    return `${weightLabel}不能超过发货批次可退重量 ${formatWeight(line.returnable_weight_kg)}`;
  }
  return "";
}

function validateReturnInboundForm() {
  return validateReturnQuantityWeight(
    selectedReturnInboundDeliveryLine.value,
    Number(returnInboundForm.return_qty || 0),
    Number(returnInboundForm.return_weight_kg || 0),
    "退货数量",
    "退货重量"
  );
}

function validateReturnScrapInboundForm() {
  const unitCost = Number(returnScrapInboundForm.unit_cost || 0);
  if (returnScrapInboundForm.unit_cost !== "" && (!Number.isFinite(unitCost) || unitCost < 0)) {
    return "废料单价不能小于0";
  }
  return validateReturnQuantityWeight(
    selectedReturnScrapDeliveryLine.value,
    Number(returnScrapInboundForm.scrap_qty || 0),
    Number(returnScrapInboundForm.scrap_weight_kg || 0),
    "报废数量",
    "报废重量"
  );
}

function validateReturnReworkForm() {
  if (!selectedReturnReworkItem.value) {
    return "请选择待返工退货明细";
  }
  if (Number(returnReworkForm.extra_cost_amount || 0) < 0) {
    return "额外人工成本不能小于0";
  }
  return "";
}

function validateGenericSourceLots() {
  if (!usesGenericSourceLotSelector.value) {
    return "";
  }
  if (!selectedGenericSourceLots.value.length) {
    if (isFinishedOutsourcingInbound.value) {
      return "";
    }
    return "请选择至少一个来源库存批次号";
  }
  if (isProductTraceOnlySourceLotInbound.value) {
    return "";
  }
  if (isProductionSurplusInbound.value) {
    const hasPositiveReturnWeight = selectedGenericSourceLots.value.some((lot) => Number(lot.return_weight_kg || 0) > 0);
    if (!hasPositiveReturnWeight) {
      return "请至少填写一个来源库存批次号的归还重量";
    }
  }
  for (const lot of selectedGenericSourceLots.value) {
    const weight = Number(isSourceLotSelectedOutbound.value ? lot.issued_weight_kg : lot.return_weight_kg || 0);
    const sourceRemainingWeight = Number(lot.source_remaining_weight_kg ?? 0);
    if (isProductionSurplusInbound.value && sourceRemainingWeight <= 0 && weight <= 0) {
      continue;
    }
    if (!Number.isFinite(weight) || weight <= 0) {
      return `来源库存批次号 ${lot.lot_no || "-"} 的${isSourceLotSelectedOutbound.value ? "出库" : "归还"}重量必须大于0`;
    }
    if (
      isProductionSurplusInbound.value &&
      Number.isFinite(sourceRemainingWeight) &&
      weight > sourceRemainingWeight
    ) {
      return `来源库存批次号 ${lot.lot_no || "-"} 的归还重量不能超过该批次原领料未归还重量 ${formatWeight(sourceRemainingWeight)}`;
    }
    if (isSourceLotSelectedOutbound.value && weight > Number(lot.remaining_weight_kg || 0)) {
      return `来源库存批次号 ${lot.lot_no || "-"} 的出库重量不能超过可用重量 ${formatWeight(lot.remaining_weight_kg)}`;
    }
  }
  if (isProductionSurplusInbound.value) {
    const toleranceMax = Number(productionSurplusToleranceRemainingWeight.value || 0);
    if (!genericForm.settle_work_order && toleranceMax > 0 && genericSourceLotReturnWeight.value > toleranceMax) {
      return "本次归还总重量超过系统允许范围，请调整后重试";
    }
    if (genericForm.settle_work_order && productionSurplusSettleNeedsRemark.value && !String(genericForm.settle_remark || "").trim()) {
      return "该批材料余料结单归还重量超出标准归还重量上下15%，请填写余料结单说明";
    }
  }
  return "";
}

function specialAdjustmentSubmitQty(line) {
  return specialAdjustmentShowQtyInput(line) ? Number(line.adjust_qty || 0) : 0;
}

function specialAdjustmentSubmitWeight(line) {
  return specialAdjustmentShowWeightInput(line) ? Number(line.adjust_weight_kg || 0) : 0;
}

function validateSpecialAdjustmentBeforeSubmit() {
  const reason = String(specialAdjustmentForm.reason || "").trim();
  if (!reason) {
    return `${specialAdjustmentDirectionText.value}必须填写调整说明`;
  }
  if (reason.length < 4) {
    return "调整说明请至少填写4个字";
  }
  if (!Number(specialAdjustmentForm.warehouse_id || 0)) {
    return "请选择仓库";
  }
  if (!specialAdjustmentForm.lines.length) {
    return `${specialAdjustmentDirectionText.value}必须至少添加一条明细`;
  }
  for (const [index, line] of specialAdjustmentForm.lines.entries()) {
    const lineNo = index + 1;
    const lot = selectedSpecialAdjustmentLot(line);
    const lotRequired = specialAdjustmentForm.direction === "OUT" || specialAdjustmentLineUsesExistingLot(line);
    if (specialAdjustmentForm.direction === "OUT" && (!Number(line.lot_id || 0) || !lot)) {
      return `第${lineNo}行特殊出库必须选择库存明细`;
    }
    if (specialAdjustmentForm.direction === "IN" && lotRequired && (!Number(line.lot_id || 0) || !lot)) {
      return `第${lineNo}行请选择要补增的库存明细，或切换为新增特殊明细`;
    }
    if (!Number(line.item_id || 0)) {
      return specialAdjustmentForm.direction === "IN" && !lotRequired
        ? `第${lineNo}行新增特殊明细必须选择物料`
        : `第${lineNo}行库存明细未带入物料`;
    }
    const qty = specialAdjustmentSubmitQty(line);
    const weight = specialAdjustmentSubmitWeight(line);
    if (specialAdjustmentShowQtyInput(line) && (!Number.isFinite(qty) || qty <= 0)) {
      return `第${lineNo}行必须填写调整数量`;
    }
    if (specialAdjustmentWeightRequired(line) && (!Number.isFinite(weight) || weight <= 0)) {
      return `第${lineNo}行必须填写调整重量`;
    }
    if (specialAdjustmentShowWeightInput(line) && (!Number.isFinite(weight) || weight < 0)) {
      return `第${lineNo}行调整重量不能小于0`;
    }
    if (specialAdjustmentEffectiveMeasureMode(line) === "SEMI_SELECT") {
      return `第${lineNo}行必须选择调整口径`;
    }
    if (specialAdjustmentForm.direction === "OUT") {
      const remainingQty = Number(lot.remaining_qty || 0);
      const remainingWeight = Number(lot.remaining_weight_kg || 0);
      if (qty > remainingQty) {
        return `第${lineNo}行调整数量不能超过当前剩余数量 ${formatQty(remainingQty)}`;
      }
      if (weight > remainingWeight) {
        return `第${lineNo}行调整重量不能超过当前剩余重量 ${formatWeight(remainingWeight)}`;
      }
    }
  }
  return "";
}

function specialAdjustmentLineLotId(line) {
  const lotId = Number(line.lot_id || 0);
  return lotId > 0 ? lotId : null;
}

function buildSpecialAdjustmentPayloadLine(line) {
  const lotId = specialAdjustmentLineLotId(line);
  if (!lotId) {
    return {
      item_id: Number(line.item_id),
      lot_id: null,
      location_id: line.location_id ? Number(line.location_id) : null,
      adjust_qty: specialAdjustmentSubmitQty(line),
      adjust_weight_kg: specialAdjustmentSubmitWeight(line),
      unit_cost: Number(line.unit_cost || 0),
      line_reason: line.line_reason || null
    };
  }
  return {
    item_id: Number(line.item_id),
    lot_id: lotId,
    location_id: line.location_id ? Number(line.location_id) : null,
    adjust_qty: specialAdjustmentSubmitQty(line),
    adjust_weight_kg: specialAdjustmentSubmitWeight(line),
    unit_cost: Number(line.unit_cost || 0),
    line_reason: line.line_reason || null
  };
}

async function submitSpecialAdjustment() {
  resetFeedback();
  const validationError = validateSpecialAdjustmentBeforeSubmit();
  if (validationError) {
    errorMessage.value = validationError;
    return;
  }
  const confirmed = window.confirm(`确认执行${specialAdjustmentDirectionText.value}？保存后会直接改变库存明细，并写入库存流水和特殊调整记录。`);
  if (!confirmed) {
    return;
  }
  specialAdjustmentSubmitting.value = true;
  try {
    const payload = {
      warehouse_id: Number(specialAdjustmentForm.warehouse_id),
      direction: specialAdjustmentForm.direction,
      reason: String(specialAdjustmentForm.reason || "").trim(),
      lines: specialAdjustmentForm.lines.map((line) => buildSpecialAdjustmentPayloadLine(line))
    };
    const result = await postResource("/inventory/special-adjustments", payload);
    const savedNo = result.adjustment_no || result.id || "";
    rememberWarehouseArchiveResult(result, specialAdjustmentDirectionText.value);
    feedbackMessage.value = `${specialAdjustmentDirectionText.value} ${savedNo} 已保存${formatArchiveFeedback(result)}`;
    closeSpecialAdjustmentDrawer();
    await loadAll();
    resetSpecialAdjustmentForm();
  } catch (error) {
    errorMessage.value = error.message || `${specialAdjustmentDirectionText.value}保存失败`;
  } finally {
    specialAdjustmentSubmitting.value = false;
  }
}

async function submitGenericOperation() {
  resetFeedback();
  if (activeOperation.value?.bizType === "SCRAP_SALE_OUT" && Number(genericForm.sale_unit_price || 0) <= 0) {
    errorMessage.value = "售卖单价必须大于0";
    return;
  }
  if (genericRequiresLogistics.value) {
    const logisticsError = validateLogisticsForm(genericForm);
    if (logisticsError) {
      errorMessage.value = logisticsError;
      return;
    }
  }
  if (isProductionWorkOrderControlledInbound.value && !selectedGenericWorkOrder.value) {
    errorMessage.value = `${activeOperation.value?.label || "生产入库"}必须先选择${isReworkScrapInbound.value ? "返工工单" : "生产台账"}`;
    return;
  }
  if (isProductionScrapInbound.value) {
    const inboundWeight = Number(genericForm.weight_kg || 0);
    const scrapToleranceMax = Number(productionScrapToleranceRemainingWeight.value || 0);
    if (!genericForm.settle_work_order && scrapToleranceMax > 0 && inboundWeight > scrapToleranceMax) {
      errorMessage.value = "生产废料入库重量超过系统允许范围，请调整后重试";
      return;
    }
    if (genericForm.settle_work_order && productionScrapSettleNeedsRemark.value && !String(genericForm.settle_remark || "").trim()) {
      errorMessage.value = "该批材料废料结单入库废料重量超出标准废料重量上下15%，请填写废料结单说明";
      return;
    }
  }
  if (isReworkScrapInbound.value) {
    const inboundQty = Number(genericForm.qty || 0);
    const inboundWeight = Number(genericForm.weight_kg || 0);
    if (!Number.isFinite(inboundQty) || inboundQty <= 0) {
      errorMessage.value = "返工废料入库数量必须大于0";
      return;
    }
    if (!Number.isFinite(inboundWeight) || inboundWeight <= 0) {
      errorMessage.value = "返工废料入库重量必须大于0";
      return;
    }
  }
  submittingOperation.value = true;
  try {
    const selectedOption = selectedGenericItemOption.value;
    const selectedItemId = Number(selectedOption?.item_id || genericForm.item_id || 0);
    const selectedSourceDocId = selectedOption?.route_id || selectedOption?.source_doc_id || genericForm.source_doc_id || null;
    const selectedSourceLineId = selectedOption?.route_operation_id || selectedOption?.source_line_id || genericForm.source_line_id || null;
    const selectedSourceSummary =
      selectedOption?.source_material_summary ||
      selectedOption?.operation_label ||
      genericForm.provider_name ||
      genericForm.source_material_sub_batch_no ||
      null;
    if (!selectedItemId) {
      errorMessage.value = `请选择${genericItemFieldLabel.value}`;
      submittingOperation.value = false;
      return;
    }
    if (activeWarehouseType.value === "SEMI" && !selectedSourceLineId) {
      errorMessage.value = "半成品入出库必须选择半成品";
      submittingOperation.value = false;
      return;
    }
    if (activeOperation.value?.direction === "out") {
      if (activeWarehouseType.value === "AUX") {
        const outboundQty = Number(genericForm.qty || 0);
        if (!Number.isFinite(outboundQty) || outboundQty <= 0) {
          errorMessage.value = "辅料生产出库数量必须大于0";
          submittingOperation.value = false;
          return;
        }
        if (!String(genericForm.remark || "").trim()) {
          errorMessage.value = "辅料生产出库必须填写用途";
          submittingOperation.value = false;
          return;
        }
      }
      if (isSemiAutoSourceOutbound.value) {
        const measureBasis = selectedSemiMeasureBasis.value;
        const outboundWeight = measureBasis === "WEIGHT" ? Number(genericForm.weight_kg || 0) : 0;
        const outboundQty = measureBasis === "PIECE" ? Number(genericForm.qty || 0) : 0;
        const operationLabel = activeOperation.value?.label || "半成品出库";
        if (measureBasis === "MIXED") {
          errorMessage.value = "该半成品已有历史混合库存，请先清理历史库存后再出库";
          submittingOperation.value = false;
          return;
        }
        if (!["PIECE", "WEIGHT"].includes(measureBasis)) {
          errorMessage.value = "请选择有可用库存的半成品";
          submittingOperation.value = false;
          return;
        }
        if ((!Number.isFinite(outboundWeight) || outboundWeight < 0) || (!Number.isFinite(outboundQty) || outboundQty < 0)) {
          errorMessage.value = measureBasis === "PIECE" ? "出库数量不能小于0" : "出库重量不能小于0";
          submittingOperation.value = false;
          return;
        }
        if (measureBasis === "PIECE" && outboundQty <= 0) {
          errorMessage.value = `${operationLabel}的出库数量必须大于0`;
          submittingOperation.value = false;
          return;
        }
        if (measureBasis === "WEIGHT" && outboundWeight <= 0) {
          errorMessage.value = `${operationLabel}的出库重量必须大于0`;
          submittingOperation.value = false;
          return;
        }
        if (outboundQty > 0 && outboundQty > semiAutoSourceOutboundAvailableQty.value) {
          errorMessage.value = `${operationLabel}数量不能超过该半成品可用数量 ${formatQty(semiAutoSourceOutboundAvailableQty.value)}`;
          submittingOperation.value = false;
          return;
        }
        if (outboundWeight > 0 && outboundWeight > semiAutoSourceOutboundAvailableWeight.value) {
          errorMessage.value = `${operationLabel}重量不能超过该半成品可用重量 ${formatWeight(semiAutoSourceOutboundAvailableWeight.value)}`;
          submittingOperation.value = false;
          return;
        }
      }
      if (isSourceLotSelectedOutbound.value) {
        const validationError = validateGenericSourceLots();
        if (validationError) {
          errorMessage.value = validationError;
          submittingOperation.value = false;
          return;
        }
      }
      const selectedOutboundSourceLots = isSourceLotSelectedOutbound.value
        ? selectedGenericSourceLots.value.map((lot) => ({
            source_lot_id: Number(lot.lot_id),
            outbound_weight_kg: Number(lot.issued_weight_kg || 0)
          }))
        : [];
      const result = await postResource("/inventory/outbound", {
        biz_type: activeOperation.value.bizType,
        item_id: selectedItemId,
        warehouse_id: Number(genericForm.warehouse_id),
        location_id: genericForm.location_id ? Number(genericForm.location_id) : null,
        outbound_weight_kg: activeWarehouseType.value === "AUX"
          ? 0
          : isSourceLotSelectedOutbound.value
            ? Number(genericSourceLotOutboundWeight.value.toFixed(3))
            : isSemiAutoSourceOutbound.value && selectedSemiMeasureBasis.value === "PIECE"
              ? 0
              : Number(genericForm.weight_kg || 0),
        outbound_qty: activeWarehouseType.value === "AUX"
          ? Number(genericForm.qty || 0)
          : isSemiAutoSourceOutbound.value && selectedSemiMeasureBasis.value === "PIECE"
            ? Number(genericForm.qty || 0)
            : 0,
        target_doc_type: activeWarehouseType.value === "SEMI" ? "PRODUCT_ROUTE_OPERATION" : null,
        target_doc_id: activeWarehouseType.value === "SEMI" ? Number(selectedSourceDocId || 0) : null,
        target_doc_line_id: activeWarehouseType.value === "SEMI" ? Number(selectedSourceLineId || 0) : null,
        target_material_sub_batch_no: isSourceLotSelectedOutbound.value
          ? selectedGenericSourceLots.value.map((lot) => lot.lot_no).filter(Boolean).join("、") || null
          : activeWarehouseType.value === "SEMI"
            ? isSemiAutoSourceOutbound.value
              ? semiAutoSourceOutboundSourceLotSummary.value || selectedOption?.operation_label || selectedSourceSummary
              : selectedOption?.operation_label || selectedSourceSummary
            : genericForm.source_material_sub_batch_no || null,
        sale_unit_price: activeOperation.value.bizType === "SCRAP_SALE_OUT" ? Number(genericForm.sale_unit_price || 0) : null,
        buyer_name: activeOperation.value.bizType === "SCRAP_SALE_OUT" ? genericForm.buyer_name || null : null,
        sale_remark: activeOperation.value.bizType === "SCRAP_SALE_OUT" ? genericForm.sale_remark || genericForm.remark || null : null,
        waybill_no: genericRequiresLogistics.value ? genericForm.waybill_no || null : null,
        order_photo_url: genericRequiresLogistics.value ? genericForm.order_photo_url || null : null,
        freight_amount: genericRequiresLogistics.value ? Number(genericForm.freight_amount) : null,
        outsourcing_party_name: activeOperation.value.bizType === "OUTSOURCING_OUT" ? genericForm.outsourcing_party_name || null : null,
        remark: genericForm.remark || null,
        source_lots: selectedOutboundSourceLots
      });
      rememberWarehouseArchiveResult(result, activeOperation.value.label);
      feedbackMessage.value = `${activeOperation.value.label}已登记${formatArchiveFeedback(result)}`;
    } else {
      if (activeWarehouseType.value === "AUX") {
        const inboundQty = Number(genericForm.qty || 0);
        if (!Number.isFinite(inboundQty) || inboundQty <= 0) {
          errorMessage.value = "辅料入库数量必须大于0";
          submittingOperation.value = false;
          return;
        }
      }
      if (isFinishedOutsourcingInbound.value) {
        const inboundQty = Number(genericForm.qty || 0);
        if (!Number.isFinite(inboundQty) || inboundQty <= 0) {
          errorMessage.value = "成品委外入库数量必须大于0";
          submittingOperation.value = false;
          return;
        }
      }
      if (isSemiSourceLotTraceInbound.value) {
        const measureBasis = selectedSemiMeasureBasis.value;
        const inboundWeight = measureBasis === "WEIGHT" ? Number(genericForm.weight_kg || 0) : 0;
        const inboundQty = measureBasis === "PIECE" ? Number(genericForm.qty || 0) : 0;
        if (selectedSemiExistingMeasureBasis.value === "MIXED") {
          errorMessage.value = "该半成品已有历史混合库存，请先清理历史库存后再入库";
          submittingOperation.value = false;
          return;
        }
        if (!["PIECE", "WEIGHT"].includes(measureBasis)) {
          errorMessage.value = "请选择入库类型";
          submittingOperation.value = false;
          return;
        }
        if ((!Number.isFinite(inboundWeight) || inboundWeight < 0) || (!Number.isFinite(inboundQty) || inboundQty < 0)) {
          errorMessage.value = measureBasis === "PIECE" ? "入库数量不能小于0" : "入库重量不能小于0";
          submittingOperation.value = false;
          return;
        }
        if (measureBasis === "PIECE" && inboundQty <= 0) {
          errorMessage.value = `${activeOperation.value?.label || "半成品入库"}的入库数量必须大于0`;
          submittingOperation.value = false;
          return;
        }
        if (measureBasis === "WEIGHT" && inboundWeight <= 0) {
          errorMessage.value = `${activeOperation.value?.label || "半成品入库"}的入库重量必须大于0`;
          submittingOperation.value = false;
          return;
        }
        const validationError = validateGenericSourceLots();
        if (validationError) {
          errorMessage.value = validationError;
          submittingOperation.value = false;
          return;
        }
      }
      if (isSourceLotReturnInbound.value) {
        const validationError = validateGenericSourceLots();
        if (validationError) {
          errorMessage.value = validationError;
          submittingOperation.value = false;
          return;
        }
        const sourceLotsToSubmit = selectedGenericSourceLots.value.filter((lot) => !isProductionSurplusInbound.value || Number(lot.return_weight_kg || 0) > 0);
        let lastResult = null;
        for (const [sourceLotIndex, lot] of sourceLotsToSubmit.entries()) {
          const isLastSourceLotReturn = sourceLotIndex === sourceLotsToSubmit.length - 1;
          lastResult = await postResource("/inventory/inbound", {
            biz_type: activeOperation.value.bizType,
            item_id: isProductionSurplusInbound.value ? Number(lot.item_id || selectedItemId) : selectedItemId,
            warehouse_id: Number(genericForm.warehouse_id),
            location_id: genericForm.location_id ? Number(genericForm.location_id) : null,
            source_lot_id: Number(lot.lot_id),
            inbound_weight_kg: Number(lot.return_weight_kg || 0),
            unit_cost: Number(lot.unit_cost ?? genericForm.unit_cost ?? 0),
            lot_no: null,
            source_doc_type: isProductionSurplusInbound.value ? "PRODUCTION_LEDGER" : null,
            source_doc_id: isProductionSurplusInbound.value ? Number(selectedGenericWorkOrder.value.production_ledger_id || selectedGenericWorkOrder.value.work_order_id) : null,
            source_material_sub_batch_no: lot.lot_no || null,
            source_material_summary: isProductionSurplusInbound.value
              ? `${selectedGenericWorkOrder.value.work_order_no || "生产台账"} · ${lot.lot_no || ""}`
              : lot.lot_no || null,
            provider_name: genericForm.provider_name || null,
            waybill_no: genericRequiresLogistics.value ? genericForm.waybill_no || null : null,
            order_photo_url: genericRequiresLogistics.value ? genericForm.order_photo_url || null : null,
            freight_amount: genericRequiresLogistics.value ? Number(genericForm.freight_amount) : null,
            remark: genericForm.remark || null,
            settle_work_order: isProductionSurplusInbound.value && isLastSourceLotReturn && Boolean(genericForm.settle_work_order),
            settle_remark: isProductionSurplusInbound.value && isLastSourceLotReturn ? genericForm.settle_remark || null : null
          });
        }
        rememberWarehouseArchiveResult(lastResult, activeOperation.value.label);
        feedbackMessage.value = `${activeOperation.value.label}已登记${formatArchiveFeedback(lastResult)}`;
        genericDrawerOpen.value = false;
        await loadAll();
        return;
      }
      const inboundWeightKg = activeWarehouseType.value === "AUX" || isFinishedOutsourcingInbound.value
        ? 0
        : isSemiSourceLotTraceInbound.value && selectedSemiMeasureBasis.value === "PIECE"
          ? 0
          : Number(genericForm.weight_kg || 0);
      const inboundQty = activeWarehouseType.value === "AUX" || isFinishedOutsourcingInbound.value
        ? Number(genericForm.qty || 0)
        : isReworkScrapInbound.value
          ? Number(genericForm.qty || 0)
        : isSemiSourceLotTraceInbound.value && selectedSemiMeasureBasis.value === "PIECE"
          ? Number(genericForm.qty || 0)
          : 0;
      const selectedTraceSourceLotIds = isProductTraceOnlySourceLotInbound.value || isOutsourcingScrapInbound.value
        ? selectedGenericSourceLots.value.map((lot) => Number(lot.lot_id)).filter(Boolean)
        : [];
      const selectedTraceSourceLotNos = isProductTraceOnlySourceLotInbound.value || isOutsourcingScrapInbound.value
        ? selectedGenericSourceLots.value.map((lot) => lot.lot_no).filter(Boolean).join("、") || null
        : null;
      const result = await postResource("/inventory/inbound", {
        biz_type: activeOperation.value.bizType,
        item_id: selectedItemId,
        warehouse_id: Number(genericForm.warehouse_id),
        location_id: genericForm.location_id ? Number(genericForm.location_id) : null,
        inbound_weight_kg: inboundWeightKg,
        inbound_qty: inboundQty,
        unit_cost: isSemiSourceLotTraceInbound.value ? null : activeOperation.value.bizType === "CUSTOMER_SUPPLIED" ? 0 : Number(genericForm.unit_cost || 0),
        lot_no: isCustomerSuppliedInbound.value ? null : isProductionScrapInbound.value ? null : isReworkScrapInbound.value ? null : isProductTraceOnlySourceLotInbound.value ? null : isOutsourcingScrapInbound.value ? null : genericForm.lot_no || null,
        source_doc_type: isProductionScrapInbound.value ? "PRODUCTION_LEDGER" : isReworkScrapInbound.value ? "WORK_ORDER" : activeWarehouseType.value === "SEMI" ? "PRODUCT_ROUTE_OPERATION" : null,
        source_doc_id: isProductionScrapInbound.value
          ? Number(selectedGenericWorkOrder.value.production_ledger_id || selectedGenericWorkOrder.value.work_order_id)
          : isReworkScrapInbound.value
            ? Number(selectedGenericWorkOrder.value.work_order_id)
            : activeWarehouseType.value === "SEMI"
              ? Number(selectedSourceDocId || 0)
              : null,
        source_line_id: activeWarehouseType.value === "SEMI" ? Number(selectedSourceLineId || 0) : null,
        source_lot_ids: selectedTraceSourceLotIds,
        source_material_sub_batch_no: isCustomerSuppliedInbound.value
          ? null
          : isProductTraceOnlySourceLotInbound.value || isOutsourcingScrapInbound.value
            ? selectedTraceSourceLotNos
            : genericForm.source_material_sub_batch_no || null,
        source_material_summary: isProductionScrapInbound.value
          ? selectedGenericWorkOrder.value.source_lot_summary || selectedGenericWorkOrder.value.work_order_no || null
          : isReworkScrapInbound.value
            ? selectedGenericWorkOrder.value.work_order_no || null
          : activeWarehouseType.value === "SEMI"
            ? selectedSourceSummary
            : isFinishedOutsourcingInbound.value
              ? genericForm.provider_name || null
            : isOutsourcingScrapInbound.value
              ? genericForm.provider_name || null
            : genericForm.provider_name || genericForm.source_material_sub_batch_no || null,
        provider_name: genericForm.provider_name || null,
        waybill_no: genericRequiresLogistics.value ? genericForm.waybill_no || null : null,
        order_photo_url: genericRequiresLogistics.value ? genericForm.order_photo_url || null : null,
        freight_amount: genericRequiresLogistics.value ? Number(genericForm.freight_amount) : null,
        remark: genericForm.remark || null,
        settle_work_order: (isProductionScrapInbound.value || isReworkScrapInbound.value) && Boolean(genericForm.settle_work_order),
        settle_remark: isProductionScrapInbound.value || isReworkScrapInbound.value ? genericForm.settle_remark || null : null
      });
      rememberWarehouseArchiveResult(result, activeOperation.value.label);
      feedbackMessage.value = `${activeOperation.value.label}已登记${formatArchiveFeedback(result)}`;
    }
    genericDrawerOpen.value = false;
    await loadAll();
  } catch (error) {
    errorMessage.value = error.message || `${activeOperation.value?.label || "出入库"}登记失败`;
  } finally {
    submittingOperation.value = false;
  }
}

async function exportOpeningTemplate() {
  resetFeedback();
  openingImportBusy.value = true;
  try {
    await downloadResource(
      `/inventory/opening/template?warehouse_type=${encodeURIComponent(activeWarehouseType.value)}`,
      `${activeInventoryLabel.value}期初导入模版.xlsx`
    );
    feedbackMessage.value = `${activeInventoryLabel.value}期初导入模版已导出`;
  } catch (error) {
    errorMessage.value = error.message || "期初导入模版导出失败";
  } finally {
    openingImportBusy.value = false;
  }
}

async function importOpeningInventory(event) {
  const file = event.target.files?.[0];
  if (!file) {
    errorMessage.value = "请选择期初库存 Excel 文件";
    return;
  }
  resetFeedback();
  openingImportBusy.value = true;
  try {
    const formData = new FormData();
    formData.append("file", file);
    const result = await uploadResource(`/inventory/opening/import?warehouse_type=${encodeURIComponent(activeWarehouseType.value)}`, formData);
    feedbackMessage.value = result.message || `${activeInventoryLabel.value}期初库存导入完成`;
    openingImportDrawerOpen.value = false;
    await loadAll();
  } catch (error) {
    errorMessage.value = error.message || "期初库存导入失败";
  } finally {
    openingImportBusy.value = false;
    if (openingImportInputRef.value) {
      openingImportInputRef.value.value = "";
    }
  }
}

async function submitRawReturnOutbound() {
  resetFeedback();
  const validationError = validateRawReturnForm();
  if (validationError) {
    errorMessage.value = validationError;
    return;
  }
  submittingOperation.value = true;
  try {
    const result = await postResource("/inventory/raw-material-return-outbound", {
      warehouse_id: Number(rawReturnForm.warehouse_id),
      waybill_no: rawReturnForm.waybill_no || null,
      order_photo_url: rawReturnForm.order_photo_url || null,
      freight_amount: isFreightBlank(rawReturnForm.freight_amount) ? null : Number(rawReturnForm.freight_amount),
      reason: rawReturnForm.reason || null,
      remark: rawReturnForm.remark || null,
      items: [
        {
          lot_id: Number(rawReturnForm.lot_id),
          purchase_order_item_id: Number(rawReturnForm.purchase_order_item_id),
          return_weight_kg: Number(rawReturnForm.return_weight_kg || 0),
          reason: rawReturnForm.reason || null,
          remark: rawReturnForm.remark || null
        }
      ]
    });
    rememberWarehouseArchiveResult(result, "退货出库");
    feedbackMessage.value = `退货出库 ${result.return_no} 已登记${formatArchiveFeedback(result)}`;
    rawReturnDrawerOpen.value = false;
    await loadAll();
    resetRawReturnForm();
  } catch (error) {
    errorMessage.value = error.message || "退货出库登记失败";
  } finally {
    submittingOperation.value = false;
  }
}

async function submitReturnInbound() {
  resetFeedback();
  const validationError = validateReturnInboundForm();
  if (validationError) {
    errorMessage.value = validationError;
    return;
  }
  const line = selectedReturnInboundDeliveryLine.value;
  submittingOperation.value = true;
  try {
    const result = await postResource("/returns/orders", {
      customer_id: Number(line.customer_id),
      sales_order_id: line.sales_order_id ? Number(line.sales_order_id) : null,
      delivery_id: Number(line.delivery_id),
      warehouse_id: Number(returnInboundForm.warehouse_id),
      handler_employee_id: returnInboundForm.handler_employee_id ? Number(returnInboundForm.handler_employee_id) : null,
      return_reason: returnInboundForm.return_reason || null,
      remark: returnInboundForm.remark || null,
      items: [
        {
          delivery_item_id: Number(line.delivery_item_id),
          sales_order_item_id: line.sales_order_item_id ? Number(line.sales_order_item_id) : null,
          product_item_id: Number(line.product_item_id),
          source_lot_id: line.lot_id ? Number(line.lot_id) : null,
          return_qty: Number(returnInboundForm.return_qty || 0),
          return_weight_kg: Number(returnInboundForm.return_weight_kg || 0),
          remark: returnInboundForm.remark || null
        }
      ]
    });
    rememberWarehouseArchiveResult(result, "退货入库");
    feedbackMessage.value = `退货入库 ${result.return_no} 已创建${formatArchiveFeedback(result)}`;
    returnInboundDrawerOpen.value = false;
    await loadAll();
    resetReturnInboundForm();
  } catch (error) {
    errorMessage.value = error.message || "退货入库失败";
  } finally {
    submittingOperation.value = false;
  }
}

async function submitReturnScrapInbound() {
  resetFeedback();
  const validationError = validateReturnScrapInboundForm();
  if (validationError) {
    errorMessage.value = validationError;
    return;
  }
  const line = selectedReturnScrapDeliveryLine.value;
  submittingOperation.value = true;
  try {
    const result = await postResource("/returns/scrap-inbound", {
      customer_id: Number(line.customer_id),
      delivery_id: Number(line.delivery_id),
      warehouse_id: Number(returnScrapInboundForm.warehouse_id),
      handler_employee_id: returnScrapInboundForm.handler_employee_id ? Number(returnScrapInboundForm.handler_employee_id) : null,
      return_reason: returnScrapInboundForm.return_reason || null,
      remark: returnScrapInboundForm.remark || null,
      items: [
        {
          delivery_item_id: Number(line.delivery_item_id),
          product_item_id: Number(line.product_item_id),
          source_lot_id: line.lot_id ? Number(line.lot_id) : null,
          scrap_qty: Number(returnScrapInboundForm.scrap_qty || 0),
          scrap_weight_kg: Number(returnScrapInboundForm.scrap_weight_kg || 0),
          unit_cost: returnScrapInboundForm.unit_cost === "" ? null : Number(returnScrapInboundForm.unit_cost || 0),
          remark: returnScrapInboundForm.remark || null
        }
      ]
    });
    rememberWarehouseArchiveResult(result, "退货废料入库");
    feedbackMessage.value = `退货废料入库 ${result.return_no} 已创建${formatArchiveFeedback(result)}`;
    returnScrapInboundDrawerOpen.value = false;
    await loadAll();
    resetReturnScrapInboundForm();
  } catch (error) {
    errorMessage.value = error.message || "退货废料入库失败";
  } finally {
    submittingOperation.value = false;
  }
}

async function submitReturnReworkOutbound() {
  resetFeedback();
  const validationError = validateReturnReworkForm();
  if (validationError) {
    errorMessage.value = validationError;
    return;
  }
  submittingOperation.value = true;
  try {
    const result = await postResource("/returns/rework-outbound", {
      return_item_id: Number(returnReworkForm.return_item_id),
      disposition_type: "REWORK",
      extra_cost_amount: Number(returnReworkForm.extra_cost_amount || 0),
      remark: returnReworkForm.remark || null
    });
    rememberWarehouseArchiveResult(result, "返工出库");
    feedbackMessage.value = `返工出库 ${result.disposition_no} 已完成${formatArchiveFeedback(result)}`;
    returnReworkDrawerOpen.value = false;
    await loadAll();
    resetReturnReworkForm();
  } catch (error) {
    errorMessage.value = error.message || "返工出库失败";
  } finally {
    submittingOperation.value = false;
  }
}

async function submitProductionOut() {
  resetFeedback();
  if (!Number(productionForm.product_item_id)) {
    errorMessage.value = "请选择产品";
    return;
  }
  const lotValidationError = validateSelectedProductionStockLots(selectedProductionStockLots.value);
  if (lotValidationError) {
    errorMessage.value = lotValidationError;
    return;
  }
  submittingOperation.value = true;
  try {
    const selectedStockLotPayload = selectedProductionStockLots.value.map((lot) => ({
      source_lot_id: Number(lot.lot_id),
      material_item_id: Number(lot.item_id),
      issued_weight_kg: Number(lot.issued_weight_kg)
    }));
    const result = await postResource("/production/work-orders", {
      source_sales_order_item_id: null,
      product_item_id: Number(productionForm.product_item_id),
      planned_qty: Number(referenceProductionQty.value || 0),
      issue_weight_kg: Number(productionForm.issue_weight_kg || 0),
      planned_start_time: null,
      planned_end_time: null,
      priority_level: 3,
      auto_issue_materials: false,
      selected_stock_lots: selectedStockLotPayload,
      remark: productionForm.remark || null
    });
    feedbackMessage.value = `生产出库 ${result.work_order_no} 已创建`;
    productionDrawerOpen.value = false;
    await loadAll();
    resetProductionForm();
  } catch (error) {
    errorMessage.value = error.message || "生产出库失败";
  } finally {
    submittingOperation.value = false;
  }
}

function productionWorkOrderInboundDeviationExceeded(actual, theoretical) {
  const actualValue = Number(actual || 0);
  const theoreticalValue = Number(theoretical || 0);
  if (!Number.isFinite(actualValue) || !Number.isFinite(theoreticalValue)) {
    return false;
  }
  if (theoreticalValue === 0) {
    return actualValue !== 0;
  }
  return Math.abs(actualValue - theoreticalValue) / Math.abs(theoreticalValue) > 0.15;
}

function productionWorkOrderInboundFinishedQtyExceedsReference() {
  const preview = productionWorkOrderInboundPreview.value;
  if (!preview) {
    return false;
  }
  const totalFinishedQty = Number(preview.finished_inbound_qty || 0) + Number(productionWorkOrderInboundForm.finished_qty || 0);
  const referenceFinishedQty = Number(preview.reference_finished_qty || 0);
  if (!Number.isFinite(totalFinishedQty) || !Number.isFinite(referenceFinishedQty)) {
    return false;
  }
  if (referenceFinishedQty <= 0) {
    return totalFinishedQty > 0;
  }
  return totalFinishedQty > referenceFinishedQty * 1.15;
}

function productionWorkOrderInboundNeedsDeviationRemark() {
  const preview = productionWorkOrderInboundPreview.value;
  if (!preview) {
    return false;
  }
  return (
    productionWorkOrderInboundFinishedQtyExceedsReference() ||
    productionWorkOrderInboundDeviationExceeded(
      productionWorkOrderInboundForm.surplus_weight_kg,
      preview.theoretical_surplus_weight_kg
    ) ||
    productionWorkOrderInboundDeviationExceeded(
      productionWorkOrderInboundForm.scrap_weight_kg,
      preview.theoretical_scrap_weight_kg
    )
  );
}

function buildProductionWorkOrderInboundDeviationPrompt() {
  const preview = productionWorkOrderInboundPreview.value || {};
  const reasons = [];
  if (productionWorkOrderInboundFinishedQtyExceedsReference()) {
    reasons.push(
      `成品入库数量超过本生产台账参考生产数量上限15%：参考 ${formatQty(preview.reference_finished_qty)} 件，已入库 ${formatQty(preview.finished_inbound_qty)} 件，本次 ${formatQty(productionWorkOrderInboundForm.finished_qty)} 件`
    );
  }
  if (productionWorkOrderInboundDeviationExceeded(productionWorkOrderInboundForm.surplus_weight_kg, preview.theoretical_surplus_weight_kg)) {
    reasons.push(`余料入库重量与理论值偏差超过上下15%：理论 ${formatWeight(preview.theoretical_surplus_weight_kg)}，本次 ${formatWeight(productionWorkOrderInboundForm.surplus_weight_kg)}`);
  }
  if (productionWorkOrderInboundDeviationExceeded(productionWorkOrderInboundForm.scrap_weight_kg, preview.theoretical_scrap_weight_kg)) {
    reasons.push(`废料重量与理论值偏差超过上下15%：理论 ${formatWeight(preview.theoretical_scrap_weight_kg)}，本次 ${formatWeight(productionWorkOrderInboundForm.scrap_weight_kg)}`);
  }
  return [
    "以下指标超过15%导致异常，请填写偏差说明：",
    "",
    ...reasons.map((reason, index) => `${index + 1}. ${reason}`)
  ].join("\n");
}

function ensureProductionWorkOrderInboundDeviationRemark() {
  if (!productionWorkOrderInboundNeedsDeviationRemark() || String(productionWorkOrderInboundForm.deviation_remark || "").trim()) {
    return true;
  }
  const remark = window.prompt(buildProductionWorkOrderInboundDeviationPrompt());
  if (!String(remark || "").trim()) {
    errorMessage.value = "偏差超过上下15%时必须填写偏差说明";
    return false;
  }
  productionWorkOrderInboundForm.deviation_remark = String(remark).trim();
  return true;
}

function buildProductionWorkOrderInboundConfirmMessage(finishedQty, surplusWeight, scrapWeight) {
  const preview = productionWorkOrderInboundPreview.value || {};
  const lines = [
    "请确认本次生产台账入库",
    "",
    `生产台账：${preview.material_lot_no || ""} · ${preview.product_name || ""}`,
    `产品：${preview.product_name || ""}`,
    `材料库存批次号：${preview.material_lot_no || ""}`,
    `参考成品数量：${formatQty(preview.reference_finished_qty)} 件`,
    `已入库成品：${formatQty(preview.finished_inbound_qty)} 件`,
    `成品入库：${formatQty(finishedQty)} 件`,
    `余料入库：${formatWeight(surplusWeight)}`,
    `废料入库：${formatWeight(scrapWeight)}`,
    `理论余料：${formatWeight(preview.theoretical_surplus_weight_kg)}`,
    `理论废料：${formatWeight(preview.theoretical_scrap_weight_kg)}`
  ];
  if (productionWorkOrderInboundForm.settle_work_order) {
    lines.push(
      "",
      "本次勾选了该批材料结单。确认后该生产台账将变为锁单，小程序不再显示该材料库存批次号，后续不能再对该批材料继续生产入库。"
    );
  }
  return lines.join("\n");
}

async function submitProductionWorkOrderInbound() {
  resetFeedback();
  if (!Number(selectedInboundWorkOrderId.value || 0)) {
    errorMessage.value = "请选择生产台账";
    return;
  }
  if (!productionWorkOrderInboundPreview.value) {
    await loadProductionWorkOrderInboundPreview(true);
  }
  const finishedQty = Number(productionWorkOrderInboundForm.finished_qty || 0);
  const surplusWeight = Number(productionWorkOrderInboundForm.surplus_weight_kg || 0);
  const scrapWeight = Number(productionWorkOrderInboundForm.scrap_weight_kg || 0);
  if (finishedQty < 0 || surplusWeight < 0 || scrapWeight < 0) {
    errorMessage.value = "入库数量或重量不能小于0";
    return;
  }
  if (finishedQty <= 0 && surplusWeight <= 0 && scrapWeight <= 0) {
    errorMessage.value = "生产台账入库至少需要填写成品、余料或废料中的一项";
    return;
  }
  const confirmed = window.confirm(buildProductionWorkOrderInboundConfirmMessage(finishedQty, surplusWeight, scrapWeight));
  if (!confirmed) {
    return;
  }
  if (!ensureProductionWorkOrderInboundDeviationRemark()) {
    return;
  }
  submittingOperation.value = true;
  try {
    const result = await postResource("/production/production-ledger-inbounds", {
      production_ledger_id: Number(selectedInboundWorkOrderId.value),
      finished_qty: finishedQty,
      surplus_weight_kg: surplusWeight,
      scrap_weight_kg: scrapWeight,
      deviation_remark: productionWorkOrderInboundForm.deviation_remark || null,
      remark: productionWorkOrderInboundForm.remark || null,
      lock_material_batch: Boolean(productionWorkOrderInboundForm.settle_work_order),
      lock_confirmed: Boolean(productionWorkOrderInboundForm.settle_work_order)
    });
    rememberWarehouseArchiveResult(result, "生产台账入库");
    feedbackMessage.value = `生产台账入库 ${result.material_lot_no || ""} 已保存${formatArchiveFeedback(result)}`;
    productionWorkOrderInboundDrawerOpen.value = false;
    await loadAll();
    resetProductionWorkOrderInboundForm();
  } catch (error) {
    errorMessage.value = error.message || "生产台账入库保存失败";
  } finally {
    submittingOperation.value = false;
  }
}

async function submitCompletionInbound() {
  resetFeedback();
  if (!selectedCompletionWorkOrder.value) {
    errorMessage.value = `${isReworkCompletionOperation.value ? "返工入库" : "生产入库"}必须先选择${isReworkCompletionOperation.value ? "返工工单" : "生产工单"}`;
    return;
  }
  const receiptQty = Number(completionForm.receipt_qty || 0);
  const toleranceMax = Number(completionForm.tolerance_receivable_qty || completionForm.max_receivable_qty || 0);
  if (!Number.isFinite(receiptQty) || receiptQty <= 0) {
    errorMessage.value = "入库数量必须大于0";
    return;
  }
  if (toleranceMax > 0 && receiptQty > toleranceMax) {
    errorMessage.value = "入库数量超过系统允许范围，请调整后重试";
    return;
  }
  if (completionForm.settle_work_order && completionSettleNeedsRemark.value && !String(completionForm.settle_remark || "").trim()) {
    errorMessage.value = "该工单结单入库成品数量超出标准成品数量上下15%，请填写结单说明";
    return;
  }
  submittingOperation.value = true;
  try {
    const result = await postResource("/production/completion-receipts", {
      work_order_id: Number(completionForm.work_order_id),
      warehouse_id: Number(completionForm.warehouse_id),
      receiver_employee_id: completionForm.receiver_employee_id ? Number(completionForm.receiver_employee_id) : null,
      remark: completionForm.remark || null,
      settle_work_order: Boolean(completionForm.settle_work_order),
      settle_remark: completionForm.settle_work_order ? completionForm.settle_remark || null : null,
      items: [
        {
          product_item_id: Number(completionForm.product_item_id),
          location_id: completionForm.location_id ? Number(completionForm.location_id) : null,
          receipt_qty: Number(completionForm.receipt_qty || 0),
          receipt_weight_kg: Number(completionForm.receipt_weight_kg || 0)
        }
      ]
    });
    feedbackMessage.value = `${isReworkCompletionOperation.value ? "返工入库单" : "生产入库单"} ${result.receipt_no} 已创建`;
    completionDrawerOpen.value = false;
    await loadAll();
    resetCompletionForm();
  } catch (error) {
    errorMessage.value = error.message || `${isReworkCompletionOperation.value ? "返工入库" : "生产入库"}失败`;
  } finally {
    submittingOperation.value = false;
  }
}

async function submitSalesOut() {
  resetFeedback();
  if (isSalesOrderDelivery.value && !Number(deliveryForm.sales_order_id)) {
    errorMessage.value = "请选择销售订单";
    return;
  }
  if (isSalesOrderDelivery.value && !Number(deliveryForm.sales_order_item_id)) {
    errorMessage.value = "请选择销售订单明细";
    return;
  }
  if (!Number(deliveryForm.customer_id)) {
    errorMessage.value = "请选择收货客户";
    return;
  }
  if (!Number(deliveryForm.product_item_id)) {
    errorMessage.value = "请选择发货产品";
    return;
  }
  if (!Number(deliveryForm.warehouse_id)) {
    errorMessage.value = "请选择发货仓";
    return;
  }
  const requestedQty = Number(deliveryForm.delivery_qty || 0);
  if (!Number.isFinite(requestedQty) || requestedQty <= 0) {
    errorMessage.value = "发货数量必须大于0";
    return;
  }
  if (totalAllocatedQty.value < requestedQty) {
    errorMessage.value = deliveryStockShortageMessage(requestedQty, totalAvailableQty.value);
    return;
  }
  const logisticsError = validateLogisticsForm(deliveryForm);
  if (logisticsError) {
    errorMessage.value = logisticsError;
    return;
  }
  submittingOperation.value = true;
  try {
    const result = await postResource("/sales/deliveries", {
      sales_order_id: isSalesOrderDelivery.value ? Number(deliveryForm.sales_order_id) : null,
      customer_id: Number(deliveryForm.customer_id),
      warehouse_id: Number(deliveryForm.warehouse_id),
      shipper_employee_id: deliveryForm.shipper_employee_id ? Number(deliveryForm.shipper_employee_id) : null,
      consignee_name: deliveryForm.consignee_name || null,
      consignee_phone: deliveryForm.consignee_phone || null,
      delivery_address: deliveryForm.delivery_address || null,
      waybill_no: deliveryForm.waybill_no || null,
      order_photo_url: deliveryForm.order_photo_url || null,
      freight_amount: Number(deliveryForm.freight_amount),
      remark: deliveryForm.remark || null,
      items: allocationRows.value.map((item) => ({
        sales_order_item_id: isSalesOrderDelivery.value ? Number(deliveryForm.sales_order_item_id) : null,
        product_item_id: Number(deliveryForm.product_item_id),
        lot_id: Number(item.lot_id),
        delivery_qty: Number(item.delivery_qty || 0),
        delivery_weight_kg: Number(item.delivery_weight_kg || 0),
        unit_price: Number(selectedSalesOrderItem.value?.unit_price || 0),
        remark: deliveryForm.remark || null
      }))
    });
    rememberWarehouseArchiveResult(result, "销售出库单");
    feedbackMessage.value = `销售出库单 ${result.delivery_no} 已创建${formatArchiveFeedback(result)}`;
    deliveryDrawerOpen.value = false;
    await loadAll();
    resetDeliveryForm();
  } catch (error) {
    errorMessage.value = error.message || "销售出库失败";
  } finally {
    submittingOperation.value = false;
  }
}

async function loadProductionStockLotOptions(materialItemIds) {
  const requestSeq = ++productionStockLotOptionsRequestSeq;
  const ids = Array.from(new Set((materialItemIds || []).map((id) => Number(id)).filter(Boolean)));
  if (!ids.length) {
    productionStockLotOptions.value = [];
    return;
  }
  const resultSets = await Promise.all(
    ids.map((materialItemId) => fetchResource(`/inventory/stock-lot-options?material_item_id=${materialItemId}&limit=1000`, []))
  );
  if (requestSeq !== productionStockLotOptionsRequestSeq) {
    return;
  }
  const optionRows = resultSets.flatMap((rows) => (Array.isArray(rows) ? rows : []));
  productionStockLotOptions.value = optionRows;
  const optionMap = new Map(optionRows.map((lot) => [Number(lot.lot_id), lot]));
  selectedProductionStockLots.value = selectedProductionStockLots.value
    .filter((lot) => optionMap.has(Number(lot.lot_id)))
    .map((lot) => ({
      ...optionMap.get(Number(lot.lot_id)),
      issued_weight_kg: normalizeIssuedWeight(lot.issued_weight_kg)
    }));
}

async function loadGenericSourceLotOptions(materialItemIds) {
  const requestSeq = ++genericSourceLotOptionsRequestSeq;
  const ids = [...new Set((Array.isArray(materialItemIds) ? materialItemIds : [materialItemIds]).map((item) => Number(item || 0)).filter(Boolean))];
  if (!ids.length || !usesGenericSourceLotSelector.value) {
    genericSourceLotOptions.value = [];
    selectedGenericSourceLots.value = [];
    return;
  }
  try {
    if (isScrapSaleOutbound.value) {
      const optionRows = buildScrapSaleSourceLotOptions(genericForm.item_id, genericForm.warehouse_id);
      if (requestSeq !== genericSourceLotOptionsRequestSeq) {
        return;
      }
      genericSourceLotOptions.value = optionRows;
      const optionMap = new Map(optionRows.map((lot) => [Number(lot.lot_id), lot]));
      selectedGenericSourceLots.value = selectedGenericSourceLots.value
        .filter((lot) => optionMap.has(Number(lot.lot_id)))
        .map((lot) => ({
          ...optionMap.get(Number(lot.lot_id)),
          issued_weight_kg: normalizeIssuedWeight(lot.issued_weight_kg)
        }));
      return;
    }
    if (isProductionSurplusInbound.value && selectedGenericWorkOrder.value) {
      const optionRows = buildWorkOrderSourceLotOptions(selectedGenericWorkOrder.value);
      if (requestSeq !== genericSourceLotOptionsRequestSeq) {
        return;
      }
      genericSourceLotOptions.value = optionRows;
      const optionMap = new Map(optionRows.map((lot) => [Number(lot.lot_id), lot]));
      selectedGenericSourceLots.value = selectedGenericSourceLots.value
        .filter((lot) => optionMap.has(Number(lot.lot_id)))
        .map((lot) => {
          const latestOption = optionMap.get(Number(lot.lot_id)) || {};
          return {
            ...latestOption,
            return_weight_kg: Number(lot.return_weight_kg || 0),
            issued_weight_kg: Number(latestOption.issued_weight_kg || 0)
          };
        });
      return;
    }
    const resultSets = await Promise.all(
      ids.map((materialItemId) => fetchResource(`/inventory/stock-lot-options?material_item_id=${materialItemId}&limit=1000`, []))
    );
    if (requestSeq !== genericSourceLotOptionsRequestSeq) {
      return;
    }
    const rowsByLotId = new Map();
    resultSets.flatMap((rows) => (Array.isArray(rows) ? rows : [])).forEach((lot) => rowsByLotId.set(Number(lot.lot_id), lot));
    genericSourceLotOptions.value = Array.from(rowsByLotId.values());
    const optionMap = new Map(genericSourceLotOptions.value.map((lot) => [Number(lot.lot_id), lot]));
    selectedGenericSourceLots.value = selectedGenericSourceLots.value
      .filter((lot) => optionMap.has(Number(lot.lot_id)))
      .map((lot) => ({
        ...optionMap.get(Number(lot.lot_id)),
        return_weight_kg: Number(lot.return_weight_kg || 0),
        issued_weight_kg: normalizeIssuedWeight(lot.issued_weight_kg)
      }));
  } catch (error) {
    if (requestSeq === genericSourceLotOptionsRequestSeq) {
      genericSourceLotOptions.value = [];
      selectedGenericSourceLots.value = [];
      errorMessage.value = error.message || "来源库存批次号加载失败";
    }
  }
}

function normalizeIssuedWeight(value) {
  const issuedWeight = Number(value);
  return Number.isFinite(issuedWeight) ? issuedWeight : 0;
}

function validateSelectedProductionStockLots(lots) {
  if (!lots.length) {
    return "请选择一个材料库存批次号";
  }
  if (lots.length > 1) {
    return "一次生产出库只能选择一个材料库存批次号";
  }
  const allowedMaterialIds = new Set(selectedProductionBomMaterialRows.value.map((item) => Number(item.material_item_id || 0)).filter(Boolean));
  const lot = lots[0];
  if (allowedMaterialIds.size && !allowedMaterialIds.has(Number(lot.item_id || 0))) {
    return `材料库存批次号 ${lot.lot_no || "未命名批次"} 不是该产品需用清单中的默认用料`;
  }
  const issuedWeight = Number(lot.issued_weight_kg);
  const remainingWeight = Number(lot.remaining_weight_kg || 0);
  const lotNo = lot.lot_no || "未命名批次";
  if (!Number.isFinite(issuedWeight) || issuedWeight <= 0) {
    return `材料库存批次号 ${lotNo} 的本次出库重量必须大于0`;
  }
  if (issuedWeight > remainingWeight) {
    return `材料库存批次号 ${lotNo} 的本次出库重量不能超过剩余重量 ${formatWeight(remainingWeight)}`;
  }
  return "";
}

async function loadAll() {
  const [
    stockLots,
    stockBalances,
    inventoryTransactions,
    materialRows,
    productRows,
    bomRows,
    processRouteRows,
    workOrderRows,
    workOrderLedgerRowData,
    productionLedgerRowData,
    warehouseRows,
    locationRows,
    employeeRows,
    customerRows,
    salesOrderRows,
    salesOrderItemRows,
    deliveryRows,
    deliveryItemRows,
    returnItemRows
  ] = await Promise.all([
    fetchResource("/inventory/stock-lots?limit=500", []),
    fetchResource("/inventory/stock-balances?limit=500", []),
    fetchResource("/inventory/transactions?limit=800", []),
    fetchResource("/master-data/materials?limit=200", []),
    fetchResource("/master-data/products?limit=200", []),
    fetchResource("/master-data/boms?limit=500", []),
    fetchResource("/master-data/process-routes?limit=500", []),
    fetchResource("/production/work-orders?limit=500", []),
    fetchResource("/production/work-order-ledger?limit=500", []),
    fetchResource("/production/production-ledger?limit=500", []),
    fetchResource("/master-data/warehouses", []),
    fetchResource("/master-data/locations", []),
    fetchPermissionEmployeeOptions(PERSONNEL_PERMISSION_CODES.INVENTORY_LEDGER, 500),
    fetchResource("/sales/customers?limit=200", []),
    fetchResource("/sales/orders?limit=500", []),
    fetchResource("/sales/order-items?limit=1000", []),
    fetchResource("/sales/deliveries?limit=500", []),
    fetchResource("/sales/delivery-items?limit=500", []),
    fetchResource("/returns/items?limit=500", [])
  ]);
  lots.value = stockLots;
  balances.value = stockBalances;
  transactions.value = inventoryTransactions;
  materials.value = materialRows;
  products.value = productRows;
  boms.value = bomRows;
  processRoutes.value = processRouteRows;
  workOrders.value = workOrderRows;
  workOrderLedgerRows.value = workOrderLedgerRowData;
  productionLedgerRows.value = productionLedgerRowData;
  warehouses.value = warehouseRows;
  locations.value = locationRows;
  employees.value = employeeRows;
  customers.value = customerRows;
  salesOrders.value = salesOrderRows;
  salesOrderItems.value = salesOrderItemRows;
  deliveries.value = deliveryRows;
  deliveryItems.value = deliveryItemRows;
  returnItems.value = returnItemRows;
}

watch([warehouseLedgerPage, warehouseLedgerPageSize], () => {
  if (warehouseLedgerDrawerOpen.value) {
    loadWarehouseLedger();
  }
});

watch(activeInventoryTab, () => {
  if (warehouseLedgerDrawerOpen.value) {
    warehouseLedgerPage.value = 1;
    Object.assign(warehouseLedgerFilters, buildEmptyWarehouseLedgerFilters());
    loadWarehouseLedger();
  }
});

watch(
  () => [
    genericForm.item_id,
    genericForm.source_line_id,
    genericForm.work_order_id,
    genericForm.warehouse_id,
    activeWarehouseType.value,
    activeOperation.value?.bizType,
    genericDrawerOpen.value,
    genericSourceLotMaterialIds.value.join(",")
  ],
  async () => {
    if (!genericDrawerOpen.value || !usesGenericSourceLotSelector.value) {
      return;
    }
    selectedGenericSourceLots.value = [];
    await loadGenericSourceLotOptions(genericSourceLotMaterialIds.value);
  }
);

watch(
  selectedGenericSourceLots,
  () => {
    if (usesGenericSourceLotSelector.value && !isProductTraceOnlySourceLotInbound.value) {
      genericForm.weight_kg = Number(genericSelectedSourceLotWeight.value.toFixed(3));
    }
  },
  { deep: true }
);

watch(() => productionForm.product_item_id, async () => {
  if (!productionDrawerOpen.value) {
    return;
  }
  selectedProductionStockLots.value = [];
  productionForm.issue_weight_kg = 0;
  await loadProductionStockLotOptions(selectedProductionBomMaterialRows.value.map((item) => item.material_item_id));
});

watch(
  selectedProductionStockLots,
  () => {
    if (selectedProductionStockLots.value.length) {
      productionForm.issue_weight_kg = Number(selectedProductionIssueWeight.value.toFixed(3));
    } else {
      productionForm.issue_weight_kg = 0;
    }
  },
  { deep: true }
);

onMounted(async () => {
  await loadAll();
});

onBeforeUnmount(clearWarehouseLedgerTooltipListeners);
</script>

<style scoped>
.work-order-settle-panel {
  display: grid;
  gap: 12px;
  padding: 14px;
  border: 1px solid rgba(37, 99, 235, 0.14);
  border-radius: 18px;
  background: linear-gradient(135deg, rgba(239, 246, 255, 0.92), rgba(255, 255, 255, 0.96));
  box-shadow: 0 12px 28px rgba(37, 99, 235, 0.08);
}

.work-order-settle-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  width: fit-content;
  cursor: pointer;
  color: #1f2937;
}

.work-order-settle-toggle.is-disabled {
  cursor: not-allowed;
  color: #94a3b8;
}

.work-order-settle-toggle input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.settle-dot {
  width: 18px;
  height: 18px;
  border: 2px solid #94a3b8;
  border-radius: 999px;
  background: #fff;
  box-shadow: inset 0 0 0 4px #fff;
  transition: border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
}

.work-order-settle-toggle input:checked + .settle-dot {
  border-color: #2563eb;
  background: #2563eb;
  box-shadow: inset 0 0 0 4px #fff, 0 0 0 4px rgba(37, 99, 235, 0.12);
}

.work-order-settle-toggle input:disabled + .settle-dot {
  border-color: #cbd5e1;
  background: #f8fafc;
}

.inventory-live-summary {
  margin-top: 14px;
  margin-bottom: 8px;
  padding: 12px 16px;
  border: 1px solid #d9ecff;
  border-radius: 4px;
  background: #ecf5ff;
  color: #606266;
  font-size: 14px;
}

.special-adjustment-warning {
  display: grid;
  gap: 6px;
  padding: 14px 16px;
  border: 1px solid rgba(180, 83, 9, 0.24);
  border-radius: 16px;
  background: linear-gradient(135deg, rgba(255, 247, 237, 0.96), rgba(254, 243, 199, 0.72));
  color: #78350f;
}

.special-adjustment-warning strong {
  font-size: 15px;
}

.special-adjustment-warning span,
.special-adjustment-tip,
.special-adjustment-selected-note p {
  color: #92400e;
  font-size: 12px;
}

.special-adjustment-lines {
  display: grid;
  grid-column: 1 / -1;
  gap: 14px;
  min-width: 0;
}

.special-adjustment-line-card {
  position: relative;
  overflow: hidden;
  border: 1.5px solid rgba(20, 14, 6, 0.72);
  background:
    linear-gradient(90deg, rgba(31, 122, 74, 0.06), transparent 44%),
    rgba(255, 250, 240, 0.72);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.5),
    0 8px 18px rgba(52, 39, 15, 0.08);
}

.special-adjustment-line-card::before {
  position: absolute;
  inset: 5px;
  content: "";
  border: 1px dashed rgba(20, 14, 6, 0.16);
  pointer-events: none;
}

.special-adjustment-line-head {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 44px;
  padding: 8px 10px 8px 12px;
  border-bottom: 1.5px solid rgba(20, 14, 6, 0.72);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.26), transparent),
    rgba(227, 207, 161, 0.62);
}

.special-adjustment-line-head div {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}

.special-adjustment-line-head strong {
  color: #17120a;
  font-family: var(--ui-font-display);
  font-size: 14px;
  font-weight: 900;
  letter-spacing: 0.06em;
}

.special-adjustment-line-index {
  display: inline-grid;
  place-items: center;
  min-width: 58px;
  height: 26px;
  padding: 0 8px;
  color: var(--document-paper-stamp, #1f7a4a);
  border: 1.5px solid currentColor;
  background: rgba(232, 246, 232, 0.58);
  font-family: var(--ui-font-display);
  font-size: 12px;
  font-weight: 900;
}

.special-adjustment-card-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  border-left: 1.5px solid rgba(20, 14, 6, 0.72);
}

.special-adjustment-card-field,
.special-adjustment-card-preview {
  display: grid;
  grid-template-rows: auto minmax(48px, auto);
  min-width: 0;
  margin: 0;
  border-right: 1.5px solid rgba(20, 14, 6, 0.72);
  border-bottom: 1.5px solid rgba(20, 14, 6, 0.72);
  background: rgba(255, 250, 240, 0.42);
}

.special-adjustment-card-field-wide,
.special-adjustment-card-preview {
  grid-column: span 2;
}

.special-adjustment-card-field > span,
.special-adjustment-card-preview > span {
  display: flex;
  align-items: center;
  min-height: 30px;
  padding: 7px 10px;
  color: rgba(23, 18, 10, 0.74);
  border-bottom: 1px solid rgba(20, 14, 6, 0.48);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.2), transparent),
    rgba(227, 207, 161, 0.5);
  font-family: var(--ui-font-display);
  font-size: 12px;
  font-weight: 900;
  letter-spacing: 0.06em;
}

.special-adjustment-card-field input,
.special-adjustment-card-field select,
.special-adjustment-card-static,
.special-adjustment-selected-note p,
.special-adjustment-card-preview strong {
  width: 100%;
  min-width: 0;
  min-height: 48px;
  margin: 0;
  padding: 10px 11px;
  color: #17120a;
  border: 0;
  border-radius: 0;
  outline: 0;
  background: transparent;
  box-shadow: none;
  font-family: "Songti SC", "SimSun", var(--ui-font-body);
  font-size: 14px;
  font-weight: 800;
  line-height: 1.45;
}

.special-adjustment-card-field input:focus,
.special-adjustment-card-field select:focus {
  background:
    linear-gradient(90deg, rgba(31, 122, 74, 0.12), transparent 42%),
    rgba(255, 255, 255, 0.48);
  box-shadow: inset 0 0 0 2px rgba(31, 122, 74, 0.42);
}

.special-adjustment-card-field select {
  cursor: pointer;
}

.special-adjustment-card-static {
  display: flex;
  align-items: center;
  color: #6a5631;
}

.special-adjustment-selected-note {
  background:
    repeating-linear-gradient(-45deg, rgba(31, 122, 74, 0.05) 0 5px, transparent 5px 10px),
    rgba(232, 246, 232, 0.34);
}

.special-adjustment-selected-note p {
  display: flex;
  align-items: center;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.special-adjustment-card-preview {
  background:
    linear-gradient(90deg, rgba(31, 122, 74, 0.1), transparent 56%),
    rgba(255, 250, 240, 0.62);
}

.special-adjustment-card-preview strong {
  display: flex;
  align-items: center;
  color: #145c39;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.special-adjustment-empty {
  display: grid;
  gap: 6px;
  min-height: 92px;
  padding: 18px;
  color: #6a5631;
  border: 1.5px dashed rgba(106, 86, 49, 0.42);
  background: rgba(255, 250, 240, 0.58);
}

.special-adjustment-empty strong {
  color: #17120a;
  font-family: var(--ui-font-display);
  font-size: 15px;
  font-weight: 900;
}

.special-adjustment-empty span {
  font-size: 13px;
  font-weight: 800;
}

@media (max-width: 960px) {
  .special-adjustment-card-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 620px) {
  .special-adjustment-line-head {
    align-items: stretch;
    flex-direction: column;
  }

  .special-adjustment-card-grid {
    grid-template-columns: 1fr;
  }

  .special-adjustment-card-field-wide,
  .special-adjustment-card-preview {
    grid-column: 1 / -1;
  }
}

.settle-summary-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.settle-summary-grid article {
  padding: 10px 12px;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.82);
  border: 1px solid rgba(148, 163, 184, 0.22);
}

.settle-summary-grid span {
  display: block;
  margin-bottom: 4px;
  color: #64748b;
  font-size: 12px;
}

.settle-summary-grid strong {
  color: #0f172a;
}

.settle-summary-grid .settle-warning {
  color: #b45309;
}

@media (max-width: 720px) {
  .settle-summary-grid {
    grid-template-columns: 1fr;
  }
}
</style>
