Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2529376
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
39 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/tests/Browser/WalletTest.php b/src/tests/Browser/WalletTest.php
index febfbe0a..b87e9432 100644
--- a/src/tests/Browser/WalletTest.php
+++ b/src/tests/Browser/WalletTest.php
@@ -1,256 +1,260 @@
<?php
namespace Tests\Browser;
use App\Payment;
use App\Providers\PaymentProvider;
use App\Transaction;
use App\Wallet;
use Carbon\Carbon;
use Tests\Browser;
use Tests\Browser\Pages\Dashboard;
use Tests\Browser\Pages\Home;
use Tests\Browser\Pages\Wallet as WalletPage;
use Tests\TestCaseDusk;
class WalletTest extends TestCaseDusk
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('wallets-controller@kolabnow.com');
$john = $this->getTestUser('john@kolab.org');
Wallet::where('user_id', $john->id)->update(['balance' => -1234]);
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('wallets-controller@kolabnow.com');
$john = $this->getTestUser('john@kolab.org');
Wallet::where('user_id', $john->id)->update(['balance' => 0]);
parent::tearDown();
}
/**
* Test wallet page (unauthenticated)
*/
public function testWalletUnauth(): void
{
// Test that the page requires authentication
$this->browse(function (Browser $browser) {
$browser->visit('/wallet')->on(new Home());
});
}
/**
* Test wallet "box" on Dashboard
*/
public function testDashboard(): void
{
// Test that the page requires authentication
$this->browse(function (Browser $browser) {
$browser->visit(new Home())
->submitLogon('john@kolab.org', 'simple123', true)
->on(new Dashboard())
->assertSeeIn('@links .link-wallet .name', 'Wallet')
->assertSeeIn('@links .link-wallet .badge', '-12,34 CHF');
});
}
/**
* Test wallet page
*
* @depends testDashboard
*/
public function testWallet(): void
{
$this->browse(function (Browser $browser) {
$browser->click('@links .link-wallet')
->on(new WalletPage())
->assertSeeIn('#wallet .card-title', 'Account balance -12,34 CHF')
->assertSeeIn('#wallet .card-title .text-danger', '-12,34 CHF')
->assertSeeIn('#wallet .card-text', 'You are out of credit');
});
}
/**
* Test Receipts tab
*/
public function testReceipts(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com', ['password' => 'simple123']);
$wallet = $user->wallets()->first();
$wallet->payments()->delete();
// Log out John and log in the test user
$this->browse(function (Browser $browser) {
$browser->visit('/logout')
->waitForLocation('/login')
->on(new Home())
->submitLogon('wallets-controller@kolabnow.com', 'simple123', true);
});
// Assert Receipts tab content when there's no receipts available
$this->browse(function (Browser $browser) {
$browser->on(new Dashboard())
->click('@links .link-wallet')
->on(new WalletPage())
->assertSeeIn('#wallet .card-title', 'Account balance 0,00 CHF')
->assertSeeIn('#wallet .card-title .text-success', '0,00 CHF')
->assertSeeIn('#wallet .card-text', 'You are in your free trial period.')
->assertSeeIn('@nav #tab-receipts', 'Receipts')
->with('@receipts-tab', function (Browser $browser) {
$browser->waitUntilMissing('.app-loader')
->assertSeeIn('p', 'There are no receipts for payments')
->assertDontSeeIn('p', 'Here you can download')
->assertMissing('select')
->assertMissing('button');
});
});
// Create some sample payments
$receipts = [];
$date = Carbon::create(intval(date('Y')) - 1, 3, 30);
$payment = Payment::create([
'id' => 'AAA1',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Paid in March',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 1111,
+ 'currency_amount' => 1111,
+ 'currency' => 'CHF',
]);
$payment->updated_at = $date;
$payment->save();
$receipts[] = $date->format('Y-m');
$date = Carbon::create(intval(date('Y')) - 1, 4, 30);
$payment = Payment::create([
'id' => 'AAA2',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Paid in April',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 1111,
+ 'currency_amount' => 1111,
+ 'currency' => 'CHF',
]);
$payment->updated_at = $date;
$payment->save();
$receipts[] = $date->format('Y-m');
// Assert Receipts tab with receipts available
$this->browse(function (Browser $browser) use ($receipts) {
$browser->refresh()
->on(new WalletPage())
->assertSeeIn('@nav #tab-receipts', 'Receipts')
->with('@receipts-tab', function (Browser $browser) use ($receipts) {
$browser->waitUntilMissing('.app-loader')
->assertDontSeeIn('p', 'There are no receipts for payments')
->assertSeeIn('p', 'Here you can download')
->assertSeeIn('button', 'Download')
->assertElementsCount('select > option', 2)
->assertSeeIn('select > option:nth-child(1)', $receipts[1])
->assertSeeIn('select > option:nth-child(2)', $receipts[0]);
// Download a receipt file
$browser->select('select', $receipts[0])
->click('button')
->pause(2000);
$files = glob(__DIR__ . '/downloads/*.pdf');
$filename = pathinfo($files[0], PATHINFO_BASENAME);
$this->assertTrue(strpos($filename, $receipts[0]) !== false);
$content = $browser->readDownloadedFile($filename, 0);
$this->assertStringStartsWith("%PDF-1.", $content);
$browser->removeDownloadedFile($filename);
});
});
}
/**
* Test History tab
*/
public function testHistory(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com', ['password' => 'simple123']);
// Log out John and log in the test user
$this->browse(function (Browser $browser) {
$browser->visit('/logout')
->waitForLocation('/login')
->on(new Home())
->submitLogon('wallets-controller@kolabnow.com', 'simple123', true);
});
$package_kolab = \App\Package::where('title', 'kolab')->first();
$user->assignPackage($package_kolab);
$wallet = $user->wallets()->first();
// Create some sample transactions
$transactions = $this->createTestTransactions($wallet);
$transactions = array_reverse($transactions);
$pages = array_chunk($transactions, 10 /* page size*/);
$this->browse(function (Browser $browser) use ($pages) {
$browser->on(new Dashboard())
->click('@links .link-wallet')
->on(new WalletPage())
->assertSeeIn('@nav #tab-history', 'History')
->click('@nav #tab-history')
->with('@history-tab', function (Browser $browser) use ($pages) {
$browser->waitUntilMissing('.app-loader')
->assertElementsCount('table tbody tr', 10)
->assertMissing('table td.email')
->assertSeeIn('#transactions-loader button', 'Load more');
foreach ($pages[0] as $idx => $transaction) {
$selector = 'table tbody tr:nth-child(' . ($idx + 1) . ')';
$priceStyle = $transaction->type == Transaction::WALLET_AWARD ? 'text-success' : 'text-danger';
$browser->assertSeeIn("$selector td.description", $transaction->shortDescription())
->assertMissing("$selector td.selection button")
->assertVisible("$selector td.price.{$priceStyle}");
// TODO: Test more transaction details
}
// Load the next page
$browser->click('#transactions-loader button')
->waitUntilMissing('.app-loader')
->assertElementsCount('table tbody tr', 12)
->assertMissing('#transactions-loader button');
$debitEntry = null;
foreach ($pages[1] as $idx => $transaction) {
$selector = 'table tbody tr:nth-child(' . ($idx + 1 + 10) . ')';
$priceStyle = $transaction->type == Transaction::WALLET_CREDIT ? 'text-success' : 'text-danger';
$browser->assertSeeIn("$selector td.description", $transaction->shortDescription());
if ($transaction->type == Transaction::WALLET_DEBIT) {
$debitEntry = $selector;
} else {
$browser->assertMissing("$selector td.selection button");
}
}
// Load sub-transactions
$browser->click("$debitEntry td.selection button")
->waitUntilMissing('.app-loader')
->assertElementsCount("$debitEntry td.description ul li", 2)
->assertMissing("$debitEntry td.selection button");
});
});
}
}
diff --git a/src/tests/Feature/Controller/WalletsTest.php b/src/tests/Feature/Controller/WalletsTest.php
index 22cb707a..e327f1a2 100644
--- a/src/tests/Feature/Controller/WalletsTest.php
+++ b/src/tests/Feature/Controller/WalletsTest.php
@@ -1,351 +1,353 @@
<?php
namespace Tests\Feature\Controller;
use App\Http\Controllers\API\V4\WalletsController;
use App\Payment;
use App\Providers\PaymentProvider;
use App\Transaction;
use Carbon\Carbon;
use Tests\TestCase;
class WalletsTest extends TestCase
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('wallets-controller@kolabnow.com');
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('wallets-controller@kolabnow.com');
parent::tearDown();
}
/**
* Test for getWalletNotice() method
*/
public function testGetWalletNotice(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$package = \App\Package::where('title', 'kolab')->first();
$user->assignPackage($package);
$wallet = $user->wallets()->first();
$controller = new WalletsController();
$method = new \ReflectionMethod($controller, 'getWalletNotice');
$method->setAccessible(true);
// User/entitlements created today, balance=0
$notice = $method->invoke($controller, $wallet);
$this->assertSame('You are in your free trial period.', $notice);
$wallet->owner->created_at = Carbon::now()->subDays(15);
$wallet->owner->save();
$notice = $method->invoke($controller, $wallet);
$this->assertSame('Your free trial is about to end, top up to continue.', $notice);
// User/entitlements created today, balance=-10 CHF
$wallet->balance = -1000;
$notice = $method->invoke($controller, $wallet);
$this->assertSame('You are out of credit, top up your balance now.', $notice);
// User/entitlements created slightly more than a month ago, balance=9,99 CHF (monthly)
$wallet->owner->created_at = Carbon::now()->subMonthsWithoutOverflow(1)->subDays(1);
$wallet->owner->save();
// test "1 month"
$wallet->balance = 999;
$notice = $method->invoke($controller, $wallet);
$this->assertRegExp('/\((1 month|4 weeks)\)/', $notice);
// test "2 months"
$wallet->balance = 999 * 2.6;
$notice = $method->invoke($controller, $wallet);
$this->assertRegExp('/\(2 months 2 weeks\)/', $notice);
// test "almost 2 years"
$wallet->balance = 999 * 23.5;
$notice = $method->invoke($controller, $wallet);
$this->assertRegExp('/\(1 year 11 months\)/', $notice);
// Old entitlements, 100% discount
$this->backdateEntitlements($wallet->entitlements, Carbon::now()->subDays(40));
$discount = \App\Discount::where('discount', 100)->first();
$wallet->discount()->associate($discount);
$notice = $method->invoke($controller, $wallet->refresh());
$this->assertSame(null, $notice);
}
/**
* Test fetching pdf receipt
*/
public function testReceiptDownload(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
// Unauth access not allowed
$response = $this->get("api/v4/wallets/{$wallet->id}/receipts/2020-05");
$response->assertStatus(401);
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts/2020-05");
$response->assertStatus(403);
// Invalid receipt id (current month)
$receiptId = date('Y-m');
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}");
$response->assertStatus(404);
// Invalid receipt id
$receiptId = '1000-03';
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}");
$response->assertStatus(404);
// Valid receipt id
$year = intval(date('Y')) - 1;
$receiptId = "$year-12";
$filename = \config('app.name') . " Receipt for $year-12";
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}");
$response->assertStatus(200);
$response->assertHeader('content-type', 'application/pdf');
$response->assertHeader('content-disposition', 'attachment; filename="' . $filename . '"');
$response->assertHeader('content-length');
$length = $response->headers->get('content-length');
$content = $response->content();
$this->assertStringStartsWith("%PDF-1.", $content);
$this->assertEquals(strlen($content), $length);
}
/**
* Test fetching list of receipts
*/
public function testReceipts(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
$wallet->payments()->delete();
// Unauth access not allowed
$response = $this->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(401);
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(403);
// Empty list expected
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame([], $json['list']);
$this->assertSame(1, $json['page']);
$this->assertSame(0, $json['count']);
$this->assertSame(false, $json['hasMore']);
// Insert a payment to the database
$date = Carbon::create(intval(date('Y')) - 1, 4, 30);
$payment = Payment::create([
'id' => 'AAA1',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Paid in April',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 1111,
+ 'currency' => 'CHF',
+ 'currency_amount' => 1111,
]);
$payment->updated_at = $date;
$payment->save();
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame([$date->format('Y-m')], $json['list']);
$this->assertSame(1, $json['page']);
$this->assertSame(1, $json['count']);
$this->assertSame(false, $json['hasMore']);
}
/**
* Test fetching a wallet (GET /api/v4/wallets/:id)
*/
public function testShow(): void
{
$john = $this->getTestUser('john@kolab.org');
$jack = $this->getTestUser('jack@kolab.org');
$wallet = $john->wallets()->first();
$wallet->balance = -100;
$wallet->save();
// Accessing a wallet of someone else
$response = $this->actingAs($jack)->get("api/v4/wallets/{$wallet->id}");
$response->assertStatus(403);
// Accessing non-existing wallet
$response = $this->actingAs($jack)->get("api/v4/wallets/aaa");
$response->assertStatus(404);
// Wallet owner
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}");
$response->assertStatus(200);
$json = $response->json();
$this->assertSame($wallet->id, $json['id']);
$this->assertSame('CHF', $json['currency']);
$this->assertSame($wallet->balance, $json['balance']);
$this->assertTrue(empty($json['description']));
$this->assertTrue(!empty($json['notice']));
}
/**
* Test fetching wallet transactions
*/
public function testTransactions(): void
{
$package_kolab = \App\Package::where('title', 'kolab')->first();
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$user->assignPackage($package_kolab);
$john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
// Unauth access not allowed
$response = $this->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(401);
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(403);
// Expect empty list
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame([], $json['list']);
$this->assertSame(1, $json['page']);
$this->assertSame(0, $json['count']);
$this->assertSame(false, $json['hasMore']);
// Create some sample transactions
$transactions = $this->createTestTransactions($wallet);
$transactions = array_reverse($transactions);
$pages = array_chunk($transactions, 10 /* page size*/);
// Get the first page
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(1, $json['page']);
$this->assertSame(10, $json['count']);
$this->assertSame(true, $json['hasMore']);
$this->assertCount(10, $json['list']);
foreach ($pages[0] as $idx => $transaction) {
$this->assertSame($transaction->id, $json['list'][$idx]['id']);
$this->assertSame($transaction->type, $json['list'][$idx]['type']);
$this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']);
$this->assertFalse($json['list'][$idx]['hasDetails']);
$this->assertFalse(array_key_exists('user', $json['list'][$idx]));
}
$search = null;
// Get the second page
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=2");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(2, $json['page']);
$this->assertSame(2, $json['count']);
$this->assertSame(false, $json['hasMore']);
$this->assertCount(2, $json['list']);
foreach ($pages[1] as $idx => $transaction) {
$this->assertSame($transaction->id, $json['list'][$idx]['id']);
$this->assertSame($transaction->type, $json['list'][$idx]['type']);
$this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']);
$this->assertSame(
$transaction->type == Transaction::WALLET_DEBIT,
$json['list'][$idx]['hasDetails']
);
$this->assertFalse(array_key_exists('user', $json['list'][$idx]));
if ($transaction->type == Transaction::WALLET_DEBIT) {
$search = $transaction->id;
}
}
// Get a non-existing page
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=3");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(3, $json['page']);
$this->assertSame(0, $json['count']);
$this->assertSame(false, $json['hasMore']);
$this->assertCount(0, $json['list']);
// Sub-transaction searching
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction=123");
$response->assertStatus(404);
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(1, $json['page']);
$this->assertSame(2, $json['count']);
$this->assertSame(false, $json['hasMore']);
$this->assertCount(2, $json['list']);
$this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][0]['type']);
$this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][1]['type']);
// Test that John gets 404 if he tries to access
// someone else's transaction ID on his wallet's endpoint
$wallet = $john->wallets()->first();
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}");
$response->assertStatus(404);
}
}
diff --git a/src/tests/Feature/Documents/ReceiptTest.php b/src/tests/Feature/Documents/ReceiptTest.php
index e0fc5726..356151bb 100644
--- a/src/tests/Feature/Documents/ReceiptTest.php
+++ b/src/tests/Feature/Documents/ReceiptTest.php
@@ -1,367 +1,385 @@
<?php
namespace Tests\Feature\Documents;
use App\Documents\Receipt;
use App\Payment;
use App\Providers\PaymentProvider;
use App\User;
use App\Wallet;
use Carbon\Carbon;
use Illuminate\Support\Facades\Bus;
use Tests\TestCase;
class ReceiptTest extends TestCase
{
private $paymentIDs = ['AAA1', 'AAA2', 'AAA3', 'AAA4', 'AAA5', 'AAA6', 'AAA7'];
public function setUp(): void
{
parent::setUp();
Payment::whereIn('id', $this->paymentIDs)->delete();
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('receipt-test@kolabnow.com');
Payment::whereIn('id', $this->paymentIDs)->delete();
parent::tearDown();
}
/**
* Test receipt HTML output (without VAT)
*/
public function testHtmlOutput(): void
{
$appName = \config('app.name');
$wallet = $this->getTestData();
$receipt = new Receipt($wallet, 2020, 5);
$html = $receipt->htmlOutput();
$this->assertStringStartsWith('<!DOCTYPE html>', $html);
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadHTML($html);
// Title
$title = $dom->getElementById('title');
$this->assertSame("Receipt for May 2020", $title->textContent);
// Company name/address
$header = $dom->getElementById('header');
$companyOutput = $this->getNodeContent($header->getElementsByTagName('td')[0]);
$companyExpected = \config('app.company.name') . "\n" . \config('app.company.address');
$this->assertSame($companyExpected, $companyOutput);
// The main table content
$content = $dom->getElementById('content');
$records = $content->getElementsByTagName('tr');
$this->assertCount(7, $records);
$headerCells = $records[0]->getElementsByTagName('th');
$this->assertCount(3, $headerCells);
$this->assertSame('Date', $this->getNodeContent($headerCells[0]));
$this->assertSame('Description', $this->getNodeContent($headerCells[1]));
$this->assertSame('Amount', $this->getNodeContent($headerCells[2]));
$cells = $records[1]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-01', $this->getNodeContent($cells[0]));
$this->assertSame("$appName Services", $this->getNodeContent($cells[1]));
$this->assertSame('12,34 CHF', $this->getNodeContent($cells[2]));
$cells = $records[2]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-10', $this->getNodeContent($cells[0]));
$this->assertSame("$appName Services", $this->getNodeContent($cells[1]));
$this->assertSame('0,01 CHF', $this->getNodeContent($cells[2]));
$cells = $records[3]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-21', $this->getNodeContent($cells[0]));
$this->assertSame("$appName Services", $this->getNodeContent($cells[1]));
$this->assertSame('1,00 CHF', $this->getNodeContent($cells[2]));
$cells = $records[4]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-30', $this->getNodeContent($cells[0]));
$this->assertSame("Refund", $this->getNodeContent($cells[1]));
$this->assertSame('-1,00 CHF', $this->getNodeContent($cells[2]));
$cells = $records[5]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-31', $this->getNodeContent($cells[0]));
$this->assertSame("Chargeback", $this->getNodeContent($cells[1]));
$this->assertSame('-0,10 CHF', $this->getNodeContent($cells[2]));
$summaryCells = $records[6]->getElementsByTagName('td');
$this->assertCount(2, $summaryCells);
$this->assertSame('Total', $this->getNodeContent($summaryCells[0]));
$this->assertSame('12,25 CHF', $this->getNodeContent($summaryCells[1]));
// Customer data
$customer = $dom->getElementById('customer');
$customerCells = $customer->getElementsByTagName('td');
$customerOutput = $this->getNodeContent($customerCells[0]);
$customerExpected = "Firstname Lastname\nTest Unicode Straße 150\n10115 Berlin";
$this->assertSame($customerExpected, $this->getNodeContent($customerCells[0]));
$customerIdents = $this->getNodeContent($customerCells[1]);
//$this->assertTrue(strpos($customerIdents, "Account ID {$wallet->id}") !== false);
$this->assertTrue(strpos($customerIdents, "Customer No. {$wallet->owner->id}") !== false);
// Company details in the footer
$footer = $dom->getElementById('footer');
$footerOutput = $footer->textContent;
$this->assertStringStartsWith(\config('app.company.details'), $footerOutput);
$this->assertTrue(strpos($footerOutput, \config('app.company.email')) !== false);
}
/**
* Test receipt HTML output (with VAT)
*/
public function testHtmlOutputVat(): void
{
\config(['app.vat.rate' => 7.7]);
\config(['app.vat.countries' => 'ch']);
$appName = \config('app.name');
$wallet = $this->getTestData('CH');
$receipt = new Receipt($wallet, 2020, 5);
$html = $receipt->htmlOutput();
$this->assertStringStartsWith('<!DOCTYPE html>', $html);
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadHTML($html);
// The main table content
$content = $dom->getElementById('content');
$records = $content->getElementsByTagName('tr');
$this->assertCount(9, $records);
$cells = $records[1]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-01', $this->getNodeContent($cells[0]));
$this->assertSame("$appName Services", $this->getNodeContent($cells[1]));
$this->assertSame('11,39 CHF', $this->getNodeContent($cells[2]));
$cells = $records[2]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-10', $this->getNodeContent($cells[0]));
$this->assertSame("$appName Services", $this->getNodeContent($cells[1]));
$this->assertSame('0,01 CHF', $this->getNodeContent($cells[2]));
$cells = $records[3]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-21', $this->getNodeContent($cells[0]));
$this->assertSame("$appName Services", $this->getNodeContent($cells[1]));
$this->assertSame('0,92 CHF', $this->getNodeContent($cells[2]));
$cells = $records[4]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-30', $this->getNodeContent($cells[0]));
$this->assertSame("Refund", $this->getNodeContent($cells[1]));
$this->assertSame('-0,92 CHF', $this->getNodeContent($cells[2]));
$cells = $records[5]->getElementsByTagName('td');
$this->assertCount(3, $cells);
$this->assertSame('2020-05-31', $this->getNodeContent($cells[0]));
$this->assertSame("Chargeback", $this->getNodeContent($cells[1]));
$this->assertSame('-0,09 CHF', $this->getNodeContent($cells[2]));
$subtotalCells = $records[6]->getElementsByTagName('td');
$this->assertCount(2, $subtotalCells);
$this->assertSame('Subtotal', $this->getNodeContent($subtotalCells[0]));
$this->assertSame('11,31 CHF', $this->getNodeContent($subtotalCells[1]));
$vatCells = $records[7]->getElementsByTagName('td');
$this->assertCount(2, $vatCells);
$this->assertSame('VAT (7.7%)', $this->getNodeContent($vatCells[0]));
$this->assertSame('0,94 CHF', $this->getNodeContent($vatCells[1]));
$totalCells = $records[8]->getElementsByTagName('td');
$this->assertCount(2, $totalCells);
$this->assertSame('Total', $this->getNodeContent($totalCells[0]));
$this->assertSame('12,25 CHF', $this->getNodeContent($totalCells[1]));
}
/**
* Test receipt PDF output
*/
public function testPdfOutput(): void
{
$wallet = $this->getTestData();
$receipt = new Receipt($wallet, 2020, 5);
$pdf = $receipt->PdfOutput();
$this->assertStringStartsWith("%PDF-1.", $pdf);
$this->assertTrue(strlen($pdf) > 5000);
// TODO: Test the content somehow
}
/**
* Prepare data for a test
*
* @param string $country User country code
*
* @return \App\Wallet
*/
protected function getTestData(string $country = null): Wallet
{
Bus::fake();
$user = $this->getTestUser('receipt-test@kolabnow.com');
$user->setSettings([
'first_name' => 'Firstname',
'last_name' => 'Lastname',
'billing_address' => "Test Unicode Straße 150\n10115 Berlin",
'country' => $country
]);
$wallet = $user->wallets()->first();
// Create two payments out of the 2020-05 period
// and three in it, plus one in the period but unpaid,
// and one with amount 0, and an extra refund and chanrgeback
$payment = Payment::create([
'id' => 'AAA1',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Paid in April',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 1111,
+ 'currency' => 'CHF',
+ 'currency_amount' => 1111,
]);
$payment->updated_at = Carbon::create(2020, 4, 30, 12, 0, 0);
$payment->save();
$payment = Payment::create([
'id' => 'AAA2',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Paid in June',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 2222,
+ 'currency' => 'CHF',
+ 'currency_amount' => 2222,
]);
$payment->updated_at = Carbon::create(2020, 6, 1, 0, 0, 0);
$payment->save();
$payment = Payment::create([
'id' => 'AAA3',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Auto-Payment Setup',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 0,
+ 'currency' => 'CHF',
+ 'currency_amount' => 0,
]);
$payment->updated_at = Carbon::create(2020, 5, 1, 0, 0, 0);
$payment->save();
$payment = Payment::create([
'id' => 'AAA4',
'status' => PaymentProvider::STATUS_OPEN,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Payment not yet paid',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 999,
+ 'currency' => 'CHF',
+ 'currency_amount' => 999,
]);
$payment->updated_at = Carbon::create(2020, 5, 1, 0, 0, 0);
$payment->save();
// ... so we expect the five three on the receipt
$payment = Payment::create([
'id' => 'AAA5',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Payment OK',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 1234,
+ 'currency' => 'CHF',
+ 'currency_amount' => 1234,
]);
$payment->updated_at = Carbon::create(2020, 5, 1, 0, 0, 0);
$payment->save();
$payment = Payment::create([
'id' => 'AAA6',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Payment OK',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 1,
+ 'currency' => 'CHF',
+ 'currency_amount' => 1,
]);
$payment->updated_at = Carbon::create(2020, 5, 10, 0, 0, 0);
$payment->save();
$payment = Payment::create([
'id' => 'AAA7',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_RECURRING,
'description' => 'Payment OK',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 100,
+ 'currency' => 'CHF',
+ 'currency_amount' => 100,
]);
$payment->updated_at = Carbon::create(2020, 5, 21, 23, 59, 0);
$payment->save();
$payment = Payment::create([
'id' => 'ref1',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_REFUND,
'description' => 'refund desc',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => -100,
+ 'currency' => 'CHF',
+ 'currency_amount' => -100,
]);
$payment->updated_at = Carbon::create(2020, 5, 30, 23, 59, 0);
$payment->save();
$payment = Payment::create([
'id' => 'chback1',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_CHARGEBACK,
'description' => '',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => -10,
+ 'currency' => 'CHF',
+ 'currency_amount' => -10,
]);
$payment->updated_at = Carbon::create(2020, 5, 31, 23, 59, 0);
$payment->save();
// Make sure some config is set so we can test it's put into the receipt
if (empty(\config('app.company.name'))) {
\config(['app.company.name' => 'Company Co.']);
}
if (empty(\config('app.company.email'))) {
\config(['app.company.email' => 'email@domina.tld']);
}
if (empty(\config('app.company.details'))) {
\config(['app.company.details' => 'VAT No. 123456789']);
}
if (empty(\config('app.company.address'))) {
\config(['app.company.address' => "Test Street 12\n12345 Some Place"]);
}
return $wallet;
}
/**
* Extract text from a HTML element replacing <br> with \n
*
* @param \DOMElement $node The HTML element
*
* @return string The content
*/
protected function getNodeContent(\DOMElement $node)
{
$content = [];
foreach ($node->childNodes as $child) {
if ($child->nodeName == 'br') {
$content[] = "\n";
} else {
$content[] = $child->textContent;
}
}
return trim(implode($content));
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Feb 2, 1:32 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
426924
Default Alt Text
(39 KB)
Attached To
Mode
R2 kolab
Attached
Detach File
Event Timeline
Log In to Comment