Settings > Advanced > Features). */ public static function enabled(): bool { return self::supported() && class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) && \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( self::FEATURE_ID ); } /** * Where the merchant turns the feature on. */ public static function settings_url(): string { return admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=features' ); } /** * Write costs. * * @param array[] $items Each: productId, variantId, costCents. * @return array[] One result per item: variantId, ok, and either previousCents/newCents or code/message. */ public function write( array $items ): array { $results = array(); foreach ( $items as $item ) { $results[] = $this->write_one( is_array( $item ) ? $item : array() ); } /** * Fires after a batch of costs was written, so an add-on can keep a history. * * @param array[] $results One entry per item: productId, variantId, ok, and previousCents/newCents when ok. */ do_action( 'madecost_costs_written', $results ); return $results; } /** * Write one cost. * * @param array $item productId, variantId, costCents. * @return array */ private function write_one( array $item ): array { $product_id = $item['productId'] ?? null; $variant_id = $item['variantId'] ?? null; $cents = $item['costCents'] ?? null; $result = array( 'productId' => is_scalar( $product_id ) ? (string) $product_id : '', 'variantId' => is_scalar( $variant_id ) ? (string) $variant_id : '', 'ok' => false, ); if ( ! self::enabled() ) { return $result + array( 'code' => 'cogs_disabled', 'message' => __( 'Turn on Cost of Goods Sold in WooCommerce first (Settings > Advanced > Features).', 'madecost' ), ); } if ( ! Validator::is_int_in( $cents, 1, Validator::MAX_CENTS ) || ! ctype_digit( (string) $product_id ) || ! ctype_digit( (string) $variant_id ) ) { return $result + array( 'code' => 'invalid', 'message' => __( 'This cost cannot be written: it is empty or out of range.', 'madecost' ), ); } $product = wc_get_product( (int) $variant_id ); // A variation must belong to the product the recipe is attached to. $belongs = $product && ( ( $product instanceof \WC_Product_Variation && (int) $product->get_parent_id() === (int) $product_id ) || ( $product->is_type( 'simple' ) && (int) $product->get_id() === (int) $product_id ) ); if ( ! $belongs || 'trash' === $product->get_status() ) { return $result + array( 'code' => 'not_found', 'message' => __( 'This product or variation no longer exists.', 'madecost' ), ); } if ( ! current_user_can( 'edit_post', (int) $product_id ) ) { return $result + array( 'code' => 'forbidden', 'message' => __( 'You are not allowed to edit this product.', 'madecost' ), ); } $previous = Catalog::to_cents( $product->get_cogs_total_value() ); $product->set_cogs_value( round( ( (int) $cents ) / 100, 2 ) ); if ( $product instanceof \WC_Product_Variation ) { $product->set_cogs_value_is_additive( false ); } $product->save(); // Read it back from the database: the cost WooCommerce will use must be the one we meant. $stored = wc_get_product( (int) $variant_id ); if ( ! $stored || Catalog::to_cents( $stored->get_cogs_total_value() ) !== (int) $cents ) { return $result + array( 'code' => 'not_saved', 'message' => __( 'WooCommerce did not keep the cost. Another plugin may be changing it.', 'madecost' ), ); } return array( 'productId' => (string) $product_id, 'variantId' => (string) $variant_id, 'ok' => true, 'previousCents' => ( null === $previous || $previous <= 0 ) ? null : $previous, 'newCents' => (int) $cents, ); } }