197 lines
9.2 KiB
PHP
197 lines
9.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Business;
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Modules\EcommerceFinance\Repositories\GoodsReconciliationRepository;
|
|
use Modules\EcommerceFinance\Repositories\SupplierPayableReportRepository;
|
|
use Modules\EcommerceFinance\Repositories\SupplierPayableRepository;
|
|
use Modules\EcommerceFinance\Services\SupplierPayableMaintenanceRegistry;
|
|
use Modules\EcommerceFinance\Services\SupplierPayableImportTemplate;
|
|
use Modules\EcommerceFinance\Services\SupplierPayableOverrideStore;
|
|
use Modules\EcommerceFinance\Services\SupplierPayableWorkbookImporter;
|
|
use RuntimeException;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
class SupplierPayableActions extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly SupplierPayableRepository $supplier,
|
|
private readonly GoodsReconciliationRepository $goods,
|
|
private readonly SupplierPayableReportRepository $reports,
|
|
private readonly SupplierPayableMaintenanceRegistry $registry,
|
|
private readonly SupplierPayableImportTemplate $template,
|
|
private readonly SupplierPayableOverrideStore $overrides,
|
|
private readonly SupplierPayableWorkbookImporter $importer,
|
|
) {
|
|
$this->middleware('auth');
|
|
$this->middleware('company.identify');
|
|
}
|
|
|
|
public function definition(Request $request): JsonResponse
|
|
{
|
|
return response()->json($this->registry->publicDefinition($this->dataset($request)));
|
|
}
|
|
|
|
public function update(Request $request, string $recordKey): JsonResponse|RedirectResponse
|
|
{
|
|
$dataset = $this->dataset($request);
|
|
$values = $this->registry->normalize($dataset, (array) $request->input('values', []));
|
|
$result = $this->overrides->save(
|
|
$dataset,
|
|
$recordKey,
|
|
$values,
|
|
(int) (company_id() ?: 1),
|
|
user_id() ? (int) user_id() : null,
|
|
);
|
|
|
|
return $request->expectsJson()
|
|
? response()->json(['message' => '保存成功。'] + $result)
|
|
: back()->with('success', '保存成功。');
|
|
}
|
|
|
|
public function import(Request $request): JsonResponse|RedirectResponse
|
|
{
|
|
$request->validate(['import' => ['required', 'file', 'mimes:xlsx,xls,csv', 'max:10240']]);
|
|
$result = $this->importer->import(
|
|
$this->dataset($request),
|
|
$request->file('import'),
|
|
(int) (company_id() ?: 1),
|
|
user_id() ? (int) user_id() : null,
|
|
);
|
|
$message = "导入完成:新增维护 {$result['created']} 条,更新维护 {$result['updated']} 条。";
|
|
|
|
return $request->expectsJson()
|
|
? response()->json(['message' => $message] + $result)
|
|
: back()->with('success', $message);
|
|
}
|
|
|
|
public function template(Request $request): StreamedResponse
|
|
{
|
|
$dataset = $this->dataset($request);
|
|
|
|
return $this->template->download($this->title($dataset), $this->registry->fields($dataset));
|
|
}
|
|
|
|
public function export(Request $request): StreamedResponse
|
|
{
|
|
$dataset = $this->dataset($request);
|
|
$fields = $this->registry->fields($dataset);
|
|
$filters = $this->filters($dataset, $request);
|
|
$companyId = (int) (company_id() ?: 1);
|
|
|
|
return response()->streamDownload(function () use ($dataset, $fields, $filters, $companyId): void {
|
|
$csv = fopen('php://output', 'w');
|
|
if ($csv === false) {
|
|
throw new RuntimeException('无法创建导出文件。');
|
|
}
|
|
fputs($csv, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
|
fputcsv($csv, array_merge(['记录标识'], array_column($fields, 'label')));
|
|
|
|
foreach ($this->allRows($dataset, $filters) as $batch) {
|
|
foreach ($this->overrides->decorate($dataset, $batch, $companyId) as $row) {
|
|
$values = [$this->csvText($row['_action_key'] ?? '')];
|
|
foreach ($fields as $field => $definition) {
|
|
$values[] = $definition['type'] === 'number' && is_numeric($row[$field] ?? null)
|
|
? $row[$field]
|
|
: $this->csvText($row[$field] ?? '');
|
|
}
|
|
fputcsv($csv, $values);
|
|
}
|
|
}
|
|
fclose($csv);
|
|
}, $this->title($dataset) . '_' . now()->format('Ymd_His') . '.csv', [
|
|
'Content-Type' => 'text/csv; charset=UTF-8',
|
|
'Cache-Control' => 'private, no-store',
|
|
]);
|
|
}
|
|
|
|
/** @return iterable<array<int,array<string,mixed>>> */
|
|
private function allRows(string $dataset, array $filters): iterable
|
|
{
|
|
if ($dataset === 'payment-reconciliation' && ($filters['record_type'] ?? 'all') === 'all') {
|
|
foreach (['purchase', 'sale'] as $type) {
|
|
yield from $this->allRows($dataset, array_replace($filters, ['record_type' => $type]));
|
|
}
|
|
return;
|
|
}
|
|
|
|
$page = 1;
|
|
do {
|
|
$result = $this->page($dataset, $filters, $page, 100);
|
|
$rows = array_slice((array) ($result['data'] ?? $result['rows'] ?? []), 0, 100);
|
|
if ($rows !== []) {
|
|
yield $rows;
|
|
}
|
|
$total = (int) ($result['total'] ?? -1);
|
|
$more = $rows !== [] && ($total < 0 ? count($rows) === 100 : $page * 100 < $total);
|
|
$page++;
|
|
} while ($more);
|
|
}
|
|
|
|
private function page(string $dataset, array $filters, int $page, int $limit): array
|
|
{
|
|
return match ($dataset) {
|
|
'payment-reconciliation' => $this->goods->list($filters, $page, $limit),
|
|
'return-reconciliation' => $this->supplier->returnReconciliations($filters, $page, $limit),
|
|
'invoice-deductions' => $this->supplier->invoiceDeductions($filters, $page, $limit),
|
|
'compensation-details' => $this->supplier->compensationDetails($filters, $page, $limit),
|
|
'advance-details' => $this->supplier->advanceDetails($filters, $page, $limit),
|
|
'sample-deductions' => $this->supplier->sampleDeductions($filters, $page, $limit),
|
|
'delivery-unpriced' => $this->supplier->deliveryUnpriced($filters, $page, $limit),
|
|
'return-unpriced' => $this->supplier->returnUnpriced($filters, $page, $limit),
|
|
'other-adjustments' => $this->supplier->otherAdjustments($filters, $page, $limit),
|
|
'dropshipping-express' => $this->supplier->dropshippingExpress($filters, $page, $limit),
|
|
'board-deductions' => $this->reports->boardDeductions($filters, $page, $limit),
|
|
'supplier-payables-report' => $this->reports->supplierPayables($filters, $page, $limit),
|
|
};
|
|
}
|
|
|
|
private function filters(string $dataset, Request $request): array
|
|
{
|
|
$allowed = match ($dataset) {
|
|
'payment-reconciliation' => ['record_type', 'supplier', 'warehouse', 'stock_no', 'purchase_no', 'product_code', 'express_no', 'send_time_from', 'send_time_to', 'create_time_from', 'create_time_to', 'sent_status', 'has_diff'],
|
|
'return-reconciliation', 'return-unpriced' => ['supplier', 'warehouse', 'sku_id', 'express_no', 'date_from', 'date_to'],
|
|
'invoice-deductions', 'compensation-details', 'advance-details', 'other-adjustments' => ['supplier', 'month_from', 'month_to'],
|
|
'sample-deductions' => ['supplier', 'department', 'creator', 'sku_id', 'date_from', 'date_to'],
|
|
'delivery-unpriced' => ['supplier', 'warehouse', 'purchase', 'barcode', 'express_no', 'date_from', 'date_to'],
|
|
'dropshipping-express' => ['supplier', 'express_name', 'month_from', 'month_to'],
|
|
'board-deductions' => ['stock_in_date', 'stock_out_date', 'furniture_factory', 'lamination_factory', 'deduction_type', 'material_name', 'claim_number'],
|
|
'supplier-payables-report' => ['month', 'supplier', 'checker', 'settlement_method'],
|
|
};
|
|
|
|
return collect($request->only($allowed))
|
|
->map(static fn (mixed $value): mixed => is_string($value) ? trim($value) : $value)
|
|
->reject(static fn (mixed $value): bool => $value === '' || $value === null || $value === false)
|
|
->all();
|
|
}
|
|
|
|
private function dataset(Request $request): string
|
|
{
|
|
$dataset = (string) $request->route('dataset');
|
|
$this->registry->fields($dataset);
|
|
return $dataset;
|
|
}
|
|
|
|
private function title(string $dataset): string
|
|
{
|
|
return [
|
|
'payment-reconciliation' => '货款对账', 'return-reconciliation' => '退货对账',
|
|
'invoice-deductions' => '发票扣款', 'compensation-details' => '赔偿明细',
|
|
'advance-details' => '借支明细', 'sample-deductions' => '样品扣款',
|
|
'delivery-unpriced' => '送货未定价', 'return-unpriced' => '退货未定价',
|
|
'other-adjustments' => '其他扣款补款', 'dropshipping-express' => '代发快递',
|
|
'board-deductions' => '板材扣款', 'supplier-payables-report' => '供应商应付表',
|
|
][$dataset];
|
|
}
|
|
|
|
private function csvText(mixed $value): string
|
|
{
|
|
$value = (string) $value;
|
|
return preg_match('/^[=+\-@\t\r]/u', $value) === 1 ? "\t" . $value : $value;
|
|
}
|
|
}
|