PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / includes / Traits / PaymentCalculationTrait.php

PaymentCalculationTrait.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.0, at includes/Traits/PaymentCalculationTrait.php

152 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace EasyInvoice\Traits;
4
5 /**
6 * Trait for calculating payment totals
7 *
8 * This trait provides shared functionality for calculating total payments
9 * for invoices across different payment gateways.
10 */
11 trait PaymentCalculationTrait
12 {
13 /**
14 * Calculate total payments for a specific invoice
15 *
16 * @param int $invoice_id
17 * @return float
18 */
19 private function calculateTotalPaymentsForInvoice($invoice_id): float
20 {
21 // Get all payment records for this invoice
22 $payments = get_posts([
23 'post_type' => 'easy_invoice_payment',
24 'post_status' => 'publish',
25 'meta_query' => [
26 [
27 'key' => '_invoice_id',
28 'value' => $invoice_id,
29 'compare' => '='
30 ],
31 [
32 'key' => '_status',
33 'value' => 'completed',
34 'compare' => '='
35 ]
36 ],
37 'numberposts' => -1
38 ]);
39
40 $total_payments = 0.0;
41
42 foreach ($payments as $payment) {
43 $amount = get_post_meta($payment->ID, '_amount', true);
44 if ($amount && is_numeric($amount)) {
45 $total_payments += floatval($amount);
46 }
47 }
48
49 return $total_payments;
50 }
51
52 /**
53 * Amount of the most recently completed payment on an invoice.
54 *
55 * @param int $invoice_id
56 * @return float Zero when there is none.
57 */
58 private function latestCompletedPaymentAmount($invoice_id): float
59 {
60 $payments = get_posts([
61 'post_type' => 'easy_invoice_payment',
62 'post_status' => 'publish',
63 'numberposts' => 1,
64 'orderby' => 'date',
65 'order' => 'DESC',
66 'meta_query' => [
67 ['key' => '_invoice_id', 'value' => $invoice_id, 'compare' => '='],
68 ['key' => '_status', 'value' => 'completed', 'compare' => '='],
69 ],
70 ]);
71 return $payments ? (float) get_post_meta($payments[0]->ID, '_amount', true) : 0.0;
72 }
73
74 /**
75 * Check if invoice should be marked as paid based on total payments
76 *
77 * @param int $invoice_id
78 * @param \EasyInvoice\Models\Invoice $invoice
79 * @return bool
80 */
81 private function shouldMarkInvoiceAsPaid($invoice_id, $invoice): bool
82 {
83 $total_payments = $this->calculateTotalPaymentsForInvoice($invoice_id);
84 $invoice_total = (float) $invoice->getTotal();
85 // Credit notes reduce what is owed, so they count towards settlement.
86 $credited = class_exists('\\EasyInvoice\\Services\\InvoiceBalance') ? \EasyInvoice\Services\InvoiceBalance::credited((int) $invoice_id) : 0.0;
87
88 return $total_payments + $credited + 0.005 >= $invoice_total;
89 }
90
91 /**
92 * Update invoice status to paid only if total payments are sufficient
93 *
94 * @param int $invoice_id
95 * @param \EasyInvoice\Models\Invoice $invoice
96 * @param string $gateway_name
97 * @return bool True if status was updated, false otherwise
98 */
99 private function updateInvoiceStatusIfPaid($invoice_id, $invoice, $gateway_name = ''): bool
100 {
101 if ($this->shouldMarkInvoiceAsPaid($invoice_id, $invoice)) {
102 $invoice->setStatus('paid');
103 $invoice->save();
104
105 // Log the status update
106 if (method_exists($this, 'log')) {
107 $this->log("Invoice #$invoice_id status updated to 'paid' - payments complete", 'info');
108 } else {
109 error_log("Easy Invoice: Invoice #$invoice_id status updated to 'paid' - payments complete");
110 }
111
112 // Trigger hook when invoice is marked as paid (for email notifications, etc.)
113 // Get payment method and transaction ID from the most recent payment
114 $payment_method = get_post_meta($invoice_id, '_payment_method', true) ?: $gateway_name ?: 'online';
115 $transaction_id = get_post_meta($invoice_id, '_transaction_id', true) ?: '';
116 // The receipt names the payment that settled the invoice, which
117 // is the whole total only when it was paid in one go.
118 $payment_amount = $this->latestCompletedPaymentAmount($invoice_id) ?: $invoice->getTotal();
119
120 do_action('easy_invoice_payment_completed', $invoice_id, $invoice, [
121 'payment_method' => $payment_method,
122 'gateway_name' => $gateway_name,
123 'transaction_id' => $transaction_id,
124 'amount' => $payment_amount
125 ]);
126
127 return true;
128 } else {
129 // Something has been received but not everything: say so, unless
130 // the invoice is in a state that should not change (draft,
131 // cancelled) or already reads as part paid.
132 $current = strtolower((string) $invoice->getStatus());
133 $received = $this->calculateTotalPaymentsForInvoice($invoice_id);
134 if (in_array($current, ['available', 'unpaid', 'overdue'], true) && $received > 0) {
135 $invoice->setStatus('partial');
136 $invoice->save();
137 }
138 if ($received > 0) {
139 /** This action is documented in includes/Controllers/PaymentController.php */
140 do_action('easy_invoice_payment_received', $invoice_id, $invoice, [
141 'payment_method' => get_post_meta($invoice_id, '_payment_method', true) ?: $gateway_name ?: 'online',
142 'gateway_name' => $gateway_name,
143 'transaction_id' => get_post_meta($invoice_id, '_transaction_id', true) ?: '',
144 'amount' => $this->latestCompletedPaymentAmount($invoice_id),
145 ]);
146 }
147
148 return false;
149 }
150 }
151 }
152