mirror of
https://github.com/woodchen-ink/czlexpress-for-woocommerce.git
synced 2025-07-18 14:01:58 +08:00
first commit
This commit is contained in:
commit
6fa8720de1
31
admin/views/admin-page.php
Normal file
31
admin/views/admin-page.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
$tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'settings';
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<nav class="nav-tab-wrapper">
|
||||
<a href="?page=czl-express&tab=settings" class="nav-tab <?php echo $tab === 'settings' ? 'nav-tab-active' : ''; ?>">
|
||||
<?php _e('基本设置', 'woo-czl-express'); ?>
|
||||
</a>
|
||||
<a href="?page=czl-express&tab=test" class="nav-tab <?php echo $tab === 'test' ? 'nav-tab-active' : ''; ?>">
|
||||
<?php _e('接口测试', 'woo-czl-express'); ?>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<div class="tab-content">
|
||||
<?php
|
||||
switch ($tab) {
|
||||
case 'test':
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/api-test.php';
|
||||
break;
|
||||
default:
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/settings.php';
|
||||
break;
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
132
admin/views/api-settings.php
Normal file
132
admin/views/api-settings.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
$settings = array(
|
||||
array(
|
||||
'title' => __('API Settings', 'woo-czl-express'),
|
||||
'type' => 'title',
|
||||
'id' => 'czl_api_settings'
|
||||
),
|
||||
array(
|
||||
'title' => __('API URL', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'id' => 'czl_api_url',
|
||||
'desc' => __('Enter the CZL Express API URL', 'woo-czl-express'),
|
||||
'default' => ''
|
||||
),
|
||||
array(
|
||||
'title' => __('Username', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'id' => 'czl_username',
|
||||
'desc' => __('Enter your CZL Express API username', 'woo-czl-express'),
|
||||
'default' => ''
|
||||
),
|
||||
array(
|
||||
'title' => __('Password', 'woo-czl-express'),
|
||||
'type' => 'password',
|
||||
'id' => 'czl_password',
|
||||
'desc' => __('Enter your CZL Express API password', 'woo-czl-express'),
|
||||
'default' => ''
|
||||
),
|
||||
array(
|
||||
'title' => __('Warehouse Settings', 'woo-czl-express'),
|
||||
'type' => 'title',
|
||||
'id' => 'czl_warehouse_settings'
|
||||
),
|
||||
array(
|
||||
'title' => __('Province', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'id' => 'czl_warehouse_province',
|
||||
'desc' => __('Enter warehouse province', 'woo-czl-express'),
|
||||
'default' => ''
|
||||
),
|
||||
array(
|
||||
'title' => __('City', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'id' => 'czl_warehouse_city',
|
||||
'desc' => __('Enter warehouse city', 'woo-czl-express'),
|
||||
'default' => ''
|
||||
),
|
||||
array(
|
||||
'title' => __('Address', 'woo-czl-express'),
|
||||
'type' => 'textarea',
|
||||
'id' => 'czl_warehouse_address',
|
||||
'desc' => __('Enter warehouse address', 'woo-czl-express'),
|
||||
'default' => ''
|
||||
),
|
||||
array('type' => 'sectionend', 'id' => 'czl_api_settings'),
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields('czl_api_options');
|
||||
do_settings_sections('czl_api_options');
|
||||
|
||||
WC_Admin_Settings::output_fields($settings);
|
||||
|
||||
submit_button();
|
||||
?>
|
||||
</form>
|
||||
|
||||
<div class="czl-api-test">
|
||||
<h2><?php _e('API Connection Test', 'woo-czl-express'); ?></h2>
|
||||
<button type="button" class="button" id="czl-test-connection">
|
||||
<?php _e('Test Connection', 'woo-czl-express'); ?>
|
||||
</button>
|
||||
<button type="button" class="button" id="czl-test-shipping-rate">
|
||||
<?php _e('Test Shipping Rate', 'woo-czl-express'); ?>
|
||||
</button>
|
||||
<div id="czl-test-result"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
$('#czl-test-connection').on('click', function() {
|
||||
var $button = $(this);
|
||||
var $result = $('#czl-test-result');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
$result.html('<?php _e('Testing...', 'woo-czl-express'); ?>');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
action: 'czl_test_connection',
|
||||
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
|
||||
}, function(response) {
|
||||
$button.prop('disabled', false);
|
||||
if (response.success) {
|
||||
$result.html('<div class="notice notice-success"><p>' + response.data.message + '</p></div>');
|
||||
} else {
|
||||
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#czl-test-shipping-rate').on('click', function() {
|
||||
var $button = $(this);
|
||||
var $result = $('#czl-test-result');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
$result.html('<?php _e('Testing...', 'woo-czl-express'); ?>');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
action: 'czl_test_shipping_rate',
|
||||
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
|
||||
}, function(response) {
|
||||
$button.prop('disabled', false);
|
||||
if (response.success) {
|
||||
var html = '<div class="notice notice-success"><p><?php _e('Shipping rates retrieved successfully:', 'woo-czl-express'); ?></p>';
|
||||
html += '<pre>' + JSON.stringify(response.data, null, 2) + '</pre></div>';
|
||||
$result.html(html);
|
||||
} else {
|
||||
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
69
admin/views/api-test.php
Normal file
69
admin/views/api-test.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
?>
|
||||
|
||||
<div class="czl-api-test">
|
||||
<h2><?php _e('API连接测试', 'woo-czl-express'); ?></h2>
|
||||
|
||||
<p class="description">
|
||||
<?php _e('点击下面的按钮测试与CZL Express API的连接。', 'woo-czl-express'); ?>
|
||||
</p>
|
||||
|
||||
<div class="test-buttons">
|
||||
<button type="button" class="button" id="czl-test-connection">
|
||||
<?php _e('测试连接', 'woo-czl-express'); ?>
|
||||
</button>
|
||||
|
||||
<button type="button" class="button" id="czl-test-shipping-rate">
|
||||
<?php _e('测试运费查询', 'woo-czl-express'); ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="czl-test-result"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
$('#czl-test-connection').on('click', function() {
|
||||
var $button = $(this);
|
||||
var $result = $('#czl-test-result');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
$result.html('<?php _e('测试中...', 'woo-czl-express'); ?>');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
action: 'czl_test_connection',
|
||||
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
|
||||
}, function(response) {
|
||||
$button.prop('disabled', false);
|
||||
if (response.success) {
|
||||
$result.html('<div class="notice notice-success"><p>' + response.data.message + '</p></div>');
|
||||
} else {
|
||||
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#czl-test-shipping-rate').on('click', function() {
|
||||
var $button = $(this);
|
||||
var $result = $('#czl-test-result');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
$result.html('<?php _e('测试中...', 'woo-czl-express'); ?>');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
action: 'czl_test_shipping_rate',
|
||||
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
|
||||
}, function(response) {
|
||||
$button.prop('disabled', false);
|
||||
if (response.success) {
|
||||
var html = '<div class="notice notice-success"><p><?php _e('运费查询成功:', 'woo-czl-express'); ?></p>';
|
||||
html += '<pre>' + JSON.stringify(response.data, null, 2) + '</pre></div>';
|
||||
$result.html(html);
|
||||
} else {
|
||||
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
203
admin/views/exchange-rates.php
Normal file
203
admin/views/exchange-rates.php
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
$current_currency = get_woocommerce_currency();
|
||||
$supported_currencies = get_woocommerce_currencies();
|
||||
$exchange_rates = array();
|
||||
|
||||
// 获取所有已保存的汇率
|
||||
foreach ($supported_currencies as $code => $name) {
|
||||
if ($code !== 'CNY') {
|
||||
$rate = get_option('czl_exchange_rate_' . $code, '');
|
||||
if ($rate !== '') {
|
||||
$exchange_rates[$code] = $rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="wrap czl-exchange-rates-page">
|
||||
<h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<?php settings_errors(); ?>
|
||||
|
||||
<div class="czl-page-description">
|
||||
<p>
|
||||
<?php _e('在这里设置人民币(CNY)与其他货币的汇率。运费将根据这些汇率自动转换。', 'woo-czl-express'); ?>
|
||||
<?php printf(__('当前商店使用的货币是: %s', 'woo-czl-express'), '<strong>' . $current_currency . '</strong>'); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field('czl_save_exchange_rates', 'czl_exchange_rates_nonce'); ?>
|
||||
|
||||
<div class="czl-table-container">
|
||||
<table class="widefat czl-exchange-rates" id="czl-exchange-rates">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="column-currency"><?php _e('货币', 'woo-czl-express'); ?></th>
|
||||
<th class="column-rate"><?php _e('汇率 (1 CNY =)', 'woo-czl-express'); ?></th>
|
||||
<th class="column-actions"><?php _e('操作', 'woo-czl-express'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($exchange_rates)) : ?>
|
||||
<?php foreach ($exchange_rates as $code => $rate): ?>
|
||||
<tr>
|
||||
<td class="column-currency">
|
||||
<select name="rates[<?php echo esc_attr($code); ?>][currency]" class="currency-select">
|
||||
<?php
|
||||
foreach ($supported_currencies as $currency_code => $currency_name) {
|
||||
if ($currency_code !== 'CNY') {
|
||||
printf(
|
||||
'<option value="%s" %s>%s (%s)</option>',
|
||||
esc_attr($currency_code),
|
||||
selected($currency_code, $code, false),
|
||||
esc_html($currency_name),
|
||||
esc_html($currency_code)
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</td>
|
||||
<td class="column-rate">
|
||||
<input type="number" name="rates[<?php echo esc_attr($code); ?>][rate]"
|
||||
value="<?php echo esc_attr($rate); ?>" step="0.000001" min="0" class="regular-text">
|
||||
</td>
|
||||
<td class="column-actions">
|
||||
<button type="button" class="button remove-rate" title="<?php esc_attr_e('删除此汇率', 'woo-czl-express'); ?>">
|
||||
<span class="dashicons dashicons-trash"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<button type="button" class="button add-rate">
|
||||
<span class="dashicons dashicons-plus-alt2"></span>
|
||||
<?php _e('添加汇率', 'woo-czl-express'); ?>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php submit_button(__('保存汇率设置', 'woo-czl-express')); ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="rate-row-template">
|
||||
<tr>
|
||||
<td class="column-currency">
|
||||
<select name="rates[{{index}}][currency]" class="currency-select">
|
||||
<?php
|
||||
foreach ($supported_currencies as $code => $name) {
|
||||
if ($code !== 'CNY') {
|
||||
printf(
|
||||
'<option value="%s">%s (%s)</option>',
|
||||
esc_attr($code),
|
||||
esc_html($name),
|
||||
esc_html($code)
|
||||
);
|
||||
}
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</td>
|
||||
<td class="column-rate">
|
||||
<input type="number" name="rates[{{index}}][rate]" value="" step="0.000001" min="0" class="regular-text">
|
||||
</td>
|
||||
<td class="column-actions">
|
||||
<button type="button" class="button remove-rate" title="<?php esc_attr_e('删除此汇率', 'woo-czl-express'); ?>">
|
||||
<span class="dashicons dashicons-trash"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.czl-exchange-rates-page {
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.czl-page-description {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-left: 4px solid #2271b1;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.czl-table-container {
|
||||
margin: 20px 0;
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.czl-exchange-rates {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.czl-exchange-rates th {
|
||||
padding: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.czl-exchange-rates td {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.column-currency {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.column-rate {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.column-actions {
|
||||
width: 20%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.currency-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.remove-rate .dashicons {
|
||||
margin-top: 3px;
|
||||
color: #b32d2e;
|
||||
}
|
||||
|
||||
.add-rate .dashicons {
|
||||
margin-top: 3px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
var $table = $('#czl-exchange-rates');
|
||||
var template = $('#rate-row-template').html();
|
||||
var rateIndex = $table.find('tbody tr').length;
|
||||
|
||||
$('.add-rate').on('click', function() {
|
||||
var newRow = template.replace(/{{index}}/g, rateIndex++);
|
||||
$table.find('tbody').append(newRow);
|
||||
});
|
||||
|
||||
$table.on('click', '.remove-rate', function() {
|
||||
var $row = $(this).closest('tr');
|
||||
$row.fadeOut(300, function() {
|
||||
$row.remove();
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
186
admin/views/orders.php
Normal file
186
admin/views/orders.php
Normal file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// 获取订单列表
|
||||
$orders_query = new WC_Order_Query(array(
|
||||
'limit' => -1,
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'status' => array('processing', 'completed', 'shipping', 'delivered')
|
||||
));
|
||||
|
||||
$orders = $orders_query->get_orders();
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
<h1><?php _e('CZL Express 订单管理', 'woo-czl-express'); ?></h1>
|
||||
|
||||
<div class="tablenav top">
|
||||
<div class="alignleft actions">
|
||||
<select id="bulk-action-selector-top">
|
||||
<option value="-1"><?php _e('批量操作', 'woo-czl-express'); ?></option>
|
||||
<option value="create_shipment"><?php _e('创建运单', 'woo-czl-express'); ?></option>
|
||||
</select>
|
||||
<button class="button" id="doaction"><?php _e('应用', 'woo-czl-express'); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="manage-column column-cb check-column">
|
||||
<input type="checkbox" id="cb-select-all-1">
|
||||
</td>
|
||||
<th><?php _e('订单号', 'woo-czl-express'); ?></th>
|
||||
<th><?php _e('日期', 'woo-czl-express'); ?></th>
|
||||
<th><?php _e('状态', 'woo-czl-express'); ?></th>
|
||||
<th><?php _e('收件人', 'woo-czl-express'); ?></th>
|
||||
<th><?php _e('运单信息', 'woo-czl-express'); ?></th>
|
||||
<th><?php _e('操作', 'woo-czl-express'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
<?php
|
||||
$tracking_number = $order->get_meta('_czl_tracking_number');
|
||||
$czl_order_id = $order->get_meta('_czl_order_id');
|
||||
$reference_number = $order->get_meta('_czl_reference_number');
|
||||
$label_url = $order->get_meta('_czl_label_url');
|
||||
|
||||
$shipping_methods = $order->get_shipping_methods();
|
||||
$shipping_method = current($shipping_methods);
|
||||
$is_czl = $shipping_method && strpos($shipping_method->get_method_id(), 'czl_express') !== false;
|
||||
?>
|
||||
<?php if ($is_czl): ?>
|
||||
<tr>
|
||||
<th scope="row" class="check-column">
|
||||
<input type="checkbox" name="order_ids[]" value="<?php echo esc_attr($order->get_id()); ?>">
|
||||
</th>
|
||||
<td>
|
||||
<a href="<?php echo esc_url($order->get_edit_order_url()); ?>" target="_blank">
|
||||
#<?php echo esc_html($order->get_order_number()); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo esc_html($order->get_date_created()->date_i18n('Y-m-d H:i:s')); ?></td>
|
||||
<td><?php echo esc_html(wc_get_order_status_name($order->get_status())); ?></td>
|
||||
<td>
|
||||
<?php echo esc_html($order->get_formatted_shipping_full_name()); ?><br>
|
||||
<?php echo esc_html($order->get_shipping_address_1()); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($tracking_number): ?>
|
||||
<strong><?php _e('运单号:', 'woo-czl-express'); ?></strong>
|
||||
<a href="https://exp.czl.net/track/?query=<?php echo esc_attr($tracking_number); ?>" target="_blank">
|
||||
<?php echo esc_html($tracking_number); ?>
|
||||
</a><br>
|
||||
<strong><?php _e('CZL订单号:', 'woo-czl-express'); ?></strong>
|
||||
<?php echo esc_html($czl_order_id); ?><br>
|
||||
<strong><?php _e('参考号:', 'woo-czl-express'); ?></strong>
|
||||
<?php echo esc_html($reference_number); ?><br>
|
||||
<?php if ($label_url): ?>
|
||||
<a href="<?php echo esc_url($label_url); ?>" target="_blank" class="button">
|
||||
<?php _e('打印标签', 'woo-czl-express'); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<?php _e('未创建运单', 'woo-czl-express'); ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (!$tracking_number): ?>
|
||||
<button class="button create-shipment" data-order-id="<?php echo esc_attr($order->get_id()); ?>">
|
||||
<?php _e('创建运单', 'woo-czl-express'); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(document).ready(function($) {
|
||||
// 单个订单创建运单
|
||||
$('.create-shipment').click(function() {
|
||||
var button = $(this);
|
||||
var orderId = button.data('order-id');
|
||||
|
||||
button.prop('disabled', true).text('<?php _e('处理中...', 'woo-czl-express'); ?>');
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'czl_create_shipment',
|
||||
order_id: orderId,
|
||||
nonce: '<?php echo wp_create_nonce('czl_create_shipment'); ?>'
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data.message);
|
||||
button.prop('disabled', false).text('<?php _e('创建运单', 'woo-czl-express'); ?>');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('<?php _e('请求失败,请重试', 'woo-czl-express'); ?>');
|
||||
button.prop('disabled', false).text('<?php _e('创建运单', 'woo-czl-express'); ?>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 批量创建运单
|
||||
$('#doaction').click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var action = $('#bulk-action-selector-top').val();
|
||||
if (action !== 'create_shipment') {
|
||||
return;
|
||||
}
|
||||
|
||||
var orderIds = [];
|
||||
$('input[name="order_ids[]"]:checked').each(function() {
|
||||
orderIds.push($(this).val());
|
||||
});
|
||||
|
||||
if (orderIds.length === 0) {
|
||||
alert('<?php _e('请选择订单', 'woo-czl-express'); ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('<?php _e('确定要为选中的订单创建运单吗?', 'woo-czl-express'); ?>')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).prop('disabled', true).text('<?php _e('处理中...', 'woo-czl-express'); ?>');
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'czl_bulk_create_shipment',
|
||||
order_ids: orderIds,
|
||||
nonce: '<?php echo wp_create_nonce('czl_bulk_create_shipment'); ?>'
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data.message);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('<?php _e('请求失败,请重试', 'woo-czl-express'); ?>');
|
||||
},
|
||||
complete: function() {
|
||||
$('#doaction').prop('disabled', false).text('<?php _e('应用', 'woo-czl-express'); ?>');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
195
admin/views/product-groups.php
Normal file
195
admin/views/product-groups.php
Normal file
@ -0,0 +1,195 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
$groups = get_option('czl_product_groups', array());
|
||||
?>
|
||||
|
||||
<div class="wrap czl-product-groups-page">
|
||||
<h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<?php settings_errors(); ?>
|
||||
|
||||
<div class="czl-page-description">
|
||||
<p><?php _e('在这里管理运输方式的分组显示。相同分组的运输方式将合并显示,并显示最低价格。', 'woo-czl-express'); ?></p>
|
||||
</div>
|
||||
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field('czl_save_product_groups', 'czl_product_groups_nonce'); ?>
|
||||
|
||||
<div class="czl-table-container">
|
||||
<table class="widefat czl-product-groups" id="czl-product-groups">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="column-enabled"><?php _e('启用', 'woo-czl-express'); ?></th>
|
||||
<th class="column-name"><?php _e('分组名称', 'woo-czl-express'); ?></th>
|
||||
<th class="column-prefixes"><?php _e('匹配前缀', 'woo-czl-express'); ?></th>
|
||||
<th class="column-actions"><?php _e('操作', 'woo-czl-express'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($groups)) : ?>
|
||||
<?php foreach ($groups as $key => $group): ?>
|
||||
<tr>
|
||||
<td class="column-enabled">
|
||||
<input type="checkbox" name="groups[<?php echo esc_attr($key); ?>][enabled]"
|
||||
value="1" <?php checked(!empty($group['enabled'])); ?>>
|
||||
</td>
|
||||
<td class="column-name">
|
||||
<input type="text" name="groups[<?php echo esc_attr($key); ?>][groupName]"
|
||||
value="<?php echo esc_attr($group['groupName']); ?>" class="regular-text">
|
||||
</td>
|
||||
<td class="column-prefixes">
|
||||
<textarea name="groups[<?php echo esc_attr($key); ?>][prefixes]" rows="3" class="large-text"
|
||||
><?php echo esc_textarea(implode("\n", $group['prefixes'])); ?></textarea>
|
||||
<p class="description"><?php _e('每行输入一个前缀,运输方式名称以此前缀开头将被归入此分组', 'woo-czl-express'); ?></p>
|
||||
</td>
|
||||
<td class="column-actions">
|
||||
<button type="button" class="button remove-group" title="<?php esc_attr_e('删除此分组', 'woo-czl-express'); ?>">
|
||||
<span class="dashicons dashicons-trash"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr class="no-items">
|
||||
<td colspan="4"><?php _e('没有找到分组配置', 'woo-czl-express'); ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<button type="button" class="button add-group">
|
||||
<span class="dashicons dashicons-plus-alt2"></span>
|
||||
<?php _e('添加分组', 'woo-czl-express'); ?>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php submit_button(__('保存分组设置', 'woo-czl-express'), 'primary', 'submit', true); ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="group-row-template">
|
||||
<tr>
|
||||
<td class="column-enabled">
|
||||
<input type="checkbox" name="groups[{{index}}][enabled]" value="1" checked>
|
||||
</td>
|
||||
<td class="column-name">
|
||||
<input type="text" name="groups[{{index}}][groupName]" value="" class="regular-text" placeholder="<?php esc_attr_e('输入分组名称', 'woo-czl-express'); ?>">
|
||||
</td>
|
||||
<td class="column-prefixes">
|
||||
<textarea name="groups[{{index}}][prefixes]" rows="3" class="large-text" placeholder="<?php esc_attr_e('每行输入一个前缀', 'woo-czl-express'); ?>"></textarea>
|
||||
<p class="description"><?php _e('每行输入一个前缀,运输方式名称以此前缀开头将被归入此分组', 'woo-czl-express'); ?></p>
|
||||
</td>
|
||||
<td class="column-actions">
|
||||
<button type="button" class="button remove-group" title="<?php esc_attr_e('删除此分组', 'woo-czl-express'); ?>">
|
||||
<span class="dashicons dashicons-trash"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.czl-product-groups-page {
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.czl-page-description {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border-left: 4px solid #2271b1;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.czl-table-container {
|
||||
margin: 20px 0;
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.czl-product-groups {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.czl-product-groups th {
|
||||
padding: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.czl-product-groups td {
|
||||
vertical-align: top;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.column-enabled {
|
||||
width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.column-name {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.column-actions {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.remove-group .dashicons {
|
||||
margin-top: 3px;
|
||||
color: #b32d2e;
|
||||
}
|
||||
|
||||
.add-group .dashicons {
|
||||
margin-top: 3px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.no-items td {
|
||||
text-align: center;
|
||||
padding: 20px !important;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: 5px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
jQuery(function($) {
|
||||
var $table = $('#czl-product-groups');
|
||||
var template = $('#group-row-template').html();
|
||||
var groupIndex = $table.find('tbody tr').length;
|
||||
|
||||
$('.add-group').on('click', function() {
|
||||
var newRow = template.replace(/{{index}}/g, groupIndex++);
|
||||
if ($table.find('.no-items').length) {
|
||||
$table.find('.no-items').remove();
|
||||
}
|
||||
$table.find('tbody').append(newRow);
|
||||
});
|
||||
|
||||
$table.on('click', '.remove-group', function() {
|
||||
var $row = $(this).closest('tr');
|
||||
$row.fadeOut(300, function() {
|
||||
$row.remove();
|
||||
if ($table.find('tbody tr').length === 0) {
|
||||
$table.find('tbody').append('<tr class="no-items"><td colspan="4"><?php _e('没有找到分组配置', 'woo-czl-express'); ?></td></tr>');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
16
admin/views/settings.php
Normal file
16
admin/views/settings.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
||||
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields('czl_options_group');
|
||||
do_settings_sections('czl_options');
|
||||
submit_button();
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
</rewritten_file>
|
30
assets/css/shipping-method-groups.css
Normal file
30
assets/css/shipping-method-groups.css
Normal file
@ -0,0 +1,30 @@
|
||||
.shipping-method-group .toggle-sub-methods {
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.shipping-method-group .sub-methods {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.shipping-method-group .sub-methods-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.shipping-method-group .sub-methods-table th,
|
||||
.shipping-method-group .sub-methods-table td {
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.shipping-method-group .remote-fee-notice {
|
||||
font-size: 0.9em;
|
||||
color: #e2401c;
|
||||
margin-top: 5px;
|
||||
}
|
60
assets/css/shipping-method.css
Normal file
60
assets/css/shipping-method.css
Normal file
@ -0,0 +1,60 @@
|
||||
.czl-express-rate {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.czl-express-rate .delivery-time {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.czl-express-rate .rate-price {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.czl-express-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.czl-express-group-header {
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
background: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.czl-express-group-content {
|
||||
padding-left: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.czl-express-group.expanded .czl-express-group-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.czl-tracking-info {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.czl-tracking-history {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.czl-tracking-history li {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.czl-tracking-history .tracking-date {
|
||||
color: #666;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.czl-tracking-history .tracking-location {
|
||||
color: #999;
|
||||
margin-left: 10px;
|
||||
}
|
11
assets/js/shipping-method-groups.js
Normal file
11
assets/js/shipping-method-groups.js
Normal file
@ -0,0 +1,11 @@
|
||||
jQuery(function($) {
|
||||
$('.shipping-method-group .toggle-sub-methods').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $group = $(this).closest('.shipping-method-group');
|
||||
var $subMethods = $group.find('.sub-methods');
|
||||
|
||||
$subMethods.slideToggle();
|
||||
$(this).toggleClass('dashicons-arrow-down-alt2 dashicons-arrow-up-alt2');
|
||||
});
|
||||
});
|
41
includes/class-czl-ajax.php
Normal file
41
includes/class-czl-ajax.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
class CZL_Ajax {
|
||||
public function __construct() {
|
||||
add_action('wp_ajax_czl_test_connection', array($this, 'test_connection'));
|
||||
add_action('wp_ajax_czl_test_shipping_rate', array($this, 'test_shipping_rate'));
|
||||
}
|
||||
|
||||
public function test_connection() {
|
||||
check_ajax_referer('czl_test_api', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Permission denied', 'woo-czl-express')));
|
||||
}
|
||||
|
||||
$tester = new CZL_API_Test();
|
||||
$result = $tester->test_connection();
|
||||
|
||||
if ($result['success']) {
|
||||
wp_send_json_success($result);
|
||||
} else {
|
||||
wp_send_json_error($result);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_shipping_rate() {
|
||||
check_ajax_referer('czl_test_api', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('Permission denied', 'woo-czl-express')));
|
||||
}
|
||||
|
||||
$tester = new CZL_API_Test();
|
||||
$result = $tester->test_shipping_rate();
|
||||
|
||||
if ($result['success']) {
|
||||
wp_send_json_success($result['data']);
|
||||
} else {
|
||||
wp_send_json_error($result);
|
||||
}
|
||||
}
|
||||
}
|
67
includes/class-czl-api-test.php
Normal file
67
includes/class-czl-api-test.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
class CZL_API_Test {
|
||||
private $api;
|
||||
|
||||
public function __construct() {
|
||||
$this->api = new CZL_API();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试API连接
|
||||
*/
|
||||
public function test_connection() {
|
||||
try {
|
||||
// 尝试获取token
|
||||
$this->api->get_token();
|
||||
return array(
|
||||
'success' => true,
|
||||
'message' => __('Successfully connected to CZL Express API', 'woo-czl-express')
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return array(
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试运费查询
|
||||
*/
|
||||
public function test_shipping_rate() {
|
||||
$test_package = array(
|
||||
'destination' => array(
|
||||
'country' => 'US',
|
||||
'state' => 'CA',
|
||||
'city' => 'Los Angeles',
|
||||
'address' => '123 Test St',
|
||||
'postcode' => '90001'
|
||||
),
|
||||
'contents' => array(
|
||||
array(
|
||||
'data' => new WC_Product_Simple(array(
|
||||
'weight' => 1,
|
||||
'length' => 10,
|
||||
'width' => 10,
|
||||
'height' => 10,
|
||||
'price' => 100
|
||||
)),
|
||||
'quantity' => 1
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
$rates = $this->api->get_shipping_rate($test_package);
|
||||
return array(
|
||||
'success' => true,
|
||||
'data' => $rates
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
return array(
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
505
includes/class-czl-api.php
Normal file
505
includes/class-czl-api.php
Normal file
@ -0,0 +1,505 @@
|
||||
<?php
|
||||
class CZL_API {
|
||||
private $api_url;
|
||||
private $username;
|
||||
private $password;
|
||||
private $token;
|
||||
private $token_expires;
|
||||
private $country_mapping;
|
||||
private $customer_id;
|
||||
private $customer_userid;
|
||||
|
||||
public function __construct() {
|
||||
$this->api_url = get_option('czl_api_url', '');
|
||||
$this->username = get_option('czl_username', '');
|
||||
$this->password = get_option('czl_password', '');
|
||||
$this->token = get_transient('czl_api_token');
|
||||
$this->token_expires = get_transient('czl_api_token_expires');
|
||||
$this->init_country_mapping();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化国家代码映射
|
||||
*/
|
||||
private function init_country_mapping() {
|
||||
// 从API获取国家列表并缓存
|
||||
$cached_mapping = get_transient('czl_country_mapping');
|
||||
if ($cached_mapping !== false) {
|
||||
$this->country_mapping = $cached_mapping;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = wp_remote_get('https://tms-api-go.czl.net/api/countries', array(
|
||||
'timeout' => 30
|
||||
));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||||
if (!empty($data['data'])) {
|
||||
$mapping = array();
|
||||
foreach ($data['data'] as $country) {
|
||||
$mapping[$country['ename']] = $country['code'];
|
||||
}
|
||||
$this->country_mapping = $mapping;
|
||||
set_transient('czl_country_mapping', $mapping, DAY_IN_SECONDS);
|
||||
error_log('CZL Express: Country mapping updated');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: Failed to get country mapping - ' . $e->getMessage());
|
||||
$this->country_mapping = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换国家代码
|
||||
*/
|
||||
private function convert_country_code($wc_country) {
|
||||
error_log('CZL Express: Converting country code ' . $wc_country);
|
||||
|
||||
// 获取WooCommerce国家名称
|
||||
$countries = WC()->countries->get_countries();
|
||||
$country_name = isset($countries[$wc_country]) ? $countries[$wc_country] : '';
|
||||
|
||||
// 在映射中查找
|
||||
foreach ($this->country_mapping as $name => $code) {
|
||||
if (stripos($name, $country_name) !== false || stripos($country_name, $name) !== false) {
|
||||
error_log('CZL Express: Found country mapping ' . $wc_country . ' => ' . $code);
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没找到映射,返回原始代码
|
||||
error_log('CZL Express: No mapping found for ' . $wc_country . ', using original code');
|
||||
return $wc_country;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取API认证Token
|
||||
*/
|
||||
private function get_token() {
|
||||
if ($this->token && time() < $this->token_expires) {
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
$response = wp_remote_post($this->api_url . '/auth/login', array(
|
||||
'body' => array(
|
||||
'username' => $this->username,
|
||||
'password' => md5($this->password) // CZL API需要MD5加密的密码
|
||||
),
|
||||
'timeout' => 30
|
||||
));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (empty($body['success'])) {
|
||||
throw new Exception(!empty($body['message']) ? $body['message'] : '认证失败');
|
||||
}
|
||||
|
||||
$this->token = $body['data']['token'];
|
||||
$this->token_expires = time() + 7200; // CZL token有效期通常是2小时
|
||||
|
||||
set_transient('czl_api_token', $this->token, 7200);
|
||||
set_transient('czl_api_token_expires', $this->token_expires, 7200);
|
||||
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送API请求
|
||||
*/
|
||||
private function request($endpoint, $method = 'GET', $data = null) {
|
||||
$args = array(
|
||||
'method' => $method,
|
||||
'headers' => array(
|
||||
'Authorization' => $this->get_token(),
|
||||
'Content-Type' => 'application/json'
|
||||
),
|
||||
'timeout' => 30
|
||||
);
|
||||
|
||||
if ($data !== null) {
|
||||
$args['body'] = json_encode($data);
|
||||
}
|
||||
|
||||
$response = wp_remote_request($this->api_url . $endpoint, $args);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (empty($body['success'])) {
|
||||
throw new Exception(!empty($body['message']) ? $body['message'] : '请求失败');
|
||||
}
|
||||
|
||||
return $body['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运费报价
|
||||
*/
|
||||
public function get_shipping_rate($params) {
|
||||
try {
|
||||
$api_url = 'https://tms.czl.net/defaultPriceSearchJson.htm';
|
||||
|
||||
// 转换国家代码
|
||||
$country_code = $this->convert_country_code($params['country']);
|
||||
|
||||
// 构建请求参数
|
||||
$query = array(
|
||||
'weight' => $params['weight'],
|
||||
'country' => $country_code,
|
||||
'cargoType' => $params['cargoType'],
|
||||
'length' => $params['length'],
|
||||
'width' => $params['width'],
|
||||
'height' => $params['height'],
|
||||
'postcode' => $params['postcode']
|
||||
);
|
||||
|
||||
error_log('CZL Express: Shipping rate request - ' . print_r($query, true));
|
||||
|
||||
// 发送请求
|
||||
$response = wp_remote_post($api_url . '?' . http_build_query($query), array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Basic ' . base64_encode($this->username . ':' . $this->password)
|
||||
),
|
||||
'timeout' => 30
|
||||
));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
error_log('CZL Express: API raw response - ' . $body);
|
||||
|
||||
$data = json_decode($body, true);
|
||||
if (empty($data)) {
|
||||
throw new Exception('Empty API response');
|
||||
}
|
||||
|
||||
// 检查响应格式
|
||||
if (!is_array($data)) {
|
||||
throw new Exception('Invalid API response format');
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express API Error: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化运费报价结果
|
||||
*/
|
||||
private function format_shipping_rates($rates) {
|
||||
$formatted_rates = array();
|
||||
foreach ($rates as $rate) {
|
||||
$formatted_rates[] = array(
|
||||
'method_id' => 'czl_express_' . sanitize_title($rate['product_id']),
|
||||
'method_title' => $rate['product_name'],
|
||||
'method_name' => $rate['product_name'],
|
||||
'cost' => floatval($rate['total_amount']),
|
||||
'delivery_time' => $rate['product_aging'],
|
||||
'product_id' => $rate['product_id'],
|
||||
'product_note' => $rate['product_note']
|
||||
);
|
||||
}
|
||||
return $formatted_rates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建运单
|
||||
*/
|
||||
public function create_order($order_data) {
|
||||
try {
|
||||
// 添加请求前的日志
|
||||
error_log('CZL Express: Creating order with data - ' . print_r($order_data, true));
|
||||
|
||||
$response = wp_remote_post('https://tms.czl.net/createOrderApi.htm', array(
|
||||
'body' => array(
|
||||
'Param' => json_encode($order_data)
|
||||
),
|
||||
'timeout' => 30
|
||||
));
|
||||
|
||||
// 添加响应日志
|
||||
error_log('CZL Express: Raw API response - ' . print_r($response, true));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception('API请求失败: ' . $response->get_error_message());
|
||||
}
|
||||
|
||||
$result = json_decode(wp_remote_retrieve_body($response), true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new Exception('JSON解析失败: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
if (empty($result['ack']) || $result['ack'] !== 'true') {
|
||||
throw new Exception(!empty($result['message']) ? urldecode($result['message']) : '未知错误');
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: Create order failed - ' . $e->getMessage());
|
||||
error_log('CZL Express Error Stack Trace: ' . $e->getTraceAsString());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运单跟踪信息
|
||||
*/
|
||||
public function get_tracking($tracking_number) {
|
||||
try {
|
||||
$response = wp_remote_post('https://tms.czl.net/selectTrack.htm', array(
|
||||
'body' => array(
|
||||
'documentCode' => $tracking_number
|
||||
),
|
||||
'timeout' => 30
|
||||
));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$result = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (empty($result[0]['ack']) || $result[0]['ack'] !== 'true') {
|
||||
throw new Exception('获取跟踪信息失败');
|
||||
}
|
||||
|
||||
return $result[0]['data'][0];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express API Error: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运单标签
|
||||
*/
|
||||
public function get_label($order_id) {
|
||||
$url = sprintf(
|
||||
'https://tms-label.czl.net/order/FastRpt/PDF_NEW.aspx?Format=lbl_sub一票多件161810499441.frx&PrintType=1&order_id=%s',
|
||||
$order_id
|
||||
);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消运单
|
||||
*/
|
||||
public function cancel_order($order_number) {
|
||||
return $this->request('/shipping/cancel', 'POST', array(
|
||||
'order_number' => $order_number
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试认证
|
||||
*/
|
||||
public function test_auth() {
|
||||
try {
|
||||
$response = wp_remote_post('https://tms.czl.net/selectAuth.htm', array(
|
||||
'body' => array(
|
||||
'username' => $this->username,
|
||||
'password' => $this->password
|
||||
),
|
||||
'timeout' => 30
|
||||
));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$result = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (empty($result['success'])) {
|
||||
throw new Exception('认证失败');
|
||||
}
|
||||
|
||||
return array(
|
||||
'customer_id' => $result['customer_id'],
|
||||
'customer_userid' => $result['customer_userid']
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express API Error: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支持的国家列表
|
||||
*/
|
||||
public function get_countries() {
|
||||
try {
|
||||
$response = wp_remote_get('https://tms-api-go.czl.net/api/countries', array(
|
||||
'timeout' => 30
|
||||
));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$result = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if ($result['code'] !== 200) {
|
||||
throw new Exception('获取国家列表失败');
|
||||
}
|
||||
|
||||
return $result['data'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express API Error: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function ensure_logged_in() {
|
||||
try {
|
||||
error_log('CZL Express: Starting authentication');
|
||||
|
||||
$auth_url = 'https://tms.czl.net/selectAuth.htm';
|
||||
$auth_data = array(
|
||||
'username' => $this->username,
|
||||
'password' => $this->password
|
||||
);
|
||||
|
||||
error_log('CZL Express: Auth request data - ' . print_r($auth_data, true));
|
||||
|
||||
$ch = curl_init($auth_url);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($auth_data));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Accept-Language: zh-cn',
|
||||
'Connection: Keep-Alive',
|
||||
'Cache-Control: no-cache'
|
||||
));
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
error_log('CZL Express: Auth raw response - ' . $response);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
throw new Exception('认证请求失败');
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
// 解析响应
|
||||
$result = json_decode(str_replace("'", '"', $response), true);
|
||||
error_log('CZL Express: Auth decoded response - ' . print_r($result, true));
|
||||
|
||||
if (empty($result) || !isset($result['customer_id'])) {
|
||||
throw new Exception('认证失败');
|
||||
}
|
||||
|
||||
// 保存认证信息
|
||||
$this->customer_id = $result['customer_id'];
|
||||
$this->customer_userid = $result['customer_userid'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: Authentication failed - ' . $e->getMessage());
|
||||
throw new Exception('认证失败,请联系CZL Express');
|
||||
}
|
||||
}
|
||||
|
||||
public function create_shipment($data) {
|
||||
try {
|
||||
error_log('CZL Express: Starting create shipment');
|
||||
|
||||
// 确保已登录
|
||||
$this->ensure_logged_in();
|
||||
|
||||
// 使用类属性中的认证信息
|
||||
$data['customer_id'] = $this->customer_id;
|
||||
$data['customer_userid'] = $this->customer_userid;
|
||||
|
||||
$api_url = 'https://tms.czl.net/createOrderApi.htm';
|
||||
|
||||
// 构建请求数据
|
||||
$request_data = array(
|
||||
'param' => json_encode($data, JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
error_log('CZL Express: Create shipment request data - ' . print_r($request_data, true));
|
||||
|
||||
$ch = curl_init($api_url);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_data));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'Accept-Language: zh-cn',
|
||||
'Connection: Keep-Alive',
|
||||
'Cache-Control: no-cache',
|
||||
'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
|
||||
));
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
|
||||
// 添加更多调试信息
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||
$verbose = fopen('php://temp', 'w+');
|
||||
curl_setopt($ch, CURLOPT_STDERR, $verbose);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// 记录详细的curl信息
|
||||
if (curl_errno($ch)) {
|
||||
rewind($verbose);
|
||||
$verboseLog = stream_get_contents($verbose);
|
||||
error_log('CZL Express: Curl verbose log - ' . $verboseLog);
|
||||
error_log('CZL Express: Curl error - ' . curl_error($ch));
|
||||
throw new Exception('请求失败: ' . curl_error($ch));
|
||||
}
|
||||
|
||||
error_log('CZL Express: Create shipment raw response - ' . $response);
|
||||
error_log('CZL Express: Response info - ' . print_r(curl_getinfo($ch), true));
|
||||
|
||||
curl_close($ch);
|
||||
fclose($verbose);
|
||||
|
||||
// 检查响应是否为空
|
||||
if (empty($response)) {
|
||||
throw new Exception('服务器未返回数据');
|
||||
}
|
||||
|
||||
// 解析响应
|
||||
$result = json_decode($response, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
error_log('CZL Express: JSON decode error - ' . json_last_error_msg());
|
||||
throw new Exception('响应数据格式错误');
|
||||
}
|
||||
|
||||
error_log('CZL Express: Create shipment decoded response - ' . print_r($result, true));
|
||||
|
||||
if (!isset($result['ack']) || $result['ack'] !== 'true') {
|
||||
$error_msg = isset($result['message']) ? urldecode($result['message']) : '未知错误';
|
||||
error_log('CZL Express: API error message - ' . $error_msg);
|
||||
throw new Exception($error_msg);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: Create shipment failed - ' . $e->getMessage());
|
||||
error_log('CZL Express Error Stack Trace: ' . $e->getTraceAsString());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
52
includes/class-czl-install.php
Normal file
52
includes/class-czl-install.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
class CZL_Install {
|
||||
public static function init() {
|
||||
self::create_tables();
|
||||
self::create_options();
|
||||
}
|
||||
|
||||
public static function create_tables() {
|
||||
global $wpdb;
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
// 创建运单表
|
||||
$sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}czl_shipments (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
order_id bigint(20) NOT NULL,
|
||||
tracking_number varchar(50) NOT NULL,
|
||||
label_url varchar(255) DEFAULT NULL,
|
||||
shipping_method varchar(100) NOT NULL,
|
||||
status varchar(50) DEFAULT 'pending',
|
||||
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY order_id (order_id),
|
||||
KEY tracking_number (tracking_number)
|
||||
) $charset_collate;";
|
||||
|
||||
// 创建运费规则表
|
||||
$sql .= "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}czl_shipping_rules (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
name varchar(100) NOT NULL,
|
||||
shipping_method varchar(100) NOT NULL,
|
||||
czl_product_code varchar(100) NOT NULL,
|
||||
price_adjustment varchar(255) DEFAULT NULL,
|
||||
status int(1) DEFAULT 1,
|
||||
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
) $charset_collate;";
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
dbDelta($sql);
|
||||
}
|
||||
|
||||
public static function create_options() {
|
||||
// 添加默认配置选项
|
||||
add_option('czl_api_url', '');
|
||||
add_option('czl_username', '');
|
||||
add_option('czl_password', '');
|
||||
add_option('czl_exchange_rate', '1');
|
||||
add_option('czl_product_groups', '');
|
||||
}
|
||||
}
|
53
includes/class-czl-label.php
Normal file
53
includes/class-czl-label.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
class CZL_Label {
|
||||
/**
|
||||
* 获取标签URL
|
||||
*/
|
||||
public static function get_label_url($order_id) {
|
||||
$czl_order_id = get_post_meta($order_id, '_czl_order_id', true);
|
||||
if (!$czl_order_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$api = new CZL_API();
|
||||
return $api->get_label($czl_order_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加标签打印按钮
|
||||
*/
|
||||
public static function add_print_actions($actions, $order) {
|
||||
if (self::get_label_url($order->get_id())) {
|
||||
$actions['czl_print_label'] = array(
|
||||
'url' => wp_nonce_url(admin_url('admin-ajax.php?action=czl_print_label&order_id=' . $order->get_id()), 'czl_print_label'),
|
||||
'name' => __('打印运单', 'woo-czl-express'),
|
||||
'action' => 'czl_print_label'
|
||||
);
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理标签打印请求
|
||||
*/
|
||||
public static function handle_print_request() {
|
||||
if (!current_user_can('edit_shop_orders')) {
|
||||
wp_die(__('您没有权限执行此操作', 'woo-czl-express'));
|
||||
}
|
||||
|
||||
check_admin_referer('czl_print_label');
|
||||
|
||||
$order_id = isset($_GET['order_id']) ? absint($_GET['order_id']) : 0;
|
||||
if (!$order_id) {
|
||||
wp_die(__('订单ID无效', 'woo-czl-express'));
|
||||
}
|
||||
|
||||
$label_url = self::get_label_url($order_id);
|
||||
if (!$label_url) {
|
||||
wp_die(__('未找到运单标签', 'woo-czl-express'));
|
||||
}
|
||||
|
||||
wp_redirect($label_url);
|
||||
exit;
|
||||
}
|
||||
}
|
87
includes/class-czl-order-data.php
Normal file
87
includes/class-czl-order-data.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
class CZL_Order_Data {
|
||||
private $order;
|
||||
private $customer_id;
|
||||
private $customer_userid;
|
||||
|
||||
public function __construct($order) {
|
||||
$this->order = $order;
|
||||
|
||||
// 获取认证信息
|
||||
$auth = get_option('czl_auth_info', array());
|
||||
$this->customer_id = !empty($auth['customer_id']) ? $auth['customer_id'] : '';
|
||||
$this->customer_userid = !empty($auth['customer_userid']) ? $auth['customer_userid'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备创建运单的数据
|
||||
*/
|
||||
public function prepare() {
|
||||
$shipping_address = $this->order->get_shipping_address_1();
|
||||
if ($this->order->get_shipping_address_2()) {
|
||||
$shipping_address .= ' ' . $this->order->get_shipping_address_2();
|
||||
}
|
||||
|
||||
// 基础订单数据
|
||||
$data = array(
|
||||
'buyerid' => '',
|
||||
'consignee_address' => $shipping_address,
|
||||
'order_piece' => 1, // 默认1件
|
||||
'consignee_city' => $this->order->get_shipping_city(),
|
||||
'consignee_mobile' => $this->order->get_shipping_phone(),
|
||||
'order_returnsign' => 'N',
|
||||
'consignee_name' => $this->order->get_shipping_first_name() . ' ' . $this->order->get_shipping_last_name(),
|
||||
'trade_type' => 'ZYXT',
|
||||
'consignee_postcode' => $this->order->get_shipping_postcode(),
|
||||
'consignee_state' => $this->order->get_shipping_state(),
|
||||
'consignee_telephone' => $this->order->get_shipping_phone(),
|
||||
'country' => $this->order->get_shipping_country(),
|
||||
'customer_id' => $this->customer_id,
|
||||
'customer_userid' => $this->customer_userid,
|
||||
'order_customerinvoicecode' => $this->order->get_order_number(),
|
||||
'product_id' => $this->get_shipping_product_id(),
|
||||
'consignee_email' => $this->order->get_billing_email(),
|
||||
'consignee_companyname' => $this->order->get_shipping_company(),
|
||||
'order_cargoamount' => $this->order->get_total(),
|
||||
'orderInvoiceParam' => $this->prepare_items()
|
||||
);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备商品信息
|
||||
*/
|
||||
private function prepare_items() {
|
||||
$items = array();
|
||||
$order_items = $this->order->get_items();
|
||||
|
||||
foreach ($order_items as $item) {
|
||||
$product = $item->get_product();
|
||||
|
||||
$items[] = array(
|
||||
'invoice_amount' => $item->get_total(),
|
||||
'invoice_pcs' => $item->get_quantity(),
|
||||
'invoice_title' => $product->get_meta('_czl_name_en') ?: $product->get_name(),
|
||||
'invoice_weight' => $product->get_weight(),
|
||||
'sku' => $product->get_name(),
|
||||
'hs_code' => $product->get_meta('_czl_hs_code')
|
||||
);
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配送方式ID
|
||||
*/
|
||||
private function get_shipping_product_id() {
|
||||
$shipping_methods = $this->order->get_shipping_methods();
|
||||
foreach ($shipping_methods as $shipping_method) {
|
||||
if (strpos($shipping_method->get_method_id(), 'czl_express_') === 0) {
|
||||
return str_replace('czl_express_', '', $shipping_method->get_method_id());
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
121
includes/class-czl-order-handler.php
Normal file
121
includes/class-czl-order-handler.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
class CZL_Order_Handler {
|
||||
private $api;
|
||||
|
||||
public function __construct() {
|
||||
$this->api = new CZL_API();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建运单
|
||||
*/
|
||||
public function create_shipment($order_id) {
|
||||
try {
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否已创建运单
|
||||
$tracking_number = get_post_meta($order_id, '_czl_tracking_number', true);
|
||||
if ($tracking_number) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否使用CZL Express配送
|
||||
if (!$this->is_czl_shipping($order)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用统一的CZL_Order类创建运单
|
||||
$czl_order = new CZL_Order();
|
||||
$result = $czl_order->create_shipment($order_id);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: ' . $e->getMessage());
|
||||
if (isset($order)) {
|
||||
$order->add_order_note(
|
||||
sprintf(
|
||||
__('CZL Express运单创建失败: %s', 'woo-czl-express'),
|
||||
$e->getMessage()
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否使用CZL Express配送
|
||||
*/
|
||||
private function is_czl_shipping($order) {
|
||||
$shipping_methods = $order->get_shipping_methods();
|
||||
foreach ($shipping_methods as $shipping_method) {
|
||||
if (strpos($shipping_method->get_method_id(), 'czl_express_') === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消运单
|
||||
*/
|
||||
public function cancel_shipment($order_id) {
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) {
|
||||
return;
|
||||
}
|
||||
|
||||
$czl_order_id = get_post_meta($order_id, '_czl_order_id', true);
|
||||
if (!$czl_order_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->api->cancel_order($czl_order_id);
|
||||
|
||||
// 添加订单备注
|
||||
$note = __('CZL Express运单已取消', 'woo-czl-express');
|
||||
$order->add_order_note($note);
|
||||
|
||||
// 清除运单信息
|
||||
delete_post_meta($order_id, '_czl_order_id');
|
||||
delete_post_meta($order_id, '_czl_tracking_number');
|
||||
delete_post_meta($order_id, '_czl_label_url');
|
||||
|
||||
} catch (Exception $e) {
|
||||
$error_message = sprintf(
|
||||
__('CZL Express运单取消失败: %s', 'woo-czl-express'),
|
||||
$e->getMessage()
|
||||
);
|
||||
$order->add_order_note($error_message);
|
||||
error_log('CZL Express Error: ' . $error_message);
|
||||
}
|
||||
}
|
||||
|
||||
public function process_order_action($order) {
|
||||
try {
|
||||
error_log('CZL Express: Processing order action for order ' . $order->get_id());
|
||||
|
||||
// 检查是否已经创建过运单
|
||||
$tracking_number = $order->get_meta('_czl_tracking_number');
|
||||
if (!empty($tracking_number)) {
|
||||
error_log('CZL Express: Order already has tracking number: ' . $tracking_number);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建运单
|
||||
$czl_order = new CZL_Order();
|
||||
$result = $czl_order->create_shipment($order->get_id());
|
||||
|
||||
error_log('CZL Express: Create shipment result - ' . print_r($result, true));
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: Failed to process order action - ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
201
includes/class-czl-order.php
Normal file
201
includes/class-czl-order.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
// 确保没有命名空间冲突
|
||||
if (!class_exists('CZL_Order')) {
|
||||
class CZL_Order {
|
||||
private $api;
|
||||
|
||||
public function __construct() {
|
||||
$this->api = new CZL_API();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建运单
|
||||
*/
|
||||
public function create_shipment($order_id) {
|
||||
try {
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) {
|
||||
throw new Exception('订单不存在');
|
||||
}
|
||||
|
||||
// 检查是否已有运单
|
||||
$tracking_number = $order->get_meta('_czl_tracking_number');
|
||||
if (!empty($tracking_number)) {
|
||||
throw new Exception('订单已存在运单号');
|
||||
}
|
||||
|
||||
// 获取运输方式信息
|
||||
$shipping_methods = $order->get_shipping_methods();
|
||||
$shipping_method = current($shipping_methods);
|
||||
|
||||
// 从配送方式元数据中获取product_id
|
||||
$product_id = $shipping_method->get_meta('product_id');
|
||||
if (empty($product_id)) {
|
||||
throw new Exception('未找到运输方式ID');
|
||||
}
|
||||
|
||||
// 准备运单数据
|
||||
$shipment_data = array(
|
||||
'buyerid' => $order->get_id(),
|
||||
'consignee_address' => $order->get_shipping_address_1() . ' ' . $order->get_shipping_address_2(),
|
||||
'consignee_city' => $order->get_shipping_city(),
|
||||
'consignee_mobile' => $order->get_billing_phone(),
|
||||
'consignee_name' => $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
|
||||
'consignee_postcode' => $order->get_shipping_postcode(),
|
||||
'consignee_state' => $order->get_shipping_state(),
|
||||
'consignee_email' => $order->get_billing_email(),
|
||||
'country' => $order->get_shipping_country(),
|
||||
'order_piece' => $this->get_order_items_count($order),
|
||||
'product_id' => $product_id,
|
||||
'trade_type' => 'ZYXT',
|
||||
'weight' => $this->get_order_weight($order),
|
||||
'orderInvoiceParam' => array()
|
||||
);
|
||||
|
||||
// 添加发票信息
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
if (!$product) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取海关编码
|
||||
$hs_code = $product->get_meta('_czl_hs_code');
|
||||
if (empty($hs_code)) {
|
||||
throw new Exception('产品 ' . $product->get_name() . ' 缺少海关编码');
|
||||
}
|
||||
|
||||
$shipment_data['orderInvoiceParam'][] = array(
|
||||
'invoice_amount' => $item->get_total(),
|
||||
'invoice_pcs' => $item->get_quantity(),
|
||||
'invoice_title' => $item->get_name(),
|
||||
'invoice_weight' => $product->get_weight() * $item->get_quantity(),
|
||||
'item_id' => $product->get_id(),
|
||||
'sku' => $product->get_sku(),
|
||||
'hs_code' => $hs_code // 添加海关编码
|
||||
);
|
||||
}
|
||||
|
||||
// 调用API创建运单
|
||||
$result = $this->api->create_shipment($shipment_data);
|
||||
|
||||
if (!empty($result['tracking_number'])) {
|
||||
// 更新订单元数据
|
||||
$order->update_meta_data('_czl_tracking_number', $result['tracking_number']);
|
||||
$order->update_meta_data('_czl_order_id', $result['order_id']);
|
||||
$order->update_meta_data('_czl_reference_number', $result['reference_number']);
|
||||
$order->update_meta_data('_czl_order_privatecode', $result['order_privatecode']);
|
||||
$order->update_meta_data('_czl_order_transfercode', $result['order_transfercode']);
|
||||
$order->update_meta_data('_czl_label_url', $result['label_url']);
|
||||
|
||||
// 添加订单备注
|
||||
$order->add_order_note(
|
||||
sprintf(
|
||||
__('CZL Express运单创建成功。
|
||||
运单号: %s
|
||||
订单号: %s
|
||||
参考号: %s',
|
||||
'woo-czl-express'),
|
||||
$result['tracking_number'],
|
||||
$result['order_id'],
|
||||
$result['reference_number']
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
// 更新订单状态
|
||||
$order->update_status('shipping', __('运单已创建,包裹开始运输', 'woo-czl-express'));
|
||||
|
||||
$order->save();
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单总重量
|
||||
*/
|
||||
private function get_order_weight($order) {
|
||||
$total_weight = 0;
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
if ($product && $product->get_weight()) {
|
||||
$total_weight += ($product->get_weight() * $item->get_quantity());
|
||||
}
|
||||
}
|
||||
return max(0.1, $total_weight); // 最小重量0.1kg
|
||||
}
|
||||
|
||||
private function get_order_items_count($order) {
|
||||
$count = 0;
|
||||
foreach ($order->get_items() as $item) {
|
||||
$count += $item->get_quantity();
|
||||
}
|
||||
return max(1, $count);
|
||||
}
|
||||
|
||||
private function prepare_invoice_items($order) {
|
||||
$items = array();
|
||||
foreach ($order->get_items() as $item) {
|
||||
$product = $item->get_product();
|
||||
$items[] = array(
|
||||
'invoice_amount' => $item->get_total(),
|
||||
'invoice_pcs' => $item->get_quantity(),
|
||||
'invoice_title' => $item->get_name(),
|
||||
'invoice_weight' => $product ? ($product->get_weight() * $item->get_quantity()) : 0.1,
|
||||
'item_id' => $product ? $product->get_id() : '',
|
||||
'sku' => $product ? $product->get_sku() : ''
|
||||
);
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单轨迹信息
|
||||
*/
|
||||
public function update_tracking_info($order_id) {
|
||||
try {
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tracking_number = $order->get_meta('_czl_tracking_number');
|
||||
if (empty($tracking_number)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取轨迹信息
|
||||
$tracking_info = $this->api->get_tracking_info($tracking_number);
|
||||
if (empty($tracking_info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存轨迹信息
|
||||
$order->update_meta_data('_czl_tracking_history', $tracking_info);
|
||||
|
||||
// 根据最新轨迹更新订单状态
|
||||
$latest_status = end($tracking_info['trackDetails']);
|
||||
if ($latest_status) {
|
||||
// 检查是否已签收
|
||||
if (strpos($latest_status['track_content'], '已签收') !== false ||
|
||||
strpos($latest_status['track_content'], 'Delivered') !== false) {
|
||||
$order->update_status('delivered', __('包裹已送达', 'woo-czl-express'));
|
||||
} else {
|
||||
$order->update_status('shipping', __('包裹运输中', 'woo-czl-express'));
|
||||
}
|
||||
}
|
||||
|
||||
$order->save();
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: Failed to update tracking info - ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
includes/class-czl-product-fields.php
Normal file
41
includes/class-czl-product-fields.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
class CZL_Product_Fields {
|
||||
public function __construct() {
|
||||
add_action('woocommerce_product_options_shipping', array($this, 'add_custom_fields'));
|
||||
add_action('woocommerce_process_product_meta', array($this, 'save_custom_fields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义字段
|
||||
*/
|
||||
public function add_custom_fields() {
|
||||
echo '<div class="options_group">';
|
||||
|
||||
woocommerce_wp_text_input(array(
|
||||
'id' => '_czl_name_en',
|
||||
'label' => __('英文品名', 'woo-czl-express'),
|
||||
'description' => __('用于国际物流申报', 'woo-czl-express'),
|
||||
'desc_tip' => true,
|
||||
));
|
||||
|
||||
woocommerce_wp_text_input(array(
|
||||
'id' => '_czl_hs_code',
|
||||
'label' => __('HS编码', 'woo-czl-express'),
|
||||
'description' => __('海关商品编码', 'woo-czl-express'),
|
||||
'desc_tip' => true,
|
||||
));
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存自定义字段
|
||||
*/
|
||||
public function save_custom_fields($post_id) {
|
||||
$name_en = isset($_POST['_czl_name_en']) ? sanitize_text_field($_POST['_czl_name_en']) : '';
|
||||
$hs_code = isset($_POST['_czl_hs_code']) ? sanitize_text_field($_POST['_czl_hs_code']) : '';
|
||||
|
||||
update_post_meta($post_id, '_czl_name_en', $name_en);
|
||||
update_post_meta($post_id, '_czl_hs_code', $hs_code);
|
||||
}
|
||||
}
|
378
includes/class-czl-rate-calculator.php
Normal file
378
includes/class-czl-rate-calculator.php
Normal file
@ -0,0 +1,378 @@
|
||||
<?php
|
||||
class CZL_Rate_Calculator {
|
||||
private $api;
|
||||
private $product_groups;
|
||||
private $exchange_rate;
|
||||
|
||||
public function __construct() {
|
||||
$this->api = new CZL_API();
|
||||
$this->product_groups = $this->get_product_groups();
|
||||
$this->exchange_rate = $this->get_exchange_rate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品分组配置
|
||||
*/
|
||||
private function get_product_groups() {
|
||||
// 获取产品分组配置
|
||||
$groups = get_option('czl_product_groups', array());
|
||||
|
||||
// 如果没有配置,使用默认分组
|
||||
if (empty($groups)) {
|
||||
$groups = array(
|
||||
'ups_blue' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'UPS Expedited',
|
||||
'prefixes' => array('UPS 蓝单')
|
||||
),
|
||||
'ups_red' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'UPS Saver',
|
||||
'prefixes' => array('UPS 红单')
|
||||
),
|
||||
'fedex_ie' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'FEDEX IE',
|
||||
'prefixes' => array('FEDEX IE')
|
||||
),
|
||||
'fedex_ip' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'FEDEX IP',
|
||||
'prefixes' => array('FEDEX IP')
|
||||
),
|
||||
'dhl' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'DHL',
|
||||
'prefixes' => array('DHL')
|
||||
),
|
||||
'europe_normal' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'European and American general package tax line',
|
||||
'prefixes' => array(
|
||||
'欧美经济专线(普货)',
|
||||
'欧美标准专线(普货)',
|
||||
'欧洲经济专线(普货)',
|
||||
'欧洲标准专线(普货)'
|
||||
)
|
||||
),
|
||||
'europe_b' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'European and American B-class tax line',
|
||||
'prefixes' => array(
|
||||
'欧美经济专线(B类)',
|
||||
'欧美标准专线(B类)',
|
||||
'欧洲经济专线(B类)',
|
||||
'欧洲标准专线(B类)'
|
||||
)
|
||||
),
|
||||
'europe_battery' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'European and American battery tax line',
|
||||
'prefixes' => array(
|
||||
'欧美经济专线(带电)',
|
||||
'欧美标准专线(带电)',
|
||||
'欧洲经济专线(带电)',
|
||||
'欧洲标准专线(带电)'
|
||||
)
|
||||
),
|
||||
'dubai_dhl' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'Dubai DHL',
|
||||
'prefixes' => array('迪拜DHL')
|
||||
),
|
||||
'dubai_ups' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'Dubai UPS',
|
||||
'prefixes' => array('迪拜UPS')
|
||||
),
|
||||
'dubai_fedex' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'Dubai FEDEX',
|
||||
'prefixes' => array('迪拜FEDEX')
|
||||
),
|
||||
'post' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'Post',
|
||||
'prefixes' => array('E特快', 'EMS')
|
||||
),
|
||||
'czl_uae' => array(
|
||||
'enabled' => true,
|
||||
'groupName' => 'CZL UAE Line',
|
||||
'prefixes' => array('CZL阿联酋')
|
||||
)
|
||||
);
|
||||
update_option('czl_product_groups', $groups);
|
||||
}
|
||||
|
||||
// 只返回启用的分组
|
||||
return array_filter($groups, function($group) {
|
||||
return !empty($group['enabled']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取汇率
|
||||
*/
|
||||
private function get_exchange_rate() {
|
||||
// 获取WooCommerce货币设置
|
||||
$wc_currency = get_woocommerce_currency();
|
||||
|
||||
if ($wc_currency === 'CNY') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 尝试从设置获取自定义汇率
|
||||
$custom_rate = get_option('czl_exchange_rate_' . $wc_currency);
|
||||
if (!empty($custom_rate)) {
|
||||
return floatval($custom_rate);
|
||||
}
|
||||
|
||||
// 如果没有自定义汇率,使用WooCommerce的汇率转换
|
||||
if (function_exists('wc_get_price_in_currency')) {
|
||||
return wc_get_price_in_currency(1, $wc_currency);
|
||||
}
|
||||
|
||||
// 如果都没有,返回默认汇率
|
||||
$default_rates = array(
|
||||
'USD' => 0.14, // 1 CNY = 0.14 USD
|
||||
'EUR' => 0.13, // 1 CNY = 0.13 EUR
|
||||
'GBP' => 0.11, // 1 CNY = 0.11 GBP
|
||||
// 添加其他常用货币...
|
||||
);
|
||||
|
||||
return isset($default_rates[$wc_currency]) ? $default_rates[$wc_currency] : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换货币
|
||||
*/
|
||||
private function convert_currency($amount) {
|
||||
return $amount * $this->exchange_rate;
|
||||
}
|
||||
|
||||
private function adjust_shipping_rate($rate) {
|
||||
$adjustment = get_option('czl_rate_adjustment', '');
|
||||
if (empty($adjustment)) {
|
||||
return $rate;
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析调整公式
|
||||
$formula = strtolower(str_replace(' ', '', $adjustment));
|
||||
$original_rate = $rate;
|
||||
|
||||
// 处理百分比
|
||||
if (strpos($formula, '%') !== false) {
|
||||
preg_match('/(\d+)%/', $formula, $matches);
|
||||
if (!empty($matches[1])) {
|
||||
$percentage = floatval($matches[1]) / 100;
|
||||
$rate = $rate * (1 + $percentage);
|
||||
}
|
||||
$formula = preg_replace('/\d+%/', '', $formula);
|
||||
}
|
||||
|
||||
// 处理固定金额
|
||||
if (preg_match('/([+-])(\d+)/', $formula, $matches)) {
|
||||
$operator = $matches[1];
|
||||
$amount = floatval($matches[2]);
|
||||
$rate = $operator === '+' ? $rate + $amount : $rate - $amount;
|
||||
}
|
||||
|
||||
error_log(sprintf(
|
||||
'CZL Express: Adjusted rate from %f to %f using formula: %s',
|
||||
$original_rate,
|
||||
$rate,
|
||||
$adjustment
|
||||
));
|
||||
|
||||
return max(0, $rate);
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express: Rate adjustment error - ' . $e->getMessage());
|
||||
return $rate;
|
||||
}
|
||||
}
|
||||
|
||||
public function calculate_shipping_rate($package) {
|
||||
try {
|
||||
error_log('CZL Express: Calculating shipping rate for package: ' . print_r($package, true));
|
||||
|
||||
// 基本验证
|
||||
if (empty($package['destination']['country'])) {
|
||||
error_log('CZL Express: Empty destination country');
|
||||
return array();
|
||||
}
|
||||
|
||||
// 获取包裹信息
|
||||
$weight = 0;
|
||||
$length = 0;
|
||||
$width = 0;
|
||||
$height = 0;
|
||||
|
||||
foreach ($package['contents'] as $item) {
|
||||
$product = $item['data'];
|
||||
$quantity = $item['quantity'];
|
||||
|
||||
// 累加重量
|
||||
$item_weight = (float)$product->get_weight();
|
||||
if ($item_weight > 0) {
|
||||
$weight += $item_weight * $quantity;
|
||||
}
|
||||
|
||||
// 获取最大尺寸
|
||||
$item_length = (float)$product->get_length();
|
||||
$item_width = (float)$product->get_width();
|
||||
$item_height = (float)$product->get_height();
|
||||
|
||||
$length = max($length, $item_length);
|
||||
$width = max($width, $item_width);
|
||||
$height = max($height, $item_height);
|
||||
}
|
||||
|
||||
// 调用API获取运费
|
||||
$api_params = array(
|
||||
'weight' => $weight > 0 ? $weight : 0.1, // 默认最小重量0.1kg
|
||||
'country' => $package['destination']['country'],
|
||||
'postcode' => $package['destination']['postcode'],
|
||||
'length' => $length > 0 ? $length : 1,
|
||||
'width' => $width > 0 ? $width : 1,
|
||||
'height' => $height > 0 ? $height : 1,
|
||||
'cargoType' => 'P'
|
||||
);
|
||||
|
||||
error_log('CZL Express: API params: ' . print_r($api_params, true));
|
||||
|
||||
// 调用API获取运费
|
||||
$api_rates = $this->api->get_shipping_rate($api_params);
|
||||
error_log('CZL Express: API response: ' . print_r($api_rates, true));
|
||||
|
||||
if (empty($api_rates)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// 按产品分组处理运费
|
||||
$grouped_rates = array();
|
||||
$all_rates = array(); // 存储所有线路
|
||||
|
||||
foreach ($api_rates as $rate) {
|
||||
$product_name = $rate['product_name'];
|
||||
$group_found = false;
|
||||
|
||||
// 调整运费
|
||||
$adjusted_amount = $this->adjust_shipping_rate(floatval($rate['total_amount']));
|
||||
|
||||
// 添加单独的线路选项
|
||||
$rate_id = sanitize_title($product_name);
|
||||
$all_rates[$rate_id] = array(
|
||||
'id' => 'czl_express_' . $rate_id,
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
$product_name,
|
||||
$this->translate_delivery_time($rate['product_aging'])
|
||||
),
|
||||
'cost' => $this->convert_currency($adjusted_amount),
|
||||
'calc_tax' => 'per_order',
|
||||
'meta_data' => array(
|
||||
'product_id' => $rate['product_id'],
|
||||
'delivery_time' => $rate['product_aging'],
|
||||
'is_group' => false,
|
||||
'original_amount' => $rate['total_amount'],
|
||||
'adjusted_amount' => $adjusted_amount
|
||||
)
|
||||
);
|
||||
|
||||
// 查找匹配的分组
|
||||
foreach ($this->product_groups as $group_key => $group) {
|
||||
foreach ($group['prefixes'] as $prefix) {
|
||||
if (strpos($product_name, $prefix) === 0) {
|
||||
$group_id = sanitize_title($group['groupName']);
|
||||
if (!isset($grouped_rates[$group_id]) ||
|
||||
$rate['total_amount'] < $grouped_rates[$group_id]['meta_data']['original_amount']) {
|
||||
$grouped_rates[$group_id] = array(
|
||||
'id' => 'czl_express_group_' . $group_id,
|
||||
'label' => sprintf(
|
||||
'%s (%s)',
|
||||
$group['groupName'],
|
||||
$this->translate_delivery_time($rate['product_aging'])
|
||||
),
|
||||
'cost' => $this->convert_currency(floatval($rate['total_amount'])),
|
||||
'calc_tax' => 'per_order',
|
||||
'meta_data' => array(
|
||||
'product_id' => $rate['product_id'],
|
||||
'delivery_time' => $rate['product_aging'],
|
||||
'original_name' => $product_name,
|
||||
'is_group' => true,
|
||||
'group_name' => $group['groupName'],
|
||||
'original_amount' => $rate['total_amount']
|
||||
)
|
||||
);
|
||||
}
|
||||
$group_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($group_found) break;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据设置决定返回分组还是所有线路
|
||||
$show_all_rates = get_option('czl_show_all_rates', 'no');
|
||||
if ($show_all_rates === 'yes') {
|
||||
return array_values($all_rates);
|
||||
} else {
|
||||
return array_values($grouped_rates);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: ' . $e->getMessage());
|
||||
wc_add_notice($e->getMessage(), 'error');
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
private function translate_delivery_time($delivery_time) {
|
||||
// 获取当前语言环境
|
||||
$locale = determine_locale();
|
||||
|
||||
// 如果是中文环境,保持原样
|
||||
if (strpos($locale, 'zh_') === 0) {
|
||||
return $delivery_time;
|
||||
}
|
||||
|
||||
// 匹配中文时效格式
|
||||
if (preg_match('/(\d+)-(\d+)个工作日/', $delivery_time, $matches)) {
|
||||
return sprintf('%d-%d working days', $matches[1], $matches[2]);
|
||||
}
|
||||
|
||||
if (preg_match('/(\d+)个工作日/', $delivery_time, $matches)) {
|
||||
return sprintf('%d working days', $matches[1]);
|
||||
}
|
||||
|
||||
// 匹配"预计XX天左右"的格式
|
||||
if (preg_match('/预计(\d+)天左右/', $delivery_time, $matches)) {
|
||||
return sprintf('About %d days', $matches[1]);
|
||||
}
|
||||
|
||||
// 匹配"预计XX-XX天"的格式
|
||||
if (preg_match('/预计(\d+)-(\d+)天/', $delivery_time, $matches)) {
|
||||
return sprintf('About %d-%d days', $matches[1], $matches[2]);
|
||||
}
|
||||
|
||||
// 其他常见格式的翻译
|
||||
$translations = array(
|
||||
'当天送达' => 'Same day delivery',
|
||||
'次日送达' => 'Next day delivery',
|
||||
'隔日送达' => 'Second day delivery',
|
||||
'工作日' => 'working days',
|
||||
'左右' => 'about'
|
||||
);
|
||||
|
||||
foreach ($translations as $cn => $en) {
|
||||
if (strpos($delivery_time, $cn) !== false) {
|
||||
return $en;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有匹配到任何格式,返回原始值
|
||||
return $delivery_time;
|
||||
}
|
||||
}
|
55
includes/class-czl-settings.php
Normal file
55
includes/class-czl-settings.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
class CZL_Settings {
|
||||
private static $instance = null;
|
||||
|
||||
public static function instance() {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
add_action('admin_init', array($this, 'register_settings'));
|
||||
}
|
||||
|
||||
public function register_settings() {
|
||||
register_setting('czl_options_group', 'czl_api_url');
|
||||
register_setting('czl_options_group', 'czl_username');
|
||||
register_setting('czl_options_group', 'czl_password');
|
||||
register_setting('czl_options_group', 'czl_exchange_rate');
|
||||
register_setting('czl_options_group', 'czl_product_groups');
|
||||
}
|
||||
|
||||
public function get_settings_fields() {
|
||||
return array(
|
||||
'basic' => array(
|
||||
array(
|
||||
'name' => 'czl_api_url',
|
||||
'label' => __('API URL', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
),
|
||||
array(
|
||||
'name' => 'czl_username',
|
||||
'label' => __('Username', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'default' => '',
|
||||
),
|
||||
array(
|
||||
'name' => 'czl_password',
|
||||
'label' => __('Password', 'woo-czl-express'),
|
||||
'type' => 'password',
|
||||
'default' => '',
|
||||
),
|
||||
array(
|
||||
'name' => 'czl_exchange_rate',
|
||||
'label' => __('Exchange Rate', 'woo-czl-express'),
|
||||
'type' => 'number',
|
||||
'default' => '1',
|
||||
'step' => '0.0001',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
161
includes/class-czl-shipping-method.php
Normal file
161
includes/class-czl-shipping-method.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
class WC_CZL_Shipping_Method extends WC_Shipping_Method {
|
||||
private $api;
|
||||
private $calculator;
|
||||
|
||||
public function __construct($instance_id = 0) {
|
||||
parent::__construct($instance_id);
|
||||
|
||||
$this->id = 'czl_express';
|
||||
$this->instance_id = absint($instance_id);
|
||||
$this->title = __('CZL Express', 'woo-czl-express');
|
||||
$this->method_title = __('CZL Express', 'woo-czl-express');
|
||||
$this->method_description = __('CZL Express shipping integration', 'woo-czl-express');
|
||||
|
||||
$this->supports = array(
|
||||
'shipping-zones',
|
||||
'instance-settings',
|
||||
'instance-settings-modal',
|
||||
);
|
||||
|
||||
$this->enabled = 'yes';
|
||||
|
||||
$this->init();
|
||||
|
||||
$this->api = new CZL_API();
|
||||
$this->calculator = new CZL_Rate_Calculator();
|
||||
|
||||
// 添加前端资源
|
||||
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
|
||||
}
|
||||
|
||||
public function init() {
|
||||
$this->init_form_fields();
|
||||
$this->init_settings();
|
||||
|
||||
$this->title = $this->get_option('title', $this->method_title);
|
||||
$this->enabled = $this->get_option('enabled', 'yes');
|
||||
|
||||
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
|
||||
}
|
||||
|
||||
public function init_form_fields() {
|
||||
$this->instance_form_fields = array(
|
||||
'enabled' => array(
|
||||
'title' => __('Enable/Disable', 'woo-czl-express'),
|
||||
'type' => 'checkbox',
|
||||
'label' => __('Enable this shipping method', 'woo-czl-express'),
|
||||
'default' => 'yes'
|
||||
),
|
||||
'title' => array(
|
||||
'title' => __('Method Title', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'description' => __('This controls the title which the user sees during checkout.', 'woo-czl-express'),
|
||||
'default' => __('CZL Express', 'woo-czl-express'),
|
||||
'desc_tip' => true
|
||||
),
|
||||
'show_all_rates' => array(
|
||||
'title' => __('显示方式', 'woo-czl-express'),
|
||||
'type' => 'select',
|
||||
'description' => __('选择是显示分组运费还是显示所有具体线路', 'woo-czl-express'),
|
||||
'default' => 'no',
|
||||
'options' => array(
|
||||
'no' => __('显示分组运费', 'woo-czl-express'),
|
||||
'yes' => __('显示所有线路', 'woo-czl-express')
|
||||
)
|
||||
),
|
||||
'sort_by' => array(
|
||||
'title' => __('排序方式', 'woo-czl-express'),
|
||||
'type' => 'select',
|
||||
'description' => __('选择运费显示的排序方式', 'woo-czl-express'),
|
||||
'default' => 'price',
|
||||
'options' => array(
|
||||
'price' => __('按价格排序', 'woo-czl-express'),
|
||||
'time' => __('按时效排序', 'woo-czl-express')
|
||||
)
|
||||
),
|
||||
'exchange_rate' => array(
|
||||
'title' => __('汇率设置', 'woo-czl-express'),
|
||||
'type' => 'text',
|
||||
'description' => sprintf(
|
||||
__('设置CNY到%s的汇率。例如:如果1CNY=%s0.14,输入0.14', 'woo-czl-express'),
|
||||
get_woocommerce_currency(),
|
||||
get_woocommerce_currency_symbol()
|
||||
),
|
||||
'default' => $this->get_default_exchange_rate(),
|
||||
'desc_tip' => true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function get_default_exchange_rate() {
|
||||
$currency = get_woocommerce_currency();
|
||||
$default_rates = array(
|
||||
'USD' => 0.14,
|
||||
'EUR' => 0.13,
|
||||
'GBP' => 0.11,
|
||||
// 添加其他常用货币...
|
||||
);
|
||||
return isset($default_rates[$currency]) ? $default_rates[$currency] : 1;
|
||||
}
|
||||
|
||||
public function calculate_shipping($package = array()) {
|
||||
if ($this->enabled !== 'yes') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
error_log('CZL Express: Starting shipping calculation');
|
||||
|
||||
$rates = $this->calculator->calculate_shipping_rate($package);
|
||||
|
||||
if (!empty($rates)) {
|
||||
foreach ($rates as $rate) {
|
||||
$rate_id = $this->id . '_' . $rate['product_id'];
|
||||
$this->add_rate(array(
|
||||
'id' => $rate_id,
|
||||
'label' => $rate['method_title'],
|
||||
'cost' => $rate['cost'],
|
||||
'meta_data' => array(
|
||||
'product_id' => $rate['product_id'],
|
||||
'delivery_time' => $rate['delivery_time'],
|
||||
'original_name' => $rate['original_name'],
|
||||
'is_group' => $rate['is_group'],
|
||||
'group_name' => $rate['group_name'],
|
||||
'original_amount' => $rate['original_amount']
|
||||
)
|
||||
));
|
||||
}
|
||||
error_log('CZL Express: Added ' . count($rates) . ' shipping rates');
|
||||
} else {
|
||||
error_log('CZL Express: No shipping rates returned');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: ' . $e->getMessage());
|
||||
wc_add_notice($e->getMessage(), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueue_scripts() {
|
||||
if (is_cart() || is_checkout()) {
|
||||
wp_enqueue_style(
|
||||
'czl-shipping-method',
|
||||
WOO_CZL_EXPRESS_URL . 'assets/css/shipping-method.css',
|
||||
array(),
|
||||
WOO_CZL_EXPRESS_VERSION
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function process_admin_options() {
|
||||
parent::process_admin_options();
|
||||
|
||||
// 保存汇率设置
|
||||
$currency = get_woocommerce_currency();
|
||||
$rate = $this->get_option('exchange_rate');
|
||||
if (!empty($rate)) {
|
||||
update_option('czl_exchange_rate_' . $currency, $rate);
|
||||
}
|
||||
}
|
||||
}
|
150
includes/class-czl-tracking.php
Normal file
150
includes/class-czl-tracking.php
Normal file
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
class CZL_Tracking {
|
||||
/**
|
||||
* 在订单详情页显示跟踪信息
|
||||
*/
|
||||
public static function display_tracking_info($order) {
|
||||
$tracking_number = get_post_meta($order->get_id(), '_czl_tracking_number', true);
|
||||
if (!$tracking_number) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$api = new CZL_API();
|
||||
$tracking_info = $api->get_tracking($tracking_number);
|
||||
|
||||
if (!empty($tracking_info)) {
|
||||
echo '<h2>' . __('物流跟踪信息', 'woo-czl-express') . '</h2>';
|
||||
echo '<div class="czl-tracking-info">';
|
||||
echo '<p><strong>' . __('运单号:', 'woo-czl-express') . '</strong>' . esc_html($tracking_number) . '</p>';
|
||||
|
||||
if (!empty($tracking_info['trackDetails'])) {
|
||||
echo '<table class="czl-tracking-details">';
|
||||
echo '<thead><tr>';
|
||||
echo '<th>' . __('时间', 'woo-czl-express') . '</th>';
|
||||
echo '<th>' . __('地点', 'woo-czl-express') . '</th>';
|
||||
echo '<th>' . __('状态', 'woo-czl-express') . '</th>';
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
foreach ($tracking_info['trackDetails'] as $detail) {
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html($detail['track_date']) . '</td>';
|
||||
echo '<td>' . esc_html($detail['track_location']) . '</td>';
|
||||
echo '<td>' . esc_html($detail['track_content']) . '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
echo '</tbody></table>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Tracking Error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加跟踪链接到订单邮件
|
||||
*/
|
||||
public static function add_tracking_to_email($order, $sent_to_admin = false) {
|
||||
$tracking_number = get_post_meta($order->get_id(), '_czl_tracking_number', true);
|
||||
if ($tracking_number) {
|
||||
$tracking_url = 'https://exp.czl.net/track/?query=' . urlencode($tracking_number);
|
||||
echo '<p><strong>' . __('物流跟踪:', 'woo-czl-express') . '</strong>';
|
||||
echo '<a href="' . esc_url($tracking_url) . '" target="_blank">' . esc_html($tracking_number) . '</a></p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在管理员订单页面显示轨迹信息
|
||||
*/
|
||||
public static function display_admin_tracking_info($order) {
|
||||
$tracking_number = $order->get_meta('_czl_tracking_number');
|
||||
if (empty($tracking_number)) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="czl-admin-tracking-info">
|
||||
<h3><?php _e('CZL Express 运单信息', 'woo-czl-express'); ?></h3>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
__('运单号: %s', 'woo-czl-express'),
|
||||
'<a href="https://exp.czl.net/track/?query=' . esc_attr($tracking_number) . '" target="_blank">' .
|
||||
esc_html($tracking_number) . '</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
// 显示子单号
|
||||
$child_numbers = $order->get_meta('_czl_child_numbers');
|
||||
if (!empty($child_numbers)) {
|
||||
echo '<p><strong>' . __('子单号:', 'woo-czl-express') . '</strong> ' .
|
||||
implode(', ', array_map('esc_html', $child_numbers)) . '</p>';
|
||||
}
|
||||
|
||||
// 显示参考号
|
||||
$reference_number = $order->get_meta('_czl_reference_number');
|
||||
if (!empty($reference_number)) {
|
||||
echo '<p><strong>' . __('参考号:', 'woo-czl-express') . '</strong> ' .
|
||||
esc_html($reference_number) . '</p>';
|
||||
}
|
||||
|
||||
// 显示偏远信息
|
||||
$is_remote = $order->get_meta('_czl_is_remote');
|
||||
if (!empty($is_remote)) {
|
||||
$remote_text = '';
|
||||
switch ($is_remote) {
|
||||
case 'Y':
|
||||
$remote_text = __('偏远地区', 'woo-czl-express');
|
||||
break;
|
||||
case 'A':
|
||||
$remote_text = __('FedEx偏远A级', 'woo-czl-express');
|
||||
break;
|
||||
case 'B':
|
||||
$remote_text = __('FedEx偏远B级', 'woo-czl-express');
|
||||
break;
|
||||
case 'C':
|
||||
$remote_text = __('FedEx偏远C级', 'woo-czl-express');
|
||||
break;
|
||||
case 'N':
|
||||
$remote_text = __('非偏远地区', 'woo-czl-express');
|
||||
break;
|
||||
}
|
||||
if ($remote_text) {
|
||||
echo '<p><strong>' . __('地区类型:', 'woo-czl-express') . '</strong> ' .
|
||||
esc_html($remote_text) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// 显示住宅信息
|
||||
$is_residential = $order->get_meta('_czl_is_residential');
|
||||
if ($is_residential === 'Y') {
|
||||
echo '<p><strong>' . __('地址类型:', 'woo-czl-express') . '</strong> ' .
|
||||
__('住宅地址', 'woo-czl-express') . '</p>';
|
||||
}
|
||||
|
||||
// 显示轨迹信息
|
||||
$tracking_history = $order->get_meta('_czl_tracking_history');
|
||||
if (!empty($tracking_history['trackDetails'])) {
|
||||
?>
|
||||
<div class="czl-tracking-details">
|
||||
<h4><?php _e('最新轨迹', 'woo-czl-express'); ?></h4>
|
||||
<?php
|
||||
$latest = reset($tracking_history['trackDetails']);
|
||||
?>
|
||||
<p>
|
||||
<span class="tracking-date"><?php echo esc_html($latest['track_date']); ?></span>
|
||||
<span class="tracking-content"><?php echo esc_html($latest['track_content']); ?></span>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
22
includes/class-wc-czlexpress-install.php
Normal file
22
includes/class-wc-czlexpress-install.php
Normal file
@ -0,0 +1,22 @@
|
||||
class WC_CZLExpress_Install {
|
||||
|
||||
public function migrate_to_hpos() {
|
||||
global $wpdb;
|
||||
|
||||
// 获取所有需要迁移的订单
|
||||
$orders = $wpdb->get_results("
|
||||
SELECT post_id, meta_key, meta_value
|
||||
FROM {$wpdb->postmeta}
|
||||
WHERE meta_key LIKE '_czlexpress_%'
|
||||
");
|
||||
|
||||
foreach ($orders as $order_data) {
|
||||
$order = wc_get_order($order_data->post_id);
|
||||
if ($order) {
|
||||
// 迁移元数据到新系统
|
||||
$order->update_meta_data($order_data->meta_key, $order_data->meta_value);
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
includes/class-wc-czlexpress-order.php
Normal file
27
includes/class-wc-czlexpress-order.php
Normal file
@ -0,0 +1,27 @@
|
||||
class WC_CZLExpress_Order {
|
||||
|
||||
public function save_tracking_info($order_id, $tracking_number, $label_url) {
|
||||
// 使用新的HPOS API保存跟踪信息
|
||||
$order = wc_get_order($order_id);
|
||||
|
||||
if ($order) {
|
||||
// 使用新的元数据API
|
||||
$order->update_meta_data('_czlexpress_tracking_number', $tracking_number);
|
||||
$order->update_meta_data('_czlexpress_label_url', $label_url);
|
||||
$order->save();
|
||||
}
|
||||
}
|
||||
|
||||
public function get_tracking_info($order_id) {
|
||||
$order = wc_get_order($order_id);
|
||||
|
||||
if ($order) {
|
||||
return array(
|
||||
'tracking_number' => $order->get_meta('_czlexpress_tracking_number'),
|
||||
'label_url' => $order->get_meta('_czlexpress_label_url')
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
13
includes/class-wc-czlexpress-orders-list.php
Normal file
13
includes/class-wc-czlexpress-orders-list.php
Normal file
@ -0,0 +1,13 @@
|
||||
class WC_CZLExpress_Orders_List {
|
||||
|
||||
public function get_orders($args = array()) {
|
||||
// 使用新的订单查询API
|
||||
$query = new \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableQuery($args);
|
||||
return $query->get_orders();
|
||||
}
|
||||
|
||||
public function get_order_items($order_id) {
|
||||
$order = wc_get_order($order_id);
|
||||
return $order ? $order->get_items() : array();
|
||||
}
|
||||
}
|
16
includes/class-wc-czlexpress-shipping.php
Normal file
16
includes/class-wc-czlexpress-shipping.php
Normal file
@ -0,0 +1,16 @@
|
||||
class WC_CZLExpress_Shipping {
|
||||
|
||||
public function get_shipping_info($order_id) {
|
||||
$order = wc_get_order($order_id);
|
||||
|
||||
if ($order) {
|
||||
return array(
|
||||
'weight' => $order->get_meta('_czlexpress_weight'),
|
||||
'remote_fee' => $order->get_meta('_czlexpress_remote_fee'),
|
||||
'shipping_method' => $order->get_meta('_czlexpress_shipping_method')
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
631
includes/class-woo-czl-express.php
Normal file
631
includes/class-woo-czl-express.php
Normal file
@ -0,0 +1,631 @@
|
||||
<?php
|
||||
class WooCzlExpress {
|
||||
private static $instance = null;
|
||||
private $order_handler;
|
||||
|
||||
public static function instance() {
|
||||
if (is_null(self::$instance)) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
register_activation_hook(WOO_CZL_EXPRESS_PATH . 'woo-commerce-czlexpress.php', array('CZL_Install', 'init'));
|
||||
$this->init();
|
||||
add_action('admin_init', array($this, 'register_settings'));
|
||||
add_action('wp_ajax_czl_test_connection', array($this, 'handle_test_connection'));
|
||||
|
||||
// 添加自定义订单状态
|
||||
add_action('init', array($this, 'register_custom_order_statuses'));
|
||||
add_filter('wc_order_statuses', array($this, 'add_custom_order_statuses'));
|
||||
|
||||
// 添加订单状态自动更新
|
||||
add_action('wp_ajax_czl_update_order_status', array($this, 'update_order_status'));
|
||||
add_action('wp_ajax_nopriv_czl_update_order_status', array($this, 'update_order_status'));
|
||||
|
||||
// 添加订单详情页面的轨迹显示
|
||||
add_action('woocommerce_order_details_after_order_table', array('CZL_Tracking', 'display_tracking_info'));
|
||||
add_action('woocommerce_admin_order_data_after_shipping_address', array('CZL_Tracking', 'display_admin_tracking_info'));
|
||||
|
||||
// 添加定时任务
|
||||
add_action('czl_update_tracking_info', array($this, 'schedule_tracking_updates'));
|
||||
|
||||
// 注册定时任务
|
||||
if (!wp_next_scheduled('czl_update_tracking_info')) {
|
||||
wp_schedule_event(time(), 'hourly', 'czl_update_tracking_info');
|
||||
}
|
||||
}
|
||||
|
||||
private function init() {
|
||||
// 加载依赖文件
|
||||
$this->load_dependencies();
|
||||
|
||||
// 初始化产品字段
|
||||
new CZL_Product_Fields();
|
||||
|
||||
// 初始化钩子
|
||||
$this->init_hooks();
|
||||
|
||||
// 添加菜单
|
||||
add_action('admin_menu', array($this, 'add_admin_menu'));
|
||||
}
|
||||
|
||||
private function load_dependencies() {
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-api.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-rate-calculator.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-shipping-method.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-install.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-settings.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-order-handler.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-order-data.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-label.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-tracking.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-product-fields.php';
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-order.php';
|
||||
}
|
||||
|
||||
private function init_hooks() {
|
||||
add_action('woocommerce_shipping_init', array($this, 'init_shipping_method'));
|
||||
add_filter('woocommerce_shipping_methods', array($this, 'add_shipping_method'));
|
||||
|
||||
// 订单处理钩子
|
||||
$order_handler = new CZL_Order_Handler();
|
||||
add_action('woocommerce_order_status_processing', array($order_handler, 'create_shipment'));
|
||||
add_action('woocommerce_order_status_cancelled', array($order_handler, 'cancel_shipment'));
|
||||
|
||||
// 运单标签钩子
|
||||
add_filter('woocommerce_order_actions', array('CZL_Label', 'add_print_actions'), 10, 2);
|
||||
add_action('wp_ajax_czl_print_label', array('CZL_Label', 'handle_print_request'));
|
||||
|
||||
// 跟踪信息显示钩子
|
||||
add_action('woocommerce_order_details_after_order_table', array('CZL_Tracking', 'display_tracking_info'));
|
||||
add_action('woocommerce_admin_order_data_after_shipping_address', array('CZL_Tracking', 'display_admin_tracking_info'));
|
||||
|
||||
// 添加订单操作
|
||||
add_filter('woocommerce_order_actions', array($this, 'add_order_actions'), 10, 2);
|
||||
|
||||
// 添加AJAX处理
|
||||
add_action('wp_ajax_czl_create_shipment', array($this, 'handle_create_shipment'));
|
||||
add_action('wp_ajax_czl_bulk_create_shipment', array($this, 'handle_bulk_create_shipment'));
|
||||
}
|
||||
|
||||
public function init_shipping_method() {
|
||||
if (!class_exists('WC_CZL_Shipping_Method')) {
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-shipping-method.php';
|
||||
}
|
||||
}
|
||||
|
||||
public function add_shipping_method($methods) {
|
||||
if (class_exists('WC_CZL_Shipping_Method')) {
|
||||
$methods['czl_express'] = 'WC_CZL_Shipping_Method';
|
||||
}
|
||||
return $methods;
|
||||
}
|
||||
|
||||
public function add_admin_menu() {
|
||||
add_menu_page(
|
||||
__('CZL Express', 'woo-czl-express'),
|
||||
__('CZL Express', 'woo-czl-express'),
|
||||
'manage_woocommerce',
|
||||
'czl-express',
|
||||
array($this, 'render_settings_page'),
|
||||
'dashicons-airplane'
|
||||
);
|
||||
|
||||
// 添加订单管理子菜单
|
||||
add_submenu_page(
|
||||
'czl-express',
|
||||
__('订单管理', 'woo-czl-express'),
|
||||
__('订单管理', 'woo-czl-express'),
|
||||
'manage_woocommerce',
|
||||
'czl-express-orders',
|
||||
array($this, 'render_orders_page')
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'czl-express',
|
||||
__('基本设置', 'woo-czl-express'),
|
||||
__('基本设置', 'woo-czl-express'),
|
||||
'manage_options',
|
||||
'czl-express'
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'czl-express',
|
||||
__('产品分组', 'woo-czl-express'),
|
||||
__('产品分组', 'woo-czl-express'),
|
||||
'manage_options',
|
||||
'czl-product-groups',
|
||||
array($this, 'render_product_groups_page')
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'czl-express',
|
||||
__('汇率设置', 'woo-czl-express'),
|
||||
__('汇率设置', 'woo-czl-express'),
|
||||
'manage_options',
|
||||
'czl-exchange-rates',
|
||||
array($this, 'render_exchange_rates_page')
|
||||
);
|
||||
}
|
||||
|
||||
public function render_product_groups_page() {
|
||||
// 处理表单提交
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' &&
|
||||
check_admin_referer('czl_save_product_groups', 'czl_product_groups_nonce')) {
|
||||
|
||||
$groups = array();
|
||||
if (!empty($_POST['groups'])) {
|
||||
foreach ($_POST['groups'] as $key => $group) {
|
||||
if (empty($group['groupName'])) continue;
|
||||
|
||||
$prefixes = array_filter(array_map('trim', explode("\n", $group['prefixes'])));
|
||||
if (empty($prefixes)) continue;
|
||||
|
||||
$groups[sanitize_key($group['groupName'])] = array(
|
||||
'enabled' => !empty($group['enabled']),
|
||||
'groupName' => sanitize_text_field($group['groupName']),
|
||||
'prefixes' => array_map('sanitize_text_field', $prefixes)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
update_option('czl_product_groups', $groups);
|
||||
add_settings_error('czl_messages', 'czl_message', __('产品分组设置已保存', 'woo-czl-express'), 'updated');
|
||||
}
|
||||
|
||||
// 显示设置页面
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/product-groups.php';
|
||||
}
|
||||
|
||||
public function admin_page() {
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/admin-page.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加订单操作按钮
|
||||
*/
|
||||
public function add_order_actions($actions, $order) {
|
||||
// 检查是否已创建运单
|
||||
$tracking_number = get_post_meta($order->get_id(), '_czl_tracking_number', true);
|
||||
|
||||
if ($tracking_number) {
|
||||
$actions['czl_cancel_shipment'] = __('取消CZL运单', 'woo-czl-express');
|
||||
} else {
|
||||
$actions['czl_create_shipment'] = __('创建CZL运单', 'woo-czl-express');
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
public function register_settings() {
|
||||
// 注册设置组
|
||||
register_setting(
|
||||
'czl_options_group', // 设置组名称
|
||||
'czl_username' // 用户名选项
|
||||
);
|
||||
|
||||
register_setting(
|
||||
'czl_options_group',
|
||||
'czl_password'
|
||||
);
|
||||
|
||||
register_setting(
|
||||
'czl_options_group',
|
||||
'czl_rate_adjustment'
|
||||
);
|
||||
|
||||
// 添加设置分节
|
||||
add_settings_section(
|
||||
'czl_api_settings',
|
||||
__('API设置', 'woo-czl-express'),
|
||||
null,
|
||||
'czl_options'
|
||||
);
|
||||
|
||||
// 添加设置字段
|
||||
add_settings_field(
|
||||
'czl_username',
|
||||
__('用户名', 'woo-czl-express'),
|
||||
array($this, 'username_field_callback'),
|
||||
'czl_options',
|
||||
'czl_api_settings'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'czl_password',
|
||||
__('密码', 'woo-czl-express'),
|
||||
array($this, 'password_field_callback'),
|
||||
'czl_options',
|
||||
'czl_api_settings'
|
||||
);
|
||||
|
||||
// 添加运费调整设置分节
|
||||
add_settings_section(
|
||||
'czl_rate_settings',
|
||||
__('运费设置', 'woo-czl-express'),
|
||||
null,
|
||||
'czl_options'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'czl_rate_adjustment',
|
||||
__('运费调整公式', 'woo-czl-express'),
|
||||
array($this, 'rate_adjustment_field_callback'),
|
||||
'czl_options',
|
||||
'czl_rate_settings'
|
||||
);
|
||||
}
|
||||
|
||||
public function username_field_callback() {
|
||||
$value = get_option('czl_username');
|
||||
echo '<input type="text" name="czl_username" value="' . esc_attr($value) . '" />';
|
||||
}
|
||||
|
||||
public function password_field_callback() {
|
||||
$value = get_option('czl_password');
|
||||
echo '<input type="password" name="czl_password" value="' . esc_attr($value) . '" />';
|
||||
}
|
||||
|
||||
public function rate_adjustment_field_callback() {
|
||||
$value = get_option('czl_rate_adjustment');
|
||||
?>
|
||||
<input type="text" name="czl_rate_adjustment" value="<?php echo esc_attr($value); ?>" class="regular-text">
|
||||
<p class="description">
|
||||
<?php _e('设置运费调整公式,支持百分比和固定金额。例如:', 'woo-czl-express'); ?><br>
|
||||
- 10% + 10 (运费乘以1.1后加10)<br>
|
||||
- 20% (运费乘以1.2)<br>
|
||||
- +15 (运费加15)<br>
|
||||
<?php _e('留空表示不调整运费', 'woo-czl-express'); ?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function handle_test_connection() {
|
||||
check_ajax_referer('czl_test_api', 'nonce');
|
||||
|
||||
$username = get_option('czl_username');
|
||||
$password = get_option('czl_password');
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
wp_send_json_error(array(
|
||||
'message' => __('请先配置API账号和密码', 'woo-czl-express')
|
||||
));
|
||||
}
|
||||
|
||||
// 测试API连接
|
||||
$response = wp_remote_get('https://tms-api-go.czl.net/api/countries', array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Basic ' . base64_encode($username . ':' . $password)
|
||||
)
|
||||
));
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
wp_send_json_error(array(
|
||||
'message' => $response->get_error_message()
|
||||
));
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body($response);
|
||||
$data = json_decode($body, true);
|
||||
|
||||
if (!empty($data) && isset($data['code']) && $data['code'] == 200) {
|
||||
wp_send_json_success(array(
|
||||
'message' => __('API连接测试成功', 'woo-czl-express')
|
||||
));
|
||||
} else {
|
||||
wp_send_json_error(array(
|
||||
'message' => __('API连接测试失败,请检查账号密码是否正确', 'woo-czl-express')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function render_settings_page() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示设置页面
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/settings.php';
|
||||
}
|
||||
|
||||
public function render_exchange_rates_page() {
|
||||
// 处理表单提交
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' &&
|
||||
check_admin_referer('czl_save_exchange_rates', 'czl_exchange_rates_nonce')) {
|
||||
|
||||
// 删除所有现有汇率
|
||||
global $wpdb;
|
||||
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'czl_exchange_rate_%'");
|
||||
|
||||
// 保存新的汇率
|
||||
if (!empty($_POST['rates'])) {
|
||||
foreach ($_POST['rates'] as $data) {
|
||||
if (empty($data['currency']) || !isset($data['rate'])) continue;
|
||||
|
||||
$currency = sanitize_text_field($data['currency']);
|
||||
$rate = (float)$data['rate'];
|
||||
|
||||
if ($rate > 0) {
|
||||
update_option('czl_exchange_rate_' . $currency, $rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_settings_error('czl_messages', 'czl_message', __('汇率设置已保存', 'woo-czl-express'), 'updated');
|
||||
}
|
||||
|
||||
// 显示设置页面
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/exchange-rates.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册自定义订单状态
|
||||
*/
|
||||
public function register_custom_order_statuses() {
|
||||
register_post_status('wc-shipping', array(
|
||||
'label' => __('运输中', 'woo-czl-express'),
|
||||
'public' => true,
|
||||
'show_in_admin_status_list' => true,
|
||||
'show_in_admin_all_list' => true,
|
||||
'exclude_from_search' => false,
|
||||
'label_count' => _n_noop('运输中 <span class="count">(%s)</span>',
|
||||
'运输中 <span class="count">(%s)</span>', 'woo-czl-express')
|
||||
));
|
||||
|
||||
register_post_status('wc-delivered', array(
|
||||
'label' => __('已送达', 'woo-czl-express'),
|
||||
'public' => true,
|
||||
'show_in_admin_status_list' => true,
|
||||
'show_in_admin_all_list' => true,
|
||||
'exclude_from_search' => false,
|
||||
'label_count' => _n_noop('已送达 <span class="count">(%s)</span>',
|
||||
'已送达 <span class="count">(%s)</span>', 'woo-czl-express')
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义订单状态到WooCommerce状态列表
|
||||
*/
|
||||
public function add_custom_order_statuses($order_statuses) {
|
||||
$new_statuses = array(
|
||||
'wc-shipping' => __('运输中', 'woo-czl-express'),
|
||||
'wc-delivered' => __('已送达', 'woo-czl-express')
|
||||
);
|
||||
|
||||
return array_merge($order_statuses, $new_statuses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在订单详情页显示轨迹信息
|
||||
*/
|
||||
public function display_tracking_info($order) {
|
||||
$tracking_number = $order->get_meta('_czl_tracking_number');
|
||||
if (empty($tracking_number)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tracking_history = $order->get_meta('_czl_tracking_history');
|
||||
if (empty($tracking_history)) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<h2><?php _e('物流轨迹', 'woo-czl-express'); ?></h2>
|
||||
<div class="czl-tracking-info">
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
__('运单号: %s', 'woo-czl-express'),
|
||||
'<a href="https://exp.czl.net/track/?query=' . esc_attr($tracking_number) . '" target="_blank">' .
|
||||
esc_html($tracking_number) . '</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<ul class="czl-tracking-history">
|
||||
<?php foreach ($tracking_history['trackDetails'] as $track): ?>
|
||||
<li>
|
||||
<span class="tracking-date"><?php echo esc_html($track['track_date']); ?></span>
|
||||
<span class="tracking-content"><?php echo esc_html($track['track_content']); ?></span>
|
||||
<?php if (!empty($track['track_location'])): ?>
|
||||
<span class="tracking-location"><?php echo esc_html($track['track_location']); ?></span>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时更新所有运输中订单的轨迹
|
||||
*/
|
||||
public function schedule_tracking_updates() {
|
||||
$orders = wc_get_orders(array(
|
||||
'status' => array('shipping'),
|
||||
'limit' => -1,
|
||||
'meta_key' => '_czl_tracking_number',
|
||||
'meta_compare' => 'EXISTS'
|
||||
));
|
||||
|
||||
foreach ($orders as $order) {
|
||||
$this->order_handler->update_tracking_info($order->get_id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在管理员订单页面显示轨迹信息
|
||||
*/
|
||||
public function display_admin_tracking_info($order) {
|
||||
$tracking_number = $order->get_meta('_czl_tracking_number');
|
||||
if (empty($tracking_number)) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="czl-admin-tracking-info">
|
||||
<h3><?php _e('CZL Express 运单信息', 'woo-czl-express'); ?></h3>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
__('运单号: %s', 'woo-czl-express'),
|
||||
'<a href="https://exp.czl.net/track/?query=' . esc_attr($tracking_number) . '" target="_blank">' .
|
||||
esc_html($tracking_number) . '</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function handle_create_shipment() {
|
||||
try {
|
||||
// 验证nonce
|
||||
check_ajax_referer('czl_create_shipment', 'nonce');
|
||||
|
||||
// 检查权限
|
||||
if (!current_user_can('manage_woocommerce')) {
|
||||
throw new Exception(__('权限不足', 'woo-czl-express'));
|
||||
}
|
||||
|
||||
// 验证订单ID
|
||||
$order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
|
||||
if (!$order_id) {
|
||||
throw new Exception(__('无效的订单ID', 'woo-czl-express'));
|
||||
}
|
||||
|
||||
// 获取WooCommerce订单对象
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) {
|
||||
throw new Exception('订单不存在');
|
||||
}
|
||||
|
||||
// 创建运单
|
||||
$czl_order = new CZL_Order();
|
||||
$result = $czl_order->create_shipment($order_id);
|
||||
|
||||
if (!empty($result['tracking_number'])) {
|
||||
wp_send_json_success(array(
|
||||
'message' => __('运单创建成功', 'woo-czl-express'),
|
||||
'tracking_number' => $result['tracking_number']
|
||||
));
|
||||
} else {
|
||||
throw new Exception('运单创建失败');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: ' . $e->getMessage());
|
||||
if (isset($order)) {
|
||||
$order->add_order_note(
|
||||
sprintf(
|
||||
__('CZL Express运单创建失败: %s', 'woo-czl-express'),
|
||||
$e->getMessage()
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
wp_send_json_error(array(
|
||||
'message' => $e->getMessage() // 直接返回错误信息
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_bulk_create_shipment() {
|
||||
check_ajax_referer('czl_bulk_create_shipment', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_woocommerce')) {
|
||||
wp_send_json_error(array('message' => __('权限不足', 'woo-czl-express')));
|
||||
}
|
||||
|
||||
$order_ids = isset($_POST['order_ids']) ? array_map('intval', $_POST['order_ids']) : array();
|
||||
if (empty($order_ids)) {
|
||||
wp_send_json_error(array('message' => __('请选择订单', 'woo-czl-express')));
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$failed = 0;
|
||||
$failed_orders = array();
|
||||
$czl_order = new CZL_Order();
|
||||
|
||||
foreach ($order_ids as $order_id) {
|
||||
try {
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) {
|
||||
throw new Exception('订单不存在');
|
||||
}
|
||||
|
||||
$result = $czl_order->create_shipment($order_id);
|
||||
|
||||
if (!empty($result)) {
|
||||
// 更新WooCommerce订单元数据
|
||||
$order->update_meta_data('_czl_tracking_number', $result['tracking_number']);
|
||||
$order->update_meta_data('_czl_order_id', $result['order_id']);
|
||||
|
||||
// 保存子单号
|
||||
if (!empty($result['childList'])) {
|
||||
$child_numbers = array_map(function($child) {
|
||||
return $child['child_number'];
|
||||
}, $result['childList']);
|
||||
$order->update_meta_data('_czl_child_numbers', $child_numbers);
|
||||
}
|
||||
|
||||
// 添加订单备注
|
||||
$order->add_order_note(
|
||||
sprintf(
|
||||
__('CZL Express运单创建成功。运单号: %s', 'woo-czl-express'),
|
||||
$result['tracking_number']
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
// 更新订单状态为运输中
|
||||
$order->update_status('shipping', __('运单已创建,包裹开始运输', 'woo-czl-express'));
|
||||
|
||||
// 保存更改
|
||||
$order->save();
|
||||
|
||||
$success++;
|
||||
} else {
|
||||
throw new Exception('运单创建失败');
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('CZL Express Error: Failed to create shipment for order ' . $order_id . ' - ' . $e->getMessage());
|
||||
if (isset($order)) {
|
||||
$order->add_order_note(
|
||||
sprintf(
|
||||
__('CZL Express运单创建失败: %s', 'woo-czl-express'),
|
||||
$e->getMessage()
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
$failed++;
|
||||
$failed_orders[] = $order_id;
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => sprintf(
|
||||
__('处理完成。成功:%d,失败:%d', 'woo-czl-express'),
|
||||
$success,
|
||||
$failed
|
||||
),
|
||||
'failed_orders' => $failed_orders
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染订单管理页面
|
||||
*/
|
||||
public function render_orders_page() {
|
||||
// 检查权限
|
||||
if (!current_user_can('manage_woocommerce')) {
|
||||
wp_die(__('您没有足够的权限访问此页面', 'woo-czl-express'));
|
||||
}
|
||||
|
||||
// 加载订单列表页面模板
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/orders.php';
|
||||
}
|
||||
}
|
103
readme.md
Normal file
103
readme.md
Normal file
@ -0,0 +1,103 @@
|
||||
# woocommerce-czlexpress
|
||||
|
||||
## 描述
|
||||
|
||||
1. 配置信息写入到mysql
|
||||
2. order_id, tracking_number, label_url, 写入到mysql
|
||||
3. 适配PHP7和PHP8
|
||||
4. 功能嵌入到woocommerce内, 通过woocommerce的钩子函数, 实现功能
|
||||
5. 查价运费是人民币, 支持按汇率转换为其他货币,主要是USD
|
||||
|
||||
## 系统要求
|
||||
|
||||
1. WordPress 5.0+
|
||||
2. WooCommerce 6.0.0+
|
||||
3. PHP 7.0+
|
||||
4. MySQL 5.6+
|
||||
|
||||
## 特性
|
||||
|
||||
1. 完全支持WooCommerce高性能订单存储(HPOS)
|
||||
2. 支持WooCommerce远程日志记录
|
||||
3. 支持多语言
|
||||
|
||||
## 功能
|
||||
1. 支持快捷查价, 然后映射产品和woocommerce的运输方式. 其中, 查价结果里, 有一些产品, 比如 "UPS 红单-TA价", "UPS 红单-DA55价", 这种以"UPS 红单"开头的产品. 还有比如: "FEDEX IE-DT价", "FEDEX IE-DE价", 这种以"FEDEX IE"开头的产品. 我希望在查价结果里, 把他们显示为一个类别, 比如"UPS 红单", 然后这一行只显示最便宜的一个"UPS 红单"开头的产品价格和时效. 然后这个类别可以点击展开, 下面一个列表显示所有以"UPS 红单"开头的具体产品和相关信息. 并且可以自定义类别名称和产品开头, 例如这个配置:
|
||||
productGroupConfig: {
|
||||
'UPS 蓝单': {
|
||||
prefixes: ['UPS 蓝单'],
|
||||
groupName: 'UPS 蓝单'
|
||||
},
|
||||
'UPS 红单': {
|
||||
prefixes: ['UPS 红单'],
|
||||
groupName: 'UPS 红单'
|
||||
},
|
||||
"江苏FEDEX IE": {
|
||||
prefixes: ["江苏FEDEX IE"],
|
||||
groupName: "江苏FEDEX IE"
|
||||
},
|
||||
"江苏FEDEX IP": {
|
||||
prefixes: ["江苏FEDEX IP"],
|
||||
groupName: "江苏FEDEX IP"
|
||||
},
|
||||
'FEDEX IE': {
|
||||
prefixes: ['FEDEX IE'],
|
||||
groupName: 'FEDEX IE'
|
||||
},
|
||||
'FEDEX IP': {
|
||||
prefixes: ['FEDEX IP'],
|
||||
groupName: 'FEDEX IP'
|
||||
},
|
||||
"DHL": {
|
||||
prefixes: ["DHL"],
|
||||
groupName: "DHL"
|
||||
},
|
||||
"欧美普货包税专线": {
|
||||
prefixes: ["欧美经济专线(普货)","欧美标准专线(普货)","欧洲经济专线(普货)","欧洲标准专线(普货)"],
|
||||
groupName: "欧美普货包税专线"
|
||||
},
|
||||
"欧美B类包税专线": {
|
||||
prefixes: ["欧美经济专线(B类)","欧美标准专线(B类)","欧洲经济专线(B类)","欧洲标准专线(B类)"],
|
||||
groupName: "欧美B类包税专线"
|
||||
},
|
||||
"欧美带电包税专线": {
|
||||
prefixes: ["欧美经济专线(带电)","欧美标准专线(带电)","欧洲经济专线(带电)","欧洲标准专线(带电)"],
|
||||
groupName: "欧美带电包税专线"
|
||||
},
|
||||
"迪拜DHL": {
|
||||
prefixes: ["迪拜DHL"],
|
||||
groupName: "迪拜DHL"
|
||||
},
|
||||
"迪拜UPS": {
|
||||
prefixes: ["迪拜UPS"],
|
||||
groupName: "迪拜UPS"
|
||||
},
|
||||
"迪拜FEDEX": {
|
||||
prefixes: ["迪拜FEDEX"],
|
||||
groupName: "迪拜FEDEX"
|
||||
},
|
||||
"邮政": {
|
||||
prefixes: ["E特快","EMS"],
|
||||
groupName: "邮政"
|
||||
},
|
||||
"CZL阿联酋专线": {
|
||||
prefixes: ["CZL阿联酋"],
|
||||
groupName: "CZL阿联酋专线"
|
||||
},
|
||||
}
|
||||
|
||||
用户可以选择按产品分组映射woocommerce的运输方式. 也可以选择具体产品映射woocommerce的运输方式.
|
||||
|
||||
2. 设置运输价格时, 可以设置在CZLExpress的运费上额外加上一定比例和固定金额, 支持表达式, 例如"10% + 10", 那么就是CZLExpress的运费乘以1.1, 然后加上10元.
|
||||
|
||||
3. 支持设置账号密码, 然后在woocommerce订单列表, 支持快捷下单到CZLExpress, 如果下单无误, 支持打印标签,如果下单有问题, 显示报错内容, 可以手动修改, 然后打印标签.
|
||||
|
||||
4. 在woocommerce的订单详情页, 显示CZLExpress的订单跟踪号, 并且可以点击跟踪号, 跳转到CZLExpress的订单跟踪页面.
|
||||
|
||||
5. 在woocommerce的订单详情页, 显示CZLExpress的订单标签, 并且可以点击标签, 跳转到CZLExpress的订单标签下载页面.
|
||||
|
||||
6. 可以把订单运输轨迹, 显示在woocommerce的订单详情页.
|
||||
|
||||
7. 当用户在woocommerce下单时, 自动查询偏远和偏远费用, 然后加到运输费用里. 并且也单独显示偏远和偏远费用.
|
||||
|
||||
|
56
templates/shipping-method-group.php
Normal file
56
templates/shipping-method-group.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* 运费方式分组显示模板
|
||||
*/
|
||||
defined('ABSPATH') || exit;
|
||||
?>
|
||||
|
||||
<tr class="shipping-method-group">
|
||||
<td>
|
||||
<input type="radio"
|
||||
name="shipping_method[<?php echo $index; ?>]"
|
||||
data-index="<?php echo $index; ?>"
|
||||
value="<?php echo esc_attr($rate->id); ?>"
|
||||
class="shipping_method"
|
||||
<?php checked($rate->id, $chosen_method); ?> />
|
||||
</td>
|
||||
<td>
|
||||
<label for="shipping_method_<?php echo $index; ?>">
|
||||
<?php echo wp_kses_post($rate->label); ?>
|
||||
<?php if (!empty($rate->has_sub_methods)): ?>
|
||||
<span class="toggle-sub-methods dashicons dashicons-arrow-down-alt2"></span>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
|
||||
<?php if ($rate->remote_fee > 0): ?>
|
||||
<div class="remote-fee-notice">
|
||||
<?php printf(__('Remote Area Fee: %s', 'woo-czl-express'),
|
||||
wc_price($rate->remote_fee)); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($rate->has_sub_methods)): ?>
|
||||
<div class="sub-methods" style="display: none;">
|
||||
<table class="sub-methods-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php _e('Method', 'woo-czl-express'); ?></th>
|
||||
<th><?php _e('Delivery Time', 'woo-czl-express'); ?></th>
|
||||
<th><?php _e('Cost', 'woo-czl-express'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($rate->sub_methods as $sub_method): ?>
|
||||
<tr>
|
||||
<td><?php echo esc_html($sub_method['method_title']); ?></td>
|
||||
<td><?php echo esc_html($sub_method['delivery_time']); ?></td>
|
||||
<td><?php echo wc_price($sub_method['cost']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo wc_price($rate->cost); ?></td>
|
||||
</tr>
|
65
woocommerce-czlexpress.php
Normal file
65
woocommerce-czlexpress.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: WooCommerce CZL Express Shipping
|
||||
* Plugin URI: https://your-domain.com/
|
||||
* Description: CZL Express shipping integration for WooCommerce
|
||||
* Version: 1.0.0
|
||||
* Author: Your Name
|
||||
* Author URI: https://your-domain.com/
|
||||
* Text Domain: woo-czl-express
|
||||
* Domain Path: /languages
|
||||
* Requires PHP: 7.0
|
||||
* WC requires at least: 6.0.0
|
||||
* WC tested up to: 8.0.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// 定义插件常量
|
||||
define('WOO_CZL_EXPRESS_VERSION', '1.0.0');
|
||||
define('WOO_CZL_EXPRESS_PATH', plugin_dir_path(__FILE__));
|
||||
define('WOO_CZL_EXPRESS_URL', plugin_dir_url(__FILE__));
|
||||
|
||||
// 声明支持HPOS
|
||||
add_action('before_woocommerce_init', function() {
|
||||
if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) {
|
||||
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查环境
|
||||
function wc_czlexpress_check_environment() {
|
||||
if (!class_exists('WooCommerce')) {
|
||||
add_action('admin_notices', function() {
|
||||
echo '<div class="error"><p>' .
|
||||
__('CZL Express requires WooCommerce to be installed and active', 'woocommerce-czlexpress') .
|
||||
'</p></div>';
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version_compare(WC_VERSION, '6.0.0', '<')) {
|
||||
add_action('admin_notices', function() {
|
||||
echo '<div class="error"><p>' .
|
||||
__('CZL Express requires WooCommerce 6.0.0 or higher', 'woocommerce-czlexpress') .
|
||||
'</p></div>';
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初始化插件
|
||||
function woo_czl_express_init() {
|
||||
if (wc_czlexpress_check_environment()) {
|
||||
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-woo-czl-express.php';
|
||||
WooCzlExpress::instance();
|
||||
}
|
||||
}
|
||||
|
||||
add_action('plugins_loaded', 'woo_czl_express_init');
|
||||
|
||||
add_action('woocommerce_order_status_processing', array('CZL_Order', 'create_shipment'));
|
Loading…
x
Reference in New Issue
Block a user