commit 6fa8720de18dfae4b8dcd8446dc4e34104312297 Author: wood chen Date: Thu Dec 19 20:38:24 2024 +0800 first commit diff --git a/admin/views/admin-page.php b/admin/views/admin-page.php new file mode 100644 index 0000000..b4bbb4c --- /dev/null +++ b/admin/views/admin-page.php @@ -0,0 +1,31 @@ + + +
+

+ + + +
+ +
+
\ No newline at end of file diff --git a/admin/views/api-settings.php b/admin/views/api-settings.php new file mode 100644 index 0000000..c90575a --- /dev/null +++ b/admin/views/api-settings.php @@ -0,0 +1,132 @@ + __('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'), +); + +?> + +
+

+ +
+ +
+ +
+

+ + +
+
+
+ + \ No newline at end of file diff --git a/admin/views/api-test.php b/admin/views/api-test.php new file mode 100644 index 0000000..12c7eec --- /dev/null +++ b/admin/views/api-test.php @@ -0,0 +1,69 @@ + + +
+

+ +

+ +

+ +
+ + + +
+ +
+
+ + \ No newline at end of file diff --git a/admin/views/exchange-rates.php b/admin/views/exchange-rates.php new file mode 100644 index 0000000..c0aa0d9 --- /dev/null +++ b/admin/views/exchange-rates.php @@ -0,0 +1,203 @@ + $name) { + if ($code !== 'CNY') { + $rate = get_option('czl_exchange_rate_' . $code, ''); + if ($rate !== '') { + $exchange_rates[$code] = $rate; + } + } +} +?> + +
+

+ + + +
+

+ + ' . $current_currency . ''); ?> +

+
+ +
+ + +
+ + + + + + + + + + + $rate): ?> + + + + + + + + + + + + + +
+ + + + + +
+ +
+
+ + +
+
+ + + + + + \ No newline at end of file diff --git a/admin/views/orders.php b/admin/views/orders.php new file mode 100644 index 0000000..7a80ab0 --- /dev/null +++ b/admin/views/orders.php @@ -0,0 +1,186 @@ + -1, + 'orderby' => 'date', + 'order' => 'DESC', + 'status' => array('processing', 'completed', 'shipping', 'delivered') +)); + +$orders = $orders_query->get_orders(); +?> + +
+

+ +
+
+ + +
+
+ + + + + + + + + + + + + + + + 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; + ?> + + + + + + + + + + + + + +
+ +
+ + + + #get_order_number()); ?> + + get_date_created()->date_i18n('Y-m-d H:i:s')); ?>get_status())); ?> + get_formatted_shipping_full_name()); ?>
+ get_shipping_address_1()); ?> +
+ + + + +
+ +
+ +
+ + + + + + + + +
+ + + +
+
+ + \ No newline at end of file diff --git a/admin/views/product-groups.php b/admin/views/product-groups.php new file mode 100644 index 0000000..1644244 --- /dev/null +++ b/admin/views/product-groups.php @@ -0,0 +1,195 @@ + + +
+

+ + + +
+

+
+ +
+ + +
+ + + + + + + + + + + + $group): ?> + + + + + + + + + + + + + + + + + + +
+ > + + + + +

+
+ +
+ +
+
+ + +
+
+ + + + + + \ No newline at end of file diff --git a/admin/views/settings.php b/admin/views/settings.php new file mode 100644 index 0000000..32ffeab --- /dev/null +++ b/admin/views/settings.php @@ -0,0 +1,16 @@ + + +
+

