CaiWuXiTong/modules/EcommerceFinance/Repositories/SupplierPayableRepository.php

904 lines
34 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Modules\EcommerceFinance\Repositories;
use Modules\EcommerceFinance\Services\JianSunConnection;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
use PDO;
/**
* 供应商应付 - 子功能页面数据仓库
*
* 封装 5 个子页面的数据访问逻辑:
* - returnReconciliations() 退货对账
* - invoiceDeductions() 发票扣款
* - compensationDetails() 赔偿明细
* - advanceDetails() 借支明细
* - sampleDeductions() 样品扣款
*
* 所有方法返回统一结构: ['data' => array<int,array>, 'total' => int]
*/
class SupplierPayableRepository
{
private ?PDO $db = null;
private readonly UpstreamReadOnlyQuery $query;
public function __construct(UpstreamReadOnlyQueryFactory $queries)
{
$this->query = $queries->forSource('jiansun');
}
private function db(): PDO
{
return $this->db ??= JianSunConnection::get();
}
/**
* 通用工具:带筛选 + 分页的查询
*/
private function runQuery(
string $sql,
array $params,
int $page,
int $pageSize,
string $orderBy = '(SELECT 0)',
int $countThreshold = 5000
): array
{
$offset = ($page - 1) * $pageSize;
// 1. 总数(先查总数,超阈值再用近似)
$countSql = 'SELECT COUNT(*) AS cnt FROM (' . $sql . ') AS sub';
$total = 0;
try {
$stmt = $this->db()->prepare($countSql);
$stmt->execute($params);
$total = (int) $stmt->fetchColumn();
} catch (\Throwable $e) {
// 极端情况:表太大查不到总数时退回到 -1
$total = -1;
}
// 2. 分页数据:排序字段只允许由仓库内部固定传入,不接受请求参数。
// ROW_NUMBER 位于无 ORDER BY 的派生表外层,兼容旧版 SQL Server。
$pagedSql = 'SELECT * FROM (SELECT src.*, ROW_NUMBER() OVER (ORDER BY ' . $orderBy . ') AS rn FROM (' . $sql . ') AS src) AS t WHERE rn BETWEEN ? AND ? ORDER BY rn';
$paramsWithRange = array_merge($params, [$offset + 1, $offset + $pageSize]);
$stmt = $this->db()->prepare($pagedSql);
$stmt->execute($paramsWithRange);
return [
'data' => $stmt->fetchAll(),
'total' => $total,
];
}
/**
* 计算分页的 SELECT TOP N(无 COUNT),加速大表查询
*/
private function runQueryFast(
string $sql,
array $params,
int $page,
int $pageSize,
int $total = -1,
string $orderBy = '(SELECT 0)'
): array
{
$offset = ($page - 1) * $pageSize;
$pagedSql = 'SELECT * FROM (SELECT src.*, ROW_NUMBER() OVER (ORDER BY ' . $orderBy . ') AS rn FROM (' . $sql . ') AS src) AS t WHERE rn BETWEEN ? AND ? ORDER BY rn';
$paramsWithRange = array_merge($params, [$offset + 1, $offset + $pageSize]);
$stmt = $this->db()->prepare($pagedSql);
$stmt->execute($paramsWithRange);
return [
'data' => $stmt->fetchAll(),
'total' => $total,
];
}
// ============================================================
// 1. 退货对账 — finance_purchaseReturn (36791行)
// ============================================================
public function returnReconciliations(array $filters, int $page, int $pageSize): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplierName LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['warehouse'])) {
$where[] = 'warehouseName LIKE ?';
$params[] = '%' . $filters['warehouse'] . '%';
}
if (!empty($filters['sku_id'])) {
$where[] = 'sku_id LIKE ?';
$params[] = '%' . $filters['sku_id'] . '%';
}
if (!empty($filters['express_no'])) {
$where[] = 'expressNo LIKE ?';
$params[] = '%' . $filters['express_no'] . '%';
}
if (!empty($filters['date_from'])) {
$where[] = 'date >= ?';
$params[] = $filters['date_from'];
}
if (!empty($filters['date_to'])) {
$where[] = 'date <= ?';
$params[] = $filters['date_to'];
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT
id AS id,
CONVERT(varchar(10), date, 120) AS send_time,
supplierName AS supplier,
sku_id AS barcode,
name AS sku_name,
CAST(qty AS decimal(18,2)) AS qty,
CAST(price AS decimal(18,2)) AS unit_price,
CAST(price * qty AS decimal(18,2)) AS amount,
CAST(priceB AS decimal(18,2)) AS guess_unit_price,
CAST(priceB * qty AS decimal(18,2)) AS guess_amount,
remark,
io_id AS wms_pick_no,
warehouseName AS warehouse,
expressNo AS express_no,
expressName AS express_name,
CONVERT(varchar(10), io_date, 120) AS io_date,
labels,
creator_name AS creator,
'' AS title,
'' AS supplier_barcode,
'' AS original_barcode,
'' AS reconciler
FROM finance_purchaseReturn
$whereSql";
$result = $this->runQuery($sql, $params, $page, $pageSize, 'send_time DESC, wms_pick_no DESC');
return [
'data' => $result['data'],
'total' => $result['total'],
];
}
public function returnReconciliationSummary(array $filters): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplierName LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['date_from'])) {
$where[] = 'date >= ?';
$params[] = $filters['date_from'];
}
if (!empty($filters['date_to'])) {
$where[] = 'date <= ?';
$params[] = $filters['date_to'];
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT
ISNULL(SUM(price * qty), 0) AS total_amount,
ISNULL(SUM(priceB * qty), 0) AS total_guess_amount,
COUNT(*) AS total_count
FROM finance_purchaseReturn $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalAmount' => (float) $row['total_amount'],
'totalGuessAmount' => (float) $row['total_guess_amount'],
'totalCount' => (int) $row['total_count'],
];
}
// ============================================================
// 2. 发票扣款 — finance_invoiceDeduction (195行)
// ============================================================
public function invoiceDeductions(array $filters, int $page, int $pageSize): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['month_from'])) {
$where[] = 'month >= ?';
$params[] = $filters['month_from'];
}
if (!empty($filters['month_to'])) {
$where[] = 'month <= ?';
$params[] = $filters['month_to'];
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT
id AS id,
CONVERT(varchar(7), month, 120) AS month_str,
'' AS category,
supplier AS payee,
supplier AS receiver,
'' AS board_factory,
CAST(0 AS decimal(18,2)) AS subsidy_fee,
remark,
CAST(price AS decimal(18,2)) AS price,
remark AS detail,
'' AS note,
personCharge AS person_charge,
CONVERT(varchar(19), createTime, 120) AS create_time,
creator
FROM finance_invoiceDeduction
$whereSql";
return $this->runQuery($sql, $params, $page, $pageSize, 'month_str DESC, create_time DESC');
}
public function invoiceDeductionSummary(array $filters): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT ISNULL(SUM(price), 0) AS total_price, COUNT(*) AS total_count FROM finance_invoiceDeduction $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalPrice' => (float) $row['total_price'],
'totalCount' => (int) $row['total_count'],
];
}
// ============================================================
// 3. 赔偿明细 — fys_supplierPaymentSummary
// ============================================================
public function compensationDetails(array $filters, int $page, int $pageSize): array
{
[$whereSql, $bindings] = $this->compensationWhere($filters);
$page = max($page, 1);
$pageSize = min(max($pageSize, 1), 100);
$offset = ($page - 1) * $pageSize;
$total = (int) ($this->query->scalar(
"SELECT COUNT(*) FROM [dbo].[fys_supplierPaymentSummary] WHERE $whereSql",
$bindings,
) ?? 0);
$rows = $this->query->select(
"SELECT
[id] AS id,
[Month] AS month,
[supplier] AS supplier,
CAST([Totalindemnity] AS decimal(18,2)) AS amount,
[reason] AS reason,
[remarks] AS remark
FROM [dbo].[fys_supplierPaymentSummary]
WHERE $whereSql
ORDER BY [Month] DESC, [id] DESC
OFFSET $offset ROWS FETCH NEXT $pageSize ROWS ONLY",
$bindings,
);
foreach ($rows as &$row) {
$row['amount'] = (float) ($row['amount'] ?? 0);
}
unset($row);
return ['data' => $rows, 'total' => $total];
}
public function compensationDetailSummary(array $filters): array
{
[$whereSql, $bindings] = $this->compensationWhere($filters);
$row = $this->query->first(
"SELECT
ISNULL(SUM([Totalindemnity]), 0) AS total_amount,
COUNT(*) AS total_count
FROM [dbo].[fys_supplierPaymentSummary]
WHERE $whereSql",
$bindings,
) ?? [];
return [
'totalAmount' => (float) ($row['total_amount'] ?? 0),
'totalCount' => (int) ($row['total_count'] ?? 0),
];
}
/** @return array{0:string,1:array<int,string>} */
private function compensationWhere(array $filters): array
{
$where = ['[Totalindemnity] IS NOT NULL', '[Totalindemnity] <> 0'];
$bindings = [];
if (! empty($filters['supplier'])) {
$where[] = "[supplier] LIKE ? ESCAPE '\\'";
$bindings[] = '%' . str_replace(
['\\', '%', '_'],
['\\\\', '\\%', '\\_'],
trim((string) $filters['supplier']),
) . '%';
}
if (! empty($filters['month_from'])) {
$where[] = '[Month] >= ?';
$bindings[] = (string) $filters['month_from'];
}
if (! empty($filters['month_to'])) {
$where[] = '[Month] <= ?';
$bindings[] = (string) $filters['month_to'];
}
return [implode(' AND ', $where), $bindings];
}
// ============================================================
// 4. 借支明细 — finance_advanceLoans (4行, 字段不足,部分列显示-)
// ============================================================
public function advanceDetails(array $filters, int $page, int $pageSize): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['month_from'])) {
$where[] = 'month >= ?';
$params[] = $filters['month_from'];
}
if (!empty($filters['month_to'])) {
$where[] = 'month <= ?';
$params[] = $filters['month_to'];
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT
id AS id,
supplier,
'' AS contract_name,
CAST(price AS decimal(18,2)) AS advance_amount,
CAST(0 AS decimal(18,2)) AS actual_pay,
CAST(0 AS decimal(18,2)) AS month_deduct,
CAST(0 AS decimal(18,2)) AS should_deduct,
'' AS deduct_start_month,
'' AS deduct_end_month,
0 AS remain_periods,
CONVERT(varchar(7), month, 120) AS month_str,
'' AS loan_term,
remark,
'' AS is_paid,
CONVERT(varchar(7), month, 120) AS pay_month,
'' AS serial_no,
'' AS loan_agreement,
personCharge AS person_charge,
'' AS receipt_0,
'' AS receipt_1,
'' AS receipt_2,
CONVERT(varchar(19), createTime, 120) AS create_time,
creator
FROM finance_advanceLoans
$whereSql";
return $this->runQuery($sql, $params, $page, $pageSize, 'month_str DESC, create_time DESC');
}
public function advanceDetailSummary(array $filters): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT ISNULL(SUM(price), 0) AS total_amount, COUNT(*) AS total_count FROM finance_advanceLoans $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalAmount' => (float) $row['total_amount'],
'totalCount' => (int) $row['total_count'],
];
}
// ============================================================
// 5. 样品扣款 — finance_sampleDeduction (12行)
// ============================================================
public function sampleDeductions(array $filters, int $page, int $pageSize): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['department'])) {
$where[] = 'department LIKE ?';
$params[] = '%' . $filters['department'] . '%';
}
if (!empty($filters['creator'])) {
$where[] = 'creator LIKE ?';
$params[] = '%' . $filters['creator'] . '%';
}
if (!empty($filters['sku_id'])) {
$where[] = 'sku_id LIKE ?';
$params[] = '%' . $filters['sku_id'] . '%';
}
if (!empty($filters['date_from'])) {
$where[] = 'finishTime >= ?';
$params[] = $filters['date_from'];
}
if (!empty($filters['date_to'])) {
$where[] = 'finishTime <= ?';
$params[] = $filters['date_to'];
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT
id AS id,
CONVERT(varchar(10), finishTime, 120) AS finish_time,
department,
creator,
sku_id,
skuName,
CAST(num AS decimal(18,2)) AS num,
supplier,
purchase,
productCode,
CAST(shippingFee AS decimal(18,2)) AS shipping_fee,
CAST(costPrice AS decimal(18,2)) AS cost_price,
CAST(totalAmount AS decimal(18,2)) AS total_amount,
CAST(estimatedPrice AS decimal(18,2)) AS estimated_price,
CAST(estimatedPriceAll AS decimal(18,2)) AS estimated_price_all,
'' AS remark,
businessId,
CONVERT(varchar(19), updateAt, 120) AS update_time
FROM finance_sampleDeduction
$whereSql";
return $this->runQuery($sql, $params, $page, $pageSize, 'finish_time DESC');
}
public function sampleDeductionSummary(array $filters): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT
ISNULL(SUM(totalAmount), 0) AS total_amount,
ISNULL(SUM(estimatedPriceAll), 0) AS total_estimate,
COUNT(*) AS total_count
FROM finance_sampleDeduction $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalAmount' => (float) $row['total_amount'],
'totalEstimate' => (float) $row['total_estimate'],
'totalCount' => (int) $row['total_count'],
];
}
// ============================================================
// 6. 送货未定价 — pm_unOrderDetails (265452行)
// ============================================================
public function deliveryUnpriced(array $filters, int $page, int $pageSize): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['warehouse'])) {
$where[] = 'warehouse LIKE ?';
$params[] = '%' . $filters['warehouse'] . '%';
}
if (!empty($filters['purchase'])) {
$where[] = 'purchase LIKE ?';
$params[] = '%' . $filters['purchase'] . '%';
}
if (!empty($filters['barcode'])) {
$where[] = 'barCode LIKE ?';
$params[] = '%' . $filters['barcode'] . '%';
}
if (!empty($filters['express_no'])) {
$where[] = 'expressNo LIKE ?';
$params[] = '%' . $filters['express_no'] . '%';
}
if (!empty($filters['date_from'])) {
$where[] = 'date >= ?';
$params[] = $filters['date_from'];
}
if (!empty($filters['date_to'])) {
$where[] = 'date <= ?';
$params[] = $filters['date_to'];
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT
Id AS id,
CONVERT(varchar(10), date, 120) AS date,
purchase,
warehouse,
supplier,
transactionNo,
distributionNo,
expressNo,
sku,
barCode,
skuName,
CONVERT(varchar(19), distributionDate, 120) AS distribution_date,
CONVERT(varchar(10), shipDate, 120) AS ship_date,
expressName
FROM pm_unOrderDetails
$whereSql";
return $this->runQuery($sql, $params, $page, $pageSize, 'date DESC, id DESC');
}
public function deliveryUnpricedSummary(array $filters): array
{
$where = [];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
$whereSql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$sql = "SELECT COUNT(*) AS total_count FROM pm_unOrderDetails $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalCount' => (int) $row['total_count'],
];
}
// ============================================================
// 7. 退货未定价 — finance_purchaseReturn WHERE priceB = 0 (35928行)
// ============================================================
public function returnUnpriced(array $filters, int $page, int $pageSize): array
{
$where = ['(priceB = 0 OR priceB IS NULL)'];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplierName LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['warehouse'])) {
$where[] = 'warehouseName LIKE ?';
$params[] = '%' . $filters['warehouse'] . '%';
}
if (!empty($filters['sku_id'])) {
$where[] = 'sku_id LIKE ?';
$params[] = '%' . $filters['sku_id'] . '%';
}
if (!empty($filters['express_no'])) {
$where[] = 'expressNo LIKE ?';
$params[] = '%' . $filters['express_no'] . '%';
}
if (!empty($filters['date_from'])) {
$where[] = 'date >= ?';
$params[] = $filters['date_from'];
}
if (!empty($filters['date_to'])) {
$where[] = 'date <= ?';
$params[] = $filters['date_to'];
}
$whereSql = 'WHERE ' . implode(' AND ', $where);
$sql = "SELECT
id AS id,
CONVERT(varchar(10), date, 120) AS send_time,
supplierName AS supplier,
sku_id AS barcode,
name AS sku_name,
CAST(qty AS decimal(18,2)) AS qty,
CAST(price AS decimal(18,2)) AS unit_price,
CAST(price * qty AS decimal(18,2)) AS amount,
CAST(ISNULL(priceB, 0) AS decimal(18,2)) AS guess_unit_price,
CAST(ISNULL(priceB, 0) * qty AS decimal(18,2)) AS guess_amount,
remark,
io_id AS wms_pick_no,
warehouseName AS warehouse,
expressNo AS express_no,
expressName AS express_name,
CONVERT(varchar(10), io_date, 120) AS io_date,
labels,
creator_name AS creator
FROM finance_purchaseReturn
$whereSql";
return $this->runQuery($sql, $params, $page, $pageSize, 'send_time DESC');
}
public function returnUnpricedSummary(array $filters): array
{
$where = ['(priceB = 0 OR priceB IS NULL)'];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplierName LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
$whereSql = 'WHERE ' . implode(' AND ', $where);
$sql = "SELECT
ISNULL(SUM(price * qty), 0) AS total_amount,
COUNT(*) AS total_count
FROM finance_purchaseReturn $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalAmount' => (float) $row['total_amount'],
'totalCount' => (int) $row['total_count'],
];
}
// ============================================================
// 筛选器选项
// ============================================================
public function filterOptionsForReturn(): array
{
$suppliers = $this->fetchDistinct('SELECT DISTINCT supplierName FROM finance_purchaseReturn WHERE supplierName IS NOT NULL ORDER BY supplierName');
$warehouses = $this->fetchDistinct('SELECT DISTINCT warehouseName FROM finance_purchaseReturn WHERE warehouseName IS NOT NULL ORDER BY warehouseName');
return compact('suppliers', 'warehouses');
}
public function filterOptionsForInvoice(): array
{
$suppliers = $this->fetchDistinct('SELECT DISTINCT supplier FROM finance_invoiceDeduction WHERE supplier IS NOT NULL ORDER BY supplier');
return ['suppliers' => $suppliers];
}
public function filterOptionsForCompensation(): array
{
$suppliers = array_column($this->query->select(
'SELECT DISTINCT TOP 200 [supplier] AS value '
. 'FROM [dbo].[fys_supplierPaymentSummary] '
. 'WHERE [Totalindemnity] IS NOT NULL AND [Totalindemnity] <> 0 AND [supplier] IS NOT NULL '
. 'ORDER BY [supplier]',
), 'value');
return ['suppliers' => $suppliers];
}
public function filterOptionsForAdvance(): array
{
$suppliers = $this->fetchDistinct('SELECT DISTINCT supplier FROM finance_advanceLoans WHERE supplier IS NOT NULL ORDER BY supplier');
return ['suppliers' => $suppliers];
}
public function filterOptionsForDeliveryUnpriced(): array
{
$suppliers = $this->fetchDistinct('SELECT DISTINCT supplier FROM pm_unOrderDetails WHERE supplier IS NOT NULL ORDER BY supplier');
$warehouses = $this->fetchDistinct('SELECT DISTINCT warehouse FROM pm_unOrderDetails WHERE warehouse IS NOT NULL ORDER BY warehouse');
$purchases = $this->fetchDistinct('SELECT DISTINCT purchase FROM pm_unOrderDetails WHERE purchase IS NOT NULL ORDER BY purchase');
return compact('suppliers', 'warehouses', 'purchases');
}
public function filterOptionsForReturnUnpriced(): array
{
$suppliers = $this->fetchDistinct("SELECT DISTINCT supplierName FROM finance_purchaseReturn WHERE (priceB = 0 OR priceB IS NULL) AND supplierName IS NOT NULL ORDER BY supplierName");
$warehouses = $this->fetchDistinct("SELECT DISTINCT warehouseName FROM finance_purchaseReturn WHERE (priceB = 0 OR priceB IS NULL) AND warehouseName IS NOT NULL ORDER BY warehouseName");
return compact('suppliers', 'warehouses');
}
public function filterOptionsForSample(): array
{
$suppliers = $this->fetchDistinct('SELECT DISTINCT supplier FROM finance_sampleDeduction WHERE supplier IS NOT NULL ORDER BY supplier');
$departments = $this->fetchDistinct('SELECT DISTINCT department FROM finance_sampleDeduction WHERE department IS NOT NULL ORDER BY department');
$creators = $this->fetchDistinct('SELECT DISTINCT creator FROM finance_sampleDeduction WHERE creator IS NOT NULL ORDER BY creator');
return compact('suppliers', 'departments', 'creators');
}
private function fetchDistinct(string $sql): array
{
$rows = $this->db()->query($sql)->fetchAll(PDO::FETCH_COLUMN);
return array_values(array_filter($rows, fn ($v) => $v !== null && $v !== ''));
}
// ============================================================
// 8. 其他扣款补款 — fys_supplierPaymentSummary WHERE other != 0 (460行)
// ============================================================
public function otherAdjustments(array $filters, int $page, int $pageSize): array
{
$where = ['(other IS NOT NULL AND other != 0)'];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['month_from'])) {
$where[] = 'Month >= ?';
$params[] = $filters['month_from'];
}
if (!empty($filters['month_to'])) {
$where[] = 'Month <= ?';
$params[] = $filters['month_to'];
}
$whereSql = 'WHERE ' . implode(' AND ', $where);
$sql = "SELECT
id AS id,
Month AS month_str,
supplier,
CAST(other AS decimal(18,2)) AS supplement_amount,
reason AS supplement_reason,
remarks AS remark,
mark AS remark2,
checker AS person_charge
FROM fys_supplierPaymentSummary
$whereSql";
$result = $this->runQuery($sql, $params, $page, $pageSize, 'month_str DESC, supplier');
foreach ($result['data'] as &$row) {
$row['project'] = '其他扣款补款';
}
unset($row);
return $result;
}
public function otherAdjustmentsSummary(array $filters): array
{
$where = ['(other IS NOT NULL AND other != 0)'];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'supplier LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
$whereSql = 'WHERE ' . implode(' AND ', $where);
$sql = "SELECT
ISNULL(SUM(other), 0) AS total_amount,
COUNT(*) AS total_count
FROM fys_supplierPaymentSummary $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalAmount' => (float) $row['total_amount'],
'totalCount' => (int) $row['total_count'],
];
}
// ============================================================
// 9. 代发快递 — fys_express_courier_settlement (940行)
// ============================================================
public function dropshippingExpress(array $filters, int $page, int $pageSize): array
{
$where = ['(is_delete = 0 OR is_delete IS NULL)'];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'courier_company_name LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
if (!empty($filters['express_name'])) {
$where[] = 'expressname LIKE ?';
$params[] = '%' . $filters['express_name'] . '%';
}
if (!empty($filters['month_from'])) {
$where[] = 'month >= ?';
$params[] = $filters['month_from'];
}
if (!empty($filters['month_to'])) {
$where[] = 'month <= ?';
$params[] = $filters['month_to'];
}
$whereSql = 'WHERE ' . implode(' AND ', $where);
$sql = "SELECT
id AS id,
reconciliation_person,
month AS month_str,
courier_company_name AS supplier,
CAST(ISNULL(waybill, 0) AS decimal(18,2)) AS express_freight,
CAST(ISNULL(courier_provided_amount, 0) AS decimal(18,2)) AS express_account,
CAST(ISNULL(replenish_courier, 0) AS decimal(18,2)) AS replenish_freight,
CAST(ISNULL(confirmed_amount, 0) AS decimal(18,2)) AS replenish_account,
CAST(ISNULL(reconciliation_difference, 0) AS decimal(18,2)) AS return_fee,
CAST(ISNULL(compensation_amount, 0) AS decimal(18,2)) AS intercept_fee,
CAST(ISNULL(other_replenishment, 0) AS decimal(18,2)) AS change_address_fee,
CAST(ISNULL(corporate_payment_amount, 0) AS decimal(18,2)) AS turnover_fee,
CAST(ISNULL(personal_payment_amount, 0) AS decimal(18,2)) AS overlong_fee,
CAST(ISNULL(payable_amount, 0) AS decimal(18,2)) AS total_freight,
status AS remark,
expressname AS express_name,
file_status AS remark2,
creator,
CONVERT(varchar(19), create_time, 120) AS create_time
FROM fys_express_courier_settlement
$whereSql";
return $this->runQuery($sql, $params, $page, $pageSize, 'month_str DESC, express_name');
}
public function dropshippingExpressSummary(array $filters): array
{
$where = ['(is_delete = 0 OR is_delete IS NULL)'];
$params = [];
if (!empty($filters['supplier'])) {
$where[] = 'courier_company_name LIKE ?';
$params[] = '%' . $filters['supplier'] . '%';
}
$whereSql = 'WHERE ' . implode(' AND ', $where);
$sql = "SELECT
ISNULL(SUM(payable_amount), 0) AS total_amount,
COUNT(*) AS total_count
FROM fys_express_courier_settlement $whereSql";
$stmt = $this->db()->prepare($sql);
$stmt->execute($params);
$row = $stmt->fetch();
return [
'totalAmount' => (float) $row['total_amount'],
'totalCount' => (int) $row['total_count'],
];
}
public function filterOptionsForOtherAdjustments(): array
{
$suppliers = $this->fetchDistinct("SELECT DISTINCT supplier FROM fys_supplierPaymentSummary WHERE (other IS NOT NULL AND other != 0) AND supplier IS NOT NULL ORDER BY supplier");
return ['suppliers' => $suppliers];
}
public function filterOptionsForDropshippingExpress(): array
{
$suppliers = $this->fetchDistinct("SELECT DISTINCT courier_company_name FROM fys_express_courier_settlement WHERE (is_delete = 0 OR is_delete IS NULL) AND courier_company_name IS NOT NULL ORDER BY courier_company_name");
$expressNames = $this->fetchDistinct("SELECT DISTINCT expressname FROM fys_express_courier_settlement WHERE (is_delete = 0 OR is_delete IS NULL) AND expressname IS NOT NULL ORDER BY expressname");
return compact('suppliers', 'expressNames');
}
}