$name, 'value' => (string) $value, ); } }; $invoice = $order->get_meta( '_storesuit_invoice_info' ); if ( is_array( $invoice ) ) { $add( I18n::t( 'Invoice company' ), $invoice['company'] ?? '' ); $add( I18n::t( 'Invoice tax office' ), $invoice['tax_office'] ?? '' ); $add( I18n::t( 'Invoice tax or identity number' ), $invoice['tax_number'] ?? '' ); $add( I18n::t( 'Invoice document link' ), $invoice['document_url'] ?? '' ); $add( I18n::t( 'Invoice note' ), $invoice['note'] ?? '' ); } $add( I18n::t( 'Social media handle' ), $order->get_meta( '_storesuit_handle' ) ); foreach ( self::internal_note_ids( $order ) as $note_id ) { $add( I18n::t( 'Internal note' ), (string) get_comment( $note_id )->comment_content ); } foreach ( (array) $order->get_meta( '_storesuit_returns' ) as $row ) { if ( is_array( $row ) ) { $add( I18n::t( 'Return reason' ), $row['reason'] ?? '' ); } } $confirmed = $order->get_meta( '_storesuit_delivery_confirmed' ); if ( is_array( $confirmed ) ) { $add( I18n::t( 'Delivery recipient or reference' ), $confirmed['reference'] ?? '' ); } foreach ( (array) $order->get_meta( '_storesuit_payments' ) as $row ) { if ( is_array( $row ) ) { $add( I18n::t( 'Payment reference' ), $row['reference'] ?? '' ); } } foreach ( self::notice_ids( (int) $order->get_id() ) as $notice ) { $add( I18n::t( 'Payment notice payer' ), get_post_meta( $notice, '_payer', true ) ); $add( I18n::t( 'Payment notice reference' ), get_post_meta( $notice, '_reference', true ) ); $add( I18n::t( 'Payment notice note' ), get_post_meta( $notice, '_note', true ) ); $add( I18n::t( 'Payment notice review note' ), get_post_meta( $notice, '_review_note', true ) ); } return $data; } /** The internal notes a manual sale saved on this order that still exist as its notes (a paid build's order, read in both builds). @return int[] */ private static function internal_note_ids( \WC_Order $order ) : array { $ids = array(); foreach ( (array) (( $order->get_meta( Orders::INTERNAL_NOTES ) ?: array() )) as $id ) { $comment = get_comment( absint( $id ) ); if ( $comment && (int) $comment->comment_post_ID === (int) $order->get_id() && $comment->comment_type === 'order_note' ) { $ids[] = (int) $comment->comment_ID; } } return $ids; } /** The order's payment notices (their post type is named here: the free build also keeps notices from the paid one). @return int[] */ private static function notice_ids( int $order_id ) : array { return array_map( 'intval', get_posts( array( 'post_type' => 'ss_payment_notice', 'post_status' => 'any', 'fields' => 'ids', 'posts_per_page' => -1, 'meta_query' => array(array( 'key' => '_order_id', 'value' => $order_id, )), ) ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- one order's notices. } /** Runs after WooCommerce has anonymised and saved the order; a second run changes nothing. */ public static function erase( $order ) : void { if ( !$order instanceof \WC_Order ) { return; } $changed = false; $invoice = $order->get_meta( '_storesuit_invoice_info' ); if ( is_array( $invoice ) ) { $clean = self::invoice( $invoice ); if ( $clean !== $invoice ) { $order->update_meta_data( '_storesuit_invoice_info', $clean ); $changed = true; } } if ( $order->get_meta( '_storesuit_handle' ) !== '' ) { $order->delete_meta_data( '_storesuit_handle' ); $changed = true; } // The team's internal notes (ORD-5) are private order notes that may describe the person: deleted, with their list. foreach ( self::internal_note_ids( $order ) as $note_id ) { wc_delete_order_note( $note_id ); } if ( $order->get_meta( Orders::INTERNAL_NOTES ) ) { $order->delete_meta_data( Orders::INTERNAL_NOTES ); $changed = true; } $returns = $order->get_meta( '_storesuit_returns' ); if ( is_array( $returns ) ) { $clean = $returns; foreach ( $clean as $key => $row ) { if ( is_array( $row ) && ($row['reason'] ?? '') !== '' ) { $clean[$key]['reason'] = ''; } } if ( $clean !== $returns ) { $order->update_meta_data( '_storesuit_returns', $clean ); $changed = true; } } $link = $order->get_meta( '_storesuit_guest_link' ); if ( is_array( $link ) && !empty( $link['customer_id'] ) ) { $link['customer_id'] = 0; $order->update_meta_data( '_storesuit_guest_link', $link ); $changed = true; } // The references typed with the order's payments (an approved payment notice's reference is copied here; security audit S16). $payments = $order->get_meta( '_storesuit_payments' ); if ( is_array( $payments ) ) { $clean = $payments; foreach ( $clean as $key => $row ) { if ( is_array( $row ) && ($row['reference'] ?? '') !== '' ) { $clean[$key]['reference'] = ''; } } if ( $clean !== $payments ) { $order->update_meta_data( '_storesuit_payments', $clean ); $changed = true; } } if ( $changed ) { $order->save(); } self::events( (int) $order->get_id() ); self::notices( (int) $order->get_id() ); } /** The order's payment notices lose who paid, the reference and the free notes; one the customer sent, their account too. */ private static function notices( int $order_id ) : void { global $wpdb; foreach ( self::notice_ids( $order_id ) as $notice ) { foreach ( array( '_payer', '_reference', '_note', '_review_note' ) as $key ) { if ( (string) get_post_meta( (int) $notice, $key, true ) !== '' ) { update_post_meta( (int) $notice, $key, '' ); } } // Sent by the customer: the account that sent it is personal data (review of 0.57). One the team recorded keeps its author. if ( get_post_meta( (int) $notice, '_source', true ) !== 'customer' ) { continue; } if ( (int) get_post_field( 'post_author', (int) $notice ) !== 0 ) { $wpdb->update( $wpdb->posts, array( 'post_author' => 0, ), array( 'ID' => (int) $notice, ) ); clean_post_cache( (int) $notice ); } $wpdb->query( $wpdb->prepare( 'UPDATE %i SET actor=0 WHERE object_type=%s AND object_id=%d AND event=%s AND actor<>0', $wpdb->prefix . 'storesuit_events', 'payment_notice', (int) $notice, 'payment_notice_customer' ) ); } } private static function invoice( array $info ) : array { foreach ( self::INVOICE_PERSONAL as $key ) { if ( array_key_exists( $key, $info ) ) { $info[$key] = ''; } } return $info; } /** The order's activity rows: invoice details before and after, and order_edited snapshots without the person. */ private static function events( int $order_id ) : void { global $wpdb; $table = $wpdb->prefix . 'storesuit_events'; $rows = (array) $wpdb->get_results( $wpdb->prepare( "SELECT id,event,payload FROM %i WHERE object_type='order' AND object_id=%d AND event IN ('invoice_info_updated','order_edited','guest_order_linked')", $table, $order_id ), ARRAY_A ); foreach ( $rows as $row ) { $payload = json_decode( (string) $row['payload'], true ); if ( !is_array( $payload ) ) { continue; } $clean = $payload; if ( $row['event'] === 'guest_order_linked' ) { unset($clean['customer_id']); } foreach ( array('before', 'after') as $side ) { if ( !is_array( $clean[$side] ?? null ) ) { continue; } $clean[$side] = ( $row['event'] === 'invoice_info_updated' ? self::invoice( $clean[$side] ) : array_diff_key( $clean[$side], array_flip( self::SNAPSHOT_PERSONAL ) ) ); } if ( $clean !== $payload ) { $wpdb->update( $table, array( 'payload' => wp_json_encode( $clean ), ), array( 'id' => (int) $row['id'], ) ); } } } }