+ +
+ +
+
+ \ No newline at end of file diff --git a/api.md b/api.md new file mode 100644 index 0000000..34a75e7 --- /dev/null +++ b/api.md @@ -0,0 +1,172 @@ +# API文档 + +## 国家接口 +GET https://tms-api-go.czl.net/api/countries + +返回: +{"code":200,"msg":"success","data":[{"code":"AD","name":"安道尔","ename":"ANDORRA"},{"code":"AE","name":"阿拉伯联合酋长国","ename":"United Arab Emirates"},{"code":"AF","name":"阿富汗","ename":"Afghanistan"},{"code":"AG","name":"安提瓜岛","ename":"ANTIGUA AND BARBUDA"},{"code":"AI","name":"安圭拉","ename":"Anguilla"},{"code":"AL","name":"阿尔巴尼亚","ename":"Albania"},{"code":"AM","name":"亚美尼亚","ename":"Armenia"},{"code":"AN","name":"荷属安的列斯群岛","ename":"NETHERLANDS ANTILLES"},{"code":"AO","name":"安哥拉","ename":"Angola"},{"code":"AR","name":"阿根廷","ename":"Argentina"}...]} + +## 查询偏远数据 + +GET https://tms-api-go.czl.net/api/remote/query + +返回: +{"dhl":{"is_remote":true,"remote_type":"","matched_city":"","matched_post":"082001"},"fedex":{"is_remote":true,"remote_type":"TIERB","matched_city":"","matched_post":"082001"},"tnt":{"is_remote":false,"remote_type":"","matched_city":"","matched_post":""},"ups":{"is_remote":false,"remote_type":"","matched_city":"","matched_post":""}} + +## 偏远附加费等附加费数据 + +GET https://i-cf.czl.net/g-f/exp.czl.net/json/surcharges.json + +返回: +[{"name":"FEDEX A级偏远费最低收费","baseCharge":23,"courierType":"fedex","unit":"元/票","displayCategory":"偏远费","note":"按票计费"},{"name":"FEDEX B级偏远费单价","baseCharge":3.5,"courierType":"fedex","unit":"元/KG","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"FEDEX B级偏远费最低收费","baseCharge":168,"courierType":"fedex","unit":"元/票","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"FEDEX C级偏远费单价","baseCharge":4.6,"courierType":"fedex","unit":"元/KG","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"FEDEX C级偏远费最低收费","baseCharge":218,"courierType":"fedex","unit":"元/票","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"UPS偏远费单价","baseCharge":3.5,"courierType":"ups","unit":"元/KG","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"UPS偏远费最低收费","baseCharge":168,"courierType":"ups","unit":"元/票","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"DHL偏远费单价","baseCharge":4,"courierType":"dhl","unit":"元/KG","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"DHL偏远费最低收费","baseCharge":172,"courierType":"dhl","unit":"元/票","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"顺丰偏远费单价","baseCharge":3.5,"courierType":"0","unit":"元/KG","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"顺丰偏远费最低收费","baseCharge":50,"courierType":"0","unit":"元/票","displayCategory":"偏远费","note":"单价*计费重 与 最低收费, 取大计费"},{"name":"FEDEX基础附加费","baseCharge":195,"courierType":"fedex","unit":"元/件","displayCategory":"FEDEX附加费","note":"异形,最长边超1.2m,次边超76cm,单件超31KG,围长超266cm, 满足任意一条收取;并且将最低按18KG计费"},{"name":"FEDEX超大货件费","baseCharge":516,"courierType":"fedex","unit":"元/件","displayCategory":"FEDEX附加费","note":"最长边超243cm,周长超330cm,满足任意一条收取(最低18KG计费)"},{"name":"附加服务费 - 重货","baseCharge":1384,"courierType":"fedex","unit":"元/件","displayCategory":"FEDEX附加费","note":"此附加费适用于最长单边超过157厘米的单件重货处理(IEF和IPF)."},{"name":"更改地址","baseCharge":83,"courierType":"fedex","unit":"元/票","displayCategory":"FEDEX附加费","note":"如果收件人地址不完整(缺少门牌号等)或不正确,我们可能会尝试找到正确的地址并完成递送,并且我们将收取附加费。"},{"name":"住宅交付附加费","baseCharge":24,"courierType":"fedex","unit":"元/票","displayCategory":"FEDEX附加费","note":"(仅适用于运往美国和加拿大的货件)派送至住宅附加费适用于寄往家庭或私人住宅(包括在家中经营企业的地址)的货物,或寄件人将送货地址指定为住宅的任何货物。"},{"name":"指定清关代理人(最低)","baseCharge":96,"courierType":"fedex","unit":"元/票","displayCategory":"FEDEX附加费","note":"(两者取大)当发生以下所有情况时,将收取此附加费:代理(FedEx Express 或指定的FedEx Express以外的代理)负责清关货物。FedEx Express 在客户的代理地点取件并将其交付给最终收件人。收件人所在位置的清关点与代理的清关点不同。"},{"name":"指定清关代理人","baseCharge":10.7,"courierType":"fedex","unit":"元/KG","displayCategory":"FEDEX附加费","note":"(两者取大)当发生以下所有情况时,将收取此附加费:代理(FedEx Express 或指定的FedEx Express以外的代理)负责清关货物。FedEx Express 在客户的代理地点取件并将其交付给最终收件人。收件人所在位置的清关点与代理的清关点不同。"},{"name":"非堆叠货件附加费","baseCharge":1750,"courierType":"fedex","unit":"元/件","displayCategory":"FEDEX附加费","note":"国际优先快递重货服务(IPF)和国际经济快递重货服务(IEF)每件重货处理价为1,750 元人民币。此附加费适用于任何不可堆叠的重货货件处理,这意味着单件、滑橇或托盘不能以安全可靠的方式垂直堆叠。例如,包括但不限于以下不可堆叠的重货货件处理:1) 没有平坦且稳定的顶部或底座,2) 如果在此类重货上加上另一件包裹,则可能会损坏,或 3) 表面具有平坦且稳定的顶部或底座,但太窄而无法安全可靠地支撑其他单件重货货件(例如 1-2 个滚筒或鼓状物)。"},{"name":"UPS附加服务费","baseCharge":108,"courierType":"ups","unit":"元/件","displayCategory":"UPS附加费","note":"异形,最长边超1.2m,次边超76cm,平均单件超25KG,围长超300cm, 满足任意一条收取"},{"name":"UPS大型包裹附加费","baseCharge":514,"courierType":"ups","unit":"元/件","displayCategory":"UPS附加费","note":"当一个包裹的围长超过300cm, 但不超过UPS的最大尺寸400cm时,该包裹将被视为“大型包裹”。大型包裹的最小计费重量为40公斤"},{"name":"超重超长费","baseCharge":1938,"courierType":"ups","unit":"元/件","displayCategory":"UPS附加费","note":"单件超70KG,最长边超274cm,围长超400cm, 满足任意一条一般不接收. 如果接收了,收取此费用."},{"name":"更改地址","baseCharge":73,"courierType":"ups","unit":"元/件","displayCategory":"UPS附加费","note":"每更改收件人地址一次,如因地址不正确或因地址为邮政信箱无法递送时,此费用由寄件人支付。"},{"name":"更改地址(最高)","baseCharge":280,"courierType":"ups","unit":"元/票","displayCategory":"UPS附加费","note":"(按件计费,单票最高此费用)每更改收件人地址一次,如因地址不正确或因地址为邮政信箱无法递送时,此费用由寄件人支付。"},{"name":"住宅地址递送附加费","baseCharge":24,"courierType":"ups","unit":"元/票","displayCategory":"UPS附加费","note":"UPS 提供将货件递送至住宅地址和商务办公地址的服务。住宅地址包括私人住所及非公共办公场所,向此类地址递送货件每票需加收此费用。"},{"name":"海关查验费","baseCharge":150,"courierType":"ups","unit":"元/票","displayCategory":"UPS附加费","note":"当海关或相关部门需要查验进口或出口包裹货物时,每票货件加收查验费。此项收费用于承担 UPS 配合完成查验手续所产生的相关成本。"},{"name":"第三方清关代理费","baseCharge":127,"courierType":"ups","unit":"元/票","displayCategory":"UPS附加费","note":"收件人可以选择自己的清关代理进行 UPS 国际货件的海关清关。UPS 会将从托运人处收到的海关清关信息和文件转发给收件人或其指定的第三方清关代理。"},{"name":"寄件人支付目的地进口关税附加费","baseCharge":150,"courierType":"ups","unit":"元/票","displayCategory":"UPS附加费","note":"当选择“寄件人支付目的地进口关税 /税款”付款方式时,将向寄件人收取此附加费。"},{"name":"出口申报附加费","baseCharge":97,"courierType":"ups","unit":"元/票","displayCategory":"UPS附加费","note":"满足一条加收: (1)货件含所适用的当地法规所规定的策略性、受管控或监管的商品。(2)没达到强制正式报关要求(100KG或5000人民币)时,要求正式报关"},{"name":"违禁物品费","baseCharge":1038,"courierType":"ups","unit":"元/件","displayCategory":"UPS附加费","note":"UPS 将对每票交由 UPS 的违反或不符合适用法律法规,或不符合 UPS 服务或契约式服务条款和条件的货件(包括但不限于违禁货件或 UPS 无一例外不接受或不承运的货件,例如:危险货物和危险化学品)收取费用每件包裹 RMB1,038。此费用由发件人承担,且除了因违反或不遵守规定而对 UPS可能产生的所有其他费用之外,发件人还需承担相应的责任。"},{"name":"DHL超长费","baseCharge":155,"courierType":"dhl","unit":"元/件","displayCategory":"DHL附加费","note":"最长边超过100cm"},{"name":"DHL超重费","baseCharge":750,"courierType":"dhl","unit":"元/件","displayCategory":"DHL附加费","note":"单件实重或体积重超过70KG"},{"name":"DHL高风险地区附加费","baseCharge":230,"courierType":"dhl","unit":"元/票","displayCategory":"DHL附加费","note":"DHL在某些国家/地区运营具有高风险。 使用DHL发送或接收来自深受战争、内乱、恐怖主义困扰的高风险国家/地区的快件时所征收的附加费。"},{"name":"DHL限运目的地附加费","baseCharge":230,"courierType":"dhl","unit":"元/票","displayCategory":"DHL附加费","note":"快递服务需要遵从相关贸易限制 使用DHL发送或接收来自联合国安理会确定的贸易限运国家/地区的快件时所征收的附加费。"},{"name":"更正地址附加费","baseCharge":87,"courierType":"dhl","unit":"元/票","displayCategory":"DHL附加费","note":"由于运单上所填写的收件人地址信息不完整、过期或错误导致DHL无法完成派送时,DHL获取到正确地址以完成派送收取该项附加费。"},{"name":"无法搬运附加费","baseCharge":155,"courierType":"dhl","unit":"元/件","displayCategory":"DHL附加费","note":"对于每票快件中每个单件包裹,实际重量在25公斤至70公斤之间收取的附加费用。不适用于已经收取超重或超长附加费的货件。"},{"name":"无法堆叠托盘附加费","baseCharge":2340,"courierType":"dhl","unit":"元/件","displayCategory":"DHL附加费","note":"当您的托盘货物无法堆叠时 此附加费适用于由发件人提出要求或因形状、物品或包装材料导致无法堆叠的每个单件托盘货物。"}] + +## 查价接口 + +POST https://tms.czl.net/defaultPriceSearchJson.htm?weight=重量&country=国家二字代码(例如US)&cargoType=P(P包裹,D文件)&length=10(长度)&width=10(宽度)&height=10(高度)&postcode=100000(邮编) + + +返回: +[{"address_type":"","api_priceid":"","channelCode":"","channelName":"","channel_id":"","channel_note":"","charge_weight":"12.0","customer_code":"","customer_id":"","customer_shortname":"","customer_type":"","express_type":"顺丰","freight_amount":"","freight_priceId":"","incidental_priceId":"","is_api":"","oil_amount":"","old_freight_amount":"","old_freight_amount_currency":"","old_oil_amount":"","old_oil_amount_currency":"","otherFeeDetails":[],"other_amount":"","price_formula":"","price_groupname":"","price_inside_note":"","price_note":"","price_type":"","product_aging":"5-8个工作日","product_cargoattribute":"","product_id":"10601","product_name":"顺丰特惠-T价","product_note":"**周一到周六 13:00截单**\r\n\r\n**查看详细操作规定:**[https://l.czl.net/251](https://l.czl.net/251)\r\n\r\n**不接亚马逊件**\r\n\r\n顺丰保险费,申报金额的千分之八,最低收费20元,面签服务50元一票\r\n\r\n1. 转运了客户原因退回有上海顺丰同城双程退运费2/KG+20元手续费,国外退回有国际进口运费和进口关税,一律发件人支付\r\n2. 美国流向偏远地区费用3.7元一KG,最低收费50/票。马来西亚偏远费1.9元/kg\r\n3. 美国货物必须贴MADE IN CHINA,因原产地查验的货物,延误或者被退回(退回运费自付),我司概不负责\r\n4. 理赔标准;外包装完好货物损坏不作任何赔偿,丢失件需在24小时内反馈,超时反馈无法判断,丢失赔偿标准按照申报价值但最高赔偿金额不超过100美金加退运费华沙条款,价值较高请自行购买保险\r\n5. 价格未包含目的地清关税费、偏远地区附加费、更改地址等可能产生的其他费用,如因收件人原因导致拒付的一切费用,均需发件人承担!","product_tags":"","product_tracking":"","product_volumnrate":"5000","remote_type":"是","serveCode":"","serveName":"","serve_id":"","total_amount":"883","unitPriceAmount":"73.58","url":""},{"address_type":"","api_priceid":"","channelCode":"","channelName":"","channel_id":"","channel_note":"","charge_weight":"12.0","customer_code":"","customer_id":"","customer_shortname":"","customer_type":"","express_type":"UPS","freight_amount":"","freight_priceId":"","incidental_priceId":"","is_api":"","oil_amount":"","old_freight_amount":"","old_freight_amount_currency":"","old_oil_amount":"","old_oil_amount_currency":"","otherFeeDetails":[],"other_amount":"","price_formula":"","price_groupname":"","price_inside_note":"","price_note":"","price_type":"","product_aging":"3-6个工作日","product_cargoattribute":"","product_id":"11461","product_name":"UPS 红单-TA价","product_note":"**周一至周六 13:00截单;次日提取;免异形,免一般超长超重,需收方邮箱**\r\n\r\n**UPS附加费在我司官网查看**\r\n\r\nUPS袋子包裹计材积\r\n\r\n\r\n**附加费**\r\n\r\n2. 有单报关费100,买单报关费150元。\r\n3. 上网了客户原因退回的收取2/KG,最低20元一票。禁止拨打800客服,问题请发送我司客服处理\r\n4. 接磁检货,费用:1元/KG,单件最低30元,手续费30元/票\r\n5. 接FBA货物,如需关税预付DDP,手续费120元/票","product_tags":"","product_tracking":"","product_volumnrate":"5000","remote_type":"","serveCode":"","serveName":"","serve_id":"","total_amount":"918","unitPriceAmount":"76.50","url":""},{"address_type":"","api_priceid":"","channelCode":"","channelName":"","channel_id":"","channel_note":"","charge_weight":"12.000","customer_code":"","customer_id":"","customer_shortname":"","customer_type":"","express_type":"ZX","freight_amount":"","freight_priceId":"","incidental_priceId":"","is_api":"","oil_amount":"","old_freight_amount":"","old_freight_amount_currency":"","old_oil_amount":"","old_oil_amount_currency":"","otherFeeDetails":[],"other_amount":"","price_formula":"","price_groupname":"","price_inside_note":"","price_note":"","price_type":"","product_aging":"7-9个工作日","product_cargoattribute":"","product_id":"11641","product_name":"欧美标准专线(普货)-D","product_note":"**周一至周六 13:00截单, 双清包税,时效略慢**\r\n\r\n一、 派送到爱尔兰、罗马尼亚两个目的地国后可能发生二次清关,由此产生的清关费、杂费和税费等费用由收件人承担。\r\n\r\n二、 欧美UPS派送,支持一票多件及一票一件(美国只接收一票一件),一票多件时单件计费重最低11KG;\r\n\r\n**限制条件** \r\n1. 单件实重<=20KG,计费重<=30KG,单票<=3件;\r\n2. 当单箱实重 W≥25kg时,按照200元/件收取;\r\n3. 长+宽x2+高x2<295cm;最长边<100cm,次长边<70cm;(超过无服务)\r\n\r\n三、 附加费:\r\n - 磁检费\r\n - 异形包装费:麻袋包装、木箱包装、铁箱包装、圆柱形包装、不规则包装,按照80元/件收取。\r\n - 代操作费:需仓库代操作换箱,按照20元/件收取材料和操作费。\r\n - 偏远附加费:5.5元/kg,最低270元/票;偏远费保留3个月追溯期。\r\n - 部分偏远邮编会收取偏远附加费RMB55/件;部分超偏远邮编会收取偏远附加费RMB70/件;部分夏威夷邮编收取偏远费RMB95/件;外岛、夏威夷、阿拉斯加、军事基地等不可发货;","product_tags":"","product_tracking":"","product_volumnrate":"5000","remote_type":"","serveCode":"","serveName":"","serve_id":"","total_amount":"936","unitPriceAmount":"78.00","url":""},......] + +## 查轨迹接口 + +POST https://tms.czl.net/selectTrack.htm?documentCode=(Order No/tracking NO) + +返回: + +[{ +"ack": "true", +"data": [ + { + "consigneeCountry": "country code country code", + "productKindName": "Logistics express type", +"referenceNumber": "reference number ref number", +"trackContent": "last track content", +"trackDate": "the last track date", +"trackLocation": "Shanghai the last track location", +"trackSignperson": "sign person, if no sign it's empty", +"trackingNumber": "tracking number tracking number", +"trackDetails": [ + { +"track_content":"track content", +"track_date":"track date", +"track_location":"Shanghai track location", +"track_signdate":"sign date,if no sign it's empty", + "track_signperson":"sign person,if no sign it's empty" + } +] +} +] +}] + + + +## 查轨迹链接(自动跳转) + +GET https://exp.czl.net/track/?query=(Order No/tracking NO) + + +## Get tracking number + +This method can query the tracking number of an order. It is usually used after the system tracking number is modified or updated + +POST https://tms.czl.net/getOrderTrackingNumber.htm?documentCode=orderNo + +POST https://tms.czl.net/getOrderTrackingNumber.htm?order_id=order_id + +## authentication + +This is the identity authentication interface, which is used to obtain "customer ID" and "customer userid", which will be used in order creation + +POST https://tms.czl.net/selectAuth.htm?username=TEST&password=123456 + +## create order(UTF-8) +This method can be create order . You can push the order data to CZL Express system to obtain the order tracking number and order ID for printing. + +POST https://tms.czl.net/createOrderApi.htm + +Request : +Parameter Name | Data Type | REQUIRED | Description +Param | string | Require | Json param + +### Param: +{ + "buyerid": "", + "consignee_address": "Receipt address street, required", + "order_piece": "Number of pieces, small package default 1, express delivery must be filled in", + "consignee_city" : "City", + "consignee_mobile": "Mobile number, optional. To facilitate delivery, it is best to fill in", + "order_returnsign": "Return sign, default N means no return, Y sign means return. China Post can ignore this attribute" , + "consignee_name": "Recipient, required", + "trade_type": "ZYXT", + "consignee_postcode": "Zip code, country with post code is required", + "consignee_state": "State / Province", + "consignee_telephone" : "Receiving phone number, required", + "country": "Receipt country code, required", + "customer_id": "Customer ID, required", + "customer_userid": "Login ID, required" , + "orderInvoiceParam": [ + { + "invoice_amount": "Declared total value, required", + "invoice_pcs": "Number of pieces, required", + "invoice_title": "English name, required", + "invoice_weight": "Single Heavy ", + "item_id ":"", + "item_transactionid ":"", + "sku ":" Chinese product name", + "sku_code ":"distribution information", + "hs_code ":"customs code" + } + ], + "order_customerinvoicecode" : "Original order number, required", + "product_id": "Transport mode ID, required ", + "weight": "Total weight, optional, if there is a single weight on sku, do not fill in this item ", + "product_imagepath": "Picture address, multiple picture addresses are separated by semicolons Open ", + "shipper_name": "Sender's name, optional ", + "shipper_companyname": "Sender's company name, optional ", + "shipper_address1": "Sender's address 1, optional ", +"shipper_address2": "Sender address 2, optional", + "shipper_address3": "Sender address 3, optional", + "shipper_city": "Sender city, optional", + "shipper_state": "Send Shipper State, optional ", + "shipper_postcode":"Sender Postcode, optional ", + "shipper_country":"Sender Country, optional ", + "shipper_telephone":" Sender's Phone, optional ", + "order_transactionurl": "Product sales address", + "consignee_email": "Mailbox, optional", + "consignee_companyname": "Recipient company name, if best, fill in", + "order_codamount": "Account Collection", + "order_codcurrency": "Currency, Standard Three-character Code", + "order_cargoamount": "Actual Order Amount, Used for Customs Declaration", + "order_insurance": "Insurance Amount", + "consignee_taxno":"tax number ", + "consignee_doorno":"door number " +} + +### Response: + +Parameter Name | Data Type | REQUIRED | Description +ack | string | Require | true/false +message | string | Require | The description for the messagePlease use URL to decode +order_transfercode | string | +reference_number | string | +tracking_number | string | Tracking number +order_id | string | Require | Unique identification of orderQuery parameters of order and face order + +Json: + +{ + "ack":"true", + "message":"%C7%EB%C7%F3%B3%C9%B9%A6", + "order_transfercode": "", + "reference_number":"", + "tracking_number":"tracking number", + "order_id":"xxxxxxx" +} + +## getLabel + +GET https://tms-label.czl.net/order/FastRpt/PDF_NEW.aspx?Format=lbl_sub一票多件161810499441.frx&PrintType=1&order_id=1024353 + +会跳转到类似这种文件: https://tms-label.czl.net/order/FastRpt/temp/12/19/b551a8f620d64ab8a0d24867fcc4e1a4.pdf + diff --git a/assets/css/shipping-method-groups.css b/assets/css/shipping-method-groups.css new file mode 100644 index 0000000..9ac549f --- /dev/null +++ b/assets/css/shipping-method-groups.css @@ -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; +} \ No newline at end of file diff --git a/assets/css/shipping-method.css b/assets/css/shipping-method.css new file mode 100644 index 0000000..6ce8887 --- /dev/null +++ b/assets/css/shipping-method.css @@ -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; +} \ No newline at end of file diff --git a/assets/js/shipping-method-groups.js b/assets/js/shipping-method-groups.js new file mode 100644 index 0000000..f3bda83 --- /dev/null +++ b/assets/js/shipping-method-groups.js @@ -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'); + }); +}); \ No newline at end of file diff --git a/includes/class-czl-ajax.php b/includes/class-czl-ajax.php new file mode 100644 index 0000000..de2fc78 --- /dev/null +++ b/includes/class-czl-ajax.php @@ -0,0 +1,41 @@ + __('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); + } + } +} \ No newline at end of file diff --git a/includes/class-czl-api-test.php b/includes/class-czl-api-test.php new file mode 100644 index 0000000..c1261d4 --- /dev/null +++ b/includes/class-czl-api-test.php @@ -0,0 +1,67 @@ +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() + ); + } + } +} \ No newline at end of file diff --git a/includes/class-czl-api.php b/includes/class-czl-api.php new file mode 100644 index 0000000..d5c7f8a --- /dev/null +++ b/includes/class-czl-api.php @@ -0,0 +1,505 @@ +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; + } + } +} \ No newline at end of file diff --git a/includes/class-czl-install.php b/includes/class-czl-install.php new file mode 100644 index 0000000..372fcd0 --- /dev/null +++ b/includes/class-czl-install.php @@ -0,0 +1,52 @@ +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', ''); + } +} \ No newline at end of file diff --git a/includes/class-czl-label.php b/includes/class-czl-label.php new file mode 100644 index 0000000..3df302f --- /dev/null +++ b/includes/class-czl-label.php @@ -0,0 +1,53 @@ +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; + } +} \ No newline at end of file diff --git a/includes/class-czl-order-data.php b/includes/class-czl-order-data.php new file mode 100644 index 0000000..5b08558 --- /dev/null +++ b/includes/class-czl-order-data.php @@ -0,0 +1,87 @@ +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 ''; + } +} \ No newline at end of file diff --git a/includes/class-czl-order-handler.php b/includes/class-czl-order-handler.php new file mode 100644 index 0000000..fe83291 --- /dev/null +++ b/includes/class-czl-order-handler.php @@ -0,0 +1,121 @@ +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()); + } + } +} \ No newline at end of file diff --git a/includes/class-czl-order.php b/includes/class-czl-order.php new file mode 100644 index 0000000..3f7c2a2 --- /dev/null +++ b/includes/class-czl-order.php @@ -0,0 +1,201 @@ +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()); + } + } + } +} \ No newline at end of file diff --git a/includes/class-czl-product-fields.php b/includes/class-czl-product-fields.php new file mode 100644 index 0000000..2b4e1ca --- /dev/null +++ b/includes/class-czl-product-fields.php @@ -0,0 +1,41 @@ +'; + + 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 ''; + } + + /** + * 保存自定义字段 + */ + 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); + } +} \ No newline at end of file diff --git a/includes/class-czl-rate-calculator.php b/includes/class-czl-rate-calculator.php new file mode 100644 index 0000000..03e76fa --- /dev/null +++ b/includes/class-czl-rate-calculator.php @@ -0,0 +1,378 @@ +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; + } +} \ No newline at end of file diff --git a/includes/class-czl-settings.php b/includes/class-czl-settings.php new file mode 100644 index 0000000..8977585 --- /dev/null +++ b/includes/class-czl-settings.php @@ -0,0 +1,55 @@ + 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', + ), + ) + ); + } +} \ No newline at end of file diff --git a/includes/class-czl-shipping-method.php b/includes/class-czl-shipping-method.php new file mode 100644 index 0000000..f531da7 --- /dev/null +++ b/includes/class-czl-shipping-method.php @@ -0,0 +1,161 @@ +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); + } + } +} \ No newline at end of file diff --git a/includes/class-czl-tracking.php b/includes/class-czl-tracking.php new file mode 100644 index 0000000..b371100 --- /dev/null +++ b/includes/class-czl-tracking.php @@ -0,0 +1,150 @@ +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 '

