323 lines
15 KiB
PHP
323 lines
15 KiB
PHP
<?php
|
||
|
||
namespace Modules\EcommerceFinance\Repositories;
|
||
|
||
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
|
||
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
|
||
|
||
/**
|
||
* 原材料-封边条只读数据仓库。
|
||
*
|
||
* 数据源只暴露固定的 SELECT,页面请求通过 JianSunQueryCache 做快照、限流
|
||
* 和熔断。源表中不存在的展示字段使用空值,不从演示数据倒灌真实业务数据。
|
||
*/
|
||
class EdgeBandingRepository
|
||
{
|
||
private const SOURCE = 'jiansun';
|
||
|
||
private ?UpstreamReadOnlyQuery $query = null;
|
||
|
||
public function __construct(private readonly UpstreamReadOnlyQueryFactory $queries)
|
||
{
|
||
}
|
||
|
||
/** @return array{data:array<int,array<string,mixed>>,total:int} */
|
||
public function prices(array $filters, int $page, int $limit): array
|
||
{
|
||
$where = [
|
||
"ISNULL(d.[delete], 0) = 0",
|
||
'price_info.latest_price IS NOT NULL',
|
||
];
|
||
$bindings = [];
|
||
|
||
if (($filters['raw_name'] ?? '') !== '') {
|
||
$where[] = 'd.name LIKE ? ESCAPE \'\\\'';
|
||
$bindings[] = $this->contains($filters['raw_name']);
|
||
}
|
||
if (($filters['raw_code'] ?? '') !== '') {
|
||
$where[] = 'd.number LIKE ? ESCAPE \'\\\'';
|
||
$bindings[] = $this->contains($filters['raw_code']);
|
||
}
|
||
if (($filters['production_code'] ?? '') !== '') {
|
||
$where[] = 'd.proNumber LIKE ? ESCAPE \'\\\'';
|
||
$bindings[] = $this->contains($filters['production_code']);
|
||
}
|
||
if (($filters['supplier'] ?? '') !== '') {
|
||
$where[] = 'supplier_info.target_supplier LIKE ? ESCAPE \'\\\'';
|
||
$bindings[] = $this->contains($filters['supplier']);
|
||
}
|
||
|
||
$whereSql = implode(' AND ', $where);
|
||
$page = max($page, 1);
|
||
$limit = min(max($limit, 1), 100);
|
||
$offset = ($page - 1) * $limit;
|
||
|
||
$sql = "WITH source_rows AS (
|
||
SELECT
|
||
d.name AS raw_name,
|
||
d.number AS raw_code,
|
||
d.proNumber AS production_code,
|
||
CAST(price_info.latest_price AS decimal(18,2)) AS latest_price,
|
||
supplier_info.target_supplier,
|
||
ROW_NUMBER() OVER (ORDER BY d.number, d.proNumber) AS row_no,
|
||
COUNT(*) OVER () AS total_count
|
||
FROM [dbo].[pm_edgeBandData] d
|
||
OUTER APPLY (
|
||
SELECT TOP 1 ad.giveSupplierName AS target_supplier
|
||
FROM [dbo].[pm_edgeBandApplyDetail] ad
|
||
WHERE ad.proNumber = d.proNumber
|
||
AND ISNULL(ad.giveSupplierName, '') <> ''
|
||
ORDER BY ad.inDate DESC, ad.Id DESC
|
||
) supplier_info
|
||
OUTER APPLY (
|
||
SELECT TOP 1 p.priceA AS latest_price
|
||
FROM [dbo].[pm_edgeBandProfitInfo] p
|
||
WHERE p.name = d.name
|
||
AND p.giveSupplierName = supplier_info.target_supplier
|
||
AND ISNULL(p.priceA, 0) > 0
|
||
ORDER BY p.inDate DESC, p.id DESC
|
||
) price_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<int,array<string,mixed>>,total:int} */
|
||
public function details(array $filters, int $page, int $limit): array
|
||
{
|
||
$where = ["d.number LIKE 'FBT%'"];
|
||
$bindings = [];
|
||
|
||
if (($filters['date'] ?? '') !== '') {
|
||
$where[] = 'CONVERT(date, d.inDate) = ?';
|
||
$bindings[] = $filters['date'];
|
||
}
|
||
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['return_flag'] ?? '') === 'return') {
|
||
$where[] = 'COALESCE(d.inCount, d.deliveryCount, 0) < 0';
|
||
} elseif (($filters['return_flag'] ?? '') === 'normal') {
|
||
$where[] = 'COALESCE(d.inCount, d.deliveryCount, 0) >= 0';
|
||
}
|
||
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;
|
||
}
|
||
|
||
$whereSql = implode(' AND ', $where);
|
||
$page = max($page, 1);
|
||
$limit = min(max($limit, 1), 100);
|
||
$offset = ($page - 1) * $limit;
|
||
|
||
$sql = "WITH source_rows AS (
|
||
SELECT
|
||
CONVERT(varchar(19), d.inDate, 120) AS inbound_date,
|
||
d.number AS production_code,
|
||
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(COALESCE(d.priceBAll, ISNULL(d.inCount, 0) * ISNULL(d.priceB, 0), 0) AS decimal(18,2)) AS reconciliation_amount,
|
||
CAST(ISNULL(freight_info.transportCost, 0) AS decimal(18,2)) AS freight,
|
||
CAST(ISNULL(d.price, 0) AS decimal(18,2)) AS edge_band_unit_price,
|
||
CAST(COALESCE(d.priceAll, ISNULL(d.inCount, 0) * ISNULL(d.price, 0), 0) AS decimal(18,2)) AS payable_amount,
|
||
CAST(ISNULL(d.priceB, 0) AS decimal(18,2)) AS furniture_unit_price,
|
||
CAST(COALESCE(d.priceBAll, ISNULL(d.inCount, 0) * ISNULL(d.priceB, 0), 0) AS decimal(18,2)) AS furniture_amount,
|
||
CAST(ISNULL(d.priceB, 0) - ISNULL(d.price, 0) AS decimal(18,2)) AS price_difference,
|
||
CAST(
|
||
COALESCE(d.priceBAll, ISNULL(d.inCount, 0) * ISNULL(d.priceB, 0), 0)
|
||
- COALESCE(d.priceAll, ISNULL(d.inCount, 0) * ISNULL(d.price, 0), 0)
|
||
AS decimal(18,2)
|
||
) AS profit,
|
||
ROW_NUMBER() OVER (ORDER BY d.inDate DESC, d.id DESC) AS row_no,
|
||
COUNT(*) OVER () AS total_count
|
||
FROM [dbo].[finance_edgeBandDeductio] d
|
||
OUTER APPLY (
|
||
SELECT TOP 1 ad.transportCost
|
||
FROM [dbo].[pm_edgeBandApplyDetail] ad
|
||
WHERE ad.applyNo = d.applyNo
|
||
AND (ad.name = d.name OR ad.proNumber LIKE CONCAT(d.number, '%'))
|
||
ORDER BY CASE WHEN ad.name = d.name THEN 0 ELSE 1 END, ad.Id DESC
|
||
) freight_info
|
||
WHERE {$whereSql}
|
||
)
|
||
SELECT inbound_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, reconciliation_amount, freight, edge_band_unit_price,
|
||
payable_amount, furniture_unit_price, furniture_amount, price_difference,
|
||
profit, 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['unit'] = '米';
|
||
$row['return_flag'] = (float) ($row['outbound_quantity'] ?? 0) < 0 ? '退货' : '正常出库';
|
||
}
|
||
|
||
return $this->pageResult($rows);
|
||
}
|
||
|
||
/** @return array{data:array<int,array<string,mixed>>,total:int} */
|
||
public function payables(array $filters, int $page, int $limit): array
|
||
{
|
||
$where = [
|
||
"EXISTS (
|
||
SELECT 1
|
||
FROM [dbo].[pm_averageUnitPrice] edge_price
|
||
WHERE edge_price.number LIKE 'FBT%'
|
||
AND edge_price.supplier = p.supplier
|
||
)",
|
||
];
|
||
$bindings = [];
|
||
|
||
if (($filters['period'] ?? '') !== '') {
|
||
$where[] = 'p.Month = ?';
|
||
$bindings[] = $filters['period'];
|
||
}
|
||
if (($filters['supplier'] ?? '') !== '') {
|
||
$where[] = 'p.supplier LIKE ? ESCAPE \'\\\'';
|
||
$bindings[] = $this->contains($filters['supplier']);
|
||
}
|
||
|
||
$whereSql = implode(' AND ', $where);
|
||
$page = max($page, 1);
|
||
$limit = min(max($limit, 1), 100);
|
||
$offset = ($page - 1) * $limit;
|
||
|
||
$sql = "WITH source_rows AS (
|
||
SELECT
|
||
p.supplier,
|
||
CAST(SUM(ISNULL(p.bginMonthPay, 0)) AS decimal(18,2)) AS month_start_unpaid,
|
||
CAST(SUM(ISNULL(p.loan, 0)) AS decimal(18,2)) AS goods_amount,
|
||
CAST(SUM(ISNULL(p.ExpFieght, 0)) AS decimal(18,2)) AS prepaid_freight,
|
||
CAST(SUM(ISNULL(p.Totalindemnity, 0)) AS decimal(18,2)) AS compensation,
|
||
CAST(SUM(ISNULL(p.supplySup, 0)) AS decimal(18,2)) AS supplier_refund,
|
||
CAST(SUM(ISNULL(p.ShouldPay, 0)) AS decimal(18,2)) AS payable_amount,
|
||
CAST(SUM(ISNULL(p.factPay, 0)) AS decimal(18,2)) AS paid_amount,
|
||
CAST(SUM(ISNULL(p.cashierbypaid, 0)) AS decimal(18,2)) AS actual_payment,
|
||
CAST(SUM(ISNULL(p.toPrivate, 0)) AS decimal(18,2)) AS private_payment,
|
||
CAST(SUM(ISNULL(p.toCorporate, 0)) AS decimal(18,2)) AS corporate_payment,
|
||
MAX(ISNULL(p.remarks, '')) AS remark,
|
||
CAST(SUM(SUM(ISNULL(p.bginMonthPay, 0))) OVER () AS decimal(18,2)) AS summary_month_start_unpaid,
|
||
CAST(SUM(SUM(ISNULL(p.loan, 0))) OVER () AS decimal(18,2)) AS summary_goods_amount,
|
||
CAST(SUM(SUM(ISNULL(p.ExpFieght, 0))) OVER () AS decimal(18,2)) AS summary_prepaid_freight,
|
||
CAST(SUM(SUM(ISNULL(p.Totalindemnity, 0))) OVER () AS decimal(18,2)) AS summary_compensation,
|
||
CAST(SUM(SUM(ISNULL(p.supplySup, 0))) OVER () AS decimal(18,2)) AS summary_supplier_refund,
|
||
CAST(SUM(SUM(ISNULL(p.ShouldPay, 0))) OVER () AS decimal(18,2)) AS summary_payable_amount,
|
||
CAST(SUM(SUM(ISNULL(p.factPay, 0))) OVER () AS decimal(18,2)) AS summary_paid_amount,
|
||
CAST(SUM(SUM(ISNULL(p.cashierbypaid, 0))) OVER () AS decimal(18,2)) AS summary_actual_payment,
|
||
CAST(SUM(SUM(ISNULL(p.toPrivate, 0))) OVER () AS decimal(18,2)) AS summary_private_payment,
|
||
CAST(SUM(SUM(ISNULL(p.toCorporate, 0))) OVER () AS decimal(18,2)) AS summary_corporate_payment,
|
||
ROW_NUMBER() OVER (ORDER BY p.supplier) AS row_no,
|
||
COUNT(*) OVER () AS total_count
|
||
FROM [dbo].[fys_supplierPaymentSummary] p
|
||
WHERE {$whereSql}
|
||
GROUP BY p.supplier
|
||
)
|
||
SELECT supplier, month_start_unpaid, goods_amount, prepaid_freight,
|
||
compensation, supplier_refund, payable_amount, paid_amount,
|
||
actual_payment, private_payment, corporate_payment, remark,
|
||
summary_month_start_unpaid, summary_goods_amount,
|
||
summary_prepaid_freight, summary_compensation,
|
||
summary_supplier_refund, summary_payable_amount,
|
||
summary_paid_amount, summary_actual_payment,
|
||
summary_private_payment, summary_corporate_payment,
|
||
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]));
|
||
|
||
return $this->payablesPageResult($rows);
|
||
}
|
||
|
||
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<int,array<string,mixed>> $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];
|
||
}
|
||
|
||
/** @param array<int,array<string,mixed>> $rows */
|
||
private function payablesPageResult(array $rows): array
|
||
{
|
||
$summaryKeys = [
|
||
'month_start_unpaid', 'goods_amount', 'prepaid_freight', 'compensation',
|
||
'supplier_refund', 'payable_amount', 'paid_amount', 'actual_payment',
|
||
'private_payment', 'corporate_payment',
|
||
];
|
||
$totals = array_fill_keys($summaryKeys, 0.0);
|
||
|
||
if ($rows !== []) {
|
||
foreach ($summaryKeys as $key) {
|
||
$totals[$key] = (float) ($rows[0]['summary_' . $key] ?? 0);
|
||
}
|
||
}
|
||
|
||
foreach ($rows as &$row) {
|
||
foreach ($summaryKeys as $key) {
|
||
unset($row['summary_' . $key]);
|
||
}
|
||
}
|
||
|
||
return array_merge($this->pageResult($rows), ['totals' => $totals]);
|
||
}
|
||
}
|