PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.3
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.3
2.4.3 2.4.4 2.4.2 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 All 60 releases
← All changes | templates/payments/list.php +67 -59 2.4.1 → 2.4.3 View file →
@@ -37,58 +37,20 @@
37 37 'pending_payments' => 0,
38 38 'failed_payments' => 0
39 39 ], $stats);
40 40
41 -// Initialize amounts_by_currency array
42 -$amounts_by_currency = [];
43 -// Calculate stats from the payments array
44 -foreach ($payments as $payment) {
45 - $amount = floatval($payment->getAmount());
46 - $status = $payment->getStatus();
47 -
48 - $stats['total_amount'] += $amount;
49 -
50 - switch ($status) {
51 - case 'completed':
52 - $stats['completed_payments']++;
53 - break;
54 - case 'pending':
55 - $stats['pending_payments']++;
56 - break;
57 - case 'failed':
58 - $stats['failed_payments']++;
59 - break;
60 - }
61 -
62 - // Calculate currency breakdown
63 - $payment_currency = $payment->getCurrency();
64 -
65 - // If currency is empty or "global", get the actual currency that was used
66 - if (empty($payment_currency) || $payment_currency === 'global') {
67 - $actual_currency = get_post_meta($payment->getId(), '_currency', true);
68 - $payment_currency = !empty($actual_currency) ? $actual_currency : get_option('easy_invoice_currency_code', 'USD');
69 - }
70 -
71 - // If currency is still "global", use the global setting
72 - if ($payment_currency === 'global') {
73 - $payment_currency = get_option('easy_invoice_currency_code', 'USD');
41 +// Site-wide figures come from the controller (one query each); the page's own
42 +// twenty rows used to be added on top of them, which inflated "Completed" and
43 +// "Awaiting confirmation" and made "Total amount" the page's sum.
44 +$amounts_by_currency = $amounts_by_currency ?? [];
45 +if (empty($amounts_by_currency) && !empty($stats['total_amount'])) {
46 + $ei_code = strtoupper((string) get_option('easy_invoice_currency_code', 'USD'));
47 + $amounts_by_currency[$ei_code] = [
48 + 'amount' => (float) $stats['total_amount'],
49 + 'symbol' => \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($ei_code),
50 + ];
74 51 }
75 52
76 - $payment_currency_symbol = $payment->getCurrencySymbol() ?: \EasyInvoice\Helpers\CurrencyHelper::getCurrencySymbol($payment_currency);
77 -
78 - // Normalize currency code to uppercase for consistent grouping
79 - $payment_currency = strtoupper($payment_currency);
80 -
81 - // Group amounts by currency
82 - if (!isset($amounts_by_currency[$payment_currency])) {
83 - $amounts_by_currency[$payment_currency] = [
84 - 'amount' => 0,
85 - 'symbol' => $payment_currency_symbol
86 - ];
87 -}
88 - $amounts_by_currency[$payment_currency]['amount'] += $amount;
89 -}
90 -
91 53 // Check for bulk action notifications
92 54 $notification = false;
93 55
94 56 if (isset($_GET['bulk_trashed']) && $_GET['bulk_trashed'] > 0) {
@@ -519,8 +481,16 @@
519 481 $method_label = __('Manual', 'easy-invoice');
520 482 }
521 483
522 484 echo esc_html($method_label);
485 + $ei_reference = (string) get_post_meta((int) $payment->getId(), \EasyInvoice\Services\OfflinePayments::META_REFERENCE, true);
486 + if ('' !== $ei_reference) :
487 + /* translators: %s: reference the client gave. */
488 + echo '<span class="block text-xs text-gray-400" title="' . esc_attr__('Reference given by the client', 'easy-invoice') . '">' . esc_html(sprintf(__('Ref: %s', 'easy-invoice'), $ei_reference)) . '</span>';
489 + endif;
490 + if (\EasyInvoice\Services\OfflinePayments::hasProof((int) $payment->getId())) :
491 + echo '<a class="inline-flex items-center gap-1 mt-1 text-xs font-medium text-indigo-600 hover:text-indigo-900" target="_blank" rel="noopener" href="' . esc_url(\EasyInvoice\Services\OfflinePayments::proofUrl((int) $payment->getId())) . '"><svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13"></path></svg>' . esc_html__('View receipt', 'easy-invoice') . '</a>';
492 + endif;
523 493 ?>
524 494 </td>
525 495 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
526 496 <?php
@@ -533,11 +503,18 @@
533 503 $status_class = 'bg-green-100 text-green-800';
534 504 $status_text = __('Completed', 'easy-invoice');
535 505 break;
536 506 case 'pending':
507 + case 'pending-bank':
508 + case 'pending-cheque':
509 + case 'pending_verification':
537 510 $status_class = 'bg-yellow-100 text-yellow-800';
538 - $status_text = __('Pending', 'easy-invoice');
511 + $status_text = __('Awaiting confirmation', 'easy-invoice');
539 512 break;
513 + case 'rejected':
514 + $status_class = 'bg-gray-200 text-gray-700';
515 + $status_text = __('Rejected', 'easy-invoice');
516 + break;
540 517 case 'failed':
541 518 $status_class = 'bg-red-100 text-red-800';
542 519 $status_text = __('Failed', 'easy-invoice');
543 520 break;
@@ -568,10 +545,10 @@
568 545 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
569 546 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
570 547 </svg>
571 548 </a>
572 - <?php if (in_array($status, ['pending', 'pending-bank', 'pending-cheque'])): ?>
573 - <button type="button" class="approve-payment-btn text-green-600 hover:text-green-900"
549 + <?php if (in_array($status, \EasyInvoice\Services\OfflinePayments::pendingStatuses(), true)): ?>
550 + <button type="button" class="approve-payment-btn text-green-600 hover:text-green-900" title="<?php esc_attr_e('Confirm this payment', 'easy-invoice'); ?>"
574 551 data-payment-id="<?php echo (int) $payment->getId(); ?>"
575 552 data-invoice-id="<?php echo esc_attr($payment->getInvoiceId()); ?>"
576 553 data-nonce="<?php echo esc_attr(wp_create_nonce('easy_invoice_approve_payment')); ?>">
577 554 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
@@ -576,9 +553,18 @@
576 553 data-nonce="<?php echo esc_attr(wp_create_nonce('easy_invoice_approve_payment')); ?>">
577 554 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
578 555 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
579 556 </svg>
557 + <span class="sr-only"><?php esc_html_e('Confirm', 'easy-invoice'); ?></span>
580 558 </button>
559 + <button type="button" class="reject-payment-btn text-red-600 hover:text-red-900" title="<?php esc_attr_e('Reject this payment', 'easy-invoice'); ?>"
560 + data-payment-id="<?php echo (int) $payment->getId(); ?>"
561 + data-nonce="<?php echo esc_attr(wp_create_nonce('easy_invoice_approve_payment')); ?>">
562 + <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
563 + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
564 + </svg>
565 + <span class="sr-only"><?php esc_html_e('Reject', 'easy-invoice'); ?></span>
566 + </button>
581 567 <?php endif; ?>
582 568
583 569 <?php
584 570 // Hook for additional payment actions (used by Pro plugin for receipts)
@@ -702,9 +688,9 @@
702 688 <?php esc_html_e('Approve Payment', 'easy-invoice'); ?>
703 689 </h3>
704 690 <div class="mt-2">
705 691 <p class="text-sm text-gray-500">
706 - <?php esc_html_e('You are about to approve this payment. This will mark the invoice as paid and update the payment records.', 'easy-invoice'); ?>
692 + <span data-ei-mode-text="approve"><?php esc_html_e('Confirm that this payment arrived. The payment is marked completed and the invoice becomes Paid when its balance is covered, or Partially paid otherwise.', 'easy-invoice'); ?></span><span data-ei-mode-text="reject" hidden><?php esc_html_e('Reject this submission. The record is kept for the audit trail, nothing counts toward the balance, and the invoice keeps its status.', 'easy-invoice'); ?></span>
707 693 </p>
708 694 <div class="mt-4">
709 695 <label for="approval-notes" class="block text-sm font-medium text-gray-700">
710 696 <?php esc_html_e('Notes (optional)', 'easy-invoice'); ?>
@@ -716,9 +702,9 @@
716 702 </div>
717 703 </div>
718 704 <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
719 705 <button type="button" id="confirm-approve-btn" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-600 text-base font-medium text-white hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm">
720 - <?php esc_html_e('Approve Payment', 'easy-invoice'); ?>
706 + <span data-ei-mode-text="approve"><?php esc_html_e('Confirm payment', 'easy-invoice'); ?></span><span data-ei-mode-text="reject" hidden><?php esc_html_e('Reject payment', 'easy-invoice'); ?></span>
721 707 </button>
722 708 <button type="button" id="cancel-approve-btn" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
723 709 <?php esc_html_e('Cancel', 'easy-invoice'); ?>
724 710 </button>
@@ -787,13 +773,33 @@
787 773 // Store the data for the current payment
788 774 currentInvoiceId = $(this).data('invoice-id');
789 775 currentPaymentId = $(this).data('payment-id');
790 776 currentNonce = $(this).data('nonce');
777 + currentMode = 'approve';
778 + setModalMode('approve');
791 779
792 780 // Show the approval modal
793 781 $('#payment-approval-modal').removeClass('hidden');
794 782 });
795 783
784 + // Reject uses the same dialog with different copy and a different endpoint.
785 + var currentMode = 'approve';
786 + function setModalMode(mode) {
787 + $('#payment-approval-modal [data-ei-mode-text]').each(function() {
788 + $(this).prop('hidden', $(this).data('ei-mode-text') !== mode);
789 + });
790 + $('#confirm-approve-btn').toggleClass('bg-red-600 hover:bg-red-700', mode === 'reject').toggleClass('bg-green-600 hover:bg-green-700', mode !== 'reject');
791 + }
792 + $('.reject-payment-btn').on('click', function(e) {
793 + e.preventDefault();
794 + currentInvoiceId = null;
795 + currentPaymentId = $(this).data('payment-id');
796 + currentNonce = $(this).data('nonce');
797 + currentMode = 'reject';
798 + setModalMode('reject');
799 + $('#payment-approval-modal').removeClass('hidden');
800 + });
801 +
796 802 // Handle Cancel button in modal
797 803 $('#cancel-approve-btn').on('click', function() {
798 804 // Hide the modal and reset the form
799 805 $('#payment-approval-modal').addClass('hidden');
@@ -805,9 +811,9 @@
805 811 // Get the notes from the textarea
806 812 const notes = $('#approval-notes').val();
807 813
808 814 // Disable the button to prevent double clicks
809 - $(this).prop('disabled', true).text('Processing...');
815 + $(this).prop('disabled', true);
810 816
811 817 // Make AJAX request to approve the payment
812 818 $.ajax({
813 819 url: ajaxurl,
@@ -812,10 +818,11 @@
812 818 $.ajax({
813 819 url: ajaxurl,
814 820 type: 'POST',
815 821 data: {
816 - action: 'easy_invoice_approve_payment',
822 + action: currentMode === 'reject' ? 'easy_invoice_reject_payment' : 'easy_invoice_approve_payment',
817 823 invoice_id: currentInvoiceId,
824 + payment_id: currentPaymentId,
818 825 nonce: currentNonce,
819 826 notes: notes
820 827 },
821 828 success: function(response) {
@@ -821,12 +828,13 @@
821 828 success: function(response) {
822 829 // Hide the modal
823 830 $('#payment-approval-modal').addClass('hidden');
824 831
832 + var toast = response && response.data && response.data.toast ? response.data.toast.message : '';
825 833 if (response.success) {
826 834 // Show success toast
827 835 if (typeof EasyInvoiceToast !== 'undefined') {
828 - EasyInvoiceToast.success('Payment approved successfully.');
836 + EasyInvoiceToast.success(toast || (currentMode === 'reject' ? 'Payment rejected.' : 'Payment confirmed.'));
829 837 }
830 838
831 839 // Refresh the page after a short delay
832 840 setTimeout(function() {
@@ -834,13 +842,13 @@
834 842 }, 1500);
835 843 } else {
836 844 // Show error toast
837 845 if (typeof EasyInvoiceToast !== 'undefined') {
838 - EasyInvoiceToast.error('Failed to approve payment.');
846 + EasyInvoiceToast.error(toast || 'The payment could not be updated.');
839 847 }
840 848
841 849 // Re-enable the button
842 - $('#confirm-approve-btn').prop('disabled', false).text('Approve Payment');
850 + $('#confirm-approve-btn').prop('disabled', false); setModalMode(currentMode);
843 851 }
844 852 },
845 853 error: function() {
846 854 // Hide the modal
@@ -851,9 +859,9 @@
851 859 EasyInvoiceToast.error('Server error occurred. Please try again.');
852 860 }
853 861
854 862 // Re-enable the button
855 - $('#confirm-approve-btn').prop('disabled', false).text('Approve Payment');
863 + $('#confirm-approve-btn').prop('disabled', false); setModalMode(currentMode);
856 864 }
857 865 });
858 866 });
859 867