diff --git a/package-lock.json b/package-lock.json
index e74ced018..c3d813ec5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "woocommerce-germanized",
- "version": "3.15.0",
+ "version": "3.18.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "woocommerce-germanized",
- "version": "3.15.0",
+ "version": "3.18.4",
"license": "GPL-3.0+",
"dependencies": {
"@wordpress/autop": "3.16.0",
diff --git a/packages/woocommerce-eu-tax-helper/src/Helper.php b/packages/woocommerce-eu-tax-helper/src/Helper.php
new file mode 100644
index 000000000..01ee79bf8
--- /dev/null
+++ b/packages/woocommerce-eu-tax-helper/src/Helper.php
@@ -0,0 +1,1338 @@
+queue() : false;
+ }
+
+ private static function load() {
+ $callback = function () {
+ if ( $queue = self::get_queue() ) {
+ if ( self::enable_tax_rate_observer() ) {
+ // Schedule once per day at 0:00 in local timezone
+ if ( null === $queue->get_next( 'woocommerce_eu_tax_helper_rate_observer', array(), 'woocommerce_eu_tax_helper' ) ) {
+ /**
+ * Use WC helper method which calculates the date in current
+ * local timezone.
+ */
+ $date = wc_string_to_datetime( 'tomorrow midnight' );
+ $date->modify( '+1 second' );
+
+ $queue->cancel_all( 'woocommerce_eu_tax_helper_rate_observer', array(), 'woocommerce_eu_tax_helper' );
+
+ /**
+ * Action scheduler expects the time in UTC.
+ */
+ $queue->schedule_recurring( $date->getTimestamp(), DAY_IN_SECONDS, 'woocommerce_eu_tax_helper_rate_observer', array(), 'woocommerce_eu_tax_helper' );
+ }
+ } else {
+ $queue->cancel( 'woocommerce_eu_tax_helper_rate_observer', array(), 'woocommerce_eu_tax_helper' );
+ }
+ }
+ };
+
+ if ( ! did_action( 'init' ) ) {
+ add_action( 'init', $callback, 10 );
+ } else {
+ $callback();
+ }
+
+ add_action(
+ 'woocommerce_eu_tax_helper_rate_observer',
+ function () {
+ self::maybe_apply_tax_rate_changesets();
+ },
+ 10
+ );
+ }
+
+ public static function apply_tax_rate_changesets() {
+ $changes = self::get_eu_tax_rate_changesets();
+ $last_applied_changes_date = null;
+ $today = new \WC_DateTime();
+
+ if ( $last_applied_changes = get_option( 'woocommerce_eu_tax_helper_last_rate_changeset' ) ) {
+ $last_applied_changes_date = wc_string_to_datetime( $last_applied_changes . ' 00:00:00' );
+ }
+
+ self::log( sprintf( 'Checking for tax rate changes @ %1$s. Last applied changes: %2$s', $today->date_i18n( 'Y-m-d' ), ( $last_applied_changes_date ? $last_applied_changes_date->date_i18n( 'Y-m-d' ) : '-' ) ) );
+
+ foreach ( $changes as $date => $changeset ) {
+ $changeset_date = wc_string_to_datetime( $date . ' 00:00:00' );
+
+ if ( ! $last_applied_changes_date || $changeset_date > $last_applied_changes_date ) {
+ $is_oss = self::oss_procedure_is_enabled();
+ $countries = array_keys( $changeset );
+
+ if ( $today >= $changeset_date ) {
+ self::log( sprintf( 'Updating tax rates changes for %1$s @ %2$s: %3$s', $changeset_date->date_i18n( 'Y-m-d' ), $today->date_i18n( 'Y-m-d' ), wc_print_r( $changeset, true ) ) );
+
+ if ( $is_oss ) {
+ foreach ( $countries as $country ) {
+ self::delete_tax_rates_by_country( $country );
+ }
+
+ $tax_rates = self::generate_tax_rates( true, array(), $changeset, false );
+
+ self::log( sprintf( 'New tax rates: %1$s', wc_print_r( $tax_rates, true ) ) );
+
+ foreach ( $tax_rates as $tax_class_type => $tax_rate_data ) {
+ $class = $tax_rate_data['tax_class'];
+ $rates = $tax_rate_data['rates'];
+
+ self::import_rates( $rates, $class, $tax_class_type, false );
+ }
+ } elseif ( in_array( self::get_base_country(), $countries, true ) ) {
+ $eu_rates = self::get_eu_tax_rates( false );
+
+ foreach ( $changeset as $country => $tax_rates ) {
+ $eu_rates[ $country ] = $tax_rates;
+ }
+
+ $tax_rates = self::generate_tax_rates( false, array(), $eu_rates, false );
+
+ self::log( sprintf( 'New tax rates: %1$s', wc_print_r( $tax_rates, true ) ) );
+
+ foreach ( $tax_rates as $tax_class_type => $tax_rate_data ) {
+ $class = $tax_rate_data['tax_class'];
+ $rates = $tax_rate_data['rates'];
+
+ self::import_rates( $rates, $class, $tax_class_type );
+ }
+ }
+
+ update_option( 'woocommerce_eu_tax_helper_last_rate_changeset', $date );
+ }
+ }
+ }
+ }
+
+ protected static function maybe_apply_tax_rate_changesets() {
+ if ( self::enable_tax_rate_observer() ) {
+ self::apply_tax_rate_changesets();
+ }
+ }
+
+ protected static function log( $message, $type = 'info' ) {
+ $logger = wc_get_logger();
+
+ if ( ! $logger || ! apply_filters( 'woocommerce_eu_tax_helper_enable_logging', true ) ) {
+ return;
+ }
+
+ if ( ! is_callable( array( $logger, $type ) ) ) {
+ $type = 'info';
+ }
+
+ $logger->{$type}( $message, array( 'source' => 'woocommerce-eu-tax-helper' ) );
+ }
+
+ public static function enable_tax_rate_observer() {
+ return apply_filters( 'woocommerce_eu_tax_helper_enable_tax_rate_observer', true );
+ }
+
+ public static function oss_procedure_is_enabled() {
+ return apply_filters( 'woocommerce_eu_tax_helper_oss_procedure_is_enabled', false );
+ }
+
+ /**
+ * There are different opinions on how the entrepreneurial status must be proven.
+ * By default, deliveries without a valid VAT ID are classified as b2c deliveries
+ * and therefore as relevant for the OSS procedure.
+ *
+ * @return bool
+ */
+ public static function exclude_b2b_without_vat_id_from_oss() {
+ return apply_filters( 'woocommerce_eu_tax_helper_exclude_b2b_without_vat_id_from_oss', false );
+ }
+
+ public static function get_eu_countries() {
+ if ( ! WC()->countries ) {
+ return array();
+ }
+
+ $countries = WC()->countries->get_european_union_countries();
+
+ return $countries;
+ }
+
+ public static function get_eu_vat_countries() {
+ $vat_countries = WC()->countries ? WC()->countries->get_european_union_countries( 'eu_vat' ) : array();
+
+ return apply_filters( 'woocommerce_eu_tax_helper_eu_vat_countries', $vat_countries );
+ }
+
+ public static function is_northern_ireland( $country, $postcode = '' ) {
+ if ( 'GB' === $country && 'BT' === strtoupper( substr( trim( $postcode ), 0, 2 ) ) ) {
+ return true;
+ } elseif ( 'IX' === $country ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static function is_eu_vat_country( $country, $postcode = '' ) {
+ $country = wc_strtoupper( $country );
+ $postcode = wc_normalize_postcode( $postcode );
+ $is_eu_vat_country = in_array( $country, self::get_eu_vat_countries(), true );
+
+ if ( self::is_northern_ireland( $country, $postcode ) ) {
+ $is_eu_vat_country = true;
+ } elseif ( self::is_eu_vat_postcode_exemption( $country, $postcode ) ) {
+ $is_eu_vat_country = false;
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_is_eu_vat_country', $is_eu_vat_country, $country, $postcode );
+ }
+
+ public static function is_third_country( $country, $postcode = '' ) {
+ $is_third_country = true;
+
+ /**
+ * In case the base country is within EU consider all non-EU VAT countries as third countries.
+ * In any other case consider every non-base-country as third country.
+ */
+ if ( in_array( self::get_base_country(), self::get_eu_vat_countries(), true ) ) {
+ $is_third_country = ! self::is_eu_vat_country( $country, $postcode );
+ } else {
+ $is_third_country = self::get_base_country() !== $country;
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_is_third_country', $is_third_country, $country, $postcode );
+ }
+
+ public static function is_eu_country( $country ) {
+ return in_array( $country, self::get_eu_countries(), true );
+ }
+
+ public static function is_eu_vat_postcode_exemption( $country, $postcode = '' ) {
+ $country = wc_strtoupper( $country );
+ $postcode = wc_normalize_postcode( $postcode );
+ $exemptions = self::get_vat_postcode_exemptions_by_country();
+ $is_exempt = false;
+
+ if ( ! empty( $postcode ) && in_array( $country, self::get_eu_vat_countries(), true ) ) {
+ if ( array_key_exists( $country, $exemptions ) ) {
+ $wildcards = wc_get_wildcard_postcodes( $postcode, $country );
+
+ foreach ( $exemptions[ $country ] as $exempt_postcode ) {
+ if ( in_array( $exempt_postcode, $wildcards, true ) ) {
+ $is_exempt = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return $is_exempt;
+ }
+
+ /**
+ * Get VAT exemptions (of EU countries) for certain postcodes (e.g. canary islands)
+ *
+ * @see https://www.hk24.de/produktmarken/beratung-service/recht-und-steuern/steuerrecht/umsatzsteuer-mehrwertsteuer/umsatzsteuer-mehrwertsteuer-international/verfahrensrecht/territoriale-besonderheiten-umsatzsteuer-zollrecht-1167674
+ * @see https://github.com/woocommerce/woocommerce/issues/5143
+ * @see https://ec.europa.eu/taxation_customs/business/vat/eu-vat-rules-topic/territorial-status-eu-countries-certain-territories_en
+ *
+ * @return \string[][]
+ */
+ public static function get_vat_postcode_exemptions_by_country( $country = '' ) {
+ $country = wc_strtoupper( $country );
+ $exemptions = self::get_vat_postcode_exemptions();
+
+ if ( empty( $country ) ) {
+ return $exemptions;
+ } elseif ( array_key_exists( $country, $exemptions ) ) {
+ return $exemptions[ $country ];
+ } else {
+ return array();
+ }
+ }
+
+ public static function get_vat_postcode_exemptions() {
+ $exemptions = array(
+ 'DE' => array(
+ '27498', // Helgoland
+ '78266', // Büsingen am Hochrhein
+ ),
+ 'ES' => array(
+ '35*', // Canary Islands
+ '38*', // Canary Islands
+ '51*', // Ceuta
+ '52*', // Melilla
+ ),
+ 'GR' => array(
+ '63086', // Mount Athos
+ '63087', // Mount Athos
+ ),
+ 'FR' => array(
+ '971*', // Guadeloupe
+ '972*', // Martinique
+ '973*', // French Guiana
+ '974*', // Réunion
+ '976*', // Mayotte
+ ),
+ 'IT' => array(
+ '22060', // Livigno, Campione d’Italia
+ '23030', // Lake Lugano
+ ),
+ 'FI' => array(
+ '22*', // Aland islands
+ ),
+ );
+
+ return $exemptions;
+ }
+
+ /**
+ * @param integer|\WC_Order $order
+ *
+ * @return array
+ */
+ public static function get_order_taxable_location( $order ) {
+ $order = is_a( $order, 'WC_Order' ) ? $order : wc_get_order( $order );
+
+ $taxable_address = array(
+ self::get_base_country(),
+ self::get_base_state(),
+ self::get_base_postcode(),
+ self::get_base_city(),
+ );
+
+ if ( ! $order ) {
+ return $taxable_address;
+ }
+
+ $tax_based_on = get_option( 'woocommerce_tax_based_on' );
+
+ if ( is_a( $order, 'WC_Order_Refund' ) ) {
+ $order = wc_get_order( $order->get_parent_id() );
+
+ if ( ! $order ) {
+ return $taxable_address;
+ }
+ }
+
+ /**
+ * Shipping address data does not exist
+ */
+ if ( 'shipping' === $tax_based_on && ! $order->get_shipping_country() ) {
+ $tax_based_on = 'billing';
+ }
+
+ $is_vat_exempt = apply_filters( 'woocommerce_order_is_vat_exempt', 'yes' === $order->get_meta( 'is_vat_exempt' ), $order );
+
+ /**
+ * In case the order is a VAT exempt, calculate net prices based on taxes from base country.
+ */
+ if ( $is_vat_exempt ) {
+ $tax_based_on = 'base';
+ }
+
+ $country = 'shipping' === $tax_based_on ? $order->get_shipping_country() : $order->get_billing_country();
+
+ if ( 'base' !== $tax_based_on && ! empty( $country ) ) {
+ $taxable_address = array(
+ $country,
+ 'billing' === $tax_based_on ? $order->get_billing_state() : $order->get_shipping_state(),
+ 'billing' === $tax_based_on ? $order->get_billing_postcode() : $order->get_shipping_postcode(),
+ 'billing' === $tax_based_on ? $order->get_billing_city() : $order->get_shipping_city(),
+ );
+ }
+
+ return $taxable_address;
+ }
+
+ public static function get_taxable_location() {
+ $is_admin_order_request = self::is_admin_order_request();
+
+ if ( $is_admin_order_request ) {
+ $taxable_address = array(
+ self::get_base_country(),
+ self::get_base_state(),
+ self::get_base_postcode(),
+ self::get_base_city(),
+ );
+
+ if ( isset( $_POST['order_id'] ) && ( $order = wc_get_order( absint( $_POST['order_id'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $taxable_address = self::get_order_taxable_location( $order );
+ }
+
+ return $taxable_address;
+ } else {
+ return \WC_Tax::get_tax_location();
+ }
+ }
+
+ public static function is_admin_order_ajax_request() {
+ $order_actions = array( 'woocommerce_calc_line_taxes', 'woocommerce_save_order_items', 'add_coupon_discount', 'refund_line_items', 'delete_refund' );
+
+ return isset( $_POST['action'], $_POST['order_id'] ) && ( strstr( wc_clean( wp_unslash( $_POST['action'] ) ), '_order_' ) || in_array( wc_clean( wp_unslash( $_POST['action'] ) ), $order_actions, true ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ }
+
+ public static function is_admin_order_request() {
+ return is_admin() && current_user_can( 'edit_shop_orders' ) && self::is_admin_order_ajax_request();
+ }
+
+ protected static function is_rest_api_request() {
+ if ( function_exists( 'WC' ) ) {
+ $wc = WC();
+
+ if ( is_callable( array( $wc, 'is_rest_api_request' ) ) ) {
+ return $wc->is_rest_api_request();
+ }
+ }
+
+ return false;
+ }
+
+ protected static function get_current_request_value( $key ) {
+ $value = null;
+ $is_admin_order_request = self::is_admin_order_request();
+
+ if ( $is_admin_order_request ) {
+ if ( $order = wc_get_order( absint( $_POST['order_id'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+ $getter = "get_{$key}";
+
+ if ( is_callable( array( $order, $getter ) ) ) {
+ $value = $order->{ $getter }();
+ }
+ }
+ } elseif ( did_action( 'woocommerce_checkout_update_order_review' ) ) {
+ if ( in_array(
+ $key,
+ array(
+ 'billing_country',
+ 'billing_state',
+ 'billing_postcode',
+ 'billing_city',
+ 'billing_address_1',
+ 'billing_address_2',
+ 'shipping_country',
+ 'shipping_state',
+ 'shipping_postcode',
+ 'shipping_city',
+ 'shipping_address_1',
+ 'shipping_address_2',
+ ),
+ true
+ ) ) {
+ $customer = WC()->customer;
+ $getter = "get_{$key}";
+
+ if ( $customer && is_callable( array( $customer, $getter ) ) ) {
+ $value = $customer->{ $getter }();
+ }
+ } elseif ( isset( $_POST['post_data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $posted = array();
+
+ if ( is_string( $_POST['post_data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ parse_str( $_POST['post_data'], $posted ); // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
+ $posted = wc_clean( wp_unslash( $posted ) );
+ } elseif ( is_array( $_POST['post_data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $posted = wc_clean( wp_unslash( $_POST['post_data'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ }
+
+ $value = isset( $posted[ $key ] ) ? $posted[ $key ] : null;
+
+ /**
+ * Do only allow retrieving shipping-related data in case shipping address is activated
+ */
+ if ( 'shipping_' === substr( $key, 0, 9 ) ) {
+ if ( ! isset( $posted['ship_to_different_address'] ) || ! $posted['ship_to_different_address'] || wc_ship_to_billing_address_only() ) {
+ return self::get_current_request_value( str_replace( 'shipping_', 'billing_', $key ) );
+ }
+ }
+ }
+ } else {
+ $getter = "get_{$key}";
+ $customer = WC()->customer;
+
+ if ( $customer && is_callable( array( $customer, $getter ) ) ) {
+ $value = $customer->{ $getter }();
+ }
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_current_request_data', $value, $key );
+ }
+
+ public static function current_request_is_b2b() {
+ $is_admin_order_request = self::is_admin_order_request();
+ $company = false;
+
+ if ( $is_admin_order_request ) {
+ if ( $order = wc_get_order( absint( $_POST['order_id'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+ $company = $order->get_billing_company();
+
+ if ( $order->has_shipping_address() ) {
+ $company = $order->get_shipping_company();
+ }
+ }
+ } else {
+ $use_shipping_address = self::get_current_request_value( 'shipping_address_1' ) || self::get_current_request_value( 'shipping_address_2' );
+ $company = self::get_current_request_value( 'billing_company' );
+
+ if ( $use_shipping_address ) {
+ $company = self::get_current_request_value( 'shipping_company' );
+ }
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_current_request_is_b2b', ! empty( $company ) );
+ }
+
+ public static function current_request_has_vat_exempt() {
+ $is_admin_order_request = self::is_admin_order_request();
+ $is_vat_exempt = false;
+
+ if ( $is_admin_order_request ) {
+ if ( $order = wc_get_order( absint( $_POST['order_id'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+ $is_vat_exempt = apply_filters( 'woocommerce_order_is_vat_exempt', 'yes' === $order->get_meta( 'is_vat_exempt' ), $order );
+ }
+ } elseif ( WC()->customer && WC()->customer->is_vat_exempt() ) {
+ $is_vat_exempt = true;
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_current_request_has_vat_exempt', $is_vat_exempt );
+ }
+
+ public static function get_base_country() {
+ if ( WC()->countries ) {
+ $base_country = WC()->countries->get_base_country();
+ } else {
+ $base_country = wc_get_base_location()['country'];
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_base_country', $base_country );
+ }
+
+ public static function get_base_postcode() {
+ $base_postcode = '';
+
+ if ( WC()->countries ) {
+ $base_postcode = WC()->countries->get_base_postcode();
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_base_postcode', $base_postcode );
+ }
+
+ public static function get_base_state() {
+ $base_state = '';
+
+ if ( WC()->countries ) {
+ $base_state = WC()->countries->get_base_state();
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_base_state', $base_state );
+ }
+
+ public static function get_base_city() {
+ $base_city = '';
+
+ if ( WC()->countries ) {
+ $base_city = WC()->countries->get_base_city();
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_base_city', $base_city );
+ }
+
+ /**
+ * Returns a list of EU countries except base country.
+ *
+ * @return string[]
+ */
+ public static function get_non_base_eu_countries( $include_gb = false ) {
+ $countries = self::get_eu_vat_countries();
+
+ /**
+ * Include GB to allow Northern Ireland
+ */
+ if ( $include_gb && ! in_array( 'GB', $countries, true ) ) {
+ $countries = array_merge( $countries, array( 'GB' ) );
+ }
+
+ $base_country = self::get_base_country();
+ $countries = array_diff( $countries, array( $base_country ) );
+
+ return $countries;
+ }
+
+ public static function country_supports_eu_vat( $country, $postcode = '' ) {
+ return self::is_eu_vat_country( $country, $postcode );
+ }
+
+ public static function import_oss_tax_rates( $tax_class_slug_names = array() ) {
+ self::import_tax_rates_internal( true, $tax_class_slug_names );
+ }
+
+ public static function import_default_tax_rates( $tax_class_slug_names = array() ) {
+ self::import_tax_rates_internal( false, $tax_class_slug_names );
+ }
+
+ public static function import_tax_rates( $tax_class_slug_names = array() ) {
+ self::import_tax_rates_internal( self::oss_procedure_is_enabled(), $tax_class_slug_names );
+ }
+
+ protected static function parse_tax_class_slug_names( $tax_class_slug_names = array() ) {
+ return wp_parse_args(
+ $tax_class_slug_names,
+ array(
+ 'reduced' => apply_filters( 'woocommerce_eu_tax_helper_tax_class_reduced_name', __( 'Reduced rate', 'woocommerce' ) ), // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
+ 'greater-reduced' => apply_filters( 'woocommerce_eu_tax_helper_tax_class_greater_reduced_name', _x( 'Greater reduced rate', 'tax-helper-tax-class-name', 'woocommerce-germanized' ) ),
+ 'super-reduced' => apply_filters( 'woocommerce_eu_tax_helper_tax_class_super_reduced_name', _x( 'Super reduced rate', 'tax-helper-tax-class-name', 'woocommerce-germanized' ) ),
+ 'zero' => apply_filters( 'woocommerce_eu_tax_helper_tax_class_zero_name', __( 'Zero rate', 'woocommerce' ) ), // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
+ )
+ );
+ }
+
+ public static function maybe_create_tax_classes( $tax_class_slug_names = array() ) {
+ $tax_class_slugs = self::get_tax_class_slugs( $tax_class_slug_names );
+ $tax_class_slug_names = self::parse_tax_class_slug_names( $tax_class_slug_names );
+
+ foreach ( $tax_class_slugs as $tax_class_type => $class ) {
+ /**
+ * Maybe create missing tax classes
+ */
+ if ( false === $class ) {
+ switch ( $tax_class_type ) {
+ case 'reduced':
+ \WC_Tax::create_tax_class( $tax_class_slug_names['reduced'] );
+ break;
+ case 'greater-reduced':
+ \WC_Tax::create_tax_class( $tax_class_slug_names['greater-reduced'] );
+ break;
+ case 'super-reduced':
+ \WC_Tax::create_tax_class( $tax_class_slug_names['super-reduced'] );
+ break;
+ case 'zero':
+ \WC_Tax::create_tax_class( $tax_class_slug_names['zero'] );
+ break;
+ }
+ }
+ }
+ }
+
+ public static function generate_tax_rates( $is_oss = true, $tax_class_slug_names = array(), $eu_rates = array(), $add_zero_rates = true ) {
+ self::clear_cache();
+
+ $tax_class_slugs = self::get_tax_class_slugs( $tax_class_slug_names );
+ $tax_class_slug_names = self::parse_tax_class_slug_names( $tax_class_slug_names );
+ $eu_rates = empty( $eu_rates ) ? self::get_eu_tax_rates() : $eu_rates;
+
+ self::maybe_create_tax_classes( $tax_class_slug_names );
+
+ $tax_rates = array();
+
+ foreach ( $tax_class_slugs as $tax_class_type => $class ) {
+ $new_rates = array();
+
+ if ( 'zero' === $tax_class_type ) {
+ if ( $add_zero_rates ) {
+ $new_rates = array(
+ array(
+ 'country' => '*',
+ 'rate' => 0.0,
+ 'name' => '',
+ ),
+ );
+ }
+ } else {
+ foreach ( $eu_rates as $country => $rates_data ) {
+ /**
+ * Use base country rates in case OSS is disabled
+ */
+ if ( ! $is_oss ) {
+ $base_country = self::get_base_country();
+
+ if ( isset( $eu_rates[ $base_country ] ) ) {
+ /**
+ * In case the country includes multiple rules (e.g. postcode exempts) by default
+ * do only use the last rule (which does not include exempts) to construct non-base country tax rules.
+ */
+ if ( $base_country !== $country ) {
+ $base_country_base_rate = array_values( array_slice( $eu_rates[ $base_country ], - 1 ) )[0];
+
+ foreach ( $rates_data as $key => $rate_data ) {
+ $rates_data[ $key ] = array_replace_recursive( $rate_data, $base_country_base_rate );
+
+ foreach ( $tax_class_slugs as $tmp_class_type => $class_data ) {
+ /**
+ * Do not include tax classes which are not supported by the base country.
+ */
+ if ( isset( $rates_data[ $key ][ $tmp_class_type ] ) && ! isset( $base_country_base_rate[ $tmp_class_type ] ) ) {
+ unset( $rates_data[ $key ][ $tmp_class_type ] );
+ } elseif ( isset( $rates_data[ $key ][ $tmp_class_type ] ) ) {
+ /**
+ * Replace tax class data with base data to make sure that reduced
+ * classes have the same dimensions
+ */
+ $rates_data[ $key ][ $tmp_class_type ] = $base_country_base_rate[ $tmp_class_type ];
+
+ /**
+ * In case this is an exempt make sure to replace with zero tax rates
+ */
+ if ( isset( $rate_data['is_exempt'] ) && $rate_data['is_exempt'] ) {
+ if ( is_array( $rates_data[ $key ][ $tmp_class_type ] ) ) {
+ foreach ( $rates_data[ $key ][ $tmp_class_type ] as $k => $rate ) {
+ $rates_data[ $key ][ $tmp_class_type ][ $k ] = 0;
+ }
+ } else {
+ $rates_data[ $key ][ $tmp_class_type ] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ continue;
+ }
+ }
+
+ /**
+ * Each country may contain multiple tax rates
+ */
+ foreach ( $rates_data as $rates ) {
+ $rates = wp_parse_args(
+ $rates,
+ array(
+ 'name' => '',
+ 'postcodes' => array(),
+ 'reduced' => array(),
+ )
+ );
+
+ if ( ! empty( $rates['postcode'] ) ) {
+ foreach ( $rates['postcode'] as $postcode ) {
+ $tax_rate = self::get_single_tax_rate_data( $tax_class_type, $rates, $country, $postcode );
+
+ if ( false !== $tax_rate ) {
+ $new_rates[] = $tax_rate;
+ }
+ }
+ } else {
+ $tax_rate = self::get_single_tax_rate_data( $tax_class_type, $rates, $country );
+
+ if ( false !== $tax_rate ) {
+ $new_rates[] = $tax_rate;
+ }
+ }
+ }
+ }
+ }
+
+ $tax_rates[ $tax_class_type ] = array(
+ 'tax_class' => $class,
+ 'rates' => $new_rates,
+ );
+ }
+
+ return $tax_rates;
+ }
+
+ protected static function import_tax_rates_internal( $is_oss = true, $tax_class_slug_names = array() ) {
+ $tax_rates = self::generate_tax_rates( $is_oss, $tax_class_slug_names );
+
+ foreach ( $tax_rates as $tax_class_type => $tax_rate_data ) {
+ $class = $tax_rate_data['tax_class'];
+ $rates = $tax_rate_data['rates'];
+
+ self::import_rates( $rates, $class, $tax_class_type );
+ }
+ }
+
+ private static function get_single_tax_rate_data( $tax_class_type, $rates, $country, $postcode = '' ) {
+ $rates = wp_parse_args(
+ $rates,
+ array(
+ 'name' => '',
+ 'reduced' => array(),
+ )
+ );
+
+ $single_rate = array(
+ 'name' => $rates['name'],
+ 'rate' => false,
+ 'country' => $country,
+ 'postcode' => $postcode,
+ );
+
+ switch ( $tax_class_type ) {
+ case 'greater-reduced':
+ if ( count( $rates['reduced'] ) > 1 ) {
+ $single_rate['rate'] = $rates['reduced'][1];
+ }
+ break;
+ case 'reduced':
+ if ( ! empty( $rates['reduced'] ) ) {
+ $single_rate['rate'] = $rates['reduced'][0];
+ }
+ break;
+ default:
+ if ( isset( $rates[ $tax_class_type ] ) ) {
+ $single_rate['rate'] = $rates[ $tax_class_type ];
+ }
+ break;
+ }
+
+ if ( false === $single_rate['rate'] ) {
+ return false;
+ }
+
+ return $single_rate;
+ }
+
+ protected static function clear_cache() {
+ $cache_key = \WC_Cache_Helper::get_cache_prefix( 'taxes' ) . 'eu_tax_helper_tax_class_slugs';
+
+ wp_cache_delete( $cache_key, 'taxes' );
+ }
+
+ public static function get_tax_class_slugs( $tax_class_slug_names = array() ) {
+ $tax_class_slug_names = self::parse_tax_class_slug_names( $tax_class_slug_names );
+ $cache_key = \WC_Cache_Helper::get_cache_prefix( 'taxes' ) . 'eu_tax_helper_tax_class_slugs';
+ $slugs = wp_cache_get( $cache_key, 'taxes' );
+
+ if ( false === $slugs ) {
+ $reduced_tax_class = false;
+ $greater_reduced_tax_class = false;
+ $super_reduced_tax_class = false;
+ $zero_tax_class = false;
+ $tax_classes = \WC_Tax::get_tax_class_slugs();
+
+ /**
+ * Try to determine the reduced tax rate class
+ */
+ foreach ( $tax_classes as $slug ) {
+ if ( strstr( $slug, 'virtual' ) ) {
+ continue;
+ }
+
+ if ( ! $greater_reduced_tax_class && strstr( $slug, sanitize_title( 'Greater reduced rate' ) ) ) {
+ $greater_reduced_tax_class = $slug;
+ } elseif ( ! $greater_reduced_tax_class && strstr( $slug, sanitize_title( $tax_class_slug_names['greater-reduced'] ) ) ) {
+ $greater_reduced_tax_class = $slug;
+ } elseif ( ! $super_reduced_tax_class && strstr( $slug, sanitize_title( 'Super reduced rate' ) ) ) {
+ $super_reduced_tax_class = $slug;
+ } elseif ( ! $super_reduced_tax_class && strstr( $slug, sanitize_title( $tax_class_slug_names['super-reduced'] ) ) ) {
+ $super_reduced_tax_class = $slug;
+ } elseif ( ! $reduced_tax_class && strstr( $slug, sanitize_title( 'Reduced rate' ) ) ) {
+ $reduced_tax_class = $slug;
+ } elseif ( ! $reduced_tax_class && strstr( $slug, sanitize_title( $tax_class_slug_names['reduced'] ) ) ) { // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
+ $reduced_tax_class = $slug;
+ } elseif ( ! $reduced_tax_class && strstr( $slug, 'reduced' ) ) {
+ $reduced_tax_class = $slug;
+ } elseif ( ! $zero_tax_class && strstr( $slug, sanitize_title( $tax_class_slug_names['zero'] ) ) ) {
+ $zero_tax_class = $slug;
+ } elseif ( ! $zero_tax_class && strstr( $slug, 'zero' ) ) {
+ $zero_tax_class = $slug;
+ }
+ }
+
+ $slugs = array(
+ 'reduced' => $reduced_tax_class,
+ 'greater-reduced' => $greater_reduced_tax_class,
+ 'super-reduced' => $super_reduced_tax_class,
+ 'standard' => '',
+ 'zero' => $zero_tax_class,
+ );
+
+ wp_cache_set( $cache_key, $slugs, 'taxes' );
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_tax_rate_class_slugs', $slugs );
+ }
+
+ public static function get_tax_type_by_country_rate( $rate_percentage, $country ) {
+ $country = strtoupper( $country );
+
+ /**
+ * Map northern ireland to GB
+ */
+ if ( 'XI' === $country ) {
+ $country = 'GB';
+ }
+
+ $eu_rates = self::get_eu_tax_rates();
+ $tax_type = 'standard';
+ $rate_percentage = (float) $rate_percentage;
+
+ if ( array_key_exists( $country, $eu_rates ) ) {
+ $rates = $eu_rates[ $country ];
+
+ foreach ( $rates as $rate ) {
+ foreach ( $rate as $tax_rate_type => $tax_rate_percent ) {
+ if ( ( is_array( $tax_rate_percent ) && in_array( $rate_percentage, $tax_rate_percent, true ) ) || (float) $tax_rate_percent === $rate_percentage ) {
+ $tax_type = $tax_rate_type;
+ break;
+ }
+ }
+ }
+ }
+
+ return apply_filters( 'woocommerce_eu_tax_helper_country_rate_tax_type', $tax_type, $country, $rate_percentage );
+ }
+
+ public static function get_eu_tax_rate_changesets( $apply_postcode_exempts = true ) {
+ $changesets = array(
+ '2024-01-01' => array(
+ 'CZ' => array(
+ array(
+ 'standard' => 21,
+ 'reduced' => array( 12 ),
+ ),
+ ),
+ 'EE' => array(
+ array(
+ 'standard' => 22,
+ 'reduced' => array( 9 ),
+ ),
+ ),
+ ),
+ '2024-01-02' => array(
+ 'LU' => array(
+ array(
+ 'standard' => 17,
+ 'reduced' => array( 8 ),
+ 'super-reduced' => 3,
+ ),
+ ),
+ ),
+ );
+
+ if ( $apply_postcode_exempts ) {
+ foreach ( $changesets as $date => $tax_rates ) {
+ $changesets[ $date ] = self::apply_vat_postcode_exempts( $tax_rates );
+ }
+ }
+
+ return $changesets;
+ }
+
+ public static function get_eu_tax_rates( $apply_changesets = true ) {
+ /**
+ * @see https://europa.eu/youreurope/business/taxation/vat/vat-rules-rates/index_en.htm
+ *
+ * Include Great Britain to allow including Norther Ireland
+ */
+ $rates = array(
+ 'AT' => array(
+ array(
+ 'standard' => 20,
+ 'reduced' => array( 10, 13 ),
+ ),
+ ),
+ 'BE' => array(
+ array(
+ 'standard' => 21,
+ 'reduced' => array( 6, 12 ),
+ ),
+ ),
+ 'BG' => array(
+ array(
+ 'standard' => 20,
+ 'reduced' => array( 9 ),
+ ),
+ ),
+ 'CY' => array(
+ array(
+ 'standard' => 19,
+ 'reduced' => array( 5, 9 ),
+ ),
+ ),
+ 'CZ' => array(
+ array(
+ 'standard' => 21,
+ 'reduced' => array( 12 ),
+ ),
+ ),
+ 'DE' => array(
+ array(
+ 'standard' => 19,
+ 'reduced' => array( 7 ),
+ ),
+ ),
+ 'DK' => array(
+ array(
+ 'standard' => 25,
+ 'reduced' => array(),
+ ),
+ ),
+ 'EE' => array(
+ array(
+ 'standard' => 22,
+ 'reduced' => array( 9 ),
+ ),
+ ),
+ 'GR' => array(
+ array(
+ 'standard' => 24,
+ 'reduced' => array( 6, 13 ),
+ ),
+ ),
+ 'ES' => array(
+ array(
+ 'standard' => 21,
+ 'reduced' => array( 10 ),
+ 'super-reduced' => 4,
+ ),
+ ),
+ 'FI' => array(
+ array(
+ 'standard' => 24,
+ 'reduced' => array( 10, 14 ),
+ ),
+ ),
+ 'FR' => array(
+ array(
+ 'standard' => 20,
+ 'reduced' => array( 5.5, 10 ),
+ 'super-reduced' => 2.1,
+ ),
+ ),
+ 'HR' => array(
+ array(
+ 'standard' => 25,
+ 'reduced' => array( 5, 13 ),
+ ),
+ ),
+ 'HU' => array(
+ array(
+ 'standard' => 27,
+ 'reduced' => array( 5, 18 ),
+ ),
+ ),
+ 'IE' => array(
+ array(
+ 'standard' => 23,
+ 'reduced' => array( 9, 13.5 ),
+ 'super-reduced' => 4.8,
+ ),
+ ),
+ 'IT' => array(
+ array(
+ 'standard' => 22,
+ 'reduced' => array( 5, 10 ),
+ 'super-reduced' => 4,
+ ),
+ ),
+ 'LT' => array(
+ array(
+ 'standard' => 21,
+ 'reduced' => array( 5, 9 ),
+ ),
+ ),
+ 'LU' => array(
+ array(
+ 'standard' => 17,
+ 'reduced' => array( 8 ),
+ 'super-reduced' => 3,
+ ),
+ ),
+ 'LV' => array(
+ array(
+ 'standard' => 21,
+ 'reduced' => array( 12, 5 ),
+ ),
+ ),
+ 'MC' => array(
+ array(
+ 'standard' => 20,
+ 'reduced' => array( 5.5, 10 ),
+ 'super-reduced' => 2.1,
+ ),
+ ),
+ 'MT' => array(
+ array(
+ 'standard' => 18,
+ 'reduced' => array( 5, 7 ),
+ ),
+ ),
+ 'NL' => array(
+ array(
+ 'standard' => 21,
+ 'reduced' => array( 9 ),
+ ),
+ ),
+ 'PL' => array(
+ array(
+ 'standard' => 23,
+ 'reduced' => array( 5, 8 ),
+ ),
+ ),
+ 'PT' => array(
+ array(
+ // Madeira
+ 'postcode' => array( '90*', '91*', '92*', '93*', '94*' ),
+ 'standard' => 22,
+ 'reduced' => array( 5, 12 ),
+ 'name' => _x( 'Madeira', 'tax-helper', 'woocommerce-germanized' ),
+ ),
+ array(
+ // Acores
+ 'postcode' => array( '95*', '96*', '97*', '98*', '99*' ),
+ 'standard' => 18,
+ 'reduced' => array( 4, 9 ),
+ 'name' => _x( 'Acores', 'tax-helper', 'woocommerce-germanized' ),
+ ),
+ array(
+ 'standard' => 23,
+ 'reduced' => array( 6, 13 ),
+ ),
+ ),
+ 'RO' => array(
+ array(
+ 'standard' => 19,
+ 'reduced' => array( 5, 9 ),
+ ),
+ ),
+ 'SE' => array(
+ array(
+ 'standard' => 25,
+ 'reduced' => array( 6, 12 ),
+ ),
+ ),
+ 'SI' => array(
+ array(
+ 'standard' => 22,
+ 'reduced' => array( 9.5 ),
+ ),
+ ),
+ 'SK' => array(
+ array(
+ 'standard' => 20,
+ 'reduced' => array( 10 ),
+ ),
+ ),
+ 'GB' => array(
+ array(
+ 'standard' => 20,
+ 'reduced' => array( 5 ),
+ 'postcode' => array( 'BT*' ),
+ 'name' => _x( 'Northern Ireland', 'tax-helper', 'woocommerce-germanized' ),
+ ),
+ ),
+ );
+
+ if ( $apply_changesets ) {
+ $changesets = self::get_eu_tax_rate_changesets( false );
+ $today = new \WC_DateTime();
+
+ foreach ( $changesets as $date => $changeset ) {
+ $changeset_date = wc_string_to_datetime( $date . ' 00:00:00' );
+
+ if ( $today >= $changeset_date ) {
+ foreach ( $changeset as $country => $tax_rates ) {
+ $rates[ $country ] = $tax_rates;
+ }
+ }
+ }
+ }
+
+ $rates = self::apply_vat_postcode_exempts( $rates );
+
+ return $rates;
+ }
+
+ protected static function apply_vat_postcode_exempts( $rates ) {
+ foreach ( self::get_vat_postcode_exemptions_by_country() as $country => $exempt_postcodes ) {
+ if ( array_key_exists( $country, $rates ) ) {
+ $default_rate = array_values( $rates[ $country ] )[0];
+
+ $postcode_exempt = array(
+ 'postcode' => $exempt_postcodes,
+ 'standard' => 0,
+ 'reduced' => count( $default_rate['reduced'] ) > 1 ? array( 0, 0 ) : array( 0 ),
+ 'name' => _x( 'Exempt', 'tax-helper-rate-import', 'woocommerce-germanized' ),
+ 'is_exempt' => true,
+ );
+
+ if ( array_key_exists( 'super-reduced', $default_rate ) ) {
+ $postcode_exempt['super-reduced'] = 0;
+ }
+
+ // Prepend before other tax rates
+ $rates[ $country ] = array_merge( array( $postcode_exempt ), $rates[ $country ] );
+ }
+ }
+
+ return $rates;
+ }
+
+ /**
+ * @param \stdClass $rate
+ *
+ * @return bool
+ */
+ public static function tax_rate_is_northern_ireland( $rate ) {
+ if ( 'GB' === $rate->tax_rate_country && isset( $rate->postcode ) && ! empty( $rate->postcode ) ) {
+ foreach ( $rate->postcode as $postcode ) {
+ if ( self::is_northern_ireland( $rate->tax_rate_country, $postcode ) ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static function delete_tax_rates_by_country( $country ) {
+ global $wpdb;
+
+ $country = strtoupper( $country );
+
+ foreach ( self::get_tax_class_slugs() as $tax_class ) {
+ $tax_rates = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$wpdb->prefix}woocommerce_tax_rates` WHERE `tax_rate_class` = %s AND `tax_rate_country` = %s;", $tax_class, $country ) );
+
+ foreach ( $tax_rates as $tax_rate ) {
+ \WC_Tax::_delete_tax_rate( $tax_rate->tax_rate_id );
+ }
+ }
+ }
+
+ public static function import_rates( $rates, $tax_class = '', $tax_class_type = '', $clean = true ) {
+ $eu_countries = self::get_eu_vat_countries();
+
+ /**
+ * Delete EU tax rates and make sure tax rate locations are deleted too
+ */
+ if ( $clean ) {
+ foreach ( \WC_Tax::get_rates_for_tax_class( $tax_class ) as $rate_id => $rate ) {
+ if ( in_array( $rate->tax_rate_country, $eu_countries, true ) || self::tax_rate_is_northern_ireland( $rate ) || ( 'GB' === $rate->tax_rate_country && 'GB' !== self::get_base_country() ) ) {
+ \WC_Tax::_delete_tax_rate( $rate_id );
+ } elseif ( 'zero' === $tax_class_type && empty( $rate->tax_rate_country ) ) {
+ \WC_Tax::_delete_tax_rate( $rate_id );
+ }
+ }
+ }
+
+ $count = 0;
+
+ foreach ( $rates as $rate ) {
+ $rate = wp_parse_args(
+ $rate,
+ array(
+ 'rate' => 0,
+ 'country' => '',
+ 'postcode' => '',
+ 'name' => '',
+ )
+ );
+
+ $iso = wc_strtoupper( $rate['country'] );
+ $vat_desc = '*' !== $iso ? $iso : '';
+
+ if ( ! empty( $rate['name'] ) ) {
+ $vat_desc = ( ! empty( $vat_desc ) ? $vat_desc . ' ' : '' ) . $rate['name'];
+ }
+
+ $vat_rate = wc_format_decimal( $rate['rate'], false, true );
+
+ $tax_rate_name = apply_filters( 'woocommerce_eu_tax_helper_import_tax_rate_name', sprintf( _x( 'VAT %1$s %% %2$s', 'tax-helper-rate-import', 'woocommerce-germanized' ), $vat_rate, $vat_desc ), $rate['rate'], $iso, $tax_class, $rate );
+
+ $_tax_rate = array(
+ 'tax_rate_country' => $iso,
+ 'tax_rate_state' => '',
+ 'tax_rate' => (string) number_format( (float) wc_clean( $rate['rate'] ), 4, '.', '' ),
+ 'tax_rate_name' => $tax_rate_name,
+ 'tax_rate_compound' => 0,
+ 'tax_rate_priority' => 1,
+ 'tax_rate_order' => $count++,
+ 'tax_rate_shipping' => ( strstr( $tax_class, 'virtual' ) ? 0 : 1 ),
+ 'tax_rate_class' => \WC_Tax::format_tax_rate_class( $tax_class ),
+ );
+
+ $new_tax_rate_id = \WC_Tax::_insert_tax_rate( $_tax_rate );
+
+ if ( ! empty( $rate['postcode'] ) ) {
+ \WC_Tax::_update_tax_rate_postcodes( $new_tax_rate_id, $rate['postcode'] );
+ }
+ }
+ }
+
+ /**
+ * @param $rate_id
+ * @param \WC_Order $order
+ */
+ public static function get_tax_rate_percent( $rate_id, $order ) {
+ $taxes = $order->get_taxes();
+ $percentage = null;
+
+ foreach ( $taxes as $tax ) {
+ if ( (int) $tax->get_rate_id() === (int) $rate_id ) {
+ if ( is_callable( array( $tax, 'get_rate_percent' ) ) ) {
+ $percentage = $tax->get_rate_percent();
+ }
+ }
+ }
+
+ /**
+ * WC_Order_Item_Tax::get_rate_percent returns null by default.
+ * Fallback to global tax rates (DB) in case the percentage is not available within order data.
+ */
+ if ( is_null( $percentage ) || '' === $percentage ) {
+ $rate_percentage = self::get_tax_rate_percentage( $rate_id );
+
+ if ( false !== $rate_percentage ) {
+ $percentage = $rate_percentage;
+ }
+ }
+
+ if ( ! is_numeric( $percentage ) ) {
+ $percentage = 0;
+ }
+
+ return $percentage;
+ }
+
+ public static function get_tax_rate_percentage( $rate_id ) {
+ $percentage = false;
+
+ if ( is_callable( array( 'WC_Tax', 'get_rate_percent_value' ) ) ) {
+ $percentage = \WC_Tax::get_rate_percent_value( $rate_id );
+ } elseif ( is_callable( array( 'WC_Tax', 'get_rate_percent' ) ) ) {
+ $percentage = filter_var( \WC_Tax::get_rate_percent( $rate_id ), FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION );
+ }
+
+ return $percentage;
+ }
+}
diff --git a/packages/woocommerce-germanized-dhl/assets/css/admin.scss b/packages/woocommerce-germanized-dhl/assets/css/admin.scss
new file mode 100644
index 000000000..4ab852e27
--- /dev/null
+++ b/packages/woocommerce-germanized-dhl/assets/css/admin.scss
@@ -0,0 +1,62 @@
+.shipments-create-label {
+ .wc-gzd-shipment-im-additional-services {
+ p.label {
+ margin-top: 10px;
+ width: 100%;
+ display: block;
+ margin-bottom: 5px;
+ font-weight: bold;
+ }
+ }
+
+ .wc-gzd-dhl-im-product-data {
+ margin-top: 2em;
+ min-width: 700px;
+ margin-left: -1rem !important;
+ margin-right: -1rem !important;
+
+ .column {
+ padding-left: 1rem !important;
+ padding-right: 1rem !important;
+
+ p:first-child {
+ margin-top: 1.5em !important;
+ }
+ }
+
+ .wc-gzd-dhl-im-product-price {
+ background: #ffd633;
+ border-radius: 4px;
+ padding: .5em 1em;
+
+ .amount {
+ font-size: 18px;
+ font-weight: bold;
+ }
+ .price-suffix {
+ display: block;
+ font-size: 11px;
+ line-height: 15px;
+ }
+ }
+
+ .col-dimensions {
+ color: #999;
+ }
+
+ .col-preview {
+ .image-preview {
+ img {
+ height: auto;
+ max-height: 140px;
+ }
+ }
+ }
+
+ .wc-gzd-dhl-im-product-information-text, .wc-gzd-dhl-im-product-description {
+ font-size: 11px;
+ color: #999;
+ line-height: 1.5em;
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/woocommerce-germanized-dhl/assets/css/parcel-finder.scss b/packages/woocommerce-germanized-dhl/assets/css/parcel-finder.scss
new file mode 100644
index 000000000..9a7c9833b
--- /dev/null
+++ b/packages/woocommerce-germanized-dhl/assets/css/parcel-finder.scss
@@ -0,0 +1,218 @@
+#dhl-parcel-finder-wrapper {
+ backface-visibility: hidden;
+ transform: translateZ(0);
+ width: 100%;
+ height: 100%;
+ left: 0;
+ position: fixed;
+ top: 0;
+ z-index: 99992;
+ visibility: hidden;
+
+ * {
+ box-sizing: border-box;
+ }
+
+ #dhl-parcel-finder-bg-overlay {
+ background: #1e1e1e;
+ opacity: 0;
+ transition-duration: inherit;
+ transition-property: opacity;
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+ }
+
+ &.open {
+ visibility: visible;
+
+ #dhl-parcel-finder-bg-overlay {
+ opacity: .87;
+ transition-timing-function: cubic-bezier(.22,.61,.36,1);
+ }
+ }
+
+ #dhl-parcel-finder-inner {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+ overflow: hidden;
+ z-index: 99994;
+ }
+
+ #dhl-parcel-finder-inner-wrapper {
+ padding: 6px 6px 0;
+ display: block;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ height: 100%;
+ left: 0;
+ outline: none;
+ overflow: auto;
+ -webkit-overflow-scrolling: touch;
+ position: absolute;
+ text-align: center;
+ top: 0;
+ transition-property: transform,opacity;
+ white-space: normal;
+ width: 100%;
+ z-index: 99994;
+
+ &::before {
+ content: "";
+ display: inline-block;
+ height: 100%;
+ margin-right: -.25em;
+ vertical-align: middle;
+ width: 0;
+ }
+
+ #dhl-parcel-finder {
+ width: 95%;
+ height: 95%;
+ display: inline-block;
+ background: #fff;
+ margin: 0 0 6px;
+ max-width: 100%;
+ overflow: auto;
+ padding: 34px;
+ position: relative;
+ text-align: left;
+ vertical-align: middle;
+ }
+ }
+
+ .dhl-parcel-finder-close {
+ background: transparent;
+ border: 0;
+ border-radius: 0;
+ color: #555;
+ cursor: pointer;
+ height: 44px;
+ margin: 0;
+ padding: 6px;
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 44px;
+ z-index: 10;
+
+ svg {
+ fill: transparent;
+ opacity: .8;
+ stroke: currentColor;
+ stroke-width: 1.5;
+ transition: stroke .1s;
+ }
+ }
+
+ #dhl-parcel-finder-map {
+ width: 100%;
+ height: 85%;
+ position: relative !important;
+
+ #parcel-content {
+
+ #bodyContent {
+ line-height: 1.5em;
+ }
+
+ address {
+ margin-bottom: 0;
+ }
+
+ .parcel-title {
+ padding-top: 0;
+ margin-bottom: .5em;
+ }
+
+ .parcel-subtitle {
+ font-size: 0.8125rem;
+ color: #767676;
+ font-weight: 800;
+ letter-spacing: 0.15em;
+ text-transform: uppercase;
+ padding: 1em 0 0;
+ }
+
+ .dhl-parcelshop-select-btn {
+ width: 100%;
+ margin-top: 10px;
+ }
+ }
+ }
+
+ form#dhl-parcel-finder-form {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+ align-items: center;
+
+ .form-field {
+ margin-right: 1.5em;
+
+ &.large {
+ min-width: 20%;
+ }
+
+ &.finder-pickup-type {
+ margin-right: 25px;
+ min-width: 150px;
+ display: flex;
+ align-items: center;
+
+ &.hidden {
+ display: none;
+ }
+
+ .icon {
+ width: 40px;
+ height: 40px;
+ display: inline-block;
+ background-repeat: no-repeat;
+ background-size: contain;
+ margin-left: 10px;
+ }
+ }
+
+ dhl-search-button {
+ text-align: right;
+ margin-right: 0;
+
+ .button {
+ width: 100%;
+ margin: 0;
+ }
+ }
+ }
+ }
+}
+
+@media( max-width: 700px ) {
+ #dhl-parcel-finder-wrapper {
+ form#dhl-parcel-finder-form {
+ .form-field {
+ width: 100%;
+ margin-right: 0;
+
+ &.packstation, &.parcelshop {
+ width: auto;
+ margin-right: 1.5em;
+ }
+
+ dhl-search-button {
+ text-align: left;
+
+ .button {
+ width: 100%;
+ margin: 0;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/woocommerce-germanized-dhl/assets/css/preferred-services.scss b/packages/woocommerce-germanized-dhl/assets/css/preferred-services.scss
new file mode 100644
index 000000000..72123a694
--- /dev/null
+++ b/packages/woocommerce-germanized-dhl/assets/css/preferred-services.scss
@@ -0,0 +1,230 @@
+#tiptip_holder {
+ display: none;
+ z-index: 8675309;
+ position: absolute;
+ top: 0;
+
+ /*rtl:ignore*/
+ left: 0;
+
+ &.tip_top {
+ padding-bottom: 5px;
+
+ #tiptip_arrow_inner {
+ margin-top: -7px;
+ margin-left: -6px;
+ border-top-color: #333;
+ }
+ }
+
+ &.tip_bottom {
+ padding-top: 5px;
+
+ #tiptip_arrow_inner {
+ margin-top: -5px;
+ margin-left: -6px;
+ border-bottom-color: #333;
+ }
+ }
+
+ &.tip_right {
+ padding-left: 5px;
+
+ #tiptip_arrow_inner {
+ margin-top: -6px;
+ margin-left: -5px;
+ border-right-color: #333;
+ }
+ }
+
+ &.tip_left {
+ padding-right: 5px;
+
+ #tiptip_arrow_inner {
+ margin-top: -6px;
+ margin-left: -7px;
+ border-left-color: #333;
+ }
+ }
+}
+
+#tiptip_content {
+ color: #fff;
+ font-size: 0.8em;
+ max-width: 150px;
+ background: #333;
+ text-align: center;
+ border-radius: 3px;
+ padding: 0.618em 1em;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
+
+ code {
+ padding: 1px;
+ background: #888;
+ }
+}
+
+#tiptip_arrow,
+#tiptip_arrow_inner {
+ position: absolute;
+ border-color: transparent;
+ border-style: solid;
+ border-width: 6px;
+ height: 0;
+ width: 0;
+}
+
+.dhl-preferred-service-content {
+ margin-top: 1em;
+
+ .dhl-hidden {
+ display: none;
+ }
+
+ .dhl-preferred-service-cost {
+ font-size: .9em;
+ }
+
+ .dhl-preferred-service-item {
+ margin-bottom: 1em;
+
+ .dhl-preferred-service-logo {
+ img {
+ margin: 0;
+ padding: 0;
+ max-height: 100px;
+ max-width: 100px;
+ background: #FFCC00;
+ }
+ margin-bottom: 1em;
+ }
+
+ .dhl-preferred-service-title {
+ font-weight: bold;
+ font-size: 1em;
+ margin-bottom: .5em;
+ }
+
+ .dhl-preferred-service-cost {
+ margin-bottom: .5em;
+ }
+
+ .dhl-preferred-service-desc {
+ font-size: .9em;
+ margin-bottom: .5em;
+ }
+
+ .dhl-preferred-service-times {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ margin: 0;
+ padding: 0;
+
+ li {
+ flex-basis: 10%;
+ display: inline-block;
+ text-align: center;
+ padding: 10px 0 0;
+ margin: 0 8px 8px 0;
+ background-color: #e3e3e3;
+
+ label {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ padding: 5px 10px;
+ font-size: .9em;
+ font-weight: bold;
+ background-color: #eef4f2;
+ cursor: pointer;
+ margin: 0;
+ color: #5f7285;
+
+ .dhl-preferred-time-title {
+ font-size: 1.2em;
+ }
+ }
+
+ input[type=radio] {
+ opacity: 0;
+ width: 1px;
+ height: 1px;
+ position: absolute;
+
+ &:checked ~ label {
+ background-color: #FFCC00;
+ }
+ }
+ }
+
+ &.dhl-preferred-service-time {
+ li {
+ flex-grow: inherit;
+ flex-basis: inherit;
+
+ label {
+ .dhl-preferred-time-title {
+ font-size: 1em;
+ }
+ }
+ }
+ }
+ }
+
+ .dhl-preferred-service-data {
+ input[type=text] {
+ width: 100%;
+ margin-bottom: .5em;
+ }
+ }
+
+ .woocommerce-help-tip {
+ background: #ffcc00;
+ display: inline-block;
+ font-size: 1em;
+ font-style: normal;
+ height: 18px;
+ padding: 3px;
+ margin-top: -5px;
+ margin-left: 5px;
+ border-radius: 50%;
+ line-height: 18px;
+ position: relative;
+ vertical-align: middle;
+ width: 18px;
+
+ &::after {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ text-align: center;
+ cursor: help;
+ content: "?";
+ }
+ }
+
+ .dhl-preferred-location-types, .dhl-preferred-delivery-types {
+ list-style: none;
+ margin: 0;
+ margin-bottom: 1em;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+ align-items: center;
+
+ li {
+ margin-right: 1em;
+ }
+ }
+
+ &.dhl-preferred-service-header {
+ .dhl-preferred-service-title {
+ font-size: 1.1em;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/woocommerce-germanized-dhl/assets/img/dhl-official.png b/packages/woocommerce-germanized-dhl/assets/img/dhl-official.png
new file mode 100755
index 000000000..18dab3cf6
Binary files /dev/null and b/packages/woocommerce-germanized-dhl/assets/img/dhl-official.png differ
diff --git a/packages/woocommerce-germanized-dhl/assets/img/packstation.png b/packages/woocommerce-germanized-dhl/assets/img/packstation.png
new file mode 100755
index 000000000..903d8388b
Binary files /dev/null and b/packages/woocommerce-germanized-dhl/assets/img/packstation.png differ
diff --git a/packages/woocommerce-germanized-dhl/assets/img/parcelshop.png b/packages/woocommerce-germanized-dhl/assets/img/parcelshop.png
new file mode 100755
index 000000000..15fafae42
Binary files /dev/null and b/packages/woocommerce-germanized-dhl/assets/img/parcelshop.png differ
diff --git a/packages/woocommerce-germanized-dhl/assets/img/post_office.png b/packages/woocommerce-germanized-dhl/assets/img/post_office.png
new file mode 100755
index 000000000..097023dcd
Binary files /dev/null and b/packages/woocommerce-germanized-dhl/assets/img/post_office.png differ
diff --git a/packages/woocommerce-germanized-dhl/assets/img/wp-int-eu-preview.png b/packages/woocommerce-germanized-dhl/assets/img/wp-int-eu-preview.png
new file mode 100644
index 000000000..a4438cd37
Binary files /dev/null and b/packages/woocommerce-germanized-dhl/assets/img/wp-int-eu-preview.png differ
diff --git a/packages/woocommerce-germanized-dhl/assets/img/wp-int-preview.png b/packages/woocommerce-germanized-dhl/assets/img/wp-int-preview.png
new file mode 100644
index 000000000..6ac0f7e34
Binary files /dev/null and b/packages/woocommerce-germanized-dhl/assets/img/wp-int-preview.png differ
diff --git a/packages/woocommerce-germanized-dhl/assets/js/blocks/checkout/index.js b/packages/woocommerce-germanized-dhl/assets/js/blocks/checkout/index.js
new file mode 100644
index 000000000..59bb39b0d
--- /dev/null
+++ b/packages/woocommerce-germanized-dhl/assets/js/blocks/checkout/index.js
@@ -0,0 +1,2 @@
+import './slotfills/dom-watcher';
+import './slotfills/preferred-services';
\ No newline at end of file
diff --git a/packages/woocommerce-germanized-dhl/assets/js/blocks/checkout/slotfills/dom-watcher.js b/packages/woocommerce-germanized-dhl/assets/js/blocks/checkout/slotfills/dom-watcher.js
new file mode 100644
index 000000000..2ce02ebb8
--- /dev/null
+++ b/packages/woocommerce-germanized-dhl/assets/js/blocks/checkout/slotfills/dom-watcher.js
@@ -0,0 +1,39 @@
+import { ExperimentalOrderMeta } from '@woocommerce/blocks-checkout';
+import { registerPlugin } from '@wordpress/plugins';
+import { useEffect } from "@wordpress/element";
+import { dispatch, select } from '@wordpress/data';
+import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
+
+const DomWatcher = ({
+ extensions,
+ cart
+}) => {
+ /**
+ * Use this little helper which sets the default checkout data in case it
+ * does not exist, e.g. the checkboxes block is missing to prevent extension errors.
+ *
+ * @see https://github.com/woocommerce/woocommerce-blocks/issues/11446
+ */
+ useEffect(() => {
+ const extensionsData = select( CHECKOUT_STORE_KEY ).getExtensionData();
+
+ if ( ! extensionsData.hasOwnProperty( 'woocommerce-gzd-dhl' ) ) {
+ dispatch( CHECKOUT_STORE_KEY ).__internalSetExtensionData( 'woocommerce-gzd-dhl', {} );
+ }
+ }, [] );
+
+ return null;
+};
+
+const render = () => {
+ return (
+
+ { _x( 'Choose a delivery day', 'dhl', 'woocommerce-germanized' ) }
+
+ { costValue > 0 &&
+ (+
{ _x( 'Choose a preferred location', 'dhl', 'woocommerce-germanized' ) }
+{ _x( 'Choose a delivery type', 'dhl', 'woocommerce-germanized' ) }
+ +' + message + '
' + $addressInput.data( 'desc-dhl' ) + '
' ); + } + } + }, + + isEnabled: function() { + var self = germanized.dhl_parcel_locator; + + return self.isAvailable() && $( self.wrapper + ' #shipping_address_type' ).val() === 'dhl'; + }, + + getPaymentMethod: function() { + var $selected = $( '.payment_methods .input-radio:checked' ); + + if ( $selected ) { + return $selected.val(); + } + + return ''; + }, + + getShippingMethod: function( pwithInstanceId ) { + var current = ''; + var withInstanceId = pwithInstanceId ? pwithInstanceId : true; + + if ( $( 'select.shipping_method' ).length > 0 ) { + current = $( 'select.shipping_method' ).val(); + } else if ( $( 'input[name^="shipping_method"]:checked' ).length > 0 ) { + current = $( 'input[name^="shipping_method"]:checked' ).val(); + } else if ( $( 'input[name^="shipping_method"][type="hidden"]' ).length > 0 ) { + current = $( 'input[name^="shipping_method"][type="hidden"]' ).val(); + } + + if ( ! withInstanceId ) { + if ( 'undefined' !== typeof current && current.length > 0 ) { + var currentParts = current.split(':'); + + if ( currentParts.length > 0 ) { + current = currentParts[0]; + } + } + } else { + // In case an instance id is needed but missing - assume 0 as instance id + if ( 'undefined' !== typeof current && current.length > 0 ) { + var currentParts = current.split(':'); + + if ( currentParts.length <= 1 ) { + current = current + ':0'; + } + } + } + + return current; + }, + + pickupTypeIsAvailable: function( pickupType ) { + var self = germanized.dhl_parcel_locator, + shippingMethod = self.getShippingMethod(), + isAvailable = true; + + if ( ! self.shippingMethodSupportsPickupType( shippingMethod, pickupType ) ) { + isAvailable = false; + } + + return isAvailable; + }, + + isAvailable: function() { + var self = germanized.dhl_parcel_locator, + shippingCountry = $( self.wrapper + ' #shipping_country' ).val(), + shippingMethod = self.getShippingMethod(), + paymentMethod = self.getPaymentMethod(), + methodData = self.getShippingMethodData( shippingMethod ), + isAvailable = true; + + if ( $.inArray( paymentMethod, self.params.excluded_gateways ) !== -1 ) { + isAvailable = false; + } + + if ( $.inArray( shippingCountry, self.params.supported_countries ) === -1 ) { + isAvailable = false; + } + + if ( self.isCheckout() ) { + if ( ! methodData || methodData.supports.length === 0 ) { + isAvailable = false; + } + } + + return isAvailable; + } + }; + + $( document ).ready( function() { + germanized.dhl_parcel_locator.init(); + }); + +})( jQuery, window.germanized ); diff --git a/packages/woocommerce-germanized-dhl/assets/js/static/preferred-services.js b/packages/woocommerce-germanized-dhl/assets/js/static/preferred-services.js new file mode 100644 index 000000000..7b212fe78 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/assets/js/static/preferred-services.js @@ -0,0 +1,79 @@ + +window.germanized = window.germanized || {}; +window.germanized.dhl_preferred_services = window.germanized.dhl_preferred_services || {}; + +( function( $, germanized ) { + + /** + * Core + */ + germanized.dhl_preferred_services = { + + params: {}, + + init: function () { + var self = germanized.dhl_preferred_services; + self.params = wc_gzd_dhl_preferred_services_params; + + $( document.body ).on( 'updated_checkout', self.afterRefreshCheckout ); + + $( document ) + .on( 'change', '.dhl-preferred-service-content .dhl-preferred-location-types input', self.onChangeLocationType ) + .on( 'change', '.woocommerce-checkout #billing_postcode', self.triggerCheckoutRefresh ) + .on( 'change', '.woocommerce-checkout #shipping_postcode', self.triggerCheckoutRefresh ) + .on( 'change', '.dhl-preferred-service-content .dhl-preferred-service-times input', self.triggerCheckoutRefresh ) + .on( 'change', '.dhl-preferred-service-content .dhl-preferred-delivery-types input', self.triggerCheckoutRefresh ); + + if ( self.params.payment_gateways_excluded ) { + $( document.body ).on( 'payment_method_selected', self.triggerCheckoutRefresh ); + } + + self.afterRefreshCheckout(); + }, + + triggerCheckoutRefresh: function() { + $( document.body ).trigger( 'update_checkout' ); + }, + + afterRefreshCheckout: function() { + var self = germanized.dhl_preferred_services; + + self.initTipTip(); + self.onChangeLocationType(); + }, + + onChangeLocationType: function() { + var self = germanized.dhl_preferred_services, + $box = $( '.dhl-preferred-service-content .dhl-preferred-location-types input:checked' ); + + $( '.dhl-preferred-service-content .dhl-preferred-service-location-data' ).hide(); + + if ( $box.length > 0 ) { + if ( 'place' === $box.val() ) { + $( '.dhl-preferred-service-content .dhl-preferred-service-location-place' ).show(); + } else if ( 'neighbor' === $box.val() ) { + $( '.dhl-preferred-service-content .dhl-preferred-service-location-neighbor' ).show(); + } + } + }, + + initTipTip: function() { + + // Remove any lingering tooltips + $( '#tiptip_holder' ).removeAttr( 'style' ); + $( '#tiptip_arrow' ).removeAttr( 'style' ); + + $( '.dhl-preferred-service-content .woocommerce-help-tip' ).tipTip({ + 'attribute': 'data-tip', + 'fadeIn': 50, + 'fadeOut': 50, + 'delay': 200 + }); + } + }; + + $( document ).ready( function() { + germanized.dhl_preferred_services.init(); + }); + +})( jQuery, window.germanized ); diff --git a/packages/woocommerce-germanized-dhl/assets/wsdl/OneClickForAppV3.wsdl b/packages/woocommerce-germanized-dhl/assets/wsdl/OneClickForAppV3.wsdl new file mode 100644 index 000000000..ee4fe0066 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/assets/wsdl/OneClickForAppV3.wsdl @@ -0,0 +1,2743 @@ +
+ Element provides terms of trades, incoterms codes:
+
+ - DDP (Delivery Duty Paid)
+ - DXV (Delivery Duty Paid (excl. VAT))
+ - DAP (formerly DDU, Delivery At Place)
+ - DDX (Delivery Duty Paid (excl. Duties, taxes and VAT)
+ - CPT (Carriage Paid To (within EU only))
+
+ are vaild values.
+
+ '+e+"
'+i.data("desc-dhl")+"
")))},isEnabled:function(){var r=a.dhl_parcel_locator;return r.isAvailable()&&"dhl"===e(r.wrapper+" #shipping_address_type").val()},getPaymentMethod:function(){var a=e(".payment_methods .input-radio:checked");return a?a.val():""},getShippingMethod:function(a){var r,o="",p=a||!0;return e("select.shipping_method").length>0?o=e("select.shipping_method").val():e('input[name^="shipping_method"]:checked').length>0?o=e('input[name^="shipping_method"]:checked').val():e('input[name^="shipping_method"][type="hidden"]').length>0&&(o=e('input[name^="shipping_method"][type="hidden"]').val()),p?void 0!==o&&o.length>0&&(r=o.split(":")).length<=1&&(o+=":0"):void 0!==o&&o.length>0&&(r=o.split(":")).length>0&&(o=r[0]),o},pickupTypeIsAvailable:function(e){var r=a.dhl_parcel_locator,o=r.getShippingMethod(),p=!0;return r.shippingMethodSupportsPickupType(o,e)||(p=!1),p},isAvailable:function(){var r=a.dhl_parcel_locator,o=e(r.wrapper+" #shipping_country").val(),p=r.getShippingMethod(),t=r.getPaymentMethod(),d=r.getShippingMethodData(p),i=!0;return-1!==e.inArray(t,r.params.excluded_gateways)&&(i=!1),-1===e.inArray(o,r.params.supported_countries)&&(i=!1),r.isCheckout()&&(d&&0!==d.supports.length||(i=!1)),i}},e(document).ready((function(){a.dhl_parcel_locator.init()})),((window.germanizedShipments=window.germanizedShipments||{}).static=window.germanizedShipments.static||{})["parcel-locator"]={}}(); \ No newline at end of file diff --git a/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.asset.php b/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.asset.php new file mode 100644 index 000000000..e150bc586 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.asset.php @@ -0,0 +1 @@ + array(), 'version' => '8e52d369588e281ba2f7'); diff --git a/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.css b/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.css new file mode 100644 index 000000000..9875538b3 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.css @@ -0,0 +1 @@ +#tiptip_holder{display:none;left:0;position:absolute;top:0;z-index:8675309}#tiptip_holder.tip_top{padding-bottom:5px}#tiptip_holder.tip_top #tiptip_arrow_inner{border-top-color:#333;margin-left:-6px;margin-top:-7px}#tiptip_holder.tip_bottom{padding-top:5px}#tiptip_holder.tip_bottom #tiptip_arrow_inner{border-bottom-color:#333;margin-left:-6px;margin-top:-5px}#tiptip_holder.tip_right{padding-left:5px}#tiptip_holder.tip_right #tiptip_arrow_inner{border-right-color:#333;margin-left:-5px;margin-top:-6px}#tiptip_holder.tip_left{padding-right:5px}#tiptip_holder.tip_left #tiptip_arrow_inner{border-left-color:#333;margin-left:-7px;margin-top:-6px}#tiptip_content{background:#333;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.2);color:#fff;font-size:.8em;max-width:150px;padding:.618em 1em;text-align:center}#tiptip_content code{background:#888;padding:1px}#tiptip_arrow,#tiptip_arrow_inner{border:6px solid transparent;height:0;position:absolute;width:0}.dhl-preferred-service-content{margin-top:1em}.dhl-preferred-service-content .dhl-hidden{display:none}.dhl-preferred-service-content .dhl-preferred-service-cost{font-size:.9em}.dhl-preferred-service-content .dhl-preferred-service-item,.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-logo{margin-bottom:1em}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-logo img{background:#fc0;margin:0;max-height:100px;max-width:100px;padding:0}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-title{font-size:1em;font-weight:700;margin-bottom:.5em}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-cost{margin-bottom:.5em}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-desc{font-size:.9em;margin-bottom:.5em}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times{display:flex;flex-direction:row;flex-wrap:wrap;margin:0;padding:0}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times li{background-color:#e3e3e3;display:inline-block;flex-basis:10%;margin:0 8px 8px 0;padding:10px 0 0;text-align:center}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times li label{background-color:#eef4f2;color:#5f7285;cursor:pointer;display:flex;flex-direction:column;flex-wrap:wrap;font-size:.9em;font-weight:700;margin:0;padding:5px 10px;position:relative}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times li label .dhl-preferred-time-title{font-size:1.2em}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times li input[type=radio]{height:1px;opacity:0;position:absolute;width:1px}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times li input[type=radio]:checked~label{background-color:#fc0}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times.dhl-preferred-service-time li{flex-basis:inherit;flex-grow:inherit}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-times.dhl-preferred-service-time li label .dhl-preferred-time-title{font-size:1em}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-service-data input[type=text]{margin-bottom:.5em;width:100%}.dhl-preferred-service-content .dhl-preferred-service-item .woocommerce-help-tip{background:#fc0;border-radius:50%;display:inline-block;font-size:1em;font-style:normal;height:18px;line-height:18px;margin-left:5px;margin-top:-5px;padding:3px;position:relative;vertical-align:middle;width:18px}.dhl-preferred-service-content .dhl-preferred-service-item .woocommerce-help-tip:after{content:"?";cursor:help;height:100%;left:0;position:absolute;text-align:center;top:0;width:100%}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-delivery-types,.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-location-types{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start;list-style:none;margin:0 0 1em}.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-delivery-types li,.dhl-preferred-service-content .dhl-preferred-service-item .dhl-preferred-location-types li{margin-right:1em}.dhl-preferred-service-content .dhl-preferred-service-item.dhl-preferred-service-header .dhl-preferred-service-title{font-size:1.1em} diff --git a/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.js b/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.js new file mode 100644 index 000000000..4bf49343d --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/static/preferred-services-styles.js @@ -0,0 +1 @@ +!function(){"use strict";var e={};(function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})})(e),((window.germanizedShipments=window.germanizedShipments||{}).static=window.germanizedShipments.static||{})["preferred-services-styles"]=e}(); \ No newline at end of file diff --git a/packages/woocommerce-germanized-dhl/build/static/preferred-services.asset.php b/packages/woocommerce-germanized-dhl/build/static/preferred-services.asset.php new file mode 100644 index 000000000..d83d8fc4b --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/static/preferred-services.asset.php @@ -0,0 +1 @@ + array(), 'version' => '004db9f9bf1c616872db'); diff --git a/packages/woocommerce-germanized-dhl/build/static/preferred-services.js b/packages/woocommerce-germanized-dhl/build/static/preferred-services.js new file mode 100644 index 000000000..9c62e9885 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/static/preferred-services.js @@ -0,0 +1 @@ +!function(){var e,r;window.germanized=window.germanized||{},window.germanized.dhl_preferred_services=window.germanized.dhl_preferred_services||{},e=jQuery,(r=window.germanized).dhl_preferred_services={params:{},init:function(){var t=r.dhl_preferred_services;t.params=wc_gzd_dhl_preferred_services_params,e(document.body).on("updated_checkout",t.afterRefreshCheckout),e(document).on("change",".dhl-preferred-service-content .dhl-preferred-location-types input",t.onChangeLocationType).on("change",".woocommerce-checkout #billing_postcode",t.triggerCheckoutRefresh).on("change",".woocommerce-checkout #shipping_postcode",t.triggerCheckoutRefresh).on("change",".dhl-preferred-service-content .dhl-preferred-service-times input",t.triggerCheckoutRefresh).on("change",".dhl-preferred-service-content .dhl-preferred-delivery-types input",t.triggerCheckoutRefresh),t.params.payment_gateways_excluded&&e(document.body).on("payment_method_selected",t.triggerCheckoutRefresh),t.afterRefreshCheckout()},triggerCheckoutRefresh:function(){e(document.body).trigger("update_checkout")},afterRefreshCheckout:function(){var e=r.dhl_preferred_services;e.initTipTip(),e.onChangeLocationType()},onChangeLocationType:function(){r.dhl_preferred_services;var t=e(".dhl-preferred-service-content .dhl-preferred-location-types input:checked");e(".dhl-preferred-service-content .dhl-preferred-service-location-data").hide(),t.length>0&&("place"===t.val()?e(".dhl-preferred-service-content .dhl-preferred-service-location-place").show():"neighbor"===t.val()&&e(".dhl-preferred-service-content .dhl-preferred-service-location-neighbor").show())},initTipTip:function(){e("#tiptip_holder").removeAttr("style"),e("#tiptip_arrow").removeAttr("style"),e(".dhl-preferred-service-content .woocommerce-help-tip").tipTip({attribute:"data-tip",fadeIn:50,fadeOut:50,delay:200})}},e(document).ready((function(){r.dhl_preferred_services.init()})),((window.germanizedShipments=window.germanizedShipments||{}).static=window.germanizedShipments.static||{})["preferred-services"]={}}(); \ No newline at end of file diff --git a/packages/woocommerce-germanized-dhl/build/style-checkout.css b/packages/woocommerce-germanized-dhl/build/style-checkout.css new file mode 100644 index 000000000..a35a156a8 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/style-checkout.css @@ -0,0 +1 @@ +.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-days,.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-location{margin-bottom:1em}.wc-gzd-checkout-dhl .wc-gzd-checkout-dhl-title{display:flex;flex-wrap:wrap;align-items:center;font-size:1em}.wc-gzd-checkout-dhl .wc-gzd-checkout-dhl-title .dhl-icon{width:130px;height:auto;margin-left:auto}.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-day-select{display:flex;flex-direction:row;flex-wrap:wrap;margin:0;padding:0}.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-day-select .wc-gzd-dhl-preferred-day{color:inherit;flex-basis:10%;flex-grow:1;text-align:center;padding:10px 0 0;margin:0 8px 8px 0;background-color:#e3e3e3;border:none}.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-day-select .wc-gzd-dhl-preferred-day:last-child{margin-right:0}.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-day-select .wc-gzd-dhl-preferred-day .inner{position:relative;display:flex;flex-direction:column;flex-wrap:wrap;padding:5px 10px;font-size:1em;background-color:#eef4f2;cursor:pointer;margin:0;line-height:1.8em}.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-day-select .wc-gzd-dhl-preferred-day .inner .day{font-size:1.4em}.wc-gzd-checkout-dhl .wc-gzd-dhl-preferred-day-select .wc-gzd-dhl-preferred-day.active .inner{background-color:#fc0} diff --git a/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl-style.asset.php b/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl-style.asset.php new file mode 100644 index 000000000..3ced3c40e --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl-style.asset.php @@ -0,0 +1 @@ + array(), 'version' => '2303301f334e9e994f3c'); diff --git a/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl.asset.php b/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl.asset.php new file mode 100644 index 000000000..3ced3c40e --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl.asset.php @@ -0,0 +1 @@ + array(), 'version' => '2303301f334e9e994f3c'); diff --git a/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl.js b/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl.js new file mode 100644 index 000000000..c3d6d1e81 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/build/wc-gzd-shipments-blocks-dhl.js @@ -0,0 +1 @@ +((window.wcShipments=window.wcShipments||{}).blocks=window.wcShipments.blocks||{})["wc-gzd-shipments-blocks-dhl"]={}; \ No newline at end of file diff --git a/packages/woocommerce-germanized-dhl/i18n/holidays.php b/packages/woocommerce-germanized-dhl/i18n/holidays.php new file mode 100644 index 000000000..733cd756f --- /dev/null +++ b/packages/woocommerce-germanized-dhl/i18n/holidays.php @@ -0,0 +1,55 @@ + array( + '2024-01-01', + '2024-01-06', + '2024-03-08', + '2024-03-29', + '2024-03-31', + '2024-04-01', + '2024-05-01', + '2024-05-09', + '2024-05-19', + '2024-05-20', + '2024-05-30', + '2024-08-08', + '2024-08-15', + '2024-09-20', + '2024-10-03', + '2024-10-31', + '2024-11-01', + '2024-11-20', + '2024-12-25', + '2024-12-26', + '2025-01-01', + '2025-01-06', + '2025-03-08', + '2025-04-18', + '2025-04-20', + '2025-04-21', + '2025-05-01', + '2025-05-29', + '2025-06-08', + '2025-06-09', + '2025-06-19', + '2025-08-08', + '2025-08-15', + '2025-09-20', + '2025-10-03', + '2025-10-31', + '2025-11-01', + '2025-11-19', + '2025-12-25', + '2025-12-26', + ), +); diff --git a/packages/woocommerce-germanized-dhl/i18n/iso.php b/packages/woocommerce-germanized-dhl/i18n/iso.php new file mode 100644 index 000000000..75cc005be --- /dev/null +++ b/packages/woocommerce-germanized-dhl/i18n/iso.php @@ -0,0 +1,260 @@ + 'AFG', + 'AX' => 'ALA', + 'AL' => 'ALB', + 'DZ' => 'DZA', + 'AS' => 'ASM', + 'AD' => 'AND', + 'AO' => 'AGO', + 'AI' => 'AIA', + 'AQ' => 'ATA', + 'AG' => 'ATG', + 'AR' => 'ARG', + 'AM' => 'ARM', + 'AW' => 'ABW', + 'AU' => 'AUS', + 'AT' => 'AUT', + 'AZ' => 'AZE', + 'BS' => 'BHS', + 'BH' => 'BHR', + 'BD' => 'BGD', + 'BB' => 'BRB', + 'BY' => 'BLR', + 'BE' => 'BEL', + 'BZ' => 'BLZ', + 'BJ' => 'BEN', + 'BM' => 'BMU', + 'BT' => 'BTN', + 'BO' => 'BOL', + 'BA' => 'BIH', + 'BW' => 'BWA', + 'BV' => 'BVT', + 'BR' => 'BRA', + 'VG' => 'VGB', + 'IO' => 'IOT', + 'BN' => 'BRN', + 'BG' => 'BGR', + 'BF' => 'BFA', + 'BI' => 'BDI', + 'KH' => 'KHM', + 'CM' => 'CMR', + 'CA' => 'CAN', + 'CV' => 'CPV', + 'KY' => 'CYM', + 'CF' => 'CAF', + 'TD' => 'TCD', + 'CL' => 'CHL', + 'CN' => 'CHN', + 'HK' => 'HKG', + 'MO' => 'MAC', + 'CX' => 'CXR', + 'CC' => 'CCK', + 'CO' => 'COL', + 'KM' => 'COM', + 'CG' => 'COG', + 'CD' => 'COD', + 'CK' => 'COK', + 'CR' => 'CRI', + 'CI' => 'CIV', + 'HR' => 'HRV', + 'CU' => 'CUB', + 'CY' => 'CYP', + 'CZ' => 'CZE', + 'DK' => 'DNK', + 'DJ' => 'DJI', + 'DM' => 'DMA', + 'DO' => 'DOM', + 'EC' => 'ECU', + 'EG' => 'EGY', + 'SV' => 'SLV', + 'GQ' => 'GNQ', + 'ER' => 'ERI', + 'EE' => 'EST', + 'ET' => 'ETH', + 'FK' => 'FLK', + 'FO' => 'FRO', + 'FJ' => 'FJI', + 'FI' => 'FIN', + 'FR' => 'FRA', + 'GF' => 'GUF', + 'PF' => 'PYF', + 'TF' => 'ATF', + 'GA' => 'GAB', + 'GM' => 'GMB', + 'GE' => 'GEO', + 'DE' => 'DEU', + 'GH' => 'GHA', + 'GI' => 'GIB', + 'GR' => 'GRC', + 'GL' => 'GRL', + 'GD' => 'GRD', + 'GP' => 'GLP', + 'GU' => 'GUM', + 'GT' => 'GTM', + 'GG' => 'GGY', + 'GN' => 'GIN', + 'GW' => 'GNB', + 'GY' => 'GUY', + 'HT' => 'HTI', + 'HM' => 'HMD', + 'VA' => 'VAT', + 'HN' => 'HND', + 'HU' => 'HUN', + 'IS' => 'ISL', + 'IN' => 'IND', + 'ID' => 'IDN', + 'IR' => 'IRN', + 'IQ' => 'IRQ', + 'IE' => 'IRL', + 'IM' => 'IMN', + 'IL' => 'ISR', + 'IT' => 'ITA', + 'JM' => 'JAM', + 'JP' => 'JPN', + 'JE' => 'JEY', + 'JO' => 'JOR', + 'KZ' => 'KAZ', + 'KE' => 'KEN', + 'KI' => 'KIR', + 'KP' => 'PRK', + 'KR' => 'KOR', + 'KW' => 'KWT', + 'KG' => 'KGZ', + 'LA' => 'LAO', + 'LV' => 'LVA', + 'LB' => 'LBN', + 'LS' => 'LSO', + 'LR' => 'LBR', + 'LY' => 'LBY', + 'LI' => 'LIE', + 'LT' => 'LTU', + 'LU' => 'LUX', + 'MK' => 'MKD', + 'MG' => 'MDG', + 'MW' => 'MWI', + 'MY' => 'MYS', + 'MV' => 'MDV', + 'ML' => 'MLI', + 'MT' => 'MLT', + 'MH' => 'MHL', + 'MQ' => 'MTQ', + 'MR' => 'MRT', + 'MU' => 'MUS', + 'YT' => 'MYT', + 'MX' => 'MEX', + 'FM' => 'FSM', + 'MD' => 'MDA', + 'MC' => 'MCO', + 'MN' => 'MNG', + 'ME' => 'MNE', + 'MS' => 'MSR', + 'MA' => 'MAR', + 'MZ' => 'MOZ', + 'MM' => 'MMR', + 'NA' => 'NAM', + 'NR' => 'NRU', + 'NP' => 'NPL', + 'NL' => 'NLD', + 'AN' => 'ANT', + 'NC' => 'NCL', + 'NZ' => 'NZL', + 'NI' => 'NIC', + 'NE' => 'NER', + 'NG' => 'NGA', + 'NU' => 'NIU', + 'NF' => 'NFK', + 'MP' => 'MNP', + 'NO' => 'NOR', + 'OM' => 'OMN', + 'PK' => 'PAK', + 'PW' => 'PLW', + 'PS' => 'PSE', + 'PA' => 'PAN', + 'PG' => 'PNG', + 'PY' => 'PRY', + 'PE' => 'PER', + 'PH' => 'PHL', + 'PN' => 'PCN', + 'PL' => 'POL', + 'PT' => 'PRT', + 'PR' => 'PRI', + 'QA' => 'QAT', + 'RE' => 'REU', + 'RO' => 'ROU', + 'RU' => 'RUS', + 'RW' => 'RWA', + 'BL' => 'BLM', + 'SH' => 'SHN', + 'KN' => 'KNA', + 'LC' => 'LCA', + 'MF' => 'MAF', + 'PM' => 'SPM', + 'VC' => 'VCT', + 'WS' => 'WSM', + 'SM' => 'SMR', + 'ST' => 'STP', + 'SA' => 'SAU', + 'SN' => 'SEN', + 'RS' => 'SRB', + 'SC' => 'SYC', + 'SL' => 'SLE', + 'SG' => 'SGP', + 'SK' => 'SVK', + 'SI' => 'SVN', + 'SB' => 'SLB', + 'SO' => 'SOM', + 'ZA' => 'ZAF', + 'GS' => 'SGS', + 'SS' => 'SSD', + 'ES' => 'ESP', + 'LK' => 'LKA', + 'SD' => 'SDN', + 'SR' => 'SUR', + 'SJ' => 'SJM', + 'SZ' => 'SWZ', + 'SE' => 'SWE', + 'CH' => 'CHE', + 'SY' => 'SYR', + 'TW' => 'TWN', + 'TJ' => 'TJK', + 'TZ' => 'TZA', + 'TH' => 'THA', + 'TL' => 'TLS', + 'TG' => 'TGO', + 'TK' => 'TKL', + 'TO' => 'TON', + 'TT' => 'TTO', + 'TN' => 'TUN', + 'TR' => 'TUR', + 'TM' => 'TKM', + 'TC' => 'TCA', + 'TV' => 'TUV', + 'UG' => 'UGA', + 'UA' => 'UKR', + 'AE' => 'ARE', + 'GB' => 'GBR', + 'US' => 'USA', + 'UM' => 'UMI', + 'UY' => 'URY', + 'UZ' => 'UZB', + 'VU' => 'VUT', + 'VE' => 'VEN', + 'VN' => 'VNM', + 'VI' => 'VIR', + 'WF' => 'WLF', + 'EH' => 'ESH', + 'YE' => 'YEM', + 'ZM' => 'ZMB', + 'ZW' => 'ZWE', +); diff --git a/packages/woocommerce-germanized-dhl/includes/wc-gzd-dhl-core-functions.php b/packages/woocommerce-germanized-dhl/includes/wc-gzd-dhl-core-functions.php new file mode 100644 index 000000000..33f4eb3d5 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/includes/wc-gzd-dhl-core-functions.php @@ -0,0 +1,662 @@ +get_shipment() ) { + return false; + } + + $customs_data = $label->get_customs_data( $max_desc_length ); + + foreach ( $customs_data['items'] as $key => $item ) { + if ( $shipment_item = $shipment->get_item( $key ) ) { + /** + * Apply legacy filters + */ + $item['description'] = apply_filters( 'woocommerce_gzd_dhl_customs_item_description', $item['description'], $shipment_item, $label, $shipment ); + $item['category'] = apply_filters( 'woocommerce_gzd_dhl_customs_item_category', $item['category'], $shipment_item, $label, $shipment ); + + $customs_data['items'][ $key ] = apply_filters( 'woocommerce_gzd_dhl_customs_item', $item, $shipment_item, $shipment, $label ); + } + } + + return apply_filters( 'woocommerce_gzd_dhl_customs_data', $customs_data, $label, $shipment ); +} + +/** + * @param false|Shipment $shipment + * + * @return array + */ +function wc_gzd_dhl_get_label_payment_ref_placeholder( $shipment = false ) { + return apply_filters( + 'woocommerce_gzd_dhl_label_payment_ref_placeholder', + array( + '{shipment_id}' => $shipment ? $shipment->get_shipment_number() : '', + '{order_id}' => $shipment ? $shipment->get_order_number() : '', + '{email}' => $shipment ? $shipment->get_email() : '', + ) + ); +} + +function wc_gzd_dhl_get_preferred_days_select_options( $days, $current = '' ) { + $preferred_days = array( 0 => _x( 'None', 'dhl day context', 'woocommerce-germanized' ) ); + + if ( ! empty( $days ) ) { + $days = array_keys( $days ); + + foreach ( $days as $day ) { + if ( empty( $day ) ) { + continue; + } + + $date = new \WC_DateTime( $day ); + $date->setTimezone( new DateTimeZone( 'Europe/Berlin' ) ); + + $formatted_day = $date->date_i18n( wc_date_format() ); + $preferred_days = array_merge( $preferred_days, array( $day => $formatted_day ) ); + } + } + + if ( ! empty( $current ) ) { + $date = new \WC_DateTime( $current ); + $date->setTimezone( new DateTimeZone( 'Europe/Berlin' ) ); + + $preferred_days[ $current ] = $date->date_i18n( wc_date_format() ); + } + + return $preferred_days; +} + +function wc_gzd_dhl_get_duties() { + $duties = array( + 'DDP' => _x( 'Delivery Duty Paid', 'dhl', 'woocommerce-germanized' ), + 'DAP' => _x( 'Delivery At Place', 'dhl', 'woocommerce-germanized' ), + 'DXV' => _x( 'Delivery Duty Paid (excl. VAT )', 'dhl', 'woocommerce-germanized' ), + 'DDX' => _x( 'Delivery Duty Paid (excl. Duties, taxes and VAT)', 'dhl', 'woocommerce-germanized' ), + ); + + return $duties; +} + +function wc_gzd_dhl_is_valid_visual_min_age( $min_age ) { + $ages = wc_gzd_dhl_get_visual_min_ages(); + + if ( empty( $min_age ) || ( ! array_key_exists( $min_age, $ages ) && ! in_array( $min_age, $ages, true ) ) ) { + return false; + } + + return true; +} + +function wc_gzd_dhl_is_valid_ident_min_age( $min_age ) { + $ages = wc_gzd_dhl_get_ident_min_ages(); + + if ( empty( $min_age ) || ( ! array_key_exists( $min_age, $ages ) && ! in_array( $min_age, $ages, true ) ) ) { + return false; + } + + return true; +} + +function wc_gzd_dhl_get_visual_min_ages() { + $visual_age = array( + '0' => _x( 'None', 'age context', 'woocommerce-germanized' ), + 'A16' => _x( 'Minimum age of 16', 'dhl', 'woocommerce-germanized' ), + 'A18' => _x( 'Minimum age of 18', 'dhl', 'woocommerce-germanized' ), + ); + + return $visual_age; +} + +function wc_gzd_dhl_get_ident_min_ages() { + return wc_gzd_dhl_get_visual_min_ages(); +} + +function wc_gzd_dhl_get_label_reference( $reference_text, $placeholders = array() ) { + return str_replace( array_keys( $placeholders ), array_values( $placeholders ), $reference_text ); +} + +/** + * @param Label\Label $label + * @param Shipment $shipment + * + * @return string + */ +function wc_gzd_dhl_get_label_customer_reference( $label, $shipment ) { + $dhl = wc_gzd_get_shipping_provider( 'dhl' ); + $default_ref = $dhl->get_formatted_label_reference( $label, 'simple', 'ref_1' ); + + /** + * Filter to adjust the customer reference field placed on the DHL label. Maximum characeter length: 35. + * + * @param string $text The customer reference text. + * @param Label\Label $label The label instance. + * @param SimpleShipment $shipment The shipment instance. + * + * @since 3.0.0 + * @package Vendidero/Germanized/DHL + */ + $ref = apply_filters( + 'woocommerce_gzd_dhl_label_customer_reference', + $default_ref, + $label, + $shipment + ); + + return wc_gzd_dhl_escape_reference( $ref ); +} + +function wc_gzd_dhl_get_endorsement_types() { + return array( + 'return' => _x( 'Return shipment', 'dhl', 'woocommerce-germanized' ), + 'abandon' => _x( 'Abandon shipment', 'dhl', 'woocommerce-germanized' ), + ); +} + +/** + * @param Label\DHL $label + * @param $shipment + * @param string $api_type + * + * @return string + */ +function wc_gzd_dhl_get_label_endorsement_type( $label, $shipment, $api_type = 'default' ) { + $type = $label->get_endorsement(); + + /** + * Filter to adjust the endorsement type for internation shipments. + * + * @param string $text The endorsement type: return or abandon. + * @param Label\Label $label The label instance. + * @param SimpleShipment $shipment The shipment instance. + * + * @since 3.0.0 + * @package Vendidero/Germanized/DHL + */ + $type = strtolower( apply_filters( 'woocommerce_gzd_dhl_label_endorsement_type', $type, $label, $shipment ) ); + + /** + * SOAP Label API was using IMMEDIATE instead of RETURN + */ + if ( 'immediate' === $type ) { + $type = 'return'; + } elseif ( 'abandonment' === $type ) { + $type = 'abandon'; + } + + if ( ! in_array( $type, array_keys( wc_gzd_dhl_get_endorsement_types() ), true ) ) { + $type = 'return'; + } + + /** + * The SOAP API uses abandonment instead of abandon and + * immediate instead of return. + */ + if ( 'default' === $api_type ) { + if ( 'abandon' === $type ) { + $type = 'abandonment'; + } else { + $type = 'immediate'; + } + } + + return strtoupper( $type ); +} + +function wc_gzd_dhl_get_return_label_customer_reference( $label, $shipment ) { + $dhl = wc_gzd_get_shipping_provider( 'dhl' ); + $default_ref = $dhl->get_formatted_label_reference( $label, 'return', 'ref_1' ); + + /** + * Filter to adjust the customer reference field placed on the DHL return label. Maximum characeter length: 30. + * + * @param string $text The customer reference text. + * @param Label\Label $label The label instance. + * @param ReturnShipment $shipment The shipment instance. + * + * @since 3.0.0 + * @package Vendidero/Germanized/DHL + */ + $ref = apply_filters( + 'woocommerce_gzd_dhl_return_label_customer_reference', + $default_ref, + $label, + $shipment + ); + + return wc_gzd_dhl_escape_reference( $ref, 30 ); +} + +function wc_gzd_dhl_escape_reference( $ref, $length = 35 ) { + /** + * Seems like DHL REST API does not properly escape those strings which leads to cryptic error messages, e.g.: + * Error: CLT103x150 is not a valid print format for shipment null. + */ + $ref = str_replace( array( '{', '}' ), '', $ref ); + + return sanitize_text_field( wc_gzd_shipments_substring( $ref, 0, $length ) ); +} + +function wc_gzd_dhl_get_inlay_return_label_reference( $label, $shipment ) { + $dhl = wc_gzd_get_shipping_provider( 'dhl' ); + $default_ref = $dhl->get_formatted_label_reference( $label, 'simple', 'inlay' ); + + /** + * Filter to adjust the inlay return reference field placed on the DHL label. Maximum characeter length: 35. + * + * @param string $text The customer reference text. + * @param Label\Label $label The label instance. + * @param SimpleShipment $shipment The shipment instance. + * + * @since 3.0.0 + * @package Vendidero/Germanized/DHL + */ + $ref = apply_filters( + 'woocommerce_gzd_dhl_inlay_return_label_reference', + $default_ref, + $label, + $shipment + ); + + return wc_gzd_dhl_escape_reference( $ref, 35 ); +} + +/** + * @return \Vendidero\Germanized\Shipments\ShippingMethod\ProviderMethod|false + */ +function wc_gzd_dhl_get_current_shipping_method() { + if ( $current = wc_gzd_get_current_shipping_method_id() ) { + return wc_gzd_get_shipping_provider_method( $current ); + } + + return false; +} + +/** + * @param $instance_id + * + * @return \Vendidero\Germanized\Shipments\ShippingMethod\ProviderMethod + */ +function wc_gzd_dhl_get_shipping_method( $instance_id ) { + return wc_gzd_get_shipping_provider_method( $instance_id ); +} + +function wc_gzd_dhl_get_deutsche_post_shipping_method( $instance_id ) { + return wc_gzd_dhl_get_shipping_method( $instance_id ); +} + +function wc_gzd_dhl_get_pickup_types() { + wc_deprecated_function( 'wc_gzd_dhl_get_pickup_types', '3.1' ); + + return array( + 'packstation' => _x( 'Packstation', 'dhl', 'woocommerce-germanized' ), + 'postoffice' => _x( 'Postfiliale', 'dhl', 'woocommerce-germanized' ), + 'parcelshop' => _x( 'Postfiliale', 'dhl', 'woocommerce-germanized' ), + ); +} + +function wc_gzd_dhl_is_pickup_type( $maybe_type, $type = 'packstation' ) { + wc_deprecated_function( 'wc_gzd_dhl_is_pickup_type', '3.1' ); + + $label = wc_gzd_dhl_get_pickup_type( $type ); + + if ( ! $label ) { + return false; + } + + $label = strtolower( trim( $label ) ); + $maybe_type = strtolower( trim( $maybe_type ) ); + + if ( strpos( $maybe_type, $label ) !== false ) { + return true; + } + + return false; +} + +function wc_gzd_dhl_get_excluded_working_days() { + $work_days = array( + 'mon', + 'tue', + 'wed', + 'thu', + 'fri', + 'sat', + ); + + $excluded = array(); + + foreach ( $work_days as $value ) { + if ( ParcelServices::is_preferred_day_excluded( $value ) ) { + $excluded[] = $value; + } + } + + return $excluded; +} + +function wc_gzd_dhl_order_has_pickup( $order ) { + wc_deprecated_function( 'wc_gzd_dhl_order_has_pickup', '3.1' ); + + return ParcelLocator::order_has_pickup( $order ); +} + +function wc_gzd_dhl_get_pickup_type( $type ) { + wc_deprecated_function( 'wc_gzd_dhl_get_pickup_type', '3.1' ); + + $types = wc_gzd_dhl_get_pickup_types(); + + if ( array_key_exists( $type, $types ) ) { + return $types[ $type ]; + } elseif ( in_array( $type, $types, true ) ) { + return $type; + } else { + return false; + } +} + +/** + * @param WP_Error $error + * + * @return bool + */ +function wc_gzd_dhl_wp_error_has_errors( $error ) { + return wc_gzd_shipment_wp_error_has_errors( $error ); +} + +function wc_gzd_dhl_is_valid_datetime( $maybe_datetime, $format = 'Y-m-d' ) { + if ( ! is_a( $maybe_datetime, 'DateTime' && ! is_numeric( $maybe_datetime ) ) ) { + if ( ! DateTime::createFromFormat( $format, $maybe_datetime ) ) { + return false; + } + } + + return true; +} + +function wc_gzd_dhl_format_label_state( $state, $country ) { + // If not USA or Australia, then change state from ISO code to name + if ( ! in_array( $country, array( 'US', 'AU' ), true ) ) { + // Get all states for a country + $states = WC()->countries->get_states( $country ); + + // If the state is empty, it was entered as free text + if ( ! empty( $states ) && ! empty( $state ) ) { + // Change the state to be the name and not the code + $state = $states[ $state ]; + + // Remove anything in parentheses (e.g. TH) + $ind = strpos( $state, ' (' ); + + if ( false !== $ind ) { + $state = substr( $state, 0, $ind ); + } + } + } + + // No need to transmit states for DE + if ( 'DE' === $country ) { + $state = ''; + } + + return $state; +} + +/** + * @param Shipment $shipment + * + * @return string + */ +function wc_gzd_dhl_get_parcel_outlet_routing_email_address( $shipment ) { + $email = $shipment->get_email(); + + if ( empty( $email ) ) { + $email = $shipment->get_sender_email(); + } + + return apply_filters( 'woocommerce_gzd_dhl_parcel_outlet_routing_email_address', $email, $shipment ); +} + +/** + * @param $the_product + * + * @return \Vendidero\Germanized\Shipments\Product + */ +function wc_gzd_dhl_get_product( $the_product ) { + return wc_gzd_shipments_get_product( $the_product ); +} + +/** + * @param Shipment $shipment + */ +function wc_gzd_dhl_get_label_shipment_address_addition( $shipment ) { + return wc_gzd_get_shipment_address_addition( $shipment ); +} + +/** + * @param Shipment $shipment + * + * @return mixed + */ +function wc_gzd_dhl_get_label_shipment_street_number( $shipment ) { + $street_number = $shipment->get_address_street_number(); + + if ( ! Package::is_shipping_domestic( $shipment->get_country(), $shipment->get_postcode() ) ) { + + if ( empty( $street_number ) ) { + /** + * Filter to adjust the placeholder used as street number for the DHL API in case + * the shipment is not domestic (inner Germnany) and a street number was not provided. + * + * @param string $placeholder The placeholder to use - default 0 as advised by DHL support. + * + * @since 3.1.0 + * @package Vendidero/Germanized/DHL + */ + $street_number = apply_filters( 'woocommerce_gzd_dhl_label_shipment_street_number_placeholder', '0' ); + } + } + + return $street_number; +} + +/** + * @param \Vendidero\Germanized\DHL\Label\ReturnLabel $label + */ +function wc_gzd_dhl_get_return_label_sender_street_number( $label ) { + $street_number = $label->get_sender_street_number(); + + if ( ! Package::is_shipping_domestic( $label->get_sender_country(), $label->get_sender_postcode() ) ) { + if ( empty( $street_number ) ) { + /** + * This filter is documented in includes/wc-gzd-dhl-core-functions.php + */ + $street_number = apply_filters( 'woocommerce_gzd_dhl_label_shipment_street_number_placeholder', '0' ); + } + } + + return $street_number; +} + +/** + * @param Label\DHL $label + * @param string $type + * + * @return mixed|string|void + * @see https://entwickler.dhl.de/group/ep/grundlagen2 + */ +function wc_gzd_dhl_get_custom_label_format( $label, $type = '' ) { + $shipment = $label->get_shipment(); + $available = $shipment ? $shipment->get_shipping_provider_instance()->get_print_formats()->filter( array( 'product_id' => $label->get_product_id() ) )->as_options() : array(); + $label_format = 'default' === $label->get_print_format() ? '' : $label->get_print_format(); + + if ( 'inlay_return' === $type ) { + $available = array_diff( $available, array( '100x70mm' ) ); + } + + /** + * This filter allows adjusting the default label format (GUI) to a custom format e.g. 910-300-700. + * The following formats are available: + * + *easily create DHL labels to your shipments.', 'dhl', 'woocommerce-germanized' ), esc_url( 'https://vendidero.de/dokument/dhl-labels-zu-sendungen-erstellen' ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>
+'EUR' ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
++ | + | + |
---|---|---|
+ | + + | ++ + | +
' . esc_html_x( 'This label has been generated by the DHL for WooCommerce Plugin and is shown for legacy purposes.', 'dhl', 'woocommerce-germanized' ) . '
'; + echo '' . esc_html_x( 'Download label', 'dhl', 'woocommerce-germanized' ) . ''; + } + } + + public static function get_legacy_label_download_url( $order_id ) { + $url = add_query_arg( + array( + 'action' => 'wc-gzd-dhl-download-legacy-label', + 'order_id' => $order_id, + 'force' => 'yes', + ), + wp_nonce_url( admin_url(), 'dhl-download-legacy-label' ) + ); + + return esc_url_raw( $url ); + } + + public static function download_legacy_label() { + if ( isset( $_GET['action'] ) && 'wc-gzd-dhl-download-legacy-label' === $_GET['action'] && isset( $_REQUEST['_wpnonce'] ) ) { + if ( isset( $_GET['order_id'] ) && wp_verify_nonce( wp_unslash( $_REQUEST['_wpnonce'] ), 'dhl-download-legacy-label' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $order_id = absint( $_GET['order_id'] ); + $args = \Vendidero\Germanized\Shipments\Labels\DownloadHandler::parse_args( + array( + 'force' => wc_string_to_bool( isset( $_GET['force'] ) ? wc_clean( wp_unslash( $_GET['force'] ) ) : false ), + ) + ); + + if ( current_user_can( 'edit_shop_orders' ) ) { + if ( $order = wc_get_order( $order_id ) ) { + $meta = (array) $order->get_meta( '_pr_shipment_dhl_label_tracking' ); + + if ( ! empty( $meta ) ) { + $path = $meta['label_path']; + + if ( file_exists( $path ) ) { + $filename = basename( $path ); + + \Vendidero\Germanized\Shipments\Labels\DownloadHandler::download( $path, $filename, $args['force'] ); + } + } + } + } + } + } + } + + public static function admin_scripts() { + global $post; + + $screen = get_current_screen(); + $screen_id = $screen ? $screen->id : ''; + $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; + + wp_register_script( 'wc-gzd-admin-dhl-internetmarke', Package::get_assets_build_url( 'static/admin-internetmarke.js' ), array( 'jquery' ), Package::get_version() ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.NotInFooter + wp_register_script( 'wc-gzd-admin-deutsche-post-label', Package::get_assets_build_url( 'static/admin-deutsche-post-label.js' ), array( 'wc-gzd-shipments-admin-shipment-modal' ), Package::get_version() ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.NotInFooter + + if ( wp_script_is( 'wc-gzd-shipments-admin-shipment-modal', 'enqueued' ) ) { + wp_enqueue_script( 'wc-gzd-admin-deutsche-post-label' ); + + wp_localize_script( + 'wc-gzd-admin-deutsche-post-label', + 'wc_gzd_admin_deutsche_post_label_params', + array( + 'refresh_label_preview_nonce' => wp_create_nonce( 'wc-gzd-dhl-refresh-deutsche-post-label-preview' ), + ) + ); + } + + // Shipping zone methods + if ( 'woocommerce_page_wc-settings' === $screen_id && isset( $_GET['provider'] ) && 'deutsche_post' === $_GET['provider'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + wp_enqueue_script( 'wc-gzd-admin-dhl-internetmarke' ); + } + } + + public static function admin_styles() { + $screen = get_current_screen(); + $screen_id = $screen ? $screen->id : ''; + $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; + + // Register admin styles. + wp_register_style( 'woocommerce_gzd_dhl_admin', Package::get_assets_build_url( 'static/admin-styles.css' ), array( 'woocommerce_admin_styles' ), Package::get_version() ); + + // Admin styles for WC pages only. + if ( in_array( $screen_id, self::get_screen_ids(), true ) ) { + wp_enqueue_style( 'woocommerce_gzd_dhl_admin' ); + } + } + + public static function get_screen_ids() { + return \Vendidero\Germanized\Shipments\Admin\Admin::get_screen_ids(); + } +} diff --git a/packages/woocommerce-germanized-dhl/src/Admin/Importer/DHL.php b/packages/woocommerce-germanized-dhl/src/Admin/Importer/DHL.php new file mode 100644 index 000000000..56d6fe33b --- /dev/null +++ b/packages/woocommerce-germanized-dhl/src/Admin/Importer/DHL.php @@ -0,0 +1,195 @@ +get_customer_number(); + } + + return ( ( ! empty( $options ) && empty( $user ) && 'yes' !== $imported && Package::base_country_is_supported() ) ? true : false ); + } + + public static function is_plugin_enabled() { + return class_exists( 'PR_DHL_WC' ) ? true : false; + } + + public static function import_settings() { + $old_settings = (array) get_option( 'woocommerce_pr_dhl_paket_settings' ); + $dhl = Package::get_dhl_shipping_provider(); + + if ( ! $dhl ) { + return false; + } + + $settings_mapping = array( + 'account_num' => 'account_number', + 'participation_V01PAK' => 'participation_V01PAK', + 'participation_V01PRIO' => 'participation_V01PRIO', + 'participation_V06PAK' => 'participation_V06PAK', + 'participation_V55PAK' => 'participation_V55PAK', + 'participation_V54EPAK' => 'participation_V54EPAK', + 'participation_V53WPAK' => 'participation_V53WPAK', + 'participation_V62WP' => 'participation_V62WP', + 'participation_V66WPI' => 'participation_V66WPI', + 'participation_return' => 'participation_return', + 'api_user' => 'api_username', + 'api_pwd' => 'api_password', + 'default_product_dom' => 'label_default_product_dom', + 'default_product_int' => 'label_default_product_int', + 'default_print_codeable' => 'label_address_codeable_only', + 'bank_holder' => 'bank_holder', + 'bank_name' => 'bank_name', + 'bank_iban' => 'bank_iban', + 'bank_bic' => 'bank_bic', + 'bank_ref' => 'bank_ref', + 'bank_ref_2' => 'bank_ref_2', + 'preferred_day' => 'PreferredDay_enable', + 'preferred_day_cost' => 'PreferredDay_cost', + 'preferred_day_cutoff' => 'PreferredDay_cutoff_time', + 'preferred_exclusion_mon' => 'PreferredDay_exclusion_mon', + 'preferred_exclusion_tue' => 'PreferredDay_exclusion_tue', + 'preferred_exclusion_wed' => 'PreferredDay_exclusion_wed', + 'preferred_exclusion_thu' => 'PreferredDay_exclusion_thu', + 'preferred_exclusion_fri' => 'PreferredDay_exclusion_fri', + 'preferred_exclusion_sat' => 'PreferredDay_exclusion_sat', + 'preferred_location' => 'PreferredLocation_enable', + 'preferred_neighbour' => 'PreferredNeighbour_enable', + 'payment_gateway' => 'preferred_payment_gateways_excluded', + 'display_packstation' => 'parcel_pickup_packstation_enable', + 'display_parcelshop' => 'parcel_pickup_parcelshop_enable', + 'display_post_office' => 'parcel_pickup_postoffice_enable', + 'parcel_limit' => 'parcel_pickup_map_max_results', + 'google_maps_api_key' => 'parcel_pickup_map_api_password', + ); + + // Bulk update settings + foreach ( $settings_mapping as $setting_old_key => $setting_new_key ) { + if ( isset( $old_settings[ 'dhl_' . $setting_old_key ] ) && ! empty( $old_settings[ 'dhl_' . $setting_old_key ] ) ) { + $dhl->update_setting( $setting_new_key, $old_settings[ 'dhl_' . $setting_old_key ] ); + } + } + + /** + * Default address update + */ + foreach ( array( 'shipper', 'return' ) as $address_type ) { + $plain_address = array( + 'company' => 'company', + 'address_city' => 'city', + 'address_zip' => 'postcode', + 'phone' => 'phone', + 'email' => 'email', + ); + + foreach ( $plain_address as $prop => $new_prop ) { + $prop_name = $address_type . '_' . $prop; + + if ( ! empty( $old_settings[ 'dhl_' . $prop_name ] ) ) { + update_option( "woocommerce_gzd_shipments_{$address_type}_address_{$new_prop}", $old_settings[ 'dhl_' . $prop_name ] ); + } + } + + if ( ! empty( $old_settings[ "dhl_{$address_type}_address" ] ) ) { + $address_1 = $old_settings[ "dhl_{$address_type}_address" ] . ' ' . ( isset( $old_settings[ "dhl_{$address_type}_address_no" ] ) ? $old_settings[ "dhl_{$address_type}_address_no" ] : '' ); + + update_option( "woocommerce_gzd_shipments_{$address_type}_address_address_1", $address_1 ); + } + + if ( ! empty( $old_settings[ "dhl_{$address_type}_name" ] ) ) { + $name = explode( ' ', $old_settings[ "dhl_{$address_type}_name" ] ); + $name_first = $name; + $first_name = implode( ' ', array_splice( $name_first, 0, ( count( $name ) - 1 ) ) ); + $last_name = $name[ count( $name ) - 1 ]; + + update_option( "woocommerce_gzd_shipments_{$address_type}_address_first_name", $first_name ); + update_option( "woocommerce_gzd_shipments_{$address_type}_address_last_name", $last_name ); + } + } + + // Enable maps if API key exists + if ( isset( $settings['dhl_google_maps_api_key'] ) && ! empty( $settings['dhl_google_maps_api_key'] ) ) { + $dhl->update_setting( 'parcel_pickup_map_enable', 'yes' ); + } + + // Shipper state to country ISO mapping + $countries = WC()->countries; + $shipper_country = ( isset( $old_settings['dhl_shipper_address_state'] ) && ! empty( $old_settings['dhl_shipper_address_state'] ) ) ? $old_settings['dhl_shipper_address_state'] : ''; + $return_country = ( isset( $old_settings['dhl_return_address_state'] ) && ! empty( $old_settings['dhl_return_address_state'] ) ) ? $old_settings['dhl_return_address_state'] : ''; + $isos = ( $countries ) ? $countries->get_countries() : array(); + + if ( ! empty( $shipper_country ) && ! empty( $isos ) ) { + if ( ( $key = array_search( $shipper_country, $isos, true ) ) !== false ) { + update_option( 'woocommerce_gzd_shipments_shipper_address_country', $key ); + } + } + + if ( ! empty( $return_country ) && ! empty( $isos ) ) { + if ( ( $key = array_search( $return_country, $isos, true ) ) !== false ) { + update_option( 'woocommerce_gzd_shipments_return_address_country', $key ); + } + } + + $dhl->save(); + + return true; + } + + public static function import_order_data( $limit = 10, $offset = 0 ) { + + $orders = wc_get_orders( + array( + 'limit' => $limit, + 'offset' => $offset, + 'orderby' => 'date', + 'order' => 'DESC', + 'type' => 'shop_order', + ) + ); + + if ( ! empty( $orders ) ) { + foreach ( $orders as $order ) { + + if ( ! $order->get_meta( '_shipping_address_type' ) ) { + + // Update order pickup type from official DHL plugin + if ( self::order_has_pickup( $order ) ) { + + $order->update_meta_data( '_shipping_address_type', 'dhl' ); + $order->update_meta_data( '_shipping_dhl_postnumber', $order->get_meta( '_shipping_dhl_postnum' ) ); + + // Remove data to make sure we do not show data twice + $order->delete_meta_data( '_shipping_dhl_address_type' ); + $order->delete_meta_data( '_shipping_dhl_postnum' ); + + $order->save(); + } + } + } + } + } + + protected static function order_has_pickup( $order ) { + $pos_ps = stripos( $order->get_shipping_address_1(), 'Packstation' ); + $pos_fl = stripos( $order->get_shipping_address_1(), 'Postfiliale' ); + + if ( false !== $pos_ps || false !== $pos_fl ) { + return true; + } + + return false; + } +} diff --git a/packages/woocommerce-germanized-dhl/src/Admin/Importer/Internetmarke.php b/packages/woocommerce-germanized-dhl/src/Admin/Importer/Internetmarke.php new file mode 100644 index 000000000..26abfb1de --- /dev/null +++ b/packages/woocommerce-germanized-dhl/src/Admin/Importer/Internetmarke.php @@ -0,0 +1,54 @@ +get_api_username(); + } + + return ( ( ! empty( $options ) && empty( $user ) && 'yes' !== $imported && Package::base_country_is_supported() ) ? true : false ); + } + + public static function is_plugin_enabled() { + return defined( 'WCDPI_PLUGIN_FILE' ) ? true : false; + } + + public static function import_settings() { + $old_settings = array_merge( (array) get_option( '_wcdpi_settings_portokasse' ), (array) get_option( '_wcdpi_settings_internetmarke_1c4a' ) ); + + $settings_mapping = array( + '_wcdpi_portokasse_email' => 'api_username', + '_wcdpi_portokasse_password' => 'api_password', + ); + + $deutsche_post = Package::get_deutsche_post_shipping_provider(); + + if ( ! $deutsche_post ) { + return false; + } + + // Bulk update settings + foreach ( $settings_mapping as $setting_old_key => $setting_new_key ) { + if ( isset( $old_settings[ $setting_old_key ] ) ) { + $deutsche_post->update_setting( $setting_new_key, $old_settings[ $setting_old_key ] ); + } + } + + $deutsche_post->save(); + return true; + } +} diff --git a/packages/woocommerce-germanized-dhl/src/Admin/Status.php b/packages/woocommerce-germanized-dhl/src/Admin/Status.php new file mode 100644 index 000000000..953d07589 --- /dev/null +++ b/packages/woocommerce-germanized-dhl/src/Admin/Status.php @@ -0,0 +1,118 @@ + ++ + | +||
---|---|---|
|
+ + '; + } else { + echo ' ' . esc_html_x( 'Unable to connect to the URL. Please make sure that your webhost allows outgoing connections to that specific URL.', 'dhl', 'woocommerce-germanized' ) . ''; + } + ?> + | +
+ + | +||
---|---|---|
: | ++ | ' : '–' ); ?> | +
' . esc_html( $ref_placeholders_str ) . '
' ) . '' . esc_html( $ref_placeholders_str ) . '
' ) . '' . esc_html_x( 'Insert your DHL business customer number (EKP) here. If you are not yet a business customer you might want to create a new account first.', 'dhl', 'woocommerce-germanized' ) . '
', + 'position' => array( + 'edge' => 'left', + 'align' => 'left', + ), + ), + ), + 'api' => array( + 'target' => Package::is_debug_mode() ? '#api_sandbox_username' : '#api_username', + 'next' => '', + 'next_url' => add_query_arg( array( 'tutorial' => 'yes' ), $this->get_edit_link( 'config_set_simple_label' ) ), + 'next_trigger' => array(), + 'options' => array( + 'content' => '' . esc_html_x( 'To create labels and embed DHL services, our software needs access to the API. You will need to fill out the username and password fields accordingly.', 'dhl', 'woocommerce-germanized' ) . '
', + 'position' => array( + 'edge' => 'left', + 'align' => 'left', + ), + ), + ), + ), + ); + } elseif ( 'config_set_simple_label' === $section ) { + $pointers = array( + 'pointers' => array( + 'zones' => array( + 'target' => '#select2-label_config_set_-p-dhl-s-simple-z-dom-g-product-n-product-container', + 'next' => '', + 'next_url' => $this->get_edit_link( 'automation' ), + 'next_trigger' => array(), + 'options' => array( + 'content' => '' . esc_html_x( 'Configure separate service(s) based on your customer\'s location.', 'dhl', 'woocommerce-germanized' ) . '
', + 'position' => array( + 'edge' => 'left', + 'align' => 'left', + ), + ), + ), + ), + ); + } elseif ( 'automation' === $section ) { + $pointers = array( + 'pointers' => array( + 'auto' => array( + 'target' => '#label_auto_enable-toggle', + 'next' => '', + 'next_url' => add_query_arg( array( 'tutorial' => 'yes' ), $this->get_edit_link( 'pickup_locations' ) ), + 'next_trigger' => array(), + 'options' => array( + 'content' => '' . esc_html_x( 'You might want to save some time and generate labels automatically as soon as a shipment switches to a certain status.', 'dhl', 'woocommerce-germanized' ) . '
', + 'position' => array( + 'edge' => 'left', + 'align' => 'left', + ), + ), + ), + ), + ); + } elseif ( 'pickup_locations' === $section ) { + $next_url = Tutorial::get_tutorial_url( 'packaging' ); + + if ( $tab = Settings::get_tab( 'shipping_provider' ) ) { + $next_url = $tab->get_next_pointers_link( $this->get_name() ); + } + + $pointers = array( + 'pointers' => array( + 'day' => array( + 'target' => '#parcel_pickup_packstation_enable-toggle', + 'next' => '', + 'next_url' => $next_url, + 'next_trigger' => array(), + 'options' => array( + 'content' => '' . esc_html_x( 'Allow your customers to ship to a packstation (and/or other DHL location types as configured below).', 'dhl', 'woocommerce-germanized' ) . '
', + 'position' => array( + 'edge' => 'left', + 'align' => 'left', + ), + ), + ), + ), + ); + } + + return $pointers; + } + + public function get_supported_label_config_set_shipment_types() { + return array( 'simple' ); + } +} diff --git a/packages/woocommerce-germanized-dhl/src/ShippingProvider/DeutschePost.php b/packages/woocommerce-germanized-dhl/src/ShippingProvider/DeutschePost.php new file mode 100644 index 000000000..37f9c453c --- /dev/null +++ b/packages/woocommerce-germanized-dhl/src/ShippingProvider/DeutschePost.php @@ -0,0 +1,691 @@ +get_shipping_country() ) { + return false; + } + + return parent::supports_customer_returns( $order ); + } + + public function supports_labels( $label_type, $shipment = false ) { + $label_types = array( 'simple', 'return' ); + + /** + * Return labels are only supported for DE + */ + if ( 'return' === $label_type && $shipment && 'return' === $shipment->get_type() && 'DE' !== $shipment->get_sender_country() ) { + return false; + } + + return in_array( $label_type, $label_types, true ); + } + + public function get_title( $context = 'view' ) { + return _x( 'Deutsche Post', 'dhl', 'woocommerce-germanized' ); + } + + public function get_name( $context = 'view' ) { + return 'deutsche_post'; + } + + public function get_description( $context = 'view' ) { + return _x( 'Integration for products of the Deutsche Post through Internetmarke.', 'dhl', 'woocommerce-germanized' ); + } + + public function get_default_tracking_url_placeholder() { + return 'https://deutschepost.de/de/s/sendungsverfolgung.html?piececode={tracking_id}'; + } + + public function get_api_username( $context = 'view' ) { + return $this->get_meta( 'api_username', true, $context ); + } + + public function set_api_username( $username ) { + $this->update_meta_data( 'api_username', strtolower( $username ) ); + } + + protected function get_available_base_countries() { + return Package::get_available_countries(); + } + + protected function get_connection_status_html( $maybe_error ) { + return '' . ( sprintf( _x( 'Status: %1$s', 'dhl', 'woocommerce-germanized' ), ( is_wp_error( $maybe_error ) ? $maybe_error->get_error_message() : _x( 'Connected', 'dhl', 'woocommerce-germanized' ) ) ) ) . ''; + } + + protected function get_printing_settings() { + $settings = parent::get_printing_settings(); + $settings_url = $this->get_edit_link( '' ); + + $settings = array_merge( + array( + array( + 'title' => _x( 'Printing', 'dhl', 'woocommerce-germanized' ), + 'type' => 'title', + 'id' => 'shipping_provider_label_printing_options', + 'desc' => '
+ composer install',
+ '' . esc_html( str_replace( ABSPATH, '', __DIR__ ) ) . '
'
+ );
+ ?>
+
{_x('Sorry, we did not find any pickup locations nearby.', 'shipments', 'woocommerce-germanized')}
+ )} +' + message + '
' + message + '
' + data.message + '
1&&(n-=1),n<1/6?e+6*(t-e)*n:n<.5?t:n<2/3?e+(t-e)*(2/3-n)*6:e}if(e=Mt(e,360),t=Mt(t,100),n=Mt(n,100),0===t)r=o=i=n;else{var s=n<.5?n*(1+t):n+t-n*t,l=2*n-s;r=a(l,s,e+1/3),o=a(l,s,e),i=a(l,s,e-1/3)}return{r:255*r,g:255*o,b:255*i}}(e.h,a,l),c=!0,u="hsl"),e.hasOwnProperty("a")&&(i=e.a)),i=At(i),{ok:c,format:e.format||u,r:Math.min(255,Math.max(o.r,0)),g:Math.min(255,Math.max(o.g,0)),b:Math.min(255,Math.max(o.b,0)),a:i}}(e);this._originalInput=e,this._r=n.r,this._g=n.g,this._b=n.b,this._a=n.a,this._roundA=Math.round(100*this._a)/100,this._format=t.format||n.format,this._gradientType=t.gradientType,this._r<1&&(this._r=Math.round(this._r)),this._g<1&&(this._g=Math.round(this._g)),this._b<1&&(this._b=Math.round(this._b)),this._ok=n.ok}function ct(e,t,n){e=Mt(e,255),t=Mt(t,255),n=Mt(n,255);var r,o,i=Math.max(e,t,n),a=Math.min(e,t,n),s=(i+a)/2;if(i==a)r=o=0;else{var l=i-a;switch(o=s>.5?l/(2-i-a):l/(i+a),i){case e:r=(t-n)/l+(t '+e+' '+e+' '+n.message+"
+
+
+
+
+
+
+ has_packaging() ? 'disabled="disabled"' : '' ); ?> size="6" class="wc_input_decimal wc-gzd-shipment-dimension has_packaging() ? 'disabled' : '' ); ?>" value="get_length( 'edit' ) ) ); ?>" name="shipment_length[get_id() ); ?>]" id="shipment-length-get_id() ); ?>" placeholder="get_content_length() ) ); ?>" />
+ has_packaging() ? 'disabled="disabled"' : '' ); ?> size="6" class="wc_input_decimal wc-gzd-shipment-dimension has_packaging() ? 'disabled' : '' ); ?>" value="get_width( 'edit' ) ) ); ?>" name="shipment_width[get_id() ); ?>]" id="shipment-width-get_id() ); ?>" placeholder="get_content_width() ) ); ?>" />
+ has_packaging() ? 'disabled="disabled"' : '' ); ?> size="6" class="wc_input_decimal wc-gzd-shipment-dimension has_packaging() ? 'disabled' : '' ); ?>" value="get_height( 'edit' ) ) ); ?>" name="shipment_height[get_id() ); ?>]" id="shipment-height-get_id() ); ?>" placeholder="get_content_height() ) ); ?>" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_type() ) ), esc_html( $position_number ), esc_html( $total_shipments ) ); ?>
+ get_order() ) && is_callable( array( $s_order, 'get_edit_order_url' ) ) ) : ?>
+ get_order_number() ) ); ?>
+
+ get_order_number() ) ); ?>
+
+
+
+
+ has_tracking() && ( $tracking_url = $shipment->get_tracking_url() ) ) : ?>
+
+
+
+
+
+ get_packaging() ) : ?>
+ get_description() ); ?> get_description() ); ?> ' . sprintf( esc_html_x( 'The following products cannot be returned due to their nature: %1$s', 'shipments', 'woocommerce-germanized' ), esc_html( implode( ', ', $product_names ) ) ) . '
+
+
+
+
+
+
+
+ wp-content/uploads/' . esc_html( $dirname ) . '' ); ?> ' . sprintf( esc_html_x( 'Labels partially generated. %s', 'shipments', 'woocommerce-germanized' ), $download_button ) . ' get_success_message() ); ?> ';
+
+ /**
+ * Action that fires before table actions are outputted for a Shipment.
+ *
+ * The dynamic portion of this hook, `$this->get_hook_prefix()` is used to construct a
+ * unique hook for a shipment type e.g. return. In case of simple shipments the type is omitted.
+ *
+ * Example hook name: woocommerce_gzd_return_shipments_table_actions_start
+ *
+ * @param Shipment $shipment The shipment object.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ do_action( "{$this->get_hook_prefix()}actions_start", $shipment );
+
+ $actions = array();
+
+ if ( $shipment->has_status( array( 'draft' ) ) ) {
+ $actions['processing'] = array(
+ 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_gzd_update_shipment_status&status=processing&shipment_id=' . $shipment->get_id() ), 'update-shipment-status' ),
+ 'name' => _x( 'Processing', 'shipments', 'woocommerce-germanized' ),
+ 'action' => 'processing',
+ );
+ }
+
+ if ( $shipment->has_status( array( 'draft', 'processing' ) ) ) {
+ $actions['shipped'] = array(
+ 'url' => wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_gzd_update_shipment_status&status=shipped&shipment_id=' . $shipment->get_id() ), 'update-shipment-status' ),
+ 'name' => _x( 'Shipped', 'shipments', 'woocommerce-germanized' ),
+ 'action' => 'shipped',
+ );
+ }
+
+ if ( $shipment->supports_label() ) {
+ if ( $label = $shipment->get_label() ) {
+ $actions['download_label'] = array(
+ 'url' => $label->get_download_url(),
+ 'name' => _x( 'Download label', 'shipments', 'woocommerce-germanized' ),
+ 'action' => 'download-label download',
+ 'target' => '_blank',
+ );
+ } elseif ( $shipment->needs_label() ) {
+ $actions['generate_label'] = array(
+ 'url' => '#',
+ 'name' => _x( 'Generate label', 'shipments', 'woocommerce-germanized' ),
+ 'action' => 'generate-label has-shipment-modal generate',
+ 'custom_attributes' => array(
+ 'id' => 'wc-gzd-create-label-' . $shipment->get_id(),
+ 'data-id' => 'wc-gzd-modal-create-shipment-label',
+ 'data-load-async' => true,
+ 'data-reference' => $shipment->get_id(),
+ 'data-nonce-params' => 'wc_gzd_shipments_admin_shipments_table_params',
+ ),
+ );
+
+ include Package::get_path() . '/includes/admin/views/label/html-shipment-label-backbone.php';
+ }
+ }
+
+ $actions = $this->get_custom_actions( $shipment, $actions );
+
+ /**
+ * Filters the actions available for Shipments table list column.
+ *
+ * The dynamic portion of this hook, `$this->get_hook_prefix()` is used to construct a
+ * unique hook for a shipment type e.g. return. In case of simple shipments the type is omitted.
+ *
+ * Example hook name: woocommerce_gzd_return_shipments_table_actions
+ *
+ * @param array $actions The registered Shipment actions.
+ * @param Shipment $shipment The shipment object.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ $actions = apply_filters( "{$this->get_hook_prefix()}actions", $actions, $shipment );
+
+ echo wc_gzd_render_shipment_action_buttons( $actions ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+
+ /**
+ * Action that fires after table actions are outputted for a Shipment.
+ *
+ * The dynamic portion of this hook, `$this->get_hook_prefix()` is used to construct a
+ * unique hook for a shipment type e.g. return. In case of simple shipments the type is omitted.
+ *
+ * Example hook name: woocommerce_gzd_return_shipments_table_actions_end
+ *
+ * @param Shipment $shipment The shipment object.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ do_action( "{$this->get_hook_prefix()}actions_end", $shipment );
+
+ echo ' ' . esc_html_x( 'By enabling this option customers receive an email notification as soon as a shipment is marked as shipped.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'Decide whether you want to automatically create shipments to orders reaching a specific status. You can always adjust your shipments by manually editing the shipment within the edit order screen.', 'shipments', 'woocommerce-germanized' ) . ' ' . sprintf( _x( 'Minimize manual work while handling customer returns. Learn more about returns within our %s.', 'shipments', 'woocommerce-germanized' ), '' . _x( 'documentation', 'shipments', 'woocommerce-germanized' ) . '' ) . ' ' . _x( 'Make sure to keep your business information up-to-date as the data will be used within labels and returns.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'Configure additional packaging settings such as shipping class restrictions or inner dimensions.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'Add all your available packaging options to make sure the packing algorithm knows about it.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'You may create yearly packaging reports, e.g. for recycling purposes.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'The packing algorithm will determine which packaging option to use and may split an order into multiple shipments automatically.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'You may find all the available shipping providers as a list here. Click on the link to edit the provider-specific settings.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'Activate or deactivate a shipping provider by toggling this button.', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html_x( 'You may want to manually add a new shipping provider in case a built-in integration is not available.', 'shipments', 'woocommerce-germanized' ) . ' ' . wp_kses_post( $desc ) . ' ' . wp_kses_post( $this->get_description() ) . ' get_description() ); ?> ' . esc_html_x( 'Click on a tab to adjust the settings related to a specific section.', 'shipments', 'woocommerce-germanized' ) . '
+
+
+ get_date_start()->date_i18n( wc_date_format() ) ); ?> – get_date_end()->date_i18n( wc_date_format() ) ); ?>: get_total_weight(), wc_gzd_get_packaging_weight_unit() ) ); ?> (get_total_count() ) ); ?>) get_total_packaging_weight_by_country( $country ), wc_gzd_get_packaging_weight_unit() ) ); ?> (get_total_packaging_count_by_country( $country ) ) ); ?>) Find pending actions', 'shipments', 'woocommerce-germanized' ), esc_html( $details['shipment_count'] ), ( $details['next_date'] ? esc_html( $details['next_date']->date_i18n( wc_date_format() . ' @ ' . wc_time_format() ) ) : esc_html_x( 'Not yet known', 'shipments', 'woocommerce-germanized' ) ), esc_url( $details['link'] ) ) ); ?>
+
+ get_label() : '' ); ?>
+ ' . " ', '
+
+ get_formatted_sender_full_name() ) ); ?> get_billing_first_name() ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch ?>
+ get_order_number() ) ); ?>
+ get_billing_first_name() ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch ?>
+
+ get_billing_first_name() ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch ?>
+
+ get_billing_first_name() ) ); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch ?>
+
+ get_est_delivery_date(), wc_date_format() ) ); ?> get_tracking_instruction() ); ?> get_return_instructions() ) ) . PHP_EOL ); ?> get_est_delivery_date(), wc_date_format() ) ); ?> get_tracking_instruction() ); ?>
+ ' . $shipment->get_shipment_number() . '', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ '' . wc_format_datetime( $shipment->get_date_created() ) . '', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ '' . wc_gzd_get_shipment_status_name( $shipment->get_status() ) . '' // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ );
+ ?>
+ get_phone() ); ?> get_email() ); ?> get_tracking_instruction() ); ?>
+ composer install',
+ '0&&(a(this).is(":checked")?m.show():m.hide(),a(t.body).trigger("wc_gzd_shipments_admin_shipment_modal_show_if",[o]),o.$modalTrigger.trigger("wc_gzd_shipments_admin_shipment_modal_show_if",[o]))}else n.find(":input[data-show-if-"+i+"]").parents(".form-field").hide(),a(this).is(":visible")&&(a(this).is(":checkbox")?a(this).is(":checked")&&n.find(":input[data-show-if-"+i+"]").parents(".form-field").show():("0"!==d&&""!==d&&n.find(":input[data-show-if-"+i+'=""]').parents(".form-field").show(),n.find(":input[data-show-if-"+i+'*="'+d+'"]').parents(".form-field").show())),n.find(":input[data-show-if-"+i+"]").trigger("change"),n.find(".show-more-wrapper").each((function(){var e=a(this),o="none"!==e.find("p.form-field").css("display"),t=!!e.data("trigger")&&n.find(e.data("trigger"));t.length>0&&(o?t.show():t.hide())}))},i.prototype.onClose=function(e,a){var o=e.data.adminShipmentModal;-1!==a.indexOf(o.modalId)&&o.$modal&&o.$modal.length>0&&o.$modal.off("click.gzd-modal-"+o.modalId)},i.prototype.onOpen=function(e,o){var n=e.data.adminShipmentModal;-1!==o.indexOf(n.modalId)&&(n.setup(),n.$modal=a("."+n.modalClass),n.$modal.data("self",n),n.loadAsync?(params={action:n.getAction("load"),reference_id:n.referenceId,security:n.getNonce("load")},n.doAjax(params,n.onLoadSuccess)):n.initData(),a(t.body).trigger("wc_gzd_shipments_admin_shipment_modal_open",[n]),n.$modalTrigger.trigger("wc_gzd_shipments_admin_shipment_modal_open",[n]))},i.prototype.onLoadSuccess=function(e,o){o.initData(),a(t.body).trigger("wc_gzd_shipments_admin_shipment_modal_after_load_success",[e,o]),o.$modalTrigger.trigger("wc_gzd_shipments_admin_shipment_modal_after_load_success",[e,o])},i.prototype.onAjaxSuccess=function(e,a){},i.prototype.onAjaxError=function(e,a){},i.prototype.getModalMainContent=function(){return this.$modal.find("article")},i.prototype.doAjax=function(e,o,i){var d=this,r=d.getModalMainContent();o=o||d.onAjaxSuccess,i=i||d.onAjaxError,e.hasOwnProperty("reference_id")||(e.reference_id=d.referenceId),d.$modal.find(".wc-backbone-modal-content").block({message:null,overlayCSS:{background:"#fff",opacity:.6}}),d.$modal.find(".notice-wrapper").empty(),a.ajax({type:"POST",url:d.params.ajax_url,data:e,success:function(e){e.success?(e.fragments&&a.each(e.fragments,(function(e,o){a(e).replaceWith(o)})),d.$modal.find("#btn-ok").prop("disabled",!1),d.$modal.find(".wc-backbone-modal-content").unblock(),o.apply(d,[e,d]),n.admin.shipments&&n.admin.shipments.refresh(e),a(t.body).trigger("wc_gzd_shipments_admin_shipment_modal_ajax_success",[e,d]),d.$modalTrigger.trigger("wc_gzd_shipments_admin_shipment_modal_ajax_success",[e,d]),e.fragments&&d.afterRefresh()):(d.$modal.find("#btn-ok").prop("disabled",!1),d.$modal.find(".wc-backbone-modal-content").unblock(),i.apply(d,[e,d]),d.printNotices(r,e),r.animate({scrollTop:0},500),a(t.body).trigger("wc_gzd_shipments_admin_shipment_modal_ajax_error",[e,d]),d.$modalTrigger.trigger("wc_gzd_shipments_admin_shipment_modal_ajax_error",[e,d]))},error:function(e){},dataType:"json"})},i.prototype.afterRefresh=function(){0===this.$modal.find(".notice-wrapper").length&&this.getModalMainContent().prepend(''),a(t.body).trigger("wc-enhanced-select-init"),a(t.body).trigger("wc-init-datepickers"),a(t.body).trigger("init_tooltips")},i.prototype.initData=function(){var e=this;e.$modal=a("."+e.modalClass),e.$modal.data("self",e),e.afterRefresh(),e.$modal.on("click.gzd-modal-"+e.modalId,"#btn-ok",{adminShipmentModal:e},e.onSubmit),e.$modal.on("touchstart.gzd-modal-"+e.modalId,"#btn-ok",{adminShipmentModal:e},e.onSubmit),e.$modal.on("keydown.gzd-modal-"+e.modalId,{adminShipmentModal:e},e.onKeyDown),e.$modal.on("click.gzd-modal-"+e.modalId,".notice .notice-dismiss",{adminShipmentModal:e},e.onRemoveNotice),e.$modal.on("change.gzd-modal-"+e.modalId,":input[id]",{adminShipmentModal:e},e.onChangeField),e.$modal.on("click.gzd-modal-"+e.modalId,".show-more",{adminShipmentModal:e},e.onExpandMore),e.$modal.on("click.gzd-modal-"+e.modalId,".show-fewer",{adminShipmentModal:e},e.onHideMore),a(t.body).trigger("wc_gzd_shipments_admin_shipment_modal_after_init_data",[e]),e.$modalTrigger.trigger("wc_gzd_shipments_admin_shipment_modal_after_init_data",[e]),e.$modal.find(":input:visible").trigger("change",[e])},i.prototype.printNotices=function(e,o){var t=this;o.hasOwnProperty("message")?t.addNotice(o.message,"error",e):o.hasOwnProperty("messages")&&a.each(o.messages,(function(o,n){"string"==typeof n||n instanceof String?t.addNotice(n,"error",e):a.each(n,(function(a,n){t.addNotice(n,"soft"===o?"warning":o,e)}))}))},i.prototype.onSubmitSuccess=function(e,o){var i=o.getModalMainContent();e.hasOwnProperty("messages")&&(e.messages.hasOwnProperty("error")||e.messages.hasOwnProperty("soft"))?(o.printNotices(i,e),o.$modal.find("footer").find("#btn-ok").addClass("modal-close").attr("id","btn-close").text(o.params.i18n_modal_close)):o.$modal.find(".modal-close").trigger("click"),e.hasOwnProperty("shipment_id")&&a("div#shipment-"+e.shipment_id).length>0&&n.admin.shipments.initShipment(e.shipment_id),a(t.body).trigger("wc_gzd_shipments_admin_shipment_modal_after_submit_success",[e,o]),o.$modalTrigger.trigger("wc_gzd_shipments_admin_shipment_modal_after_submit_success",[e,o])},i.prototype.getCleanId=function(e=!1){var a=this.modalClass.split("-").join("_").replace("_modal_","_");return e&&(a=a.replace("wc_gzd_","").replace("wc_gzdp_","")),a},i.prototype.getNonceParams=function(){return o.hasOwnProperty(this.nonceParams)?o[this.nonceParams]:{}},i.prototype.getNonce=function(e){var a=this.getCleanId(!0)+"_"+e+"_nonce",o=this.getNonceParams();return o.hasOwnProperty(a)?o[a]:this.params[e+"_nonce"]},i.prototype.getAction=function(e){return this.getCleanId().replace("wc_","woocommerce_")+"_"+e},i.prototype.onKeyDown=function(a){var o=a.data.adminShipmentModal;13!==(a.keyCode||a.which)||a.target.tagName&&("input"===a.target.tagName.toLowerCase()||"textarea"===a.target.tagName.toLowerCase())||o.onSubmit.apply(o.$modal.find("button#btn-ok"),[e])},i.prototype.getFormData=function(e){var o={};return e.find(".show-more-wrapper").each((function(){a(this).is(":visible")||a(this).addClass("show-more-wrapper-force-show").show()})),a.each(e.find(":input").serializeArray(),(function(t,n){var i=e.find(':input[name="'+n.name+'"]');if(i&&!i.is(":visible")&&"hidden"!==i.attr("type"))return!0;-1!==n.name.indexOf("[]")?(n.name=n.name.replace("[]",""),o[n.name]=a.makeArray(o[n.name]),o[n.name].push(n.value)):o[n.name]=n.value})),e.find(".show-more-wrapper-force-show").each((function(){a(this).removeClass("show-more-wrapper-force-show").hide()})),o},i.prototype.onSubmit=function(e){var a=e.data.adminShipmentModal,o=a.getModalMainContent().find("form"),t=a.getFormData(o),n=a.$modal.find("#btn-ok");n.length>0&&n.prop("disabled",!0),t.security=a.getNonce("submit"),t.reference_id=a.referenceId,t.action=a.getAction("submit"),a.doAjax(t,a.onSubmitSuccess),e.preventDefault(),e.stopPropagation()},i.prototype.addNotice=function(e,a,o){o.find(".notice-wrapper").append('
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/includes/admin/views/html-order-add-shipment-item.php b/packages/woocommerce-germanized-shipments/includes/admin/views/html-order-add-shipment-item.php
new file mode 100644
index 000000000..f344de423
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/admin/views/html-order-add-shipment-item.php
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/includes/admin/views/html-order-shipment-content.php b/packages/woocommerce-germanized-shipments/includes/admin/views/html-order-shipment-content.php
new file mode 100644
index 000000000..2543e6264
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/admin/views/html-order-shipment-content.php
@@ -0,0 +1,293 @@
+
+
+
+
+
+
+
+
+get_type() ) ), esc_html( $shipment->get_shipment_number() ) ); ?>
+ get_status() ) ); ?>
+
+
+
+ get_formatted_dimensions() ); ?>, get_total_weight(), $shipment->get_weight_unit() ) ); ?>
+
+ _x( 'Position', 'shipments', 'woocommerce-germanized' ),
+ 'quantity' => _x( 'Quantity', 'shipments', 'woocommerce-germanized' ),
+ 'dimensions' => _x( 'Dimensions', 'shipments', 'woocommerce-germanized' ),
+ 'weight' => _x( 'Weight', 'shipments', 'woocommerce-germanized' ),
+ ),
+ $shipment->get_type()
+ );
+ ?>
+
+
+
+ $column_title ) : ?>
+
+
+
+ get_items() as $item ) : ?>
+
+
+
+ $column_title ) : ?>
+
+
+
+
+
+ get_product() ) : ?>
+ get_name() ); ?>
+
+ get_name() ); ?>
+
+
+ get_sku() ? '
+
+
' . esc_html_x( 'SKU:', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html( $item->get_sku() ) . '' : '' ); ?>
+
+ get_quantity() ); ?>x
+
+ get_dimensions(), $shipment->get_dimension_unit() ) ); ?>
+
+ get_weight(), $shipment->get_weight_unit() ) ); ?>
+
+
+
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/includes/admin/views/label/html-shipment-label-backbone-error.php b/packages/woocommerce-germanized-shipments/includes/admin/views/label/html-shipment-label-backbone-error.php
new file mode 100644
index 000000000..c61a6ff83
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/admin/views/label/html-shipment-label-backbone-error.php
@@ -0,0 +1,25 @@
+
+
+
+
+
+ $provider ) : ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ get_title() ); ?>
+
+
+
+
+
+ is_pro() ) : ?>
+
+
+
+
+
+ is_activated() ? esc_attr_x( 'Yes', 'shipments', 'woocommerce-germanized' ) : esc_attr_x( 'No', 'shipments', 'woocommerce-germanized' ) ); ?>
+
+
+
+
+
+
+
+ get_help_link() ) : ?>
+
+
+ get_edit_link() ) : ?>
+
+
+
+
+ get_shipping_provider() ) ) ); ?> has_label() && $shipment->get_tracking_id() ) ? wp_kses_post( Admin::get_shipment_tracking_html( $shipment ) ) : '' ); ?>
+
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-guest-return-shipment-request.php b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-guest-return-shipment-request.php
new file mode 100644
index 000000000..400f0a6dd
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-guest-return-shipment-request.php
@@ -0,0 +1,214 @@
+customer_email = true;
+ $this->id = 'customer_guest_return_shipment_request';
+ $this->title = _x( 'Order guest return request', 'shipments', 'woocommerce-germanized' );
+ $this->description = _x( 'Order guest return request are sent to the customer after submitting a new return request as a guest.', 'shipments', 'woocommerce-germanized' );
+
+ $this->template_html = 'emails/customer-guest-return-shipment-request.php';
+ $this->template_plain = 'emails/plain/customer-guest-return-shipment-request.php';
+ $this->template_base = Package::get_path() . '/templates/';
+ $this->helper = function_exists( 'wc_gzd_get_email_helper' ) ? wc_gzd_get_email_helper( $this ) : false;
+
+ $this->placeholders = array(
+ '{site_title}' => $this->get_blogname(),
+ '{order_number}' => '',
+ '{order_date}' => '',
+ );
+
+ // Call parent constructor.
+ parent::__construct();
+ }
+
+ /**
+ * Get email subject.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_subject() {
+ return _x( 'Your return request to your order {order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Get email heading.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_heading() {
+ return _x( 'Return request to your order: {order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Switch Woo and Germanized locale
+ */
+ public function setup_locale() {
+
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_switch_to_site_locale' ) && apply_filters( 'woocommerce_email_setup_locale', true ) ) {
+ wc_gzd_switch_to_site_locale();
+ }
+
+ parent::setup_locale();
+ }
+
+ /**
+ * Restore Woo and Germanized locale
+ */
+ public function restore_locale() {
+
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_restore_locale' ) && apply_filters( 'woocommerce_email_restore_locale', true ) ) {
+ wc_gzd_restore_locale();
+ }
+
+ parent::restore_locale();
+ }
+
+ /**
+ * Trigger.
+ *
+ * @param int $order_id order ID.
+ */
+ public function trigger( $order_id ) {
+ if ( $this->helper ) {
+ $this->helper->setup_locale();
+ } else {
+ $this->setup_locale();
+ }
+
+ if ( $this->object = wc_get_order( $order_id ) ) {
+
+ $this->placeholders['{order_number}'] = $this->object->get_order_number();
+
+ if ( ( $order_shipment = wc_gzd_get_shipment_order( $this->object ) ) && ( $this->request_url = wc_gzd_get_order_customer_add_return_url( $this->object ) ) ) {
+ $this->recipient = $this->object->get_billing_email();
+ $this->placeholders['{order_date}'] = wc_format_datetime( $this->object->get_date_created() );
+ $this->placeholders['{order_number}'] = $this->object->get_order_number();
+ }
+ }
+
+ if ( $this->helper ) {
+ $this->helper->setup_email_locale();
+ }
+
+ if ( $this->is_enabled() && $this->get_recipient() ) {
+ $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_email_locale();
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_locale();
+ } else {
+ $this->restore_locale();
+ }
+ }
+
+ /**
+ * Return content from the additional_content field.
+ *
+ * Displayed above the footer.
+ *
+ * @since 2.0.4
+ * @return string
+ */
+ public function get_additional_content() {
+ if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) {
+ return parent::get_additional_content();
+ }
+
+ return '';
+ }
+
+ /**
+ * Get content html.
+ *
+ * @return string
+ */
+ public function get_content_html() {
+ return wc_get_template_html(
+ $this->template_html,
+ array(
+ 'order' => $this->object,
+ 'add_return_request_url' => $this->request_url,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => false,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Get content plain.
+ *
+ * @return string
+ */
+ public function get_content_plain() {
+ return wc_get_template_html(
+ $this->template_plain,
+ array(
+ 'order' => $this->object,
+ 'add_return_request_url' => $this->request_url,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => true,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Default content to show below main email content.
+ *
+ * @since 1.0.1
+ * @return string
+ */
+ public function get_default_additional_content() {
+ return '';
+ }
+ }
+
+endif;
+
+return new WC_GZD_Email_Customer_Guest_Return_Shipment_Request();
diff --git a/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-return-shipment-delivered.php b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-return-shipment-delivered.php
new file mode 100644
index 000000000..8e8260051
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-return-shipment-delivered.php
@@ -0,0 +1,234 @@
+customer_email = true;
+ $this->id = 'customer_return_shipment_delivered';
+ $this->title = _x( 'Order return delivered', 'shipments', 'woocommerce-germanized' );
+ $this->description = _x( 'Order return notifications are sent to the customer after a return shipment has been returned (delivered) successfully.', 'shipments', 'woocommerce-germanized' );
+
+ $this->template_html = 'emails/customer-return-shipment-delivered.php';
+ $this->template_plain = 'emails/plain/customer-return-shipment-delivered.php';
+ $this->template_base = Package::get_path() . '/templates/';
+ $this->helper = function_exists( 'wc_gzd_get_email_helper' ) ? wc_gzd_get_email_helper( $this ) : false;
+
+ $this->placeholders = array(
+ '{site_title}' => $this->get_blogname(),
+ '{shipment_number}' => '',
+ '{order_number}' => '',
+ '{order_date}' => '',
+ '{date_sent}' => '',
+ );
+
+ // Triggers for this email.
+ add_action( 'woocommerce_gzd_return_shipment_status_processing_to_delivered_notification', array( $this, 'trigger' ), 10 );
+ add_action( 'woocommerce_gzd_return_shipment_status_shipped_to_delivered_notification', array( $this, 'trigger' ), 10 );
+
+ // Call parent constructor.
+ parent::__construct();
+ }
+
+ /**
+ * Get email subject.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_subject() {
+ return _x( 'Return to your order {order_number} has been received', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Get email heading.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_heading() {
+ return _x( 'Received return to your order: {order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Switch Woo and Germanized locale
+ */
+ public function setup_locale() {
+
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_switch_to_site_locale' ) && apply_filters( 'woocommerce_email_setup_locale', true ) ) {
+ wc_gzd_switch_to_site_locale();
+ }
+
+ parent::setup_locale();
+ }
+
+ /**
+ * Restore Woo and Germanized locale
+ */
+ public function restore_locale() {
+
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_restore_locale' ) && apply_filters( 'woocommerce_email_restore_locale', true ) ) {
+ wc_gzd_restore_locale();
+ }
+
+ parent::restore_locale();
+ }
+
+ /**
+ * Trigger.
+ *
+ * @param int $shipment_id Shipment ID.
+ * @param bool $is_confirmation
+ */
+ public function trigger( $shipment_id ) {
+ if ( $this->helper ) {
+ $this->helper->setup_locale();
+ } else {
+ $this->setup_locale();
+ }
+
+ if ( $this->shipment = wc_gzd_get_shipment( $shipment_id ) ) {
+
+ if ( 'return' !== $this->shipment->get_type() ) {
+ return;
+ }
+
+ $this->placeholders['{shipment_number}'] = $this->shipment->get_shipment_number();
+
+ if ( $order_shipment = wc_gzd_get_shipment_order( $this->shipment->get_order() ) ) {
+
+ $this->object = $this->shipment->get_order();
+ $this->recipient = $order_shipment->get_order()->get_billing_email();
+ $this->placeholders['{order_date}'] = wc_format_datetime( $order_shipment->get_order()->get_date_created() );
+ $this->placeholders['{order_number}'] = $order_shipment->get_order()->get_order_number();
+
+ if ( $this->shipment->get_date_sent() ) {
+ $this->placeholders['{date_sent}'] = wc_format_datetime( $this->shipment->get_date_sent() );
+ }
+ }
+ }
+
+ if ( $this->helper ) {
+ $this->helper->setup_email_locale();
+ }
+
+ if ( $this->is_enabled() && $this->get_recipient() ) {
+ $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_email_locale();
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_locale();
+ } else {
+ $this->restore_locale();
+ }
+ }
+
+ /**
+ * Return content from the additional_content field.
+ *
+ * Displayed above the footer.
+ *
+ * @since 2.0.4
+ * @return string
+ */
+ public function get_additional_content() {
+ if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) {
+ return parent::get_additional_content();
+ }
+
+ return '';
+ }
+
+ /**
+ * Get content html.
+ *
+ * @return string
+ */
+ public function get_content_html() {
+ return wc_get_template_html(
+ $this->template_html,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => false,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Get content plain.
+ *
+ * @return string
+ */
+ public function get_content_plain() {
+ return wc_get_template_html(
+ $this->template_plain,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => true,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Default content to show below main email content.
+ *
+ * @since 1.0.1
+ * @return string
+ */
+ public function get_default_additional_content() {
+ return '';
+ }
+ }
+
+endif;
+
+return new WC_GZD_Email_Customer_Return_Shipment_Delivered();
diff --git a/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-return-shipment.php b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-return-shipment.php
new file mode 100644
index 000000000..fd541a241
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-return-shipment.php
@@ -0,0 +1,263 @@
+customer_email = true;
+ $this->id = 'customer_return_shipment';
+ $this->title = _x( 'Order return', 'shipments', 'woocommerce-germanized' );
+ $this->description = _x( 'Order return notifications are sent to the customer after a return shipment was marked as processing.', 'shipments', 'woocommerce-germanized' );
+
+ $this->template_html = 'emails/customer-return-shipment.php';
+ $this->template_plain = 'emails/plain/customer-return-shipment.php';
+ $this->template_base = Package::get_path() . '/templates/';
+ $this->helper = function_exists( 'wc_gzd_get_email_helper' ) ? wc_gzd_get_email_helper( $this ) : false;
+
+ $this->placeholders = array(
+ '{site_title}' => $this->get_blogname(),
+ '{shipment_number}' => '',
+ '{order_number}' => '',
+ '{order_date}' => '',
+ '{date_sent}' => '',
+ );
+
+ // Triggers for this email.
+ add_action( 'woocommerce_gzd_return_shipment_status_draft_to_processing_notification', array( $this, 'trigger' ), 10 );
+ add_action( 'woocommerce_gzd_return_shipment_status_requested_to_processing_notification', array( $this, 'trigger' ), 10 );
+
+ // Call parent constructor.
+ parent::__construct();
+ }
+
+ /**
+ * Get email subject.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_subject() {
+ return _x( 'Return to your order {order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Get email heading.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_heading() {
+ return _x( 'Return to your order: {order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Switch Woo and Germanized locale
+ */
+ public function setup_locale() {
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_switch_to_site_locale' ) && apply_filters( 'woocommerce_email_setup_locale', true ) ) {
+ wc_gzd_switch_to_site_locale();
+ }
+
+ parent::setup_locale();
+ }
+
+ /**
+ * Restore Woo and Germanized locale
+ */
+ public function restore_locale() {
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_restore_locale' ) && apply_filters( 'woocommerce_email_restore_locale', true ) ) {
+ wc_gzd_restore_locale();
+ }
+
+ parent::restore_locale();
+ }
+
+ /**
+ * Trigger.
+ *
+ * @param int $shipment_id Shipment ID.
+ * @param bool $is_confirmation
+ */
+ public function trigger( $shipment_id, $is_confirmation = false ) {
+ if ( $this->helper ) {
+ $this->helper->setup_locale();
+ } else {
+ $this->setup_locale();
+ }
+
+ $this->is_confirmation = $is_confirmation;
+
+ if ( $this->shipment = wc_gzd_get_shipment( $shipment_id ) ) {
+ if ( 'return' !== $this->shipment->get_type() ) {
+ return;
+ }
+
+ // Check if this is a customer request.
+ if ( $this->shipment->is_customer_requested() ) {
+ $this->is_confirmation = true;
+ }
+
+ $this->placeholders['{shipment_number}'] = $this->shipment->get_shipment_number();
+
+ if ( $order_shipment = wc_gzd_get_shipment_order( $this->shipment->get_order() ) ) {
+ $this->object = $this->shipment->get_order();
+ $this->recipient = $order_shipment->get_order()->get_billing_email();
+ $this->placeholders['{order_date}'] = wc_format_datetime( $order_shipment->get_order()->get_date_created() );
+ $this->placeholders['{order_number}'] = $order_shipment->get_order()->get_order_number();
+
+ if ( $this->shipment->get_date_sent() ) {
+ $this->placeholders['{date_sent}'] = wc_format_datetime( $this->shipment->get_date_sent() );
+ }
+ }
+ }
+
+ $this->id = 'customer_return_shipment';
+
+ if ( $this->helper ) {
+ $this->helper->setup_email_locale();
+ }
+
+ if ( $this->is_enabled() && $this->get_recipient() ) {
+ $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_email_locale();
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_locale();
+ } else {
+ $this->restore_locale();
+ }
+ }
+
+ /**
+ * Return content from the additional_content field.
+ *
+ * Displayed above the footer.
+ *
+ * @since 2.0.4
+ * @return string
+ */
+ public function get_additional_content() {
+ if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) {
+ return parent::get_additional_content();
+ }
+
+ return '';
+ }
+
+ /**
+ * Get content html.
+ *
+ * @return string
+ */
+ public function get_content_html() {
+ return wc_get_template_html(
+ $this->template_html,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'is_confirmation' => $this->is_confirmation,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => false,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Get content plain.
+ *
+ * @return string
+ */
+ public function get_content_plain() {
+ return wc_get_template_html(
+ $this->template_plain,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'is_confirmation' => $this->is_confirmation,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => true,
+ 'email' => $this,
+ )
+ );
+ }
+
+ public function get_attachments() {
+ $attachments = array();
+
+ if ( $this->shipment->has_label() ) {
+ $label = $this->shipment->get_label();
+
+ if ( $file = $label->get_file() ) {
+ $attachments[] = $file;
+ }
+ }
+
+ return apply_filters( 'woocommerce_email_attachments', $attachments, $this->id, $this->object, $this );
+ }
+
+ /**
+ * Default content to show below main email content.
+ *
+ * @since 1.0.1
+ * @return string
+ */
+ public function get_default_additional_content() {
+ return '';
+ }
+ }
+
+endif;
+
+return new WC_GZD_Email_Customer_Return_Shipment();
diff --git a/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-shipment.php b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-shipment.php
new file mode 100644
index 000000000..f1ee50292
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-customer-shipment.php
@@ -0,0 +1,415 @@
+customer_email = true;
+ $this->id = 'customer_shipment';
+ $this->title = _x( 'Order shipped', 'shipments', 'woocommerce-germanized' );
+ $this->description = _x( 'Shipment notifications are sent to the customer when a shipment gets shipped.', 'shipments', 'woocommerce-germanized' );
+
+ $this->template_html = 'emails/customer-shipment.php';
+ $this->template_plain = 'emails/plain/customer-shipment.php';
+ $this->template_base = Package::get_path() . '/templates/';
+ $this->helper = function_exists( 'wc_gzd_get_email_helper' ) ? wc_gzd_get_email_helper( $this ) : false;
+
+ $this->placeholders = array(
+ '{site_title}' => $this->get_blogname(),
+ '{shipment_number}' => '',
+ '{order_number}' => '',
+ '{order_date}' => '',
+ '{date_sent}' => '',
+ '{current_shipment_num}' => '',
+ '{total_shipments}' => '',
+ );
+
+ // Triggers for this email.
+ if ( 'yes' === Package::get_setting( 'notify_enable' ) ) {
+ add_action( 'woocommerce_gzd_shipment_status_draft_to_shipped_notification', array( $this, 'trigger' ), 10 );
+ add_action( 'woocommerce_gzd_shipment_status_processing_to_shipped_notification', array( $this, 'trigger' ), 10 );
+ }
+
+ // Call parent constructor.
+ parent::__construct();
+ }
+
+ /**
+ * Get email subject.
+ *
+ * @param bool $partial Whether it is a partial refund or a full refund.
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_subject( $partial = false ) {
+ if ( $partial ) {
+ return _x( 'Your {site_title} order #{order_number} has been partially shipped ({current_shipment_num}/{total_shipments})', 'shipments', 'woocommerce-germanized' );
+ } else {
+ return _x( 'Your {site_title} order #{order_number} has been shipped ({current_shipment_num}/{total_shipments})', 'shipments', 'woocommerce-germanized' );
+ }
+ }
+
+ /**
+ * Get email heading.
+ *
+ * @param bool $partial Whether it is a partial refund or a full refund.
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_heading( $partial = false ) {
+ if ( $partial ) {
+ return _x( 'Partial shipment to your order: {order_number}', 'shipments', 'woocommerce-germanized' );
+ } else {
+ return _x( 'Shipment to your order: {order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+ }
+
+ /**
+ * Get email subject.
+ *
+ * @return string
+ */
+ public function get_subject() {
+ if ( $this->partial_shipment ) {
+ $subject = $this->get_option( 'subject_partial', $this->get_default_subject( true ) );
+ } else {
+ $subject = $this->get_option( 'subject_full', $this->get_default_subject() );
+ }
+
+ /**
+ * Filter to adjust the email subject for a shipped Shipment.
+ *
+ * @param string $subject The subject.
+ * @param WC_GZD_Email_Customer_Shipment $email The email instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_email_subject_customer_shipment', $this->format_string( $subject ), $this->object, $this );
+ }
+
+ /**
+ * Get email heading.
+ *
+ * @return string
+ */
+ public function get_heading() {
+ if ( $this->partial_shipment ) {
+ $heading = $this->get_option( 'heading_partial', $this->get_default_heading( true ) );
+ } else {
+ $heading = $this->get_option( 'heading_full', $this->get_default_heading() );
+ }
+
+ /**
+ * Filter to adjust the email heading for a shipped Shipment.
+ *
+ * @param string $heading The heading.
+ * @param WC_GZD_Email_Customer_Shipment $email The email instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_email_heading_customer_shipment', $this->format_string( $heading ), $this->object, $this );
+ }
+
+ /**
+ * Switch Woo and Germanized locale
+ */
+ public function setup_locale() {
+
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_switch_to_site_locale' ) && apply_filters( 'woocommerce_email_setup_locale', true ) ) {
+ wc_gzd_switch_to_site_locale();
+ }
+
+ parent::setup_locale();
+ }
+
+ /**
+ * Restore Woo and Germanized locale
+ */
+ public function restore_locale() {
+
+ if ( $this->is_customer_email() && function_exists( 'wc_gzd_restore_locale' ) && apply_filters( 'woocommerce_email_restore_locale', true ) ) {
+ wc_gzd_restore_locale();
+ }
+
+ parent::restore_locale();
+ }
+
+ public function get_shipped_position_number( $shipment_id ) {
+ $shipped_count = 1;
+
+ if ( ! $this->object || ( ! $order = wc_gzd_get_shipment_order( $this->object ) ) ) {
+ return $shipped_count;
+ }
+
+ if ( is_a( $shipment_id, '\Vendidero\Germanized\Shipments\Shipment' ) ) {
+ $shipment_id = $shipment_id->get_id();
+ }
+
+ if ( ! is_numeric( $shipment_id ) ) {
+ return $shipped_count;
+ }
+
+ foreach ( $order->get_simple_shipments() as $key => $shipment ) {
+ if ( $shipment->is_shipped() ) {
+ if ( (int) $shipment->get_id() !== (int) $shipment_id ) {
+ ++$shipped_count;
+ }
+ }
+ }
+
+ return $shipped_count;
+ }
+
+ /**
+ * Trigger.
+ *
+ * @param int $shipment_id Shipment ID.
+ */
+ public function trigger( $shipment_id ) {
+ if ( $this->helper ) {
+ $this->helper->setup_locale();
+ } else {
+ $this->setup_locale();
+ }
+
+ $this->partial_shipment = false;
+
+ if ( $this->shipment = wc_gzd_get_shipment( $shipment_id ) ) {
+
+ $this->placeholders['{shipment_number}'] = $this->shipment->get_shipment_number();
+
+ if ( $order_shipment = wc_gzd_get_shipment_order( $this->shipment->get_order() ) ) {
+ $this->object = $this->shipment->get_order();
+ $this->total_shipments = count( $order_shipment->get_simple_shipments() );
+ $this->cur_position = $this->get_shipped_position_number( $shipment_id );
+
+ if ( $order_shipment->needs_shipping() || $this->total_shipments > 1 ) {
+ if ( $order_shipment->needs_shipping() || ( $this->cur_position < $this->total_shipments ) ) {
+ $this->partial_shipment = true;
+ }
+ }
+
+ $this->recipient = $order_shipment->get_order()->get_billing_email();
+ $this->placeholders['{order_date}'] = wc_format_datetime( $order_shipment->get_order()->get_date_created() );
+ $this->placeholders['{order_number}'] = $order_shipment->get_order()->get_order_number();
+ $this->placeholders['{total_shipments}'] = $this->total_shipments;
+ $this->placeholders['{current_shipment_num}'] = $this->cur_position;
+
+ if ( $this->shipment->get_date_sent() ) {
+ $this->placeholders['{date_sent}'] = wc_format_datetime( $this->shipment->get_date_sent() );
+ }
+ }
+ }
+
+ $this->id = $this->partial_shipment ? 'customer_partial_shipment' : 'customer_shipment';
+
+ if ( $this->helper ) {
+ $this->helper->setup_email_locale();
+ }
+
+ if ( $this->is_enabled() && $this->get_recipient() ) {
+ $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_email_locale();
+ }
+
+ if ( $this->helper ) {
+ $this->helper->restore_locale();
+ } else {
+ $this->restore_locale();
+ }
+ }
+
+ /**
+ * Return content from the additional_content field.
+ *
+ * Displayed above the footer.
+ *
+ * @since 2.0.4
+ * @return string
+ */
+ public function get_additional_content() {
+ if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) {
+ return parent::get_additional_content();
+ }
+
+ return '';
+ }
+
+ /**
+ * Get content html.
+ *
+ * @return string
+ */
+ public function get_content_html() {
+ return wc_get_template_html(
+ $this->template_html,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'partial_shipment' => $this->partial_shipment,
+ 'cur_position' => $this->cur_position,
+ 'total_shipments' => $this->total_shipments,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => false,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Get content plain.
+ *
+ * @return string
+ */
+ public function get_content_plain() {
+ return wc_get_template_html(
+ $this->template_plain,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'partial_shipment' => $this->partial_shipment,
+ 'cur_position' => $this->cur_position,
+ 'total_shipments' => $this->total_shipments,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => false,
+ 'plain_text' => true,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Default content to show below main email content.
+ *
+ * @since 1.0.1
+ * @return string
+ */
+ public function get_default_additional_content() {
+ return '';
+ }
+
+ /**
+ * Initialise settings form fields.
+ */
+ public function init_form_fields() {
+ /* translators: %s: list of placeholders */
+ $placeholder_text = sprintf( _x( 'Available placeholders: %s', 'shipments', 'woocommerce-germanized' ), '
+
+
+
+ $tab ) : // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ if ( $tab->hide_from_main_panel() ) {
+ continue;
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+ needs_install() ) : ?>
+ get_label() ); ?>
+
+ get_label() ); ?>
+
+
+
+ needs_install() ) : ?>
+
+
+
+ is_enabled() ? esc_attr_x( 'Yes', 'shipments', 'woocommerce-germanized' ) : esc_attr_x( 'No', 'shipments', 'woocommerce-germanized' ) ); ?>
+
+
+ supports_disabling() ) : ?>
+
+
+ is_enabled() ? esc_attr_x( 'Yes', 'shipments', 'woocommerce-germanized' ) : esc_attr_x( 'No', 'shipments', 'woocommerce-germanized' ) ); ?>
+
+
+
+ get_description() ); ?>
+
+ has_help_link() ) : ?>
+
+
+
+ needs_install() ) : ?>
+
+
+
+
+ ' . esc_html( implode( '
, ', array_keys( $this->placeholders ) ) ) . '
' );
+
+ $this->form_fields = array(
+ 'enabled' => array(
+ 'title' => _x( 'Enable/Disable', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'checkbox',
+ 'label' => _x( 'Enable this email notification', 'shipments', 'woocommerce-germanized' ),
+ 'default' => 'yes',
+ ),
+ 'subject_full' => array(
+ 'title' => _x( 'Full shipment subject', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'text',
+ 'desc_tip' => true,
+ 'description' => $placeholder_text,
+ 'placeholder' => $this->get_default_subject(),
+ 'default' => '',
+ ),
+ 'subject_partial' => array(
+ 'title' => _x( 'Partial shipment subject', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'text',
+ 'desc_tip' => true,
+ 'description' => $placeholder_text,
+ 'placeholder' => $this->get_default_subject( true ),
+ 'default' => '',
+ ),
+ 'heading_full' => array(
+ 'title' => _x( 'Full shipment email heading', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'text',
+ 'desc_tip' => true,
+ 'description' => $placeholder_text,
+ 'placeholder' => $this->get_default_heading(),
+ 'default' => '',
+ ),
+ 'heading_partial' => array(
+ 'title' => _x( 'Partial shipment email heading', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'text',
+ 'desc_tip' => true,
+ 'description' => $placeholder_text,
+ 'placeholder' => $this->get_default_heading( true ),
+ 'default' => '',
+ ),
+ 'additional_content' => array(
+ 'title' => _x( 'Additional content', 'shipments', 'woocommerce-germanized' ),
+ 'description' => _x( 'Text to appear below the main email content.', 'shipments', 'woocommerce-germanized' ) . ' ' . $placeholder_text,
+ 'css' => 'width:400px; height: 75px;',
+ 'placeholder' => _x( 'N/A', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'textarea',
+ 'default' => $this->get_default_additional_content(),
+ 'desc_tip' => true,
+ ),
+ 'email_type' => array(
+ 'title' => _x( 'Email type', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'select',
+ 'description' => _x( 'Choose which format of email to send.', 'shipments', 'woocommerce-germanized' ),
+ 'default' => 'html',
+ 'class' => 'email_type wc-enhanced-select',
+ 'options' => $this->get_email_type_options(),
+ 'desc_tip' => true,
+ ),
+ );
+ }
+ }
+
+endif;
+
+return new WC_GZD_Email_Customer_Shipment();
diff --git a/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-new-return-shipment-request.php b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-new-return-shipment-request.php
new file mode 100644
index 000000000..026105b46
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/emails/class-wc-gzd-email-new-return-shipment-request.php
@@ -0,0 +1,221 @@
+id = 'new_return_shipment_request';
+ $this->title = _x( 'New order return request', 'shipments', 'woocommerce-germanized' );
+ $this->description = _x( 'New order return request emails are sent to chosen recipient(s) when a new return is requested.', 'shipments', 'woocommerce-germanized' );
+
+ $this->template_html = 'emails/admin-new-return-shipment-request.php';
+ $this->template_plain = 'emails/plain/admin-new-return-shipment-request.php';
+ $this->template_base = Package::get_path() . '/templates/';
+
+ $this->placeholders = array(
+ '{site_title}' => $this->get_blogname(),
+ '{shipment_number}' => '',
+ '{order_number}' => '',
+ '{order_date}' => '',
+ );
+
+ // Triggers for this email.
+ add_action( 'woocommerce_gzd_new_customer_return_shipment_request', array( $this, 'trigger' ), 10 );
+
+ // Call parent constructor.
+ parent::__construct();
+
+ // Other settings.
+ $this->recipient = $this->get_option( 'recipient', get_option( 'admin_email' ) );
+ }
+
+ /**
+ * Get email subject.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_subject() {
+ return _x( '[{site_title}]: New return request to #{order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Get email heading.
+ *
+ * @since 3.1.0
+ * @return string
+ */
+ public function get_default_heading() {
+ return _x( 'New return request to: #{order_number}', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Trigger.
+ *
+ * @param int|ReturnShipment $shipment_id Shipment ID.
+ */
+ public function trigger( $shipment_id ) {
+ $this->setup_locale();
+
+ if ( $this->shipment = wc_gzd_get_shipment( $shipment_id ) ) {
+
+ if ( 'return' !== $this->shipment->get_type() ) {
+ return;
+ }
+
+ $this->placeholders['{shipment_number}'] = $this->shipment->get_shipment_number();
+
+ if ( $order_shipment = wc_gzd_get_shipment_order( $this->shipment->get_order() ) ) {
+ $this->object = $this->shipment->get_order();
+ $this->placeholders['{order_date}'] = wc_format_datetime( $order_shipment->get_order()->get_date_created() );
+ $this->placeholders['{order_number}'] = $order_shipment->get_order()->get_order_number();
+ }
+ }
+
+ if ( $this->is_enabled() && $this->get_recipient() ) {
+ $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
+ }
+
+ $this->restore_locale();
+ }
+
+ /**
+ * Return content from the additional_content field.
+ *
+ * Displayed above the footer.
+ *
+ * @since 2.0.4
+ * @return string
+ */
+ public function get_additional_content() {
+ if ( method_exists( get_parent_class( $this ), 'get_additional_content' ) ) {
+ return parent::get_additional_content();
+ }
+
+ return '';
+ }
+
+ /**
+ * Get content html.
+ *
+ * @return string
+ */
+ public function get_content_html() {
+ return wc_get_template_html(
+ $this->template_html,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => true,
+ 'plain_text' => false,
+ 'email' => $this,
+ )
+ );
+ }
+
+ /**
+ * Get content plain.
+ *
+ * @return string
+ */
+ public function get_content_plain() {
+ return wc_get_template_html(
+ $this->template_plain,
+ array(
+ 'shipment' => $this->shipment,
+ 'order' => $this->object,
+ 'email_heading' => $this->get_heading(),
+ 'additional_content' => $this->get_additional_content(),
+ 'sent_to_admin' => true,
+ 'plain_text' => true,
+ 'email' => $this,
+ )
+ );
+ }
+
+ public function get_attachments() {
+ $attachments = array();
+
+ if ( $this->shipment->has_label() ) {
+ $label = $this->shipment->get_label();
+
+ if ( $file = $label->get_file() ) {
+ $attachments[] = $file;
+ }
+ }
+
+ return apply_filters( 'woocommerce_email_attachments', $attachments, $this->id, $this->object, $this );
+ }
+
+ /**
+ * Default content to show below main email content.
+ *
+ * @since 1.0.1
+ * @return string
+ */
+ public function get_default_additional_content() {
+ return '';
+ }
+
+ /**
+ * Initialise settings form fields.
+ */
+ public function init_form_fields() {
+ parent::init_form_fields();
+
+ $this->form_fields = array_merge(
+ $this->form_fields,
+ array(
+ 'recipient' => array(
+ 'title' => _x( 'Recipient(s)', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'text',
+ /* translators: %s: WP admin email */
+ 'description' => sprintf( _x( 'Enter recipients (comma separated) for this email. Defaults to %s.', 'shipments', 'woocommerce-germanized' ), '' . esc_attr( get_option( 'admin_email' ) ) . '
' ),
+ 'placeholder' => '',
+ 'default' => '',
+ 'desc_tip' => true,
+ ),
+ )
+ );
+ }
+ }
+
+endif;
+
+return new WC_GZD_Email_New_Return_Shipment_Request();
diff --git a/packages/woocommerce-germanized-shipments/includes/wc-gzd-label-functions.php b/packages/woocommerce-germanized-shipments/includes/wc-gzd-label-functions.php
new file mode 100644
index 000000000..a7b71c78b
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/wc-gzd-label-functions.php
@@ -0,0 +1,152 @@
+get_labels();
+}
+
+function wc_gzd_get_label_type_by_shipment( $shipment ) {
+ $type = is_a( $shipment, '\Vendidero\Germanized\Shipments\Shipment' ) ? $shipment->get_type() : $shipment;
+
+ return apply_filters( 'woocommerce_gzd_shipment_label_type', $type, $shipment );
+}
+
+function wc_gzd_get_shipment_label_types() {
+ return apply_filters(
+ 'woocommerce_gzd_shipment_label_types',
+ array(
+ 'simple',
+ 'return',
+ )
+ );
+}
+
+function wc_gzd_get_label_by_shipment( $the_shipment, $type = '' ) {
+ $shipment_id = \Vendidero\Germanized\Shipments\ShipmentFactory::get_shipment_id( $the_shipment );
+ $label = false;
+
+ if ( $shipment_id ) {
+ $args = array(
+ 'shipment_id' => $shipment_id,
+ 'limit' => 1,
+ );
+
+ if ( ! empty( $type ) ) {
+ $args['type'] = $type;
+ }
+
+ $labels = wc_gzd_get_shipment_labels( $args );
+
+ if ( ! empty( $labels ) ) {
+ $label = $labels[0];
+ }
+ }
+
+ return apply_filters( 'woocommerce_gzd_shipment_label_for_shipment', $label, $the_shipment );
+}
+
+/**
+ * @param false $the_label
+ * @param string $shipping_provider
+ * @param string $type
+ *
+ * @return \Vendidero\Germanized\Shipments\Interfaces\ShipmentLabel|boolean
+ */
+function wc_gzd_get_shipment_label( $the_label = false, $shipping_provider = '', $type = 'simple' ) {
+ return apply_filters( 'woocommerce_gzd_shipment_label', \Vendidero\Germanized\Shipments\Labels\Factory::get_label( $the_label, $shipping_provider, $type ), $the_label, $shipping_provider, $type );
+}
+
+/**
+ * @param \Vendidero\Germanized\Shipments\Shipment $shipment
+ * @param bool $net_weight
+ * @param string $unit
+ *
+ * @return float
+ */
+function wc_gzd_get_shipment_label_weight( $shipment, $net_weight = false, $unit = 'kg' ) {
+ $shipment_weight = $shipment->get_total_weight();
+ $shipment_content_weight = $shipment->get_weight();
+ $shipment_packaging_weight = $shipment->get_packaging_weight();
+
+ if ( ! empty( $shipment_weight ) ) {
+ $shipment_weight = wc_get_weight( $shipment_weight, $unit, $shipment->get_weight_unit() );
+ }
+
+ if ( ! empty( $shipment_content_weight ) ) {
+ $shipment_content_weight = wc_get_weight( $shipment_content_weight, $unit, $shipment->get_weight_unit() );
+ }
+
+ if ( ! empty( $shipment_packaging_weight ) ) {
+ $shipment_packaging_weight = wc_get_weight( $shipment_packaging_weight, $unit, $shipment->get_weight_unit() );
+ }
+
+ /**
+ * The net weight does not include packaging weight.
+ */
+ if ( $net_weight ) {
+ $shipment_packaging_weight = 0;
+ $shipment_weight = $shipment_content_weight;
+ }
+
+ if ( $provider = $shipment->get_shipping_provider_instance() ) {
+ $min_weight = wc_get_weight( $provider->get_label_minimum_shipment_weight(), $unit, 'kg' );
+ $default_weight = wc_get_weight( $provider->get_label_default_shipment_weight(), $unit, 'kg' );
+
+ if ( empty( $shipment_content_weight ) ) {
+ $shipment_weight = $default_weight;
+
+ if ( ! $net_weight ) {
+ $shipment_weight += $shipment_packaging_weight;
+ }
+ }
+
+ if ( $shipment_weight < $min_weight ) {
+ $shipment_weight = $min_weight;
+ }
+ }
+
+ $shipment_weight = wc_format_decimal( $shipment_weight, 3 );
+
+ return apply_filters( 'woocommerce_gzd_shipment_label_weight', $shipment_weight, $shipment, $unit );
+}
+
+/**
+ * @param \Vendidero\Germanized\Shipments\Shipment $shipment
+ * @param string $dimension
+ * @param string $unit
+ */
+function wc_gzd_get_shipment_label_dimensions( $shipment, $unit = 'cm' ) {
+ $dimensions = array(
+ 'length' => 0,
+ 'width' => 0,
+ 'height' => 0,
+ );
+
+ if ( $shipment->has_dimensions() ) {
+ $dimensions = $shipment->get_package_dimensions();
+
+ foreach ( $dimensions as $key => $data ) {
+ $dimensions[ $key ] = wc_get_dimension( $data, $unit, $shipment->get_dimension_unit() );
+ }
+ }
+
+ return apply_filters( 'woocommerce_gzd_shipment_label_dimensions', $dimensions, $shipment, $unit );
+}
diff --git a/packages/woocommerce-germanized-shipments/includes/wc-gzd-packaging-functions.php b/packages/woocommerce-germanized-shipments/includes/wc-gzd-packaging-functions.php
new file mode 100644
index 000000000..4aea8b23d
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/wc-gzd-packaging-functions.php
@@ -0,0 +1,59 @@
+ _x( 'Cardboard', 'shipments', 'woocommerce-germanized' ),
+ 'letter' => _x( 'Letter', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ return apply_filters( 'woocommerce_gzd_packaging_types', $types );
+}
+
+/**
+ * @return \Vendidero\Germanized\Shipments\Packaging[] $packaging_list
+ */
+function wc_gzd_get_packaging_list( $args = array() ) {
+ $list = \Vendidero\Germanized\Shipments\Packaging\Helper::get_packaging_list( $args );
+
+ return $list;
+}
+
+function wc_gzd_get_packaging_weight_unit() {
+ return apply_filters( 'woocommerce_gzd_packaging_weight_unit', get_option( 'woocommerce_weight_unit', 'kg' ) );
+}
+
+function wc_gzd_get_packaging_dimension_unit() {
+ return apply_filters( 'woocommerce_gzd_packaging_dimension_unit', get_option( 'woocommerce_dimension_unit', 'cm' ) );
+}
+
+function wc_gzd_get_packaging_select( $args = array() ) {
+ $list = wc_gzd_get_packaging_list( $args );
+ $select = array(
+ '' => _x( 'None', 'shipments-packaging', 'woocommerce-germanized' ),
+ );
+
+ foreach ( $list as $packaging ) {
+ $select[ $packaging->get_id() ] = $packaging->get_title();
+ }
+
+ return $select;
+}
diff --git a/packages/woocommerce-germanized-shipments/includes/wc-gzd-shipment-functions.php b/packages/woocommerce-germanized-shipments/includes/wc-gzd-shipment-functions.php
new file mode 100644
index 000000000..c4cfad49c
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/wc-gzd-shipment-functions.php
@@ -0,0 +1,1604 @@
+countries ? WC()->countries->get_states( $country ) : array();
+ $formatted_state = ( $states && isset( $states[ $state ] ) ) ? $states[ $state ] : $state;
+
+ return $formatted_state;
+}
+
+function wc_gzd_country_to_alpha3( $country ) {
+ return Package::get_country_iso_alpha3( $country );
+}
+
+function wc_gzd_get_customer_preferred_shipping_provider( $user_id ) {
+ $default_provider = wc_gzd_get_default_shipping_provider();
+ $provider = false;
+
+ if ( ! $default_provider ) {
+ $available = wc_gzd_get_available_shipping_providers();
+ $default_provider = 1 === count( $available ) ? array_values( $available )[0] : false;
+ }
+
+ if ( $customer = new WC_Customer( $user_id ) ) {
+ if ( $last_order = $customer->get_last_order() ) {
+ $provider = wc_gzd_get_order_shipping_provider( $last_order );
+ }
+ }
+
+ if ( ! $provider && $default_provider ) {
+ $provider = wc_gzd_get_shipping_provider( $default_provider );
+ }
+
+ return apply_filters( 'woocommerce_gzd_customer_shipping_provider', $provider, $user_id );
+}
+
+function wc_gzd_country_to_alpha2( $country ) {
+ return Package::get_country_iso_alpha2( $country );
+}
+
+function wc_gzd_get_shipment_order( $order ) {
+ return \Vendidero\Germanized\Shipments\Orders\Factory::get_order( $order );
+}
+
+function wc_gzd_get_shipment_label_title( $type, $plural = false ) {
+ $type_data = wc_gzd_get_shipment_type_data( $type );
+
+ return ( ! $plural ? $type_data['labels']['singular'] : $type_data['labels']['plural'] );
+}
+
+function wc_gzd_get_shipping_label_zones() {
+ return apply_filters(
+ 'woocommerce_gzd_shipments_shipping_label_zones',
+ array(
+ 'dom' => _x( 'Domestic', 'shipments', 'woocommerce-germanized' ),
+ 'eu' => _x( 'EU', 'shipments', 'woocommerce-germanized' ),
+ 'int' => _x( 'International', 'shipments', 'woocommerce-germanized' ),
+ )
+ );
+}
+
+function wc_gzd_get_shipping_label_zone_title( $zone ) {
+ $zones = wc_gzd_get_shipping_label_zones();
+ $title = array_key_exists( $zone, $zones ) ? $zones[ $zone ] : '';
+
+ return apply_filters( 'woocommerce_gzd_shipments_shipping_label_zone_title', $title, $zone );
+}
+
+function wc_gzd_get_shipping_shipments_label_zone_title( $zone ) {
+ $title = _x( '%1$s shipments', 'shipments-zone-title', 'woocommerce-germanized' );
+
+ $zones = array(
+ 'dom' => _x( 'Domestic Shipments', 'shipments', 'woocommerce-germanized' ),
+ 'eu' => _x( 'EU Shipments', 'shipments', 'woocommerce-germanized' ),
+ 'int' => _x( 'International Shipments', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ $title = array_key_exists( $zone, $zones ) ? $zones[ $zone ] : $title;
+
+ return apply_filters( 'woocommerce_gzd_shipments_shipping_shipments_label_zone_title', $title, $zone );
+}
+
+function wc_gzd_get_shipment_types() {
+ return array_keys( wc_gzd_get_shipment_type_data( false ) );
+}
+
+/**
+ * Get shipment type data by type.
+ *
+ * @param string $type type name.
+ * @return bool|array Details about the shipment type.
+ *
+ * @package Vendidero/Germanized/Shipments
+ */
+function wc_gzd_get_shipment_type_data( $type = false ) {
+ $types = apply_filters(
+ 'woocommerce_gzd_shipment_type_data',
+ array(
+ 'simple' => array(
+ 'class_name' => '\Vendidero\Germanized\Shipments\SimpleShipment',
+ 'labels' => array(
+ 'singular' => _x( 'Shipment', 'shipments', 'woocommerce-germanized' ),
+ 'plural' => _x( 'Shipments', 'shipments', 'woocommerce-germanized' ),
+ ),
+ ),
+ 'return' => array(
+ 'class_name' => '\Vendidero\Germanized\Shipments\ReturnShipment',
+ 'labels' => array(
+ 'singular' => _x( 'Return', 'shipments', 'woocommerce-germanized' ),
+ 'plural' => _x( 'Returns', 'shipments', 'woocommerce-germanized' ),
+ ),
+ ),
+ )
+ );
+
+ if ( $type && array_key_exists( $type, $types ) ) {
+ return $types[ $type ];
+ } elseif ( false === $type ) {
+ return $types;
+ } else {
+ return $types['simple'];
+ }
+}
+
+function wc_gzd_get_shipments_by_order( $order ) {
+ $shipments = array();
+
+ if ( $order_shipment = wc_gzd_get_shipment_order( $order ) ) {
+ $shipments = $order_shipment->get_shipments();
+ }
+
+ return $shipments;
+}
+
+function wc_gzd_get_shipment_order_shipping_statuses() {
+ $shipment_statuses = array(
+ 'gzd-not-shipped' => _x( 'Not shipped', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-partially-shipped' => _x( 'Partially shipped', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-shipped' => _x( 'Shipped', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-partially-delivered' => _x( 'Partially delivered', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-delivered' => _x( 'Delivered', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-no-shipping-needed' => _x( 'No shipping needed', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ /**
+ * Filter to adjust or add order shipping statuses.
+ * An order might retrieve a shipping status e.g. not shipped.
+ *
+ * @param array $shipment_statuses Available order shipping statuses.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_order_shipping_statuses', $shipment_statuses );
+}
+
+function wc_gzd_get_shipment_order_return_statuses() {
+ $shipment_statuses = array(
+ 'gzd-open' => _x( 'Open', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-partially-returned' => _x( 'Partially returned', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-returned' => _x( 'Returned', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ /**
+ * Filter to adjust or add order return statuses.
+ * An order might retrieve a shipping status e.g. not shipped.
+ *
+ * @param array $shipment_statuses Available order return statuses.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_order_return_statuses', $shipment_statuses );
+}
+
+/**
+ * @param $instance_id
+ *
+ * @return \Vendidero\Germanized\Shipments\ShippingMethod\ProviderMethod|false
+ */
+function wc_gzd_get_shipping_provider_method( $instance_id ) {
+ return \Vendidero\Germanized\Shipments\ShippingMethod\MethodHelper::get_provider_method( $instance_id );
+}
+
+/**
+ * Returns the current shipping method rate id.
+ *
+ * @return false|string
+ */
+function wc_gzd_get_current_shipping_method_id() {
+ $chosen_shipping_methods = WC()->session ? WC()->session->get( 'chosen_shipping_methods' ) : array();
+
+ if ( ! empty( $chosen_shipping_methods ) ) {
+ return reset( $chosen_shipping_methods );
+ }
+
+ return false;
+}
+
+function wc_gzd_get_current_shipping_provider_method() {
+ if ( $current = wc_gzd_get_current_shipping_method_id() ) {
+ return wc_gzd_get_shipping_provider_method( $current );
+ }
+
+ return false;
+}
+
+function wc_gzd_get_shipment_order_shipping_status_name( $status ) {
+ if ( 'gzd-' !== substr( $status, 0, 4 ) ) {
+ $status = 'gzd-' . $status;
+ }
+
+ $status_name = '';
+ $statuses = wc_gzd_get_shipment_order_shipping_statuses();
+
+ if ( array_key_exists( $status, $statuses ) ) {
+ $status_name = $statuses[ $status ];
+ }
+
+ /**
+ * Filter to adjust the status name for a certain order shipping status.
+ *
+ * @see wc_gzd_get_shipment_order_shipping_statuses()
+ *
+ * @param string $status_name The status name.
+ * @param string $status The shipping status.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_order_shipping_status_name', $status_name, $status );
+}
+
+function wc_gzd_get_shipment_order_return_status_name( $status ) {
+ if ( 'gzd-' !== substr( $status, 0, 4 ) ) {
+ $status = 'gzd-' . $status;
+ }
+
+ $status_name = '';
+ $statuses = wc_gzd_get_shipment_order_return_statuses();
+
+ if ( array_key_exists( $status, $statuses ) ) {
+ $status_name = $statuses[ $status ];
+ }
+
+ /**
+ * Filter to adjust the status name for a certain order return status.
+ *
+ * @see wc_gzd_get_shipment_order_return_statuses()
+ *
+ * @param string $status_name The status name.
+ * @param string $status The return status.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_order_return_status_name', $status_name, $status );
+}
+
+/**
+ * Standard way of retrieving shipments based on certain parameters.
+ *
+ * @param array $args Array of args (above).
+ *
+ * @return Shipment[] The shipments found.
+ *@since 3.0.0
+ */
+function wc_gzd_get_shipments( $args ) {
+ $query = new Vendidero\Germanized\Shipments\ShipmentQuery( $args );
+
+ return $query->get_shipments();
+}
+
+function wc_gzd_get_shipment_customer_visible_statuses( $shipment_type = 'simple' ) {
+ $statuses = array_keys( wc_gzd_get_shipment_statuses() );
+ $statuses = array_diff( $statuses, array( 'gzd-draft' ) );
+
+ /**
+ * Filter to decide which shipment statuses should be visible to customers
+ * e.g. whether a shipment of a certain status should be shown or not.
+ *
+ * @param array $shipment_statuses The available shipment statuses.
+ * @param string $shipment_type The shipment type.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipment_customer_visible_statuses', $statuses, $shipment_type );
+}
+
+/**
+ * Main function for returning shipments.
+ *
+ * @param mixed $the_shipment Object or shipment id.
+ *
+ * @return bool|SimpleShipment|ReturnShipment|Shipment
+ */
+function wc_gzd_get_shipment( $the_shipment ) {
+ return ShipmentFactory::get_shipment( $the_shipment );
+}
+
+/**
+ * Get all shipment statuses.
+ *
+ * @return array
+ */
+function wc_gzd_get_shipment_statuses() {
+ $shipment_statuses = array(
+ 'gzd-draft' => _x( 'Draft', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-processing' => _x( 'Processing', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-shipped' => _x( 'Shipped', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-delivered' => _x( 'Delivered', 'shipments', 'woocommerce-germanized' ),
+ 'gzd-requested' => _x( 'Requested', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ /**
+ * Add or adjust available Shipment statuses.
+ *
+ * @param array $shipment_statuses The available shipment statuses.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipment_statuses', $shipment_statuses );
+}
+
+/**
+ * @param Shipment $shipment
+ *
+ * @return mixed|void
+ */
+function wc_gzd_get_shipment_selectable_statuses( $shipment ) {
+ $shipment_statuses = wc_gzd_get_shipment_statuses();
+
+ if ( ! $shipment->has_status( 'requested' ) && isset( $shipment_statuses['gzd-requested'] ) ) {
+ unset( $shipment_statuses['gzd-requested'] );
+ }
+
+ /**
+ * Add or remove selectable shipment statuses for a certain shipment and/or shipment type.
+ *
+ * @param array $shipment_statuses The available shipment statuses.
+ * @param string $type The shipment type e.g. return.
+ * @param Shipment $shipment The shipment instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipment_selectable_statuses', $shipment_statuses, $shipment->get_type(), $shipment );
+}
+
+/**
+ * @param Order $order_shipment
+ * @param array $args
+ *
+ * @return ReturnShipment|WP_Error
+ */
+function wc_gzd_create_return_shipment( $order_shipment, $args = array() ) {
+ try {
+
+ if ( ! $order_shipment || ! is_a( $order_shipment, 'Vendidero\Germanized\Shipments\Order' ) ) {
+ throw new Exception( _x( 'Invalid order.', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ if ( ! $order_shipment->needs_return() ) {
+ throw new Exception( _x( 'This order is already fully returned.', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ $args = wp_parse_args(
+ $args,
+ array(
+ 'items' => array(),
+ 'props' => array(),
+ )
+ );
+
+ $shipment = ShipmentFactory::get_shipment( false, 'return' );
+
+ if ( ! $shipment ) {
+ throw new Exception( _x( 'Error while creating the shipment instance', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ // Make sure shipment knows its parent
+ $shipment->set_order_shipment( $order_shipment );
+ $shipment->sync( $args['props'] );
+ $shipment->sync_items( $args );
+ $shipment->save();
+
+ } catch ( Exception $e ) {
+ return new WP_Error( 'error', $e->getMessage() );
+ }
+
+ return $shipment;
+}
+
+/**
+ * @param Order $order_shipment
+ * @param array $args
+ *
+ * @return Shipment|WP_Error
+ */
+function wc_gzd_create_shipment( $order_shipment, $args = array() ) {
+ try {
+ if ( ! $order_shipment || ! is_a( $order_shipment, 'Vendidero\Germanized\Shipments\Order' ) ) {
+ throw new Exception( _x( 'Invalid shipment order', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ if ( ! $order = $order_shipment->get_order() ) {
+ throw new Exception( _x( 'Invalid shipment order', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ $args = wp_parse_args(
+ $args,
+ array(
+ 'items' => array(),
+ 'props' => array(),
+ )
+ );
+
+ $shipment = ShipmentFactory::get_shipment( false, 'simple' );
+
+ if ( ! $shipment ) {
+ throw new Exception( _x( 'Error while creating the shipment instance', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ $shipment->set_order_shipment( $order_shipment );
+ $shipment->sync( $args['props'] );
+ $shipment->sync_items( $args );
+ $shipment->save();
+ } catch ( Exception $e ) {
+ return new WP_Error( 'error', $e->getMessage() );
+ }
+
+ return $shipment;
+}
+
+function wc_gzd_create_shipment_item( $shipment, $order_item, $args = array() ) {
+ try {
+ if ( ! $order_item || ! is_a( $order_item, 'WC_Order_Item' ) ) {
+ throw new Exception( _x( 'Invalid order item', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ $item = new Vendidero\Germanized\Shipments\ShipmentItem();
+
+ $item->set_order_item_id( $order_item->get_id() );
+ $item->set_shipment( $shipment );
+ $item->sync( $args );
+
+ if ( $shipment->get_id() > 0 ) {
+ $item->save();
+ }
+ } catch ( Exception $e ) {
+ return new WP_Error( 'error', $e->getMessage() );
+ }
+
+ return $item;
+}
+
+function wc_gzd_allow_customer_return_empty_return_reason( $order ) {
+ return apply_filters( 'woocommerce_gzd_allow_customer_return_empty_return_reason', true, $order );
+}
+
+/**
+ * @param bool $allow_none
+ * @param bool|WC_Order_Item $order_item
+ *
+ * @return ReturnReason[]
+ */
+function wc_gzd_get_return_shipment_reasons( $order_item = false ) {
+ $reasons = Package::get_setting( 'return_reasons' );
+
+ if ( ! is_array( $reasons ) ) {
+ $reasons = array();
+ } else {
+ $reasons = array_filter( $reasons );
+ }
+
+ /**
+ * Filter that allows adjusting raw return reasons for a specific shipment (e.g. array containing reason data with code, reason and order).
+ *
+ * @param array $reasons Available return reasons.
+ * @param WC_Order_Item|false $order_item The order item object if available to further filter reasons.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ $reasons = apply_filters( 'woocommerce_gzd_return_shipment_reasons_raw', $reasons, $order_item );
+ $instances = array();
+
+ foreach ( $reasons as $reason ) {
+ $instances[] = new ReturnReason( $reason );
+ }
+
+ usort( $instances, '_wc_gzd_sort_return_shipment_reasons' );
+
+ /**
+ * Filter that allows to adjust available return reasons for a specific shipment.
+ *
+ * @param ReturnReason[] $reasons Available return reasons.
+ * @param WC_Order_Item|false $order_item The order item object if available to further filter reasons.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_return_shipment_reasons', $instances, $order_item );
+}
+
+function wc_gzd_return_shipment_reason_exists( $maybe_reason, $shipment = false ) {
+ $reasons = wc_gzd_get_return_shipment_reasons( $shipment );
+ $exists = false;
+
+ foreach ( $reasons as $reason ) {
+
+ if ( $reason->get_code() === $maybe_reason ) {
+ $exists = true;
+ break;
+ }
+ }
+
+ return $exists;
+}
+
+/**
+ * @param ReturnReason $a
+ * @param ReturnReason $b
+ */
+function _wc_gzd_sort_return_shipment_reasons( $a, $b ) {
+ if ( $a->get_order() === $b->get_order() ) {
+ return 0;
+ } elseif ( $a->get_order() > $b->get_order() ) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+/**
+ * @param WP_Error $error
+ *
+ * @return bool
+ */
+function wc_gzd_shipment_wp_error_has_errors( $error ) {
+ if ( is_callable( array( $error, 'has_errors' ) ) ) {
+ return $error->has_errors();
+ } else {
+ $errors = $error->errors;
+
+ return ( ! empty( $errors ) ? true : false );
+ }
+}
+
+/**
+ * @param Shipment $shipment
+ * @param ShipmentItem $shipment_item
+ * @param array $args
+ *
+ * @return ShipmentReturnItem|WP_Error
+ */
+function wc_gzd_create_return_shipment_item( $shipment, $shipment_item, $args = array() ) {
+ try {
+ if ( ! $shipment_item || ! is_a( $shipment_item, '\Vendidero\Germanized\Shipments\ShipmentItem' ) ) {
+ throw new Exception( _x( 'Invalid shipment item', 'shipments', 'woocommerce-germanized' ) );
+ }
+
+ $item = new Vendidero\Germanized\Shipments\ShipmentReturnItem();
+ $item->set_order_item_id( $shipment_item->get_order_item_id() );
+ $item->set_shipment( $shipment );
+ $item->sync( $args );
+ $item->save();
+ } catch ( Exception $e ) {
+ return new WP_Error( 'error', $e->getMessage() );
+ }
+
+ return $item;
+}
+
+function wc_gzd_get_shipment_editable_statuses() {
+ /**
+ * Filter that allows to adjust Shipment statuses which decide upon whether
+ * a Shipment is editable or not.
+ *
+ * @param array $statuses Statuses which should be considered as editable.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipment_editable_statuses', array( 'draft', 'requested', 'processing' ) );
+}
+
+/**
+ * @param Shipment $shipment
+ */
+function wc_gzd_get_shipment_address_addition( $shipment ) {
+ $addition = $shipment->get_address_2();
+ $street_addition = $shipment->get_address_street_addition();
+
+ if ( ! empty( $street_addition ) ) {
+ $addition = $street_addition . ( ! empty( $addition ) ? ' ' . $addition : '' );
+ }
+
+ return trim( $addition );
+}
+
+function wc_gzd_split_shipment_street( $street_str ) {
+ $return = array(
+ 'street' => $street_str,
+ 'number' => '',
+ 'addition' => '',
+ 'addition_2' => '',
+ );
+
+ try {
+ $split = AddressSplitter::split_address( $street_str );
+
+ $return['street'] = $split['streetName'];
+ $return['number'] = $split['houseNumber'];
+ /**
+ * e.g. 5. OG
+ */
+ $return['addition'] = isset( $split['additionToAddress2'] ) ? $split['additionToAddress2'] : '';
+ /**
+ * E.g. details to the location prefixed to the street name
+ */
+ $return['addition_2'] = isset( $split['additionToAddress1'] ) ? $split['additionToAddress1'] : '';
+ } catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
+ }
+
+ return $return;
+}
+
+/**
+ * @return ShippingProvider\Auto[]|ShippingProvider\Simple[]
+ */
+function wc_gzd_get_shipping_providers() {
+ return ShippingProvider\Helper::instance()->get_shipping_providers();
+}
+
+function wc_gzd_get_available_shipping_providers() {
+ return ShippingProvider\Helper::instance()->get_available_shipping_providers();
+}
+
+function wc_gzd_get_shipping_provider( $name ) {
+ return ShippingProvider\Helper::instance()->get_shipping_provider( $name );
+}
+
+function wc_gzd_get_default_shipping_provider() {
+ $default = Package::get_setting( 'default_shipping_provider' );
+
+ /**
+ * Filter to adjust the default shipping provider used as a fallback for shipments
+ * for which no provider could be determined automatically (e.g. by the chosen shipping methid).
+ *
+ * @param string $title The shipping provider slug.
+ *
+ * @since 3.0.6
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_default_shipping_provider', $default );
+}
+
+function wc_gzd_get_shipping_provider_select( $include_none = true ) {
+ $providers = wc_gzd_get_shipping_providers();
+ $select = $include_none ? array(
+ '' => _x( 'None', 'shipments', 'woocommerce-germanized' ),
+ ) : array();
+
+ foreach ( $providers as $provider ) {
+ if ( ! $provider->is_activated() ) {
+ continue;
+ }
+ $select[ $provider->get_name() ] = $provider->get_title();
+ }
+
+ return $select;
+}
+
+function wc_gzd_get_shipping_provider_title( $slug ) {
+ $providers = wc_gzd_get_shipping_providers();
+
+ if ( array_key_exists( $slug, $providers ) ) {
+ $title = $providers[ $slug ]->get_title();
+ } else {
+ $title = $slug;
+ }
+
+ /**
+ * Filter to adjust the title of a certain shipping provider e.g. DHL.
+ *
+ * @param string $title The shipping provider title.
+ * @param string $slug The shipping provider slug.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipping_provider_title', $title, $slug );
+}
+
+/**
+ * @param Shipment $shipment
+ */
+function wc_gzd_get_shipment_shipping_provider_title( $shipment ) {
+ $title = $shipment->get_shipping_provider_title();
+
+ if ( empty( $title ) ) {
+ $title = apply_filters( 'woocommerce_gzd_shipping_provider_unknown_title', _x( 'Unknown', 'shipments-shipping-provider', 'woocommerce-germanized' ) );
+ }
+
+ return $title;
+}
+
+function wc_gzd_get_shipping_provider_service_locations() {
+ return array( 'settings', 'shipping_provider_settings', 'shipping_method_settings', 'packaging_settings', 'labels', 'label_services' );
+}
+
+function wc_gzd_get_shipping_provider_slug( $provider ) {
+ $providers = wc_gzd_get_shipping_providers();
+
+ if ( in_array( $provider, $providers, true ) ) {
+ $slug = array_search( $provider, $providers, true );
+ } elseif ( array_key_exists( $provider, $providers ) ) {
+ $slug = $provider;
+ } else {
+ $slug = sanitize_key( $provider );
+ }
+
+ return $slug;
+}
+
+function _wc_gzd_shipments_keep_force_filename( $new_filename ) {
+ return isset( $GLOBALS['gzd_shipments_unique_filename'] ) ? $GLOBALS['gzd_shipments_unique_filename'] : $new_filename;
+}
+
+function wc_gzd_shipments_upload_data( $filename, $bits, $relative = true ) {
+ try {
+ Package::set_upload_dir_filter();
+ $GLOBALS['gzd_shipments_unique_filename'] = $filename;
+ add_filter( 'wp_unique_filename', '_wc_gzd_shipments_keep_force_filename', 10, 1 );
+
+ $tmp = wp_upload_bits( $filename, null, $bits );
+
+ unset( $GLOBALS['gzd_shipments_unique_filename'] );
+ remove_filter( 'wp_unique_filename', '_wc_gzd_shipments_keep_force_filename', 10 );
+ Package::unset_upload_dir_filter();
+
+ if ( isset( $tmp['file'] ) ) {
+ $path = $tmp['file'];
+
+ if ( $relative ) {
+ $path = Package::get_relative_upload_dir( $path );
+ }
+
+ return $path;
+ } else {
+ throw new Exception( _x( 'Error while uploading file.', 'shipments', 'woocommerce-germanized' ) );
+ }
+ } catch ( Exception $e ) {
+ return false;
+ }
+}
+
+function wc_gzd_get_shipment_setting_default_address_fields( $type = 'shipper' ) {
+ $address_fields = array(
+ 'first_name' => _x( 'First Name', 'shipments', 'woocommerce-germanized' ),
+ 'last_name' => _x( 'Last Name', 'shipments', 'woocommerce-germanized' ),
+ 'full_name' => _x( 'Full Name', 'shipments', 'woocommerce-germanized' ),
+ 'company' => _x( 'Company', 'shipments', 'woocommerce-germanized' ),
+ 'address_1' => _x( 'Address 1', 'shipments', 'woocommerce-germanized' ),
+ 'address_2' => _x( 'Address 2', 'shipments', 'woocommerce-germanized' ),
+ 'street' => _x( 'Street', 'shipments', 'woocommerce-germanized' ),
+ 'street_number' => _x( 'House Number', 'shipments', 'woocommerce-germanized' ),
+ 'postcode' => _x( 'Postcode', 'shipments', 'woocommerce-germanized' ),
+ 'city' => _x( 'City', 'shipments', 'woocommerce-germanized' ),
+ 'country' => _x( 'Country', 'shipments', 'woocommerce-germanized' ),
+ 'state' => _x( 'State', 'shipments', 'woocommerce-germanized' ),
+ 'phone' => _x( 'Phone', 'shipments', 'woocommerce-germanized' ),
+ 'email' => _x( 'Email', 'shipments', 'woocommerce-germanized' ),
+ 'customs_reference_number' => _x( 'Customs Reference Number', 'shipments', 'woocommerce-germanized' ),
+ 'customs_uk_vat_id' => _x( 'UK VAT ID (HMRC)', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ return apply_filters( 'woocommerce_gzd_shipment_default_address_fields', $address_fields, $type );
+}
+
+/**
+ * @return array
+ */
+function wc_gzd_get_shipment_setting_address_fields( $address_type = 'shipper' ) {
+ $default_address_fields = array_keys( wc_gzd_get_shipment_setting_default_address_fields( $address_type ) );
+
+ if ( 'return' === $address_type ) {
+ $default_address_data = wc_gzd_get_shipment_setting_address_fields( 'shipper' );
+
+ if ( 'no' === Package::get_setting( 'use_alternate_return' ) ) {
+ return apply_filters( "woocommerce_gzd_shipment_{$address_type}_address_fields", $default_address_data, $address_type );
+ }
+
+ $default_address_data['country'] = $default_address_data['country'] . ':' . $default_address_data['state'];
+ } else {
+ $default_address_data = array(
+ 'company' => get_option( 'blogname', '' ),
+ 'address_1' => get_option( 'woocommerce_store_address', '' ),
+ 'address_2' => get_option( 'woocommerce_store_address_2', '' ),
+ 'city' => get_option( 'woocommerce_store_city', '' ),
+ 'postcode' => get_option( 'woocommerce_store_postcode', '' ),
+ 'email' => get_option( 'woocommerce_email_from_address', '' ),
+ 'country' => get_option( 'woocommerce_default_country', '' ),
+ );
+ }
+
+ foreach ( $default_address_fields as $prop ) {
+ $key = "woocommerce_gzd_shipments_{$address_type}_address_{$prop}";
+ $value = get_option( $key, null );
+
+ if ( null === $value ) {
+ if ( array_key_exists( $prop, $default_address_data ) && ! in_array( $prop, array( 'state' ), true ) ) {
+ $value = $default_address_data[ $prop ];
+ } else {
+ $value = '';
+ }
+ }
+
+ $address_fields[ $prop ] = $value;
+ }
+
+ if ( ! empty( $address_fields['country'] ) && strlen( $address_fields['country'] ) > 2 ) {
+ $value = wc_format_country_state_string( $address_fields['country'] );
+ $address_fields['country'] = $value['country'];
+ $address_fields['state'] = $value['state'];
+ }
+
+ /**
+ * Format/split address 1 into street and house number
+ */
+ if ( ! empty( $address_fields['address_1'] ) ) {
+ $split = wc_gzd_split_shipment_street( $address_fields['address_1'] );
+
+ $address_fields['street'] = $split['street'];
+ $address_fields['street_number'] = $split['number'];
+ } else {
+ $address_fields['street'] = '';
+ $address_fields['street_number'] = '';
+ }
+
+ /**
+ * Attach formatted full name
+ */
+ $address_fields['full_name'] = trim( sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce-germanized' ), $address_fields['first_name'], $address_fields['last_name'] ) );
+
+ return apply_filters( "woocommerce_gzd_shipment_{$address_type}_address_fields", $address_fields, $address_type );
+}
+
+/**
+ * @param Order $shipment_order
+ *
+ * @return array
+ */
+function wc_gzd_get_shipment_return_address( $shipment_order = false ) {
+ return wc_gzd_get_shipment_setting_address_fields( 'return' );
+}
+
+/**
+ * @param WC_Order $order
+ * @return WC_Order_Item_Shipping|false
+ */
+function wc_gzd_get_shipment_order_shipping_method( $order ) {
+ $methods = $order->get_shipping_methods();
+ $method = false;
+
+ if ( ! empty( $methods ) ) {
+ $method_data = array_values( $methods );
+ $method = array_shift( $method_data );
+
+ if ( ! $method ) {
+ $method = false;
+ }
+ }
+
+ /**
+ * Allows adjusting the shipping method for a certain order.
+ *
+ * @param WC_Order_Item_Shipping|false $method The order item.
+ * @param WC_Order $order The order object.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipment_order_shipping_method', $method, $order );
+}
+
+/**
+ * @param WC_Order $order
+ */
+function wc_gzd_get_shipment_order_shipping_method_id( $order ) {
+ $id = '';
+
+ if ( $method = wc_gzd_get_shipment_order_shipping_method( $order ) ) {
+ $id = $method->get_method_id() . ':' . $method->get_instance_id();
+ }
+
+ /**
+ * Allows adjusting the shipping method id for a certain Order.
+ *
+ * @param string $id The shipping method id.
+ * @param WC_Order $order The order object.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipment_order_shipping_method_id', $id, $order );
+}
+
+function wc_gzd_render_shipment_action_buttons( $actions ) {
+ $actions_html = '';
+
+ foreach ( $actions as $action ) {
+ $action = wp_parse_args(
+ $action,
+ array(
+ 'url' => '#',
+ 'group' => '',
+ 'action' => '',
+ 'target' => '_self',
+ 'classes' => '',
+ 'name' => '',
+ 'custom_attributes' => array(),
+ 'title' => '',
+ )
+ );
+
+ if ( ! empty( $action['group'] ) ) {
+ $actions_html .= ' ';
+ } elseif ( isset( $action['action'], $action['url'], $action['name'] ) ) {
+ $classes = 'button wc-gzd-shipment-action-button tip wc-gzd-shipment-action-button-' . $action['action'] . ' ' . $action['action'] . ' ' . $action['classes'];
+
+ if ( empty( $action['title'] ) ) {
+ $action['title'] = $action['name'];
+ }
+
+ $custom_attributes = '';
+
+ foreach ( $action['custom_attributes'] as $attribute => $val ) {
+ $custom_attributes .= ' ' . esc_attr( $attribute ) . '="' . esc_attr( $val ) . '"';
+ }
+
+ $actions_html .= sprintf( '%6$s', esc_attr( $classes ), esc_url( $action['url'] ), esc_attr( $action['title'] ), esc_attr( $action['target'] ), $custom_attributes, esc_html( $action['name'] ) );
+ }
+ }
+
+ return $actions_html;
+}
+
+function wc_gzd_get_shipment_status_name( $status ) {
+ if ( 'gzd-' !== substr( $status, 0, 4 ) ) {
+ $status = 'gzd-' . $status;
+ }
+
+ $status_name = '';
+ $statuses = wc_gzd_get_shipment_statuses();
+
+ if ( array_key_exists( $status, $statuses ) ) {
+ $status_name = $statuses[ $status ];
+ }
+
+ /**
+ * Filter to adjust the shipment status name or title.
+ *
+ * @param string $status_name The status name or title.
+ * @param integer $status The status slug.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipment_status_name', $status_name, $status );
+}
+
+function wc_gzd_get_shipment_sent_statuses() {
+ /**
+ * Filter to adjust which Shipment statuses should be considered as sent.
+ *
+ * @param array $statuses An array of statuses considered as shipped,
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters(
+ 'woocommerce_gzd_shipment_sent_statuses',
+ array(
+ 'shipped',
+ 'delivered',
+ )
+ );
+}
+
+function wc_gzd_get_shipment_counts( $type = '' ) {
+ $counts = array();
+
+ foreach ( array_keys( wc_gzd_get_shipment_statuses() ) as $status ) {
+ $counts[ $status ] = wc_gzd_get_shipment_count( $status, $type );
+ }
+
+ return $counts;
+}
+
+function wc_gzd_get_shipment_count( $status, $type = '' ) {
+ $count = 0;
+ $status = ( substr( $status, 0, 4 ) ) === 'gzd-' ? $status : 'gzd-' . $status;
+ $shipment_statuses = array_keys( wc_gzd_get_shipment_statuses() );
+
+ if ( ! in_array( $status, $shipment_statuses, true ) ) {
+ return 0;
+ }
+
+ $cache_key = WC_Cache_Helper::get_cache_prefix( 'shipments' ) . $status . $type;
+ $cached_count = wp_cache_get( $cache_key, 'counts' );
+
+ if ( false !== $cached_count ) {
+ return $cached_count;
+ }
+
+ $data_store = WC_Data_Store::load( 'shipment' );
+
+ if ( $data_store ) {
+ $count += $data_store->get_shipment_count( $status, $type );
+ }
+
+ wp_cache_set( $cache_key, $count, 'counts' );
+
+ return $count;
+}
+
+/**
+ * See if a string is a shipment status.
+ *
+ * @param string $maybe_status Status, including any gzd- prefix.
+ * @return bool
+ */
+function wc_gzd_is_shipment_status( $maybe_status ) {
+ $shipment_statuses = wc_gzd_get_shipment_statuses();
+
+ return isset( $shipment_statuses[ $maybe_status ] );
+}
+
+/**
+ * Main function for returning shipment items.
+ *
+ * @since 2.2
+ *
+ * @param mixed $the_item Object or shipment item id.
+ * @param string $item_type The shipment item type.
+ *
+ * @return bool|ShipmentItem
+ */
+function wc_gzd_get_shipment_item( $the_item = false, $item_type = 'simple' ) {
+ $item_id = wc_gzd_get_shipment_item_id( $the_item );
+
+ if ( ! $item_id ) {
+ return false;
+ }
+
+ $item_class = 'Vendidero\Germanized\Shipments\ShipmentItem';
+
+ if ( 'return' === $item_type ) {
+ $item_class = 'Vendidero\Germanized\Shipments\ShipmentReturnItem';
+ }
+
+ /**
+ * Filter to adjust the classname used to construct a ShipmentItem.
+ *
+ * @param string $classname The classname to be used.
+ * @param integer $item_id The shipment item id.
+ * @param string $item_type The shipment item type.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ $classname = apply_filters( 'woocommerce_gzd_shipment_item_class', $item_class, $item_id, $item_type );
+
+ if ( ! class_exists( $classname ) ) {
+ return false;
+ }
+
+ try {
+ return new $classname( $item_id );
+ } catch ( Exception $e ) {
+ wc_caught_exception( $e, __FUNCTION__, array( $the_item, $item_type ) );
+ return false;
+ }
+}
+
+/**
+ * Get the shipment item ID depending on what was passed.
+ *
+ * @since 3.0.0
+ * @param mixed $item Item data to convert to an ID.
+ * @return int|bool false on failure
+ */
+function wc_gzd_get_shipment_item_id( $item ) {
+ if ( is_numeric( $item ) ) {
+ return $item;
+ } elseif ( $item instanceof Vendidero\Germanized\Shipments\ShipmentItem ) {
+ return $item->get_id();
+ } elseif ( ! empty( $item->shipment_item_id ) ) {
+ return $item->shipment_item_id;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Format dimensions for display.
+ *
+ * @since 3.0.0
+ * @param array $dimensions Array of dimensions.
+ * @return string
+ */
+function wc_gzd_format_shipment_dimensions( $dimensions, $unit = '' ) {
+ $dimension_string = implode( ' × ', array_filter( array_map( 'wc_format_localized_decimal', $dimensions ) ) );
+
+ if ( ! empty( $dimension_string ) ) {
+ $unit = empty( $unit ) ? get_option( 'woocommerce_dimension_unit' ) : $unit;
+ $dimension_string .= ' ' . $unit;
+ } else {
+ $dimension_string = _x( 'N/A', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Filter to adjust the format of Shipment dimensions e.g. LxBxH.
+ *
+ * @param string $dimension_string The dimension string.
+ * @param array $dimensions Array containing the dimensions.
+ * @param string $unit The dimension unit.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_format_shipment_dimensions', $dimension_string, $dimensions, $unit );
+}
+
+/**
+ * Format a weight for display.
+ *
+ * @since 3.0.0
+ * @param float $weight Weight.
+ * @return string
+ */
+function wc_gzd_format_shipment_weight( $weight, $unit = '' ) {
+ $weight_string = wc_format_localized_decimal( $weight );
+
+ if ( ! empty( $weight_string ) ) {
+ $unit = empty( $unit ) ? get_option( 'woocommerce_weight_unit' ) : $unit;
+ $weight_string .= ' ' . $unit;
+ } else {
+ $weight_string = _x( 'N/A', 'shipments', 'woocommerce-germanized' );
+ }
+
+ /**
+ * Filter to adjust the format of Shipment weight.
+ *
+ * @param string $weight_string The weight string.
+ * @param string $weight The Shipment weight.
+ * @param string $unit The dimension unit.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_format_shipment_weight', $weight_string, $weight, $unit );
+}
+
+/**
+ * Get My Account > Shipments columns.
+ *
+ * @since 3.0.0
+ * @return array
+ */
+function wc_gzd_get_account_shipments_columns( $type = 'simple' ) {
+ /**
+ * Filter to adjust columns being used to display shipments in a table view on the customer
+ * account page.
+ *
+ * @param string[] $columns The columns in key => value pairs.
+ * @param string $type The shipment type e.g. simple or return.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ $columns = apply_filters(
+ 'woocommerce_gzd_account_shipments_columns',
+ array(
+ 'shipment-number' => _x( 'Shipment', 'shipments', 'woocommerce-germanized' ),
+ 'shipment-date' => _x( 'Date', 'shipments', 'woocommerce-germanized' ),
+ 'shipment-status' => _x( 'Status', 'shipments', 'woocommerce-germanized' ),
+ 'shipment-tracking' => _x( 'Tracking', 'shipments', 'woocommerce-germanized' ),
+ 'shipment-actions' => _x( 'Actions', 'shipments', 'woocommerce-germanized' ),
+ ),
+ $type
+ );
+
+ if ( ! is_user_logged_in() ) {
+ $columns = array_diff_key( $columns, array( 'shipment-actions' => '' ) );
+ }
+
+ return $columns;
+}
+
+function wc_gzd_get_order_customer_add_return_url( $order ) {
+ if ( ! $shipment_order = wc_gzd_get_shipment_order( $order ) ) {
+ return false;
+ }
+
+ $url = wc_get_endpoint_url( 'add-return-shipment', $shipment_order->get_order()->get_id(), wc_get_page_permalink( 'myaccount' ) );
+
+ if ( $shipment_order->get_order()->get_customer_id() <= 0 ) {
+ $key = $shipment_order->get_order_return_request_key();
+
+ if ( ! empty( $key ) ) {
+ $url = add_query_arg( array( 'key' => $key ), $url );
+ } else {
+ $url = '';
+ }
+ }
+
+ /**
+ * Filter to adjust the URL the customer (or guest) might access to add a return to a certain order.
+ *
+ * @param string $url The URL pointing to the add return page.
+ * @param Order $order The order object.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipments_add_return_shipment_url', $url, $shipment_order->get_order() );
+}
+
+/**
+ * @param WC_Order $order
+ *
+ * @return mixed
+ */
+function wc_gzd_order_is_customer_returnable( $order, $check_date = true ) {
+ $is_returnable = false;
+
+ if ( ! $shipment_order = wc_gzd_get_shipment_order( $order ) ) {
+ return false;
+ }
+
+ if ( $provider = wc_gzd_get_order_shipping_provider( $order ) ) {
+ $is_returnable = $provider->supports_customer_returns( $shipment_order->get_order() );
+
+ if ( $shipment_order->get_order()->get_customer_id() <= 0 && ! $provider->supports_guest_returns() ) {
+ $is_returnable = false;
+ }
+ }
+
+ // Shipment is fully returned
+ if ( ! $shipment_order->needs_return() ) {
+ $is_returnable = false;
+ }
+
+ // Check days left for return
+ $maximum_days = Package::get_setting( 'customer_return_open_days' );
+
+ if ( $check_date && ! empty( $maximum_days ) ) {
+ $maximum_days = absint( $maximum_days );
+
+ if ( ! empty( $maximum_days ) ) {
+
+ $completed_date = $shipment_order->get_order()->get_date_created();
+
+ if ( $shipment_order->get_date_shipped() ) {
+ $completed_date = $shipment_order->get_date_shipped();
+ } elseif ( $shipment_order->get_order()->get_date_completed() ) {
+ $completed_date = $shipment_order->get_order()->get_date_completed();
+ }
+
+ /**
+ * Filter to adjust the completed date of an order used to determine whether an order is
+ * still returnable by the customer or not. The date is constructed by checking for existence in the following order:
+ *
+ * 1. The date the order was shipped completely
+ * 2. The date the order was marked as completed
+ * 3. The date the order was created
+ *
+ * @param WC_DateTime $completed_date The order completed date.
+ * @param WC_Order $order The order instance.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ $completed_date = apply_filters( 'woocommerce_gzd_order_return_completed_date', $completed_date, $shipment_order->get_order() );
+
+ if ( $completed_date ) {
+ $today = new WC_DateTime();
+ $diff = $today->diff( $completed_date );
+
+ if ( $diff->days > $maximum_days ) {
+ $is_returnable = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Filter to decide whether a customer might add return request to a certain order.
+ *
+ * @param bool $is_returnable Whether or not shipment supports customer added returns
+ * @param WC_Order $order The order instance for which the return shall be created.
+ * @param bool $check_date Whether to check for a maximum date or not.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_order_is_returnable_by_customer', $is_returnable, $shipment_order->get_order(), $check_date );
+}
+
+/**
+ * @param $order
+ *
+ * @return bool|\Vendidero\Germanized\Shipments\Interfaces\ShippingProvider
+ */
+function wc_gzd_get_order_shipping_provider( $order ) {
+ if ( is_numeric( $order ) ) {
+ $order = wc_get_order( $order );
+ } elseif ( is_a( $order, '\Vendidero\Germanized\Shipments\Order' ) ) {
+ $order = $order->get_order();
+ }
+
+ if ( ! $order ) {
+ return false;
+ }
+
+ $provider = false;
+
+ foreach ( array_reverse( wc_gzd_get_shipment_order( $order )->get_shipments() ) as $shipment ) {
+ if ( $shipment->get_shipping_provider_instance() ) {
+ $provider = $shipment->get_shipping_provider_instance();
+ break;
+ }
+ }
+
+ if ( ! $provider ) {
+ $method_id = wc_gzd_get_shipment_order_shipping_method_id( $order );
+
+ if ( $method = wc_gzd_get_shipping_provider_method( $method_id ) ) {
+ $provider = $method->get_shipping_provider_instance();
+ }
+ }
+
+ /**
+ * Filters the shipping provider detected for a specific order.
+ *
+ * @param bool|\Vendidero\Germanized\Shipments\Interfaces\ShippingProvider $provider The shipping provider instance.
+ * @param WC_Order $order The order instance.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_get_order_shipping_provider', $provider, $order );
+}
+
+function wc_gzd_get_customer_order_return_request_key() {
+ $key = ( isset( $_REQUEST['key'] ) ? wc_clean( wp_unslash( $_REQUEST['key'] ) ) : '' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+ return $key;
+}
+
+function wc_gzd_shipments_additional_costs_include_tax() {
+ return apply_filters( 'woocommerce_gzd_shipments_additional_costs_include_tax', false );
+}
+
+function wc_gzd_customer_can_add_return_shipment( $order_id ) {
+ $can_view_shipments = false;
+
+ if ( isset( $_REQUEST['key'] ) && ! empty( $_REQUEST['key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $key = wc_gzd_get_customer_order_return_request_key();
+
+ if ( ( $order_shipment = wc_gzd_get_shipment_order( $order_id ) ) && ! empty( $key ) ) {
+
+ if ( hash_equals( $order_shipment->get_order_return_request_key(), $key ) ) {
+ $can_view_shipments = true;
+ }
+ }
+ } elseif ( is_user_logged_in() ) {
+ $can_view_shipments = current_user_can( 'view_order', $order_id );
+ }
+
+ /**
+ * Filters whether a logged in user (or guest) might view shipments belonging to an order or not.
+ *
+ * @param bool $can_view_shipments Whether the user (or guest) might see shipments or not.
+ * @param integer $order_id The order id.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_customer_can_view_shipments', $can_view_shipments, $order_id );
+}
+
+/**
+ * @param WC_Order|integer $order
+ */
+function wc_gzd_customer_return_needs_manual_confirmation( $order ) {
+ if ( is_numeric( $order ) ) {
+ $order = wc_get_order( $order );
+ }
+
+ if ( ! $order ) {
+ return true;
+ }
+
+ $needs_manual_confirmation = true;
+
+ if ( $provider = wc_gzd_get_order_shipping_provider( $order ) ) {
+ $needs_manual_confirmation = $provider->needs_manual_confirmation_for_returns();
+ }
+
+ /**
+ * Filter to decide whether a customer added return of a certain order
+ * needs manual confirmation by the shop manager or not.
+ *
+ * @param bool $needs_manual_confirmation Whether needs manual confirmation or not.
+ * @param WC_Order $order The order instance for which the return shall be created.
+ *
+ * @since 3.1.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_customer_return_needs_manual_confirmation', $needs_manual_confirmation, $order );
+}
+
+/**
+ * Get account shipments actions.
+ *
+ * @since 3.2.0
+ * @param int|Shipment $shipment Shipment instance or ID.
+ * @return array
+ */
+function wc_gzd_get_account_shipments_actions( $shipment ) {
+ if ( ! is_object( $shipment ) ) {
+ $shipment_id = absint( $shipment );
+ $shipment = wc_gzd_get_shipment( $shipment_id );
+ }
+
+ if ( ! $shipment ) {
+ return array();
+ }
+
+ $actions = array(
+ 'view' => array(
+ 'url' => $shipment->get_view_shipment_url(),
+ 'name' => _x( 'View', 'shipments', 'woocommerce-germanized' ),
+ ),
+ );
+
+ if ( 'return' === $shipment->get_type() && $shipment->has_label() && ! $shipment->has_status( 'delivered' ) ) {
+ $actions['download-label'] = array(
+ 'url' => $shipment->get_label()->get_download_url(),
+ 'name' => _x( 'Download label', 'shipments', 'woocommerce-germanized' ),
+ );
+ }
+
+ /**
+ * Filter to adjust available actions in the shipments table view on the customer account page
+ * for a specific shipment.
+ *
+ * @param string[] $actions Available actions containing an id as key and a URL and name.
+ * @param Shipment $shipment The shipment instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_account_shipments_actions', $actions, $shipment );
+}
+
+function wc_gzd_shipments_get_product( $the_product ) {
+ try {
+ if ( is_a( $the_product, '\Vendidero\Germanized\Shipments\Product' ) ) {
+ return $the_product;
+ }
+
+ return new \Vendidero\Germanized\Shipments\Product( $the_product );
+ } catch ( \Exception $e ) {
+ return false;
+ }
+}
+
+function wc_gzd_get_volume_dimension( $dimension, $to_unit, $from_unit = '' ) {
+ $to_unit = strtolower( $to_unit );
+
+ if ( empty( $from_unit ) ) {
+ $from_unit = strtolower( get_option( 'woocommerce_dimension_unit' ) );
+ }
+
+ // Unify all units to cm first.
+ if ( $from_unit !== $to_unit ) {
+ switch ( $from_unit ) {
+ case 'm':
+ $dimension *= 1000000;
+ break;
+ case 'mm':
+ $dimension *= 0.001;
+ break;
+ }
+
+ // Output desired unit.
+ switch ( $to_unit ) {
+ case 'm':
+ $dimension *= 0.000001;
+ break;
+ case 'mm':
+ $dimension *= 1000;
+ break;
+ }
+ }
+
+ return ( $dimension < 0 ) ? 0 : $dimension;
+}
+
+if ( ! function_exists( 'wc_gzd_wp_theme_get_element_class_name' ) ) {
+ /**
+ * Given an element name, returns a class name.
+ *
+ * If the WP-related function is not defined, return empty string.
+ *
+ * @param string $element The name of the element.
+ *
+ * @return string
+ */
+ function wc_gzd_wp_theme_get_element_class_name( $element ) {
+ if ( function_exists( 'wc_wp_theme_get_element_class_name' ) ) {
+ return wc_wp_theme_get_element_class_name( $element );
+ } elseif ( function_exists( 'wp_theme_get_element_class_name' ) ) {
+ return wp_theme_get_element_class_name( $element );
+ }
+
+ return '';
+ }
+}
+
+function wc_gzd_shipments_allow_deferred_sync( $type = 'shipments' ) {
+ $allow_defer = true;
+
+ if ( 'shipments' === $type || 'label' === $type || 'return_label' === $type ) {
+ if ( is_admin() && current_user_can( 'manage_woocommerce' ) ) {
+ $allow_defer = false;
+ }
+ }
+
+ if ( apply_filters( 'woocommerce_gzd_shipments_disable_deferred_sync', false ) ) {
+ $allow_defer = false;
+ }
+
+ return apply_filters( "woocommerce_gzd_shipments_allow_{$type}_deferred_sync", $allow_defer );
+}
+
+/**
+ * Forces a WP_Error object to be converted to a ShipmentError.
+ *
+ * @param $error
+ *
+ * @return mixed|\Vendidero\Germanized\Shipments\ShipmentError
+ */
+function wc_gzd_get_shipment_error( $error ) {
+ if ( ! is_wp_error( $error ) ) {
+ return $error;
+ } elseif ( is_a( $error, 'Vendidero\Germanized\Shipments\ShipmentError' ) ) {
+ return $error;
+ } else {
+ return \Vendidero\Germanized\Shipments\ShipmentError::from_wp_error( $error );
+ }
+}
+
+function wc_gzd_shipments_substring( $str, $start, $length = null ) {
+ if ( function_exists( 'mb_substr' ) ) {
+ $str = mb_substr( $str, $start, $length );
+ } else {
+ $str = substr( $str, $start, $length );
+ }
+
+ return $str;
+}
diff --git a/packages/woocommerce-germanized-shipments/includes/wc-gzd-shipment-template-hooks.php b/packages/woocommerce-germanized-shipments/includes/wc-gzd-shipment-template-hooks.php
new file mode 100644
index 000000000..83cb1d4b7
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/includes/wc-gzd-shipment-template-hooks.php
@@ -0,0 +1,29 @@
+ false,
+ 'show_image' => false,
+ 'image_size' => array( 32, 32 ),
+ 'plain_text' => false,
+ 'sent_to_admin' => false,
+ );
+
+ $args = wp_parse_args( $args, $defaults );
+ $template = $args['plain_text'] ? 'emails/plain/email-shipment-items.php' : 'emails/email-shipment-items.php';
+
+ wc_get_template(
+ $template,
+ /**
+ * Filter to adjust the arguments passed to retrieving ShipmentItems for display in an Email.
+ *
+ * @param array $args Array containing the arguments passed.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ apply_filters(
+ 'woocommerce_gzd_email_shipment_items_args',
+ array(
+ 'shipment' => $shipment,
+ 'items' => $shipment->get_items( 'customer' ),
+ 'show_sku' => $args['show_sku'],
+ 'show_image' => $args['show_image'],
+ 'image_size' => $args['image_size'],
+ 'plain_text' => $args['plain_text'],
+ 'sent_to_admin' => $args['sent_to_admin'],
+ )
+ )
+ );
+
+ /**
+ * Filter that allows adjusting the HTML output of the shipment email item table.
+ *
+ * @param string $html The HTML output.
+ * @param Shipment $shipment The shipment instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_email_shipment_items_table', ob_get_clean(), $shipment );
+ }
+}
+
+if ( ! function_exists( 'woocommerce_gzd_shipments_template_view_shipments' ) ) {
+
+ function woocommerce_gzd_shipments_template_view_shipments( $order_id ) {
+ if ( ! ( $order = wc_get_order( $order_id ) ) ) {
+ echo '">
+
+ '',
+ 'field_name' => '',
+ 'id' => '',
+ 'store_as' => 'separate',
+ )
+ );
+
+ if ( 'dimensions' === $option['type'] ) {
+ $value = wp_parse_args(
+ (array) $value,
+ array(
+ 'length' => 0,
+ 'width' => 0,
+ 'height' => 0,
+ )
+ );
+ $value = wc_clean( $value );
+ $option_name = ! empty( $option['field_name'] ) ? $option['field_name'] : $option['id'];
+
+ if ( 'separate' === $option['store_as'] ) {
+ $option_name = str_replace( 'dimensions', '', $option_name );
+
+ foreach ( $value as $dim => $dim_val ) {
+ update_option( "{$option_name}{$dim}", $dim_val );
+ }
+
+ $value = null;
+ }
+ }
+
+ return $value;
+ }
+
+ public static function toggle_input_field( $value ) {
+ // Description handling.
+ $field_description_data = \WC_Admin_Settings::get_field_description( $value );
+
+ if ( ! isset( $value['value'] ) ) {
+ $value['value'] = \WC_Admin_Settings::get_option( $value['id'], $value['default'] );
+ }
+
+ $option_value = $value['value'];
+
+ if ( ! isset( $value['checkboxgroup'] ) || 'start' === $value['checkboxgroup'] ) {
+ ?>
+
+
+
+
+
+
+
+ '',
+ 'css' => '',
+ 'value' => '',
+ 'class' => '',
+ 'name' => '',
+ 'suffix' => '',
+ 'desc_tip' => false,
+ 'desc' => '',
+ 'custom_attributes' => array(),
+ )
+ );
+ $args['value'] = wc_bool_to_string( $args['value'] );
+ $args['name'] = empty( $args['name'] ) ? $args['id'] : $args['name'];
+ // Description handling.
+ $field_description_data = \WC_Admin_Settings::get_field_description( $args );
+ ?>
+
+
+
+
+ $attribute_value ) {
+ echo esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '" ';
+ }
+ }
+ ?>
+ />
+ id, self::get_core_screen_ids(), true ) ) {
+ $is_connected = true;
+
+ return $is_connected;
+ }
+ }
+
+ return $is_connected;
+ }
+
+ public static function register_admin_breadcrumbs( $breadcrumbs, $current_page ) {
+ if ( ! function_exists( 'wc_admin_get_core_pages_to_connect' ) ) {
+ return $breadcrumbs;
+ }
+
+ if ( false === $current_page ) {
+ $screen = get_current_screen();
+
+ if ( $screen && in_array( $screen->id, self::get_core_screen_ids(), true ) ) {
+ $core_pages = wc_admin_get_core_pages_to_connect();
+
+ if ( 'woocommerce_page_shipment-packaging' === $screen->id ) {
+ $breadcrumbs = array(
+ array(
+ esc_url_raw( add_query_arg( 'page', 'wc-settings', 'admin.php' ) ),
+ $core_pages['wc-settings']['title'],
+ ),
+ _x( 'Edit packaging', 'shipments', 'woocommerce-germanized' ),
+ );
+ } else {
+ $page = isset( $_GET['page'] ) ? wc_clean( wp_unslash( $_GET['page'] ) ) : 'wc-gzd-shipments'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+ if ( 'wc-gzd-shipments' === $page ) {
+ $breadcrumbs = array(
+ _x( 'Shipments', 'shipments', 'woocommerce-germanized' ),
+ );
+ } elseif ( 'wc-gzd-return-shipments' === $page ) {
+ $breadcrumbs = array(
+ _x( 'Returns', 'shipments', 'woocommerce-germanized' ),
+ );
+ } elseif ( 'shipment-packaging-report' === $page ) {
+ $breadcrumbs = array(
+ _x( 'Packaging Report', 'shipments', 'woocommerce-germanized' ),
+ );
+ }
+ }
+ }
+ }
+
+ return $breadcrumbs;
+ }
+
+ public static function add_packaging_page() {
+ add_submenu_page( 'woocommerce', _x( 'Packaging', 'shipments', 'woocommerce-germanized' ), _x( 'Packaging', 'shipments', 'woocommerce-germanized' ), 'manage_woocommerce', 'shipment-packaging', array( __CLASS__, 'render_packaging_page' ) );
+ }
+
+ public static function render_shipping_provider_packaging_zone_title_close_field( $setting ) {
+ echo '';
+ }
+
+ public static function render_shipping_provider_packaging_zone_title_field( $setting ) {
+ $setting = wp_parse_args(
+ $setting,
+ array(
+ 'name' => '',
+ 'value' => 'no',
+ 'class' => '',
+ 'title' => '',
+ )
+ );
+
+ if ( empty( $setting['name'] ) ) {
+ $setting['name'] = $setting['id'];
+ }
+
+ $has_override = wc_string_to_bool( $setting['value'] );
+ ?>
+
+
+
+
+
+
+
+
+ absint( $packaging_id ) );
+
+ if ( ! empty( $provider_name ) ) {
+ $args['provider'] = $provider_name;
+ }
+
+ if ( ! empty( $section ) ) {
+ $args['section'] = $section;
+ }
+
+ return esc_url_raw( add_query_arg( $args, admin_url( 'admin.php?page=shipment-packaging' ) ) );
+ }
+
+ public static function render_packaging_page() {
+ if ( isset( $_GET['packaging'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $packaging_id = isset( $_GET['packaging'] ) ? absint( wp_unslash( $_GET['packaging'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+ if ( ! $packaging_id ) {
+ return;
+ }
+
+ if ( ! $packaging = wc_gzd_get_packaging( $packaging_id ) ) {
+ return;
+ }
+
+ $current_tab = isset( $_GET['tab'] ) ? wc_clean( wp_unslash( $_GET['tab'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $current_section = isset( $_GET['section'] ) ? wc_clean( wp_unslash( $_GET['section'] ) ) : 'simple'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $current_settings = PackagingSettings::get_settings( $packaging, $current_tab, $current_section );
+ ?>
+
+ get_id() !== $post_id ) {
+ $the_order = wc_get_order( $post_id );
+ }
+ }
+
+ if ( $shipment_order = wc_gzd_get_shipment_order( $the_order ) ) {
+ $shipping_status = $shipment_order->get_shipping_status();
+ $status_html = '' . esc_html( wc_gzd_get_shipment_order_shipping_status_name( $shipping_status ) ) . '';
+
+ if ( in_array( $shipping_status, array( 'shipped', 'partially-shipped' ), true ) && $shipment_order->get_shipments() ) {
+ if ( $last_shipment = $shipment_order->get_last_shipment_with_tracking() ) {
+ echo '' . wp_kses_post( $status_html ) . '';
+ } else {
+ echo '' . wp_kses_post( $status_html ) . '';
+ }
+ } else {
+ echo wp_kses_post( $status_html );
+ }
+ }
+ }
+ }
+
+ public static function register_order_shipping_status_column( $columns ) {
+ $new_columns = array();
+ $added_column = false;
+
+ foreach ( $columns as $column_name => $title ) {
+ if ( ! $added_column && ( 'shipping_address' === $column_name || 'wc_actions' === $column_name ) ) {
+ $new_columns['shipping_status'] = _x( 'Shipping Status', 'shipments-order-column-name', 'woocommerce-germanized' );
+ $added_column = true;
+ }
+
+ $new_columns[ $column_name ] = $title;
+ }
+
+ if ( ! $added_column ) {
+ $new_columns['shipping_status'] = _x( 'Shipping Status', 'shipments-order-column-name', 'woocommerce-germanized' );
+ }
+
+ return $new_columns;
+ }
+
+ /**
+ * In case the shipper/return country is set to AF (or DE with missing state) due to a bug in Woo, make sure
+ * to automatically adjust it to the right value in case the base country option is being saved.
+ *
+ * @return void
+ */
+ public static function observe_base_country_setting() {
+ if ( isset( $_POST['woocommerce_default_country'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $new_base_country = wc_format_country_state_string( get_option( 'woocommerce_default_country' ) );
+
+ if ( 'AF' !== $new_base_country['country'] ) {
+ $shipper_country = wc_format_country_state_string( get_option( 'woocommerce_gzd_shipments_shipper_address_country' ) );
+ $return_country = wc_format_country_state_string( get_option( 'woocommerce_gzd_shipments_return_address_country' ) );
+
+ if ( 'AF' === $shipper_country['country'] || ( 'DE' === $new_base_country['country'] && 'DE' === $shipper_country['country'] && empty( $shipper_country['state'] ) && ! empty( $new_base_country['state'] ) ) ) {
+ update_option( 'woocommerce_gzd_shipments_shipper_address_country', get_option( 'woocommerce_default_country' ) );
+ }
+
+ if ( 'AF' === $return_country['country'] || ( 'DE' === $new_base_country['country'] && 'DE' === $return_country['country'] && empty( $return_country['state'] ) && ! empty( $return_country['state'] ) ) ) {
+ update_option( 'woocommerce_gzd_shipments_return_address_country', get_option( 'woocommerce_default_country' ) );
+ }
+ }
+ }
+ }
+
+ /**
+ * @param $loop
+ * @param $variation_data
+ * @param \WP_Post $variation
+ *
+ * @return void
+ */
+ public static function product_variation_options( $loop, $variation_data, $variation ) {
+ if ( ! $variation_object = wc_get_product( $variation ) ) {
+ return;
+ }
+
+ $_parent_product = wc_get_product( $variation_object->get_parent_id() );
+ $shipments_parent_product = wc_gzd_shipments_get_product( $_parent_product );
+
+ if ( wc_product_dimensions_enabled() ) {
+ $shipments_product = wc_gzd_shipments_get_product( $variation_object );
+ $parent_length = $shipments_parent_product ? wc_format_localized_decimal( $shipments_parent_product->get_shipping_length() ) : '';
+ $parent_width = $shipments_parent_product ? wc_format_localized_decimal( $shipments_parent_product->get_shipping_width() ) : '';
+ $parent_height = $shipments_parent_product ? wc_format_localized_decimal( $shipments_parent_product->get_shipping_height() ) : '';
+ ?>
+
+ countries->get_countries();
+ $countries = array_merge( array( '0' => _x( 'Select a country', 'shipments', 'woocommerce-germanized' ) ), $countries );
+ ?>
+
+
+
+ $attribute_value ) {
+ $custom_attributes[] = esc_attr( $attribute ) . '="' . esc_attr( $attribute_value ) . '"';
+ }
+ }
+
+ // Description handling.
+ $field_description = \WC_Admin_Settings::get_field_description( $value );
+ $description = $field_description['description'];
+ $tooltip_html = $field_description['tooltip_html'];
+
+ $country_setting = (string) $value['value'];
+
+ if ( strstr( $country_setting, ':' ) ) {
+ $country_setting = explode( ':', $country_setting );
+ $country = current( $country_setting );
+ $state = end( $country_setting );
+ } else {
+ $country = $country_setting;
+ $state = '*';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_title() ); ?> get_status() ) ); ?>
+
+ get_date_start()->date_i18n( wc_date_format() );
+
+ printf(
+ '',
+ esc_attr( $report->get_date_start()->date( 'c' ) ),
+ esc_html( $report->get_date_start()->date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ),
+ esc_html( $show_date )
+ );
+ ?>
+
+
+ get_date_end()->date_i18n( wc_date_format() );
+
+ printf(
+ '',
+ esc_attr( $report->get_date_end()->date( 'c' ) ),
+ esc_html( $report->get_date_end()->date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ),
+ esc_html( $show_date )
+ );
+ ?>
+
+
+ get_total_weight(), wc_gzd_get_packaging_weight_unit() ) ); ?>
+
+
+ get_total_count() ); ?>
+
+
+
+ 'shop_order',
+ 'bulk_action' => $report_action,
+ 'changed' => $changed,
+ 'ids' => join( ',', $ids ),
+ );
+
+ if ( Package::is_hpos_enabled() ) {
+ unset( $redirect_query_args['post_type'] );
+ $redirect_query_args['page'] = 'wc-orders';
+ }
+
+ $redirect_to = add_query_arg(
+ $redirect_query_args,
+ $redirect_to
+ );
+
+ return esc_url_raw( $redirect_to );
+ } else {
+ return $redirect_to;
+ }
+ }
+
+ public static function define_order_bulk_actions( $actions ) {
+ $actions['gzd_create_shipments'] = _x( 'Create shipments', 'shipments', 'woocommerce-germanized' );
+
+ return $actions;
+ }
+
+ public static function set_screen_option( $new_value, $option, $value ) {
+ if ( in_array( $option, array( 'woocommerce_page_wc_gzd_shipments_per_page', 'woocommerce_page_wc_gzd_return_shipments_per_page' ), true ) ) {
+ return absint( $value );
+ }
+
+ return $new_value;
+ }
+
+ public static function shipments_menu() {
+ add_submenu_page( 'woocommerce', _x( 'Shipments', 'shipments', 'woocommerce-germanized' ), _x( 'Shipments', 'shipments', 'woocommerce-germanized' ), 'edit_others_shop_orders', 'wc-gzd-shipments', array( __CLASS__, 'shipments_page' ) );
+ add_submenu_page( 'woocommerce', _x( 'Returns', 'shipments', 'woocommerce-germanized' ), _x( 'Returns', 'shipments', 'woocommerce-germanized' ), 'edit_others_shop_orders', 'wc-gzd-return-shipments', array( __CLASS__, 'returns_page' ) );
+ }
+
+ /**
+ * @param Shipment $shipment
+ */
+ public static function get_shipment_tracking_html( $shipment ) {
+ $tracking_html = '';
+
+ if ( $tracking_id = $shipment->get_tracking_id() ) {
+
+ if ( $tracking_url = $shipment->get_tracking_url() ) {
+ $tracking_html = '' . $tracking_id . '';
+ } else {
+ $tracking_html = '' . $tracking_id . '';
+ }
+ }
+
+ return $tracking_html;
+ }
+
+ /**
+ * @param Table $table
+ */
+ protected static function setup_table( $table ) {
+ global $wp_list_table;
+
+ $wp_list_table = $table; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $doaction = $wp_list_table->current_action();
+
+ if ( $doaction ) {
+ check_admin_referer( 'bulk-shipments' );
+
+ $pagenum = $wp_list_table->get_pagenum();
+ $parent_file = $wp_list_table->get_main_page();
+ $sendback = remove_query_arg( array( 'deleted', 'ids', 'changed', 'bulk_action' ), wp_get_referer() );
+
+ if ( ! $sendback ) {
+ $sendback = admin_url( $parent_file );
+ }
+
+ $sendback = add_query_arg( 'paged', $pagenum, $sendback );
+ $shipment_ids = array();
+
+ if ( isset( $_REQUEST['ids'] ) ) {
+ $shipment_ids = array_map( 'absint', explode( ',', wp_unslash( $_REQUEST['ids'] ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ } elseif ( ! empty( $_REQUEST['shipment'] ) ) {
+ $shipment_ids = array_map( 'absint', wp_unslash( $_REQUEST['shipment'] ) );
+ }
+
+ if ( ! empty( $shipment_ids ) ) {
+ $sendback = $wp_list_table->handle_bulk_actions( $doaction, $shipment_ids, $sendback );
+ }
+
+ $sendback = remove_query_arg( array( 'action', 'action2', '_status', 'bulk_edit', 'shipment' ), $sendback );
+
+ wp_safe_redirect( esc_url_raw( $sendback ) );
+ exit();
+
+ } elseif ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
+ wp_safe_redirect( esc_url_raw( remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated
+ exit;
+ }
+
+ $wp_list_table->set_bulk_notice();
+ $wp_list_table->prepare_items();
+
+ add_screen_option( 'per_page' );
+ }
+
+ public static function setup_shipments_table() {
+ $table = new Table();
+
+ self::setup_table( $table );
+ }
+
+ public static function setup_returns_table() {
+ $table = new ReturnTable( array( 'type' => 'return' ) );
+
+ self::setup_table( $table );
+ }
+
+ public static function shipments_page() {
+ global $wp_list_table;
+
+ ?>
+
+
+
+
+
+
+
+ output_notice();
+ $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'updated', 'changed', 'deleted', 'trashed', 'untrashed' ), ( isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : admin_url( 'admin.php?page=wc-gzd-shipments' ) ) );
+ ?>
+
+ views(); ?>
+
+
+
+
+
+
+
+ output_notice();
+ $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'updated', 'changed', 'deleted', 'trashed', 'untrashed' ), ( isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : admin_url( 'admin.php?page=wc-gzd-shipments' ) ) );
+ ?>
+
+ views(); ?>
+
+
+
+
+
+
#i', ', ', $address ) ) . '';
+ } else {
+ echo '–';
+ }
+ }
+}
diff --git a/packages/woocommerce-germanized-shipments/src/Admin/Settings.php b/packages/woocommerce-germanized-shipments/src/Admin/Settings.php
new file mode 100644
index 000000000..c0ba0519a
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/src/Admin/Settings.php
@@ -0,0 +1,249 @@
+ 'main',
+ 'href' => 'shipments' === $current_tab ? '' : admin_url( 'admin.php?page=wc-settings&tab=shipments' ),
+ 'title' => _x( 'Shipments', 'shipments-settings-page-title', 'woocommerce-germanized' ),
+ ),
+ )
+ );
+
+ return $breadcrumb;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return bool|Tab
+ */
+ public static function get_tab( $name ) {
+ $setting_pages = \WC_Admin_Settings::get_settings_pages();
+ $setting_page = false;
+
+ foreach ( $setting_pages as $page ) {
+ if ( is_a( $page, '\Vendidero\Germanized\Shipments\Admin\Tabs\Tabs' ) ) {
+ $setting_page = $page;
+ break;
+ }
+ }
+
+ if ( ! $setting_page ) {
+ $setting_page = new Tabs();
+ }
+
+ return $setting_page->get_tab_by_name( $name );
+ }
+
+ public static function get_settings_url( $tab = '', $section = '' ) {
+ $url = admin_url( 'admin.php?page=wc-settings&tab=shipments' );
+
+ if ( ! empty( $tab ) ) {
+ $url = add_query_arg( array( 'tab' => 'shipments-' . $tab ), $url );
+ }
+
+ if ( ! empty( $section ) ) {
+ $url = add_query_arg( array( 'section' => $section ), $url );
+ }
+
+ return esc_url_raw( $url );
+ }
+
+ public static function get_sanitized_settings( $settings, $data = null ) {
+ if ( is_null( $data ) ) {
+ $data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ }
+
+ if ( empty( $data ) ) {
+ return false;
+ }
+
+ $settings_to_save = array();
+
+ // Loop options and get values to save.
+ foreach ( $settings as $option ) {
+ if ( ! isset( $option['id'] ) || empty( $option['id'] ) || ! isset( $option['type'] ) || in_array( $option['type'], array( 'title', 'sectionend' ), true ) || ( isset( $option['is_option'] ) && false === $option['is_option'] ) ) {
+ continue;
+ }
+
+ $option_key = $option['id'];
+ $raw_value = isset( $data[ $option_key ] ) ? wp_unslash( $data[ $option_key ] ) : null;
+
+ // Format the value based on option type.
+ switch ( $option['type'] ) {
+ case 'checkbox':
+ $value = '1' === $raw_value || 'yes' === $raw_value ? 'yes' : 'no';
+ break;
+ case 'textarea':
+ $value = wp_kses_post( trim( $raw_value ) );
+ break;
+ case 'password':
+ $value = is_null( $raw_value ) ? '' : addslashes( $raw_value );
+ $value = trim( $value );
+
+ $encrypted = SecretBox::encrypt( $value );
+
+ if ( ! is_wp_error( $encrypted ) ) {
+ $value = $encrypted;
+ }
+ break;
+ case 'multiselect':
+ case 'multi_select_countries':
+ $value = array_filter( array_map( 'wc_clean', (array) $raw_value ) );
+ break;
+ case 'image_width':
+ $value = array();
+ if ( isset( $raw_value['width'] ) ) {
+ $value['width'] = wc_clean( $raw_value['width'] );
+ $value['height'] = wc_clean( $raw_value['height'] );
+ $value['crop'] = isset( $raw_value['crop'] ) ? 1 : 0;
+ } else {
+ $value['width'] = $option['default']['width'];
+ $value['height'] = $option['default']['height'];
+ $value['crop'] = $option['default']['crop'];
+ }
+ break;
+ case 'select':
+ $allowed_values = empty( $option['options'] ) ? array() : array_map( 'strval', array_keys( $option['options'] ) );
+ if ( empty( $option['default'] ) && empty( $allowed_values ) ) {
+ $value = null;
+ break;
+ }
+ $default = ( empty( $option['default'] ) ? $allowed_values[0] : $option['default'] );
+ $value = in_array( $raw_value, $allowed_values, true ) ? $raw_value : $default;
+ break;
+ case 'relative_date_selector':
+ $value = wc_parse_relative_date_option( $raw_value );
+ break;
+ default:
+ $value = wc_clean( $raw_value );
+ break;
+ }
+
+ /**
+ * Sanitize the value of an option.
+ *
+ * @since 2.4.0
+ */
+ $value = apply_filters( 'woocommerce_admin_settings_sanitize_option', $value, $option, $raw_value );
+
+ $settings_to_save[ $option_key ] = $value;
+ }
+
+ return $settings_to_save;
+ }
+
+ public static function render_label_fields( $settings, $shipment, $do_echo = false ) {
+ $missing_div_closes = 0;
+ ob_start();
+ foreach ( $settings as $setting ) {
+ $setting = wp_parse_args(
+ $setting,
+ array(
+ 'id' => '',
+ 'type' => 'text',
+ 'custom_attributes' => array(),
+ )
+ );
+
+ if ( has_action( "woocommerce_gzd_shipment_label_admin_field_{$setting['id']}" ) ) {
+ do_action( "woocommerce_gzd_shipment_label_admin_field_{$setting['id']}", $setting, $shipment );
+ } elseif ( 'select' === $setting['type'] ) {
+ woocommerce_wp_select( $setting );
+ } elseif ( 'multiselect' === $setting['type'] ) {
+ $setting['class'] = 'select short wc-enhanced-select';
+ $setting['custom_attributes'] = array_merge( $setting['custom_attributes'], array( 'multiple' => 'multiple' ) );
+
+ if ( ! strstr( $setting['id'], '[]' ) ) {
+ $setting['name'] = $setting['id'] . '[]';
+ }
+
+ woocommerce_wp_select( $setting );
+ } elseif ( 'checkbox' === $setting['type'] ) {
+ $field_name = isset( $setting['name'] ) ? $setting['name'] : $setting['id'];
+ $field_value = isset( $setting['value'] ) ? $setting['value'] : 'no';
+
+ // Use a placeholder checkbox to force transmitting non-checked checkboxes with a no value to make sure default props are overridden.
+ echo ( ( 'yes' === $field_value ) ? '' : '' );
+ woocommerce_wp_checkbox( $setting );
+ } elseif ( 'textarea' === $setting['type'] ) {
+ woocommerce_wp_textarea_input( $setting );
+ } elseif ( 'text' === $setting['type'] ) {
+ woocommerce_wp_text_input( $setting );
+ } elseif ( 'date' === $setting['type'] ) {
+ $setting['class'] = 'datepicker';
+ $setting['type'] = 'date';
+
+ woocommerce_wp_text_input( $setting );
+ } elseif ( 'number' === $setting['type'] ) {
+ woocommerce_wp_text_input( $setting );
+ } elseif ( 'services_start' === $setting['type'] ) {
+ $hide_default = isset( $setting['hide_default'] ) ? wc_string_to_bool( $setting['hide_default'] ) : false;
+ ++$missing_div_closes;
+ ?>
+
+
+
+ get_items() as $item ) : ?>
+
+ get_formatted_address();
+
+ if ( $address ) {
+ echo '' . esc_html( preg_replace( '#
+
+
+
+
+ get_product() ) : ?>
+ get_name() ); ?>
+
+ get_name() ); ?>
+
+
+ get_sku() ? '
+
' . esc_html_x( 'SKU:', 'shipments', 'woocommerce-germanized' ) . ' ' . esc_html( $item->get_sku() ) . '' : '' ); ?>
+
+ get_hook_prefix()}item_after_name", $item->get_id(), $item, $shipment );
+ ?>
+
+ get_quantity() ); ?>x
+
+
#i', ', ', $address ) ) . '';
+ } else {
+ echo '–';
+ }
+ }
+
+ /**
+ * Handles the post author column output.
+ *
+ * @since 4.3.0
+ *
+ * @param Shipment $shipment The current shipment object.
+ */
+ public function column_status( $shipment ) {
+ echo '' . esc_html( wc_gzd_get_shipment_status_name( $shipment->get_status() ) ) . '';
+ }
+
+ /**
+ * Handles the post author column output.
+ *
+ * @since 4.3.0
+ *
+ * @param Shipment $shipment The current shipment object.
+ */
+ public function column_weight( $shipment ) {
+ echo wc_gzd_format_shipment_weight( $shipment->get_total_weight(), $shipment->get_weight_unit() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
+
+ /**
+ * Handles the post author column output.
+ *
+ * @since 4.3.0
+ *
+ * @param Shipment $shipment The current shipment object.
+ */
+ public function column_packaging( $shipment ) {
+ if ( $packaging = $shipment->get_packaging() ) {
+ echo wp_kses_post( $packaging->get_description() );
+ } else {
+ echo '–';
+ }
+ }
+
+ /**
+ * Handles the post author column output.
+ *
+ * @since 4.3.0
+ *
+ * @param Shipment $shipment The current shipment object.
+ */
+ public function column_dimensions( $shipment ) {
+ echo wc_gzd_format_shipment_dimensions( $shipment->get_dimensions(), $shipment->get_dimension_unit() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
+
+ /**
+ * Handles the post author column output.
+ *
+ * @since 4.3.0
+ *
+ * @param Shipment $shipment The current shipment object.
+ */
+ public function column_date( $shipment ) {
+ $shipment_timestamp = $shipment->get_date_created() ? $shipment->get_date_created()->getTimestamp() : '';
+
+ if ( ! $shipment_timestamp ) {
+ echo '–';
+ return;
+ }
+
+ // Check if the order was created within the last 24 hours, and not in the future.
+ if ( $shipment_timestamp > strtotime( '-1 day', time() ) && $shipment_timestamp <= time() ) {
+ $show_date = sprintf(
+ /* translators: %s: human-readable time difference */
+ _x( '%s ago', '%s = human-readable time difference', 'woocommerce-germanized' ),
+ human_time_diff( $shipment->get_date_created()->getTimestamp(), time() )
+ );
+ } else {
+ /**
+ * Filter to adjust the Shipment date format in table view.
+ *
+ * @param string $format The date format.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ $show_date = $shipment->get_date_created()->date_i18n( apply_filters( 'woocommerce_gzd_shipments_admin_shipment_date_format', _x( 'M j, Y', 'shipments', 'woocommerce-germanized' ) ) );
+ }
+
+ printf(
+ '',
+ esc_attr( $shipment->get_date_created()->date( 'c' ) ),
+ esc_html( $shipment->get_date_created()->date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ),
+ esc_html( $show_date )
+ );
+ }
+
+ /**
+ * Handles the post author column output.
+ *
+ * @since 4.3.0
+ *
+ * @param Shipment $shipment The current shipment object.
+ */
+ public function column_order( $shipment ) {
+ if ( ( $order = $shipment->get_order() ) && is_callable( array( $order, 'get_edit_order_url' ) ) ) {
+ echo '' . esc_html( $order->get_order_number() ) . '';
+ } else {
+ echo esc_html( $shipment->get_order_number() );
+ }
+ }
+
+ /**
+ *
+ * @param int|Shipment $shipment
+ */
+ public function single_row( $shipment ) {
+ $GLOBALS['shipment'] = $shipment;
+ $classes = 'shipment shipment-status-' . $shipment->get_status();
+ ?>
+
+ single_row_columns( $shipment ); ?>
+
+ shipment_type ? '' : '_' . $this->shipment_type );
+
+ return "woocommerce_gzd{$suffix}_shipments_table_";
+ }
+
+ /**
+ * @return array
+ */
+ protected function get_bulk_actions() {
+ $actions = array();
+
+ if ( current_user_can( 'delete_shop_orders' ) ) {
+ $actions['delete'] = _x( 'Delete Permanently', 'shipments', 'woocommerce-germanized' );
+ }
+
+ $actions['mark_processing'] = _x( 'Change status to processing', 'shipments', 'woocommerce-germanized' );
+ $actions['mark_shipped'] = _x( 'Change status to shipped', 'shipments', 'woocommerce-germanized' );
+ $actions['mark_delivered'] = _x( 'Change status to delivered', 'shipments', 'woocommerce-germanized' );
+ $actions['labels'] = _x( 'Generate and download labels', 'shipments', 'woocommerce-germanized' );
+
+ $actions = $this->get_custom_bulk_actions( $actions );
+
+ /**
+ * Filter to register addtional bulk actions for shipments.
+ *
+ * The dynamic portion of this hook, `$this->get_hook_prefix()` is used to construct a
+ * unique hook for a shipment type e.g. return. In case of simple shipments the type is omitted.
+ *
+ * Example hook name: woocommerce_gzd_return_shipments_table_bulk_actions
+ *
+ * @param array $actions Array containing key => value pairs.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( "{$this->get_hook_prefix()}bulk_actions", $actions );
+ }
+}
diff --git a/packages/woocommerce-germanized-shipments/src/Admin/Tabs/General.php b/packages/woocommerce-germanized-shipments/src/Admin/Tabs/General.php
new file mode 100644
index 000000000..82e78586b
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/src/Admin/Tabs/General.php
@@ -0,0 +1,412 @@
+ _x( 'General', 'shipments', 'woocommerce-germanized' ),
+ 'automation' => _x( 'Automation', 'shipments', 'woocommerce-germanized' ),
+ 'return' => _x( 'Returns', 'shipments', 'woocommerce-germanized' ),
+ 'business_information' => _x( 'Business Information', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ return $sections;
+ }
+
+ public function get_section_description( $section ) {
+ return '';
+ }
+
+ public function get_pointers() {
+ $current_section = $this->get_current_section();
+ $pointers = array();
+
+ if ( '' === $current_section ) {
+ $next_url = Tutorial::get_tutorial_url( 'general', 'automation' );
+
+ $pointers = array(
+ 'pointers' => array(
+ 'default' => array(
+ 'target' => '#woocommerce_gzd_shipments_notify_enable-toggle',
+ 'next' => '',
+ 'next_url' => $next_url,
+ 'next_trigger' => array(),
+ 'options' => array(
+ 'content' => '' . esc_html_x( 'E-Mail Notification', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Automation', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Returns', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Shipper Address', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Edit packaging', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Add packaging', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Create packaging reports', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Automated packing', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Shipping Provider', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Activate', 'shipments', 'woocommerce-germanized' ) . '
' . esc_html_x( 'Add new', 'shipments', 'woocommerce-germanized' ) . '
';
+
+ $array_keys = array_keys( $sections );
+
+ foreach ( $sections as $id => $label ) {
+ $class = ( $current_section === $id ? 'current' : '' );
+ $separator = ( end( $array_keys ) === $id ? '' : '|' );
+ $text = esc_html( $label ) . ( $this->is_pro( $id ) && ! Package::is_pro() ? 'pro' : '' );
+ echo "
';
+ }
+
+ protected function get_section_url( $section_id ) {
+ return $this->get_url( $section_id );
+ }
+
+ protected function get_breadcrumb() {
+ $sections = $this->get_sections();
+ $current_section = $this->get_current_section();
+ $section_label = $this->get_section_title( $current_section );
+ $breadcrumb = Settings::get_main_breadcrumb();
+
+ $breadcrumb[] = array(
+ 'class' => 'tab',
+ 'href' => ! empty( $current_section ) ? $this->get_url() : '',
+ 'title' => empty( $current_section ) ? $this->get_breadcrumb_label( $this->get_label() ) : $this->get_label(),
+ );
+
+ if ( ! empty( $current_section ) ) {
+ $breadcrumb[] = array(
+ 'class' => 'section',
+ 'href' => '',
+ 'title' => $this->get_breadcrumb_label( $section_label ),
+ );
+ }
+
+ $breadcrumb = $this->get_additional_breadcrumb_items( $breadcrumb );
+
+ return apply_filters( "woocommerce_gzd_shipments_admin_settings_tab_{$this->get_name()}_breadcrumb", $breadcrumb );
+ }
+
+ public function get_description() {
+ return '';
+ }
+
+ protected function get_breadcrumb_label( $label ) {
+ $section = $this->get_current_section();
+
+ if ( empty( $section ) && $this->has_help_link() ) {
+ $label = $label . '' . _x( 'Learn more', 'shipments', 'woocommerce-germanized' ) . '';
+ } elseif ( ! empty( $section ) && ! Package::is_pro() && $this->is_pro( $section ) ) {
+ $label = $label . 'pro';
+ }
+
+ return $label;
+ }
+
+ protected function get_section_description( $section ) {
+ return '';
+ }
+
+ public function supports_disabling() {
+ return false;
+ }
+
+ private function get_settings_internal( $section_id ) {
+ $settings = $this->get_tab_settings( $section_id );
+
+ if ( empty( $section_id ) ) {
+ /**
+ * Filter to adjust the settings for a certain settings tab.
+ *
+ * The dynamic portion of the hook name, `$this->get_name()` refers to the tab name e.g. checkboxes.
+ *
+ * @param array $settings Array containing settings data.
+ *
+ * @since 3.0.0
+ */
+ $settings = apply_filters( "woocommerce_gzd_shipments_admin_settings_tab_{$this->get_name()}", $settings );
+ } else {
+ /**
+ * Filter to adjust the settings for a certain section of a settings tab.
+ *
+ * The dynamic portion of the hook name, `$this->get_name()` refers to the tab name e.g. checkboxes.
+ * `$current_section` refers to the current section e.g. product_widget.
+ *
+ * @param array $settings Array containing settings data.
+ *
+ * @since 3.0.0
+ */
+ $settings = apply_filters( "woocommerce_gzd_shipments_admin_settings_tab_{$this->get_name()}_{$section_id}", $settings );
+ }
+
+ /**
+ * General filter to adjust the settings for setting tabs.
+ *
+ * @param array $settings Array containing settings data.
+ * @param string $tab_name The name of the tab e.g. checkboxes
+ * @param string $section_id The section name e.g. product_widgets. Might be empty too.
+ *
+ * @since 3.0.0
+ *
+ */
+ return apply_filters( 'woocommerce_gzd_shipments_admin_settings', $settings, $this->get_name(), $section_id );
+ }
+
+ public function get_settings_for_section_core( $section_id ) {
+ return $this->get_settings_internal( $section_id );
+ }
+
+ public function get_settings( $section_id = '' ) {
+ return $this->get_settings_internal( $section_id );
+ }
+
+ abstract public function get_tab_settings( $current_section = '' );
+
+ public function has_tutorial() {
+ $pointers = $this->get_pointers();
+
+ return ! empty( $pointers ) ? true : false;
+ }
+
+ public function get_pointers() {
+ return array();
+ }
+
+ protected function is_saveable() {
+ return ( $this->is_pro() && ! Package::is_pro() ? false : true );
+ }
+
+ public function hide_from_main_panel() {
+ return false;
+ }
+
+ public function output() {
+ $current_section = $this->get_current_section();
+ $current_tab = $this->get_id();
+ $current_tab_name = $this->get_name();
+ $settings = $this->get_settings_for_section_core( $this->get_current_section() );
+
+ if ( ! $this->is_saveable() ) {
+ $GLOBALS['hide_save_button'] = true;
+ }
+
+ /**
+ * Fires before settings for a certain tab are rendered.
+ *
+ * The dynamic portion of the hook name, $this->get_name(),
+ * refers to the current tab name e.g. checkboxes.
+ *
+ * @param string $current_section The current sub section of the tab.
+ *
+ * @since 3.0.0
+ */
+ do_action( "woocommerce_gzd_shipments_admin_settings_before_wrapper_{$this->get_name()}", $current_section );
+
+ include Package::get_path( 'includes/admin/views/tabs/html-admin-settings-section.php' );
+ }
+
+ public function is_enabled() {
+ if ( $this->supports_disabling() ) {
+ if ( ! empty( $this->get_enable_option_name() ) ) {
+ return 'yes' === get_option( $this->get_enable_option_name() );
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public function disable() {
+ if ( $this->supports_disabling() && ! empty( $this->get_enable_option_name() ) ) {
+ update_option( $this->get_enable_option_name(), 'no' );
+ }
+ }
+
+ public function enable() {
+ if ( $this->supports_disabling() && ! empty( $this->get_enable_option_name() ) ) {
+ update_option( $this->get_enable_option_name(), 'yes' );
+ }
+ }
+
+ protected function get_enable_option_name() {
+ return '';
+ }
+
+ public function get_url( $section = '' ) {
+ $link = admin_url( 'admin.php?page=wc-settings&tab=' . sanitize_title( $this->get_id() ) . ( ! empty( $section ) ? '§ion=' . sanitize_title( $section ) : '' ) );
+
+ return $link;
+ }
+
+ abstract public function get_name();
+
+ protected function before_save( $settings, $current_section = '' ) {
+ /**
+ * Fires before settings for a certain tab are saved.
+ *
+ * The dynamic portion of the hook name, `$this->get_name()`,
+ * refers to the current tab id e.g. checkboxes.
+ *
+ * @param array $settings Array containing the settings to be saved.
+ * @param string $current_section The current section.
+ *
+ * @since 3.0.0
+ */
+ do_action( "woocommerce_gzd_shipments_admin_settings_before_save_{$this->get_name()}", $settings, $current_section );
+
+ if ( ! empty( $current_section ) ) {
+
+ /**
+ * Fires before settings for a certain section of a tab are saved.
+ *
+ * The dynamic portion of the hook name, `$this->get_name()` and `$current_section`,
+ * refer to the current tab id e.g. checkboxes and the current section name e.g. product_widgets.
+ *
+ * @param array $settings Array containing the settings to be saved.
+ *
+ * @since 3.0.0
+ */
+ do_action( "woocommerce_gzd_shipments_admin_settings_before_save_{$this->get_name()}_{$current_section}", $settings );
+ }
+ }
+
+ protected function after_save( $settings, $current_section = '' ) {
+ /**
+ * Fires after settings for a certain tab have been saved.
+ *
+ * The dynamic portion of the hook name, `$this->get_name()`,
+ * refers to the current tab id e.g. checkboxes.
+ *
+ * @param array $settings Array containing the settings to be saved.
+ * @param string $current_section The current section.
+ *
+ * @since 3.0.0
+ */
+ do_action( "woocommerce_gzd_shipments_admin_settings_after_save_{$this->get_name()}", $settings, $current_section );
+
+ if ( ! empty( $current_section ) ) {
+
+ /**
+ * Fires after settings for a certain section of a tab are saved.
+ *
+ * The dynamic portion of the hook name, `$this->get_name()` and `$current_section`,
+ * refer to the current tab id e.g. checkboxes and the current section name e.g. product_widgets.
+ *
+ * @param array $settings Array containing the settings to be saved.
+ *
+ * @since 3.0.0
+ *
+ */
+ do_action( "woocommerce_gzd_shipments_admin_settings_after_save_{$this->get_name()}_{$current_section}", $settings );
+ }
+ }
+
+ public function save() {
+ global $current_section;
+
+ $settings = $this->get_settings_for_section_core( $current_section );
+
+ $this->before_save( $settings, $current_section );
+ \WC_Admin_Settings::save_fields( $settings );
+ $this->after_save( $settings, $current_section );
+ }
+}
diff --git a/packages/woocommerce-germanized-shipments/src/Admin/Tabs/Tabs.php b/packages/woocommerce-germanized-shipments/src/Admin/Tabs/Tabs.php
new file mode 100644
index 000000000..5fd60ebc1
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/src/Admin/Tabs/Tabs.php
@@ -0,0 +1,243 @@
+label = _x( 'Shipments', 'shipments-settings-page-title', 'woocommerce-germanized' );
+ $this->get_tabs();
+
+ add_filter( 'admin_body_class', array( $this, 'add_body_classes' ) );
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
+
+ add_filter( 'woocommerce_navigation_is_connected_page', array( $this, 'add_wc_admin_breadcrumbs' ), 5, 2 );
+
+ parent::__construct();
+
+ if ( Package::is_integration() ) {
+ remove_filter( 'woocommerce_settings_tabs_array', array( $this, 'add_settings_page' ), 20 );
+ }
+ }
+
+ protected function get_breadcrumb() {
+ $breadcrumb = Settings::get_main_breadcrumb();
+
+ return $breadcrumb;
+ }
+
+ public function get_description() {
+ return _x( 'Adjust settings related to packaging, packing and available shipping provider.', 'shipments', 'woocommerce-germanized' );
+ }
+
+ public function header() {
+ $breadcrumb = $this->get_breadcrumb();
+ $count = 0;
+ ?>
+
+
+ ' . esc_html_x( 'Setting tabs', 'shipments', 'woocommerce-germanized' ) . '
get_title() ); ?>
+ $action ) :
+ if ( 'view' === $action_type ) {
+ continue;
+ }
+ ?>
+
+
+
+ get_status() ) : ?>
+
+
+
+
+
+
+
+
+ get_countries() as $country ) :
+ ?>
+
+
+ $column ) : ?>
+
+
+
+
+
+
+
+
+
+
+ get_id() > 0 ? $packaging->get_description() : _x( 'Unknown', 'shipments-packaging-title', 'woocommerce-germanized' ) ) ); ?>
+ get_packaging_weight( $packaging_id ), wc_gzd_get_packaging_weight_unit() ) ); ?>
+ get_packaging_count( $packaging_id ) ); ?>
+
+
+
+
+
+
+ $column ) : ?>
+
+
+
+ get_packaging_ids_by_country( $country ) as $packaging_id ) :
+ $packaging = is_numeric( $packaging_id ) ? wc_gzd_get_packaging( $packaging_id ) : false;
+ ?>
+
+
+
+
+
+
+ get_id() > 0 ? $packaging->get_description() : _x( 'Unknown', 'shipments-packaging-title', 'woocommerce-germanized' ) ) ); ?>
+ get_packaging_weight( $packaging_id, $country ), wc_gzd_get_packaging_weight_unit() ) ); ?>
+ get_packaging_count( $packaging_id, $country ) ); ?>
+ define( '" . $constant . "', '" . $new_key . "' );
+ '',
+ 'tabs' => array(),
+ 'provider' => '',
+ )
+ );
+ $count = 0;
+ ob_start();
+ ?>
+
+
+ method = $method;
+ $this->id = $this->method->id;
+ $this->instance_id = $this->method->get_instance_id();
+ } elseif ( is_array( $method ) ) {
+ $method = wp_parse_args(
+ $method,
+ array(
+ 'id' => '',
+ 'instance_id' => 0,
+ )
+ );
+
+ $this->is_placeholder = true;
+ $this->id = $method['id'];
+ $this->instance_id = $method['instance_id'];
+ }
+ }
+
+ public function get_id() {
+ if ( ! $this->is_placeholder() ) {
+ return $this->method->id;
+ } else {
+ return '';
+ }
+ }
+
+ public function get_instance_id() {
+ if ( ! $this->is_placeholder() ) {
+ return $this->method->get_instance_id();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the Woo WC_Shipping_Method original object
+ *
+ * @return WC_Shipping_Method|null
+ */
+ public function get_method() {
+ return $this->method;
+ }
+
+ /**
+ * @return false|ShippingProvider
+ */
+ public function get_shipping_provider_instance() {
+ if ( $this->is_builtin_method() ) {
+ return $this->method->get_shipping_provider();
+ }
+
+ if ( is_null( $this->provider ) ) {
+ $provider = $this->get_shipping_provider();
+
+ if ( ! empty( $provider ) ) {
+ $this->provider = wc_gzd_get_shipping_provider( $provider );
+ }
+ }
+
+ return $this->provider ? $this->provider : false;
+ }
+
+ public function is_builtin_method() {
+ if ( is_a( $this->method, '\Vendidero\Germanized\Shipments\ShippingMethod\ShippingMethod' ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function get_shipping_provider() {
+ if ( $this->is_builtin_method() ) {
+ $provider_slug = $this->method->get_shipping_provider()->get_name();
+ } else {
+ $provider_slug = $this->get_prop( 'shipping_provider' );
+ }
+
+ /**
+ * Filter that allows adjusting the shipping provider chosen for a specific shipping method.
+ *
+ * @param string $provider_slug The shipping provider.
+ * @param string $method_id The shipping method id.
+ * @param ProviderMethod $method The method instance.
+ *
+ * @since 3.0.6
+ * @package Vendidero/Germanized/Shipments
+ */
+ return apply_filters( 'woocommerce_gzd_shipping_provider_method_provider', $provider_slug, $this->get_id(), $this );
+ }
+
+ public function set_shipping_provider( $shipping_provider_name ) {
+ $this->set_prop( 'shipping_provider', $shipping_provider_name );
+ $this->provider = null;
+ }
+
+ public function has_shipping_provider( $shipping_provider_name ) {
+ if ( ! is_array( $shipping_provider_name ) ) {
+ $shipping_provider_name = array( $shipping_provider_name );
+ }
+
+ return in_array( $this->get_shipping_provider(), $shipping_provider_name, true );
+ }
+
+ public function get_prop( $key, $context = 'view' ) {
+ $default = '';
+
+ if ( 'configuration_sets' === $key ) {
+ $default = array();
+ } elseif ( 'shipping_provider' === $key ) {
+ $default = wc_gzd_get_default_shipping_provider();
+ }
+
+ if ( ! $this->is_placeholder() && ! MethodHelper::method_is_excluded( $this->get_id() ) ) {
+ $value = $this->supports_instance_settings() ? $this->method->get_instance_option( $key, $default ) : $this->method->get_option( $key, $default );
+ } else {
+ $value = $default;
+ }
+
+ return $value;
+ }
+
+ public function set_prop( $key, $value ) {
+ if ( ! $this->is_placeholder() && ! MethodHelper::method_is_excluded( $this->get_id() ) ) {
+ if ( $this->supports_instance_settings() ) {
+ if ( empty( $this->method->instance_settings ) ) {
+ $this->method->init_instance_settings();
+ }
+
+ if ( 'configuration_sets' === $key ) {
+ $this->method->instance_settings[ $key ] = array_filter( (array) $value );
+ } else {
+ $this->method->instance_settings[ $key ] = $value;
+ }
+ } else {
+ if ( empty( $this->method->settings ) ) {
+ $this->method->init_settings();
+ }
+
+ if ( 'configuration_sets' === $key ) {
+ $this->method->settings[ $key ] = array_filter( (array) $value );
+ } else {
+ $this->method->settings[ $key ] = $value;
+ }
+ }
+ }
+ }
+
+ protected function get_configuration_set_setting_type() {
+ return 'shipping_method';
+ }
+
+ protected function supports_instance_settings() {
+ if ( $this->is_placeholder() ) {
+ return false;
+ } else {
+ $supports_settings = ( $this->method->supports( 'instance-settings' ) ) ? true : false;
+
+ return apply_filters( 'woocommerce_gzd_shipping_provider_method_supports_instance_settings', $supports_settings, $this );
+ }
+ }
+
+ public function is_placeholder() {
+ return true === $this->is_placeholder;
+ }
+
+ protected function get_hook_prefix() {
+ $prefix = 'woocommerce_gzd_shipping_provider_method_';
+
+ return $prefix;
+ }
+
+ public function get_option( $key ) {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::get_option()', '3.0.0' );
+
+ return $this->get_prop( $key );
+ }
+
+ public function set_provider( $shipping_provider_name ) {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::set_provider()', '3.0.0' );
+
+ $this->set_shipping_provider( $shipping_provider_name );
+ }
+
+ public function get_provider() {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::get_provider()', '3.0.0' );
+
+ return $this->get_shipping_provider();
+ }
+
+ public function has_option( $key ) {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::has_option()', '3.0.0' );
+
+ return false;
+ }
+
+ public function is_provider_enabled( $provider ) {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::is_provider_enabled()', '3.0.0' );
+
+ return ( $this->get_provider() === $provider ) ? true : false;
+ }
+
+ public function setting_belongs_to_provider( $setting_key, $provider = '' ) {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::setting_belongs_to_provider()', '3.0.0' );
+
+ return false;
+ }
+
+ public static function get_admin_settings() {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::get_admin_settings()', '3.0.0' );
+
+ return array();
+ }
+
+ public function get_provider_instance() {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::get_provider_instance()', '3.0.0' );
+
+ return $this->get_shipping_provider_instance();
+ }
+
+ public function get_fallback_setting_value( $setting_key ) {
+ wc_deprecated_function( 'Vendidero\Germanized\Shipments\ShippingProvider\Method::get_fallback_setting_value()', '3.0.0' );
+
+ return '';
+ }
+}
diff --git a/packages/woocommerce-germanized-shipments/src/ShippingMethod/ProviderMethodPlaceholder.php b/packages/woocommerce-germanized-shipments/src/ShippingMethod/ProviderMethodPlaceholder.php
new file mode 100644
index 000000000..4a899879c
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/src/ShippingMethod/ProviderMethodPlaceholder.php
@@ -0,0 +1,21 @@
+get_method( $instance_id );
+
+ if ( ! empty( $raw_method ) ) {
+ $method_id = str_replace( 'shipping_provider_', '', $raw_method->method_id );
+ $this->shipping_provider = wc_gzd_get_shipping_provider( $method_id );
+ }
+ }
+ } else {
+ $this->shipping_provider = is_a( $shipping_provider, 'Vendidero\Germanized\Shipments\Interfaces\ShippingProvider' ) ? $shipping_provider : wc_gzd_get_shipping_provider( $shipping_provider );
+ }
+
+ if ( ! is_a( $this->shipping_provider, 'Vendidero\Germanized\Shipments\Interfaces\ShippingProvider' ) ) {
+ return;
+ }
+
+ $this->id = 'shipping_provider_' . $this->shipping_provider->get_name();
+ $this->instance_id = absint( $instance_id );
+ $this->method_title = $this->shipping_provider->get_title();
+ $this->method_description = sprintf( _x( 'Apply rule-based shipping costs for shipments handled by %1$s based on your available packaging options. Learn how to configure →', 'shipments', 'woocommerce-germanized' ), $this->shipping_provider->get_title(), 'https://vendidero.de/dokument/versandregeln-erstellen' );
+ $this->title = $this->method_title;
+ $this->supports = array(
+ 'shipping-zones',
+ 'instance-settings',
+ );
+
+ $this->init();
+ }
+
+ /**
+ * Init user set variables.
+ */
+ public function init() {
+ // Load the settings.
+ $this->init_form_fields();
+ $this->init_settings();
+
+ // Define user set variables.
+ $this->title = $this->get_option( 'title' );
+
+ // Actions.
+ add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
+ }
+
+ /**
+ * @return ShippingProvider
+ */
+ public function get_shipping_provider() {
+ return $this->shipping_provider;
+ }
+
+ public function get_all_shipping_rules() {
+ return array_merge( ...array_values( $this->get_shipping_rules() ) );
+ }
+
+ public function get_shipping_rules() {
+ return $this->get_option( 'shipping_rules', array() );
+ }
+
+ public function get_shipping_rule_by_id( $rule_id, $packaging_id ) {
+ $rules = $this->get_shipping_rules_by_packaging( $packaging_id );
+ $rule_key = "rule_{$rule_id}";
+
+ if ( array_key_exists( $rule_key, $rules ) ) {
+ return $rules[ $rule_key ];
+ }
+
+ return false;
+ }
+
+ public function get_shipping_rules_by_packaging( $packaging ) {
+ $shipping_rules = $this->get_shipping_rules();
+ $packaging_rules = array();
+
+ if ( array_key_exists( $packaging, $shipping_rules ) ) {
+ $packaging_rules = $shipping_rules[ $packaging ];
+ }
+
+ return $packaging_rules;
+ }
+
+ public function get_fallback_shipping_rules() {
+ $shipping_rules = $this->get_shipping_rules();
+ $packaging_rules = array();
+
+ if ( array_key_exists( 'all', $shipping_rules ) ) {
+ $packaging_rules = $shipping_rules['all'];
+ }
+
+ return $packaging_rules;
+ }
+
+ public function admin_options() {
+ $locale = localeconv();
+ $decimal_point = isset( $locale['decimal_point'] ) ? $locale['decimal_point'] : '.';
+ $decimal = ( ! empty( wc_get_price_decimal_separator() ) ) ? wc_get_price_decimal_separator() : $decimal_point;
+
+ wp_localize_script(
+ 'wc-gzd-shipments-admin-shipping-rules',
+ 'wc_gzd_shipments_admin_shipping_rules_params',
+ array(
+ 'rules' => $this->get_option( 'shipping_rules', array() ),
+ 'decimal_separator' => $decimal,
+ 'price_decimal_separator' => wc_get_price_decimal_separator(),
+ 'default_shipping_rule' => array(
+ 'rule_id' => 0,
+ 'packaging' => '',
+ 'costs' => '',
+ 'conditions' => array(
+ array(
+ 'rule_id' => 0,
+ 'condition_id' => 0,
+ 'type' => 'always',
+ 'operator' => '',
+ ),
+ ),
+ ),
+ 'strings' => array(
+ 'unload_confirmation_msg' => _x( 'Your changed data will be lost if you leave this page without saving.', 'shipments', 'woocommerce-germanized' ),
+ ),
+ )
+ );
+ wp_enqueue_script( 'wc-gzd-shipments-admin-shipping-rules' );
+
+ parent::admin_options();
+ }
+
+ /**
+ * @return false|\WC_Shipping_Zone
+ */
+ public function get_zone() {
+ if ( $this->get_instance_id() > 0 ) {
+ if ( is_null( $this->zone ) ) {
+ $this->zone = \WC_Shipping_Zones::get_zone_by( 'instance_id', $this->get_instance_id() );
+ }
+
+ return $this->zone;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return admin options as a html string.
+ *
+ * @return string
+ */
+ public function get_admin_options_html() {
+ if ( $this->instance_id ) {
+ $settings_html = $this->generate_settings_html( $this->get_instance_form_fields(), false );
+ } else {
+ $settings_html = $this->generate_settings_html( $this->get_form_fields(), false );
+ }
+
+ return '
' . $settings_html . '
';
+ }
+
+ public function init_form_fields() {
+ $this->instance_form_fields = array(
+ 'title' => array(
+ 'title' => _x( 'Title', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'text',
+ 'description' => _x( 'This controls the title which the user sees during checkout.', 'shipments', 'woocommerce-germanized' ),
+ 'default' => $this->method_title,
+ 'desc_tip' => true,
+ ),
+ 'free_title' => array(
+ 'title' => _x( 'Title (free shipping)', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'text',
+ 'description' => _x( 'This controls the title which the user sees during checkout in case a free shipping option is available.', 'shipments', 'woocommerce-germanized' ),
+ 'default' => sprintf( _x( 'Free shipping (via %1$s)', 'shipments', 'woocommerce-germanized' ), $this->method_title ),
+ 'desc_tip' => true,
+ ),
+ 'shipping_rules_title' => array(
+ 'title' => _x( 'Shipping Rules', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'title',
+ 'id' => 'shipping_rules_title',
+ 'default' => '',
+ 'description' => sprintf( _x( 'Configure shipping costs per packaging option. Within cart, a rucksack algorithm will automatically fit the items in the packaging option(s) available and calculate it\'s cost.
Some important hints on the calculation logic:
', 'shipments', 'woocommerce-germanized' ) ),
+ ),
+ 'multiple_shipments_cost_calculation_mode' => array(
+ 'title' => _x( 'Multiple packages', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'select',
+ 'default' => 'sum',
+ 'options' => array(
+ 'sum' => _x( 'Sum all costs', 'shipments', 'woocommerce-germanized' ),
+ 'max' => _x( 'Apply the maximum cost only', 'shipments', 'woocommerce-germanized' ),
+ 'min' => _x( 'Apply the minimum cost only', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'desc_tip' => _x( 'The algorithm may detect that multiple packages, with possibly different packaging, for the current cart may be needed. Choose how to calculate costs.', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'multiple_rules_cost_calculation_mode' => array(
+ 'title' => _x( 'Multiple matching rules', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'select',
+ 'default' => 'max',
+ 'options' => array(
+ 'sum' => _x( 'Sum all costs', 'shipments', 'woocommerce-germanized' ),
+ 'max' => _x( 'Apply the maximum cost only', 'shipments', 'woocommerce-germanized' ),
+ 'min' => _x( 'Apply the minimum cost only', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'desc_tip' => _x( 'Decide how costs should add up in case multiple rules per packaging option match the current cart.', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'shipping_rules' => array(
+ 'title' => _x( 'Rules', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'shipping_rules',
+ 'default' => array(),
+ ),
+ 'cache' => array(
+ 'type' => 'cache',
+ 'default' => array(),
+ ),
+ );
+ }
+
+ public function get_rule_conditional_operators() {
+ return apply_filters(
+ 'woocommerce_gzd_shipping_method_rule_condition_operators',
+ array(
+ 'is' => array(
+ 'label' => _x( 'is', 'shipments', 'woocommerce-germanized' ),
+ 'is_negation' => false,
+ ),
+ 'is_not' => array(
+ 'label' => _x( 'is not', 'shipments', 'woocommerce-germanized' ),
+ 'is_negation' => true,
+ ),
+ 'any_of' => array(
+ 'label' => _x( 'any of', 'shipments', 'woocommerce-germanized' ),
+ 'is_negation' => false,
+ ),
+ 'none_of' => array(
+ 'label' => _x( 'none of', 'shipments', 'woocommerce-germanized' ),
+ 'is_negation' => true,
+ ),
+ 'exactly' => array(
+ 'label' => _x( 'Exactly', 'shipments', 'woocommerce-germanized' ),
+ 'is_negation' => false,
+ ),
+ )
+ );
+ }
+
+ public function get_condition_types() {
+ return apply_filters(
+ 'woocommerce_gzd_shipping_method_rule_condition_types',
+ array(
+ 'always' => array(
+ 'label' => _x( 'Always', 'shipments', 'woocommerce-germanized' ),
+ 'fields' => array(),
+ 'operators' => array(),
+ 'is_global' => true,
+ ),
+ 'package_weight' => array(
+ 'label' => _x( 'Package weight', 'shipments', 'woocommerce-germanized' ),
+ 'fields' => array(
+ 'weight_from' => array(
+ 'type' => 'text',
+ 'data_type' => 'decimal',
+ 'data_validation' => 'weight',
+ 'label' => _x( 'from', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'weight_to' => array(
+ 'type' => 'text',
+ 'data_type' => 'decimal',
+ 'data_validation' => 'weight',
+ 'label' => _x( 'to', 'shipments', 'woocommerce-germanized' ),
+ 'description' => class_exists( '\Automattic\WooCommerce\Utilities\I18nUtil' ) ? \Automattic\WooCommerce\Utilities\I18nUtil::get_weight_unit_label( get_option( 'woocommerce_weight_unit', 'kg' ) ) : get_option( 'woocommerce_weight_unit', 'kg' ),
+ ),
+ ),
+ 'operators' => array( 'is', 'is_not' ),
+ ),
+ 'weight' => array(
+ 'label' => _x( 'Cart weight', 'shipments', 'woocommerce-germanized' ),
+ 'fields' => array(
+ 'weight_from' => array(
+ 'type' => 'text',
+ 'data_type' => 'decimal',
+ 'data_validation' => 'weight',
+ 'label' => _x( 'from', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'weight_to' => array(
+ 'type' => 'text',
+ 'data_type' => 'decimal',
+ 'data_validation' => 'weight',
+ 'label' => _x( 'to', 'shipments', 'woocommerce-germanized' ),
+ 'description' => class_exists( '\Automattic\WooCommerce\Utilities\I18nUtil' ) ? \Automattic\WooCommerce\Utilities\I18nUtil::get_weight_unit_label( get_option( 'woocommerce_weight_unit', 'kg' ) ) : get_option( 'woocommerce_weight_unit', 'kg' ),
+ ),
+ ),
+ 'operators' => array( 'is', 'is_not' ),
+ 'is_global' => true,
+ ),
+ 'package_total' => array(
+ 'label' => _x( 'Package total', 'shipments', 'woocommerce-germanized' ),
+ 'fields' => array(
+ 'total_from' => array(
+ 'type' => 'text',
+ 'data_type' => 'price',
+ 'label' => _x( 'from', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'total_to' => array(
+ 'type' => 'text',
+ 'data_type' => 'price',
+ 'label' => _x( 'to', 'shipments', 'woocommerce-germanized' ),
+ 'description' => get_woocommerce_currency_symbol(),
+ ),
+ ),
+ 'operators' => array( 'is', 'is_not' ),
+ ),
+ 'total' => array(
+ 'label' => _x( 'Cart total', 'shipments', 'woocommerce-germanized' ),
+ 'fields' => array(
+ 'total_from' => array(
+ 'type' => 'text',
+ 'data_type' => 'price',
+ 'label' => _x( 'from', 'shipments', 'woocommerce-germanized' ),
+ ),
+ 'total_to' => array(
+ 'type' => 'text',
+ 'data_type' => 'price',
+ 'label' => _x( 'to', 'shipments', 'woocommerce-germanized' ),
+ 'description' => get_woocommerce_currency_symbol(),
+ ),
+ ),
+ 'operators' => array( 'is', 'is_not' ),
+ 'is_global' => true,
+ ),
+ 'shipping_classes' => array(
+ 'label' => _x( 'Cart shipping class', 'shipments', 'woocommerce-germanized' ),
+ 'fields' => array(
+ 'classes' => array(
+ 'type' => 'multiselect',
+ 'data_type' => 'array',
+ 'class' => 'wc-enhanced-select',
+ 'label' => _x( 'Class', 'shipments', 'woocommerce-germanized' ),
+ 'options' => function () {
+ return Package::get_shipping_classes();
+ },
+ ),
+ ),
+ 'operators' => array( 'any_of', 'none_of', 'exactly' ),
+ 'is_global' => true,
+ ),
+ 'package_shipping_classes' => array(
+ 'label' => _x( 'Package shipping class', 'shipments', 'woocommerce-germanized' ),
+ 'fields' => array(
+ 'classes' => array(
+ 'type' => 'multiselect',
+ 'data_type' => 'array',
+ 'class' => 'wc-enhanced-select',
+ 'label' => _x( 'Class', 'shipments', 'woocommerce-germanized' ),
+ 'options' => function () {
+ return Package::get_shipping_classes();
+ },
+ ),
+ ),
+ 'operators' => array( 'any_of', 'none_of', 'exactly' ),
+ ),
+ )
+ );
+ }
+
+ public function get_conditional_operator( $operator ) {
+ $operators = $this->get_rule_conditional_operators();
+
+ if ( array_key_exists( $operator, $operators ) ) {
+ return $operators[ $operator ];
+ }
+
+ return false;
+ }
+
+ public function get_condition_type( $type ) {
+ $condition_types = $this->get_condition_types();
+
+ if ( array_key_exists( $type, $condition_types ) ) {
+ return wp_parse_args(
+ $condition_types[ $type ],
+ array(
+ 'label' => '',
+ 'fields' => array(),
+ 'operators' => array(),
+ 'is_global' => false,
+ )
+ );
+ }
+
+ return false;
+ }
+
+ public function get_rate_label( $costs ) {
+ $label = $this->get_title();
+
+ if ( 0.0 === $costs ) {
+ $label = $this->get_instance_option( 'free_title', sprintf( _x( 'Free shipping (via %1$s)', 'shipments', 'woocommerce-germanized' ), $this->get_method_title() ) );
+ }
+
+ return $label;
+ }
+
+ public function get_cache( $property = null, $default_value = null ) {
+ $cache = wp_parse_args(
+ $this->get_option( 'cache', array() ),
+ array(
+ 'packaging_ids' => array(),
+ 'costs' => null,
+ 'global_rules' => null,
+ )
+ );
+
+ if ( is_null( $cache['global_rules'] ) || is_null( $cache['costs'] ) ) {
+ $cache = $this->get_updated_cache();
+ $this->update_option( 'cache', $cache );
+ }
+
+ if ( ! is_null( $property ) ) {
+ if ( array_key_exists( $property, $cache ) ) {
+ return $cache[ $property ];
+ } else {
+ return $default_value;
+ }
+ }
+
+ return $cache;
+ }
+
+ public function get_multiple_shipments_cost_calculation_mode() {
+ return $this->get_instance_option( 'multiple_shipments_cost_calculation_mode', 'sum' );
+ }
+
+ public function get_multiple_rules_cost_calculation_mode() {
+ return $this->get_instance_option( 'multiple_rules_cost_calculation_mode', 'max' );
+ }
+
+ public function get_available_packaging_boxes( $package_data = array() ) {
+ $cache = $this->get_cache();
+ $packaging_ids = $cache['packaging_ids'];
+ $global_rules = $cache['global_rules'];
+ $costs = $cache['costs'];
+
+ if ( in_array( 'all', $packaging_ids, true ) || empty( $packaging_ids ) ) {
+ $packaging_boxes = Helper::get_packaging_boxes();
+ $packaging_ids = array_diff( $packaging_ids, array( 'all' ) );
+
+ foreach ( $packaging_boxes as $id => $box ) {
+ if ( in_array( $id, $packaging_ids, true ) ) {
+ continue;
+ }
+
+ if ( $box->get_packaging()->supports_shipping_provider( $this->get_shipping_provider() ) ) {
+ $packaging_ids[] = $id;
+ }
+ }
+ }
+
+ /**
+ * Filter available packaging based on global rules, e.g. weight/total/shipping classes
+ * and do only allow applicable packaging options to be chosen for actual packing process.
+ */
+ if ( ! empty( $package_data ) && count( $global_rules ) > 0 ) {
+ $has_fallback_global_rules = array_key_exists( 'all', $global_rules );
+ $is_global_fallback_available = true;
+
+ if ( $has_fallback_global_rules ) {
+ $fallback_rules = $this->get_fallback_shipping_rules();
+
+ if ( count( $fallback_rules ) === count( $global_rules['all'] ) ) {
+ $is_global_fallback_available = false;
+
+ foreach ( array_reverse( $global_rules['all'] ) as $rule_id ) {
+ if ( $rule = $this->get_shipping_rule_by_id( $rule_id, 'all' ) ) {
+ $rule = $this->parse_rule( $rule );
+ $rule_applies = $this->rule_applies( $rule, $package_data, true );
+
+ if ( $rule_applies ) {
+ $is_global_fallback_available = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ foreach ( $packaging_ids as $packaging_id ) {
+ $global_packaging_rules = array_key_exists( $packaging_id, $global_rules ) ? $global_rules[ $packaging_id ] : array();
+ $global_packaging_rule_count = count( $global_packaging_rules );
+ $packaging_rule_count = count( $this->get_shipping_rules_by_packaging( $packaging_id ) );
+ $has_rules = $packaging_rule_count > 0;
+ $packaging_available = true;
+
+ if ( $global_packaging_rule_count > 0 && $packaging_rule_count === $global_packaging_rule_count ) {
+ $packaging_available = false;
+
+ foreach ( array_reverse( $global_packaging_rules ) as $rule_id ) {
+ if ( $rule = $this->get_shipping_rule_by_id( $rule_id, $packaging_id ) ) {
+ $rule = $this->parse_rule( $rule );
+ $rule_applies = $this->rule_applies( $rule, $package_data, true );
+
+ if ( $rule_applies ) {
+ $packaging_available = true;
+ break;
+ }
+ }
+ }
+ } elseif ( ! $has_rules && $has_fallback_global_rules ) {
+ $packaging_available = $is_global_fallback_available;
+ }
+
+ if ( ! $packaging_available ) {
+ $packaging_ids = array_diff( $packaging_ids, array( $packaging_id ) );
+ }
+ }
+ }
+
+ $packaging_ids = array_unique( array_values( $packaging_ids ) );
+ $boxes = Helper::get_packaging_boxes( apply_filters( 'woocommerce_gzd_shipping_method_available_packaging_ids', $packaging_ids, $this ) );
+
+ foreach ( $costs as $packaging_id => $cost ) {
+ if ( array_key_exists( $packaging_id, $boxes ) ) {
+ $boxes[ $packaging_id ]->set_costs( $cost['avg'] );
+ }
+ }
+
+ return $boxes;
+ }
+
+ public function calculate_shipping( $package = array() ) {
+ $applied_rules = array();
+ $debug_notices = array();
+ $is_debug_mode = Package::is_shipping_debug_mode();
+
+ if ( isset( $package['items_to_pack'], $package['package_data'] ) ) {
+ $cart_data = (array) $package['package_data'];
+ $available_boxes = $this->get_available_packaging_boxes( $cart_data );
+ $boxes = PackagingList::fromArray( $available_boxes );
+
+ $cost_calculation_mode = $this->get_multiple_shipments_cost_calculation_mode();
+ $multiple_rules_calculation_mode = $this->get_multiple_rules_cost_calculation_mode();
+
+ $total_cost = 0.0;
+ $rule_ids = array();
+ $packaging_ids = array();
+ $total_packed_item_map = array();
+ $total_packed_items = 0;
+ $packed_boxes = Helper::pack( $package['items_to_pack'], $boxes, 'cart' );
+ $unpacked_items = Helper::get_last_unpacked_items();
+
+ if ( 0 === count( $unpacked_items ) ) {
+ foreach ( $packed_boxes as $box ) {
+ $packaging = $box->getBox();
+ $items = $box->getItems();
+ $total_weight = wc_get_weight( $items->getWeight(), strtolower( get_option( 'woocommerce_weight_unit' ) ), 'g' );
+ $volume = wc_get_dimension( $items->getVolume(), strtolower( get_option( 'woocommerce_dimension_unit' ) ), 'mm' );
+ $item_count = $items->count();
+ $total = 0;
+ $subtotal = 0;
+ $products = array();
+ $shipping_classes = array();
+ $has_missing_shipping_classes = false;
+
+ foreach ( $items as $item ) {
+ $cart_item = $item->getItem();
+ $total += $cart_item->get_total();
+ $subtotal += $cart_item->get_subtotal();
+ $product = $cart_item->get_product();
+
+ if ( $product && ! array_key_exists( $product->get_id(), $products ) ) {
+ $products[ $product->get_id() ] = $product;
+
+ if ( ! empty( $product->get_shipping_class_id() ) ) {
+ $shipping_classes[] = $product->get_shipping_class_id();
+ } else {
+ $has_missing_shipping_classes = true;
+ }
+ }
+ }
+
+ $total = wc_remove_number_precision( $total );
+ $subtotal = wc_remove_number_precision( $subtotal );
+ $shipping_classes = array_unique( $shipping_classes );
+ $package_data = array_merge(
+ $cart_data,
+ array(
+ 'package_total' => $total,
+ 'package_subtotal' => $subtotal,
+ 'package_weight' => $total_weight,
+ 'package_volume' => $volume,
+ 'package_item_count' => $item_count,
+ 'packaging_id' => $packaging->get_id(),
+ 'package_products' => $products,
+ 'package_shipping_classes' => $shipping_classes,
+ 'package_has_missing_shipping_classes' => $has_missing_shipping_classes,
+ )
+ );
+
+ $package_applied_rules = array();
+ $applicable_rule_costs = array();
+
+ foreach ( array_reverse( $this->get_shipping_rules_by_packaging( $packaging->get_id() ) ) as $rule ) {
+ $rule = $this->parse_rule( $rule );
+ $rule_applies = $this->rule_applies( $rule, $package_data );
+
+ if ( $rule_applies ) {
+ $applicable_rule_costs[] = $rule['costs'];
+ $package_applied_rules[] = $rule['rule_id'];
+ }
+
+ /**
+ * In case a free shipping option is detected, stop + reset.
+ */
+ if ( $rule_applies && 0.0 === $rule['costs'] ) {
+ $applicable_rule_costs = array(
+ $rule['costs'],
+ );
+
+ $package_applied_rules = array(
+ $rule['rule_id'],
+ );
+
+ break;
+ }
+ }
+
+ /**
+ * In case no applicable rule has been found, parse fallback rules.
+ */
+ if ( empty( $package_applied_rules ) ) {
+ foreach ( array_reverse( $this->get_fallback_shipping_rules() ) as $rule ) {
+ $rule = $this->parse_rule( $rule );
+ $rule_applies = $this->rule_applies( $rule, $package_data );
+
+ if ( $rule_applies ) {
+ $applicable_rule_costs[] = $rule['costs'];
+ $package_applied_rules[] = $rule['rule_id'];
+ }
+
+ /**
+ * In case a free shipping option is detected, stop + reset.
+ */
+ if ( $rule_applies && 0.0 === $rule['costs'] ) {
+ $applicable_rule_costs = array(
+ $rule['costs'],
+ );
+
+ $package_applied_rules = array(
+ $rule['rule_id'],
+ );
+
+ break;
+ }
+ }
+ }
+
+ if ( ! empty( $package_applied_rules ) ) {
+ $applicable_rules_total_cost = 0.0;
+
+ if ( 'sum' === $multiple_rules_calculation_mode ) {
+ $applicable_rules_total_cost = array_sum( $applicable_rule_costs );
+ } elseif ( 'min' === $multiple_rules_calculation_mode ) {
+ $applicable_rules_total_cost = min( $applicable_rule_costs );
+ } elseif ( 'max' === $multiple_rules_calculation_mode ) {
+ $applicable_rules_total_cost = max( $applicable_rule_costs );
+ }
+
+ if ( 'min' === $cost_calculation_mode ) {
+ if ( $applicable_rules_total_cost <= $total_cost || 0.0 === $total_cost ) {
+ $total_cost = $applicable_rules_total_cost;
+ }
+ } elseif ( 'max' === $cost_calculation_mode ) {
+ if ( $applicable_rules_total_cost >= $total_cost ) {
+ $total_cost = $applicable_rules_total_cost;
+ }
+ } else {
+ $total_cost += $applicable_rules_total_cost;
+ }
+
+ /**
+ * Build an item map which contains a map of the cart items
+ * included within the package.
+ */
+ $item_map = array();
+ $weight = 0.0;
+
+ foreach ( $items as $item ) {
+ $cart_item_wrapper = $item->getItem();
+ $product = $cart_item_wrapper->get_product();
+ $product_key = $product->get_parent_id() . '_' . $product->get_id();
+ $weight += $cart_item_wrapper->getWeight();
+
+ if ( array_key_exists( $product_key, $item_map ) ) {
+ ++$item_map[ $product_key ];
+ } else {
+ $item_map[ $product_key ] = 1;
+ }
+
+ if ( array_key_exists( $product_key, $total_packed_item_map ) ) {
+ ++$total_packed_item_map[ $product_key ];
+ } else {
+ $total_packed_item_map[ $product_key ] = 1;
+ }
+
+ ++$total_packed_items;
+ }
+
+ $applied_rules[] = array(
+ 'packaging_id' => $packaging->get_id(),
+ 'rules' => $package_applied_rules,
+ 'items' => $item_map,
+ 'weight' => $weight + $packaging->getEmptyWeight(),
+ );
+
+ $rule_ids = array_unique( array_merge( $rule_ids, $package_applied_rules ) );
+ $packaging_ids = array_unique( array_merge( $packaging_ids, array( $packaging->get_id() ) ) );
+ }
+ }
+
+ if ( ! empty( $applied_rules ) ) {
+ if ( $is_debug_mode ) {
+ $package_count = 0;
+
+ foreach ( $applied_rules as $applied_rule ) {
+ if ( $packaging = wc_gzd_get_packaging( $applied_rule['packaging_id'] ) ) {
+ ++$package_count;
+ $debug_notices[] = sprintf( _x( '## Package %1$d/%2$d: %3$s: ', 'shipments', 'woocommerce-germanized' ), $package_count, count( $applied_rules ), $packaging->get_title() );
+
+ foreach ( $applied_rule['rules'] as $rule ) {
+ if ( $the_rule = $this->get_shipping_rule_by_id( $rule, $applied_rule['packaging_id'] ) ) {
+ $debug_notices[] = sprintf( _x( 'Rule %1$d: %2$s', 'shipments', 'woocommerce-germanized' ), $rule, wc_price( $the_rule['costs'] ) );
+ }
+ }
+
+ foreach ( $applied_rule['items'] as $item_product_key => $quantity ) {
+ $product_ids = explode( '_', $item_product_key );
+ $product_title = $product_ids[0];
+
+ if ( $product = wc_get_product( $product_ids[1] ) ) {
+ $product_title = $product->get_title();
+ }
+
+ $product_desc = ! empty( $product_ids[0] ) ? sprintf( _x( '%1$s (Parent: %2$s)', 'shipments', 'woocommerce-germanized' ), $product_title, $product_ids[0] ) : $product_title;
+ $debug_notices[] = sprintf( _x( '%1$s x %2$s', 'shipments', 'woocommerce-germanized' ), $quantity, $product_desc );
+ }
+ }
+ }
+
+ $debug_notices[] = sprintf( _x( '## Total: %1$s (%2$s, %3$s)', 'shipments', 'woocommerce-germanized' ), wc_price( $total_cost ), $cost_calculation_mode, $multiple_rules_calculation_mode );
+ }
+
+ $this->add_rate(
+ array(
+ 'cost' => $total_cost,
+ 'label' => $this->get_rate_label( $total_cost ),
+ 'package' => $package,
+ 'meta_data' => array(
+ '_packed_items' => $total_packed_items,
+ '_packed_item_map' => $total_packed_item_map,
+ '_packaging_ids' => $packaging_ids,
+ '_rule_ids' => $rule_ids,
+ '_packages' => $applied_rules,
+ ),
+ )
+ );
+ } elseif ( $is_debug_mode ) {
+ $debug_notices[] = _x( 'None of the available rules applied.', 'shipments', 'woocommerce-germanized' );
+ }
+ } elseif ( $is_debug_mode ) {
+ foreach ( $unpacked_items as $item ) {
+ $product_desc = $item->get_id();
+
+ if ( $product = $item->get_product() ) {
+ $product_desc = $product->get_title();
+ }
+
+ $debug_notices[] = sprintf( _x( '%1$s does not fit the available packaging options', 'shipments', 'woocommerce-germanized' ), $product_desc );
+ }
+ }
+
+ if ( $is_debug_mode && ! Package::is_constant_defined( 'WOOCOMMERCE_CHECKOUT' ) && ! Package::is_constant_defined( 'WC_DOING_AJAX' ) && ! empty( $debug_notices ) ) {
+ $the_notice = '';
+ $cart_wide_notice = '';
+ $available_box_list = array();
+ $cart_wide_notices = array();
+
+ $cart_wide_notices[] = _x( '### Items available to pack:', 'shipments', 'woocommerce-germanized' );
+
+ foreach ( $package['items_to_pack'] as $item_to_pack ) {
+ $cart_wide_notices[] = $item_to_pack->getDescription() . ' (' . wc_gzd_format_shipment_dimensions( $item_to_pack->get_dimensions(), 'mm' ) . ', ' . wc_gzd_format_shipment_weight( $item_to_pack->getWeight(), 'g' ) . ')';
+ }
+
+ foreach ( $cart_wide_notices as $notice ) {
+ $cart_wide_notice .= $notice . '
';
+ }
+
+ if ( ! wc_has_notice( $cart_wide_notice ) ) {
+ wc_add_notice( $cart_wide_notice );
+ }
+
+ foreach ( $available_boxes as $box ) {
+ $available_box_list[] = $box->get_packaging()->get_title();
+ }
+
+ $general_debug_notices = array(
+ sprintf( _x( '### Debug information for %1$s:', 'shipments', 'woocommerce-germanized' ), $this->get_title() ),
+ sprintf( _x( 'Available packaging options: %1$s', 'shipments', 'woocommerce-germanized' ), implode( ', ', $available_box_list ) ),
+ );
+
+ if ( empty( $applied_rules ) ) {
+ foreach ( $packed_boxes as $packed_box_index => $box ) {
+ $packaging = $box->getBox();
+ $general_debug_notices[] = sprintf( _x( '## Packed box %1$d/%2$d: %3$s', 'shipments', 'woocommerce-germanized' ), ++$packed_box_index, count( $packed_boxes ), $packaging->getReference() );
+ }
+ }
+
+ $debug_notices = array_merge( $general_debug_notices, $debug_notices );
+
+ foreach ( $debug_notices as $notice ) {
+ $the_notice .= $notice . '
';
+ }
+
+ if ( ! wc_has_notice( $the_notice ) ) {
+ wc_add_notice( $the_notice );
+ }
+ }
+ }
+ }
+
+ protected function parse_rule( $rule ) {
+ $rule = wp_parse_args(
+ $rule,
+ array(
+ 'rule_id' => '',
+ 'packaging' => '',
+ 'conditions' => array(),
+ 'costs' => 0.0,
+ 'meta' => array(),
+ )
+ );
+
+ $rule['costs'] = (float) wc_format_decimal( $rule['costs'] );
+
+ return $rule;
+ }
+
+ protected function parse_rule_condition( $condition ) {
+ $condition = wp_parse_args(
+ $condition,
+ array(
+ 'rule_id' => '',
+ 'condition_id' => '',
+ 'type' => '',
+ 'operator' => '',
+ )
+ );
+
+ $condition['type'] = sanitize_key( $condition['type'] );
+ $condition['operator'] = sanitize_key( $condition['operator'] );
+
+ return $condition;
+ }
+
+ protected function rule_applies( $rule, $package_data, $global_only = false ) {
+ $rule_applies = true;
+ $rule = $this->parse_rule( $rule );
+ $package_data = wp_parse_args(
+ $package_data,
+ array(
+ 'package_weight' => 0.0,
+ 'package_volume' => 0.0,
+ 'package_total' => 0.0,
+ 'package_subtotal' => 0.0,
+ 'package_products' => array(),
+ 'package_shipping_classes' => array(),
+ 'package_has_missing_shipping_classes' => false,
+ 'weight' => 0.0,
+ 'volume' => 0.0,
+ 'total' => 0.0,
+ 'subtotal' => 0.0,
+ 'products' => array(),
+ 'shipping_classes' => array(),
+ )
+ );
+
+ foreach ( $rule['conditions'] as $condition ) {
+ $condition = $this->parse_rule_condition( $condition );
+ $condition_applies = false;
+
+ if ( $condition_type = $this->get_condition_type( $condition['type'] ) ) {
+ $condition_type_name = $condition['type'];
+ $operator_name = $condition['operator'];
+
+ /**
+ * Skip non-global conditions, e.g. packaging conditions in case set.
+ */
+ if ( $global_only && ! $condition_type['is_global'] ) {
+ continue;
+ }
+
+ if ( $operator = $this->get_conditional_operator( $operator_name ) ) {
+ if ( $operator['is_negation'] ) {
+ $condition_applies = true;
+ }
+ }
+
+ if ( has_filter( "woocommerce_gzd_shipping_method_rule_condition_{$condition_type_name}_applies" ) ) {
+ $condition_applies = apply_filters( "woocommerce_gzd_shipping_method_rule_condition_{$condition_type_name}_applies", $package_data, $rule, $condition, $this );
+ } elseif ( 'always' === $condition_type_name ) {
+ $condition_applies = true;
+ } elseif ( 'weight' === $condition_type_name || 'package_weight' === $condition_type_name ) {
+ $from = isset( $condition['weight_from'] ) && ! empty( $condition['weight_from'] ) ? (float) wc_format_decimal( $condition['weight_from'] ) : 0.0;
+ $to = isset( $condition['weight_to'] ) && ! empty( $condition['weight_to'] ) ? (float) wc_format_decimal( $condition['weight_to'] ) : 0.0;
+
+ if ( $package_data[ $condition_type_name ] >= $from && ( $package_data[ $condition_type_name ] < $to || 0.0 === $to ) ) {
+ if ( 'is' === $operator_name ) {
+ $condition_applies = true;
+ } elseif ( 'is_not' === $operator_name ) {
+ $condition_applies = false;
+ }
+ }
+ } elseif ( 'total' === $condition_type_name || 'package_total' === $condition_type_name ) {
+ $from = isset( $condition['total_from'] ) && ! empty( $condition['total_from'] ) ? (float) wc_format_decimal( $condition['total_from'] ) : 0.0;
+ $to = isset( $condition['total_to'] ) && ! empty( $condition['total_to'] ) ? (float) wc_format_decimal( $condition['total_to'] ) : 0.0;
+
+ if ( $package_data[ $condition_type_name ] >= $from && ( $package_data[ $condition_type_name ] < $to || 0.0 === $to ) ) {
+ if ( 'is' === $operator_name ) {
+ $condition_applies = true;
+ } elseif ( 'is_not' === $operator_name ) {
+ $condition_applies = false;
+ }
+ }
+ } elseif ( 'shipping_classes' === $condition_type_name || 'package_shipping_classes' === $condition_type_name ) {
+ $classes = isset( $condition['classes'] ) && ! empty( $condition['classes'] ) ? apply_filters( 'woocommerce_gzd_shipments_shipping_method_shipping_classes', array_map( 'absint', (array) $condition['classes'] ) ) : array();
+
+ if ( 'exactly' === $operator_name ) {
+ $has_missing_shipping_classes = 'package_shipping_classes' === $condition_type_name ? $package_data['package_has_missing_shipping_classes'] : $package_data['has_missing_shipping_classes'];
+ $condition_applies = ! $has_missing_shipping_classes && $package_data[ $condition_type_name ] === $classes;
+ } elseif ( array_intersect( $package_data[ $condition_type_name ], $classes ) ) {
+ if ( 'any_of' === $operator_name ) {
+ $condition_applies = true;
+ } elseif ( 'none_of' === $operator_name ) {
+ $condition_applies = false;
+ }
+ }
+ }
+ }
+
+ if ( ! $condition_applies ) {
+ $rule_applies = false;
+ break;
+ }
+ }
+
+ return $rule_applies;
+ }
+
+ protected function get_packaging_list( $add_all_option = true ) {
+ $packaging_select = array();
+
+ foreach ( wc_gzd_get_packaging_list( array( 'shipping_provider' => $this->get_shipping_provider()->get_name() ) ) as $packaging ) {
+ $packaging_select[ $packaging->get_id() ] = $packaging->get_title();
+ }
+
+ if ( $add_all_option ) {
+ $packaging_select['all'] = _x( 'All remaining packaging', 'shipments', 'woocommerce-germanized' );
+ }
+
+ return $packaging_select;
+ }
+
+ protected function get_packaging_edit_url( $packaging ) {
+ $url = PackagingSettings::get_settings_url( $packaging );
+
+ if ( 'all' === $packaging ) {
+ $url = Settings::get_settings_url( 'packaging' );
+ }
+
+ return $url;
+ }
+
+ protected function get_packaging_help_tip( $packaging ) {
+ $help_tip = '';
+
+ if ( 'all' === $packaging ) {
+ $help_tip = _x( 'These rules will be parsed for all remaining, available packaging without rules and/or in case no rules matched.', 'shipments', 'woocommerce-germanized' );
+ }
+
+ return $help_tip;
+ }
+
+ protected function generate_cache_html() {
+ return '';
+ }
+
+ protected function validate_cache_field() {
+ return $this->get_updated_cache();
+ }
+
+ protected function get_updated_cache() {
+ $rules = $this->get_option( 'shipping_rules', array() );
+ $global_rules = array();
+ $costs = array();
+
+ foreach ( $rules as $packaging_id => $packaging_rules ) {
+ $costs[ $packaging_id ] = array(
+ 'min' => 0.0,
+ 'max' => 0.0,
+ 'avg' => 0.0,
+ );
+
+ foreach ( $packaging_rules as $packaging_rule ) {
+ /**
+ * Global rules
+ */
+ $is_global = true;
+
+ foreach ( $packaging_rule['conditions'] as $condition ) {
+ if ( $condition_type = $this->get_condition_type( $condition['type'] ) ) {
+ if ( ! $condition_type['is_global'] ) {
+ $is_global = false;
+ break;
+ }
+ }
+ }
+
+ if ( $is_global ) {
+ if ( ! array_key_exists( $packaging_id, $global_rules ) ) {
+ $global_rules[ $packaging_id ] = array();
+ }
+
+ $global_rules[ $packaging_id ][] = $packaging_rule['rule_id'];
+ }
+
+ /**
+ * Min, max costs
+ */
+ $cost = (float) wc_format_decimal( $packaging_rule['costs'] );
+
+ if ( $cost >= $costs[ $packaging_id ]['max'] ) {
+ $costs[ $packaging_id ]['max'] = $cost;
+ }
+
+ if ( $cost <= $costs[ $packaging_id ]['min'] || 0.0 === $costs[ $packaging_id ]['min'] ) {
+ $costs[ $packaging_id ]['min'] = $cost;
+ }
+
+ $costs[ $packaging_id ]['avg'] += $cost;
+ }
+
+ if ( count( $packaging_rules ) > 0 ) {
+ $costs[ $packaging_id ]['avg'] = $costs[ $packaging_id ]['avg'] / count( $packaging_rules );
+ }
+ }
+
+ $cache = array(
+ 'packaging_ids' => array_keys( $rules ),
+ 'global_rules' => $global_rules,
+ 'costs' => $costs,
+ );
+
+ return $cache;
+ }
+
+ protected function validate_shipping_rules_field( $option_name, $option_value ) {
+ $option_value = stripslashes_deep( $option_value );
+
+ if ( is_null( $option_value ) ) {
+ return $option_value;
+ }
+
+ $ids = array_keys( $option_value['costs'] );
+ $rules = array();
+ $condition_types = $this->get_condition_types();
+ $index = 0;
+
+ foreach ( $ids as $id ) {
+ $rule_id = $index++;
+ $packaging = 'all' === $option_value['packaging'][ $id ] ? 'all' : absint( $option_value['packaging'][ $id ] );
+ $costs = (float) wc_format_decimal( isset( $option_value['costs'][ $id ] ) ? wc_clean( $option_value['costs'][ $id ] ) : 0, false, true );
+
+ $rule = array(
+ 'rule_id' => $rule_id,
+ 'packaging' => $packaging,
+ 'conditions' => array(),
+ 'costs' => $costs,
+ 'meta' => array(),
+ );
+
+ $conditions = (array) $option_value['conditions'][ $id ];
+ $condition_index = 0;
+
+ foreach ( $conditions as $condition ) {
+ $condition_type = isset( $condition['type'] ) ? wc_clean( $condition['type'] ) : '';
+
+ if ( ! array_key_exists( $condition_type, $condition_types ) ) {
+ continue;
+ }
+
+ $condition_type_data = $condition_types[ $condition_type ];
+ $available_operators = $condition_type_data['operators'];
+ $operator = isset( $condition['operator'][ $condition_type ] ) ? wc_clean( $condition['operator'][ $condition_type ] ) : '';
+ $condition_id = $condition_index++;
+ $default_operator = empty( $available_operators ) ? '' : $available_operators[0];
+
+ $new_condition = array(
+ 'rule_id' => $rule_id,
+ 'type' => $condition_type,
+ 'condition_id' => $condition_id,
+ 'operator' => in_array( $operator, $available_operators, true ) ? $operator : $default_operator,
+ );
+
+ foreach ( $condition_type_data['fields'] as $field_name => $field ) {
+ $field = wp_parse_args(
+ $field,
+ array(
+ 'type' => '',
+ 'data_type' => '',
+ 'data_validation' => '',
+ )
+ );
+
+ $field_unique_id = "{$condition_type}_{$field_name}";
+ $validation_type = empty( $field['data_validation'] ) ? $condition_type : $field['data_validation'];
+ $rule[ $field_name ] = isset( $field['default'] ) ? $field['default'] : '';
+
+ if ( isset( $condition[ $field_unique_id ] ) ) {
+ $value = wc_clean( $condition[ $field_unique_id ] );
+
+ if ( has_filter( "woocommerce_gzd_shipping_method_rule_validate_{$validation_type}" ) ) {
+ $value = apply_filters( "woocommerce_gzd_shipping_method_rule_validate_{$validation_type}", $value, $field, $condition_type, $this );
+ } elseif ( 'weight' === $validation_type ) {
+ $unit = get_option( 'woocommerce_weight_unit', 'kg' );
+
+ if ( in_array( $unit, array( 'kg', 'g' ), true ) ) {
+ $decimals = 3;
+
+ if ( 'g' === $unit ) {
+ $decimals = 0;
+ }
+
+ $value = (float) wc_format_decimal( $value, $decimals, true );
+ } else {
+ $value = (float) wc_format_decimal( $value, false, true );
+ }
+ } elseif ( 'price' === $field['data_type'] ) {
+ $value = (float) wc_format_decimal( $value, wc_get_price_decimals(), true );
+ } elseif ( 'decimal' === $field['data_type'] ) {
+ $value = (float) wc_format_decimal( $value );
+ } elseif ( 'array' === $field['data_type'] ) {
+ $value = (array) $value;
+ }
+
+ $new_condition[ $field_name ] = $value;
+ }
+ }
+
+ $rule['conditions'][ "condition_{$condition_id}" ] = $new_condition;
+ }
+
+ if ( ! isset( $rules[ $packaging ] ) ) {
+ $rules[ $packaging ] = array();
+ }
+
+ $rules[ $packaging ][ "rule_{$rule_id}" ] = $rule;
+ }
+
+ return $rules;
+ }
+
+ protected function generate_shipping_rules_html( $option_name, $option ) {
+ ob_start();
+ $field_key = $this->get_field_key( 'shipping_rules' );
+ $condition_types = $this->get_condition_types();
+ ?>
+
+
+
+
+
+
+ instance_settings ) ) {
+ $this->init_instance_settings();
+ }
+
+ $this->instance_settings[ $key ] = $value;
+
+ return update_option( $this->get_instance_option_key(), apply_filters( 'woocommerce_shipping_' . $this->id . '_instance_settings_values', $this->instance_settings, $this ), 'yes' );
+ }
+}
diff --git a/packages/woocommerce-germanized-shipments/src/ShippingProvider/Auto.php b/packages/woocommerce-germanized-shipments/src/ShippingProvider/Auto.php
new file mode 100644
index 000000000..f12c44db4
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/src/ShippingProvider/Auto.php
@@ -0,0 +1,1727 @@
+ '',
+ 'label_auto_enable' => false,
+ 'label_auto_shipment_status' => 'gzd-processing',
+ 'label_return_auto_enable' => false,
+ 'label_return_auto_shipment_status' => 'gzd-processing',
+ 'label_auto_shipment_status_shipped' => false,
+ 'label_references' => array(),
+ 'pickup_locations_enable' => true,
+ 'pickup_locations_max_results' => 20,
+ 'configuration_sets' => array(),
+ );
+
+ public function get_label_default_shipment_weight( $context = 'view' ) {
+ return apply_filters( "{$this->get_hook_prefix()}label_default_shipment_weight", 0.5, $this );
+ }
+
+ protected function get_default_label_default_print_format() {
+ return '';
+ }
+
+ /**
+ * Returns the minimum weight applied to the label. Defaults to 1g.
+ *
+ * @param $context
+ *
+ * @return float
+ */
+ public function get_label_minimum_shipment_weight( $context = 'view' ) {
+ return apply_filters( "{$this->get_hook_prefix()}label_minimum_shipment_weight", 0.001, $this );
+ }
+
+ /**
+ * @param false|Shipment $shipment
+ *
+ * @return boolean
+ */
+ public function automatically_generate_label( $shipment = false ) {
+ if ( $shipment && 'return' === $shipment->get_type() ) {
+ return $this->automatically_generate_return_label();
+ } else {
+ return $this->get_label_auto_enable();
+ }
+ }
+
+ public function automatically_generate_return_label() {
+ return $this->get_label_return_auto_enable();
+ }
+
+ /**
+ * @param false|Shipment $shipment
+ *
+ * @return string
+ */
+ public function get_label_automation_shipment_status( $shipment = false ) {
+ if ( $shipment && 'return' === $shipment->get_type() ) {
+ return $this->get_label_return_auto_shipment_status();
+ }
+
+ return $this->get_label_auto_shipment_status();
+ }
+
+ public function automatically_set_shipment_status_shipped( $shipment = false ) {
+ return $this->get_label_auto_shipment_status_shipped();
+ }
+
+ public function get_label_auto_enable( $context = 'view' ) {
+ return $this->get_prop( 'label_auto_enable', $context );
+ }
+
+ public function get_label_auto_shipment_status_shipped( $context = 'view' ) {
+ return $this->get_prop( 'label_auto_shipment_status_shipped', $context );
+ }
+
+ public function get_label_auto_shipment_status( $context = 'view' ) {
+ return $this->get_prop( 'label_auto_shipment_status', $context );
+ }
+
+ public function get_pickup_locations_enable( $context = 'view' ) {
+ return $this->get_prop( 'pickup_locations_enable', $context );
+ }
+
+ public function get_pickup_locations_max_results( $context = 'view' ) {
+ return $this->get_prop( 'pickup_locations_max_results', $context );
+ }
+
+ public function enable_pickup_location_delivery() {
+ return $this->get_pickup_locations_enable();
+ }
+
+ public function get_label_references( $context = 'view' ) {
+ $references = array_filter( (array) $this->get_prop( 'label_references', $context ) );
+
+ if ( 'view' === $context ) {
+ foreach ( $this->get_supported_shipment_types() as $shipment_type ) {
+ if ( ! isset( $references[ $shipment_type ] ) || ! is_array( $references[ $shipment_type ] ) ) {
+ $references[ $shipment_type ] = array();
+ }
+
+ foreach ( $this->get_supported_label_reference_types( $shipment_type ) as $ref_type => $ref_type_data ) {
+ $ref_type_data = wp_parse_args(
+ $ref_type_data,
+ array(
+ 'default' => '',
+ )
+ );
+
+ if ( ! isset( $references[ $shipment_type ][ $ref_type ] ) ) {
+ $references[ $shipment_type ][ $ref_type ] = $ref_type_data['default'];
+ }
+ }
+ }
+ }
+
+ return $references;
+ }
+
+ public function get_supported_label_reference_types( $shipment_type = 'simple' ) {
+ return array();
+ }
+
+ /**
+ * @param $ref_type
+ * @param $shipment_type
+ *
+ * @return array|false
+ */
+ public function get_supported_label_reference_type( $ref_type, $shipment_type = 'simple' ) {
+ $ref_types = $this->get_supported_label_reference_types( $shipment_type );
+ $type = false;
+
+ if ( array_key_exists( $ref_type, $ref_types ) ) {
+ $type = wp_parse_args(
+ $ref_types[ $ref_type ],
+ array(
+ 'max_length' => -1,
+ 'label' => '',
+ 'default' => '',
+ )
+ );
+ }
+
+ return $type;
+ }
+
+ /**
+ * @param string $shipment_type
+ * @param Label|null $label
+ *
+ * @return array
+ */
+ public function get_label_reference_placeholders( $shipment_type = 'simple', $label = null ) {
+ $placeholders = array(
+ '{shipment_number}' => _x( 'Shipment number, e.g. 5', 'shipments', 'woocommerce-germanized' ),
+ '{order_number}' => _x( 'Order number, e.g. 1234', 'shipments', 'woocommerce-germanized' ),
+ '{item_count}' => _x( 'Number of items included in the shipment, e.g. 5', 'shipments', 'woocommerce-germanized' ),
+ );
+
+ if ( $label ) {
+ if ( $shipment = $label->get_shipment() ) {
+ $placeholders['{shipment_number}'] = $shipment->get_shipment_number();
+ $placeholders['{shipment_id}'] = $shipment->get_id();
+ $placeholders['{order_number}'] = $shipment->get_order_number();
+ $placeholders['{item_count}'] = $shipment->get_item_count();
+ }
+ }
+
+ return apply_filters( "{$this->get_hook_prefix()}label_reference_placeholders", $placeholders, $shipment_type, $label );
+ }
+
+ /**
+ * @param string $shipment_type
+ * @param string $reference_type
+ *
+ * @return string
+ */
+ public function get_label_reference( $shipment_type = 'simple', $reference_type = '', $context = 'view' ) {
+ $references = $this->get_label_references( $context );
+ $reference = '';
+
+ if ( array_key_exists( $shipment_type, $references ) ) {
+ $references = $references[ $shipment_type ];
+
+ if ( array_key_exists( $reference_type, $references ) ) {
+ $reference = $references[ $reference_type ];
+ }
+ }
+
+ return $reference;
+ }
+
+ /**
+ * @param Label $label
+ * @param string $shipment_type
+ * @param string $reference_type
+ *
+ * @return string
+ */
+ public function get_formatted_label_reference( $label, $shipment_type = 'simple', $reference_type = '' ) {
+ $reference = $this->get_label_reference( $shipment_type, $reference_type );
+ $max_length = -1;
+
+ if ( $ref_type = $this->get_supported_label_reference_type( $reference_type, $shipment_type ) ) {
+ $max_length = $ref_type['max_length'];
+ }
+
+ if ( ! empty( $reference ) ) {
+ $placeholders = $this->get_label_reference_placeholders( $shipment_type, $label );
+ $reference = str_replace( array_keys( $placeholders ), array_values( $placeholders ), $reference );
+ }
+
+ $formatted_ref = apply_filters( "{$this->get_hook_prefix()}formatted_label_reference", $reference, $label, $shipment_type, $reference_type );
+
+ if ( -1 !== $max_length ) {
+ $formatted_ref = wc_gzd_shipments_substring( $formatted_ref, 0, $max_length );
+ }
+
+ return $formatted_ref;
+ }
+
+ public function get_label_return_auto_enable( $context = 'view' ) {
+ return $this->get_prop( 'label_return_auto_enable', $context );
+ }
+
+ public function get_label_print_format( $context = 'view' ) {
+ return $this->get_prop( 'label_print_format', $context );
+ }
+
+ public function get_label_return_auto_shipment_status( $context = 'view' ) {
+ return $this->get_prop( 'label_return_auto_shipment_status', $context );
+ }
+
+ public function is_sandbox() {
+ return false;
+ }
+
+ public function set_label_auto_enable( $enable ) {
+ $this->set_prop( 'label_auto_enable', wc_string_to_bool( $enable ) );
+ }
+
+ public function set_label_auto_shipment_status_shipped( $enable ) {
+ $this->set_prop( 'label_auto_shipment_status_shipped', wc_string_to_bool( $enable ) );
+ }
+
+ public function set_label_auto_shipment_status( $status ) {
+ $this->set_prop( 'label_auto_shipment_status', $status );
+ }
+
+ public function set_label_references( $references ) {
+ $this->set_prop( 'label_references', $references );
+ }
+
+ public function set_label_return_auto_enable( $enable ) {
+ $this->set_prop( 'label_return_auto_enable', wc_string_to_bool( $enable ) );
+ }
+
+ public function set_label_print_format( $format ) {
+ $this->set_prop( 'label_print_format', $format );
+ }
+
+ public function set_label_return_auto_shipment_status( $status ) {
+ $this->set_prop( 'label_return_auto_shipment_status', $status );
+ }
+
+ public function set_pickup_locations_enable( $enable ) {
+ $this->set_prop( 'pickup_locations_enable', wc_string_to_bool( $enable ) );
+ }
+
+ public function set_pickup_locations_max_results( $max_results ) {
+ $this->set_prop( 'pickup_locations_max_results', absint( $max_results ) );
+ }
+
+ public function get_label_classname( $type ) {
+ $classname = '\Vendidero\Germanized\Shipments\Labels\Label';
+
+ if ( 'return' === $type ) {
+ $classname = '\Vendidero\Germanized\Shipments\Labels\ReturnLabel';
+ }
+
+ return $classname;
+ }
+
+ /**
+ * Whether or not this instance is a manual integration.
+ * Manual integrations are constructed dynamically from DB and do not support
+ * automatic shipment handling, e.g. label creation.
+ *
+ * @return bool
+ */
+ public function is_manual_integration() {
+ return false;
+ }
+
+ /**
+ * Whether or not this instance supports a certain label type.
+ *
+ * @param string $label_type The label type e.g. simple or return.
+ *
+ * @return bool
+ */
+ public function supports_labels( $label_type, $shipment = false ) {
+ return true;
+ }
+
+ /**
+ * @return null|\WP_Error|true
+ */
+ public function test_connection() {
+ return null;
+ }
+
+ /**
+ * @param \Vendidero\Germanized\Shipments\Shipment $shipment
+ *
+ * @return mixed|void
+ */
+ public function get_label( $shipment ) {
+ $type = wc_gzd_get_label_type_by_shipment( $shipment );
+ $label = wc_gzd_get_label_by_shipment( $shipment, $type );
+
+ return apply_filters( "{$this->get_hook_prefix()}label", $label, $shipment, $this );
+ }
+
+ /**
+ * @param \Vendidero\Germanized\Shipments\Shipment $shipment
+ */
+ public function get_label_fields_html( $shipment ) {
+ /**
+ * Setup local variables
+ */
+ $settings = $this->get_label_fields( $shipment );
+ $provider = $this;
+
+ if ( is_wp_error( $settings ) ) {
+ $error = $settings;
+
+ ob_start();
+ include Package::get_path() . '/includes/admin/views/label/html-shipment-label-backbone-error.php';
+ $html = ob_get_clean();
+ } else {
+ ob_start();
+ include Package::get_path() . '/includes/admin/views/label/html-shipment-label-backbone-form.php';
+ $html = ob_get_clean();
+ }
+
+ return apply_filters( "{$this->get_hook_prefix()}label_fields_html", $html, $shipment, $this );
+ }
+
+ public function get_section_help_link( $section ) {
+ $help_link = parent::get_section_help_link( $section );
+
+ if ( 'pickup_locations' === $section ) {
+ $help_link = 'https://vendidero.de/dokument/lieferung-an-eine-abholstation';
+ }
+
+ return $help_link;
+ }
+
+ protected function get_printing_settings() {
+ $settings = array(
+ array(
+ 'title' => _x( 'Label Format', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'title',
+ 'id' => 'shipping_provider_label_format_options',
+ ),
+ );
+
+ $settings = array_merge(
+ $settings,
+ array(
+ array(
+ 'title' => _x( 'Default Format', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'select',
+ 'id' => 'label_print_format',
+ 'default' => $this->get_default_label_default_print_format(),
+ 'desc' => _x( 'Set the default print format for a label.', 'shipments', 'woocommerce-germanized' ),
+ 'options' => $this->get_print_formats()->as_options(),
+ 'class' => 'wc-enhanced-select',
+ 'value' => $this->get_setting( 'label_print_format', $this->get_default_label_default_print_format() ),
+ ),
+ )
+ );
+
+ foreach ( $this->get_products( array( 'parent_id' => 0 ) ) as $product ) {
+ $settings = array_merge(
+ $settings,
+ array(
+ array(
+ 'title' => $product->get_label(),
+ 'type' => 'select',
+ 'id' => 'label_print_format_' . $product->get_id(),
+ 'default' => '',
+ 'custom_attributes' => array( 'data-placeholder' => _x( 'Same as default format', 'shipments', 'woocommerce-germanized' ) ),
+ 'options' => array( '' => _x( 'Same as default format', 'shipments', 'woocommerce-germanized' ) ) + $this->get_print_formats( array( 'product_id' => $product->get_id() ) )->as_options(),
+ 'class' => 'wc-enhanced-select-nostd',
+ 'value' => $this->get_setting( "label_print_format_{$product->get_id()}" ),
+ ),
+ )
+ );
+ }
+
+ $settings = array_merge(
+ $settings,
+ array(
+ array(
+ 'type' => 'sectionend',
+ 'id' => 'shipping_provider_label_format_options',
+ ),
+ )
+ );
+
+ return $settings;
+ }
+
+ protected function get_pickup_locations_settings() {
+ $settings = array(
+ array(
+ 'title' => _x( 'Pickup Locations', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'title',
+ 'id' => 'shipping_provider_pickup_locations_options',
+ ),
+ array(
+ 'title' => _x( 'Enable', 'shipments', 'woocommerce-germanized' ),
+ 'desc' => _x( 'Allow customers to choose a pickup location within checkout.', 'shipments', 'woocommerce-germanized' ),
+ 'id' => 'pickup_locations_enable',
+ 'type' => 'gzd_shipments_toggle',
+ 'default' => 'yes',
+ 'value' => wc_bool_to_string( $this->get_setting( 'pickup_locations_enable', 'yes' ) ),
+ ),
+ array(
+ 'title' => _x( 'Limit results', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'number',
+ 'id' => 'pickup_locations_max_results',
+ 'value' => $this->get_setting( 'pickup_locations_max_results', 20 ),
+ 'desc_tip' => _x( 'Limit the number of pickup locations presented to the customer.', 'shipments', 'woocommerce-germanized' ),
+ 'default' => 20,
+ 'css' => 'max-width: 60px;',
+ 'custom_attributes' => array(
+ 'max' => 50,
+ 'data-show_if_pickup_locations_enable' => '',
+ ),
+ ),
+ );
+
+ $settings = array_merge(
+ $settings,
+ array(
+ array(
+ 'type' => 'sectionend',
+ 'id' => 'shipping_provider_pickup_locations_options',
+ ),
+ )
+ );
+
+ return $settings;
+ }
+
+ protected function get_automation_settings() {
+ $settings = array(
+ array(
+ 'title' => _x( 'Automation', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'title',
+ 'id' => 'shipping_provider_label_auto_options',
+ ),
+ );
+
+ $shipment_statuses = array_diff_key( wc_gzd_get_shipment_statuses(), array_fill_keys( array( 'gzd-draft', 'gzd-delivered', 'gzd-returned', 'gzd-requested' ), '' ) );
+
+ $settings = array_merge(
+ $settings,
+ array(
+ array(
+ 'title' => _x( 'Labels', 'shipments', 'woocommerce-germanized' ),
+ 'desc' => _x( 'Automatically create labels for shipments.', 'shipments', 'woocommerce-germanized' ),
+ 'id' => 'label_auto_enable',
+ 'type' => 'gzd_shipments_toggle',
+ 'value' => wc_bool_to_string( $this->get_setting( 'label_auto_enable' ) ),
+ ),
+
+ array(
+ 'title' => _x( 'Status', 'shipments', 'woocommerce-germanized' ),
+ 'type' => 'select',
+ 'id' => 'label_auto_shipment_status',
+ 'desc' => '
+
+
+ get_packaging_list() as $name => $title ) : ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ';
+
+ foreach ( $label_placeholders as $placeholder => $title ) {
+ $formatted_placeholders .= sprintf( '
';
+
+ foreach ( $this->get_supported_label_reference_types( $shipment_type ) as $reference_type => $reference_type_data ) {
+ $reference_type_data = wp_parse_args(
+ (array) $reference_type_data,
+ array(
+ 'label' => '',
+ 'default' => '',
+ 'max_length' => -1,
+ )
+ );
+ $shipment_label_title = wc_gzd_get_shipment_label_title( $shipment_type );
+
+ $reference_settings[] = array(
+ 'title' => $reference_type_data['label'],
+ 'desc' => '%s
: %s' . implode( ', ', array_keys( $this->get_tracking_placeholders() ) ) . '
' ) . '' . implode( ', ', array_keys( $this->get_tracking_placeholders() ) ) . '
' ) . '[gzd_return_request_form]
' ) . '
+
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/email-return-shipment-instructions.php b/packages/woocommerce-germanized-shipments/templates/emails/email-return-shipment-instructions.php
new file mode 100644
index 000000000..2789a1d36
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/email-return-shipment-instructions.php
@@ -0,0 +1,26 @@
+get_shipping_provider_instance();
+?>
+
+has_return_instructions() ) : ?>
+
+
+
+ $shipment ) :
+ ++$count;
+ ?>
+ 1 ) : ?>
+
+
+
+ get_est_delivery_date() ) : ?>
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/email-shipment-details.php b/packages/woocommerce-germanized-shipments/templates/emails/email-shipment-details.php
new file mode 100644
index 000000000..a6ed2569d
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/email-shipment-details.php
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+ get_formatted_address() ); ?>
+
+
+
+ get_edit_shipment_url() ) . '">';
+ $after = '';
+ } else {
+ $before = '';
+ $after = '';
+ }
+ /* translators: %s: Order ID. */
+ echo wp_kses_post( $before . ( ! $sent_to_admin ? sprintf( _x( 'Details to your %s', 'shipments', 'woocommerce-germanized' ), wc_gzd_get_shipment_label_title( $shipment->get_type() ) ) : sprintf( _x( '[%1$s #%2$s]', 'shipments', 'woocommerce-germanized' ), wc_gzd_get_shipment_label_title( $shipment->get_type() ), $shipment->get_shipment_number() ) ) . $after );
+ ?>
+
+
+
+
+
+
+
+
+
+ $sent_to_admin,
+ 'show_image' => false,
+ 'image_size' => array( 32, 32 ),
+ 'plain_text' => $plain_text,
+ 'sent_to_admin' => $sent_to_admin,
+ )
+ );
+ ?>
+
+
+
+
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/email-shipment-tracking.php b/packages/woocommerce-germanized-shipments/templates/emails/email-shipment-tracking.php
new file mode 100644
index 000000000..4d5cf3d4a
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/email-shipment-tracking.php
@@ -0,0 +1,42 @@
+
+
+
+ get_name(), $item, false ) );
+
+ // SKU.
+ if ( $show_sku && $sku ) {
+ echo wp_kses_post( ' (#' . $sku . ')' );
+ }
+
+ /*
+ * Action that fires while outputting meta data for a ShipmentItem table display in an Email.
+ *
+ * @param integer $item_id The shipment item id.
+ * @param \Vendidero\Germanized\Shipments\ShipmentItem $item The shipment item instance.
+ * @param \Vendidero\Germanized\Shipments\Shipment $shipment The shipment instance.
+ * @param boolean $plain_text Whether this email is in plaintext format or not.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ do_action( 'woocommerce_gzd_shipment_item_meta', $item_id, $item, $shipment, $plain_text );
+
+ ?>
+
+
+ get_quantity(), $item ) );
+ ?>
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/admin-new-return-shipment-request.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/admin-new-return-shipment-request.php
new file mode 100644
index 000000000..14481e946
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/admin-new-return-shipment-request.php
@@ -0,0 +1,41 @@
+get_formatted_sender_full_name() ) ) . "\n\n";
+
+echo "\n\n";
+
+/* This hook is documented in templates/emails/customer-shipment.php */
+do_action( 'woocommerce_gzd_email_shipment_details', $shipment, $sent_to_admin, $plain_text, $email );
+
+/**
+ * Show user-defined additional content - this is set in each email's settings.
+ */
+if ( $additional_content ) {
+ echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
+ echo "\n\n----------------------------------------\n\n";
+}
+
+echo "\n----------------------------------------\n\n";
+
+echo wp_kses_post( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-guest-return-shipment-request.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-guest-return-shipment-request.php
new file mode 100644
index 000000000..132b76d58
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-guest-return-shipment-request.php
@@ -0,0 +1,40 @@
+get_billing_first_name() ) ) . "\n\n"; // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
+
+echo sprintf( esc_html_x( 'You\'ve requested a return to your order %s. Please follow the link to add your return request.', 'shipments', 'woocommerce-germanized' ), esc_html( $order->get_order_number() ) ) . "\n\n";
+
+echo esc_url( $add_return_request_url ) . "\n\n";
+
+/**
+ * Show user-defined additional content - this is set in each email's settings.
+ */
+if ( $additional_content ) {
+ echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
+ echo "\n\n----------------------------------------\n\n";
+}
+
+echo "\n----------------------------------------\n\n";
+
+echo wp_kses_post( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-return-shipment-delivered.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-return-shipment-delivered.php
new file mode 100644
index 000000000..4c5660934
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-return-shipment-delivered.php
@@ -0,0 +1,43 @@
+get_billing_first_name() ) ) . "\n\n"; // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
+
+echo esc_html_x( 'Thank you! Your return has been received successfully. There are more details below for your reference:', 'shipments', 'woocommerce-germanized' );
+
+echo "\n\n";
+
+/* This hook is documented in templates/emails/customer-shipment.php */
+do_action( 'woocommerce_gzd_email_shipment_details', $shipment, $sent_to_admin, $plain_text, $email );
+
+/**
+ * Show user-defined additional content - this is set in each email's settings.
+ */
+if ( $additional_content ) {
+ echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
+ echo "\n\n----------------------------------------\n\n";
+}
+
+echo "\n----------------------------------------\n\n";
+
+echo wp_kses_post( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-return-shipment.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-return-shipment.php
new file mode 100644
index 000000000..6f2fcdcd7
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-return-shipment.php
@@ -0,0 +1,48 @@
+get_billing_first_name() ) ) . "\n\n"; // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
+
+
+if ( $is_confirmation ) {
+ echo esc_html_x( 'Your return request has been accepted. Please follow the instructions beneath to return your shipment.', 'shipments', 'woocommerce-germanized' );
+} else {
+ echo esc_html_x( 'A new return has been added to your order. Please follow the instructions beneath to return your shipment.', 'shipments', 'woocommerce-germanized' );
+}
+
+echo "\n\n";
+
+/* This hook is documented in templates/emails/customer-shipment.php */
+do_action( 'woocommerce_gzd_email_shipment_details', $shipment, $sent_to_admin, $plain_text, $email );
+
+/**
+ * Show user-defined additional content - this is set in each email's settings.
+ */
+if ( $additional_content ) {
+ echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
+ echo "\n\n----------------------------------------\n\n";
+}
+
+echo "\n----------------------------------------\n\n";
+
+echo wp_kses_post( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-shipment.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-shipment.php
new file mode 100644
index 000000000..c568b43b0
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/customer-shipment.php
@@ -0,0 +1,49 @@
+get_billing_first_name() ) ) . "\n\n"; // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch
+
+if ( $partial_shipment ) {
+ /* translators: %s: Site title */
+ printf( esc_html_x( 'Your order on %1$s has been partially shipped via %2$s. Find details below for your reference:', 'shipments', 'woocommerce-germanized' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), wc_gzd_get_shipment_shipping_provider_title( $shipment ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+} else {
+ /* translators: %s: Site title */
+ printf( esc_html_x( 'Your order on %1$s has been shipped via %2$s. Find details below for your reference:', 'shipments', 'woocommerce-germanized' ), wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), wc_gzd_get_shipment_shipping_provider_title( $shipment ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+}
+
+echo "\n\n";
+
+/* This hook is documented in templates/emails/customer-shipment.php */
+do_action( 'woocommerce_gzd_email_shipment_details', $shipment, $sent_to_admin, $plain_text, $email );
+
+/**
+ * Show user-defined additional content - this is set in each email's settings.
+ */
+if ( $additional_content ) {
+ echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
+ echo "\n\n----------------------------------------\n\n";
+}
+
+echo "\n----------------------------------------\n\n";
+
+echo wp_kses_post( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/email-order-shipments.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-order-shipments.php
new file mode 100644
index 000000000..53fd7fe77
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-order-shipments.php
@@ -0,0 +1,47 @@
+ $shipment ) {
+ ++$count;
+
+ echo "\n";
+
+ if ( count( $shipments ) > 1 ) {
+ echo sprintf( esc_html_x( 'Shipment %1$d of %2$d', 'shipments', 'woocommerce-germanized' ), esc_html( $count ), esc_html( count( $shipments ) ) ) . "\n\n";
+ }
+
+ if ( $shipment->get_est_delivery_date() ) {
+ echo esc_html( _x( 'Estimated date:', 'shipments', 'woocommerce-germanized' ) ) . ' ' . esc_html( wc_format_datetime( $shipment->get_est_delivery_date(), wc_date_format() ) ) . "\n\n";
+ }
+
+ if ( $shipment->has_tracking() ) {
+ if ( $shipment->get_tracking_url() ) {
+ echo esc_html( _x( 'Track your shipment', 'shipments', 'woocommerce-germanized' ) ) . ': ' . esc_url( $shipment->get_tracking_url() ) . "\n";
+ }
+
+ if ( $shipment->has_tracking_instruction() ) {
+ echo esc_html( $shipment->get_tracking_instruction( true ) ) . "\n";
+ }
+ } else {
+ echo esc_html( _x( 'Sorry, this shipment does currently not support tracking.', 'shipments', 'woocommerce-germanized' ) ) . "\n";
+ }
+}
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/email-return-shipment-instructions.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-return-shipment-instructions.php
new file mode 100644
index 000000000..ee24b7d3b
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-return-shipment-instructions.php
@@ -0,0 +1,23 @@
+get_shipping_provider_instance();
+
+if ( $provider && $provider->has_return_instructions() ) {
+ echo wp_kses_post( wpautop( wptexturize( $provider->get_return_instructions() ) ) . PHP_EOL );
+}
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-address.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-address.php
new file mode 100644
index 000000000..2eaac15ae
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-address.php
@@ -0,0 +1,20 @@
+#i', "\n", $shipment->get_formatted_address() ) . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-details.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-details.php
new file mode 100644
index 000000000..51c4b8206
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-details.php
@@ -0,0 +1,43 @@
+get_type() ) ) : sprintf( _x( '[%1$s #%2$s]', 'shipments', 'woocommerce-germanized' ), wc_gzd_get_shipment_label_title( $shipment->get_type() ), $shipment->get_shipment_number() ) ) ) ) . "\n";
+echo "\n" . wc_gzd_get_email_shipment_items( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ $shipment,
+ array(
+ 'show_sku' => $sent_to_admin,
+ 'show_image' => false,
+ 'image_size' => array( 32, 32 ),
+ 'plain_text' => true,
+ 'sent_to_admin' => $sent_to_admin,
+ )
+);
+
+echo "==========\n\n";
+
+if ( $sent_to_admin ) {
+ /* translators: %s: Shipment link. */
+ echo "\n" . sprintf( esc_html_x( 'View shipment: %s', 'shipments', 'woocommerce-germanized' ), esc_url( $shipment->get_edit_shipment_url() ) ) . "\n";
+}
+
+/* This hook is documented in templates/emails/customer-shipment.php */
+do_action( 'woocommerce_gzd_email_after_shipment_table', $shipment, $sent_to_admin, $plain_text, $email );
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-items.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-items.php
new file mode 100644
index 000000000..2fa1d8308
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-items.php
@@ -0,0 +1,46 @@
+ $item ) :
+ $product = $item->get_product();
+ $sku = $item->get_sku();
+ $purchase_note = '';
+
+ /* This filter is documented in templates/emails/email-shipment-items.php */
+ if ( ! apply_filters( 'woocommerce_gzd_shipment_item_visible', true, $item ) ) {
+ continue;
+ }
+
+ /* This filter is documented in templates/emails/email-shipment-items.php */
+ echo wp_kses_post( apply_filters( 'woocommerce_gzd_shipment_item_name', $item->get_name(), $item, false ) );
+
+ if ( $show_sku && $sku ) {
+ echo ' (#' . esc_html( $sku ) . ')';
+ }
+
+ /* This filter is documented in templates/emails/email-shipment-items.php */
+ echo ' X ' . wp_kses_post( apply_filters( 'woocommerce_gzd_email_shipment_item_quantity', $item->get_quantity(), $item ) );
+ echo "\n";
+
+ /* This hook is documented in templates/emails/email-shipment-items.php */
+ do_action( 'woocommerce_gzd_shipment_item_meta', $item_id, $item, $shipment, $plain_text );
+
+ echo "\n\n";
+endforeach;
diff --git a/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-tracking.php b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-tracking.php
new file mode 100644
index 000000000..403a94c5a
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/emails/plain/email-shipment-tracking.php
@@ -0,0 +1,33 @@
+get_est_delivery_date() ) {
+ echo esc_html( _x( 'Estimated date:', 'shipments', 'woocommerce-germanized' ) ) . ' ' . esc_html( wc_format_datetime( $shipment->get_est_delivery_date(), wc_date_format() ) ) . "\n\n";
+}
+
+if ( $shipment->get_tracking_url() ) {
+ echo esc_html( _x( 'Track your shipment', 'shipments', 'woocommerce-germanized' ) ) . ': ' . esc_url( $shipment->get_tracking_url() ) . "\n";
+}
+
+if ( $shipment->has_tracking_instruction() ) {
+ echo esc_html( $shipment->get_tracking_instruction( true ) ) . "\n";
+}
diff --git a/packages/woocommerce-germanized-shipments/templates/global/empty.php b/packages/woocommerce-germanized-shipments/templates/global/empty.php
new file mode 100644
index 000000000..cdf140556
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/global/empty.php
@@ -0,0 +1,19 @@
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/myaccount/add-return-shipment.php b/packages/woocommerce-germanized-shipments/templates/myaccount/add-return-shipment.php
new file mode 100644
index 000000000..2b8bab9d9
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/myaccount/add-return-shipment.php
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/myaccount/order-shipments.php b/packages/woocommerce-germanized-shipments/templates/myaccount/order-shipments.php
new file mode 100644
index 000000000..ed4571e3e
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/myaccount/order-shipments.php
@@ -0,0 +1,53 @@
+
+
+
+
+
+ 'simple',
+ 'shipments' => $shipments,
+ 'order' => $order,
+ )
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+ 'return',
+ 'shipments' => $returns,
+ 'order' => $order,
+ )
+ );
+ ?>
+
diff --git a/packages/woocommerce-germanized-shipments/templates/myaccount/shipments.php b/packages/woocommerce-germanized-shipments/templates/myaccount/shipments.php
new file mode 100644
index 000000000..58d5a4432
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/myaccount/shipments.php
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+ get_est_delivery_date() ) : ?>
+
+
+
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/myaccount/view-shipment.php b/packages/woocommerce-germanized-shipments/templates/myaccount/view-shipment.php
new file mode 100644
index 000000000..aa6bea514
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/myaccount/view-shipment.php
@@ -0,0 +1,42 @@
+
+
+ $column_name ) : ?>
+
+
+
+
+ get_item_count();
+ ?>
+
+
+
+ get_type() ) as $column_id => $column_name ) : ?>
+
+
+
+
+
+
+
+
+ get_order_id() ) ) : ?>
+
+ get_type() ), $shipment->get_shipment_number() ) ); ?>
+
+
+ get_type() ), $shipment->get_shipment_number() ) ); ?>
+
+
+
+
+
+ get_status() ) ); ?>
+
+ has_tracking() && ! $shipment->has_status( 'delivered' ) ) : ?>
+
+
+
+
+
+ $action ) { // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ echo '' . esc_html( $action['name'] ) . '';
+ }
+ }
+ ?>
+
+
+
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/shipment/shipment-details-address.php b/packages/woocommerce-germanized-shipments/templates/shipment/shipment-details-address.php
new file mode 100644
index 000000000..6e0b8713c
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/shipment/shipment-details-address.php
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+ get_product();
+ $is_visible = $product && $product->is_visible();
+ $item_sku = $item->get_sku();
+
+ /** This filter is documented in templates/myaccount/shipment/shipment-details-item.php */
+ $product_permalink = apply_filters( 'woocommerce_gzd_shipment_item_permalink', $is_visible ? $product->get_permalink() : '', $item, $order );
+
+ /** This filter is documented in templates/emails/email-shipment-items.php */
+ echo apply_filters( 'woocommerce_gzd_shipment_item_name', ( $product_permalink ? sprintf( '%s', esc_url( $product_permalink ), $item->get_name() ) : $item->get_name() ) . ( ! empty( $item_sku ) ? ' (' . esc_html( $item_sku ) . ')' : '' ), $item, $is_visible ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ ?>
+
+
+
+
+
+
+
+
+ 1
+
+ 'item[' . esc_attr( $order_item_id ) . '][quantity]',
+ 'input_value' => 1,
+ 'max_value' => $max_quantity,
+ 'min_value' => 1,
+ ),
+ $item->get_product()
+ );
+ ?>
+
+
+
diff --git a/packages/woocommerce-germanized-shipments/templates/shipment/shipment-details-tracking.php b/packages/woocommerce-germanized-shipments/templates/shipment/shipment-details-tracking.php
new file mode 100644
index 000000000..6cb603862
--- /dev/null
+++ b/packages/woocommerce-germanized-shipments/templates/shipment/shipment-details-tracking.php
@@ -0,0 +1,44 @@
+
+
+ is_visible();
+ $item_sku = $item->get_sku();
+
+ /**
+ * This filter may adjust the shipment item permalink on the customer account page.
+ *
+ * @param string $permalink The permalink.
+ * @param ShipmentItem $item The shipment item instance.
+ * @param Shipment $shipment The shipment instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ $product_permalink = apply_filters( 'woocommerce_gzd_shipment_item_permalink', $is_visible ? $product->get_permalink() : '', $item, $shipment );
+
+ /** This filter is documented in templates/emails/email-shipment-items.php */
+ echo apply_filters( 'woocommerce_gzd_shipment_item_name', ( $product_permalink ? sprintf( '%s', esc_url( $product_permalink ), $item->get_name() ) : $item->get_name() ) . ( ! empty( $item_sku ) ? ' (' . esc_html( $item_sku ) . ')' : '' ), $item, $is_visible ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ ?>
+
+
+
+ get_quantity();
+ $qty_display = esc_html( $qty );
+
+ /**
+ * This filter may adjust the shipment item quantity HTML on the customer account page.
+ *
+ * @param string $html The HTML output.
+ * @param ShipmentItem $item The shipment item instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ echo apply_filters( 'woocommerce_gzd_shipment_item_quantity_html', ' ' . sprintf( '× %s', $qty_display ) . '', $item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+ $item ) {
+ $product = $item->get_product();
+
+ wc_get_template(
+ 'shipment/shipment-details-item.php',
+ array(
+ 'shipment' => $shipment,
+ 'item_id' => $item_id,
+ 'item' => $item,
+ 'product' => $product,
+ )
+ );
+ }
+
+ /**
+ * This action is executed after printing the shipment table items on the customer account page.
+ *
+ * @param Shipment $shipment The shipment instance.
+ *
+ * @since 3.0.0
+ * @package Vendidero/Germanized/Shipments
+ */
+ do_action( 'woocommerce_gzd_shipment_details_after_shipment_table_items', $shipment );
+ ?>
+
+
+
+ ' . esc_html( str_replace( ABSPATH, '', __DIR__ ) ) . '
'
+ );
+ ?>
+