首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >WooCommerce 改询价单:结账字段、通知收件人和后台按客户查看

WooCommerce 改询价单:结账字段、通知收件人和后台按客户查看

原创
作者头像
用户10345225
发布于 2026-10-30 12:11:21
发布于 2026-10-30 12:11:21
1290
举报

很多 B2B 站把询价做成一个联系表单:姓名、邮箱、留言。客户填「需要 500 个」,销售回信的第一句永远是「请问您要的是哪个型号」。型号、数量、用途全靠邮件来回对齐,一封询价三天才落到报价上。

WooCommerce 的购物车本来就在干这件事——产品关联、数量、变体、后台列表、导出都是现成的,缺的只是付款那一步。下面把它拆成一张询价单。

先划一条边界

询价单本质是一次没有支付的订单。用订单表存它,产品、数量、客户信息、后台搜索和导出全都白拿。代价是它和真实订单混在同一张表里,所以每张询价单都必须打标记,否则财务对账时会来找你。

如果一天几十条询价、要分派给多个销售、要记录每次回访,那就不是 WooCommerce 该干的事了,那是 CRM 的活。这条线先划好,后面的代码才有意义。需要把询价单接到 ERP、或者做成按参数出配置器的,属于定制开发的范畴,边界可以看 AumCreate 的服务页。

所有改动放一个 mu-plugin

新建 wp-content/mu-plugins/inquiry-cart.php,目录不存在就建一个。mu-plugin 不能在后台被误停用,适合放这种改流程的代码。

把「加入购物车」换成「加入询价单」

代码语言:php
复制
add_filter( 'woocommerce_product_single_add_to_cart_text', function ( $text, $product ) {
	return '加入询价单';
}, 10, 2 );

add_filter( 'woocommerce_product_add_to_cart_text', function ( $text, $product ) {
	return '加入询价单';
}, 10, 2 );

购物车、结算按钮上的词也得跟着换,客户看到「购物车里有 3 件」会以为马上要付款:

代码语言:php
复制
add_filter( 'gettext', function ( $translated, $original, $domain ) {
	if ( 'woocommerce' !== $domain ) {
		return $translated;
	}

	static $map = null;
	if ( null === $map ) {
		$map = array(
			'Add to cart'         => '加入询价单',
			'View cart'           => '查看询价单',
			'Cart'                => '询价单',
			'Proceed to checkout' => '填写询价信息',
		);
	}

	return isset( $map[ $original ] ) ? $map[ $original ] : $translated;
}, 10, 3 );

gettext 每次字符串翻译都会跑一遍,短词(比如 Cart)容易误伤到别的句子。站点翻译量大就别长期挂着,改成语言包覆盖。

让结账跳过支付

代码语言:php
复制
add_filter( 'woocommerce_cart_needs_payment', function ( $needs_payment, $cart ) {
	return false;
}, 10, 2 );

add_filter( 'woocommerce_order_button_text', function () {
	return '提交询价单';
} );

这里有个坑:needs_payment 返回 false 之后,WooCommerce 仍然会调 payment_complete(),虚拟产品会直接变成「已完成」。下单后要把状态拉回来。

代码语言:php
复制
add_action( 'woocommerce_checkout_order_processed', function ( $order_id, $posted_data, $order ) {
	if ( 'yes' !== $order->get_meta( '_is_inquiry' ) ) {
		return;
	}
	if ( 'on-hold' !== $order->get_status() ) {
		$order->update_status( 'on-hold', '询价单,等待报价。' );
	}
}, 10, 3 );

客户还会收到一封「您的订单已完成」,顺手关掉:

代码语言:php
复制
add_filter( 'woocommerce_email_enabled_customer_completed_order', function ( $enabled, $order ) {
	if ( $order instanceof WC_Order && 'yes' === $order->get_meta( '_is_inquiry' ) ) {
		return false;
	}
	return $enabled;
}, 10, 2 );

结账字段:删掉收货地址,留下能报价的信息

B2B 询价不需要邮编、州省、第二行地址。用 woocommerce_checkout_fields 一次性改:

