>,total:int} */ public function prices(array $filters, int $page, int $limit): array { $where = ['ISNULL(p.price, 0) > 0']; $bindings = []; if (($filters['raw_name'] ?? '') !== '') { $where[] = "ISNULL(plain_material.name, '') LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['raw_name']); } if (($filters['raw_code'] ?? '') !== '') { $where[] = "p.number LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['raw_code']); } if (($filters['production_code'] ?? '') !== '') { $where[] = "COALESCE(NULLIF(p.proNumber, ''), plain_material.proNumber, '') LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['production_code']); } if (($filters['supplier'] ?? '') !== '') { $where[] = "COALESCE(NULLIF(supplier_info.supplierText, ''), p.supplier, '') LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['supplier']); } $page = max($page, 1); $limit = min(max($limit, 1), 100); $offset = ($page - 1) * $limit; $whereSql = implode(' AND ', $where); $sql = "WITH latest_plain_materials AS ( SELECT number, MAX(Id) AS id FROM [dbo].[pm_plainPlateData] WHERE ISNULL([delete], 0) = 0 AND NULLIF(number, '') IS NOT NULL GROUP BY number ), deduplicated_prices AS ( SELECT number, supplier, proNumber, price, MAX(COALESCE(updateTime, createTime)) AS sort_time, MAX(id) AS sort_id FROM [dbo].[pm_boardPurchasePrice] WHERE ISNULL(price, 0) > 0 GROUP BY number, supplier, proNumber, price ), source_rows AS ( SELECT ISNULL(plain_material.name, '') AS raw_name, ISNULL(p.number, '') AS raw_code, COALESCE(NULLIF(p.proNumber, ''), plain_material.proNumber, '') AS production_code, CAST(p.price AS decimal(18,2)) AS latest_price, COALESCE(NULLIF(supplier_info.supplierText, ''), p.supplier, '') AS target_supplier, ROW_NUMBER() OVER ( ORDER BY p.sort_time DESC, p.sort_id DESC ) AS row_no, COUNT(*) OVER () AS total_count FROM deduplicated_prices p INNER JOIN latest_plain_materials latest_plain ON latest_plain.number = p.number INNER JOIN [dbo].[pm_plainPlateData] plain_material ON plain_material.Id = latest_plain.id OUTER APPLY ( SELECT TOP 1 i.supplierText FROM [dbo].[pm_boardPriceChangeImport] i WHERE i.number = p.number AND i.supplier = p.supplier AND NULLIF(LTRIM(RTRIM(i.supplierText)), '') IS NOT NULL ORDER BY i.id DESC ) supplier_info WHERE {$whereSql} ) SELECT raw_name, raw_code, production_code, latest_price, target_supplier, total_count FROM source_rows WHERE row_no BETWEEN ? AND ? ORDER BY row_no"; $rows = $this->query()->select($sql, array_merge($bindings, [$offset + 1, $offset + $limit])); foreach ($rows as &$row) { $row['price_type'] = '采购价'; $row['factory_type'] = '素板厂'; } return $this->pageResult($rows); } /** @return array{data:array>,total:int,totals:array} */ public function reconciliations(array $filters, int $page, int $limit): array { $where = ['1 = 1']; $bindings = []; if (($filters['date_from'] ?? '') !== '') { $where[] = 'CONVERT(date, d.inDate) >= ?'; $bindings[] = $filters['date_from']; } if (($filters['date_to'] ?? '') !== '') { $where[] = 'CONVERT(date, d.inDate) <= ?'; $bindings[] = $filters['date_to']; } if (($filters['production_code'] ?? '') !== '') { $where[] = "d.number LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['production_code']); } if (($filters['raw_name'] ?? '') !== '') { $where[] = "d.name LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['raw_name']); } if (($filters['outbound_supplier'] ?? '') !== '') { $where[] = "d.giveSupplier LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['outbound_supplier']); } if (($filters['arrival_supplier'] ?? '') !== '') { $where[] = "d.arrSupplier LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains($filters['arrival_supplier']); } if (($filters['outbound_no'] ?? '') !== '') { $where[] = "(ISNULL(d.deliveryNo, '') LIKE ? ESCAPE '\\' OR ISNULL(d.sendNo, '') LIKE ? ESCAPE '\\')"; $needle = $this->contains($filters['outbound_no']); $bindings[] = $needle; $bindings[] = $needle; } if (($filters['return_flag'] ?? '') === 'return') { $where[] = 'ISNULL(d.deliveryCount, 0) < 0'; } elseif (($filters['return_flag'] ?? '') === 'normal') { $where[] = 'ISNULL(d.deliveryCount, 0) >= 0'; } $page = max($page, 1); $limit = min(max($limit, 1), 100); $offset = ($page - 1) * $limit; $whereSql = implode(' AND ', $where); $sql = "WITH source_rows AS ( SELECT CONVERT(varchar(19), d.inDate, 120) AS inbound_date, CONVERT(varchar(19), d.deliveryDate, 120) AS outbound_date, ISNULL(d.number, '') AS production_code, ISNULL(d.name, '') AS raw_name, CAST(ISNULL(d.deliveryCount, 0) AS decimal(18,2)) AS outbound_quantity, ISNULL(d.giveSupplier, '') AS outbound_supplier, ISNULL(d.arrSupplier, '') AS arrival_supplier, CONVERT(varchar(10), d.date, 120) AS batch_date, ISNULL(d.deliveryNo, '') AS outbound_no, ISNULL(d.deliveryRemarks, '') AS outbound_remark, ISNULL(d.applyNo, '') AS requisition_no, ISNULL(d.numberType, '') AS number_type, ISNULL(d.sendNo, '') AS delivery_no, CAST(ISNULL(d.inCount, 0) AS decimal(18,2)) AS received_quantity, CAST(ISNULL(d.priceA, 0) AS decimal(18,2)) AS unit_price, CAST( COALESCE(d.moneyA, ISNULL(d.inCount, 0) * ISNULL(d.priceA, 0), 0) AS decimal(18,2) ) AS reconciliation_amount, CAST(NULL AS decimal(18,2)) AS freight_with_allocation, CAST(NULL AS decimal(18,2)) AS allocated_freight, CAST(ISNULL(freight_info.transportCost, 0) AS decimal(18,2)) AS freight, CAST(SUM(CAST(ISNULL(d.deliveryCount, 0) AS decimal(38,2))) OVER () AS decimal(18,2)) AS summary_outbound_quantity, CAST(SUM(CAST(ISNULL(d.inCount, 0) AS decimal(38,2))) OVER () AS decimal(18,2)) AS summary_received_quantity, CAST(SUM(CAST( COALESCE(d.moneyA, ISNULL(d.inCount, 0) * ISNULL(d.priceA, 0), 0) AS decimal(38,2) )) OVER () AS decimal(18,2)) AS summary_reconciliation_amount, ROW_NUMBER() OVER (ORDER BY d.inDate DESC, d.id DESC) AS row_no, COUNT(*) OVER () AS total_count FROM [dbo].[finance_boardDeduction] d OUTER APPLY ( SELECT TOP 1 ad.transportCost FROM [dbo].[pm_boardApplyDetail] ad WHERE ad.applyNo = d.applyNo AND ad.name = d.name ORDER BY CASE WHEN ad.inCount = d.inCount AND ad.deliveryCount = d.deliveryCount THEN 0 ELSE 1 END, ad.Id DESC ) freight_info WHERE {$whereSql} ) SELECT inbound_date, outbound_date, production_code, raw_name, outbound_quantity, outbound_supplier, arrival_supplier, batch_date, outbound_no, outbound_remark, requisition_no, number_type, delivery_no, received_quantity, unit_price, reconciliation_amount, freight_with_allocation, allocated_freight, freight, summary_outbound_quantity, summary_received_quantity, summary_reconciliation_amount, total_count FROM source_rows WHERE row_no BETWEEN ? AND ? ORDER BY row_no"; $rows = $this->query()->select($sql, array_merge($bindings, [$offset + 1, $offset + $limit])); $totals = array_fill_keys(self::RECONCILIATION_SUMMARY_KEYS, 0.0); if ($rows !== []) { foreach (self::RECONCILIATION_SUMMARY_KEYS as $key) { $totals[$key] = (float) ($rows[0]['summary_' . $key] ?? 0); } } foreach ($rows as &$row) { $row['unit'] = '张'; $row['return_flag'] = (float) ($row['outbound_quantity'] ?? 0) < 0 ? '退货' : '正常出库'; foreach (self::RECONCILIATION_SUMMARY_KEYS as $key) { unset($row['summary_' . $key]); } } return array_merge($this->pageResult($rows), ['totals' => $totals]); } private function query(): UpstreamReadOnlyQuery { return $this->query ??= $this->queries->forSource(self::SOURCE); } private function contains(string $value): string { return '%' . str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], trim($value)) . '%'; } /** @param array> $rows */ private function pageResult(array $rows): array { $total = (int) ($rows[0]['total_count'] ?? 0); foreach ($rows as &$row) { unset($row['total_count']); } return ['data' => $rows, 'total' => $total]; } }