improved invoices with new renderer (#306)

* added docx renderer and demo template
* added csv renderer and demo template
* added xlsx renderer and demo template
* added ods renderer and demo template
* added user calculator
* added invoice documentation
This commit is contained in:
Kevin Papst
2018-09-21 01:27:56 +02:00
committed by GitHub
parent b035fc9ebd
commit 53f82a808b
121 changed files with 4094 additions and 770 deletions

View File

@@ -0,0 +1,111 @@
<?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\Invoice\Calculator;
use App\Entity\Timesheet;
use App\Model\InvoiceModel;
abstract class AbstractCalculator
{
/**
* @var string
*/
protected $currency;
/**
* @var InvoiceModel
*/
protected $model;
/**
* @return Timesheet[]
*/
abstract public function getEntries();
/**
* @return string
*/
abstract public function getId(): string;
/**
* @param InvoiceModel $model
*/
public function setModel(InvoiceModel $model)
{
$this->model = $model;
}
/**
* @return float
*/
public function getSubtotal(): float
{
$amount = 0;
foreach ($this->model->getEntries() as $entry) {
$amount += $entry->getRate();
}
return round($amount, 2);
}
/**
* @return float
*/
public function getVat(): ?float
{
return $this->model->getTemplate()->getVat();
}
/**
* @return float
*/
public function getTax(): float
{
$vat = $this->getVat();
if (0 == $vat) {
return 0;
}
$percent = $vat / 100.00;
return round($this->getSubtotal() * $percent, 2);
}
/**
* @return float
*/
public function getTotal(): float
{
return $this->getSubtotal() + $this->getTax();
}
/**
* @return string
*/
public function getCurrency(): string
{
return $this->model->getCustomer()->getCurrency();
}
/**
* Returns the total amount of worked time in seconds.
*
* @return int
*/
public function getTimeWorked(): int
{
$time = 0;
foreach ($this->model->getEntries() as $entry) {
$time += $entry->getDuration();
}
return $time;
}
}

View File

@@ -0,0 +1,51 @@
<?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\Invoice\Calculator;
use App\Entity\Timesheet;
abstract class AbstractMergedCalculator extends AbstractCalculator
{
/**
* @param Timesheet $timesheet
* @param Timesheet $entry
*/
protected function mergeTimesheets(Timesheet $timesheet, Timesheet $entry)
{
$timesheet->setUser($entry->getUser());
$timesheet->setFixedRate($entry->getFixedRate()); // FIXME invoice
$timesheet->setHourlyRate($entry->getHourlyRate()); // FIXME invoice
$timesheet->setRate($timesheet->getRate() + $entry->getRate());
$timesheet->setDuration($timesheet->getDuration() + $entry->getDuration());
if (null === $timesheet->getBegin() || $timesheet->getBegin()->getTimestamp() > $entry->getBegin()->getTimestamp()) {
$timesheet->setBegin($entry->getBegin());
}
if (null === $timesheet->getEnd() || $timesheet->getEnd()->getTimestamp() < $entry->getEnd()->getTimestamp()) {
$timesheet->setEnd($entry->getEnd());
}
if (null !== $this->model->getQuery()->getActivity()) {
$timesheet->setActivity($this->model->getQuery()->getActivity());
$timesheet->setDescription($this->model->getQuery()->getActivity()->getName());
} elseif (null !== $this->model->getQuery()->getProject()) {
$timesheet->setDescription($this->model->getQuery()->getProject()->getName());
}
if (null === $timesheet->getActivity()) {
$timesheet->setActivity($entry->getActivity());
}
if (empty($timesheet->getDescription())) {
$timesheet->setDescription($entry->getActivity()->getName());
}
}
}

View File

@@ -0,0 +1,38 @@
<?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\Invoice\Calculator;
use App\Entity\Timesheet;
use App\Invoice\CalculatorInterface;
/**
* Class DefaultCalculator works on all given entries using:
* - the customers currency
* - the invoice template vat rate
* - the entries rate
*/
class DefaultCalculator extends AbstractCalculator implements CalculatorInterface
{
/**
* @return Timesheet[]
*/
public function getEntries()
{
return $this->model->getEntries();
}
/**
* @return string
*/
public function getId(): string
{
return 'default';
}
}

View File

@@ -0,0 +1,47 @@
<?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\Invoice\Calculator;
use App\Entity\Timesheet;
use App\Invoice\CalculatorInterface;
/**
* A calculator that sums up all timesheet records from the model and returns only one
* entry for a compact invoice version.
*/
class ShortInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface
{
/**
* @return Timesheet[]
*/
public function getEntries()
{
$entries = $this->model->getEntries();
if (empty($entries)) {
return [];
}
$timesheet = new Timesheet();
foreach ($entries as $entry) {
$this->mergeTimesheets($timesheet, $entry);
}
return [$timesheet];
}
/**
* @return string
*/
public function getId(): string
{
return 'short';
}
}

View File

@@ -0,0 +1,51 @@
<?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\Invoice\Calculator;
use App\Entity\Timesheet;
use App\Invoice\CalculatorInterface;
/**
* A calculator that sums up the timesheet records by user.
*/
class UserInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface
{
/**
* @return Timesheet[]
*/
public function getEntries()
{
$entries = $this->model->getEntries();
if (empty($entries)) {
return [];
}
/** @var Timesheet[] $timesheets */
$timesheets = [];
foreach ($entries as $entry) {
if (!isset($timesheets[$entry->getUser()->getId()])) {
$timesheets[$entry->getUser()->getId()] = new Timesheet();
}
$timesheet = $timesheets[$entry->getUser()->getId()];
$this->mergeTimesheets($timesheet, $entry);
}
return array_values($timesheets);
}
/**
* @return string
*/
public function getId(): string
{
return 'user';
}
}