added further timesheet related invoice fields (#411)

This commit is contained in:
Kevin Papst
2018-11-12 12:19:57 +01:00
committed by GitHub
parent f9dc42d1f9
commit e9cbb872d0
4 changed files with 289 additions and 56 deletions

View File

@@ -138,17 +138,23 @@ trait RendererTrait
$project = $timesheet->getProject(); $project = $timesheet->getProject();
$customer = $project->getCustomer(); $customer = $project->getCustomer();
$begin = $timesheet->getBegin();
$end = $timesheet->getEnd();
return [ return [
'entry.description' => $description, 'entry.description' => $description,
'entry.amount' => $amount, 'entry.amount' => $amount,
'entry.rate' => $this->getFormattedMoney($hourlyRate), 'entry.rate' => $this->getFormattedMoney($hourlyRate),
'entry.total' => $this->getFormattedMoney($rate), 'entry.total' => $this->getFormattedMoney($rate),
'entry.duration' => $timesheet->getDuration(), 'entry.duration' => $timesheet->getDuration(),
'entry.begin' => $this->getFormattedDateTime($timesheet->getBegin()), 'entry.duration_minutes' => number_format($timesheet->getDuration() / 60),
'entry.begin_timestamp' => $timesheet->getBegin()->getTimestamp(), 'entry.begin' => $this->getFormattedDateTime($begin),
'entry.end' => $this->getFormattedDateTime($timesheet->getEnd()), 'entry.begin_time' => date("H:i", $begin->getTimestamp()),
'entry.end_timestamp' => $timesheet->getEnd()->getTimestamp(), 'entry.begin_timestamp' => $begin->getTimestamp(),
'entry.date' => $this->getFormattedDateTime($timesheet->getBegin()), 'entry.end' => $this->getFormattedDateTime($end),
'entry.end_time' => date("H:i", $end->getTimestamp()),
'entry.end_timestamp' => $end->getTimestamp(),
'entry.date' => $this->getFormattedDateTime($begin),
'entry.user_id' => $user->getId(), 'entry.user_id' => $user->getId(),
'entry.user_name' => $user->getUsername(), 'entry.user_name' => $user->getUsername(),
'entry.user_alias' => $user->getAlias(), 'entry.user_alias' => $user->getAlias(),

View File

@@ -0,0 +1,94 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Invoice\Renderer;
use App\Entity\InvoiceDocument;
use App\Invoice\Renderer\RendererTrait;
use App\Invoice\RendererInterface;
use App\Model\InvoiceModel;
use Symfony\Component\HttpFoundation\Response;
class DebugRenderer implements RendererInterface
{
use RendererTrait;
/**
* @return string[]
*/
protected function getFileExtensions()
{
return [];
}
/**
* @return string
*/
protected function getContentType()
{
return 'array';
}
/**
* @param \DateTime $date
* @return mixed
*/
protected function getFormattedDateTime(\DateTime $date)
{
return $date->format('d.m.Y');
}
/**
* @param $amount
* @return mixed
*/
protected function getFormattedMoney($amount)
{
return $amount;
}
/**
* @param \DateTime $date
* @return mixed
*/
protected function getFormattedMonthName(\DateTime $date)
{
return $date->format('m');
}
/**
* @param $seconds
* @return mixed
*/
protected function getFormattedDuration($seconds)
{
return $seconds;
}
/**
* Render the given InvoiceDocument with the data from the InvoiceModel into a stupid array for testing only.
*
* @param InvoiceDocument $document
* @param InvoiceModel $model
* @return Response
*/
public function render(InvoiceDocument $document, InvoiceModel $model): Response
{
$result = [
'model' => $this->modelToReplacer($model),
'entries' => [],
];
foreach ($model->getCalculator()->getEntries() as $entry) {
$result['entries'][] = $this->timesheetToArray($entry);
}
return new Response(json_encode($result));
}
}

View File

@@ -0,0 +1,130 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Invoice\Renderer;
use App\Entity\InvoiceDocument;
use App\Model\InvoiceModel;
use Symfony\Component\HttpFoundation\Response;
class DebugRendererTest extends AbstractRendererTest
{
public function getTestModel()
{
yield [$this->getInvoiceModel(), '1,947.99', 5, 5, 1, 2, 2];
yield [$this->getInvoiceModelOneEntry(), '293.27', 1, 1, 0, 1, 0];
}
/**
* @dataProvider getTestModel
*/
public function testRender(InvoiceModel $model, $expectedRate, $expectedRows, $expectedDescriptions, $expectedUser1, $expectedUser2, $expectedUser3)
{
$document = new InvoiceDocument(new \SplFileInfo(__DIR__ . '/DebugRenderer.php'));
$sut = new DebugRenderer();
/** @var Response $response */
$response = $sut->render($document, $model);
$data = json_decode($response->getContent(), true);
$this->assertModelStructure($data['model']);
$rows = $data['entries'];
$this->assertEquals($expectedRows, count($rows));
foreach($rows as $row) {
$this->assertEntryStructure($row);
}
// TODO check values or formats?
}
protected function assertModelStructure(array $model)
{
$keys = [
'invoice.due_date',
'invoice.date',
'invoice.number',
'invoice.currency',
'invoice.vat',
'invoice.tax',
'invoice.total_time',
'invoice.total',
'invoice.subtotal',
'template.name',
'template.company',
'template.address',
'template.title',
'template.payment_terms',
'template.due_days',
'query.begin',
'query.end',
'query.month',
'query.year',
'customer.address',
'customer.name',
'customer.contact',
'customer.company',
'customer.country',
'customer.number',
'customer.homepage',
'customer.comment',
];
foreach($keys as $key) {
$this->assertArrayHasKey($key, $model);
}
$expectedKeys = array_merge([], $keys);
sort($expectedKeys);
$givenKeys = array_keys($model);
sort($givenKeys);
$this->assertEquals(count($keys), count($givenKeys));
$this->assertEquals($expectedKeys, $givenKeys);
}
protected function assertEntryStructure(array $model)
{
$keys = [
'entry.description',
'entry.amount',
'entry.rate',
'entry.total',
'entry.duration',
'entry.duration_minutes',
'entry.begin',
'entry.begin_time',
'entry.begin_timestamp',
'entry.end',
'entry.end_time',
'entry.end_timestamp',
'entry.date',
'entry.user_id',
'entry.user_name',
'entry.user_alias',
'entry.activity',
'entry.activity_id',
'entry.project',
'entry.customer',
'entry.project_id',
'entry.customer_id',
];
foreach($keys as $key) {
$this->assertArrayHasKey($key, $model);
}
$expectedKeys = array_merge([], $keys);
sort($expectedKeys);
$givenKeys = array_keys($model);
sort($givenKeys);
$this->assertEquals(count($keys), count($givenKeys));
$this->assertEquals($expectedKeys, $givenKeys);
}
}

View File

@@ -93,61 +93,64 @@ See below in `Template variables` to find out which variables you can use in you
You can use the following global variables in your templates: You can use the following global variables in your templates:
| Key | Description | | Key | Description | Example |
|---|---| |---|---|---|
| ${invoice.due_date} | The due date for the invoice payment | | ${invoice.due_date} | The due date for the invoice payment | |
| ${invoice.date} | The creation date of this invoice | | ${invoice.date} | The creation date of this invoice | |
| ${invoice.number} | The generated invoice number | | ${invoice.number} | The generated invoice number | |
| ${invoice.currency} | The invoice currency | | ${invoice.currency} | The invoice currency | |
| ${invoice.total_time} | The total working time (entries with a fixed rate are always calculated with 1) | | ${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.total} | The invoices total (including tax) | |
| ${invoice.subtotal} | The invoices subtotal (excluding tax) | | ${invoice.subtotal} | The invoices subtotal (excluding tax) | |
| ${invoice.vat} | The VAT in percent for this invoice | | ${invoice.vat} | The VAT in percent for this invoice | |
| ${invoice.tax} | The tax of the invoice amount | | ${invoice.tax} | The tax of the invoice amount | |
| ${template.name} | The invoice name, as configured in your template | | ${template.name} | The invoice name, as configured in your template | |
| ${template.company} | The company name, as configured in your template | | ${template.company} | The company name, as configured in your template | |
| ${template.address} | The invoicing address, as configured in your template | | ${template.address} | The invoicing address, as configured in your template | |
| ${template.title} | The invoice title, as configured in your template | | ${template.title} | The invoice title, as configured in your template | |
| ${template.payment_terms} | Your payment terms, usage might differ from template to template | | ${template.payment_terms} | Your payment terms, usage might differ from template to template | |
| ${template.due_days} | The amount of days for the payment, starting with the day of creating the invoice | | ${template.due_days} | The amount of days for the payment, starting with the day of creating the invoice | |
| ${query.begin} | The query begin as formatted short date | | ${query.begin} | The query begin as formatted short date | |
| ${query.end} | The query end as formatted short date | | ${query.end} | The query end as formatted short date | |
| ${query.month} | The month for this query (begin date) | | ${query.month} | The month for this query (begin date) | |
| ${query.year} | The year for this query (begin date) | | ${query.year} | The year for this query (begin date) | |
| ${customer.address} | The customer address | | ${customer.address} | The customer address | |
| ${customer.name} | The customer name | | ${customer.name} | The customer name | |
| ${customer.contact} | The customer contac | | ${customer.contact} | The customer contact | |
| ${customer.company} | The customer company | | ${customer.company} | The customer company | |
| ${customer.number} | The customer number | | ${customer.number} | The customer number | |
| ${customer.country} | The customer country | | ${customer.country} | The customer country | |
| ${customer.homepage} | The customer homepage | | ${customer.homepage} | The customer homepage | |
| ${customer.comment} | The customer comment | | ${customer.comment} | The customer comment | |
### Timesheet entry variables ### Timesheet entry variables
For each timesheet entry you can use the following variables: For each timesheet entry you can use the variables from the following table.
| Key | Description | | Key | Description | Example |
|---|---| |---|---|---|
| ${entry.description} | The entries description | | ${entry.description} | The entries description | _foo bar_ |
| ${entry.amount} | The amount for this entry (normally the amount of hours) | | ${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) | | ${entry.rate} | The rate for one unit of the entry (normally one hour) | 100 |
| ${entry.total} | The total rate for this entry | | ${entry.total} | The total rate for this entry | 278,33 |
| ${entry.duration} | The duration in seconds | | ${entry.duration} | The duration in seconds | 10020 |
| ${entry.begin} | The begin date - _format may change and include the time in the future_ | | ${entry.duration_minutes} | The duration in minutes with no decimals | 167 |
| ${entry.begin_timestamp} | The timestamp for the begin of this entry | | ${entry.begin} | The begin date (format depends on the users language) | 27.10.2018 |
| ${entry.end} | The begin date - _format may change and include the time in the future_ | | ${entry.begin_time} | The formatted time for the begin of this entry | 14:57 |
| ${entry.end_timestamp} | The timestamp for the end of this entry | | ${entry.begin_timestamp} | The timestamp for the begin of this entry | 1542016273 |
| ${entry.date} | The start date when this record was created | | ${entry.end} | The begin date (format depends on the users language) | 27.10.2018 |
| ${entry.user_id} | The user ID | | ${entry.end_time} | The formatted time for the end of this entry | 17:44 |
| ${entry.user_name} | The username | | ${entry.end_timestamp} | The timestamp for the end of this entry | 1542016273 |
| ${entry.user_alias} | The user alias | | ${entry.date} | The start date when this record was created | 27.10.2018 |
| ${entry.activity} | Activity name | | ${entry.user_id} | The user ID | 1 |
| ${entry.activity_id} | Activity ID | | ${entry.user_name} | The username | susan_super |
| ${entry.project} | Project name | | ${entry.user_alias} | The user alias | Susan Miller |
| ${entry.project_id} | Project ID | | ${entry.activity} | Activity name | Post production |
| ${entry.customer} | Customer name | | ${entry.activity_id} | Activity ID | 124 |
| ${entry.customer_id} | Customer ID | | ${entry.project} | Project name | Nemesis |
| ${entry.project_id} | Project ID | 10 |
| ${entry.customer} | Customer name | Acme Studios |
| ${entry.customer_id} | Customer ID | 3 |
## Configure search path ## Configure search path