diff --git a/app/Http/Controllers/Business/Pages.php b/app/Http/Controllers/Business/Pages.php index 93303aeb..2571c78b 100644 --- a/app/Http/Controllers/Business/Pages.php +++ b/app/Http/Controllers/Business/Pages.php @@ -1543,6 +1543,29 @@ class Pages extends Controller return; } + if ($platform === 'jd') { + fputcsv($csv, [ + '平台', '店铺', '账户', '日期', '收支类型', '交易类型', + '金额(元)', '余额(元)', '备注', + ]); + foreach ($data['jd'] ?? [] as $row) { + fputcsv($csv, [ + '京东京准通', + $row['shop_name'] ?? '', + $row['account'] ?? '', + $row['show_date'] ?? $row['settle_datetime'] ?? '', + $row['in_out_type_name'] ?? '', + $row['trade_type_name'] ?? '', + $row['amount'] ?? '', + $row['balance_amount'] ?? '', + $row['remark'] ?? '', + ]); + } + fclose($csv); + + return; + } + fputcsv($csv, ['平台', '店铺/账户', '日期', '类型', '金额(元)', '余额(元)', '备注']); $emit = static function (string $platform, array $rows, array $map) use ($csv): void { foreach ($rows as $row) { @@ -1557,9 +1580,7 @@ class Pages extends Controller ]); } }; - if ($platform === 'jd') { - $emit('京东京准通', $data['jd'] ?? [], ['name' => fn ($row) => $row['shop_name'] . ' / ' . $row['account'], 'date' => fn ($row) => $row['show_date'] ?? $row['settle_datetime'], 'type' => fn ($row) => $row['trade_type_name'] ?? $row['in_out_type_name'], 'amount' => fn ($row) => $row['amount'] ?? '', 'balance' => fn ($row) => $row['balance_amount'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']); - } elseif ($platform === 'douyin') { + if ($platform === 'douyin') { $emit('抖音千川', $data['dy'] ?? [], ['name' => fn ($row) => $row['shopName'] . ' / ' . $row['account'] . ' / ' . $row['qianchuanAavid'], 'date' => fn ($row) => $row['statDate'] ?? '', 'type' => fn () => '花费', 'amount' => fn ($row) => $row['cost'] ?? '', 'balance' => fn ($row) => $row['balance'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']); } elseif ($platform === 'dewu') { $emit('得物推广', $data['dw'] ?? [], ['name' => fn ($row) => $row['shop_name'] . ' / ' . $row['account'], 'date' => fn ($row) => $row['trade_date'] ?? $row['date_time'], 'type' => fn ($row) => $row['flow_type'] ?? '', 'amount' => fn ($row) => $row['trade_amount_yuan'] ?? '', 'balance' => fn ($row) => $row['balance_yuan'] ?? '', 'remark' => fn ($row) => $row['remark'] ?? '']); diff --git a/resources/assets/js/components/business/AccountBills.vue b/resources/assets/js/components/business/AccountBills.vue index 48365b53..8b1481b6 100644 --- a/resources/assets/js/components/business/AccountBills.vue +++ b/resources/assets/js/components/business/AccountBills.vue @@ -18,7 +18,7 @@ - +
结束日期不能早于开始日期
@@ -71,6 +71,7 @@ export default { async fetch(){const requestId=++this.requestSerial;this.loading=true;this.loadError=false;try{const response=await window.axios.get(this.apiBase,{params:{...this.filters,sort:this.sort.key,dir:this.sort.dir,limit:this.perPage,page:this.currentPage}});if(requestId!==this.requestSerial)return;const body=response.data||{};this.rows=body.data||[];this.total=body.total||0;this.currentPage=body.current_page||1;this.lastPage=body.last_page||1;this.perPage=body.per_page||this.perPage;this.summary=body.summary||{income:0,expense:0,balance:0,count:0};if(body.columns){this.columnsTitle=body.columns.title||'';this.sourceGroups=body.columns.groups||[];this.sourceColumns=this.attachGroups(body.columns.columns||body.columns||[],this.sourceGroups);}}catch(error){if(requestId!==this.requestSerial)return;this.rows=[];this.total=0;this.summary={income:0,expense:0,balance:0,count:0};this.loadError=true;}finally{if(requestId===this.requestSerial)this.loading=false;}}, attachGroups(columns,groups){let index=0;return columns.map((column,columnIndex)=>{while(groups[index]&&columnIndex>=groups.slice(0,index+1).reduce((sum,group)=>sum+Number(group.span||0),0))index+=1;return{...column,key:column.field||`column-${columnIndex}`,group:groups[index]?groups[index].label:''};});}, applyFilters(){if(this.invalidDateRange)return;this.currentPage=1;this.fetch();},resetFilters(){this.filters={store_name:'',bill_item:'',start:'',end:'',keyword:''};this.applyFilters();},changePage(page){this.currentPage=page;this.fetch();this.$nextTick(()=>{if(this.$refs.scroll)this.$refs.scroll.scrollTop=0;});}, + openDatePicker(refName){const input=this.$refs[refName];if(!input)return;input.focus();if(typeof input.showPicker==='function'){try{input.showPicker();return;}catch(error){/* Fall through for embedded browsers without picker support. */}}input.click();}, maybeSort(column){if(!this.sortableField(column.field))return;this.sort.dir=this.sort.key===column.field&&this.sort.dir==='desc'?'asc':'desc';this.sort.key=column.field;this.applyFilters();},sortableField(field){return SORTABLE.indexOf(field)!==-1;},sortMark(field){return this.sort.key===field?(this.sort.dir==='asc'?'↑':'↓'):'↕';}, getValue(row,field){if(!field)return'';if(field.indexOf('payload.')===0){const payload=row.payload||{};const value=payload[field.slice(8)];return value===null||value===undefined?'':value;}const value=row[field];return value===null||value===undefined?'':value;},rawValue(row,column){return this.getValue(row,column.field);}, displayValue(row,column){const value=this.rawValue(row,column);if(value==='')return'—';if(column.kind==='money_in')return Number(value)>0?`+${this.money(value)}`:'—';if(column.kind==='money_out')return Number(value)>0?`-${this.money(value)}`:'—';if(['balance','money'].indexOf(column.kind)!==-1)return this.money(value);return value;},cellClass(column){return{'is-number':this.isNumber(column.kind),'is-id':column.kind==='num','is-income':column.kind==='money_in','is-expense':column.kind==='money_out'};},isNumber(kind){return['money','money_in','money_out','balance','num'].indexOf(kind)!==-1;},money(value){return`¥${Number(value||0).toLocaleString('zh-CN',{minimumFractionDigits:2,maximumFractionDigits:2})}`;}, @@ -81,8 +82,8 @@ export default { diff --git a/resources/views/banking/marketing_platform_promotion_bills/partials/embedded-table.blade.php b/resources/views/banking/marketing_platform_promotion_bills/partials/embedded-table.blade.php index e1552b9e..2fade9a7 100644 --- a/resources/views/banking/marketing_platform_promotion_bills/partials/embedded-table.blade.php +++ b/resources/views/banking/marketing_platform_promotion_bills/partials/embedded-table.blade.php @@ -11,7 +11,7 @@ $titles = ['jd' => '京东京准通', 'douyin' => '抖音千川', 'dewu' => '得物推广账户', 'vip' => '唯品会推广流水']; $filterLabel = $platform === 'vip' ? $titles[$platform] . '筛选' : $titles[$platform] . '推广账户筛选'; $columns = match ($platform) { - 'jd' => [['shop_name', '店铺'], ['account', '账户'], ['show_date', '日期'], ['trade_type_name', '交易类型'], ['amount', '金额'], ['balance_amount', '余额'], ['remark', '备注']], + 'jd' => [['shop_name', '店铺'], ['account', '账户'], ['show_date', '日期'], ['in_out_type_name', '收支类型'], ['trade_type_name', '交易类型'], ['amount', '金额'], ['balance_amount', '余额'], ['remark', '备注']], 'douyin' => [['shopName', '店铺'], ['account', '账户'], ['qianchuanAavid', '广告账户'], ['statDate', '日期'], ['cost', '花费'], ['income', '收入'], ['transferIn', '转入'], ['transferOut', '转出'], ['balance', '余额'], ['status', '状态'], ['remark', '备注']], 'dewu' => [['shop_name', '店铺'], ['account', '账户'], ['trade_date', '日期'], ['amount_type', '金额类型'], ['flow_type', '收支类型'], ['trade_amount_yuan', '金额'], ['balance_yuan', '余额'], ['remark', '备注']], default => [ diff --git a/tests/Feature/Business/MarketingPlatformPromotionBillsTest.php b/tests/Feature/Business/MarketingPlatformPromotionBillsTest.php index 73f88d68..96a483cc 100644 --- a/tests/Feature/Business/MarketingPlatformPromotionBillsTest.php +++ b/tests/Feature/Business/MarketingPlatformPromotionBillsTest.php @@ -32,6 +32,8 @@ class MarketingPlatformPromotionBillsTest extends FeatureTestCase ->assertSeeText('京东京准通') ->assertSee('aria-label="京东京准通推广账户筛选"', false) ->assertSeeText('测试店铺') + ->assertSeeText('收支类型') + ->assertSeeText('出账') ->assertSeeText('导出 CSV') ->assertSee('class="my-6 business-page--marketing-account-bills"', false) ->assertSee(route('business.pages.show', [ @@ -72,7 +74,12 @@ class MarketingPlatformPromotionBillsTest extends FeatureTestCase "filename*=utf-8''" . rawurlencode($filename), (string) $response->headers->get('Content-Disposition'), ); - $this->assertStringContainsString('测试店铺', $response->streamedContent()); + $csv = $response->streamedContent(); + $this->assertStringContainsString('测试店铺', $csv); + $this->assertStringContainsString('收支类型', $csv); + $this->assertStringContainsString('交易类型', $csv); + $this->assertStringContainsString('出账', $csv); + $this->assertStringContainsString('推广费', $csv); } public function test_vip_tab_uses_marketing_daily_data(): void @@ -195,7 +202,8 @@ class MarketingPlatformPromotionBillsTest extends FeatureTestCase 'shop_name' => '测试店铺', 'account' => 'account-01', 'show_date' => '2026-08-01', - 'trade_type_name' => '充值', + 'in_out_type_name' => '出账', + 'trade_type_name' => '推广费', 'amount' => 100, 'balance_amount' => 100, 'remark' => '测试记录', diff --git a/tests/Feature/Business/PagesTest.php b/tests/Feature/Business/PagesTest.php index 4c675e55..18d9cefd 100644 --- a/tests/Feature/Business/PagesTest.php +++ b/tests/Feature/Business/PagesTest.php @@ -217,14 +217,17 @@ class PagesTest extends FeatureTestCase ]); } - public function testDouyinAccountBillsExportUsesSelectedColumnOrder(): void + public function testDouyinAccountBillsExportUsesDateFiltersAndSelectedColumnOrder(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); $repository = Mockery::mock(StoreReceivingDouyinBillMirrorRepository::class); - $repository->shouldReceive('exportRows')->once()->with([], 'occurred_at', 'desc')->andReturn([[ + $repository->shouldReceive('exportRows')->once()->with([ + 'start' => '2026-09-09', + 'end' => '2026-09-09', + ], 'occurred_at', 'desc')->andReturn([[ 'record_code' => '9001', 'payload' => [ 'move_scene' => '头条放心购', @@ -235,6 +238,8 @@ class PagesTest extends FeatureTestCase $response = $this->loginAs()->get(route('ecommerce-finance.account-bills.export', [ 'bill_type' => 'douyin', + 'start' => '2026-09-09', + 'end' => '2026-09-09', 'columns' => 'payload.move_scene,record_code,payload.settle_account', ])); diff --git a/tests/Unit/EcommerceFinance/PlatformPromotionAccountRepositoryTest.php b/tests/Unit/EcommerceFinance/PlatformPromotionAccountRepositoryTest.php index 095458b1..39f96dce 100644 --- a/tests/Unit/EcommerceFinance/PlatformPromotionAccountRepositoryTest.php +++ b/tests/Unit/EcommerceFinance/PlatformPromotionAccountRepositoryTest.php @@ -21,7 +21,8 @@ class PlatformPromotionAccountRepositoryTest extends TestCase $factory->shouldReceive('forSource')->once()->with('js_data_middle_platform')->andReturn($query); $query->shouldReceive('select')->once()->ordered()->with( Mockery::on(static fn (string $sql): bool => str_contains($sql, 'SELECT TOP 5000') - && str_contains($sql, 'jd_jzt_finance_records')), + && str_contains($sql, 'jd_jzt_finance_records') + && str_contains($sql, 'in_out_type_name')), ['2026-08'], )->andReturn([['shop_name' => '测试店铺', 'amount' => 100]]); $query->shouldReceive('select')->once()->ordered()->with(