diff --git a/.travis.yml b/.travis.yml
index 65c9af6c..74eb620d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,6 +20,7 @@ matrix:
before_install:
- phpenv config-rm xdebug.ini
- composer self-update
+ - php -i
install:
- composer install
diff --git a/composer.json b/composer.json
index 1d8c5ff4..9c786813 100644
--- a/composer.json
+++ b/composer.json
@@ -33,6 +33,7 @@
"symfony/flex": "^1.0",
"symfony/form": "^4.0",
"symfony/framework-bundle": "^4.0",
+ "symfony/intl": "^4.0",
"symfony/maker-bundle": "^1.0",
"symfony/monolog-bundle": "^3.1",
"symfony/orm-pack": "^1.0",
diff --git a/src/Invoice/Renderer/AbstractRenderer.php b/src/Invoice/Renderer/AbstractRenderer.php
index d7a3dbcb..65b53e70 100644
--- a/src/Invoice/Renderer/AbstractRenderer.php
+++ b/src/Invoice/Renderer/AbstractRenderer.php
@@ -55,11 +55,12 @@ abstract class AbstractRenderer
/**
* @param $amount
- * @return mixed
+ * @param $currency
+ * @return string
*/
- protected function getFormattedMoney($amount)
+ protected function getFormattedMoney($amount, $currency)
{
- return $this->extension->money($amount);
+ return $this->extension->money($amount, $currency);
}
/**
diff --git a/src/Invoice/Renderer/RendererTrait.php b/src/Invoice/Renderer/RendererTrait.php
index 98f3f6ec..2a51c0a8 100644
--- a/src/Invoice/Renderer/RendererTrait.php
+++ b/src/Invoice/Renderer/RendererTrait.php
@@ -53,7 +53,7 @@ trait RendererTrait
* @param $amount
* @return mixed
*/
- abstract protected function getFormattedMoney($amount);
+ abstract protected function getFormattedMoney($amount, $currency);
/**
* @param \DateTime $date
@@ -75,6 +75,7 @@ trait RendererTrait
{
$customer = $model->getCustomer();
$project = $model->getQuery()->getProject();
+ $currency = $model->getCalculator()->getCurrency();
$values = [
'invoice.due_date' => $this->getFormattedDateTime($model->getDueDate()),
@@ -82,10 +83,10 @@ trait RendererTrait
'invoice.number' => $model->getNumberGenerator()->getInvoiceNumber(),
'invoice.currency' => $model->getCalculator()->getCurrency(),
'invoice.vat' => $model->getCalculator()->getVat(),
- 'invoice.tax' => $this->getFormattedMoney($model->getCalculator()->getTax()),
+ 'invoice.tax' => $this->getFormattedMoney($model->getCalculator()->getTax(), $currency),
'invoice.total_time' => $this->getFormattedDuration($model->getCalculator()->getTimeWorked()),
- 'invoice.total' => $this->getFormattedMoney($model->getCalculator()->getTotal()),
- 'invoice.subtotal' => $this->getFormattedMoney($model->getCalculator()->getSubtotal()),
+ 'invoice.total' => $this->getFormattedMoney($model->getCalculator()->getTotal(), $currency),
+ 'invoice.subtotal' => $this->getFormattedMoney($model->getCalculator()->getSubtotal(), $currency),
'template.name' => $model->getTemplate()->getName(),
'template.company' => $model->getTemplate()->getCompany(),
@@ -156,6 +157,7 @@ trait RendererTrait
$activity = $timesheet->getActivity();
$project = $timesheet->getProject();
$customer = $project->getCustomer();
+ $currency = $customer->getCurrency();
$begin = $timesheet->getBegin();
$end = $timesheet->getEnd();
@@ -164,8 +166,9 @@ trait RendererTrait
'entry.row' => '',
'entry.description' => $description,
'entry.amount' => $amount,
- 'entry.rate' => $this->getFormattedMoney($hourlyRate),
- 'entry.total' => $this->getFormattedMoney($rate),
+ 'entry.rate' => $this->getFormattedMoney($hourlyRate, $currency),
+ 'entry.total' => $this->getFormattedMoney($rate, $currency),
+ 'entry.currency' => $currency,
'entry.duration' => $timesheet->getDuration(),
'entry.duration_minutes' => number_format($timesheet->getDuration() / 60),
'entry.begin' => $this->getFormattedDateTime($begin),
diff --git a/src/Twig/Extensions.php b/src/Twig/Extensions.php
index 67fe6e0c..274db78e 100644
--- a/src/Twig/Extensions.php
+++ b/src/Twig/Extensions.php
@@ -254,7 +254,7 @@ class Extensions extends \Twig_Extension
if ($this->locale !== $locale) {
$this->locale = $locale;
- $this->numberFormatter = new NumberFormatter($locale, NumberFormatter::DECIMAL);
+ $this->numberFormatter = new NumberFormatter($locale, NumberFormatter::CURRENCY);
}
$fractionDigits = Intl::getCurrencyBundle()->getFractionDigits($currency);
@@ -262,7 +262,7 @@ class Extensions extends \Twig_Extension
$result = $this->numberFormatter->format($amount);
if (null !== $currency) {
- $result .= ' ' . Intl::getCurrencyBundle()->getCurrencySymbol($currency, $locale);
+ $result = $this->numberFormatter->formatCurrency($amount, $currency);
}
return $result;
diff --git a/tests/Export/Renderer/CsvRendererTest.php b/tests/Export/Renderer/CsvRendererTest.php
index aae2c643..14b13244 100644
--- a/tests/Export/Renderer/CsvRendererTest.php
+++ b/tests/Export/Renderer/CsvRendererTest.php
@@ -31,7 +31,7 @@ class CsvRendererTest extends AbstractRendererTest
public function getTestModel()
{
return [
- ['01:50 h', '2,437.12 €', '1,947.99 €', 7, 5, 1, 2, 2]
+ ['01:50 h', '€2,437.12', '€1,947.99', 7, 5, 1, 2, 2]
];
}
diff --git a/tests/Export/Renderer/HtmlRendererTest.php b/tests/Export/Renderer/HtmlRendererTest.php
index 982315ba..48af8dd2 100644
--- a/tests/Export/Renderer/HtmlRendererTest.php
+++ b/tests/Export/Renderer/HtmlRendererTest.php
@@ -55,7 +55,7 @@ class HtmlRendererTest extends AbstractRendererTest
$this->assertContains('
Customer Name | ', $content);
$this->assertContains('project name | ', $content);
$this->assertContains('01:50 h | ', $content);
- $this->assertContains('2,437.12 € | ', $content);
+ $this->assertContains('€2,437.12 | ', $content);
$this->assertEquals(5, substr_count($content, 'activity description | '));
}
diff --git a/tests/Invoice/Renderer/AbstractRendererTest.php b/tests/Invoice/Renderer/AbstractRendererTest.php
index 37cfc84e..2ec30b9d 100644
--- a/tests/Invoice/Renderer/AbstractRendererTest.php
+++ b/tests/Invoice/Renderer/AbstractRendererTest.php
@@ -60,7 +60,7 @@ abstract class AbstractRendererTest extends KernelTestCase
$languages = [
'en' => [
'date' => 'Y.m.d',
- 'duration' => '%%h:%%m h'
+ 'duration' => '%h:%m h'
]
];
@@ -83,6 +83,7 @@ abstract class AbstractRendererTest extends KernelTestCase
protected function getInvoiceModel()
{
$customer = new Customer();
+ $customer->setCurrency('EUR');
$template = new InvoiceTemplate();
$template->setTitle('a test invoice template title');
$template->setVat(19);
@@ -194,6 +195,7 @@ abstract class AbstractRendererTest extends KernelTestCase
protected function getInvoiceModelOneEntry()
{
$customer = new Customer();
+ $customer->setCurrency('USD');
$template = new InvoiceTemplate();
$template->setTitle('a test invoice template title');
$template->setVat(19);
diff --git a/tests/Invoice/Renderer/CsvRendererTest.php b/tests/Invoice/Renderer/CsvRendererTest.php
index 822804da..d57ab204 100644
--- a/tests/Invoice/Renderer/CsvRendererTest.php
+++ b/tests/Invoice/Renderer/CsvRendererTest.php
@@ -36,8 +36,8 @@ class CsvRendererTest extends AbstractRendererTest
public function getTestModel()
{
- yield [$this->getInvoiceModel(), '1,947.99', 6, 5, 1, 2, 2];
- yield [$this->getInvoiceModelOneEntry(), '293.27', 2, 1, 0, 1, 0];
+ yield [$this->getInvoiceModel(), '€1,947.99', 6, 5, 1, 2, 2];
+ yield [$this->getInvoiceModelOneEntry(), '$293.27', 2, 1, 0, 1, 0];
}
/**
diff --git a/tests/Invoice/Renderer/DebugRendererTest.php b/tests/Invoice/Renderer/DebugRendererTest.php
index b042b809..d001fe96 100644
--- a/tests/Invoice/Renderer/DebugRendererTest.php
+++ b/tests/Invoice/Renderer/DebugRendererTest.php
@@ -100,6 +100,7 @@ class DebugRendererTest extends AbstractRendererTest
'entry.amount',
'entry.rate',
'entry.total',
+ 'entry.currency',
'entry.duration',
'entry.duration_minutes',
'entry.begin',
diff --git a/tests/Twig/ExtensionsTest.php b/tests/Twig/ExtensionsTest.php
index fa324fd8..f829e539 100644
--- a/tests/Twig/ExtensionsTest.php
+++ b/tests/Twig/ExtensionsTest.php
@@ -15,6 +15,7 @@ use App\Utils\LocaleSettings;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\Intl\Util\IntlTestHelper;
use Twig\TwigFilter;
/**
@@ -112,10 +113,6 @@ class ExtensionsTest extends TestCase
}
/**
- * @param string $result
- * @param int $amount
- * @param string $currency
- * @param string $locale
* @dataProvider getMoneyData
*/
public function testMoney($result, $amount, $currency, $locale)
@@ -127,19 +124,36 @@ class ExtensionsTest extends TestCase
public function getMoneyData()
{
return [
- ['0 €', null, 'EUR', 'de'],
- ['2.345 €', 2345, 'EUR', 'de'],
- ['2,345 €', 2345, 'EUR', 'en'],
- ['2,345.01 €', 2345.009, 'EUR', 'en'],
- ['2.345,01 €', 2345.009, 'EUR', 'de'],
- ['13.75 $', 13.75, 'USD', 'en'],
- ['13,75 $', 13.75, 'USD', 'de'],
- ['13,75 RUB', 13.75, 'RUB', 'de'],
- ['13,5 RUB', 13.50, 'RUB', 'de'],
- ['13,75 ₽', 13.75, 'RUB', 'ru'],
- ['14 ¥', 13.75, 'JPY', 'de'],
- ['13 933 ¥', 13933.49, 'JPY', 'ru'],
- ['1.234.567,89 $', 1234567.891234567890000, 'USD', 'de'],
+ ['0,00 €', null, 'EUR', 'de'],
+ ['2.345,00 €', 2345, 'EUR', 'de'],
+ ['€2,345.00', 2345, 'EUR', 'en'],
+ ['€2,345.01', 2345.009, 'EUR', 'en'],
+ ['2.345,01 €', 2345.009, 'EUR', 'de'],
+ ['$13.75', 13.75, 'USD', 'en'],
+ ['13,75 $', 13.75, 'USD', 'de'],
+ ['13,75 RUB', 13.75, 'RUB', 'de'],
+ ['14 ¥', 13.75, 'JPY', 'de'],
+ ['13 933 ¥', 13933.49, 'JPY', 'ru'],
+ ['1.234.567,89 $', 1234567.891234567890000, 'USD', 'de'],
+ ];
+ }
+
+ /**
+ * @dataProvider getMoneyData62_1
+ */
+ public function testMoney62_1($result, $amount, $currency, $locale)
+ {
+ IntlTestHelper::requireFullIntl($this, '62.1');
+
+ $sut = $this->getSut($this->localeEn, $locale);
+ $this->assertEquals($result, $sut->money($amount, $currency));
+ }
+
+ public function getMoneyData62_1()
+ {
+ return [
+ ['RUB 13.50', 13.50, 'RUB', 'en'],
+ ['13,75 ₽', 13.75, 'RUB', 'ru'],
];
}
diff --git a/var/docs/invoices.md b/var/docs/invoices.md
index a2aafe42..c8b2a300 100644
--- a/var/docs/invoices.md
+++ b/var/docs/invoices.md
@@ -114,6 +114,7 @@ The documents which are rendered passively (ODS, XLSX, CSV, DOCX) can use the fo
| ${invoice.total_time} | The total working time (entries with a fixed rate are always calculated with 1) |
| ${invoice.total} | The invoices total (including tax) |
| ${invoice.subtotal} | The invoices subtotal (excluding tax) |
+| ${invoice.currency} | The invoices currency as string (like EUR or USD) |
| ${invoice.vat} | The VAT in percent for this invoice |
| ${invoice.tax} | The tax of the invoice amount |
| ${template.name} | The invoice name, as configured in your template |
@@ -161,6 +162,7 @@ For each timesheet entry you can use the variables from the following table.
| ${entry.amount} | The format duration/amount for this entry | 02:47 h |
| ${entry.rate} | The rate for one unit of the entry (normally one hour) | 100 |
| ${entry.total} | The total rate for this entry | 278,33 |
+| ${entry.currency} | The currency for this record as string (like EUR or USD) | EUR |
| ${entry.duration} | The duration in seconds | 10020 |
| ${entry.duration_minutes} | The duration in minutes with no decimals | 167 |
| ${entry.begin} | The begin date (format depends on the users language) | 27.10.2018 |