代码语言:php
复制
add_filter( 'woocommerce_checkout_fields', function ( $fields ) {
	unset(
		$fields['billing']['billing_last_name'],
		$fields['billing']['billing_address_1'],
		$fields['billing']['billing_address_2'],
		$fields['billing']['billing_city'],
		$fields['billing']['billing_state'],
		$fields['billing']['billing_postcode'],
		$fields['billing']['billing_country'],
		$fields['order']['order_comments'],
		$fields['shipping']
	);

	$fields['billing']['billing_first_name']['label'] = '称呼';
	$fields['billing']['billing_email']['label']      = '工作邮箱';
	$fields['billing']['billing_phone']['required']   = true;
	$fields['billing']['billing_company']['required'] = true;

	$fields['billing']['inquiry_annual_qty'] = array(
		'label'    => '年用量',
		'required' => false,
		'type'     => 'select',
		'options'  => array(
			''       => '请选择',
			'lt1k'   => '1000 以内',
			'1k-10k' => '1000 – 10000',
			'gt10k'  => '10000 以上',
			'tbd'    => '还没定',
		),
		'class'    => array( 'form-row-first' ),
		'priority' => 60,
	);

	$fields['billing']['inquiry_target_price'] = array(
		'label'    => '目标单价(可选)',
		'required' => false,
		'class'    => array( 'form-row-last' ),
		'priority' => 61,
	);

	$fields['order']['inquiry_note'] = array(
		'label'    => '补充说明:用途、认证要求、期望交期',
		'required' => false,
		'type'     => 'textarea',
		'class'    => array( 'form-row-wide' ),
		'priority' => 70,
	);

	return $fields;
} );

自定义字段要自己存。WooCommerce 只会自动把 billing_*、shipping_* 前缀的字段写进地址簿,inquiry_* 不会。同时在这里打上询价标记:

代码语言:php
复制
add_action( 'woocommerce_checkout_create_order', function ( $order, $data ) {
	$order->update_meta_data( '_is_inquiry', 'yes' );

	foreach ( array( 'inquiry_annual_qty', 'inquiry_target_price', 'inquiry_note' ) as $key ) {
		if ( ! empty( $data[ $key ] ) ) {
			$order->update_meta_data( '_' . $key, sanitize_text_field( $data[ $key ] ) );
		}
	}
}, 10, 2 );

带下划线前缀的 meta 在后台默认折叠在「自定义字段」里,销售看不到,所以要在订单详情页显式打出来:

代码语言:php
复制
add_action( 'woocommerce_admin_order_data_after_billing_address', function ( $order ) {
	if ( 'yes' !== $order->get_meta( '_is_inquiry' ) ) {
		return;
	}

	$rows = array(
		'年用量'   => $order->get_meta( '_inquiry_annual_qty' ),
		'目标单价' => $order->get_meta( '_inquiry_target_price' ),
		'补充说明' => $order->get_meta( '_inquiry_note' ),
	);

	echo '<div class="inquiry-meta"><h3>询价信息</h3><p>';
	foreach ( $rows as $label => $value ) {
		if ( ! $value ) {
			continue;
		}
		printf( '<strong>%s:</strong>%s<br>', esc_html( $label ), esc_html( $value ) );
	}
	echo '</p></div>';
} );

通知邮件发给谁

woocommerce_email_recipient_new_order 直接改收件人,按产品分类分流:

代码语言:php
复制
add_filter( 'woocommerce_email_recipient_new_order', function ( $recipient, $order ) {
	if ( ! $order instanceof WC_Order || 'yes' !== $order->get_meta( '_is_inquiry' ) ) {
		return $recipient;
	}

	$recipient = 'sales@example.com';

	foreach ( $order->get_items() as $item ) {
		if ( has_term( 'mosfet', 'product_cat', $item->get_product_id() ) ) {
			$recipient = 'fae@example.com';
			break;
		}
	}

	return $recipient;
}, 10, 2 );

收件人可以是多个,逗号分隔。但别一上来就抄送老板和销售群,先发给一个明确的人,由他转。抄送一多,没人觉得是自己的单。

客户那封确认邮件:订单落在 on-hold,客户会收到「您的订单已挂起」。要么在 WooCommerce → 设置 → 邮件里把 On-hold 那封关掉,只留站内的感谢页;要么改掉它的标题:

代码语言:php
复制
add_filter( 'woocommerce_email_subject_customer_on_hold_order', function ( $subject ) {
	return '我们收到你的询价单了';
} );

add_filter( 'woocommerce_email_heading_customer_on_hold_order', function ( $heading ) {
	return '询价单已收到';
} );

感谢页的文案也要换成承诺:

代码语言:php
复制
add_filter( 'woocommerce_thankyou_order_received_text', function ( $text, $order ) {
	if ( $order instanceof WC_Order && 'yes' === $order->get_meta( '_is_inquiry' ) ) {
		return '询价单已收到。我们核对型号和数量后,会在 1 个工作日内回一封带报价的邮件。';
	}
	return $text;
}, 10, 2 );

后台按客户看询价

单条询价看订单详情就够了。麻烦的是同一家公司问过几次、问到哪一步了——在订单列表里翻很费劲,加一个汇总页:

代码语言:php
复制
add_action( 'admin_menu', function () {
	add_submenu_page(
		'woocommerce',
		'询价汇总',
		'询价汇总',
		'manage_woocommerce',
		'inquiry-summary',
		'inquiry_summary_page'
	);
} );

function inquiry_summary_page() {
	if ( ! current_user_can( 'manage_woocommerce' ) ) {
		return;
	}

	$orders = wc_get_orders( array(
		'limit'   => 300,
		'status'  => array( 'wc-on-hold' ),
		'orderby' => 'date',
		'order'   => 'DESC',
	) );

	$rows = array();

	foreach ( $orders as $order ) {
		if ( 'yes' !== $order->get_meta( '_is_inquiry' ) ) {
			continue;
		}

		$company = $order->get_billing_company();
		if ( ! $company ) {
			$company = trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() );
		}
		$key = strtolower( $company );

		if ( ! isset( $rows[ $key ] ) ) {
			$rows[ $key ] = array(
				'company' => $company,
				'count'   => 0,
				'last'    => $order->get_date_created() ? $order->get_date_created()->date_i18n( 'Y-m-d H:i' ) : '',
				'order'   => $order,
			);
		}

		$rows[ $key ]['count']++;
	}

	echo '<div class="wrap"><h1>询价汇总</h1>';
	echo '<p>只统计状态为「待处理」且带询价标记的订单,最多 300 条。</p>';
	echo '<table class="widefat striped"><thead><tr><th>客户</th><th>询价次数</th><th>最近一次</th><th>操作</th></tr></thead><tbody>';

	foreach ( $rows as $row ) {
		printf(
			'<tr><td>%s</td><td>%d</td><td>%s</td><td><a href="%s">查看最近一单</a></td></tr>',
			esc_html( $row['company'] ),
			(int) $row['count'],
			esc_html( $row['last'] ),
			esc_url( $row['order']->get_edit_order_url() )
		);
	}

	echo '</tbody></table></div>';
}

wc_get_orders() 在传统订单存储和 HPOS 下都能用,所以这里不写 SQL。公司名大小写、后缀不一致会把同一家拆成两行(「ABC Ltd」和「abc」),量大了要么在结账处给输入提示,要么改成从客户账户带出公司名,别指望代码自动合并。

上线前自己走一遍

  • 加两个不同分类的产品,看按钮文案、结账页有没有残留支付方式
  • 提交后确认:状态是 on-hold、_is_inquiry 写进去了、四个自定义字段在订单详情页都有显示
  • 销售邮箱收到的是一封 New order,里面带着型号、数量、年用量
  • 客户邮箱收到的标题是「我们收到你的询价单了」,不是「订单已完成」
  • 后台「询价汇总」用同一家公司下两单,看是不是一行两单
  • 手机上过一遍结账页,两个 form-row-first / form-row-last 的字段有没有挤成两行错位

最后一条最容易漏:把测试单删掉再上线,别让第一条真询价排在「测试 test」后面。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 先划一条边界
  • 所有改动放一个 mu-plugin
  • 把「加入购物车」换成「加入询价单」
  • 让结账跳过支付
  • 结账字段:删掉收货地址,留下能报价的信息
  • 通知邮件发给谁
  • 后台按客户看询价
  • 上线前自己走一遍
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档