feat: 重做安装费金额汇总卡 #603
@ -1116,6 +1116,10 @@ class Pages extends Controller
|
||||
return response()->json([
|
||||
'total' => $payload['total'],
|
||||
'rows' => $payload['rows'],
|
||||
'previous_total' => $payload['previous_total'] ?? null,
|
||||
'previous_from' => $payload['previous_from'] ?? null,
|
||||
'previous_to' => $payload['previous_to'] ?? null,
|
||||
'change_rate' => $payload['change_rate'] ?? null,
|
||||
], 200, ['Cache-Control' => 'private, no-store']);
|
||||
}
|
||||
|
||||
|
||||
@ -136,29 +136,32 @@ class InstallationDetailMirrorRepository
|
||||
$items = $this->datasets->get(self::ITEM_DATASET);
|
||||
$connection = $this->mirror->connection();
|
||||
if (! $this->ready($dataset, $items, $connection->getName())) {
|
||||
return ['ready' => false, 'total' => 0.0, 'rows' => []];
|
||||
return [
|
||||
'ready' => false,
|
||||
'total' => 0.0,
|
||||
'rows' => [],
|
||||
'previous_total' => null,
|
||||
'previous_from' => null,
|
||||
'previous_to' => null,
|
||||
'change_rate' => null,
|
||||
];
|
||||
}
|
||||
|
||||
$from = trim((string) ($filters['customer_review_from'] ?? ''));
|
||||
$to = trim((string) ($filters['customer_review_to'] ?? ''));
|
||||
if ($from === '' || $to === '') {
|
||||
return ['ready' => true, 'total' => 0.0, 'rows' => []];
|
||||
return [
|
||||
'ready' => true,
|
||||
'total' => 0.0,
|
||||
'rows' => [],
|
||||
'previous_total' => null,
|
||||
'previous_from' => null,
|
||||
'previous_to' => null,
|
||||
'change_rate' => null,
|
||||
];
|
||||
}
|
||||
|
||||
$latest = $connection->table($dataset->targetTable())
|
||||
->selectRaw('MAX(Id) AS id')
|
||||
->groupBy('SourceGuid');
|
||||
$main = $connection->table($dataset->targetTable() . ' AS v')
|
||||
->joinSub($latest, 'latest_install', static fn ($join) => $join->on('latest_install.id', '=', 'v.Id'))
|
||||
->select(['v.SourceGuid', 'v.shop_name', 'v.installCompanyName', 'v.costWay']);
|
||||
$this->applyShipmentCutoff($main);
|
||||
|
||||
$query = $connection->table($items->targetTable() . ' AS i')
|
||||
->joinSub($main, 'install_main', static fn ($join) =>
|
||||
$join->on('install_main.SourceGuid', '=', 'i.guid'))
|
||||
->whereNotNull('i.kfshTime')
|
||||
->where('i.kfshTime', '>=', $from)
|
||||
->where('i.kfshTime', '<', \Illuminate\Support\Carbon::parse($to)->addDay()->toDateString());
|
||||
$query = $this->feeDetailsQuery($connection, $dataset, $items, $from, $to);
|
||||
|
||||
$platformSql = "CASE"
|
||||
. " WHEN COALESCE(TRIM(install_main.shop_name), '') LIKE '%阿里巴巴%'"
|
||||
@ -202,10 +205,80 @@ class InstallationDetailMirrorRepository
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$total = round(array_sum(array_column($rows, 'amount')), 2);
|
||||
$previousRange = $this->previousMonthRange($from, $to);
|
||||
$previousTotal = null;
|
||||
$changeRate = null;
|
||||
if ($previousRange !== null) {
|
||||
$previousQuery = $this->feeDetailsQuery(
|
||||
$connection,
|
||||
$dataset,
|
||||
$items,
|
||||
$previousRange['from'],
|
||||
$previousRange['to'],
|
||||
);
|
||||
$previousTotal = round((float) ($previousQuery
|
||||
->selectRaw('COALESCE(SUM(CAST(i.settleItemAmount AS DECIMAL(18,2))), 0) AS total')
|
||||
->first()->total ?? 0), 2);
|
||||
if ($previousTotal > 0) {
|
||||
$changeRate = round((($total - $previousTotal) / $previousTotal) * 100, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'ready' => true,
|
||||
'total' => round(array_sum(array_column($rows, 'amount')), 2),
|
||||
'total' => $total,
|
||||
'rows' => $rows,
|
||||
'previous_total' => $previousTotal,
|
||||
'previous_from' => $previousRange['from'] ?? null,
|
||||
'previous_to' => $previousRange['to'] ?? null,
|
||||
'change_rate' => $changeRate,
|
||||
];
|
||||
}
|
||||
|
||||
private function feeDetailsQuery(
|
||||
$connection,
|
||||
$dataset,
|
||||
$items,
|
||||
string $from,
|
||||
string $to,
|
||||
) {
|
||||
$latest = $connection->table($dataset->targetTable())
|
||||
->selectRaw('MAX(Id) AS id')
|
||||
->groupBy('SourceGuid');
|
||||
$main = $connection->table($dataset->targetTable() . ' AS v')
|
||||
->joinSub($latest, 'latest_install', static fn ($join) => $join->on('latest_install.id', '=', 'v.Id'))
|
||||
->select(['v.SourceGuid', 'v.shop_name', 'v.installCompanyName', 'v.costWay']);
|
||||
$this->applyShipmentCutoff($main);
|
||||
|
||||
return $connection->table($items->targetTable() . ' AS i')
|
||||
->joinSub($main, 'install_main', static fn ($join) =>
|
||||
$join->on('install_main.SourceGuid', '=', 'i.guid'))
|
||||
->whereNotNull('i.kfshTime')
|
||||
->where('i.kfshTime', '>=', $from)
|
||||
->where('i.kfshTime', '<', \Illuminate\Support\Carbon::parse($to)->addDay()->toDateString());
|
||||
}
|
||||
|
||||
/** @return array{from:string,to:string}|null */
|
||||
private function previousMonthRange(string $from, string $to): ?array
|
||||
{
|
||||
$start = \Illuminate\Support\Carbon::parse($from)->startOfDay();
|
||||
$end = \Illuminate\Support\Carbon::parse($to)->startOfDay();
|
||||
if ($end->lt($start)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($start->day === 1 && $end->isSameDay($end->copy()->endOfMonth())) {
|
||||
$previousStart = $start->copy()->subMonthNoOverflow()->startOfMonth();
|
||||
$previousEnd = $previousStart->copy()->endOfMonth();
|
||||
} else {
|
||||
$previousStart = $start->copy()->subMonthNoOverflow();
|
||||
$previousEnd = $end->copy()->subMonthNoOverflow();
|
||||
}
|
||||
|
||||
return [
|
||||
'from' => $previousStart->toDateString(),
|
||||
'to' => $previousEnd->toDateString(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -1,47 +1,21 @@
|
||||
<style>
|
||||
.installation-fee-board {
|
||||
margin: 1rem 1.5rem;
|
||||
padding: 1rem;
|
||||
border: 1px solid #d8d2c7;
|
||||
border-radius: 0.75rem;
|
||||
background: #fcfbf8;
|
||||
}
|
||||
|
||||
.installation-fee-board__head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.installation-fee-board__head strong {
|
||||
display: block;
|
||||
color: #171717;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.installation-fee-board__head span {
|
||||
display: block;
|
||||
margin-top: 0.2rem;
|
||||
color: #595959;
|
||||
font-size: 0.6875rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.installation-fee-card {
|
||||
display: flex;
|
||||
width: min(100%, 15rem);
|
||||
min-height: 6.5rem;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 0.45rem;
|
||||
padding: 0.8rem 0.9rem;
|
||||
position: relative;
|
||||
display: block;
|
||||
width: min(100%, 52rem);
|
||||
min-height: 9.25rem;
|
||||
padding: 0.95rem 1.15rem 0.8rem 1.25rem;
|
||||
border: 1px solid #d8d2c7;
|
||||
border-radius: 0.5rem;
|
||||
border-left: 3px solid #006395;
|
||||
border-radius: 0.75rem;
|
||||
background: #ffffff;
|
||||
color: #171717;
|
||||
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -49,6 +23,7 @@
|
||||
.installation-fee-card:hover,
|
||||
.installation-fee-card:focus-visible {
|
||||
border-color: #b9954f;
|
||||
border-left-color: #006395;
|
||||
background: #fffdf8;
|
||||
outline: none;
|
||||
}
|
||||
@ -57,32 +32,125 @@
|
||||
box-shadow: 0 0 0 3px rgba(133, 102, 50, 0.2);
|
||||
}
|
||||
|
||||
.installation-fee-card__label {
|
||||
color: #595959;
|
||||
.installation-fee-card__top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.installation-fee-card__title {
|
||||
flex: 0 0 auto;
|
||||
color: #171717;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.installation-fee-status {
|
||||
max-width: 31rem;
|
||||
color: #595959;
|
||||
font-size: 0.6875rem;
|
||||
line-height: 1.45;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.installation-fee-status.is-error {
|
||||
color: #b80000;
|
||||
}
|
||||
|
||||
.installation-fee-card__main {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
margin: 0.35rem 0 0.58rem;
|
||||
}
|
||||
|
||||
.installation-fee-card__value {
|
||||
color: #171717;
|
||||
font-size: 1.25rem;
|
||||
font-size: 2.625rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.035em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.installation-fee-card__value small {
|
||||
margin-left: 0.15rem;
|
||||
.installation-fee-card__unit {
|
||||
color: #595959;
|
||||
font-size: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.installation-fee-card__action {
|
||||
color: #856632;
|
||||
font-size: 0.6875rem;
|
||||
.installation-fee-card__trend {
|
||||
margin-left: 0.4rem;
|
||||
color: #595959;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.installation-fee-status {
|
||||
.installation-fee-card__trend.is-up {
|
||||
color: #935f07;
|
||||
}
|
||||
|
||||
.installation-fee-card__trend.is-down {
|
||||
color: #53793e;
|
||||
}
|
||||
|
||||
.installation-fee-card__trend-note {
|
||||
color: #595959;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.installation-fee-card__split {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
flex-wrap: wrap;
|
||||
padding-top: 0.55rem;
|
||||
border-top: 1px solid #e5e0d7;
|
||||
color: #171717;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.45;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.installation-fee-card__split strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.installation-fee-card__split-percent {
|
||||
margin-left: 0.1rem;
|
||||
color: #595959;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.installation-fee-card__split-sep {
|
||||
color: #d8d2c7;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.installation-fee-card {
|
||||
min-height: 0;
|
||||
padding: 0.9rem 0.95rem 0.8rem 1rem;
|
||||
}
|
||||
.installation-fee-card__top {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.installation-fee-status {
|
||||
max-width: none;
|
||||
text-align: left;
|
||||
}
|
||||
.installation-fee-card__main {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.installation-fee-card__value {
|
||||
font-size: 2.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.installation-fee-dialog {
|
||||
@ -283,23 +351,29 @@
|
||||
</style>
|
||||
|
||||
<section class="installation-fee-board" aria-label="安装费金额汇总">
|
||||
<div class="installation-fee-board__head">
|
||||
<div>
|
||||
<strong>安装费金额</strong>
|
||||
<span>按客服审核时间汇总安装明细的结算明细金额;收款渠道为空归为包安装,有值归为不包安装。</span>
|
||||
</div>
|
||||
<span class="installation-fee-status" data-installation-fee-status>请选择完整的客服审核起止日期</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="installation-fee-card"
|
||||
data-installation-fee-open
|
||||
data-url="{{ route('business.installation-details.summary') }}"
|
||||
aria-label="查看安装费金额明细"
|
||||
title="查看安装费金额明细"
|
||||
>
|
||||
<span class="installation-fee-card__label">安装费金额</span>
|
||||
<strong class="installation-fee-card__value"><span data-installation-fee-total>0.00</span><small>万</small></strong>
|
||||
<span class="installation-fee-card__action">查看明细 →</span>
|
||||
<span class="installation-fee-card__top">
|
||||
<strong class="installation-fee-card__title">安装费金额</strong>
|
||||
<span class="installation-fee-status" data-installation-fee-status aria-live="polite">请选择完整的客服审核起止日期</span>
|
||||
</span>
|
||||
<span class="installation-fee-card__main">
|
||||
<strong class="installation-fee-card__value" data-installation-fee-total>0.00</strong>
|
||||
<span class="installation-fee-card__unit">万元</span>
|
||||
<span class="installation-fee-card__trend" data-installation-fee-trend>—</span>
|
||||
<span class="installation-fee-card__trend-note">较上月</span>
|
||||
</span>
|
||||
<span class="installation-fee-card__split">
|
||||
<span>包安装 <strong data-installation-fee-package>0.00</strong><span class="installation-fee-card__split-percent" data-installation-fee-package-percent>(0%)</span></span>
|
||||
<span class="installation-fee-card__split-sep" aria-hidden="true">|</span>
|
||||
<span>不包安装 <strong data-installation-fee-non-package>0.00</strong><span class="installation-fee-card__split-percent" data-installation-fee-non-package-percent>(0%)</span></span>
|
||||
</span>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@ -384,7 +458,12 @@
|
||||
var fromInput = form.elements.namedItem('date_from');
|
||||
var toInput = form.elements.namedItem('date_to');
|
||||
var cardTotal = card.querySelector('[data-installation-fee-total]');
|
||||
var status = document.querySelector('[data-installation-fee-status]');
|
||||
var status = card.querySelector('[data-installation-fee-status]');
|
||||
var trend = card.querySelector('[data-installation-fee-trend]');
|
||||
var packageAmount = card.querySelector('[data-installation-fee-package]');
|
||||
var packagePercent = card.querySelector('[data-installation-fee-package-percent]');
|
||||
var nonPackageAmount = card.querySelector('[data-installation-fee-non-package]');
|
||||
var nonPackagePercent = card.querySelector('[data-installation-fee-non-package-percent]');
|
||||
var subtitle = dialog.querySelector('[data-installation-fee-subtitle]');
|
||||
var currentTotal = dialog.querySelector('[data-installation-fee-current-total]');
|
||||
var platformFilter = dialog.querySelector('[data-installation-fee-platform]');
|
||||
@ -414,6 +493,78 @@
|
||||
});
|
||||
}
|
||||
|
||||
function amount(value) {
|
||||
return Number(value || 0).toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
}
|
||||
|
||||
function captionRange(value) {
|
||||
var matchFrom = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.from);
|
||||
var matchTo = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.to);
|
||||
if (!matchFrom || !matchTo) {
|
||||
return value.from + ' 至 ' + value.to;
|
||||
}
|
||||
var end = matchFrom[1] === matchTo[1]
|
||||
? matchTo[2] + '-' + matchTo[3]
|
||||
: value.to;
|
||||
return value.from + ' 至 ' + end;
|
||||
}
|
||||
|
||||
function setTrend(changeRate) {
|
||||
trend.className = 'installation-fee-card__trend';
|
||||
if (changeRate === null || changeRate === undefined || !Number.isFinite(Number(changeRate))) {
|
||||
trend.textContent = '—';
|
||||
return;
|
||||
}
|
||||
|
||||
var rate = Number(changeRate);
|
||||
if (rate > 0) {
|
||||
trend.textContent = '↑' + Math.abs(rate).toLocaleString('zh-CN', { maximumFractionDigits: 1 }) + '%';
|
||||
trend.classList.add('is-up');
|
||||
} else if (rate < 0) {
|
||||
trend.textContent = '↓' + Math.abs(rate).toLocaleString('zh-CN', { maximumFractionDigits: 1 }) + '%';
|
||||
trend.classList.add('is-down');
|
||||
} else {
|
||||
trend.textContent = '持平';
|
||||
}
|
||||
}
|
||||
|
||||
function setCardValue(total, rows, value, data) {
|
||||
var packageTotal = 0;
|
||||
var nonPackageTotal = 0;
|
||||
rows.forEach(function (row) {
|
||||
if (row.payment_channel === '包安装') {
|
||||
packageTotal += Number(row.amount || 0);
|
||||
} else {
|
||||
nonPackageTotal += Number(row.amount || 0);
|
||||
}
|
||||
});
|
||||
var packageRate = total > 0 ? Math.round((packageTotal / total) * 100) : 0;
|
||||
var nonPackageRate = total > 0 ? 100 - packageRate : 0;
|
||||
|
||||
cardTotal.textContent = wan(total);
|
||||
packageAmount.textContent = amount(packageTotal);
|
||||
nonPackageAmount.textContent = amount(nonPackageTotal);
|
||||
packagePercent.textContent = '(' + packageRate + '%)';
|
||||
nonPackagePercent.textContent = '(' + nonPackageRate + '%)';
|
||||
setTrend(data.change_rate);
|
||||
status.classList.remove('is-error');
|
||||
status.textContent = '按客服审核时间 · ' + captionRange(value) + ' · ' + rows.length + ' 单';
|
||||
}
|
||||
|
||||
function resetCard(message) {
|
||||
cardTotal.textContent = '0.00';
|
||||
packageAmount.textContent = '0.00';
|
||||
nonPackageAmount.textContent = '0.00';
|
||||
packagePercent.textContent = '(0%)';
|
||||
nonPackagePercent.textContent = '(0%)';
|
||||
setTrend(null);
|
||||
status.textContent = message;
|
||||
status.classList.toggle('is-error', message.indexOf('失败') >= 0 || message.indexOf('不能') >= 0);
|
||||
}
|
||||
|
||||
function range() {
|
||||
return {
|
||||
from: String(fromInput?.value || ''),
|
||||
@ -542,14 +693,12 @@
|
||||
function refresh(force) {
|
||||
var value = range();
|
||||
if (!value.from || !value.to) {
|
||||
cardTotal.textContent = '0.00';
|
||||
status.textContent = '请选择完整的客服审核起止日期';
|
||||
resetCard('请选择完整的客服审核起止日期');
|
||||
resetDialog('请选择完整的客服审核起止日期');
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
if (value.from > value.to) {
|
||||
cardTotal.textContent = '0.00';
|
||||
status.textContent = '客服审核结束日期不能早于开始日期';
|
||||
resetCard('客服审核结束日期不能早于开始日期');
|
||||
resetDialog('客服审核结束日期不能早于开始日期');
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
@ -561,7 +710,8 @@
|
||||
if (controller) { controller.abort(); }
|
||||
controller = new AbortController();
|
||||
dialog.setAttribute('aria-busy', 'true');
|
||||
status.textContent = '正在汇总安装费…';
|
||||
status.classList.remove('is-error');
|
||||
status.textContent = '按客服审核时间 · 正在汇总…';
|
||||
cardTotal.textContent = '…';
|
||||
var url = new URL(card.dataset.url, window.location.origin);
|
||||
url.searchParams.set('customer_review_from', value.from);
|
||||
@ -581,8 +731,7 @@
|
||||
}).then(function (data) {
|
||||
allRows = Array.isArray(data.rows) ? data.rows : [];
|
||||
loadedSignature = currentSignature;
|
||||
cardTotal.textContent = wan(data.total);
|
||||
status.textContent = value.from + ' 至 ' + value.to + ' · ' + allRows.length + ' 条汇总';
|
||||
setCardValue(Number(data.total || 0), allRows, value, data);
|
||||
subtitle.textContent = value.from + ' 至 ' + value.to + ' · 按客服审核时间汇总';
|
||||
fillFilter(platformFilter, '全部平台', distinct('platform'));
|
||||
fillFilter(storeFilter, '全部店铺', distinct('store_name'));
|
||||
@ -593,8 +742,7 @@
|
||||
return true;
|
||||
}).catch(function (error) {
|
||||
if (error && error.name === 'AbortError') { return false; }
|
||||
cardTotal.textContent = '0.00';
|
||||
status.textContent = error.message || '安装费汇总读取失败';
|
||||
resetCard(error.message || '安装费汇总读取失败');
|
||||
resetDialog(error.message || '安装费汇总读取失败,请稍后重试。');
|
||||
dialog.removeAttribute('aria-busy');
|
||||
return false;
|
||||
|
||||
@ -279,7 +279,16 @@ class ManagementCrudTest extends FeatureTestCase
|
||||
->assertSee('data-installation-fee-open', false)
|
||||
->assertSee('id="installation-fee-dialog"', false)
|
||||
->assertSeeText('安装费金额')
|
||||
->assertSeeText('按客服审核时间汇总安装明细的结算明细金额')
|
||||
->assertSeeText('较上月')
|
||||
->assertSeeText('包安装')
|
||||
->assertSeeText('不包安装')
|
||||
->assertSee('data-installation-fee-trend', false)
|
||||
->assertSee('data-installation-fee-package', false)
|
||||
->assertSee('data-installation-fee-non-package', false)
|
||||
->assertSee('border-left: 3px solid #006395', false)
|
||||
->assertSee('font-size: 2.625rem', false)
|
||||
->assertSee('function captionRange(value)', false)
|
||||
->assertSee('function setCardValue(total, rows, value, data)', false)
|
||||
->assertSeeText('平台')
|
||||
->assertSeeText('店铺名称')
|
||||
->assertSeeText('安装公司')
|
||||
@ -406,6 +415,10 @@ class ManagementCrudTest extends FeatureTestCase
|
||||
['platform' => '淘系', 'store_name' => '店铺A', 'installation_company' => '万师傅', 'payment_channel' => '包安装', 'amount' => 1250.50],
|
||||
['platform' => '京东', 'store_name' => '店铺B', 'installation_company' => '鲁班到家', 'payment_channel' => '不包安装', 'amount' => 750.00],
|
||||
],
|
||||
'previous_total' => 1600.40,
|
||||
'previous_from' => '2026-08-01',
|
||||
'previous_to' => '2026-08-31',
|
||||
'change_rate' => 25.0,
|
||||
]);
|
||||
$this->app->instance(InstallationDetailMirrorRepository::class, $mirror);
|
||||
|
||||
@ -419,7 +432,9 @@ class ManagementCrudTest extends FeatureTestCase
|
||||
->assertJsonPath('rows.0.platform', '淘系')
|
||||
->assertJsonPath('rows.0.store_name', '店铺A')
|
||||
->assertJsonPath('rows.0.payment_channel', '包安装')
|
||||
->assertJsonPath('rows.1.payment_channel', '不包安装');
|
||||
->assertJsonPath('rows.1.payment_channel', '不包安装')
|
||||
->assertJsonPath('previous_total', 1600.4)
|
||||
->assertJsonPath('change_rate', 25.0);
|
||||
}
|
||||
|
||||
public function testInstallationSummaryRequiresCompleteCustomerReviewRange(): void
|
||||
|
||||
@ -140,6 +140,7 @@ class InstallationDetailMirrorRepositoryTest extends TestCase
|
||||
DB::connection('business_mirror')->table('fixture_install')->where('Id',3)->update(['installCompanyName'=>'鲁班到家','costWay'=>'银行转账']);
|
||||
DB::connection('business_mirror')->table('fixture_install')->where('Id',4)->update(['installCompanyName'=>'万师傅']);
|
||||
DB::connection('business_mirror')->table('fixture_items')->insert([
|
||||
['Id'=>19,'guid'=>'A','shopproid'=>'P0','Sourceno'=>'A-0','kfshTime'=>'2026-08-15 10:00:00','settleItemAmount'=>90.00,'acceptItemAmount'=>80.00],
|
||||
['Id'=>20,'guid'=>'A','shopproid'=>'P1','Sourceno'=>'A-1','kfshTime'=>'2026-09-01 09:00:00','settleItemAmount'=>60.10,'acceptItemAmount'=>40.00],
|
||||
['Id'=>21,'guid'=>'A','shopproid'=>'P2','Sourceno'=>'A-1','kfshTime'=>'2026-09-30 18:00:00','settleItemAmount'=>39.90,'acceptItemAmount'=>0.00],
|
||||
['Id'=>22,'guid'=>'B','shopproid'=>'P3','Sourceno'=>'B-1','kfshTime'=>'2026-09-15 10:00:00','settleItemAmount'=>80.00,'acceptItemAmount'=>70.00],
|
||||
@ -159,6 +160,10 @@ class InstallationDetailMirrorRepositoryTest extends TestCase
|
||||
['platform'=>'其他','store_name'=>'其他店','installation_company'=>'鲁班到家','payment_channel'=>'不包安装','amount'=>80.0],
|
||||
],$result['rows']);
|
||||
$this->assertSame(180.0,$result['total']);
|
||||
$this->assertSame(90.0,$result['previous_total']);
|
||||
$this->assertSame('2026-08-01',$result['previous_from']);
|
||||
$this->assertSame('2026-08-31',$result['previous_to']);
|
||||
$this->assertSame(100.0,$result['change_rate']);
|
||||
}
|
||||
|
||||
public function test_fee_details_classifies_platform_by_store_name_rules(): void
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user