diff --git a/app/Jobs/Cron/InvoiceTaxSummary.php b/app/Jobs/Cron/InvoiceTaxSummary.php
index 5b2e7e5bf2..8a0731b4a6 100644
--- a/app/Jobs/Cron/InvoiceTaxSummary.php
+++ b/app/Jobs/Cron/InvoiceTaxSummary.php
@@ -12,19 +12,21 @@
namespace App\Jobs\Cron;
+use Carbon\Carbon;
+use App\Models\Company;
use App\Models\Invoice;
use App\Models\Webhook;
-use App\Models\Company;
use App\Models\Timezone;
use App\Libraries\MultiDB;
use Illuminate\Bus\Queueable;
use App\Jobs\Entity\EmailEntity;
+use App\Models\TransactionEvent;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
-use Carbon\Carbon;
use App\Listeners\Invoice\InvoiceTransactionEventEntry;
+use App\Listeners\Invoice\InvoiceTransactionEventEntryAccrual;
class InvoiceTaxSummary implements ShouldQueue
{
@@ -51,7 +53,6 @@ class InvoiceTaxSummary implements ShouldQueue
$companies = $this->getCompaniesInTimezones($transitioningTimezones);
foreach ($companies as $company) {
-
$this->processCompanyTaxSummary($company);
}
}
@@ -64,6 +65,7 @@ class InvoiceTaxSummary implements ShouldQueue
// Get all timezones from the database
$timezones = app('timezones');
+ /** @var \App\Models\Timezone $timezone */
foreach ($timezones as $timezone) {
// Calculate the current UTC offset for this timezone (accounting for DST)
$currentOffset = $this->getCurrentUtcOffset($timezone->name);
@@ -152,11 +154,45 @@ class InvoiceTaxSummary implements ShouldQueue
->whereBetween('date', [$startDate, $endDate])
->whereDoesntHave('transaction_events', function ($query) use ($todayStart, $todayEnd) {
$query->where('timestamp', '>=', $todayStart)
- ->where('timestamp', '<=', $todayEnd);
+ ->where('timestamp', '<=', $todayEnd)
+ ->where('event_id', TransactionEvent::INVOICE_UPDATED);
})
->cursor()
->each(function (Invoice $invoice) {
(new InvoiceTransactionEventEntry())->run($invoice);
});
+
+ Invoice::withTrashed()
+ ->with('payments')
+ ->where('company_id', $company->id)
+ ->whereIn('status_id', [3,4,5]) // Paid statuses
+ ->where('is_deleted', 0)
+ ->whereColumn('amount', '!=', 'balance')
+ ->whereHas('client', function ($query) {
+ $query->where('is_deleted', false);
+ })
+ ->whereHas('company', function ($query) {
+ $query->where('is_disabled', 0)
+ ->whereHas('account', function ($q) {
+ $q->where('is_flagged', false);
+ });
+ })
+ ->whereHas('payments', function ($query) use ($startDate, $endDate) {
+ $query->whereHas('paymentables', function ($subQuery) use ($startDate, $endDate) {
+ $subQuery->where('paymentable_type', Invoice::class)
+ ->whereBetween('created_at', [$startDate . ' 00:00:00', $endDate . ' 23:59:59']);
+ });
+ })
+ ->whereDoesntHave('transaction_events', function ($q) use ($todayStart, $todayEnd) {
+ $q->where('event_id', TransactionEvent::PAYMENT_CASH)
+ ->where('timestamp', '>=', $todayStart)
+ ->where('timestamp', '<=', $todayEnd);
+ })
+ ->cursor()
+ ->each(function (Invoice $invoice) use ($startDate, $endDate) {
+ (new InvoiceTransactionEventEntryAccrual())->run($invoice, $startDate, $endDate);
+ });
+
}
+
}
\ No newline at end of file
diff --git a/app/Listeners/Invoice/InvoiceTransactionEventEntry.php b/app/Listeners/Invoice/InvoiceTransactionEventEntry.php
index 6580b047ef..6f3da81f86 100644
--- a/app/Listeners/Invoice/InvoiceTransactionEventEntry.php
+++ b/app/Listeners/Invoice/InvoiceTransactionEventEntry.php
@@ -64,7 +64,7 @@ class InvoiceTransactionEventEntry
'invoice_partial' => $invoice->partial ?? 0,
'invoice_paid_to_date' => $invoice->paid_to_date ?? 0,
'invoice_status' => $invoice->is_deleted ? 7 : $invoice->status_id,
- 'event_id' => $invoice->is_deleted ? TransactionEvent::INVOICE_DELETED : TransactionEvent::INVOICE_UPDATED,
+ 'event_id' => TransactionEvent::INVOICE_UPDATED,
'timestamp' => now()->timestamp,
'metadata' => $this->getMetadata($invoice),
'period' => now()->endOfMonth()->format('Y-m-d'),
diff --git a/app/Listeners/Invoice/InvoiceTransactionEventEntryAccrual.php b/app/Listeners/Invoice/InvoiceTransactionEventEntryAccrual.php
new file mode 100644
index 0000000000..ffa3ec1679
--- /dev/null
+++ b/app/Listeners/Invoice/InvoiceTransactionEventEntryAccrual.php
@@ -0,0 +1,227 @@
+setPaidRatio($invoice);
+
+ $this->payments = $invoice->payments->flatMap(function ($payment) use ($start_date, $end_date) {
+ return $payment->invoices()->get()->map(function ($invoice) use ($payment) {
+ return [
+ 'number' => $payment->number,
+ 'amount' => $invoice->pivot->amount,
+ 'refunded' => $invoice->pivot->refunded,
+ 'date' => $invoice->pivot->created_at->format('Y-m-d'),
+ ];
+ })->filter(function ($payment) use ($start_date, $end_date) {
+ // Filter payments where the pivot created_at is within the date boundaries
+ return \Carbon\Carbon::parse($payment['date'])->isBetween($start_date, $end_date);
+ });
+ });
+
+ TransactionEvent::create([
+ 'invoice_id' => $invoice->id,
+ 'client_id' => $invoice->client_id,
+ 'client_balance' => $invoice->client->balance,
+ 'client_paid_to_date' => $invoice->client->paid_to_date,
+ 'client_credit_balance' => $invoice->client->credit_balance,
+ 'invoice_balance' => $invoice->balance ?? 0,
+ 'invoice_amount' => $invoice->amount ?? 0 ,
+ 'invoice_partial' => $invoice->partial ?? 0,
+ 'invoice_paid_to_date' => $invoice->paid_to_date ?? 0,
+ 'invoice_status' => $invoice->is_deleted ? 7 : $invoice->status_id,
+ 'event_id' => TransactionEvent::INVOICE_UPDATED,
+ 'timestamp' => now()->timestamp,
+ 'metadata' => $this->getMetadata($invoice),
+ 'period' => now()->endOfMonth()->format('Y-m-d'),
+ ]);
+ }
+
+ private function setPaidRatio(Invoice $invoice): self
+ {
+ if ($invoice->amount == 0) {
+ $this->paid_ratio = 0;
+ return $this;
+ }
+
+ $this->paid_ratio = $invoice->paid_to_date / $invoice->amount;
+
+ return $this;
+ }
+
+ private function calculateRatio(float $amount): float
+ {
+ return round($amount * $this->paid_ratio, 2);
+ }
+
+ /**
+ * Existing tax details are not deleted, but pending taxes are set to 0
+ *
+ * @param mixed $invoice
+ */
+ private function getCancelledMetaData($invoice)
+ {
+
+ $calc = $invoice->calc();
+
+ $details = [];
+
+ $taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
+
+ foreach ($taxes as $tax) {
+ $tax_detail = [
+ 'tax_name' => $tax['name'],
+ 'tax_rate' => $tax['tax_rate'],
+ 'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
+ 'tax_amount' => $this->calculateRatio($tax['total']),
+ 'tax_amount_paid' => $this->calculateRatio($tax['total']),
+ 'tax_amount_remaining' => 0,
+ ];
+ $details[] = $tax_detail;
+ }
+
+ return new TransactionEventMetadata([
+ 'tax_report' => [
+ 'tax_details' => $details,
+ 'payment_history' => $this->payments->toArray(),
+ 'tax_summary' => [
+ 'total_taxes' => $invoice->total_taxes,
+ 'total_paid' => $this->getTotalTaxPaid($invoice),
+ 'status' => 'cancelled',
+ ],
+ ],
+ ]);
+
+ }
+
+ /**
+ * Set all tax details to 0
+ *
+ * @param mixed $invoice
+ */
+ private function getDeletedMetaData($invoice)
+ {
+
+ $calc = $invoice->calc();
+
+ $details = [];
+
+ $taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
+
+ foreach ($taxes as $tax) {
+ $tax_detail = [
+ 'tax_name' => $tax['name'],
+ 'tax_rate' => $tax['tax_rate'],
+ 'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
+ 'tax_amount' => $tax['total'],
+ 'tax_amount_paid' => $this->calculateRatio($tax['total']),
+ 'tax_amount_remaining' => 0,
+ ];
+ $details[] = $tax_detail;
+ }
+
+ return new TransactionEventMetadata([
+ 'tax_report' => [
+ 'tax_details' => $details,
+ 'payment_history' => $this->payments->toArray(),
+ 'tax_summary' => [
+ 'total_taxes' => $invoice->total_taxes,
+ 'total_paid' => $this->getTotalTaxPaid($invoice),0,
+ 'status' => 'deleted',
+ ],
+ ],
+ ]);
+
+ }
+
+ private function getMetadata($invoice)
+ {
+
+ if ($invoice->status_id == Invoice::STATUS_CANCELLED) {
+ return $this->getCancelledMetaData($invoice);
+ } elseif ($invoice->is_deleted) {
+ return $this->getDeletedMetaData($invoice);
+ }
+
+ $calc = $invoice->calc();
+
+ $details = [];
+
+ $taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
+
+ foreach ($taxes as $tax) {
+ $tax_detail = [
+ 'tax_name' => $tax['name'],
+ 'tax_rate' => $tax['tax_rate'],
+ 'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
+ 'tax_amount' => $tax['total'],
+ 'tax_amount_paid' => $this->calculateRatio($tax['total']),
+ 'tax_amount_remaining' => $tax['total'] - $this->calculateRatio($tax['total']),
+ ];
+ $details[] = $tax_detail;
+ }
+
+ return new TransactionEventMetadata([
+ 'tax_report' => [
+ 'tax_details' => $details,
+ 'payment_history' => $this->payments->toArray(),
+ 'tax_summary' => [
+ 'total_taxes' => $invoice->total_taxes,
+ 'total_paid' => $this->getTotalTaxPaid($invoice),
+ 'status' => 'updated',
+ ],
+ ],
+ ]);
+
+ }
+
+ private function getTotalTaxPaid($invoice)
+ {
+ if ($invoice->amount == 0) {
+ return 0;
+ }
+
+ $total_paid = $this->payments->sum('amount') - $this->payments->sum('refunded');
+
+ return round($invoice->total_taxes * ($total_paid / $invoice->amount), 2);
+
+ }
+
+
+}
diff --git a/app/Listeners/Payment/PaymentTransactionEventEntry.php b/app/Listeners/Payment/PaymentTransactionEventEntry.php
index 0e213f53d1..448dc58558 100644
--- a/app/Listeners/Payment/PaymentTransactionEventEntry.php
+++ b/app/Listeners/Payment/PaymentTransactionEventEntry.php
@@ -43,14 +43,15 @@ class PaymentTransactionEventEntry implements ShouldQueue
private float $paid_ratio;
private Collection $payments;
+
/**
*/
- public function __construct(private Payment $payment, private array $invoice_ids, private string $db)
+ public function __construct(private Payment $payment, private array $invoice_ids, private string $db, private float $invoice_adjustment = 0, private int $is_deleted = false)
{}
public function handle()
{
- nlog("PaymentTransactionEventEntry::handle");
+
//payment vs refunded
MultiDB::setDb($this->db);
@@ -81,6 +82,12 @@ class PaymentTransactionEventEntry implements ShouldQueue
$this->setPaidRatio($invoice);
+ //delete any other payment mutations here if this is a delete event, the refunds are redundant in this time period
+ $invoice->transaction_events()
+ ->where('event_id', TransactionEvent::PAYMENT_REFUNDED)
+ ->where('period', now()->endOfMonth()->format('Y-m-d'))
+ ->delete();
+
TransactionEvent::create([
'invoice_id' => $invoice->id,
'client_id' => $invoice->client_id,
@@ -92,7 +99,7 @@ class PaymentTransactionEventEntry implements ShouldQueue
'invoice_partial' => $invoice->partial ?? 0,
'invoice_paid_to_date' => $invoice->paid_to_date ?? 0,
'invoice_status' => $invoice->is_deleted ? 7 : $invoice->status_id,
- 'event_id' => $this->payment->is_deleted ? TransactionEvent::PAYMENT_DELETED : TransactionEvent::PAYMENT_REFUNDED,
+ 'event_id' => $this->is_deleted ? TransactionEvent::PAYMENT_DELETED : TransactionEvent::PAYMENT_REFUNDED,
'timestamp' => now()->timestamp,
'metadata' => $this->getMetadata($invoice),
'period' => now()->endOfMonth()->format('Y-m-d'),
@@ -137,10 +144,14 @@ class PaymentTransactionEventEntry implements ShouldQueue
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
foreach ($taxes as $tax) {
+
+ $base_amount = $tax['base_amount'] ?? $calc->getNetSubtotal();
+
+
$tax_detail = [
'tax_name' => $tax['name'],
'tax_rate' => $tax['tax_rate'],
- 'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
+ 'taxable_amount' => $base_amount,
'tax_amount' => $tax['total'],
'tax_amount_paid' => $this->calculateRatio($tax['total']),
'tax_amount_remaining' => round($tax['total'] - $this->calculateRatio($tax['total']), 2),
@@ -178,14 +189,26 @@ class PaymentTransactionEventEntry implements ShouldQueue
$taxes = array_merge($calc->getTaxMap()->merge($calc->getTotalTaxMap())->toArray());
foreach ($taxes as $tax) {
+
+
+ $base_amount = $tax['base_amount'] ?? $calc->getNetSubtotal();
+
+ if($this->invoice_adjustment > 0)
+ $tax_amount_paid = round(($this->invoice_adjustment / ($base_amount+$tax['total'])) * $tax['total'], 2);
+ else {
+ $tax_amount_paid = $this->calculateRatio($tax['total']);
+ }
+
$tax_detail = [
'tax_name' => $tax['name'],
'tax_rate' => $tax['tax_rate'],
- 'taxable_amount' => $tax['base_amount'] ?? $calc->getNetSubtotal(),
+ 'taxable_amount' => $base_amount,
'tax_amount' => $tax['total'],
- 'tax_amount_paid' => $this->calculateRatio($tax['total']),
+ 'tax_amount_paid' => $tax_amount_paid,
'tax_amount_remaining' => 0,
+ 'tax_status' => 'payment_deleted',
];
+
$details[] = $tax_detail;
}
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index c21d3b4039..6ce917f057 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -857,11 +857,15 @@ class Invoice extends BaseModel
$formatted_string = "
";
- foreach($schedule->parameters['schedule'] as $item){
+ $formatted_string .= "
".ctrans('texts.payment_schedule')."
";
+
+ foreach($schedule->parameters['schedule'] as $key => $item){
$amount = $item['is_amount'] ? $item['amount'] : round($this->amount * ($item['amount']/100),2);
$amount = \App\Utils\Number::formatMoney($amount, $this->client);
- $formatted_string .= "
".$this->formatDate($item['date'], $this->client->date_format()) . " - " . $amount."
";
+ $schedule_text = ctrans('texts.payment_schedule_table', ['key' => $key+1, 'date' => $this->formatDate($item['date'], $this->client->date_format()), 'amount' => $amount]);
+
+ $formatted_string .= "
".$schedule_text."
";
}
$formatted_string .= "
";
diff --git a/app/Models/TransactionEvent.php b/app/Models/TransactionEvent.php
index 38ff4aafaa..69f22457c1 100644
--- a/app/Models/TransactionEvent.php
+++ b/app/Models/TransactionEvent.php
@@ -38,7 +38,7 @@ use App\DataMapper\TransactionEventMetadata;
* @property int $event_id
* @property int $timestamp
* @property array|null $payment_request
- * @property array|null $metadata
+ * @property TransactionEventMetadata|null $metadata
* @property string $credit_balance
* @property string $credit_amount
* @property int|null $credit_status
@@ -65,4 +65,6 @@ class TransactionEvent extends StaticModel
public const PAYMENT_REFUNDED = 2;
public const PAYMENT_DELETED = 3;
+
+ public const PAYMENT_CASH = 4;
}
diff --git a/app/Services/Payment/DeletePayment.php b/app/Services/Payment/DeletePayment.php
index 907c0e9edc..a13697e63a 100644
--- a/app/Services/Payment/DeletePayment.php
+++ b/app/Services/Payment/DeletePayment.php
@@ -89,7 +89,7 @@ class DeletePayment
if ($this->payment->invoices()->exists()) {
- $invoice_ids = $this->payment->invoices()->pluck('id');
+ $invoice_ids = $this->payment->invoices()->pluck('invoices.id')->toArray();
$this->payment->invoices()->each(function ($paymentable_invoice) {
$net_deletable = $paymentable_invoice->pivot->amount - $paymentable_invoice->pivot->refunded;
@@ -161,10 +161,10 @@ class DeletePayment
}
+ PaymentTransactionEventEntry::dispatch($this->payment, [$paymentable_invoice->id], $this->payment->company->db, $net_deletable, true);
});
- PaymentTransactionEventEntry::dispatch($this->payment, $invoice_ids, $this->payment->company->db);
}
//sometimes the payment is NOT created properly, this catches the payment and prevents the paid to date reducing inappropriately.
diff --git a/app/Services/Payment/RefundPayment.php b/app/Services/Payment/RefundPayment.php
index 2f0e54cead..a58373f624 100644
--- a/app/Services/Payment/RefundPayment.php
+++ b/app/Services/Payment/RefundPayment.php
@@ -313,7 +313,7 @@ class RefundPayment
}
- PaymentTransactionEventEntry::dispatch($this->payment, array_column($this->refund_data['invoices'], 'invoice_id'), $this->payment->company->db);
+ PaymentTransactionEventEntry::dispatch($this->payment, array_column($this->refund_data['invoices'], 'invoice_id'), $this->payment->company->db, 0, false);
} else {
//if we are refunding and no payments have been tagged, then we need to decrement the client->paid_to_date by the total refund amount.
diff --git a/app/Services/Report/XLS/TaxReport.php b/app/Services/Report/XLS/TaxReport.php
index 958345516f..958816a22f 100644
--- a/app/Services/Report/XLS/TaxReport.php
+++ b/app/Services/Report/XLS/TaxReport.php
@@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Builder;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use App\Models\Invoice;
use App\Listeners\Invoice\InvoiceTransactionEventEntry;
+use App\Models\TransactionEvent;
class TaxReport
{
@@ -83,7 +84,7 @@ class TaxReport
$worksheet = $this->spreadsheet->createSheet();
$worksheet->setTitle(ctrans('texts.invoice')." ".ctrans('texts.cash_vs_accrual'));
- $worksheet->fromArray($this->data['invoices'], null, 'A1');
+ $worksheet->fromArray($this->data['accrual']['invoices'], null, 'A1');
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format()); // Invoice date column
$worksheet->getStyle('C:C')->getNumberFormat()->setFormatCode($this->currency_format); // Invoice total column
@@ -97,13 +98,11 @@ class TaxReport
// All paid invoices within a time period
public function createInvoiceSummarySheetCash()
{
- $cash_invoices = collect($this->data['invoices'])->filter(function($invoice){
- return $invoice[3] != 0;
- })->toArray();
$worksheet = $this->spreadsheet->createSheet();
$worksheet->setTitle(ctrans('texts.invoice')." ".ctrans('texts.cash_accounting'));
- $worksheet->fromArray($cash_invoices, null, 'A1');
+
+ $worksheet->fromArray($this->data['cash']['invoices'], null, 'A1');
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format()); // Invoice date column
$worksheet->getStyle('C:C')->getNumberFormat()->setFormatCode($this->currency_format); // Invoice total column
$worksheet->getStyle('D:D')->getNumberFormat()->setFormatCode($this->currency_format); // Paid amount column
@@ -118,7 +117,7 @@ class TaxReport
$worksheet = $this->spreadsheet->createSheet();
$worksheet->setTitle(ctrans('texts.invoice_item')." ".ctrans('texts.cash_vs_accrual'));
- $worksheet->fromArray($this->data['invoice_items'], null, 'A1');
+ $worksheet->fromArray($this->data['accrual']['invoice_items'], null, 'A1');
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format()); // Invoice date column
$worksheet->getStyle('C:C')->getNumberFormat()->setFormatCode($this->currency_format); // Invoice total column
@@ -135,13 +134,9 @@ class TaxReport
public function createInvoiceItemSummarySheetCash()
{
- $cash_invoice_items = collect($this->data['invoice_items'])->filter(function($invoice_item){
- return $invoice_item[3] != 0;
- })->toArray();
-
$worksheet = $this->spreadsheet->createSheet();
$worksheet->setTitle(ctrans('texts.invoice_item')." ".ctrans('texts.cash_accounting'));
- $worksheet->fromArray($cash_invoice_items, null, 'A1');
+ $worksheet->fromArray($this->data['cash']['invoice_items'], null, 'A1');
$worksheet->getStyle('B:B')->getNumberFormat()->setFormatCode($this->company->date_format()); // Invoice date column
$worksheet->getStyle('C:C')->getNumberFormat()->setFormatCode($this->currency_format); // Invoice total column
@@ -162,17 +157,19 @@ class TaxReport
$end_date_instance = Carbon::parse($this->tsr->end_date);
$this->data['invoices'] = [];
- $this->data['invoices'][] = [
+ $this->data['invoices'][] =
+
+ $invoice_headers = [
ctrans('texts.invoice_number'),
ctrans('texts.invoice_date'),
ctrans('texts.invoice_total'),
ctrans('texts.paid'),
ctrans('texts.total_taxes'),
- ctrans('texts.tax_paid')
+ ctrans('texts.tax_paid'),
+ ctrans('texts.notes')
];
- $this->data['invoice_items'] = [];
- $this->data['invoice_items'][] = [
+ $invoice_item_headers = [
ctrans('texts.invoice_number'),
ctrans('texts.invoice_date'),
ctrans('texts.invoice_total'),
@@ -185,26 +182,106 @@ class TaxReport
ctrans('texts.tax_nexus'),
];
- $offset = $this->company->timezone_offset();
- /** @var Invoice $invoice */
- foreach($this->query->cursor() as $invoice){
+ $this->data['accrual']['invoices'] = [$invoice_headers];
+ $this->data['cash']['invoices'] = [$invoice_headers];
+ $this->data['accrual']['invoice_items'] = [$invoice_item_headers];
+ $this->data['cash']['invoice_items'] = [$invoice_item_headers];
- if($invoice->transaction_events->count() == 0){
- (new InvoiceTransactionEventEntry())->run($invoice);
- }
- //get the invoice state as at the end of the current period.
- $invoice->transaction_events->each(function($event){
+ Invoice::withTrashed()
+ ->with('transaction_events')
+ ->where('company_id', $this->company->id)
+ ->whereHas('transaction_events', function ($query){
+ return $query->where('period', now()->endOfMonth()->format('Y-m-d'));
+ })
+ ->cursor()
+ ->each(function($invoice){
+
+ if($invoice->transaction_events->count() == 0){
+ (new InvoiceTransactionEventEntry())->run($invoice);
+ $invoice->load('transaction_events');
+ }
+
+ /** @var TransactionEvent $invoice_state */
+ $invoice_state = $invoice->transaction_events->where('event_id', TransactionEvent::INVOICE_UPDATED)->sortByDesc('timestamp')->first();
+ $adjustments = $invoice->transaction_events->whereIn('event_id',[TransactionEvent::PAYMENT_REFUNDED, TransactionEvent::PAYMENT_DELETED]);
+
+ if($invoice_state->event_id == TransactionEvent::INVOICE_UPDATED){
+ $this->data['accrual']['invoices'][] = [
+ $invoice->number,
+ $invoice->date,
+ $invoice->amount,
+ $invoice_state->invoice_paid_to_date,
+ $invoice_state->metadata->tax_report->tax_summary->total_taxes,
+ $invoice_state->metadata->tax_report->tax_summary->total_paid,
+ 'payable',
+ ];
+ }
+ elseif($invoice_state->event_id == TransactionEvent::PAYMENT_CASH){
+
+ $this->data['cash']['invoices'][] = [
+ $invoice->number,
+ $invoice->date,
+ $invoice->amount,
+ $invoice_state->invoice_paid_to_date,
+ $invoice_state->metadata->tax_report->tax_summary->total_taxes,
+ $invoice_state->metadata->tax_report->tax_summary->total_paid,
+ 'payable',
+ ];
+
+ }
+ $_adjustments = [];
+
+ foreach($adjustments as $adjustment){
+ $_adjustments[] = [
+ $invoice->number,
+ $invoice->date,
+ $invoice->amount,
+ $invoice_state->invoice_paid_to_date,
+ $invoice_state->metadata->tax_report->tax_summary->total_taxes,
+ $invoice_state->metadata->tax_report->tax_summary->adjustment,
+ 'adjustment',
+ ];
+ }
+
+ $this->data['accrual']['invoices'] = array_merge($this->data['accrual']['invoices'], $_adjustments);
+ $this->data['cash']['invoices'] = array_merge($this->data['cash']['invoices'], $_adjustments);
});
- //anything period the reporting period is considered an ADJUSTMENT
-
- }
-
- return $this;
+ return $this;
}
+ // $offset = $this->company->timezone_offset();
+
+ // /** @var Invoice $invoice */
+ // foreach($this->query->cursor() as $invoice){
+
+ // if($invoice->transaction_events->count() == 0){
+ // (new InvoiceTransactionEventEntry())->run($invoice);
+ // }
+
+ // //get the invoice state as at the end of the current period.
+ // $invoice_state =$invoice->transaction_events()
+ // ->where('period', Carbon::parse($invoice->date)->endOfMonth()->format('Y-m-d'))
+ // ->where('event_id', TransactionEvent::INVOICE_UPDATED)
+ // ->latest()
+ // ->first();
+
+
+
+ // //anything period the reporting period is considered an ADJUSTMENT
+ // }
+
+ // Invoice::withTrashed()
+ // ->where('company_id', $this->company->id)
+ // ->whereHas('transaction_events', function ($query){
+ // return $query->where('period', Carbon::parse($invoice->date)->endOfMonth()->format('Y-m-d'))
+ // ->whereIn('event_id',[TransactionEvent::PAYMENT_REFUNDED, TransactionEvent::PAYMENT_DELETED]);
+ // });
+
+ // return $this;
+ // }
public function getXlsFile()
{
@@ -214,7 +291,7 @@ class TaxReport
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($this->spreadsheet);
$writer->save($tempFile);
- // $writer->save('/home/david/ttx.xslx');
+ $writer->save('/home/david/ttx.xlsx');
// Read file content
$fileContent = file_get_contents($tempFile);
diff --git a/lang/en/texts.php b/lang/en/texts.php
index 2ab19130b9..a26a5385d2 100644
--- a/lang/en/texts.php
+++ b/lang/en/texts.php
@@ -5607,6 +5607,7 @@ $lang = array(
'first_payment_date' => 'First Payment Date',
'first_payment_date_help' => 'The date of the first payment',
'payment_schedule_interval' => 'Payment :index of :total for :amount',
+ 'payment_schedule_table' => 'Payment :key on :date for :amount',
'auto_send' => 'Auto Send',
'auto_send_help' => 'Automatically emails the invoice to the client',
'include_project_tasks' => 'Include Project Tasks',
diff --git a/tests/Feature/Export/TaxSummaryReportTest.php b/tests/Feature/Export/TaxSummaryReportTest.php
index 6ac58603c5..13efe22ae6 100644
--- a/tests/Feature/Export/TaxSummaryReportTest.php
+++ b/tests/Feature/Export/TaxSummaryReportTest.php
@@ -11,17 +11,19 @@
namespace Tests\Feature\Export;
-use App\DataMapper\CompanySettings;
-use App\Factory\InvoiceItemFactory;
-use App\Models\Account;
+use Tests\TestCase;
+use App\Models\User;
use App\Models\Client;
+use App\Models\Account;
use App\Models\Company;
use App\Models\Invoice;
-use App\Models\User;
-use App\Services\Report\TaxSummaryReport;
use App\Utils\Traits\MakesHash;
+use App\DataMapper\CompanySettings;
+use App\Factory\InvoiceItemFactory;
+use App\Services\Report\TaxSummaryReport;
use Illuminate\Routing\Middleware\ThrottleRequests;
-use Tests\TestCase;
+use App\Listeners\Invoice\InvoiceTransactionEventEntry;
+use App\Listeners\Invoice\InvoiceTransactionEventEntryAccrual;
/**
*
@@ -162,6 +164,34 @@ class TaxSummaryReportTest extends TestCase
$i = $i->calc()->getInvoice();
+ (new InvoiceTransactionEventEntry())->run($i);
+
+ $i2 = Invoice::factory()->create([
+ 'client_id' => $this->client->id,
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ 'amount' => 0,
+ 'balance' => 0,
+ 'status_id' => 2,
+ 'total_taxes' => 1,
+ 'date' => now()->format('Y-m-d'),
+ 'terms' => 'nada',
+ 'discount' => 0,
+ 'tax_rate1' => 10,
+ 'tax_rate2' => 17.5,
+ 'tax_rate3' => 5,
+ 'tax_name1' => 'GST',
+ 'tax_name2' => 'VAT',
+ 'tax_name3' => 'CA Sales Tax',
+ 'uses_inclusive_taxes' => false,
+ 'line_items' => $this->buildLineItems(),
+ ]);
+
+ $i2 = $i2->calc()->getInvoice();
+ $i2->service()->markPaid();
+
+ (new InvoiceTransactionEventEntryAccrual())->run($i2);
+
$pl = new TaxSummaryReport($this->company, $this->payload);
$response = $pl->run();
@@ -206,6 +236,34 @@ class TaxSummaryReportTest extends TestCase
$i = $i->calc()->getInvoice();
+ (new InvoiceTransactionEventEntry())->run($i);
+
+$i2 = Invoice::factory()->create([
+ 'client_id' => $this->client->id,
+ 'user_id' => $this->user->id,
+ 'company_id' => $this->company->id,
+ 'amount' => 0,
+ 'balance' => 0,
+ 'status_id' => 2,
+ 'total_taxes' => 1,
+ 'date' => now()->format('Y-m-d'),
+ 'terms' => 'nada',
+ 'discount' => 0,
+ 'tax_rate1' => 10,
+ 'tax_rate2' => 17.5,
+ 'tax_rate3' => 5,
+ 'tax_name1' => 'GST',
+ 'tax_name2' => 'VAT',
+ 'tax_name3' => 'CA Sales Tax',
+ 'uses_inclusive_taxes' => false,
+ 'line_items' => $this->buildLineItems(),
+ ]);
+
+$i2 = $i2->calc()->getInvoice();
+$i2->service()->markPaid();
+
+(new InvoiceTransactionEventEntryAccrual())->run($i2, now()->subDays(30)->format('Y-m-d'), now()->addDays(30)->format('Y-m-d'));
+
$pl = new TaxSummaryReport($this->company, $this->payload);
$query = Invoice::query()