$row */ public function enrich(array $row): array { $remark = trim((string) ($row['remark'] ?? '')); $title = trim((string) ($row['title'] ?? '')); $isLogistics = preg_match('/记账物流|物流记账|物流.*不记账|不记账.*物流/u', $remark) === 1; $isNoBookkeeping = ! $isLogistics && str_contains($remark, '不记账'); $target = $isLogistics ? '物流' : ($isNoBookkeeping ? '不记账' : '厂家'); $logisticsQty = 0.0; $originalQty = 0.0; if ($isLogistics) { if (preg_match('/\*\s*(\d+(?:\.\d+)?)\s*记账物流/u', $remark, $matches) === 1 || preg_match('/记账物流\s*[×xX*]?\s*(\d+(?:\.\d+)?)/u', $remark, $matches) === 1) { $logisticsQty = (float) $matches[1]; } if (preg_match('/原数量\s*(\d+(?:\.\d+)?)/u', $remark, $matches) === 1) { $originalQty = (float) $matches[1]; } if ($logisticsQty === 0.0 && $originalQty > 0) { $logisticsQty = max(0.0, $originalQty - $this->number($row['qty'] ?? 0)); } } $qty = $this->number($row['qty'] ?? 0); // 厂家净扣减只按实际单价/金额计算,不能用预估单价或预估金额兜底。 $unitPrice = $this->number($row['unit_price'] ?? 0); $amount = $this->number($row['amount'] ?? 0); if ($amount === 0.0 && $qty !== 0.0) { $amount = $qty * $unitPrice; } if ($unitPrice === 0.0 && $qty !== 0.0) { $unitPrice = $amount / $qty; } $supplierQty = $qty; $supplierAmount = $amount; $amountContainsLogistics = $originalQty > 0 && $logisticsQty > 0 && abs($amount - ($originalQty * $unitPrice)) < 0.01; if ($amountContainsLogistics) { $supplierQty = max(0.0, $originalQty - $logisticsQty); $supplierAmount = max(0.0, $amount - ($logisticsQty * $unitPrice)); } $row['_accounting_target'] = $target; $row['_accounting_label'] = $isLogistics ? '转物流记账' : ($isNoBookkeeping ? '不记账' : '厂家扣减'); $row['_accounting_note'] = $isLogistics ? '不扣本厂家,转' . ($title !== '' ? $title : '物流') . '记账' : ($isNoBookkeeping ? '本厂家不记账' : '按厂家退货扣减'); $row['_logistics_qty'] = $isLogistics ? $logisticsQty : 0.0; $row['_logistics_amount'] = $isLogistics ? round($logisticsQty * $unitPrice, 2) : 0.0; $row['_logistics_company'] = $isLogistics ? $title : ''; $row['_supplier_qty'] = round($supplierQty, 2); $row['_supplier_amount'] = round($supplierAmount, 2); return $row; } /** * @param list> $rows * @return array{count:int,supplier_rows:int,supplier_qty:float,supplier_amount:float,logistics_rows:int,logistics_qty:float,logistics_amount:float,logistics_companies:list,no_bookkeeping_rows:int} */ public function summarize(array $rows): array { $summary = [ 'count' => count($rows), 'supplier_rows' => 0, 'supplier_qty' => 0.0, 'supplier_amount' => 0.0, 'logistics_rows' => 0, 'logistics_qty' => 0.0, 'logistics_amount' => 0.0, 'logistics_companies' => [], 'no_bookkeeping_rows' => 0, ]; $companies = []; foreach ($rows as $row) { $row = $this->enrich($row); $target = (string) $row['_accounting_target']; if ($target === '不记账') { $summary['no_bookkeeping_rows']++; continue; } $summary['supplier_rows']++; $summary['supplier_qty'] += (float) $row['_supplier_qty']; $summary['supplier_amount'] += (float) $row['_supplier_amount']; if ($target !== '物流') { continue; } $summary['logistics_rows']++; $summary['logistics_qty'] += (float) $row['_logistics_qty']; $summary['logistics_amount'] += (float) $row['_logistics_amount']; $company = trim((string) $row['_logistics_company']); if ($company !== '') { $companies[$company] = true; } } $summary['supplier_qty'] = round($summary['supplier_qty'], 2); $summary['supplier_amount'] = round($summary['supplier_amount'], 2); $summary['logistics_qty'] = round($summary['logistics_qty'], 2); $summary['logistics_amount'] = round($summary['logistics_amount'], 2); $summary['logistics_companies'] = array_keys($companies); return $summary; } private function number(mixed $value): float { $raw = trim((string) $value); return is_numeric(str_replace(',', '', $raw)) ? (float) str_replace(',', '', $raw) : 0.0; } }