middleware('auth'); $this->middleware('company.identify'); } public function definition(Request $request): JsonResponse { return response()->json($this->registry->publicDefinition($this->dataset($request))); } public function update(Request $request, string $recordKey): JsonResponse|RedirectResponse { $dataset = $this->dataset($request); $values = $this->registry->normalize($dataset, (array) $request->input('values', [])); $result = $this->overrides->save( $dataset, $recordKey, $values, (int) (company_id() ?: 1), user_id() ? (int) user_id() : null, ); return $request->expectsJson() ? response()->json(['message' => '保存成功。'] + $result) : back()->with('success', '保存成功。'); } public function import(Request $request): JsonResponse|RedirectResponse { $request->validate(['import' => ['required', 'file', 'mimes:xlsx,xls,csv', 'max:10240']]); $result = $this->importer->import( $this->dataset($request), $request->file('import'), (int) (company_id() ?: 1), user_id() ? (int) user_id() : null, ); $message = "导入完成:新增维护 {$result['created']} 条,更新维护 {$result['updated']} 条。"; return $request->expectsJson() ? response()->json(['message' => $message] + $result) : back()->with('success', $message); } public function template(Request $request): StreamedResponse { $dataset = $this->dataset($request); return $this->template->download($this->title($dataset), $this->registry->fields($dataset)); } public function export(Request $request): StreamedResponse { $dataset = $this->dataset($request); $fields = $this->registry->fields($dataset); $filters = $this->filters($dataset, $request); $companyId = (int) (company_id() ?: 1); return response()->streamDownload(function () use ($dataset, $fields, $filters, $companyId): void { $csv = fopen('php://output', 'w'); if ($csv === false) { throw new RuntimeException('无法创建导出文件。'); } fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF)); fputcsv($csv, array_merge(['记录标识'], array_column($fields, 'label'))); foreach ($this->allRows($dataset, $filters) as $batch) { foreach ($this->overrides->decorate($dataset, $batch, $companyId) as $row) { $values = [$this->csvText($row['_action_key'] ?? '')]; foreach ($fields as $field => $definition) { $values[] = $definition['type'] === 'number' && is_numeric($row[$field] ?? null) ? $row[$field] : $this->csvText($row[$field] ?? ''); } fputcsv($csv, $values); } } fclose($csv); }, $this->title($dataset) . '_' . now()->format('Ymd_His') . '.csv', [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Cache-Control' => 'private, no-store', ]); } /** @return iterable>> */ private function allRows(string $dataset, array $filters): iterable { if ($dataset === 'payment-reconciliation' && ($filters['record_type'] ?? 'all') === 'all') { foreach (['purchase', 'sale'] as $type) { yield from $this->allRows($dataset, array_replace($filters, ['record_type' => $type])); } return; } $page = 1; do { $result = $this->page($dataset, $filters, $page, 100); $rows = array_slice((array) ($result['data'] ?? $result['rows'] ?? []), 0, 100); if ($rows !== []) { yield $rows; } $total = (int) ($result['total'] ?? -1); $more = $rows !== [] && ($total < 0 ? count($rows) === 100 : $page * 100 < $total); $page++; } while ($more); } private function page(string $dataset, array $filters, int $page, int $limit): array { return match ($dataset) { 'payment-reconciliation' => $this->goods->list($filters, $page, $limit), 'return-reconciliation' => $this->supplier->returnReconciliations($filters, $page, $limit), 'invoice-deductions' => $this->supplier->invoiceDeductions($filters, $page, $limit), 'compensation-details' => $this->supplier->compensationDetails($filters, $page, $limit), 'advance-details' => $this->supplier->advanceDetails($filters, $page, $limit), 'sample-deductions' => $this->supplier->sampleDeductions($filters, $page, $limit), 'delivery-unpriced' => $this->supplier->deliveryUnpriced($filters, $page, $limit), 'return-unpriced' => $this->supplier->returnUnpriced($filters, $page, $limit), 'other-adjustments' => $this->supplier->otherAdjustments($filters, $page, $limit), 'dropshipping-express' => $this->supplier->dropshippingExpress($filters, $page, $limit), 'board-deductions' => $this->reports->boardDeductions($filters, $page, $limit), 'supplier-payables-report' => $this->reports->supplierPayables($filters, $page, $limit), }; } private function filters(string $dataset, Request $request): array { $allowed = match ($dataset) { 'payment-reconciliation' => ['record_type', 'supplier', 'warehouse', 'stock_no', 'purchase_no', 'product_code', 'express_no', 'send_time_from', 'send_time_to', 'create_time_from', 'create_time_to', 'sent_status', 'has_diff'], 'return-reconciliation', 'return-unpriced' => ['supplier', 'warehouse', 'sku_id', 'express_no', 'date_from', 'date_to'], 'invoice-deductions', 'compensation-details', 'advance-details', 'other-adjustments' => ['supplier', 'month_from', 'month_to'], 'sample-deductions' => ['supplier', 'department', 'creator', 'sku_id', 'date_from', 'date_to'], 'delivery-unpriced' => ['supplier', 'warehouse', 'purchase', 'barcode', 'express_no', 'date_from', 'date_to'], 'dropshipping-express' => ['supplier', 'express_name', 'month_from', 'month_to'], 'board-deductions' => ['stock_in_date', 'stock_out_date', 'furniture_factory', 'lamination_factory', 'deduction_type', 'material_name', 'claim_number'], 'supplier-payables-report' => ['month', 'supplier', 'checker', 'settlement_method'], }; return collect($request->only($allowed)) ->map(static fn (mixed $value): mixed => is_string($value) ? trim($value) : $value) ->reject(static fn (mixed $value): bool => $value === '' || $value === null || $value === false) ->all(); } private function dataset(Request $request): string { $dataset = (string) $request->route('dataset'); $this->registry->fields($dataset); return $dataset; } private function title(string $dataset): string { return [ 'payment-reconciliation' => '货款对账', 'return-reconciliation' => '退货对账', 'invoice-deductions' => '发票扣款', 'compensation-details' => '赔偿明细', 'advance-details' => '借支明细', 'sample-deductions' => '样品扣款', 'delivery-unpriced' => '送货未定价', 'return-unpriced' => '退货未定价', 'other-adjustments' => '其他扣款补款', 'dropshipping-express' => '代发快递', 'board-deductions' => '板材扣款', 'supplier-payables-report' => '供应商应付表', ][$dataset]; } private function csvText(mixed $value): string { $value = (string) $value; return preg_match('/^[=+\-@\t\r]/u', $value) === 1 ? "\t" . $value : $value; } }