Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[10.0] Refactor Invoices #685

Merged
merged 1 commit into from
Jun 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@
"illuminate/routing": "~5.8.0|~5.9.0",
"illuminate/support": "~5.8.0|~5.9.0",
"illuminate/view": "~5.8.0|~5.9.0",
"moneyphp/money": "^3.2",
"nesbot/carbon": "^1.26.3|^2.0",
"stripe/stripe-php": "^6.0",
"symfony/http-kernel": "^4.2"
"symfony/http-kernel": "^4.2",
"symfony/intl": "^4.3"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^3.8",
"phpunit/phpunit": "^7.5"
},
"suggest": {
"ext-intl": "Allows for more locales besides the default \"en\" when formatting money values."
},
"autoload": {
"psr-4": {
"Laravel\\Cashier\\": "src/"
Expand Down
68 changes: 35 additions & 33 deletions resources/views/receipt.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Invoice</title>

<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
background: #fff;
Expand All @@ -25,32 +25,25 @@
.invoice-head td {
padding: 0 8px;
}
.invoice-body{
background-color:transparent;
}
.logo {
padding-bottom: 10px;
}
.table th {
vertical-align: bottom;
font-weight: bold;
padding: 8px;
line-height: 20px;
text-align: left;
border-bottom: 1px solid #dddddd;
}
.table tr.row td {
border-bottom: 1px solid #dddddd;
}
.table td {
padding: 8px;
line-height: 20px;
text-align: left;
vertical-align: top;
border-top: 1px solid #dddddd;
}
.well {
margin-top: 15px;
}
</style>
</head>

<body>
<div class="container">
<table style="margin-left: auto; margin-right: auto" width="550">
Expand Down Expand Up @@ -121,24 +114,17 @@
<th align="right">Amount</th>
</tr>

<!-- Existing Balance -->
<tr>
<td>Starting Balance</td>
<td>&nbsp;</td>
<td>{{ $invoice->startingBalance() }}</td>
</tr>

<!-- Display The Invoice Items -->
@foreach ($invoice->invoiceItems() as $item)
<tr>
<tr class="row">
<td colspan="2">{{ $item->description }}</td>
<td>{{ $item->total() }}</td>
</tr>
@endforeach

<!-- Display The Subscriptions -->
@foreach ($invoice->subscriptions() as $subscription)
<tr>
<tr class="row">
<td>Subscription ({{ $subscription->quantity }})</td>
<td>
{{ $subscription->startDateAsCarbon()->formatLocalized('%B %e, %Y') }} -
Expand All @@ -148,32 +134,48 @@
</tr>
@endforeach

<!-- Display The Subtotal -->
@if ($invoice->hasDiscount() || $invoice->tax_percent || $invoice->hasStartingBalance())
<tr>
<td colspan="2" style="text-align: right;">Subtotal</td>
<td>{{ $invoice->subtotal() }}</td>
</tr>
@endif

<!-- Display The Discount -->
@if ($invoice->hasDiscount())
<tr>
@if ($invoice->discountIsPercentage())
<td>{{ $invoice->coupon() }} ({{ $invoice->percentOff() }}% Off)</td>
@else
<td>{{ $invoice->coupon() }} ({{ $invoice->amountOff() }} Off)</td>
@endif
<td>&nbsp;</td>
<td colspan="2" style="text-align: right;">
@if ($invoice->discountIsPercentage())
{{ $invoice->coupon() }} ({{ $invoice->percentOff() }}% Off)
@else
{{ $invoice->coupon() }} ({{ $invoice->amountOff() }} Off)
@endif
</td>

<td>-{{ $invoice->discount() }}</td>
</tr>
@endif

<!-- Display The Tax Amount -->
@if ($invoice->tax_percent)
<tr>
<td>Tax ({{ $invoice->tax_percent }}%)</td>
<td>&nbsp;</td>
<td>{{ Laravel\Cashier\Cashier::formatAmount($invoice->tax) }}</td>
<td colspan="2" style="text-align: right;">Tax ({{ $invoice->tax_percent }}%)</td>
<td>{{ $invoice->tax() }}</td>
</tr>
@endif

<!-- Starting Balance -->
@if ($invoice->hasStartingBalance())
<tr>
<td colspan="2" style="text-align: right;">Customer Balance</td>
<td>{{ $invoice->startingBalance() }}</td>
</tr>
@endif

<!-- Display The Final Total -->
<tr style="border-top:2px solid #000;">
<td>&nbsp;</td>
<td style="text-align: right;"><strong>Total</strong></td>
<tr>
<td colspan="2" style="text-align: right;"><strong>Total</strong></td>
<td><strong>{{ $invoice->total() }}</strong></td>
</tr>
</table>
Expand Down
75 changes: 22 additions & 53 deletions src/Cashier.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace Laravel\Cashier;

use Exception;
use Illuminate\Support\Str;
use Money\Money;
use Money\Currency;
use NumberFormatter;
use Money\Currencies\ISOCurrencies;
use Money\Formatter\IntlMoneyFormatter;

class Cashier
{
Expand Down Expand Up @@ -36,11 +39,14 @@ class Cashier
protected static $currency = 'usd';

/**
* The current currency symbol.
* The locale used to format money values.
*
* To use more locales besides the default "en" locale, make
* sure you have the ext-intl installed on your environment.
*
* @var string
*/
protected static $currencySymbol = '$';
protected static $currencyLocale = 'en';

/**
* The custom currency formatter.
Expand Down Expand Up @@ -142,38 +148,11 @@ public static function stripeModel()
* Set the currency to be used when billing Stripe models.
*
* @param string $currency
* @param string|null $symbol
* @return void
* @throws \Exception
*/
public static function useCurrency($currency, $symbol = null)
public static function useCurrency($currency)
{
static::$currency = $currency;

static::useCurrencySymbol($symbol ?: static::guessCurrencySymbol($currency));
}

/**
* Guess the currency symbol for the given currency.
*
* @param string $currency
* @return string
* @throws \Exception
*/
protected static function guessCurrencySymbol($currency)
{
switch (strtolower($currency)) {
case 'usd':
case 'aud':
case 'cad':
return '$';
case 'eur':
return '€';
case 'gbp':
return '£';
default:
throw new Exception('Unable to guess symbol for currency. Please explicitly specify it.');
}
}

/**
Expand All @@ -187,24 +166,14 @@ public static function usesCurrency()
}

/**
* Set the currency symbol to be used when formatting currency.
* Set the currency locale to format money.
*
* @param string $symbol
* @param string $currencyLocale
* @return void
*/
public static function useCurrencySymbol($symbol)
public static function useCurrencyLocale($currencyLocale)
{
static::$currencySymbol = $symbol;
}

/**
* Get the currency symbol currently in use.
*
* @return string
*/
public static function usesCurrencySymbol()
{
return static::$currencySymbol;
static::$currencyLocale = $currencyLocale;
}

/**
Expand All @@ -222,21 +191,21 @@ public static function formatCurrencyUsing(callable $callback)
* Format the given amount into a displayable currency.
*
* @param int $amount
* @param string|null $currency
* @return string
*/
public static function formatAmount($amount)
public static function formatAmount($amount, $currency = null)
{
if (static::$formatCurrencyUsing) {
return call_user_func(static::$formatCurrencyUsing, $amount);
return call_user_func(static::$formatCurrencyUsing, $amount, $currency);
}

$amount = number_format($amount / 100, 2);
$money = new Money($amount, new Currency(strtoupper($currency ?? static::usesCurrency())));

if (Str::startsWith($amount, '-')) {
return '-'.static::usesCurrencySymbol().ltrim($amount, '-');
}
$numberFormatter = new NumberFormatter(static::$currencyLocale, NumberFormatter::CURRENCY);
$moneyFormatter = new IntlMoneyFormatter($numberFormatter, new ISOCurrencies());

return static::usesCurrencySymbol().$amount;
return $moneyFormatter->format($money);
}

/**
Expand Down
44 changes: 25 additions & 19 deletions src/Invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ public function total()
/**
* Get the raw total amount that was paid (or will be paid).
*
* @return float
* @return int
*/
public function rawTotal()
{
return max(0, $this->invoice->total - ($this->rawStartingBalance() * -1));
return $this->invoice->total + $this->rawStartingBalance();
}

/**
Expand All @@ -77,9 +77,7 @@ public function rawTotal()
*/
public function subtotal()
{
return $this->formatAmount(
max(0, $this->invoice->subtotal - ($this->rawStartingBalance() * -1))
);
return $this->formatAmount($this->invoice->subtotal);
}

/**
Expand All @@ -102,6 +100,16 @@ public function startingBalance()
return $this->formatAmount($this->rawStartingBalance());
}

/**
* Get the raw starting balance for the invoice.
*
* @return int
*/
public function rawStartingBalance()
{
return $this->invoice->starting_balance ?? 0;
}

/**
* Determine if the invoice has a discount.
*
Expand Down Expand Up @@ -174,6 +182,16 @@ public function amountOff()
return $this->formatAmount(0);
}

/**
* Get the tax total amount.
*
* @return string
*/
public function tax()
{
return $this->formatAmount($this->invoice->tax);
}

/**
* Get all of the "invoice item" line items.
*
Expand Down Expand Up @@ -216,14 +234,14 @@ public function invoiceItemsByType($type)
}

/**
* Format the given amount into a string based on the Stripe model's preferences.
* Format the given amount into a displayable currency.
*
* @param int $amount
* @return string
*/
protected function formatAmount($amount)
{
return Cashier::formatAmount($amount);
return Cashier::formatAmount($amount, $this->invoice->currency);
}

/**
Expand Down Expand Up @@ -278,18 +296,6 @@ public function download(array $data)
]);
}

/**
* Get the raw starting balance for the invoice.
*
* @return float
*/
public function rawStartingBalance()
{
return isset($this->invoice->starting_balance)
? $this->invoice->starting_balance
: 0;
}

/**
* Get the Stripe invoice instance.
*
Expand Down
4 changes: 2 additions & 2 deletions src/InvoiceItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ public function isSubscription()
}

/**
* Format the given amount into a string based on the owner model's preferences.
* Format the given amount into a displayable currency.
*
* @param int $amount
* @return string
*/
protected function formatAmount($amount)
{
return Cashier::formatAmount($amount);
return Cashier::formatAmount($amount, $this->item->currency);
}

/**
Expand Down
Loading