1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-15 07:33:04 +01:00
invoiceninja/app/Helpers/Invoice/InvoiceSum.php

418 lines
11 KiB
PHP
Raw Normal View History

<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
2024-04-12 06:15:41 +02:00
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
*
2021-06-16 08:58:16 +02:00
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Helpers\Invoice;
2023-04-26 11:25:33 +02:00
use App\Models\Credit;
2020-10-28 11:10:49 +01:00
use App\Models\Invoice;
2023-04-26 11:25:33 +02:00
use App\Models\PurchaseOrder;
2023-10-26 04:57:44 +02:00
use App\Models\Quote;
2023-04-26 11:25:33 +02:00
use App\Models\RecurringInvoice;
2023-10-26 04:57:44 +02:00
use App\Models\RecurringQuote;
use App\Utils\Number;
2023-04-26 11:25:33 +02:00
use App\Utils\Traits\NumberFormatter;
2023-10-26 04:57:44 +02:00
use Illuminate\Support\Collection;
class InvoiceSum
{
use Taxer;
use CustomValuer;
use Discounter;
use NumberFormatter;
2023-04-26 12:24:10 +02:00
protected RecurringInvoice | Invoice | Quote | Credit | PurchaseOrder | RecurringQuote $invoice;
public $tax_map;
public $invoice_item;
2021-09-06 01:37:35 +02:00
public $total_taxes = 0;
private $total;
private $total_discount;
private $total_custom_values;
private $total_tax_map;
2019-10-17 00:51:05 +02:00
private $sub_total;
2021-09-15 01:02:25 +02:00
private $gross_sub_total;
2022-06-06 14:27:17 +02:00
private $precision;
public InvoiceItemSum $invoice_items;
2024-01-14 05:05:00 +01:00
2024-03-16 02:36:40 +01:00
private $rappen_rounding = false;
/**
* Constructs the object with Invoice and Settings object.
*
2023-04-26 13:18:01 +02:00
* @param RecurringInvoice | Invoice | Quote | Credit | PurchaseOrder | RecurringQuote $invoice;
*/
public function __construct($invoice)
{
$this->invoice = $invoice;
if ($this->invoice->client) {
2022-06-06 14:27:17 +02:00
$this->precision = $this->invoice->client->currency()->precision;
2024-03-16 02:36:40 +01:00
$this->rappen_rounding = $this->invoice->client->getSetting('enable_rappen_rounding');
} else {
2022-06-07 13:07:14 +02:00
$this->precision = $this->invoice->vendor->currency()->precision;
2024-03-16 02:36:40 +01:00
$this->rappen_rounding = $this->invoice->vendor->getSetting('enable_rappen_rounding');
}
2021-10-13 06:47:56 +02:00
2024-01-14 05:05:00 +01:00
$this->tax_map = new Collection();
}
public function build()
{
$this->calculateLineItems()
->calculateDiscount()
->calculateInvoiceTaxes()
->calculateCustomValues()
->setTaxMap()
->calculateTotals()
->calculateBalance()
->calculatePartial();
return $this;
}
2023-04-26 11:25:33 +02:00
private function calculateLineItems(): self
{
$this->invoice_items = new InvoiceItemSum($this->invoice);
$this->invoice_items->process();
$this->invoice->line_items = $this->invoice_items->getLineItems();
$this->total = $this->invoice_items->getSubTotal();
$this->setSubTotal($this->invoice_items->getSubTotal());
2021-09-15 01:02:25 +02:00
$this->setGrossSubTotal($this->invoice_items->getGrossSubTotal());
return $this;
}
2023-04-26 11:25:33 +02:00
private function calculateDiscount(): self
{
$this->total_discount = $this->discount($this->invoice_items->getSubTotal());
$this->total -= $this->total_discount;
return $this;
}
2023-04-26 11:25:33 +02:00
private function calculateCustomValues(): self
{
2019-10-29 12:12:59 +01:00
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge1);
2019-10-29 12:12:59 +01:00
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge2);
2019-10-29 12:12:59 +01:00
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge3);
2019-10-29 12:12:59 +01:00
$this->total_custom_values += $this->valuer($this->invoice->custom_surcharge4);
$this->total += $this->total_custom_values;
return $this;
}
2023-04-26 11:25:33 +02:00
private function calculateInvoiceTaxes(): self
{
2024-03-02 00:14:36 +01:00
if (is_string($this->invoice->tax_name1) && strlen($this->invoice->tax_name1) >= 2) {
$tax = $this->taxer($this->total, $this->invoice->tax_rate1);
2021-04-10 01:59:19 +02:00
$tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name1, $this->invoice->tax_rate1);
$this->total_taxes += $tax;
$this->total_tax_map[] = ['name' => $this->invoice->tax_name1.' '.floatval($this->invoice->tax_rate1).'%', 'total' => $tax];
}
2024-03-02 00:14:36 +01:00
if (is_string($this->invoice->tax_name2) && strlen($this->invoice->tax_name2) >= 2) {
$tax = $this->taxer($this->total, $this->invoice->tax_rate2);
2021-04-10 01:59:19 +02:00
$tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name2, $this->invoice->tax_rate2);
$this->total_taxes += $tax;
$this->total_tax_map[] = ['name' => $this->invoice->tax_name2.' '.floatval($this->invoice->tax_rate2).'%', 'total' => $tax];
}
2024-03-02 00:14:36 +01:00
if (is_string($this->invoice->tax_name3) && strlen($this->invoice->tax_name3) >= 2) {
$tax = $this->taxer($this->total, $this->invoice->tax_rate3);
2021-04-10 01:59:19 +02:00
$tax += $this->getSurchargeTaxTotalForKey($this->invoice->tax_name3, $this->invoice->tax_rate3);
$this->total_taxes += $tax;
$this->total_tax_map[] = ['name' => $this->invoice->tax_name3.' '.floatval($this->invoice->tax_rate3).'%', 'total' => $tax];
}
return $this;
}
/**
* Calculates the balance.
*
* @return self The balance.
*/
2023-04-26 11:25:33 +02:00
private function calculateBalance(): self
{
$this->setCalculatedAttributes();
return $this;
}
2023-04-26 11:25:33 +02:00
private function calculatePartial(): self
{
if (! isset($this->invoice->id) && isset($this->invoice->partial)) {
2023-04-26 11:25:33 +02:00
$this->invoice->partial = max(0, min(Number::roundValue($this->invoice->partial, 2), $this->invoice->balance));
}
return $this;
}
2023-04-26 11:25:33 +02:00
private function calculateTotals(): self
{
$this->total += $this->total_taxes;
2019-10-17 02:25:57 +02:00
return $this;
}
/**
* Allow us to get the entity without persisting it
* @return Invoice the temp
*/
public function getTempEntity()
{
$this->setCalculatedAttributes();
return $this->invoice;
}
public function getInvoice()
{
//Build invoice values here and return Invoice
$this->setCalculatedAttributes();
2021-10-14 08:54:38 +02:00
$this->invoice->saveQuietly();
return $this->invoice;
}
public function getQuote()
{
$this->setCalculatedAttributes();
2021-10-14 08:54:38 +02:00
$this->invoice->saveQuietly();
return $this->invoice;
}
public function getCredit()
{
$this->setCalculatedAttributes();
2022-05-31 00:28:32 +02:00
$this->invoice->saveQuietly();
return $this->invoice;
}
public function getPurchaseOrder()
{
$this->setCalculatedAttributes();
2021-10-14 08:54:38 +02:00
$this->invoice->saveQuietly();
return $this->invoice;
}
2020-10-20 03:30:55 +02:00
public function getRecurringInvoice()
{
2024-04-19 07:10:48 +02:00
// $this->invoice->amount = $this->formatValue($this->getTotal(), $this->precision);
// $this->invoice->total_taxes = $this->getTotalTaxes();
$this->setCalculatedAttributes();
$this->invoice->balance = $this->invoice->amount;
2021-10-14 08:54:38 +02:00
$this->invoice->saveQuietly();
2020-10-20 03:30:55 +02:00
2024-04-19 07:10:48 +02:00
// $this->invoice->saveQuietly();
2020-10-20 03:30:55 +02:00
return $this->invoice;
}
/**
* Build $this->invoice variables after
* calculations have been performed.
*/
2023-04-26 11:25:33 +02:00
private function setCalculatedAttributes(): self
{
if($this->invoice->status_id == Invoice::STATUS_CANCELLED){
$this->invoice->balance = 0;
}
elseif ($this->invoice->status_id != Invoice::STATUS_DRAFT) {
2020-11-15 09:24:57 +01:00
if ($this->invoice->amount != $this->invoice->balance) {
2024-02-21 21:45:06 +01:00
$this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision) - $this->invoice->paid_to_date; //21-02-2024 cannot use the calculated $paid_to_date here as it could send the balance backward.
2020-11-15 09:24:57 +01:00
} else {
2023-04-26 11:25:33 +02:00
$this->invoice->balance = Number::roundValue($this->getTotal(), $this->precision);
2020-11-15 09:24:57 +01:00
}
}
/* Set new calculated total */
2022-06-06 14:27:17 +02:00
$this->invoice->amount = $this->formatValue($this->getTotal(), $this->precision);
2024-03-21 09:26:11 +01:00
if($this->rappen_rounding){
2024-03-16 02:36:40 +01:00
$this->invoice->amount = $this->roundRappen($this->invoice->amount);
2024-03-21 09:26:11 +01:00
$this->invoice->balance = $this->roundRappen($this->invoice->balance);
}
2024-03-16 02:36:40 +01:00
$this->invoice->total_taxes = $this->getTotalTaxes();
return $this;
}
2024-03-16 02:36:40 +01:00
function roundRappen($value): float
{
return round($value / .05, 0) * .05;
}
public function getSubTotal()
{
return $this->sub_total;
}
public function setSubTotal($value)
{
$this->sub_total = $value;
return $this;
}
2021-09-15 01:02:25 +02:00
public function getGrossSubTotal()
{
return $this->gross_sub_total;
}
2023-04-26 11:25:33 +02:00
public function setGrossSubTotal($value): self
2021-09-15 01:02:25 +02:00
{
$this->gross_sub_total = $value;
return $this;
}
public function getTotalDiscount()
{
return $this->total_discount;
}
public function getTotalTaxes()
{
return $this->total_taxes;
}
public function getTotalTaxMap()
{
return $this->total_tax_map;
}
public function getTotal()
{
return $this->total;
}
public function getTotalSurcharges()
{
return $this->total_custom_values;
}
2023-04-26 11:25:33 +02:00
public function setTaxMap(): self
{
2023-09-12 09:06:40 +02:00
if ($this->invoice->is_amount_discount) {
$this->invoice_items->calcTaxesWithAmountDiscount();
2023-09-12 09:06:40 +02:00
$this->invoice->line_items = $this->invoice_items->getLineItems();
}
$this->tax_map = collect();
$keys = $this->invoice_items->getGroupedTaxes()->pluck('key')->unique();
$values = $this->invoice_items->getGroupedTaxes();
foreach ($keys as $key) {
$tax_name = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->pluck('tax_name')->first();
$total_line_tax = $values->filter(function ($value, $k) use ($key) {
return $value['key'] == $key;
})->sum('total');
$this->tax_map[] = ['name' => $tax_name, 'total' => $total_line_tax];
$this->total_taxes += $total_line_tax;
}
return $this;
}
2021-04-10 01:59:19 +02:00
private function getSurchargeTaxTotalForKey($key, $rate)
{
$tax_component = 0;
if ($this->invoice->custom_surcharge_tax1) {
2021-04-10 01:59:19 +02:00
$tax_component += round($this->invoice->custom_surcharge1 * ($rate / 100), 2);
}
if ($this->invoice->custom_surcharge_tax2) {
2021-04-10 01:59:19 +02:00
$tax_component += round($this->invoice->custom_surcharge2 * ($rate / 100), 2);
}
if ($this->invoice->custom_surcharge_tax3) {
2021-04-10 01:59:19 +02:00
$tax_component += round($this->invoice->custom_surcharge3 * ($rate / 100), 2);
}
if ($this->invoice->custom_surcharge_tax4) {
2021-04-10 01:59:19 +02:00
$tax_component += round($this->invoice->custom_surcharge4 * ($rate / 100), 2);
}
2021-04-10 01:59:19 +02:00
return $tax_component;
}
public function getTaxMap()
{
return $this->tax_map;
}
public function getBalance()
{
return $this->invoice->balance;
}
public function getItemTotalTaxes()
{
return $this->getTotalTaxes();
}
2020-10-10 12:57:28 +02:00
2023-04-26 11:25:33 +02:00
public function purgeTaxes(): self
2020-10-15 11:41:59 +02:00
{
$line_items = collect($this->invoice->line_items);
2020-11-25 11:30:00 +01:00
$items = $line_items->map(function ($item) {
2020-10-15 11:41:59 +02:00
$item->tax_rate1 = 0;
$item->tax_rate2 = 0;
$item->tax_rate3 = 0;
$item->tax_name1 = '';
$item->tax_name2 = '';
$item->tax_name3 = '';
$item->discount = 0;
return $item;
});
$this->invoice->line_items = $items->toArray();
$this->build();
return $this;
}
}