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_invoice = 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;
|
||||
|
||||
@ -94,7 +100,7 @@ class ProfitLoss
|
||||
$this->filterInvoicePaymentIncome();
|
||||
}
|
||||
|
||||
$this->expenseData();
|
||||
$this->expenseData()->buildExpenseBreakDown();
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -119,6 +125,11 @@ class ProfitLoss
|
||||
return $this->expenses;
|
||||
}
|
||||
|
||||
public function getExpenseBreakDown() :array
|
||||
{
|
||||
return $this->expense_break_down;
|
||||
}
|
||||
|
||||
private function filterIncome()
|
||||
{
|
||||
$invoices = $this->invoiceIncome();
|
||||
@ -139,17 +150,27 @@ class ProfitLoss
|
||||
private function filterInvoicePaymentIncome()
|
||||
{
|
||||
|
||||
$invoices = $this->invoicePaymentIncome();
|
||||
$this->paymentEloquentIncome();
|
||||
|
||||
$this->income = 0;
|
||||
$this->income_taxes = 0;
|
||||
$this->income_map = $invoices;
|
||||
foreach($this->invoice_payment_map as $map) {
|
||||
$this->income += $map->amount_payment_paid_converted - $map->tax_amount_converted;
|
||||
$this->income_taxes += $map->tax_amount_converted;
|
||||
|
||||
foreach($invoices as $invoice){
|
||||
$this->income += $invoice->net_converted_amount;
|
||||
$this->income_taxes += $invoice->net_converted_taxes;
|
||||
$this->credit += $map->amount_credit_paid_converted - $map->tax_amount_credit_converted;
|
||||
$this->credit_taxes += $map->tax_amount_credit_converted;
|
||||
}
|
||||
|
||||
// $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;
|
||||
|
||||
}
|
||||
@ -214,8 +235,7 @@ class ProfitLoss
|
||||
private function paymentEloquentIncome()
|
||||
{
|
||||
|
||||
$amount_payment_paid = 0;
|
||||
$amount_credit_paid = 0;
|
||||
$this->invoice_payment_map = [];
|
||||
|
||||
Payment::where('company_id', $this->company->id)
|
||||
->whereIn('status_id', [1,4,5])
|
||||
@ -226,32 +246,65 @@ class ProfitLoss
|
||||
})
|
||||
->with(['company','client'])
|
||||
->cursor()
|
||||
->each(function ($payment) use($amount_payment_paid, $amount_credit_paid){
|
||||
->each(function ($payment){
|
||||
|
||||
$company = $payment->company;
|
||||
$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)
|
||||
{
|
||||
|
||||
if($pivot->paymentable instanceOf \App\Models\Invoice){
|
||||
|
||||
$invoice = $pivot->paymentable;
|
||||
|
||||
$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){
|
||||
|
||||
$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()
|
||||
{
|
||||
return \DB::select( \DB::raw("
|
||||
@ -332,31 +386,52 @@ class ProfitLoss
|
||||
->cursor();
|
||||
|
||||
|
||||
return $this->calculateExpenses($expenses);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function calculateExpenses($expenses)
|
||||
{
|
||||
|
||||
$data = [];
|
||||
|
||||
$this->expenses = [];
|
||||
|
||||
foreach($expenses as $expense)
|
||||
{
|
||||
$data[] = [
|
||||
'total' => $expense->amount,
|
||||
'converted_total' => $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate),
|
||||
'tax' => $tax = $this->getTax($expense),
|
||||
'net_converted_total' => $expense->uses_inclusive_taxes ? ( $converted_total - $tax ) : $converted_total,
|
||||
'category_id' => $expense->category_id,
|
||||
'category_name' => $expense->category ? $expense->category->name : "No Category Defined",
|
||||
];
|
||||
$map = new \stdClass;
|
||||
|
||||
$map->total = $expense->amount;
|
||||
$map->converted_total = $converted_total = $this->getConvertedTotal($expense->amount, $expense->exchange_rate);
|
||||
$map->tax = $tax = $this->getTax($expense);
|
||||
$map->net_converted_total = $expense->uses_inclusive_taxes ? ( $converted_total - $tax ) : $converted_total;
|
||||
$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\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\User;
|
||||
use App\Services\Report\ProfitLoss;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Database\Factories\ClientContactFactory;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Csv\Writer;
|
||||
@ -273,6 +276,10 @@ class ProfitAndLossReportTest extends TestCase
|
||||
'settings' => $settings,
|
||||
]);
|
||||
|
||||
$contact = ClientContact::factory()->create([
|
||||
'client_id' => $client->id
|
||||
]);
|
||||
|
||||
$i = Invoice::factory()->create([
|
||||
'client_id' => $client->id,
|
||||
'user_id' => $this->user->id,
|
||||
@ -304,4 +311,57 @@ class ProfitAndLossReportTest extends TestCase
|
||||
$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