mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
PnL Expense tests
This commit is contained in:
parent
fd67d8202e
commit
7df6b8f940
@ -36,9 +36,15 @@ class ProfitLoss
|
|||||||
|
|
||||||
private float $credit = 0;
|
private float $credit = 0;
|
||||||
|
|
||||||
|
private float $credit_invoice = 0;
|
||||||
|
|
||||||
private float $credit_taxes = 0;
|
private float $credit_taxes = 0;
|
||||||
|
|
||||||
private array $expenses;
|
private array $invoice_payment_map = [];
|
||||||
|
|
||||||
|
private array $expenses = [];
|
||||||
|
|
||||||
|
private array $expense_break_down = [];
|
||||||
|
|
||||||
private array $income_map;
|
private array $income_map;
|
||||||
|
|
||||||
@ -94,7 +100,7 @@ class ProfitLoss
|
|||||||
$this->filterInvoicePaymentIncome();
|
$this->filterInvoicePaymentIncome();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->expenseData();
|
$this->expenseData()->buildExpenseBreakDown();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -119,6 +125,11 @@ class ProfitLoss
|
|||||||
return $this->expenses;
|
return $this->expenses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getExpenseBreakDown() :array
|
||||||
|
{
|
||||||
|
return $this->expense_break_down;
|
||||||
|
}
|
||||||
|
|
||||||
private function filterIncome()
|
private function filterIncome()
|
||||||
{
|
{
|
||||||
$invoices = $this->invoiceIncome();
|
$invoices = $this->invoiceIncome();
|
||||||
@ -139,17 +150,27 @@ class ProfitLoss
|
|||||||
private function filterInvoicePaymentIncome()
|
private function filterInvoicePaymentIncome()
|
||||||
{
|
{
|
||||||
|
|
||||||
$invoices = $this->invoicePaymentIncome();
|
$this->paymentEloquentIncome();
|
||||||
|
|
||||||
$this->income = 0;
|
foreach($this->invoice_payment_map as $map) {
|
||||||
$this->income_taxes = 0;
|
$this->income += $map->amount_payment_paid_converted - $map->tax_amount_converted;
|
||||||
$this->income_map = $invoices;
|
$this->income_taxes += $map->tax_amount_converted;
|
||||||
|
|
||||||
foreach($invoices as $invoice){
|
$this->credit += $map->amount_credit_paid_converted - $map->tax_amount_credit_converted;
|
||||||
$this->income += $invoice->net_converted_amount;
|
$this->credit_taxes += $map->tax_amount_credit_converted;
|
||||||
$this->income_taxes += $invoice->net_converted_taxes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// $invoices = $this->invoicePaymentIncome();
|
||||||
|
|
||||||
|
// $this->income = 0;
|
||||||
|
// $this->income_taxes = 0;
|
||||||
|
// $this->income_map = $invoices;
|
||||||
|
|
||||||
|
// foreach($invoices as $invoice){
|
||||||
|
// $this->income += $invoice->net_converted_amount;
|
||||||
|
// $this->income_taxes += $invoice->net_converted_taxes;
|
||||||
|
// }
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -214,8 +235,7 @@ class ProfitLoss
|
|||||||
private function paymentEloquentIncome()
|
private function paymentEloquentIncome()
|
||||||
{
|
{
|
||||||
|
|
||||||
$amount_payment_paid = 0;
|
$this->invoice_payment_map = [];
|
||||||
$amount_credit_paid = 0;
|
|
||||||
|
|
||||||
Payment::where('company_id', $this->company->id)
|
Payment::where('company_id', $this->company->id)
|
||||||
->whereIn('status_id', [1,4,5])
|
->whereIn('status_id', [1,4,5])
|
||||||
@ -226,32 +246,65 @@ class ProfitLoss
|
|||||||
})
|
})
|
||||||
->with(['company','client'])
|
->with(['company','client'])
|
||||||
->cursor()
|
->cursor()
|
||||||
->each(function ($payment) use($amount_payment_paid, $amount_credit_paid){
|
->each(function ($payment){
|
||||||
|
|
||||||
$company = $payment->company;
|
$company = $payment->company;
|
||||||
$client = $payment->client;
|
$client = $payment->client;
|
||||||
|
|
||||||
|
$map = new \stdClass;
|
||||||
|
$amount_payment_paid = 0;
|
||||||
|
$amount_credit_paid = 0;
|
||||||
|
$amount_payment_paid_converted = 0;
|
||||||
|
$amount_credit_paid_converted = 0;
|
||||||
|
$tax_amount = 0;
|
||||||
|
$tax_amount_converted = 0;
|
||||||
|
$tax_amount_credit = 0;
|
||||||
|
$tax_amount_credit_converted = $tax_amount_credit_converted = 0;
|
||||||
|
|
||||||
foreach($payment->paymentables as $pivot)
|
foreach($payment->paymentables as $pivot)
|
||||||
{
|
{
|
||||||
|
|
||||||
if($pivot->paymentable instanceOf \App\Models\Invoice){
|
if($pivot->paymentable instanceOf \App\Models\Invoice){
|
||||||
|
|
||||||
|
$invoice = $pivot->paymentable;
|
||||||
|
|
||||||
$amount_payment_paid += $pivot->amount - $pivot->refunded;
|
$amount_payment_paid += $pivot->amount - $pivot->refunded;
|
||||||
//calc tax amount - pro rata if necessary
|
$amount_payment_paid_converted += $amount_payment_paid / ($payment->exchange_rate ?: 1);
|
||||||
|
|
||||||
|
$tax_amount += ($amount_payment_paid / $invoice->amount) * $invoice->total_taxes;
|
||||||
|
$tax_amount_converted += (($amount_payment_paid / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if($pivot->paymentable instanceOf \App\Models\Credit){
|
if($pivot->paymentable instanceOf \App\Models\Credit){
|
||||||
|
|
||||||
$amount_credit_paid += $pivot->amount - $pivot->refunded;
|
$amount_credit_paid += $pivot->amount - $pivot->refunded;
|
||||||
|
$amount_credit_paid_converted += $amount_payment_paid / ($payment->exchange_rate ?: 1);
|
||||||
|
|
||||||
|
$tax_amount_credit += ($amount_payment_paid / $invoice->amount) * $invoice->total_taxes;
|
||||||
|
$tax_amount_credit_converted += (($amount_payment_paid / $invoice->amount) * $invoice->total_taxes) / $payment->exchange_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
$map->amount_payment_paid = $amount_payment_paid;
|
||||||
}
|
$map->amount_payment_paid_converted = $amount_payment_paid_converted;
|
||||||
|
$map->tax_amount = $tax_amount;
|
||||||
|
$map->tax_amount_converted = $tax_amount_converted;
|
||||||
|
$map->amount_credit_paid = $amount_credit_paid;
|
||||||
|
$map->amount_credit_paid_converted = $amount_credit_paid_converted;
|
||||||
|
$map->tax_amount_credit = $tax_amount_credit;
|
||||||
|
$map->tax_amount_credit_converted = $tax_amount_credit_converted;
|
||||||
|
$map->currency_id = $payment->currency_id;
|
||||||
|
|
||||||
|
$this->invoice_payment_map[] = $map;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
=> [
|
=> [
|
||||||
@ -272,6 +325,7 @@ class ProfitLoss
|
|||||||
]
|
]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
private function invoicePaymentIncome()
|
private function invoicePaymentIncome()
|
||||||
{
|
{
|
||||||
return \DB::select( \DB::raw("
|
return \DB::select( \DB::raw("
|
||||||
@ -332,31 +386,52 @@ class ProfitLoss
|
|||||||
->cursor();
|
->cursor();
|
||||||
|
|
||||||
|
|
||||||
return $this->calculateExpenses($expenses);
|
$this->expenses = [];
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private function calculateExpenses($expenses)
|
|
||||||
{
|
|
||||||
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
|
|
||||||
foreach($expenses as $expense)
|
foreach($expenses as $expense)
|
||||||
{
|
{
|
||||||
$data[] = [
|
$map = new \stdClass;
|
||||||
'total' => $expense->amount,
|
|
||||||
'converted_total' => $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate),
|
$map->total = $expense->amount;
|
||||||
'tax' => $tax = $this->getTax($expense),
|
$map->converted_total = $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate);
|
||||||
'net_converted_total' => $expense->uses_inclusive_taxes ? ( $converted_total - $tax ) : $converted_total,
|
$map->tax = $tax = $this->getTax($expense);
|
||||||
'category_id' => $expense->category_id,
|
$map->net_converted_total = $expense->uses_inclusive_taxes ? ( $converted_total - $tax ) : $converted_total;
|
||||||
'category_name' => $expense->category ? $expense->category->name : "No Category Defined",
|
$map->category_id = $expense->category_id;
|
||||||
];
|
$map->category_name = $expense->category ? $expense->category->name : "No Category Defined";
|
||||||
|
$map->currency_id = $expense->currency_id ?: $expense->company->settings->currency_id;
|
||||||
|
|
||||||
|
$this->expenses[] = $map;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->expenses = $data;
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildExpenseBreakDown()
|
||||||
|
{
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
foreach($this->expenses as $expense)
|
||||||
|
{
|
||||||
|
if(!array_key_exists($expense->category_id, $data))
|
||||||
|
$data[$expense->category_id] = [];
|
||||||
|
|
||||||
|
if(!array_key_exists('total', $data[$expense->category_id]))
|
||||||
|
$data[$expense->category_id]['total'] = 0;
|
||||||
|
|
||||||
|
if(!array_key_exists('tax', $data[$expense->category_id]))
|
||||||
|
$data[$expense->category_id]['tax'] = 0;
|
||||||
|
|
||||||
|
$data[$expense->category_id]['total'] = $data[$expense->category_id]['total'] + $expense->net_converted_total;
|
||||||
|
$data[$expense->category_id]['category_name'] = $expense->category_name;
|
||||||
|
$data[$expense->category_id]['tax'] = $data[$expense->category_id]['tax'] + $expense->tax;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->expense_break_down = $data;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,11 +15,14 @@ use App\DataMapper\CompanySettings;
|
|||||||
use App\Factory\InvoiceFactory;
|
use App\Factory\InvoiceFactory;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
|
use App\Models\ClientContact;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
|
use App\Models\Expense;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Report\ProfitLoss;
|
use App\Services\Report\ProfitLoss;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Database\Factories\ClientContactFactory;
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use League\Csv\Writer;
|
use League\Csv\Writer;
|
||||||
@ -273,6 +276,10 @@ class ProfitAndLossReportTest extends TestCase
|
|||||||
'settings' => $settings,
|
'settings' => $settings,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$contact = ClientContact::factory()->create([
|
||||||
|
'client_id' => $client->id
|
||||||
|
]);
|
||||||
|
|
||||||
$i = Invoice::factory()->create([
|
$i = Invoice::factory()->create([
|
||||||
'client_id' => $client->id,
|
'client_id' => $client->id,
|
||||||
'user_id' => $this->user->id,
|
'user_id' => $this->user->id,
|
||||||
@ -304,4 +311,57 @@ class ProfitAndLossReportTest extends TestCase
|
|||||||
$this->account->delete();
|
$this->account->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testSimpleExpense()
|
||||||
|
{
|
||||||
|
$this->buildData();
|
||||||
|
|
||||||
|
$e = Expense::factory()->create([
|
||||||
|
'amount' => 10,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'date' => '2022-01-01',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pl = new ProfitLoss($this->company, $this->payload);
|
||||||
|
$pl->build();
|
||||||
|
|
||||||
|
$expenses = $pl->getExpenses();
|
||||||
|
|
||||||
|
$expense = $expenses[0];
|
||||||
|
|
||||||
|
$this->assertEquals(10, $expense->total);
|
||||||
|
|
||||||
|
$this->account->delete();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSimpleExpenseBreakdown()
|
||||||
|
{
|
||||||
|
$this->buildData();
|
||||||
|
|
||||||
|
$e = Expense::factory()->create([
|
||||||
|
'amount' => 10,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->user->id,
|
||||||
|
'date' => '2022-01-01',
|
||||||
|
'exchange_rate' => 1,
|
||||||
|
'currency_id' => $this->company->settings->currency_id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$pl = new ProfitLoss($this->company, $this->payload);
|
||||||
|
$pl->build();
|
||||||
|
|
||||||
|
$expenses = $pl->getExpenses();
|
||||||
|
|
||||||
|
$bd = $pl->getExpenseBreakDown();
|
||||||
|
|
||||||
|
$this->assertEquals(array_sum(array_column($bd,'total')), 10);
|
||||||
|
|
||||||
|
$this->account->delete();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user