improved invoice handling for fixed rates (#1058)
This commit is contained in:
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace App\Invoice\Calculator;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Model\InvoiceModel;
|
||||
use App\Invoice\InvoiceItem;
|
||||
use App\Invoice\InvoiceModel;
|
||||
|
||||
abstract class AbstractCalculator
|
||||
{
|
||||
@@ -25,7 +25,7 @@ abstract class AbstractCalculator
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @return Timesheet[]
|
||||
* @return InvoiceItem[]
|
||||
*/
|
||||
abstract public function getEntries();
|
||||
|
||||
@@ -103,7 +103,9 @@ abstract class AbstractCalculator
|
||||
{
|
||||
$time = 0;
|
||||
foreach ($this->model->getEntries() as $entry) {
|
||||
$time += $entry->getDuration();
|
||||
if (null === $entry->getFixedRate()) {
|
||||
$time += $entry->getDuration();
|
||||
}
|
||||
}
|
||||
|
||||
return $time;
|
||||
|
||||
@@ -10,52 +10,60 @@
|
||||
namespace App\Invoice\Calculator;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Invoice\InvoiceItem;
|
||||
|
||||
abstract class AbstractMergedCalculator extends AbstractCalculator
|
||||
{
|
||||
/**
|
||||
* @param Timesheet $timesheet
|
||||
* @param Timesheet $entry
|
||||
*/
|
||||
protected function mergeTimesheets(Timesheet $timesheet, Timesheet $entry)
|
||||
protected function mergeTimesheets(InvoiceItem $invoiceItem, Timesheet $entry)
|
||||
{
|
||||
$timesheet->setUser($entry->getUser());
|
||||
$timesheet->setRate($timesheet->getRate() + $entry->getRate());
|
||||
$invoiceItem->setAmount($invoiceItem->getAmount() + 1);
|
||||
$invoiceItem->setUser($entry->getUser());
|
||||
$invoiceItem->setRate($invoiceItem->getRate() + $entry->getRate());
|
||||
$invoiceItem->setDuration($invoiceItem->getDuration() + $entry->getDuration());
|
||||
|
||||
if (null !== $timesheet->getFixedRate() || null !== $entry->getFixedRate()) {
|
||||
$timesheet->setFixedRate($timesheet->getRate());
|
||||
}
|
||||
if (null === $timesheet->getHourlyRate() && null !== $entry->getHourlyRate() && $timesheet->getHourlyRate() !== $entry->getHourlyRate()) {
|
||||
$timesheet->setHourlyRate($entry->getHourlyRate());
|
||||
if (null !== $entry->getFixedRate()) {
|
||||
/*
|
||||
if (null !== $invoiceItem->getFixedRate() && $invoiceItem->getFixedRate() !== $entry->getFixedRate()) {
|
||||
throw new \InvalidArgumentException('Cannot mix different fixed-rates');
|
||||
}
|
||||
*/
|
||||
$invoiceItem->setFixedRate($entry->getFixedRate());
|
||||
}
|
||||
|
||||
$timesheet->setDuration($timesheet->getDuration() + $entry->getDuration());
|
||||
|
||||
if (null === $timesheet->getBegin() || $timesheet->getBegin()->getTimestamp() > $entry->getBegin()->getTimestamp()) {
|
||||
$timesheet->setBegin($entry->getBegin());
|
||||
if (null !== $entry->getHourlyRate()) {
|
||||
/*
|
||||
if (null !== $invoiceItem->getHourlyRate() && $invoiceItem->getHourlyRate() !== $entry->getHourlyRate()) {
|
||||
throw new \InvalidArgumentException('Cannot mix different hourly-rates');
|
||||
}
|
||||
*/
|
||||
$invoiceItem->setHourlyRate($entry->getHourlyRate());
|
||||
}
|
||||
|
||||
if (null === $timesheet->getEnd() || $timesheet->getEnd()->getTimestamp() < $entry->getEnd()->getTimestamp()) {
|
||||
$timesheet->setEnd($entry->getEnd());
|
||||
if (null === $invoiceItem->getBegin() || $invoiceItem->getBegin()->getTimestamp() > $entry->getBegin()->getTimestamp()) {
|
||||
$invoiceItem->setBegin($entry->getBegin());
|
||||
}
|
||||
|
||||
if (null === $invoiceItem->getEnd() || $invoiceItem->getEnd()->getTimestamp() < $entry->getEnd()->getTimestamp()) {
|
||||
$invoiceItem->setEnd($entry->getEnd());
|
||||
}
|
||||
|
||||
if (null !== $this->model->getQuery()->getActivity()) {
|
||||
$timesheet->setActivity($this->model->getQuery()->getActivity());
|
||||
$timesheet->setDescription($this->model->getQuery()->getActivity()->getName());
|
||||
$invoiceItem->setActivity($this->model->getQuery()->getActivity());
|
||||
$invoiceItem->setDescription($this->model->getQuery()->getActivity()->getName());
|
||||
} elseif (null !== $this->model->getQuery()->getProject()) {
|
||||
$timesheet->setDescription($this->model->getQuery()->getProject()->getName());
|
||||
$invoiceItem->setDescription($this->model->getQuery()->getProject()->getName());
|
||||
}
|
||||
|
||||
if (null === $timesheet->getActivity()) {
|
||||
$timesheet->setActivity($entry->getActivity());
|
||||
if (null === $invoiceItem->getActivity()) {
|
||||
$invoiceItem->setActivity($entry->getActivity());
|
||||
}
|
||||
|
||||
if (null === $timesheet->getProject()) {
|
||||
$timesheet->setProject($entry->getProject());
|
||||
if (null === $invoiceItem->getProject()) {
|
||||
$invoiceItem->setProject($entry->getProject());
|
||||
}
|
||||
|
||||
if (empty($timesheet->getDescription())) {
|
||||
$timesheet->setDescription($entry->getActivity()->getName());
|
||||
if (empty($invoiceItem->getDescription())) {
|
||||
$invoiceItem->setDescription($entry->getActivity()->getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,16 +11,17 @@ namespace App\Invoice\Calculator;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Invoice\CalculatorInterface;
|
||||
use App\Invoice\InvoiceItem;
|
||||
|
||||
/**
|
||||
* A calculator that sums up the timesheet records by activity.
|
||||
* An abstract calculator that sums up the timesheet records.
|
||||
*/
|
||||
abstract class AbstractSumInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface
|
||||
{
|
||||
abstract protected function calculateSumIdentifier(Timesheet $timesheet): string;
|
||||
|
||||
/**
|
||||
* @return Timesheet[]
|
||||
* @return InvoiceItem[]
|
||||
*/
|
||||
public function getEntries()
|
||||
{
|
||||
@@ -29,18 +30,25 @@ abstract class AbstractSumInvoiceCalculator extends AbstractMergedCalculator imp
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var Timesheet[] $timesheets */
|
||||
$timesheets = [];
|
||||
/** @var InvoiceItem[] $invoiceItems */
|
||||
$invoiceItems = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$id = $this->calculateSumIdentifier($entry);
|
||||
if (!isset($timesheets[$id])) {
|
||||
$timesheets[$id] = new Timesheet();
|
||||
|
||||
if (null !== $entry->getFixedRate()) {
|
||||
$id = $id . '_fixed_' . (string) $entry->getFixedRate();
|
||||
} else {
|
||||
$id = $id . '_hourly_' . (string) $entry->getHourlyRate();
|
||||
}
|
||||
$timesheet = $timesheets[$id];
|
||||
|
||||
if (!isset($invoiceItems[$id])) {
|
||||
$invoiceItems[$id] = new InvoiceItem();
|
||||
}
|
||||
$timesheet = $invoiceItems[$id];
|
||||
$this->mergeTimesheets($timesheet, $entry);
|
||||
}
|
||||
|
||||
return array_values($timesheets);
|
||||
return array_values($invoiceItems);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ class DateInvoiceCalculator extends AbstractSumInvoiceCalculator implements Calc
|
||||
{
|
||||
protected function calculateSumIdentifier(Timesheet $timesheet): string
|
||||
{
|
||||
if (null === $timesheet->getBegin()) {
|
||||
throw new \Exception('Cannot handle timesheets without start date');
|
||||
}
|
||||
|
||||
return $timesheet->getBegin()->format('Y-m-d');
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace App\Invoice\Calculator;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Invoice\CalculatorInterface;
|
||||
use App\Invoice\InvoiceItem;
|
||||
|
||||
/**
|
||||
* Class DefaultCalculator works on all given entries using:
|
||||
@@ -18,14 +18,25 @@ use App\Invoice\CalculatorInterface;
|
||||
* - the invoice template vat rate
|
||||
* - the entries rate
|
||||
*/
|
||||
class DefaultCalculator extends AbstractCalculator implements CalculatorInterface
|
||||
class DefaultCalculator extends AbstractMergedCalculator implements CalculatorInterface
|
||||
{
|
||||
/**
|
||||
* @return Timesheet[]
|
||||
* @return InvoiceItem[]
|
||||
*/
|
||||
public function getEntries()
|
||||
{
|
||||
return $this->model->getEntries();
|
||||
$entries = [];
|
||||
|
||||
foreach ($this->model->getEntries() as $entry) {
|
||||
$item = new InvoiceItem();
|
||||
$this->mergeTimesheets($item, $entry);
|
||||
foreach ($entry->getVisibleMetaFields() as $field) {
|
||||
$item->addAdditionalField($field->getName(), $field->getValue());
|
||||
}
|
||||
$entries[] = $item;
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace App\Invoice\Calculator;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Invoice\CalculatorInterface;
|
||||
use App\Invoice\InvoiceItem;
|
||||
|
||||
/**
|
||||
* A calculator that sums up all timesheet records from the model and returns only one
|
||||
@@ -19,7 +20,7 @@ use App\Invoice\CalculatorInterface;
|
||||
class ShortInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface
|
||||
{
|
||||
/**
|
||||
* @return Timesheet[]
|
||||
* @return InvoiceItem[]
|
||||
*/
|
||||
public function getEntries()
|
||||
{
|
||||
@@ -28,16 +29,27 @@ class ShortInvoiceCalculator extends AbstractMergedCalculator implements Calcula
|
||||
return [];
|
||||
}
|
||||
|
||||
$timesheet = new Timesheet();
|
||||
$invoiceItem = new InvoiceItem();
|
||||
$keys = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$this->mergeTimesheets($timesheet, $entry);
|
||||
$key = 'hourly_' . (string) $entry->getHourlyRate();
|
||||
if (null !== $entry->getFixedRate()) {
|
||||
$key = 'fixed_' . (string) $entry->getFixedRate();
|
||||
}
|
||||
if (!in_array($key, $keys)) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
$this->mergeTimesheets($invoiceItem, $entry);
|
||||
}
|
||||
|
||||
$timesheet->setFixedRate($timesheet->getRate());
|
||||
$timesheet->setHourlyRate($timesheet->getRate());
|
||||
if (count($keys) > 1) {
|
||||
$invoiceItem->setAmount(1);
|
||||
$invoiceItem->setFixedRate($invoiceItem->getRate());
|
||||
}
|
||||
$invoiceItem->setHourlyRate($invoiceItem->getRate());
|
||||
|
||||
return [$timesheet];
|
||||
return [$invoiceItem];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
namespace App\Invoice\Calculator;
|
||||
|
||||
use App\Entity\Timesheet;
|
||||
use App\Invoice\CalculatorInterface;
|
||||
use App\Invoice\InvoiceItem;
|
||||
|
||||
/**
|
||||
* A calculator that sums up the timesheet records by user.
|
||||
@@ -18,7 +18,7 @@ use App\Invoice\CalculatorInterface;
|
||||
class UserInvoiceCalculator extends AbstractMergedCalculator implements CalculatorInterface
|
||||
{
|
||||
/**
|
||||
* @return Timesheet[]
|
||||
* @return InvoiceItem[]
|
||||
*/
|
||||
public function getEntries()
|
||||
{
|
||||
@@ -27,18 +27,19 @@ class UserInvoiceCalculator extends AbstractMergedCalculator implements Calculat
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var Timesheet[] $timesheets */
|
||||
$timesheets = [];
|
||||
/** @var InvoiceItem[] $invoiceItems */
|
||||
$invoiceItems = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (!isset($timesheets[$entry->getUser()->getId()])) {
|
||||
$timesheets[$entry->getUser()->getId()] = new Timesheet();
|
||||
$id = $entry->getUser()->getId();
|
||||
if (!isset($invoiceItems[$id])) {
|
||||
$invoiceItems[$id] = new InvoiceItem();
|
||||
}
|
||||
$timesheet = $timesheets[$entry->getUser()->getId()];
|
||||
$this->mergeTimesheets($timesheet, $entry);
|
||||
$invoiceItem = $invoiceItems[$id];
|
||||
$this->mergeTimesheets($invoiceItem, $entry);
|
||||
}
|
||||
|
||||
return array_values($timesheets);
|
||||
return array_values($invoiceItems);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user