CaiWuXiTong/modules/EcommerceFinance/Services/AccessoryReshipRateCalculator.php

97 lines
4.6 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\Services;
use Illuminate\Validation\ValidationException;
class AccessoryReshipRateCalculator
{
/** 界面及文本导入均以百分数为单位;数据库保存万分比整数。 */
public static function basisPoints(string $percent): int
{
$percent = trim(str_replace(['%', '%'], '', $percent));
if (!preg_match('/^\d{1,3}(?:\.\d{1,2})?$/', $percent)) {
throw ValidationException::withMessages(['rate_percent' => '扣款比例须为0至100的数字,最多两位小数。']);
}
[$whole, $decimal] = array_pad(explode('.', $percent, 2), 2, '');
$points = (int) $whole * 100 + (int) str_pad($decimal, 2, '0');
if ($points > 10000) {
throw ValidationException::withMessages(['rate_percent' => '扣款比例不能超过100%。']);
}
return $points;
}
public static function amount(int $freightCents, int $basisPoints): int
{
return intdiv($freightCents * $basisPoints + 5000, 10000);
}
/** 名称精确匹配;不默认比例,不对多候选累计运费。 */
public function apply(array $row, array $configs, ?string $selectedCourier = null): array
{
// 同名快递只保留一个首发包裹;不同名称必须由业务选择,不能按比例猜归属。
$candidates = [];
foreach ($row['matches'] as $match) {
$name = trim((string) $match['courier']);
$candidates[$name] ??= array_replace($match, ['courier' => $name]);
}
$row['courier_candidates'] = array_values($candidates);
$selected = $selectedCourier !== null ? ($candidates[$selectedCourier] ?? null) : null;
if (count($candidates) === 1) $selected = reset($candidates);
$row['selected_courier'] = $selected['courier'] ?? null;
$row['matches'] = $selected === null ? array_values($candidates) : [$selected];
$row['deduction_cents'] = $row['status'] === 'unmatched' ? 0 : null;
$row['deduction_rate_basis_points'] = null;
$row['deduction_state'] = 'unmatched';
$row['deduction_note'] = '三个月内未匹配首发快递单号';
$reshipCourier = trim((string) (($row['express_name1'] ?? '') ?: ($row['express_name2'] ?? '')));
if (in_array($reshipCourier, ['外采快递', '线下快递'], true)) {
$row['deduction_cents'] = 0;
$row['deduction_state'] = 'excluded_courier';
$row['deduction_note'] = '补发快递为' . $reshipCourier . ',不参与扣款';
return $row;
}
if ($row['status'] === 'unmatched') {
return $row;
}
if ($selected === null) {
$row['deduction_state'] = 'selection_required';
$row['deduction_note'] = '请选择需要扣款的首发快递,补发运费仅计一次';
return $row;
}
$rates = [];
$missing = false;
foreach ($row['matches'] as &$match) {
$config = $configs[trim((string) $match['courier'])] ?? null;
$match['rate_basis_points'] = $config['rate_basis_points'] ?? null;
$match['courier_code'] = $config['courier_code'] ?? null;
$match['deduction_cents'] = $config !== null && $row['freight_cents'] !== null
? self::amount($row['freight_cents'], (int) $config['rate_basis_points']) : null;
if ($config === null) {
$missing = true;
} else {
$rates[] = (int) $config['rate_basis_points'];
}
}
unset($match);
if ($missing) {
$row['deduction_state'] = 'missing_config';
$row['deduction_note'] = '首发快递未配置扣款比例,暂不计入扣款合计';
} elseif ($row['freight_cents'] === null) {
$row['deduction_state'] = 'invalid_amount';
$row['deduction_note'] = '补发运费缺失或异常,待核实';
} else {
$rate = $rates[0];
$row['deduction_rate_basis_points'] = $rate;
$row['deduction_cents'] = self::amount($row['freight_cents'], $rate);
$row['deduction_state'] = $rate === 0 ? 'zero_rate' : 'ready';
$row['deduction_note'] = $rate === 0 ? '扣款比例0%,不扣款' : '按所选首发快递配置比例核算,仅计一次';
if ($rate > 0 && $row['deduction_cents'] === 0) {
$row['deduction_state'] = 'zero_amount';
$row['deduction_note'] = '扣款金额为0,不扣款';
}
}
return $row;
}
}