post_type ) { echo '

' . esc_html__( 'That invoice does not exist.', 'easy-invoice' ) . '

'; return; } $invoice = new \EasyInvoice\Models\Invoice( $post ); $remaining = CreditNote::remainingCreditable( $invoice_id ); $credited = CreditNote::creditedTotal( $invoice_id ); $notes = CreditNote::forInvoice( $invoice_id ); $notice = isset( $_GET['cn_notice'] ) ? sanitize_text_field( wp_unslash( $_GET['cn_notice'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $can_issue = easy_invoice_user_can( 'ei_create_invoice' ); include EASY_INVOICE_PLUGIN_DIR . 'templates/admin/credit-note-page.php'; } /** * Add a credit-note link to each invoice row. * * @param array $actions Actions contributed so far. * @param object $invoice Invoice model. * @return array */ public static function addRowAction( $actions, $invoice ): array { $actions = is_array( $actions ) ? $actions : []; if ( ! easy_invoice_user_can( 'ei_view_invoices' ) ) { return $actions; } $id = is_callable( [ $invoice, 'getId' ] ) ? (int) $invoice->getId() : 0; if ( $id <= 0 || ! \EasyInvoice\Services\InvoiceRetention::isIssued( $id ) ) { // Drafts are edited, not credited. return $actions; } $credited = CreditNote::creditedTotal( $id ); $actions['credit_note'] = sprintf( '%s', esc_url( add_query_arg( [ 'page' => self::PAGE_SLUG, 'invoice_id' => $id, ], admin_url( 'admin.php' ) ) ), $credited > 0 ? esc_html__( 'Credited', 'easy-invoice' ) : esc_html__( 'Credit note', 'easy-invoice' ) ); return $actions; } /** * Issue a credit note. * * @return void */ public static function handleCreate(): void { check_admin_referer( self::ACTION_CREATE ); // Issuing a credit note reduces what a customer owes. It is the same // class of act as issuing an invoice, so it takes the same capability -- // a Viewer can read every credit note and issue none. if ( ! easy_invoice_user_can( 'ei_create_invoice' ) ) { wp_die( esc_html__( 'You do not have permission to issue credit notes.', 'easy-invoice' ), '', [ 'response' => 403 ] ); } $invoice_id = isset( $_POST['invoice_id'] ) ? absint( $_POST['invoice_id'] ) : 0; $reason = isset( $_POST['reason'] ) ? sanitize_textarea_field( wp_unslash( $_POST['reason'] ) ) : ''; // An empty amount means "all of it", which is the common case: this // invoice was wrong, cancel it out. $amount = null; if ( isset( $_POST['amount'] ) && '' !== trim( (string) wp_unslash( $_POST['amount'] ) ) ) { $amount = (float) str_replace( ',', '.', (string) wp_unslash( $_POST['amount'] ) ); } $result = CreditNote::create( $invoice_id, [ 'amount' => $amount, 'reason' => $reason, ] ); $back = add_query_arg( [ 'page' => self::PAGE_SLUG, 'invoice_id' => $invoice_id, ], admin_url( 'admin.php' ) ); if ( is_wp_error( $result ) ) { wp_safe_redirect( add_query_arg( 'cn_notice', rawurlencode( $result->get_error_message() ), $back ) ); exit; } wp_safe_redirect( add_query_arg( 'cn_notice', rawurlencode( __( 'Credit note issued.', 'easy-invoice' ) ), $back ) ); exit; } /** * Serve a credit note as a PDF. * * @return void */ public static function handleDownload(): void { $credit_id = isset( $_GET['credit_id'] ) ? absint( $_GET['credit_id'] ) : 0; $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, self::ACTION_DOWNLOAD . '_' . $credit_id ) ) { wp_die( esc_html__( 'Security check failed.', 'easy-invoice' ), '', [ 'response' => 403 ] ); } if ( ! easy_invoice_user_can( 'ei_view_invoices' ) ) { wp_die( esc_html__( 'You do not have permission to download this document.', 'easy-invoice' ), '', [ 'response' => 403 ] ); } $post = get_post( $credit_id ); if ( ! $post instanceof \WP_Post || PostTypes::EASY_INVOICE_CREDIT_NOTE_POST_TYPE !== $post->post_type ) { wp_die( esc_html__( 'That credit note does not exist.', 'easy-invoice' ), '', [ 'response' => 404 ] ); } $document = new \EasyInvoice\Models\Invoice( $post ); $pdf = PdfRenderer::renderInvoice( $document ); if ( is_wp_error( $pdf ) ) { wp_die( esc_html( $pdf->get_error_message() ), '', [ 'response' => 500, 'back_link' => true ] ); } $number = sanitize_file_name( (string) $document->getNumber() ?: 'credit-note' ); if ( ob_get_length() ) { @ob_end_clean(); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged } nocache_headers(); header( 'Content-Type: application/pdf' ); header( 'Content-Disposition: attachment; filename="' . $number . '.pdf"' ); header( 'Content-Length: ' . strlen( $pdf ) ); header( 'X-Content-Type-Options: nosniff' ); echo $pdf; // phpcs:ignore WordPress.Security.EscapeOutput -- binary document. exit; } /** * URL that downloads a credit note. * * @param int $credit_id Credit note ID. * @return string */ public static function downloadUrl( int $credit_id ): string { return wp_nonce_url( add_query_arg( [ 'action' => self::ACTION_DOWNLOAD, 'credit_id' => $credit_id, ], admin_url( 'admin-post.php' ) ), self::ACTION_DOWNLOAD . '_' . $credit_id ); } }