CaiWuXiTong/modules/EcommerceFinance/Repositories/EdgeBandingRepository.php

323 lines
15 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\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]);
}
}