首页 / 教程文章 / WordPress小批量定制插件支持动态BOM管理的集成教程

WordPress小批量定制插件支持动态BOM管理的集成教程

WordPress小批量定制插件:动态BOM管理集成教程

概述:什么是动态BOM管理?

在制造业和产品开发领域,BOM(Bill of Materials,物料清单)是产品所需的所有原材料、组件和子组件的结构化列表。动态BOM管理指的是能够根据产品配置、版本变化或生产需求实时调整和更新物料清单的能力。

对于使用WordPress搭建产品展示网站或小型电商平台的企业,集成动态BOM管理功能可以帮助他们更好地管理产品组件、成本计算和生产流程。本教程将指导您开发一个WordPress插件,实现小批量定制产品的动态BOM管理功能。

插件结构与数据库设计

首先,我们需要设计插件的基本结构和数据库表。我们的插件将包含以下核心表:

  1. 产品表:存储基本产品信息
  2. BOM主表:存储BOM的基本信息
  3. BOM明细表:存储BOM的具体物料项
  4. 物料库表:存储可用的物料信息
<?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;
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/6383.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部