added short invoice calculator and timesheet invoice template #104 (#105)

This commit is contained in:
Kevin Papst
2018-01-23 22:30:41 +01:00
committed by GitHub
parent 50b5e93a10
commit 7f5b5ac254
10 changed files with 215 additions and 5 deletions

View File

@@ -4,8 +4,10 @@ kimai:
invoice:
renderer:
default: 'App\Controller\InvoiceController::invoiceAction'
timesheet: 'App\Controller\InvoiceController::timesheetAction'
calculator:
default: 'App\Invoice\DefaultCalculator'
short: 'App\Invoice\ShortInvoiceCalculator'
number_generator:
default: 'App\Invoice\DateNumberGenerator'
#random: 'App\Invoice\RandomNumberGenerator'

View File

@@ -231,6 +231,17 @@ class InvoiceController extends AbstractController
]);
}
/**
* @param InvoiceModel $model
* @return \Symfony\Component\HttpFoundation\Response
*/
public function timesheetAction(InvoiceModel $model)
{
return $this->render('invoice/timesheet.html.twig', [
'model' => $model,
]);
}
/**
* @param InvoiceQuery $query
* @return \Symfony\Component\Form\FormInterface

View File

@@ -44,6 +44,7 @@ class CustomerEditForm extends AbstractType
])
->add('number', TextType::class, [
'label' => 'label.customer_number',
'required' => false,
])
->add('comment', TextareaType::class, [
'label' => 'label.comment',

View File

@@ -11,6 +11,7 @@
namespace App\Invoice;
use App\Entity\Timesheet;
use App\Model\InvoiceModel;
/**
@@ -22,26 +23,43 @@ interface CalculatorInterface
{
/**
* Return the timesheet records that will be displayed on the invoice.
*
* @return Timesheet[]
*/
public function getEntries();
/**
* Set the invoice model and can be used to fetch the customer.
*
* @param InvoiceModel $model
*/
public function setModel(InvoiceModel $model);
/**
* Returns the subtotal before taxes.
*
* @return float
*/
public function getSubtotal(): float;
/**
* Returns the tax amount for this invoice.
*
* @return float
*/
public function getTax(): float;
/**
* Returns the total amount for this invoice including taxes.
*
* @return float
*/
public function getTotal(): float;
/**
* Returns the currency for the invoices amounts.
*
* @return string
*/
public function getCurrency(): string;

View File

@@ -11,6 +11,7 @@
namespace App\Invoice;
use App\Entity\Timesheet;
use App\Model\InvoiceModel;
/**
@@ -34,6 +35,14 @@ class DefaultCalculator implements CalculatorInterface
*/
protected $model;
/**
* @return Timesheet[]
*/
public function getEntries()
{
return $this->model->getEntries();
}
/**
* @param InvoiceModel $model
*/

View File

@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Invoice;
use App\Entity\Timesheet;
/**
* A calculator that sums up all timesheet records from the model and returns only one
* entry for a compact invoice version.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ShortInvoiceCalculator extends DefaultCalculator
{
/**
* @return Timesheet[]
*/
public function getEntries()
{
$timesheet = new Timesheet();
foreach ($this->model->getEntries() as $entry) {
$timesheet->setDuration($timesheet->getDuration() + $entry->getDuration());
if ($timesheet->getActivity() === null) {
$timesheet->setActivity($entry->getActivity());
$timesheet->setEnd($entry->getEnd());
}
$timesheet->setBegin($entry->getBegin());
}
return [$timesheet];
}
}

View File

@@ -33,11 +33,11 @@
</div>
<div class="col-sm-4 invoice-col">
<p contenteditable="true">
<b>{{ 'invoice.number'|trans }} {{ model.numberGenerator.invoiceNumber }}</b>
<b>{{ 'invoice.number'|trans }}: {{ model.numberGenerator.invoiceNumber }}</b>
</p>
<p contenteditable="true">
<b>{{ 'invoice.due_days'|trans }}:</b> {{ model.dueDate|date(kimai_context.date_1) }}<br>
<b>{{ 'label.customer_number'|trans }}:</b> {{ model.customer.number }}<br>
{% if model.customer.number is not empty %}<b>{{ 'label.customer_number'|trans }}:</b> {{ model.customer.number }}<br>{% endif %}
</p>
</div>
</div>
@@ -54,7 +54,7 @@
</tr>
</thead>
<tbody>
{% for entry in model.entries %}
{% for entry in model.calculator.entries %}
<tr>
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
<td>{{ entry.activity.name }} / {{ entry.activity.project.name }}</td>

View File

