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
← All changes | includes/Traits/PaymentCalculationTrait.php +60 -9 2.1.12.4.0 View file →
@@ -49,8 +49,30 @@
49 49 return $total_payments;
50 50 }
51 51
52 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 + /**
53 75 * Check if invoice should be marked as paid based on total payments
54 76 *
55 77 * @param int $invoice_id
56 78 * @param \EasyInvoice\Models\Invoice $invoice
@@ -58,11 +80,13 @@
58 80 */
59 81 private function shouldMarkInvoiceAsPaid($invoice_id, $invoice): bool
60 82 {
61 83 $total_payments = $this->calculateTotalPaymentsForInvoice($invoice_id);
62 - $invoice_total = $invoice->getTotal();
63 -
64 - return $total_payments >= $invoice_total;
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;
65 89 }
66 90
67 91 /**
68 92 * Update invoice status to paid only if total payments are sufficient
@@ -84,17 +108,44 @@
84 108 } else {
85 109 error_log("Easy Invoice: Invoice #$invoice_id status updated to 'paid' - payments complete");
86 110 }
87 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 +
88 127 return true;
89 128 } else {
90 - // Log that status remains unchanged
91 - if (method_exists($this, 'log')) {
92 - $this->log("Invoice #$invoice_id status remains unchanged - partial payment received", 'info');
93 - } else {
94 - error_log("Easy Invoice: Invoice #$invoice_id status remains unchanged - partial payment received");
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();
95 137 }
96 -
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 +
97 148 return false;
98 149 }
99 150 }
100 151 }