*
* @param int $order_id Th id of the order that has been deleted from the orders table.
* @return void
*/
protected function handle_order_deletion_with_sync_disabled( $order_id ): void {
global $wpdb;
$post_type = $wpdb->get_var(
$wpdb->prepare( "SELECT post_type FROM {$wpdb->posts} WHERE ID=%d", $order_id )
);
if ( DataSynchronizer::PLACEHOLDER_ORDER_POST_TYPE === $post_type ) {
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->posts} WHERE ID=%d OR post_parent=%d",
$order_id,
$order_id
)
);
} else {
// phpcs:disable WordPress.DB.SlowDBQuery
$wpdb->insert(
self::get_meta_table_name(),
array(
'order_id' => $order_id,
'meta_key' => DataSynchronizer::DELETED_RECORD_META_KEY,
'meta_value' => DataSynchronizer::DELETED_FROM_ORDERS_META_VALUE,
)
);
// phpcs:enable WordPress.DB.SlowDBQuery
// Note that at this point upshift_or_delete_child_orders will already have been invoked,
// thus all the child orders either still exist but have a different parent id,
// or have been deleted and got their own deletion record already.
// So there's no need to do anything about them.
}
}
/**
* Set the parent id of child orders to the parent order's parent if the post type
* for the order is hierarchical, just delete the child orders otherwise.
*
* @param \WC_Abstract_Order $order Order object.
*
* @return void
*/
private function upshift_or_delete_child_orders( $order ): void {
global $wpdb;
$order_table = self::get_orders_table_name();
$order_parent_id = $order->get_parent_id();
if ( $this->legacy_proxy->call_function( 'is_post_type_hierarchical', $order->get_type() ) ) {
$wpdb->update(
$order_table,
array( 'parent_order_id' => $order_parent_id ),
array( 'parent_order_id' => $order->get_id() ),
array( '%d' ),
array( '%d' )
);
} else {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$child_order_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT id FROM $order_table WHERE parent_order_id=%d",
$order->get_id()
)
);
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
foreach ( $child_order_ids as $child_order_id ) {
$child_order = wc_get_order( $child_order_id );
if ( $child_order ) {
$child_order->delete( true );
}
}
}
}
/**
* Trashes an order.
*
* @param WC_Order $order The order object.
*
* @return void
*/
public function trash_order( $order ) {
global $wpdb;
if ( 'trash' === $order->get_status( 'edit' ) ) {
return;
}
$trash_metadata = array(
'_wp_trash_meta_status' => 'wc-' . $order->get_status( 'edit' ),
'_wp_trash_meta_time' => time(),
);
$wpdb->update(
self::get_orders_table_name(),
array(
'status' => 'trash',
'date_updated_gmt' => current_time( 'Y-m-d H:i:s', true ),
),
array( 'id' => $order->get_id() ),
array( '%s', '%s' ),
array( '%d' )
);
$order->set_status( 'trash' );
foreach ( $trash_metadata as $meta_key => $meta_value ) {
$this->add_meta(
$order,
(object) array(
'key' => $meta_key,
'value' => $meta_value,
)
);
}
$data_synchronizer = wc_get_container()->get( DataSynchronizer::class );
if ( $data_synchronizer->data_sync_is_enabled() ) {
wp_trash_post( $order->get_id() );
}
}
/**
* Attempts to restore the specified order back to its original status (after having been trashed).
*
* @param WC_Order $order The order to be untrashed.
*
* @return bool If the operation was successful.
*/
public function untrash_order( WC_Order $order ): bool {
$id = $order->get_id();
$status = $order->get_status();
if ( 'trash' !== $status ) {
wc_get_logger()->warning(
sprintf(
/* translators: 1: order ID, 2: order status */
__( 'Order %1$d cannot be restored from the trash: it has already been restored to status "%2$s".', 'woocommerce' ),
$id,
$status
)
);
return false;
}
$previous_status = $order->get_meta( '_wp_trash_meta_status' );
$valid_statuses = wc_get_order_statuses();
$previous_state_is_invalid = ! array_key_exists( $previous_status, $valid_statuses );
$pending_is_valid_status = array_key_exists( 'wc-pending', $valid_statuses );
if ( $previous_state_is_invalid && $pending_is_valid_status ) {
// If the previous status is no longer valid, let's try to restore it to "pending" instead.
wc_get_logger()->warning(
sprintf(
/* translators: 1: order ID, 2: order status */
__( 'The previous status of order %1$d ("%2$s") is invalid. It has been restored to "pending" status instead.', 'woocommerce' ),
$id,
$previous_status
)
);
$previous_status = 'pending';
} elseif ( $previous_state_is_invalid ) {
// If we cannot restore to pending, we should probably stand back and let the merchant intervene some other way.
wc_get_logger()->warning(
sprintf(
/* translators: 1: order ID, 2: order status */
__( 'The previous status of order %1$d ("%2$s") is invalid. It could not be restored.', 'woocommerce' ),
$id,
$previous_status
)
);
return false;
}
/**
* Fires before an order is restored from the trash.
*
* @since 7.2.0
*
* @param int $order_id Order ID.
* @param string $previous_status The status of the order before it was trashed.
*/
do_action( 'woocommerce_untrash_order', $order->get_id(), $previous_status );
$order->set_status( $previous_status );
$order->save();
// Was the status successfully restored? Let's clean up the meta and indicate success...
if ( 'wc-' . $order->get_status() === $previous_status ) {
$order->delete_meta_data( '_wp_trash_meta_status' );
$order->delete_meta_data( '_wp_trash_meta_time' );
$order->delete_meta_data( '_wp_trash_meta_comments_status' );
$order->save_meta_data();
return true;
}
// ...Or log a warning and bail.
wc_get_logger()->warning(
sprintf(
/* translators: 1: order ID, 2: order status */
__( 'Something went wrong when trying to restore order %d from the trash. It could not be restored.', 'woocommerce' ),
$id
)
);
return false;
}
/**
* Deletes order data from custom order tables.
*
* @param int $order_id The order ID.
* @return void
*/
public function delete_order_data_from_custom_order_tables( $order_id ) {
global $wpdb;
$order_cache = wc_get_container()->get( OrderCache::class );
// Delete COT-specific data.
foreach ( $this->get_all_table_names() as $table ) {
$wpdb->delete(
$table,
( self::get_orders_table_name() === $table )
? array( 'id' => $order_id )
: array( 'order_id' => $order_id ),
array( '%d' )
);
$order_cache->remove( $order_id );
}
}
/**
* Method to create an order in the database.
*
* @param \WC_Order $order Order object.
*/
public function create( &$order ) {
if ( '' === $order->get_order_key() ) {
$order->set_order_key( wc_generate_order_key() );
}
$this->persist_save( $order );
// Do not fire 'woocommerce_new_order' for draft statuses for backwards compatibility.
if ( in_array( $order->get_status( 'edit' ), array( 'auto-draft', 'draft', 'checkout-draft' ), true ) ) {
return;
}
/**
* Fires when a new order is created.
*
* @since 2.7.0
*
* @param int Order ID.
* @param \WC_Order Order object.
*/
do_action( 'woocommerce_new_order', $order->get_id(), $order );
}
/**
* Helper method responsible for persisting new data to order table.
*
* This should not contain and specific meta or actions, so that it can be used other order types safely.
*
* @param \WC_Order $order Order object.
* @param bool $force_all_fields Force update all fields, instead of calculating and updating only changed fields.
* @param bool $backfill Whether to backfill data to post datastore.
*
* @return void
*
* @throws \Exception When unable to save data.
*/
protected function persist_save( &$order, bool $force_all_fields = false, $backfill = true ) {
$order->set_version( Constants::get_constant( 'WC_VERSION' ) );
$order->set_currency( $order->get_currency() ? $order->get_currency() : get_woocommerce_currency() );
if ( ! $order->get_date_created( 'edit' ) ) {
$order->set_date_created( time() );
}
if ( ! $order->get_date_modified( 'edit' ) ) {
$order->set_date_modified( current_time( 'mysql' ) );
}
$this->persist_order_to_db( $order, $force_all_fields );
$this->update_order_meta( $order );
$order->save_meta_data();
$order->apply_changes();
if ( $backfill ) {
self::$backfilling_order_ids[] = $order->get_id();
$r_order = wc_get_order( $order->get_id() ); // Refresh order to account for DB changes from post hooks.
$this->maybe_backfill_post_record( $r_order );
self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $order->get_id() ) );
}
$this->clear_caches( $order );
}
/**
* Method to update an order in the database.
*
* @param \WC_Order $order Order object.
*/
public function update( &$order ) {
$previous_status = ArrayUtil::get_value_or_default( $order->get_data(), 'status', 'new' );
// Before updating, ensure date paid is set if missing.
if (
! $order->get_date_paid( 'edit' )
&& version_compare( $order->get_version( 'edit' ), '3.0', '<' )
&& $order->has_status( apply_filters( 'woocommerce_payment_complete_order_status', $order->needs_processing() ? 'processing' : 'completed', $order->get_id(), $order ) ) // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
) {
$order->set_date_paid( $order->get_date_created( 'edit' ) );
}
if ( null === $order->get_date_created( 'edit' ) ) {
$order->set_date_created( time() );
}
$order->set_version( Constants::get_constant( 'WC_VERSION' ) );
// Fetch changes.
$changes = $order->get_changes();
$this->persist_updates( $order );
// Update download permissions if necessary.
if ( array_key_exists( 'billing_email', $changes ) || array_key_exists( 'customer_id', $changes ) ) {
$data_store = \WC_Data_Store::load( 'customer-download' );
$data_store->update_user_by_order_id( $order->get_id(), $order->get_customer_id(), $order->get_billing_email() );
}
// Mark user account as active.
if ( array_key_exists( 'customer_id', $changes ) ) {
wc_update_user_last_active( $order->get_customer_id() );
}
$order->apply_changes();
$this->clear_caches( $order );
$draft_statuses = array( 'new', 'auto-draft', 'draft', 'checkout-draft' );
// For backwards compatibility, this hook should be fired only if the new status is not one of the draft statuses and the previous status was one of the draft statuses.
if (
! empty( $changes['status'] )
&& $changes['status'] !== $previous_status
&& ! in_array( $changes['status'], $draft_statuses, true )
&& in_array( $previous_status, $draft_statuses, true )
) {
do_action( 'woocommerce_new_order', $order->get_id(), $order ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
return;
}
// For backwards compat with CPT, trashing/untrashing and changing previously datastore-level props does not trigger the update hook.
if ( ( ! empty( $changes['status'] ) && in_array( 'trash', array( $changes['status'], $previous_status ), true ) )
|| ( ! empty( $changes ) && ! array_diff_key( $changes, array_flip( $this->get_post_data_store_for_backfill()->get_internal_data_store_key_getters() ) ) ) ) {
return;
}
do_action( 'woocommerce_update_order', $order->get_id(), $order ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
}
/**
* Proxy to updating order meta. Here for backward compatibility reasons.
*
* @param \WC_Order $order Order object.
*
* @return void
*/
protected function update_post_meta( &$order ) {
$this->update_order_meta( $order );
}
/**
* Helper method that is responsible for persisting order updates to the database.
*
* This is expected to be reused by other order types, and should not contain any specific metadata updates or actions.
*
* @param \WC_Order $order Order object.
* @param bool $backfill Whether to backfill data to post tables.
*
* @return array $changes Array of changes.
*
* @throws \Exception When unable to persist order.
*/
protected function persist_updates( &$order, $backfill = true ) {
// Fetch changes.
$changes = $order->get_changes();
if ( ! isset( $changes['date_modified'] ) ) {
$order->set_date_modified( current_time( 'mysql' ) );
}
$this->persist_order_to_db( $order );
$this->update_order_meta( $order );
$order->save_meta_data();
if ( $backfill ) {
self::$backfilling_order_ids[] = $order->get_id();
$this->clear_caches( $order );
$r_order = wc_get_order( $order->get_id() ); // Refresh order to account for DB changes from post hooks.
$this->maybe_backfill_post_record( $r_order );
self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $order->get_id() ) );
}
return $changes;
}
/**
* Helper method to check whether to backfill post record.
*
* @return bool
*/
private function should_backfill_post_record() {
$data_sync = wc_get_container()->get( DataSynchronizer::class );
return $data_sync->data_sync_is_enabled();
}
/**
* Helper function to decide whether to backfill post record.
*
* @param \WC_Abstract_Order $order Order object.
*
* @return void
*/
private function maybe_backfill_post_record( $order ) {
if ( $this->should_backfill_post_record() ) {
$this->backfill_post_record( $order );
}
}
/**
* Helper method that updates post meta based on an order object.
* Mostly used for backwards compatibility purposes in this datastore.
*
* @param \WC_Order $order Order object.
*
* @since 7.0.0
*/
public function update_order_meta( &$order ) {
$changes = $order->get_changes();
$this->update_address_index_meta( $order, $changes );
}
/**
* Helper function to update billing and shipping address metadata.
*
* @param \WC_Abstract_Order $order Order Object.
* @param array $changes Array of changes.
*
* @return void
*/
private function update_address_index_meta( $order, $changes ) {
// If address changed, store concatenated version to make searches faster.
foreach ( array( 'billing', 'shipping' ) as $address_type ) {
$index_meta_key = "_{$address_type}_address_index";
if ( isset( $changes[ $address_type ] ) || ( is_a( $order, 'WC_Order' ) && empty( $order->get_meta( $index_meta_key ) ) ) ) {
$order->update_meta_data( $index_meta_key, implode( ' ', $order->get_address( $address_type ) ) );
}
}
}
/**
* Return array of coupon_code => meta_key for coupon which have usage limit and have tentative keys.
* Pass $coupon_id if key for only one of the coupon is needed.
*
* @param WC_Order $order Order object.
* @param int $coupon_id If passed, will return held key for that coupon.
*
* @return array|string Key value pair for coupon code and meta key name. If $coupon_id is passed, returns meta_key for only that coupon.
*/
public function get_coupon_held_keys( $order, $coupon_id = null ) {
$held_keys = $order->get_meta( '_coupon_held_keys' );
if ( $coupon_id ) {
return isset( $held_keys[ $coupon_id ] ) ? $held_keys[ $coupon_id ] : null;
}
return $held_keys;
}
/**
* Return array of coupon_code => meta_key for coupon which have usage limit per customer and have tentative keys.
*
* @param WC_Order $order Order object.
* @param int $coupon_id If passed, will return held key for that coupon.
*
* @return mixed
*/
public function get_coupon_held_keys_for_users( $order, $coupon_id = null ) {
$held_keys_for_user = $order->get_meta( '_coupon_held_keys_for_users' );
if ( $coupon_id ) {
return isset( $held_keys_for_user[ $coupon_id ] ) ? $held_keys_for_user[ $coupon_id ] : null;
}
return $held_keys_for_user;
}
/**
* Add/Update list of meta keys that are currently being used by this order to hold a coupon.
* This is used to figure out what all meta entries we should delete when order is cancelled/completed.
*
* @param WC_Order $order Order object.
* @param array $held_keys Array of coupon_code => meta_key.
* @param array $held_keys_for_user Array of coupon_code => meta_key for held coupon for user.
*
* @return mixed
*/
public function set_coupon_held_keys( $order, $held_keys, $held_keys_for_user ) {
if ( is_array( $held_keys ) && 0 < count( $held_keys ) ) {
$order->update_meta_data( '_coupon_held_keys', $held_keys );
}
if ( is_array( $held_keys_for_user ) && 0 < count( $held_keys_for_user ) ) {
$order->update_meta_data( '_coupon_held_keys_for_users', $held_keys_for_user );
}
}
/**
* Release all coupons held by this order.
*
* @param WC_Order $order Current order object.
* @param bool $save Whether to delete keys from DB right away. Could be useful to pass `false` if you are building a bulk request.
*/
public function release_held_coupons( $order, $save = true ) {
$coupon_held_keys = $this->get_coupon_held_keys( $order );
if ( is_array( $coupon_held_keys ) ) {
foreach ( $coupon_held_keys as $coupon_id => $meta_key ) {
$coupon = new \WC_Coupon( $coupon_id );
$coupon->delete_meta_data( $meta_key );
$coupon->save_meta_data();
}
}
$order->delete_meta_data( '_coupon_held_keys' );
$coupon_held_keys_for_users = $this->get_coupon_held_keys_for_users( $order );
if ( is_array( $coupon_held_keys_for_users ) ) {
foreach ( $coupon_held_keys_for_users as $coupon_id => $meta_key ) {
$coupon = new \WC_Coupon( $coupon_id );
$coupon->delete_meta_data( $meta_key );
$coupon->save_meta_data();
}
}
$order->delete_meta_data( '_coupon_held_keys_for_users' );
if ( $save ) {
$order->save_meta_data();
}
}
/**
* Performs actual query to get orders. Uses `OrdersTableQuery` to build and generate the query.
*
* @param array $query_vars Query variables.
*
* @return array|object List of orders and count of orders.
*/
public function query( $query_vars ) {
if ( ! isset( $query_vars['paginate'] ) || ! $query_vars['paginate'] ) {
$query_vars['no_found_rows'] = true;
}
if ( isset( $query_vars['anonymized'] ) ) {
$query_vars['meta_query'] = $query_vars['meta_query'] ?? array(); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
if ( $query_vars['anonymized'] ) {
$query_vars['meta_query'][] = array(
'key' => '_anonymized',
'value' => 'yes',
);
} else {
$query_vars['meta_query'][] = array(
'key' => '_anonymized',
'compare' => 'NOT EXISTS',
);
}
}
try {
$query = new OrdersTableQuery( $query_vars );
} catch ( \Exception $e ) {
$query = (object) array(
'orders' => array(),
'found_orders' => 0,
'max_num_pages' => 0,
);
}
if ( isset( $query_vars['return'] ) && 'ids' === $query_vars['return'] ) {
$orders = $query->orders;
} else {
$orders = WC()->order_factory->get_orders( $query->orders );
}
if ( isset( $query_vars['paginate'] ) && $query_vars['paginate'] ) {
return (object) array(
'orders' => $orders,
'total' => $query->found_orders,
'max_num_pages' => $query->max_num_pages,
);
}
return $orders;
}
//phpcs:enable Squiz.Commenting, Generic.Commenting
/**
* Get the SQL needed to create all the tables needed for the custom orders table feature.
*
* @return string
*/
public function get_database_schema() {
global $wpdb;
$collate = $wpdb->has_cap( 'collation' ) ? $wpdb->get_charset_collate() : '';
$orders_table_name = $this->get_orders_table_name();
$addresses_table_name = $this->get_addresses_table_name();
$operational_data_table_name = $this->get_operational_data_table_name();
$meta_table = $this->get_meta_table_name();
$max_index_length = $this->database_util->get_max_index_length();
$composite_meta_value_index_length = max( $max_index_length - 8 - 100 - 1, 20 ); // 8 for order_id, 100 for meta_key, 10 minimum for meta_value.
$composite_customer_id_email_length = max( $max_index_length - 20, 20 ); // 8 for customer_id, 20 minimum for email.
$sql = "
CREATE TABLE $orders_table_name (
id bigint(20) unsigned,
status varchar(20) null,
currency varchar(10) null,
type varchar(20) null,
tax_amount decimal(26,8) null,
total_amount decimal(26,8) null,
customer_id bigint(20) unsigned null,
billing_email varchar(320) null,
date_created_gmt datetime null,
date_updated_gmt datetime null,
parent_order_id bigint(20) unsigned null,
payment_method varchar(100) null,
payment_method_title text null,
transaction_id varchar(100) null,
ip_address varchar(100) null,
user_agent text null,
customer_note text null,
PRIMARY KEY (id),
KEY status (status),
KEY date_created (date_created_gmt),
KEY customer_id_billing_email (customer_id, billing_email({$composite_customer_id_email_length})),
KEY billing_email (billing_email($max_index_length)),
KEY type_status_date (type, status, date_created_gmt),
KEY parent_order_id (parent_order_id),
KEY date_updated (date_updated_gmt)
) $collate;
CREATE TABLE $addresses_table_name (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned NOT NULL,
address_type varchar(20) null,
first_name text null,
last_name text null,
company text null,
address_1 text null,
address_2 text null,
city text null,
state text null,
postcode text null,
country text null,
email varchar(320) null,
phone varchar(100) null,
KEY order_id (order_id),
UNIQUE KEY address_type_order_id (address_type, order_id),
KEY email (email($max_index_length)),
KEY phone (phone)
) $collate;
CREATE TABLE $operational_data_table_name (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned NULL,
created_via varchar(100) NULL,
woocommerce_version varchar(20) NULL,
prices_include_tax tinyint(1) NULL,
coupon_usages_are_counted tinyint(1) NULL,
download_permission_granted tinyint(1) NULL,
cart_hash varchar(100) NULL,
new_order_email_sent tinyint(1) NULL,
order_key varchar(100) NULL,
order_stock_reduced tinyint(1) NULL,
date_paid_gmt datetime NULL,
date_completed_gmt datetime NULL,
shipping_tax_amount decimal(26,8) NULL,
shipping_total_amount decimal(26,8) NULL,
discount_tax_amount decimal(26,8) NULL,
discount_total_amount decimal(26,8) NULL,
recorded_sales tinyint(1) NULL,
UNIQUE KEY order_id (order_id),
KEY order_key (order_key)
) $collate;
CREATE TABLE $meta_table (
id bigint(20) unsigned auto_increment primary key,
order_id bigint(20) unsigned null,
meta_key varchar(255),
meta_value text null,
KEY meta_key_value (meta_key(100), meta_value($composite_meta_value_index_length)),
KEY order_id_meta_key_meta_value (order_id, meta_key(100), meta_value($composite_meta_value_index_length))
) $collate;
";
return $sql;
}
/**
* Returns an array of meta for an object.
*
* @param WC_Data $object WC_Data object.
* @return array
*/
public function read_meta( &$object ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound
$raw_meta_data = $this->data_store_meta->read_meta( $object );
return $this->filter_raw_meta_data( $object, $raw_meta_data );
}
/**
* Deletes meta based on meta ID.
*
* @param WC_Data $object WC_Data object.
* @param \stdClass $meta (containing at least ->id).
*
* @return bool
*/
public function delete_meta( &$object, $meta ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound
global $wpdb;
if ( $this->should_backfill_post_record() && isset( $meta->id ) ) {
// Let's get the actual meta key before its deleted for backfilling. We cannot delete just by ID because meta IDs are different in HPOS and posts tables.
$db_meta = $this->data_store_meta->get_metadata_by_id( $meta->id );
if ( $db_meta ) {
$meta->key = $db_meta->meta_key;
$meta->value = $db_meta->meta_value;
}
}
$delete_meta = $this->data_store_meta->delete_meta( $object, $meta );
$changes_applied = $this->after_meta_change( $object, $meta );
if ( ! $changes_applied && $object instanceof WC_Abstract_Order && $this->should_backfill_post_record() && isset( $meta->key ) ) {
self::$backfilling_order_ids[] = $object->get_id();
if ( is_object( $meta->value ) && '__PHP_Incomplete_Class' === get_class( $meta->value ) ) {
$meta_value = maybe_serialize( $meta->value );
$wpdb->delete(
_get_meta_table( 'post' ),
array(
'post_id' => $object->get_id(),
'meta_key' => $meta->key, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_value' => $meta_value, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
),
array( '%d', '%s', '%s' )
);
wp_cache_delete( $object->get_id(), 'post_meta' );
$logger = wc_get_container()->get( LegacyProxy::class )->call_function( 'wc_get_logger' );
$logger->warning( sprintf( 'encountered an order meta value of type __PHP_Incomplete_Class during `delete_meta` in order with ID %d: "%s"', $object->get_id(), var_export( $meta_value, true ) ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
} else {
delete_post_meta( $object->get_id(), $meta->key, $meta->value );
}
self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $object->get_id() ) );
}
return $delete_meta;
}
/**
* Add new piece of meta.
*
* @param WC_Data $object WC_Data object.
* @param \stdClass $meta (containing ->key and ->value).
*
* @return int|bool meta ID or false on failure
*/
public function add_meta( &$object, $meta ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound
$add_meta = $this->data_store_meta->add_meta( $object, $meta );
$meta->id = $add_meta;
$changes_applied = $this->after_meta_change( $object, $meta );
if ( ! $changes_applied && $object instanceof WC_Abstract_Order && $this->should_backfill_post_record() ) {
self::$backfilling_order_ids[] = $object->get_id();
add_post_meta( $object->get_id(), $meta->key, $meta->value );
self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $object->get_id() ) );
}
return $add_meta;
}
/**
* Update meta.
*
* @param WC_Data $object WC_Data object.
* @param \stdClass $meta (containing ->id, ->key and ->value).
*
* @return bool The number of rows updated, or false on error.
*/
public function update_meta( &$object, $meta ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.objectFound
$update_meta = $this->data_store_meta->update_meta( $object, $meta );
$changes_applied = $this->after_meta_change( $object, $meta );
if ( ! $changes_applied && $object instanceof WC_Abstract_Order && $this->should_backfill_post_record() ) {
self::$backfilling_order_ids[] = $object->get_id();
update_post_meta( $object->get_id(), $meta->key, $meta->value );
self::$backfilling_order_ids = array_diff( self::$backfilling_order_ids, array( $object->get_id() ) );
}
return $update_meta;
}
/**
* Perform after meta change operations, including updating the date_modified field, clearing caches and applying changes.
*
* @param WC_Abstract_Order $order Order object.
* @param \WC_Meta_Data $meta Metadata object.
*
* @return bool True if changes were applied, false otherwise.
*/
protected function after_meta_change( &$order, $meta ) {
method_exists( $meta, 'apply_changes' ) && $meta->apply_changes();
// Prevent this happening multiple time in same request.
if ( $this->should_save_after_meta_change( $order, $meta ) ) {
$order->set_date_modified( current_time( 'mysql' ) );
$order->save();
return true;
} else {
$order_cache = wc_get_container()->get( OrderCache::class );
$order_cache->remove( $order->get_id() );
}
return false;
}
/**
* Helper function to check whether the modified date needs to be updated after a meta save.
*
* This method prevents order->save() call multiple times in the same request after any meta update by checking if:
* 1. Order modified date is already the current date, no updates needed in this case.
* 2. If there are changes already queued for order object, then we don't need to update the modified date as it will be updated ina subsequent save() call.
*
* @param WC_Order $order Order object.
* @param \WC_Meta_Data|null $meta Metadata object.
*
* @return bool Whether the modified date needs to be updated.
*/
private function should_save_after_meta_change( $order, $meta = null ) {
$current_time = $this->legacy_proxy->call_function( 'current_time', 'mysql', 1 );
$current_date_time = new \WC_DateTime( $current_time, new \DateTimeZone( 'GMT' ) );
$should_save =
$order->get_date_modified() < $current_date_time && empty( $order->get_changes() )
&& ( ! is_object( $meta ) || ! in_array( $meta->key, $this->ephemeral_meta_keys, true ) );
/**
* Allows code to skip a full order save() when metadata is changed.
*
* @since 8.8.0
*
* @param bool $should_save Whether to trigger a full save after metadata is changed.
*/
return apply_filters( 'woocommerce_orders_table_datastore_should_save_after_meta_change', $should_save );
}
}
Đầu ghi hình DVR KBvision KX-8104TH1 - Ngân Vũ TelecomSkip to content
• Đầu ghi hình HD (5 in 1) vỏ nhựa.
• Đầu ghi hình 4 kênh + 2 kênh IP
• Hỗ trợ kết nối cùng lúc CVI /TVI/AHD/ analog / IP (Camera IP lên đến 6Mp) dễ dàng nâng cấp từ hệ thống analog cũ và có thể kết nối camera IP từ địa điểm khác giảm chi phí
• Hỗ trợ camera HDCVI 5MP
• Khoảng cách kết nối từ camera lên đến 1200m ( đối với camera 1.0Mp và 1.3Mp khi sử dụng cáp 75-5), 800m ( dối với camera 2.0 Mp khi sử dụng cáp 75-5), 700m với camera HDCVI 4MP (2K).
• Chuẩn nén hình ảnh: H.265+ giúp tiết kiệm băng thông và ổ cứng
• Ghi hình ở độ phân giải: 5M-N, 4M-N, 1080P, 1080N, 720P,…..
• Cổng ra: VGA/HDMI
• Xem lại: 1/4 kênh đồng thời đối với đầu 4 kênh
• Audio : 1 cổng vào 1 cổng ra , Hỗ trợ âm thanh 2 chiều
• Hỗ trợ chuẩn Onvif 16.12 có thể kết nối với camera IP hãng khác Hỗ trợ Onvif
• Hỗ trợ tên miền miễn phí KBVISION.TV trọn đời sản phẩm
• Hỗ trợ Cloud (server tại Việt Nam hoạt động mạnh mẽ , ổn định) chỉ cần scan QR Code :dễ dàng cài đặt, quan sát qua phần mềm trên điện thoại.
• Hỗ trợ tính năng thông minh như: phát hiện chuyển động, phát hiện khuôn mặt, hàng rào ảo, mất hoặc bỏ rơi vật thể….
• Hỗ trợ Push video báo động qua thiết bị di động trên phần mềm KBView Pro
• Hỗ trợ: 1 SATA x 6TB (KX-8104TH1), 1 SATA x 10TB (KX-8108TH1), 2 USB 2.0
• Hỗ trợ 1 cổng RJ45 (100Mbps)
• Hỗ trợ 128 user truy cập cùng lúc thích hợp cho những dự án như trường học , trường mầm non …..
• Hỗ trợ truyền tải âm thanh, báo động qua cáp đồng trục
• Hỗ trợ kết nối camera PIR, báo động chuyển động hiệu quả hơn.
• Thiết kế quạt tản nhiệt thông minh
• Có nút reset cứng trên mainboard
• Nâng cao chất lượng hình ảnh hiển thị
HIKVISION là một trong những nhà cung cấp hàng đầu thế giới về các sản phẩm giám sát hình ảnh và các giải pháp. Được thành lập vào năm 2001, hiện nay Hikvision là doanh nghiệp toàn cầu với hơn 20.000 nhân viên – trong đó có 9.000 kỹ sư Nghiên cứu & Phát triển (R&D). Với đội R&D lớn nhất trong ngành công nghiệp an ninh với 621 đăng ký sáng chế và 259 bản quyền phần mềm giúp công ty có khả năng đổi mới liên tục. Sản phẩm của công ty có chất lượng cao, đạt các tiêu chuẩn quốc tế: ISO, CE, CCC, UL, FCC, RoHS… Những sản phẩm này đã được sử dụng tại hơn 100 quốc gia.
Các sản phẩm, giải pháp của Hikvision được tin cậy và sử dụng tại các công trình lớn trên khắp thế giới: Sân vận động tổ chim (Bắc Kinh – Trung Quốc); Hệ thống giao thông công cộng London – Anh; Hệ thống giám sát thành phố tại Singapore – Safe City với hơn 100.000 camera IP…
Trụ sở chính đặt tại Hàng Châu, Trung Quốc, Hikvision vươn ra toàn cầu với 25 chi nhánh tại các nước: Hoa Kỳ, Hà Lan, Ý, Anh, Singapore, Australia, Brazil, Nam Phi và Dubai…; công ty liên doanh tại Ấn Độ và Nga; 35 chi nhánh trên toàn Trung Quốc và một trung tâm bảo hành tại Hồng Kông. Tổng diện tích 2 nhà máy sản xuất lên tới 4,2 triệu m2
Luôn nỗ lực nghiên cứu – phát triển các sản phẩm mới, chất lượng cao, Hikvision đã khẳng định vị trí thương hiệu trên toàn thế giới với tốc độ tăng trưởng hàng năm trên 50%, doanh số toàn cầu năm 2016 đạt xấp xỉ 5 tỷ USD. Hàng năm, Hikvision đầu tư 8% doanh số cho nghiên cứu và phát triển. Luôn đứng đầu trong các bảng xếp hạng về thiết bị an ninh và camera quan sát, Hikvision được công nhận là Nhà sản xuất lớn nhất thế giới về camera quan sát – Theo nghiên cứu của Tạp chí A&S (Tạp chí uy tín hàng đầu thế giới trong lĩnh vực thiết bị an ninh – website: www.asmag.com).
SOLARTECH
CÔNG TY CỔ PHẦN SOLARTECH là một trong những chuyên gia đi đầu trong lĩnh vực năng lượng sạch tại Việt Nam, với phương châm UY TÍN – CHẤT LƯỢNG – CHUYÊN NGHIỆP chúng tôi luôn cung cấp đến khách hàng những sản phẩm tốt nhất, nhưng công nghệ hàng đầu về năng lượng tái tạo như: điện mặt trời, năng lượng gió và các sản phẩm tiện nghi như: nhà thông minh – Smart Home.
Một trong những hoạt động chính của Công ty là cung cấp các giải pháp về điện năng lượng mặt trời – nguồn năng lượng tái tạo vô tận, thân thiện với môi trường đến cho các khách hàng như hộ gia đình, cơ sở kinh doanh, các nhà máy sản xuất… nhằm góp phần bảo vệ môi trường sống của con người Việt Nam.
Trong quá trình phát triển và hội nhập, Công ty đã tạo được uy tín vững vàng trên thị trường. Cùng với hệ thống quản lý chất lượng hoạt động sản xuất của Doanh nghiệp: An toàn – Tiến độ – Chất lượng và cải tiến liên tục chất lượng phục vụ cũng như sản phẩm tiên tiến, hiện đại nhất, bắt kịp với công nghệ của thế giới hiện nay.
Với kinh nghiệm, thâm niên trong lĩnh vực cung cấp máy phát điện, CÔNG TY CỔ PHẦN SOLARTECH đã nghiên cứu và lắp ráp các hệ thống máy phát điện không khói, ứng dụng công nghệ tự động theo tiêu chuẩn của Đức, sử dụng công nghê điện mặt trời cấp điện cho gia đình, cơ sở kinh doanh của bạn, luôn đảm bảo nguồn điện cho các thiết bị hoạt động 24/24. Vì thế, quý khách hoàn tàn an tâm khi hợp tác với chúng tôi. Các chuyên gia về điện mặt trời, nhà thông minh và tập thể nhân viên công ty cam kết luôn đem lại cho quý khách những sản phẩm chất lượng nhất, tốt nhất.
dahua
Công ty Công Nghệ Dahua là nhà sản xuất chuyên nghiệp trong lĩnh vực anh ninh và giám sát. Qua hơn 15 năm phát triển, Công ty Công Nghệ Dahua có bề dày kinh nghiệm trong nghiên cứu và thiết kế thiết bị giám sát với công nghệ tiên tiến.
Cả phần cứng và phần mềm đều được thiết kế bằng module với tính linh hoạt cho các cấu hình khác nhau, quy mô ứng dụng đa dạng và cho phép mở rộng trong tương lai. Dahua hiện đang đứng thứ 9 thế giới trong top 50 nhà cung cấp thiết bị an ninh hàng đầu thế giới được xếp hạng bởi a&s International vào năm 2013
Dahua Technology là một nhà sản xuất thiết bị an ninh hàng đầu thế giới. Công ty Camera Dahua có thị phần lớn nhất nhì thế giới về camera quan sát theo báo cáo của IMS năm 2015. Camera Dahua tin tưởng vào việc đầu tư và xây dựng năng lực R & D mạnh mẽ cho công nghệ mới và sự đổi mới. Dahua đã đầu tư gần 10% doanh thu bán hàng hàng năm. Thương hiệu camera Dahua có hơn 3000 chuyên gia trong đội ngũ R & D, chuyên cung cấp các dòng sản phẩm tiên tiến với chất lượng cao và hiệu suất lớn. Giải pháp Dahua được thiết kế để được mở rộng và phát triển để cung cấp các tùy chọn cấu hình nâng cấp hệ thống linh hoạt. Camera Dahua có một danh mục đầu tư bằng sáng chế với 442 bằng sáng chế và chúng tôi cấp phép công nghệ của chúng tôi để các nhà sản xuất khác.
Danh mục sản phẩm của Dahua bao gồm: Thiết bị nâng cao Video giám sát và phần mềm, điều khiển truy cập phần cứng và phần mềm, phần cứng xâm nhập, và thông minh Hệ thống quản lý tòa nhà.
Sản phẩm của Dahua được sử dụng rộng rãi trong nhiều lĩnh vực, chẳng hạn như ngân hàng, an ninh công cộng, cơ sở hạ tầng năng lượng, viễn thông, thông minh, xây dựng, giao thông vận tải và thông minh. Nhiều dự án quan trọng đã được cài đặt với các giải pháp của Dahua bao gồm: Nhà máy thủy điện Tam Hiệp, Six-Country Summit, Olympic Địa điểm, Shanghai World Expo, UNESCO, và tàu điện ngầm London cũng như nhiều người khác.
Dahua đã thành lập doanh số bán hàng, tiếp thị và mạng lưới dịch vụ trên toàn cầu. Camera Dahua có văn phòng tại Trung Quốc đại lục, lớn châu Á, châu Mỹ, châu Âu, châu Đại Dương, châu Phi và những nơi khác. Dấu chân toàn cầu của chúng tôi làm cho đáp ứng cho thị trường với các dịch vụ tốt nhất và các sản phẩm có thể.
KBVISION
KBVISION GROUP INC được thành lập tại Trung tâm Thung lũng Silicon, Hoa Kỳ, tập đoàn phát triển và phát triển từ lợi nhuận được tạo ra hữu cơ, trở thành công ty hàng đầu thế giới trong lĩnh vực kinh doanh cốt lõi là hệ thống giám sát, năng lượng xanh, phần mềm y tế, nghiên cứu AI và phát triển trí tuệ nhân tạo và các dự án tàu cao tốc.
CÔNG TY TNHH TM KBVISION VIỆT NAM tự hào là đại diện tiêu biểu cho Tập đoàn KBVISION GROUP INC. tại Việt Nam, chuyên cung cấp giải pháp an ninh toàn diện thông qua các dòng sản phẩm: Camera, Đầu ghi hình, thiết bị báo động, chống trộm, khóa thông minh,… được đảm bảo cao về chất lượng và tiêu chuẩn của Mỹ, với mức giá cạnh tranh và dịch vụ chăm sóc khách hàng chu đáo.
Hiện tại, KBVISION VIỆT NAM có hệ thống tổ chức hơn 500 nhân viên bao gồm trong 03 trụ sở giao dịch tại Tp. Hà Nội (miền Bắc), Tp. Đà Nẵng (miền Trung) và Tp. Hồ Chí Minh (miền Nam – Trụ sở chính) đang hoạt động đồng thời với hơn 1,500 Đại lý kinh doanh và lắp đặt dàn trải trên khắp cả nước.
HDParagon
HDParagon là một trong những nhà cung cấp hàng đầu thế giới về dòng sản phẩm camera giám sát ghi hình và các giải pháp. Được thành lập vào năm 2001 tại Đài Loan, HDParagon đã phát triển từ một công ty nhỏ chỉ với 28 nhân viên thành một doanh nghiệp với quy mô toàn cầu với hơn 5.000 nhân viên. Trong đó, có 1.400 kỹ sư chuyên nghiên cứu và phát triển công nghệ – một trong những đội ngũ lớn nhất trong ngành công nghiệp và không ngừng được đổi mới.
HDParagon tham gia vào phát triển sản xuất và kinh doanh hệ thống giám sát analog và hệ thống giám sát qua mạng. HDParagon cung cấp các dòng sản phẩm cao cấp: Đầu ghi hình tích hợp số (Hybird Digital Video Recorder), Đầu ghi hình mạng (Network Video Recorder), Đầu ghi hình chuẩn kỹ thuật số (Standalone Digital Video Recorder), Bộ chuyển đổi Camera (Digital Video Servers), Camera IP độ nét cao, Camera bán cầu tốc độ cao, Camera cảm biến hồng hoại thông minh, Camera chống nước, chống va đập, phần mềm tích hợp quản lý giám sát qua mạng… Những dòng sản phẩm này được sử dụng rộng rãi ở hơn 100 quốc gia trên thế giới, đáp ứng tốt nhu cầu của các thị trường khó tính như: Mỹ, Italy và Đức
HDParagon đã có giấy chứng nhận ISO9001 và ISO14001. Không chỉ hợp tác với trung tâm nghiên cứu khoa học của các trường Đại học nổi tiếng Đài Loan, ngoài ra HDPARAGON còn hợp tác công nghệ với các quốc gia danh tiếng về công nghệ: Nhật Bản, Mỹ, Hàn Quốc, Đài Loan… Bằng cách sử dụng các công nghệ tiên tiến nhất như: DIS (Digital Image System), CCD (Charge Coupled Device), SMT (Surface Mount Technology); sử dụng hệ thống quản lý thông tin tiên tiến như: SAP, OA, CMMI; với chiến lược đổi mới công nghệ liên tục, nâng cao điểm ảnh cho sản phẩm siêu nét để có thể đáp ứng nhanh nhất với yêu cầu của thị trường. Chúng tôi đảm bảo 100% sự hài lòng của khách hàng.
Panasonic
Panasonic bắt đầu hoạt động kinh doanh tại Việt Nam từ những năm 1950, mục tiêu đóng góp cho xã hội Việt Nam thông qua các hoạt động từ nghiên cứu và phát triển, sản xuất, phân phối và bán hàng cũng như các hoạt động trách nhiệm xã hội Tập đoàn.
Công ty Panasonic Việt Nam (PV) là công ty 100% vốn nước ngoài đầu tiên giữ vai trò công ty chủ quản tại Việt Nam. Tính đến tháng 7 năm 2013, Tập đoàn Panasonic tại Việt Nam gồm tám công ty bao gồm Panasonic Việt Nam và bộ phận kinh doanh trực thuộc Panasonic Sales Việt Nam (PSV), Trung tâm nghiên cứu và phát triển (PRDCV), năm công ty sản xuất bao gồm Panasonic AVC Networks Việt Nam (PAVCV), Panasonic Appliances Việt Nam (PAPVN), Panasonic System Networks Việt Nam (PSNV), Panasonic Industrial Devices Việt Nam (PIDVN), Panasonic Life Solutions Việt Nam (PLSVN), và công ty bảo hiểm Panasonic Insurance Service Việt Nam (PISVN). Tập đoàn có tổng số nhân lực khoảng 8,000 người. Tại Việt Nam, Panasonic là một trong những doanh nghiệp đặc biệt chú trọng tới các hoạt động xã hội giáo dục và môi trường.