1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 12:12:48 +01:00

Adjustments for unapplied payments on statements

This commit is contained in:
David Bomba 2024-10-07 10:39:10 +11:00
parent 9925d3498f
commit 806e4b3f30
8 changed files with 185 additions and 7 deletions

View File

@ -459,7 +459,7 @@ class CompanySettings extends BaseSettings
public $show_email_footer = true;
public $company_logo_size = '60';
public $company_logo_size = '';
public $show_paid_stamp = false;
@ -1072,6 +1072,12 @@ class CompanySettings extends BaseSettings
'$product.description',
'$product.quantity',
],
'statement_unapplied_columns' => [
'$payment.number',
'$payment.date',
'$payment.amount',
'$payment.payment_balance',
],
];
return json_decode(json_encode($variables));

View File

@ -101,6 +101,7 @@ class Statement
'payments' => $this->getPayments()->cursor(),
'credits' => $this->getCredits()->cursor(),
'aging' => $this->getAging(),
'unapplied' => $this->getUnapplied()->cursor(),
], \App\Services\PdfMaker\Design::STATEMENT),
'variables' => $variables,
'options' => [
@ -117,6 +118,8 @@ class Statement
$pdf = null;
$html = $maker->getCompiledHTML(true);
nlog($html);
if ($this->rollback) {
\DB::connection(config('database.default'))->rollBack();
$this->rollback = false;
@ -182,6 +185,7 @@ class Statement
'payments' => $this->options['show_payments_table'] ? $this->getPayments()->get() : collect([]),
'credits' => $this->options['show_credits_table'] ? $this->getCredits()->get() : collect([]),
'aging' => $this->options['show_aging_table'] ? $this->getAging() : collect([]),
'unapplied' => $this->options['show_payments_table'] ? $this->getPayments()->get() : collect([]),
]);
$html = $ts->getHtml();
@ -362,14 +366,25 @@ class Statement
{
return Payment::withTrashed()
->with('client.country', 'invoices')
->where('is_deleted', false)
->where('company_id', $this->client->company_id)
->where('client_id', $this->client->id)
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
->whereBetween('date', [Carbon::parse($this->options['start_date']), Carbon::parse($this->options['end_date'])])
->where('is_deleted', false)
->orderBy('date', 'ASC');
}
protected function getUnapplied(): Builder
{
return Payment::query()
->withTrashed()
->where('company_id', $this->client->company_id)
->where('client_id', $this->client->id)
->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED, Payment::STATUS_REFUNDED])
->where('is_deleted', 0)
->whereRaw('payments.amount > payments.applied');
}
/**
* The collection of credits for the statement.
*
@ -385,7 +400,6 @@ class Statement
->whereIn('status_id', [Credit::STATUS_SENT, Credit::STATUS_PARTIAL, Credit::STATUS_APPLIED])
->whereBetween('date', [Carbon::parse($this->options['start_date']), Carbon::parse($this->options['end_date'])])
->where(function ($query) {
// $query->whereDate('due_date', '>=', $this->options['end_date'])
$query->whereDate('due_date', '>=', now())
->orWhereNull('due_date');
})

View File

@ -30,6 +30,8 @@ class PdfBuilder
private CommonMarkConverter $commonmark;
private float $payment_amount_total = 0;
private float $unapplied_total = 0;
/**
* an array of sections to be injected into the template
*
@ -231,6 +233,14 @@ class PdfBuilder
'id' => 'statement-payment-table',
'elements' => $this->statementPaymentTable(),
],
'statement-unapplied-payment-table' => [
'id' => 'statement-unapplied-payment-table',
'elements' => $this->statementUnappliedPaymentTable(),
],
'statement-unapplied-payment-table-totals' => [
'id' => 'statement-unapplied-payment-table-totals',
'elements' => $this->statementUnappliedPaymentTableTotals(),
],
'statement-payment-table-totals' => [
'id' => 'statement-payment-table-totals',
'elements' => $this->statementPaymentTableTotals(),
@ -421,6 +431,71 @@ class PdfBuilder
];
}
public function statementUnappliedPaymentTableTotals():array
{
if (is_null($this->service->options['unapplied']) || !$this->service->options['unapplied']->first()) {
return [];
}
if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
return [];
}
$payment = $this->service->options['unapplied']->first();
return [
['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance'), $this->service->config->formatMoney($this->unapplied_total))],
['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->translatedType())],
['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
];
}
/**
* Generates the statement unapplied payments table
*
* @return array
*
*/
public function statementUnappliedPaymentTable(): array
{
if (is_null($this->service->options['unapplied']) || !$this->service->options['unapplied']->first()) {
return [];
}
if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
return [];
}
$tbody = [];
//24-03-2022 show payments per invoice
foreach ($this->service->options['unapplied'] as $unapplied_payment) {
if ($unapplied_payment->is_deleted) {
continue;
}
$element = ['element' => 'tr', 'elements' => []];
$element['elements'][] = ['element' => 'td', 'content' => $unapplied_payment->number];
$element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($unapplied_payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' '];
$element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($unapplied_payment->amount) ?: ' '];
$element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($unapplied_payment->amount - $unapplied_payment->applied) ?: ' '];
$tbody[] = $element;
$this->unapplied_total += round($unapplied_payment->amount - $unapplied_payment->applied,2);
}
return [
['element' => 'thead', 'elements' => $this->buildTableHeader('statement_unapplied')],
['element' => 'tbody', 'elements' => $tbody],
];
}
/**
* Generates the statement aging table
*

View File

@ -55,12 +55,16 @@ class Design extends BaseDesign
public $payments;
public $unapplied_payments;
public $settings_object;
public $company;
public float $payment_amount_total = 0;
public float $unapplied_total = 0;
/** @var array */
public $aging = [];
@ -154,12 +158,20 @@ class Design extends BaseDesign
],
'statement-credit-table-totals' => [
'id' => 'statement-credit-table-totals',
'elements' => $this->statementInvoiceTableTotals(),
'elements' => $this->statementCreditTableTotals(),
],
'statement-invoice-table' => [
'id' => 'statement-invoice-table',
'elements' => $this->statementInvoiceTable(),
],
'statement-unapplied-payment-table' => [
'id' => 'statement-unapplied-payment-table',
'elements' => $this->statementUnappliedPaymentTable(),
],
'statement-unapplied-payment-table-totals' => [
'id' => 'statement-unapplied-payment-table-totals',
'elements' => $this->statementUnappliedPaymentTableTotals(),
],
'statement-invoice-table-totals' => [
'id' => 'statement-invoice-table-totals',
'elements' => $this->statementInvoiceTableTotals(),
@ -670,6 +682,69 @@ class Design extends BaseDesign
];
}
public function statementUnappliedPaymentTableTotals():array
{
if (is_null($this->unapplied_payments) || !$this->unapplied_payments->first() || $this->type !== self::STATEMENT) {
return [];
}
if (\array_key_exists('show_payments_table', $this->options) && $this->options['show_payments_table'] === false) {
return [];
}
return [
['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_balance_on_file'), Number::formatMoney($this->unapplied_total, $this->client))],
];
}
/**
* Generates the statement unapplied payments table
*
* @return array
*
*/
public function statementUnappliedPaymentTable(): array
{
if (is_null($this->unapplied_payments) && $this->type !== self::STATEMENT) {
return [];
}
if (\array_key_exists('show_payments_table', $this->options) && $this->options['show_payments_table'] === false) {
return [];
}
$tbody = [];
//24-03-2022 show payments per invoice
foreach ($this->unapplied_payments as $unapplied_payment) {
if ($unapplied_payment->is_deleted) {
continue;
}
$element = ['element' => 'tr', 'elements' => []];
$element['elements'][] = ['element' => 'td', 'content' => $unapplied_payment->number];
$element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($unapplied_payment->date, $this->client->date_format(), $this->client->locale()) ?: ' '];
$element['elements'][] = ['element' => 'td', 'content' => Number::formatMoney($unapplied_payment->amount, $this->client) ?: ' '];
$element['elements'][] = ['element' => 'td', 'content' => Number::formatMoney($unapplied_payment->amount - $unapplied_payment->applied, $this->client) ?: ' '];
$tbody[] = $element;
$this->unapplied_total += round($unapplied_payment->amount - $unapplied_payment->applied, 2);
}
return [
['element' => 'thead', 'elements' => $this->buildTableHeader('statement_unapplied')],
['element' => 'tbody', 'elements' => $tbody],
];
}
public function statementAgingTable(): array
{
if ($this->type !== self::STATEMENT) {

View File

@ -54,6 +54,10 @@ trait DesignHelpers
$this->payments = $this->context['payments'];
}
if(isset($this->context['unapplied'])){
$this->unapplied_payments = $this->context['unapplied'];
}
if (isset($this->context['credits'])) {
$this->credits = $this->context['credits'];
}

View File

@ -443,7 +443,7 @@ class TemplateService
$processed = [];
if(in_array($key, ['tasks', 'projects', 'aging']) || !$value->first()) {
if(in_array($key, ['tasks', 'projects', 'aging', 'unapplied']) || !$value->first()) {
return $processed;
}
@ -491,6 +491,7 @@ class TemplateService
'projects' => $processed = $this->processProjects($value),
'purchase_orders' => $processed = $this->processPurchaseOrders($value),
'aging' => $processed = $value,
'unapplied' => $processed = $this->processPayments($value),
default => $processed = [],
};

View File

@ -737,6 +737,7 @@ class HtmlEngine
$data['$refund'] = ['value' => '', 'label' => ctrans('texts.refund')];
$data['$refunded'] = ['value' => '', 'label' => ctrans('texts.refunded')];
$data['$payment.payment_balance'] = ['value' => '', 'label' => ctrans('texts.payment_balance')];
$data['$payment.amount'] = ['value' => '', 'label' => ctrans('texts.payment')];
$data['$payment.date'] = ['value' => '', 'label' => ctrans('texts.payment_date')];
$data['$payment.number'] = ['value' => '', 'label' => ctrans('texts.payment_number')];

View File

@ -360,6 +360,8 @@
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-unapplied-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-unapplied-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-credit-table" cellspacing="0" data-ref="table"></table>
<div id="statement-credit-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
@ -390,8 +392,8 @@ $entity_images
document.addEventListener('DOMContentLoaded', () => {
let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-aging-table',
'statement-invoice-table', 'statement-payment-table', 'statement-unapplied-payment-table','statement-aging-table-totals',
'statement-invoice-table-totals', 'statement-payment-table-totals', 'statement-unapplied-payment-table-totals', 'statement-aging-table',
'client-details', 'vendor-details', 'swiss-qr', 'shipping-details', 'statement-credit-table', 'statement-credit-table-totals',
];