@@ -0,0 +1,94 @@
{% import "macros/widgets.html.twig" as widgets %}
<div class="row">
<div class="col-xs-12">
<h2 class="page-header">
<span contenteditable="true">{{ model.template.title|default('invoice_renderer.timesheet'|trans) }}</span>
</h2>
</div>
</div>
<div class="row invoice-info" style="padding: 10px 0 20px 0">
<div class="col-sm-12 invoice-col">
<table>
<tr>
<th>{{ 'invoice.from'|trans }}</th>
<td contenteditable="true">
{% if model.query.user is not empty %}
{{ widgets.username(model.query.user) }}
{% else %}
{{ model.template.company }}
{% endif %}
</td>
</tr>
<tr>
<th style="width:50%">{{ 'label.date'|trans }}</th>
<td contenteditable="true">{{ model.invoiceDate|date('F Y') }}</td>
</tr>
<tr>
<th>{{ 'label.customer'|trans }}</th>
<td contenteditable="true">
{% if model.customer.number is not empty %}[{{ model.customer.number }}]{% endif %}
{{ model.customer.name }} / {{ model.customer.contact }}
</td>
</tr>
</table>
</div>
</div>
<div class="row">
<div class="col-xs-12 table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>{{ 'label.date'|trans }}</th>
<th>{{ 'label.activity'|trans }}</th>
<th>{{ 'label.hours'|trans }}</th>
</tr>
</thead>
<tbody>
{% for entry in model.calculator.entries %}
<tr>
<td>{{ entry.begin|date(kimai_context.date_1) }}</td>
<td>{{ entry.activity.name }} / {{ entry.activity.project.name }}</td>
<td>{{ entry.duration|duration }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="row" style="padding: 60px 0">
<div class="col-xs-12">
{% if model.template.paymentTerms is not empty %}
<p class="lead">{{ 'label.payment_terms'|trans }}</p>
<p class="text-muted well well-sm no-shadow" contenteditable="true">
{{ model.template.paymentTerms|trim|nl2br }}
</p>
{% endif %}
<div class="table-responsive">
<table class="table">
<tbody>
<tr>
<th style="width:50%;padding-bottom: 60px">{{ 'invoice.signature_user'|trans }}</th>
</tr>
<tr>
<th>{{ 'invoice.signature_customer'|trans }}</th>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="row no-print">
<div class="col-xs-12">
<button type="button" class="btn btn-success pull-right" onclick="window.print();return false;" style="margin-right: 5px;">
<i class="fa fa-print"></i> {{ 'button.print'|trans }}
</button>
</div>
</div>

View File

@@ -673,7 +673,11 @@
</trans-unit>
<trans-unit id="invoice_renderer.default">
<source>invoice_renderer.default</source>
<target>HTML Rechnung (Standard)</target>
<target>Rechnung (Standard)</target>
</trans-unit>
<trans-unit id="invoice_renderer.timesheet">
<source>invoice_renderer.timesheet</source>
<target>Stundenzettel</target>
</trans-unit>
<trans-unit id="label.invoice_number_generator">
<source>label.invoice_number_generator</source>
@@ -695,6 +699,18 @@
<source>invoice_calculator.default</source>
<target>Stundenbasiert (Standard)</target>
</trans-unit>
<trans-unit id="invoice_calculator.short">
<source>invoice_calculator.short</source>
<target>Stundenbasiert - Kurzversion (nur ein Eintrag)</target>
</trans-unit>
<trans-unit id="invoice.signature_user">
<source>invoice.signature_user</source>
<target>Leistungsbestätigung: Datum / Name Berater / Unterschrift</target>
</trans-unit>
<trans-unit id="invoice.signature_customer">
<source>invoice.signature_customer</source>
<target>Leistungsbestätigung: Datum / Name Kunde / Unterschrift</target>
</trans-unit>
<!--
Month names

View File

@@ -673,7 +673,11 @@
</trans-unit>
<trans-unit id="invoice_renderer.default">
<source>invoice_renderer.default</source>
<target>HTML Invoice (default)</target>
<target>Invoice (default)</target>
</trans-unit>
<trans-unit id="invoice_renderer.timesheet">
<source>invoice_renderer.timesheet</source>
<target>Timesheet</target>
</trans-unit>
<trans-unit id="label.invoice_number_generator">
<source>label.invoice_number_generator</source>
@@ -695,6 +699,18 @@
<source>invoice_calculator.default</source>
<target>Hourly rates (default)</target>
</trans-unit>
<trans-unit id="invoice_calculator.short">
<source>invoice_calculator.short</source>
<target>Hourly rates - short version (only one record)</target>
</trans-unit>
<trans-unit id="invoice.signature_user">
<source>invoice.signature_user</source>
<target>Confirmation: Date / Name Consultant / Signature</target>
</trans-unit>
<trans-unit id="invoice.signature_customer">
<source>invoice.signature_customer</source>
<target>Confirmation: Date / Name Customer / Signature</target>
</trans-unit>
<!--
Month names