264 lines
12 KiB
PHP
264 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Repositories;
|
|
|
|
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
|
|
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
|
|
|
|
/**
|
|
* 原材料素板应付只读数据仓库。
|
|
*/
|
|
class RawBoardPayablesRepository
|
|
{
|
|
private const SOURCE = 'jiansun';
|
|
|
|
private const RECONCILIATION_SUMMARY_KEYS = [
|
|
'outbound_quantity',
|
|
'received_quantity',
|
|
'reconciliation_amount',
|
|
];
|
|
|
|
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(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<int,array<string,mixed>>,total:int,totals:array<string,float>} */
|
|
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<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];
|
|
}
|
|
}
|