文章目录[隐藏]
WordPress小批量定制插件:动态BOM管理集成教程
概述:什么是动态BOM管理?
在制造业和产品开发领域,BOM(Bill of Materials,物料清单)是产品所需的所有原材料、组件和子组件的结构化列表。动态BOM管理指的是能够根据产品配置、版本变化或生产需求实时调整和更新物料清单的能力。
对于使用WordPress搭建产品展示网站或小型电商平台的企业,集成动态BOM管理功能可以帮助他们更好地管理产品组件、成本计算和生产流程。本教程将指导您开发一个WordPress插件,实现小批量定制产品的动态BOM管理功能。
插件结构与数据库设计
首先,我们需要设计插件的基本结构和数据库表。我们的插件将包含以下核心表:
- 产品表:存储基本产品信息
- BOM主表:存储BOM的基本信息
- BOM明细表:存储BOM的具体物料项
- 物料库表:存储可用的物料信息
<?php
/**
* WordPress动态BOM管理插件 - 数据库安装脚本
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class DynamicBOM_Installer {
/**
* 创建插件所需的数据库表
*/
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// BOM产品表
$table_products = $wpdb->prefix . 'bom_products';
$sql_products = "CREATE TABLE IF NOT EXISTS $table_products (
id INT(11) NOT NULL AUTO_INCREMENT,
product_name VARCHAR(255) NOT NULL,
product_description TEXT,
base_price DECIMAL(10,2) DEFAULT 0.00,
status ENUM('active', 'inactive') DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
// BOM主表
$table_boms = $wpdb->prefix . 'bom_master';
$sql_boms = "CREATE TABLE IF NOT EXISTS $table_boms (
id INT(11) NOT NULL AUTO_INCREMENT,
product_id INT(11) NOT NULL,
bom_name VARCHAR(255) NOT NULL,
bom_version VARCHAR(50) DEFAULT '1.0',
is_active TINYINT(1) DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (product_id) REFERENCES $table_products(id) ON DELETE CASCADE
) $charset_collate;";
// 物料库表
$table_materials = $wpdb->prefix . 'bom_materials';
$sql_materials = "CREATE TABLE IF NOT EXISTS $table_materials (
id INT(11) NOT NULL AUTO_INCREMENT,
material_code VARCHAR(100) NOT NULL UNIQUE,
material_name VARCHAR(255) NOT NULL,
material_type ENUM('raw', 'component', 'subassembly') DEFAULT 'raw',
unit_price DECIMAL(10,2) DEFAULT 0.00,
unit_of_measure VARCHAR(50) DEFAULT 'pcs',
stock_quantity INT(11) DEFAULT 0,
min_stock_level INT(11) DEFAULT 10,
supplier_info TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
// BOM明细表
$table_bom_items = $wpdb->prefix . 'bom_items';
$sql_bom_items = "CREATE TABLE IF NOT EXISTS $table_bom_items (
id INT(11) NOT NULL AUTO_INCREMENT,
bom_id INT(11) NOT NULL,
material_id INT(11) NOT NULL,
quantity DECIMAL(10,3) NOT NULL DEFAULT 1.000,
operation_sequence INT(11) DEFAULT 1,
scrap_factor DECIMAL(5,3) DEFAULT 0.000,
notes TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY (bom_id) REFERENCES $table_boms(id) ON DELETE CASCADE,
FOREIGN KEY (material_id) REFERENCES $table_materials(id) ON DELETE CASCADE
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
// 执行SQL创建表
dbDelta($sql_products);
dbDelta($sql_boms);
dbDelta($sql_materials);
dbDelta($sql_bom_items);
// 添加默认数据(可选)
self::add_default_data();
}
/**
* 添加默认数据
*/
private static function add_default_data() {
global $wpdb;
// 检查是否已有数据
$table_materials = $wpdb->prefix . 'bom_materials';
$count = $wpdb->get_var("SELECT COUNT(*) FROM $table_materials");
if ($count == 0) {
// 添加示例物料
$wpdb->insert($table_materials, array(
'material_code' => 'MAT-001',
'material_name' => '铝合金框架',
'material_type' => 'raw',
'unit_price' => 25.50,
'unit_of_measure' => 'pcs',
'stock_quantity' => 100,
'min_stock_level' => 20
));
$wpdb->insert($table_materials, array(
'material_code' => 'MAT-002',
'material_name' => '电路板',
'material_type' => 'component',
'unit_price' => 15.75,
'unit_of_measure' => 'pcs',
'stock_quantity' => 200,
'min_stock_level' => 30
));
}
}
}
?>
插件主类与初始化
接下来,我们创建插件的主类,负责初始化插件功能、注册短码和管理后台菜单。
<?php
/**
* WordPress动态BOM管理插件主类
*/
class DynamicBOM_Plugin {
private static $instance = null;
/**
* 获取插件实例(单例模式)
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 构造函数
*/
private function __construct() {
// 注册激活和停用钩子
register_activation_hook(__FILE__, array($this, 'activate_plugin'));
register_deactivation_hook(__FILE__, array($this, 'deactivate_plugin'));
// 初始化插件
add_action('plugins_loaded', array($this, 'init_plugin'));
}
/**
* 激活插件时执行
*/
public function activate_plugin() {
// 创建数据库表
DynamicBOM_Installer::create_tables();
// 添加插件版本号
update_option('dynamic_bom_version', '1.0.0');
// 设置默认选项
add_option('bom_currency', 'USD');
add_option('bom_decimal_places', 2);
}
/**
* 停用插件时执行
*/
public function deactivate_plugin() {
// 清理临时数据
// 注意:这里不删除数据库表,以便保留用户数据
}
/**
* 初始化插件
*/
public function init_plugin() {
// 加载文本域(国际化)
load_plugin_textdomain('dynamic-bom', false, dirname(plugin_basename(__FILE__)) . '/languages/');
// 添加管理菜单
add_action('admin_menu', array($this, 'add_admin_menus'));
// 注册短码
add_shortcode('bom_calculator', array($this, 'bom_calculator_shortcode'));
add_shortcode('product_bom', array($this, 'product_bom_shortcode'));
// 注册AJAX处理
add_action('wp_ajax_calculate_bom_cost', array($this, 'ajax_calculate_bom_cost'));
add_action('wp_ajax_nopriv_calculate_bom_cost', array($this, 'ajax_calculate_bom_cost'));
// 加载CSS和JS
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
}
/**
* 添加管理菜单
*/
public function add_admin_menus() {
// 主菜单
add_menu_page(
__('动态BOM管理', 'dynamic-bom'),
__('BOM管理', 'dynamic-bom'),
'manage_options',
'dynamic-bom',
array($this, 'render_main_page'),
'dashicons-clipboard',
30
);
// 子菜单
add_submenu_page(
'dynamic-bom',
__('产品管理', 'dynamic-bom'),
__('产品管理', 'dynamic-bom'),
'manage_options',
'bom-products',
array($this, 'render_products_page')
);
add_submenu_page(
'dynamic-bom',
__('物料库', 'dynamic-bom'),
__('物料库', 'dynamic-bom'),
'manage_options',
'bom-materials',
array($this, 'render_materials_page')
);
add_submenu_page(
'dynamic-bom',
__('BOM配置', 'dynamic-bom'),
__('BOM配置', 'dynamic-bom'),
'manage_options',
'bom-config',
array($this, 'render_config_page')
);
}
/**
* 渲染主页面
*/
public function render_main_page() {
include plugin_dir_path(__FILE__) . 'templates/admin/main.php';
}
/**
* 渲染产品管理页面
*/
public function render_products_page() {
include plugin_dir_path(__FILE__) . 'templates/admin/products.php';
}
/**
* 渲染物料库页面
*/
public function render_materials_page() {
include plugin_dir_path(__FILE__) . 'templates/admin/materials.php';
}
/**
* 渲染配置页面
*/
public function render_config_page() {
include plugin_dir_path(__FILE__) . 'templates/admin/config.php';
}
/**
* 加载前端资源
*/
public function enqueue_frontend_assets() {
wp_enqueue_style(
'dynamic-bom-frontend',
plugin_dir_url(__FILE__) . 'assets/css/frontend.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'dynamic-bom-frontend',
plugin_dir_url(__FILE__) . 'assets/js/frontend.js',
array('jquery'),
'1.0.0',
true
);
// 本地化脚本,传递AJAX URL
wp_localize_script('dynamic-bom-frontend', 'bom_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('bom_calculator_nonce')
));
}
/**
* 加载管理端资源
*/
public function enqueue_admin_assets($hook) {
// 仅在我们的插件页面加载
if (strpos($hook, 'dynamic-bom') !== false || strpos($hook, 'bom-') !== false) {
wp_enqueue_style(
'dynamic-bom-admin',
plugin_dir_url(__FILE__) . 'assets/css/admin.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'dynamic-bom-admin',
plugin_dir_url(__FILE__) . 'assets/js/admin.js',
array('jquery', 'jquery-ui-sortable'),
'1.0.0',
true
);
}
}
/**
* BOM计算器短码
*/
public function bom_calculator_shortcode($atts) {
// 解析短码属性
$atts = shortcode_atts(array(
'product_id' => 0,
'show_total' => 'yes'
), $atts, 'bom_calculator');
ob_start();
include plugin_dir_path(__FILE__) . 'templates/frontend/calculator.php';
return ob_get_clean();
}
/**
* 产品BOM展示短码
*/
public function product_bom_shortcode($atts) {
$atts = shortcode_atts(array(
'product_id' => 0,
'version' => 'latest'
), $atts, 'product_bom');
ob_start();
include plugin_dir_path(__FILE__) . 'templates/frontend/product-bom.php';
return ob_get_clean();
}
/**
* AJAX计算BOM成本
*/
public function ajax_calculate_bom_cost() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'bom_calculator_nonce')) {
wp_die('安全验证失败');
}
$product_id = intval($_POST['product_id']);
$quantities = $_POST['quantities']; // 预期为物料ID=>数量的数组
// 计算总成本
$total_cost = $this->calculate_total_cost($product_id, $quantities);
// 返回JSON响应
wp_send_json_success(array(
'total_cost' => $total_cost,
'formatted_cost' => $this->format_currency($total_cost)
));
}
/**
* 计算总成本
*/
private function calculate_total_cost($product_id, $quantities) {
global $wpdb;
$total = 0;
// 获取产品的基础BOM
$bom_table = $wpdb->prefix . 'bom_master';
$bom_items_table = $wpdb->prefix . 'bom_items';
$materials_table = $wpdb->prefix . 'bom_materials';
$bom_id = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM $bom_table WHERE product_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1",
$product_id
));
if ($bom_id) {
// 获取BOM项
$items = $wpdb->get_results($wpdb->prepare(
"SELECT i.material_id, i.quantity, m.unit_price, m.material_code
FROM $bom_items_table i
JOIN $materials_table m ON i.material_id = m.id
WHERE i.bom_id = %d",
$bom_id
));
foreach ($items as $item) {
$material_id = $item->material_id;
$base_quantity = floatval($item->quantity);
$unit_price = floatval($item->unit_price);
// 如果提供了自定义数量,使用自定义数量,否则使用基础数量
$quantity = isset($quantities[$material_id]) ?
floatval($quantities[$material_id]) : $base_quantity;
$total += $quantity * $unit_price;
}
}
return $total;
}
/**
* 格式化货币显示
*/
private function format_currency($amount) {
$currency = get_option('bom_currency', 'USD');
$decimal_places = intval(get_option('bom_decimal_places', 2));
$symbols = array(
'USD' => '$',
'EUR' => '€',
'GBP' => '£',
'CNY' => '¥'
);
$symbol = isset($symbols[$currency]) ? $symbols[$currency] : $currency . ' ';
return $symbol . number_format($amount, $decimal_places);
}
}
// 初始化插件
DynamicBOM_Plugin::get_instance();
?>
前端BOM计算器实现
现在,让我们创建一个前端BOM计算器模板,允许用户动态调整物料数量并实时计算成本。
<?php
/**
* 前端BOM计算器模板
* 文件路径: templates/frontend/calculator.php
*/
global $wpdb;
$product_id = isset($atts['product_id']) ? intval($atts['product_id']) : 0;
// 获取产品信息
$products_table = $wpdb->prefix . 'bom_products';
$product = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $products_table WHERE id = %d AND status = 'active'",
$product_id
));
if (!$product) {
echo '<p>' . __('未找到产品信息', 'dynamic-bom') . '</p>';
return;
}
// 获取产品的BOM
$bom_table = $wpdb->prefix . 'bom_master';
$bom_items_table = $wpdb->prefix . 'bom_items';
$materials_table = $wpdb->prefix . 'bom_materials';
$bom = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $bom_table WHERE product_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1",
$product_id
));
if (!$bom) {
echo '<p>' . __('该产品暂无BOM配置', 'dynamic-bom') . '</p>';
return;
}
// 获取BOM项
$items = $wpdb->get_results($wpdb->prepare(
"SELECT i.*, m.material_code, m.material_name, m.unit_price, m.unit_of_measure
FROM $bom_items_table i
JOIN $materials_table m ON i.material_id = m.id
WHERE i.bom_id = %d
ORDER BY i.operation_sequence ASC",
$bom->id
));
// 计算基础总成本
$base_total = 0;
foreach ($items as $item) {
$base_total += floatval($item->quantity) * floatval($item->unit_price);
}
?>
<div class="bom-calculator-container" data-product-id="<?php echo esc_attr($product_id); ?>">
<div class="bom-header">
<h3><?php echo esc_html($product->product_name); ?> - <?php _e('BOM成本计算器', 'dynamic-bom'); ?></h3>
<p class="bom-description"><?php echo esc_html($product->product_description); ?></p>
<p class="bom-version"><?php printf(__('BOM版本: %s', 'dynamic-bom'), esc_html($bom->bom_version)); ?></p>
</div>
<div class="bom-items-table">
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th width="5%"><?php _e('序号', 'dynamic-bom'); ?></th>
<th width="15%"><?php _e('物料编码', 'dynamic-bom'); ?></th>
<th width="25%"><?php _e('物料名称', 'dynamic-bom'); ?></th>
<th width="10%"><?php _e('单位', 'dynamic-bom'); ?></th>
<th width="15%"><?php _e('单价', 'dynamic-bom'); ?></th>
<th width="15%"><?php _e('基础数量', 'dynamic-bom'); ?></th>
<th width="15%"><?php _e('调整数量', 'dynamic-bom'); ?></th>
<th width="15%"><?php _e('小计', 'dynamic-bom'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($items as $index => $item): ?>
<?php
$base_qty = floatval($item->quantity);
$unit_price = floatval($item->unit_price);
$base_subtotal = $base_qty * $unit_price;
?>
<tr class="bom-item" data-material-id="<?php echo esc_attr($item->material_id); ?>">
<td><?php echo $index + 1; ?></td>
<td><?php echo esc_html($item->material_code); ?></td>
<td><?php echo esc_html($item->material_name); ?></td>
<td><?php echo esc_html($item->unit_of_measure); ?></td>
<td class="unit-price"><?php echo $this->format_currency($unit_price); ?></td>
<td class="base-quantity"><?php echo number_format($base_qty, 3); ?></td>
<td>
<input type="number"
class="quantity-input"
data-base-quantity="<?php echo esc_attr($base_qty); ?>"
data-unit-price="<?php echo esc_attr($unit_price); ?>"
value="<?php echo number_format($base_qty, 3); ?>"
step="0.001"
min="0"
style="width: 100%;">
</td>
<td class="item-subtotal"><?php echo $this->format_currency($base_subtotal); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<td colspan="7" class="text-right"><strong><?php _e('总成本:', 'dynamic-bom'); ?></strong></td>
<td id="bom-total-cost" class="total-cost"><?php echo $this->format_currency($base_total); ?></td>
</tr>
</tfoot>
</table>
</div>
<div class="bom-controls">
<button type="button" class="button button-primary" id="reset-bom"><?php _e('重置为默认', 'dynamic-bom'); ?></button>
<button type="button" class="button button-secondary" id="save-configuration"><?php _e('保存配置', 'dynamic-bom'); ?></button>
<button type="button" class="button button-secondary" id="export-bom"><?php _e('导出BOM', 'dynamic-bom'); ?></button>
</div>
<div class="bom-notes">
<h4><?php _e('配置说明', 'dynamic-bom'); ?></h4>
<ul>
<li><?php _e('调整"调整数量"列中的数值来自定义物料用量', 'dynamic-bom'); ?></li>
<li><?php _e('总成本将根据您的调整实时更新', 'dynamic-bom'); ?></li>
<li><?php _e('点击"保存配置"可将当前配置保存为新的BOM版本', 'dynamic-bom'); ?></li>
</ul>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 实时计算总成本
function calculateTotal() {
var total = 0;
$('.bom-item').each(function() {
var $row = $(this);
var quantity = parseFloat($row.find('.quantity-input').val()) || 0;
var unitPrice = parseFloat($row.find('.quantity-input').data('unit-price')) || 0;
var subtotal = quantity * unitPrice;
$row.find('.item-subtotal').text(formatCurrency(subtotal));
total += subtotal;
});
$('#bom-total-cost').text(formatCurrency(total));
}
// 格式化货币显示
function formatCurrency(amount) {
// 这里应该使用从服务器传递的货币设置
// 简化版本,实际应从localize_script获取设置
return '$' + amount.toFixed(2);
}
// 绑定输入事件
$('.quantity-input').on('input change', function() {
calculateTotal();
});
// 重置按钮
$('#reset-bom').on('click', function() {
$('.quantity-input').each(function() {
var baseQty = parseFloat($(this).data('base-quantity')) || 0;
$(this).val(baseQty.toFixed(3));
});
calculateTotal();
});
// 保存配置
$('#save-configuration').on('click', function() {
var productId = $('.bom-calculator-container').data('product-id');
var quantities = {};
$('.bom-item').each(function() {
var materialId = $(this).data('material-id');
var quantity = parseFloat($(this).find('.quantity-input').val()) || 0;
quantities[materialId] = quantity;
});
$.ajax({
url: bom_ajax.ajax_url,
type: 'POST',
data: {
action: 'save_bom_configuration',
product_id: productId,
quantities: quantities,
nonce: bom_ajax.nonce
},
success: function(response) {
if (response.success) {
alert('配置已保存为新的BOM版本: ' + response.data.version);
} else {
alert('保存失败: ' + response.data.message);
}
}
});
});
// 导出BOM
$('#export-bom').on('click', function() {
var productId = $('.bom-calculator-container').data('product-id');
var quantities = {};
$('.bom-item').each(function() {
var materialId = $(this).data('material-id');
var quantity = parseFloat($(this).find('.quantity-input').val()) || 0;
quantities[materialId] = quantity;
});
// 创建导出数据
var exportData = {
product_id: productId,
quantities: quantities,
timestamp: new Date().toISOString()
};
// 下载JSON文件
var dataStr = JSON.stringify(exportData, null, 2);
var dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
var exportFileDefaultName = 'bom-config-' + productId + '-' + new Date().getTime() + '.json';
var linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
});
});
</script>
动态BOM版本管理功能
接下来,我们实现BOM版本管理功能,允许用户保存不同的配置作为新的BOM版本。
<?php
/**
* BOM版本管理类
* 文件路径: includes/class-bom-version-manager.php
*/
class BOM_Version_Manager {
/**
* 创建新的BOM版本
*/
public static function create_version($product_id, $bom_name, $items_data) {
global $wpdb;
// 开始事务
$wpdb->query('START TRANSACTION');
try {
// 1. 获取当前版本号并递增
$bom_table = $wpdb->prefix . 'bom_master';
$current_version = $wpdb->get_var($wpdb->prepare(
"SELECT MAX(bom_version) FROM $bom_table WHERE product_id = %d",
$product_id
));
$new_version = self::increment_version($current_version);
// 2. 创建新的BOM主记录
$wpdb->insert($bom_table, array(
'product_id' => $product_id,
'bom_name' => $bom_name,
'bom_version' => $new_version,
'is_active' => 1
));
$new_bom_id = $wpdb->insert_id;
// 3. 复制BOM项
$bom_items_table = $wpdb->prefix . 'bom_items';
foreach ($items_data as $item) {
$wpdb->insert($bom_items_table, array(
'bom_id' => $new_bom_id,
'material_id' => $item['material_id'],
'quantity' => $item['quantity'],
'operation_sequence' => $item['sequence'] ?? 1,
'scrap_factor' => $item['scrap_factor'] ?? 0,
'notes' => $item['notes'] ?? ''
));
}
// 4. 将旧版本标记为非活跃(可选)
$wpdb->update($bom_table,
array('is_active' => 0),
array('product_id' => $product_id, 'id !=' => $new_bom_id)
);
// 提交事务
$wpdb->query('COMMIT');
return array(
'success' => true,
'bom_id' => $new_bom_id,
'version' => $new_version
);
} catch (Exception $e) {
// 回滚事务
$wpdb->query('ROLLBACK');
return array(
'success' => false,
'message' => $e->getMessage()
);
}
}
/**
* 递增版本号
*/
private static function increment_version($current_version) {
if (empty($current_version)) {
return '1.0';
}
// 解析版本号 (支持 x.y 格式)
$parts = explode('.', $current_version);
if (count($parts) == 2) {
// 递增小版本号
$parts[1] = intval($parts[1]) + 1;
return implode('.', $parts);
} else {
// 如果版本号格式不符合预期,返回默认递增
return '1.' . (intval($current_version) + 1);
}
}
/**
* 获取产品的所有BOM版本
*/
public static function get_product_versions($product_id) {
global $wpdb;
$bom_table = $wpdb->prefix . 'bom_master';
return $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $bom_table
WHERE product_id = %d
ORDER BY created_at DESC",
$product_id
));
}
/**
* 比较两个BOM版本的差异
*/
public static function compare_versions($bom_id_1, $bom_id_2) {
global $wpdb;
$bom_items_table = $wpdb->prefix . 'bom_items';
$materials_table = $wpdb->prefix . 'bom_materials';
// 获取第一个版本的物料
$version1_items = $wpdb->get_results($wpdb->prepare(
"SELECT i.*, m.material_code, m.material_name
FROM $bom_items_table i
JOIN $materials_table m ON i.material_id = m.id
WHERE i.bom_id = %d",
$bom_id_1
));
// 获取第二个版本的物料
$version2_items = $wpdb->get_results($wpdb->prepare(
"SELECT i.*, m.material_code, m.material_name
FROM $bom_items_table i
JOIN $materials_table m ON i.material_id = m.id
WHERE i.bom_id = %d",
$bom_id_2
));
// 转换为以物料ID为键的数组
$items1 = array();
foreach ($version1_items as $item) {
$items1[$item->material_id] = $item;
}
$items2 = array();
foreach ($version2_items as $item) {
$items2[$item->material_id] = $item;
}
// 比较差异
$differences = array(
'added' => array(), // 新增的物料
'removed' => array(), // 删除的物料
'changed' => array() // 数量变化的物料
);
// 检查新增和变化的物料
foreach ($items2 as $material_id => $item2) {
if (!isset($items1[$material_id])) {
// 新增的物料
$differences['added'][] = array(
'material_id' => $material_id,
'material_code' => $item2->material_code,
'material_name' => $item2->material_name,
'quantity_v2' => $item2->quantity
);
} else {
$item1 = $items1[$material_id];
if (floatval($item1->quantity) != floatval($item2->quantity)) {
// 数量变化的物料
$differences['changed'][] = array(
'material_id' => $material_id,
'material_code' => $item2->material_code,
'material_name' => $item2->material_name,
'quantity_v1' => $item1->quantity,
'quantity_v2' => $item2->quantity,
'change_percent' => (floatval($item2->quantity) - floatval($item1->quantity)) / floatval($item1->quantity) * 100
);
}
}
}
// 检查删除的物料
foreach ($items1 as $material_id => $item1) {
if (!isset($items2[$material_id])) {
$differences['removed'][] = array(
'material_id' => $material_id,
'material_code' => $item1->material_code,
'material_name' => $item1->material_name,
'quantity_v1' => $item1->quantity
);
}
}
return $differences;
}
}
物料库存预警系统
为了完善BOM管理,我们添加一个物料库存预警系统。
<?php
/**
* 物料库存预警系统
* 文件路径: includes/class-inventory-alert.php
*/
class Inventory_Alert_System {
/**
* 检查库存水平并发送预警
*/
public static function check_inventory_levels() {
global $wpdb;
$materials_table = $wpdb->prefix . 'bom_materials';
$alert_log_table = $wpdb->prefix . 'bom_inventory_alerts';
// 获取库存低于最低水平的物料
$low_stock_materials = $wpdb->get_results(
"SELECT * FROM $materials_table
WHERE stock_quantity <= min_stock_level
AND stock_quantity > 0"
);
// 获取库存为0的物料
$out_of_stock_materials = $wpdb->get_results(
"SELECT * FROM $materials_table
WHERE stock_quantity = 0"
);
$alerts = array();
// 处理低库存预警
foreach ($low_stock_materials as $material) {
$alert_type = 'low_stock';
$message = sprintf(
__('物料 %s (%s) 库存偏低。当前库存: %d,最低库存水平: %d', 'dynamic-bom'),
$material->material_name,
$material->material_code,
$material->stock_quantity,
$material->min_stock_level
);
$alerts[] = self::log_alert($material->id, $alert_type, $message);
// 发送邮件通知
self::send_email_alert($material, $alert_type, $message);
}
// 处理缺货预警
foreach ($out_of_stock_materials as $material) {
$alert_type = 'out_of_stock';
$message = sprintf(
__('物料 %s (%s) 已缺货!', 'dynamic-bom'),
$material->material_name,
$material->material_code
);
$alerts[] = self::log_alert($material->id, $alert_type, $message);
// 发送邮件通知
self::send_email_alert($material, $alert_type, $message);
}
return $alerts;