' . __('物流跟踪信息', 'woo-czl-express') . '

'; + echo '
'; + echo '

' . __('运单号:', 'woo-czl-express') . '' . esc_html($tracking_number) . '

'; + + if (!empty($tracking_info['trackDetails'])) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + foreach ($tracking_info['trackDetails'] as $detail) { + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo '
' . __('时间', 'woo-czl-express') . '' . __('地点', 'woo-czl-express') . '' . __('状态', 'woo-czl-express') . '
' . esc_html($detail['track_date']) . '' . esc_html($detail['track_location']) . '' . esc_html($detail['track_content']) . '
'; + } + + echo '
'; + } + } 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 '

' . __('物流跟踪:', 'woo-czl-express') . ''; + echo '' . esc_html($tracking_number) . '

'; + } + } + + /** + * 在管理员订单页面显示轨迹信息 + */ + public static function display_admin_tracking_info($order) { + $tracking_number = $order->get_meta('_czl_tracking_number'); + if (empty($tracking_number)) { + return; + } + + ?> +
+

+

+ ' . + esc_html($tracking_number) . '' + ); + ?> +

+ + get_meta('_czl_child_numbers'); + if (!empty($child_numbers)) { + echo '

' . __('子单号:', 'woo-czl-express') . ' ' . + implode(', ', array_map('esc_html', $child_numbers)) . '

'; + } + + // 显示参考号 + $reference_number = $order->get_meta('_czl_reference_number'); + if (!empty($reference_number)) { + echo '

' . __('参考号:', 'woo-czl-express') . ' ' . + esc_html($reference_number) . '

'; + } + + // 显示偏远信息 + $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 '

' . __('地区类型:', 'woo-czl-express') . ' ' . + esc_html($remote_text) . '

'; + } + } + + // 显示住宅信息 + $is_residential = $order->get_meta('_czl_is_residential'); + if ($is_residential === 'Y') { + echo '

' . __('地址类型:', 'woo-czl-express') . ' ' . + __('住宅地址', 'woo-czl-express') . '

'; + } + + // 显示轨迹信息 + $tracking_history = $order->get_meta('_czl_tracking_history'); + if (!empty($tracking_history['trackDetails'])) { + ?> +
+

+ +

+ + +

+
+ +
+ 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(); + } + } + } +} \ No newline at end of file diff --git a/includes/class-wc-czlexpress-order.php b/includes/class-wc-czlexpress-order.php new file mode 100644 index 0000000..de91735 --- /dev/null +++ b/includes/class-wc-czlexpress-order.php @@ -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; + } +} \ No newline at end of file diff --git a/includes/class-wc-czlexpress-orders-list.php b/includes/class-wc-czlexpress-orders-list.php new file mode 100644 index 0000000..8b80b53 --- /dev/null +++ b/includes/class-wc-czlexpress-orders-list.php @@ -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(); + } +} \ No newline at end of file diff --git a/includes/class-wc-czlexpress-shipping.php b/includes/class-wc-czlexpress-shipping.php new file mode 100644 index 0000000..b0f72b1 --- /dev/null +++ b/includes/class-wc-czlexpress-shipping.php @@ -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; + } +} \ No newline at end of file diff --git a/includes/class-woo-czl-express.php b/includes/class-woo-czl-express.php new file mode 100644 index 0000000..b848f50 --- /dev/null +++ b/includes/class-woo-czl-express.php @@ -0,0 +1,631 @@ +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 ''; + } + + public function password_field_callback() { + $value = get_option('czl_password'); + echo ''; + } + + public function rate_adjustment_field_callback() { + $value = get_option('czl_rate_adjustment'); + ?> + +

+
+ - 10% + 10 (运费乘以1.1后加10)
+ - 20% (运费乘以1.2)
+ - +15 (运费加15)
+ +

+ __('请先配置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('运输中 (%s)', + '运输中 (%s)', '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('已送达 (%s)', + '已送达 (%s)', '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; + } + + ?> +

+
+

+ ' . + esc_html($tracking_number) . '' + ); + ?> +

+ +
+ 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; + } + + ?> +
+

+

+ ' . + esc_html($tracking_number) . '' + ); + ?> +

+
+ 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'; + } +} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..c2570c2 --- /dev/null +++ b/readme.md @@ -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下单时, 自动查询偏远和偏远费用, 然后加到运输费用里. 并且也单独显示偏远和偏远费用. + + diff --git a/templates/shipping-method-group.php b/templates/shipping-method-group.php new file mode 100644 index 0000000..5135f19 --- /dev/null +++ b/templates/shipping-method-group.php @@ -0,0 +1,56 @@ + + + + + id, $chosen_method); ?> /> + + + + + remote_fee > 0): ?> +
+ remote_fee)); ?> +
+ + + has_sub_methods)): ?> + + + + cost); ?> + \ No newline at end of file diff --git a/woocommerce-czlexpress.php b/woocommerce-czlexpress.php new file mode 100644 index 0000000..6fbedd5 --- /dev/null +++ b/woocommerce-czlexpress.php @@ -0,0 +1,65 @@ +

' . + __('CZL Express requires WooCommerce to be installed and active', 'woocommerce-czlexpress') . + '

'; + }); + return false; + } + + if (version_compare(WC_VERSION, '6.0.0', '<')) { + add_action('admin_notices', function() { + echo '

' . + __('CZL Express requires WooCommerce 6.0.0 or higher', 'woocommerce-czlexpress') . + '

'; + }); + 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')); \ No newline at end of file