文章目录[隐藏]
WordPress小批量定制插件实现物料需求计划(MRP)的教程
概述:为什么在WordPress中实现MRP?
物料需求计划(MRP)是制造业中用于管理生产物料的核心系统。对于中小型企业来说,完整的ERP系统可能过于庞大且昂贵。本教程将展示如何在WordPress中创建一个轻量级MRP插件,特别适合小批量定制生产环境。
通过这个插件,您可以在熟悉的WordPress环境中管理物料清单、库存、生产订单和采购需求,实现基本的MRP功能。
环境准备与插件基础结构
首先,我们需要创建插件的基本结构。在WordPress的wp-content/plugins/目录下创建一个新文件夹mrp-manager。
<?php
/**
* Plugin Name: MRP Manager
* Plugin URI: https://yourwebsite.com/
* Description: 小批量定制生产的物料需求计划管理系统
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('MRP_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('MRP_PLUGIN_URL', plugin_dir_url(__FILE__));
// 初始化插件
class MRP_Manager {
public function __construct() {
$this->init_hooks();
}
private function init_hooks() {
// 激活/停用钩子
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
// 管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 加载脚本和样式
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
}
public function activate() {
$this->create_tables();
$this->insert_initial_data();
}
public function deactivate() {
// 清理临时数据
}
public function add_admin_menu() {
add_menu_page(
'MRP管理系统',
'MRP管理',
'manage_options',
'mrp-manager',
array($this, 'display_dashboard'),
'dashicons-clipboard',
30
);
// 添加子菜单
add_submenu_page(
'mrp-manager',
'物料管理',
'物料管理',
'manage_options',
'mrp-materials',
array($this, 'display_materials_page')
);
add_submenu_page(
'mrp-manager',
'BOM管理',
'BOM管理',
'manage_options',
'mrp-bom',
array($this, 'display_bom_page')
);
}
public function enqueue_admin_assets($hook) {
if (strpos($hook, 'mrp-') !== false) {
wp_enqueue_style('mrp-admin-style', MRP_PLUGIN_URL . 'assets/css/admin.css');
wp_enqueue_script('mrp-admin-script', MRP_PLUGIN_URL . 'assets/js/admin.js', array('jquery'), '1.0', true);
}
}
public function display_dashboard() {
include MRP_PLUGIN_PATH . 'templates/dashboard.php';
}
public function display_materials_page() {
include MRP_PLUGIN_PATH . 'templates/materials.php';
}
public function display_bom_page() {
include MRP_PLUGIN_PATH . 'templates/bom.php';
}
private function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 物料表
$materials_table = $wpdb->prefix . 'mrp_materials';
$sql_materials = "CREATE TABLE IF NOT EXISTS $materials_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
material_code varchar(50) NOT NULL,
material_name varchar(200) NOT NULL,
category varchar(100) DEFAULT '',
unit varchar(20) DEFAULT '',
current_stock decimal(10,2) DEFAULT 0,
safety_stock decimal(10,2) DEFAULT 0,
lead_time int DEFAULT 0,
supplier_info text,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY material_code (material_code)
) $charset_collate;";
// BOM表
$bom_table = $wpdb->prefix . 'mrp_bom';
$sql_bom = "CREATE TABLE IF NOT EXISTS $bom_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
product_code varchar(50) NOT NULL,
product_name varchar(200) NOT NULL,
material_id mediumint(9) NOT NULL,
quantity decimal(10,2) NOT NULL,
waste_rate decimal(5,2) DEFAULT 0,
operation_sequence int DEFAULT 0,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY product_code (product_code),
KEY material_id (material_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql_materials);
dbDelta($sql_bom);
}
private function insert_initial_data() {
// 可以插入一些初始数据
}
}
// 初始化插件
$mrp_manager = new MRP_Manager();
?>
物料管理模块实现
物料管理是MRP的基础。下面我们创建一个物料管理类,用于处理物料的CRUD操作。
<?php
// 文件路径: includes/class-material-manager.php
class Material_Manager {
private $table_name;
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'mrp_materials';
}
/**
* 添加新物料
* @param array $data 物料数据
* @return int|false 插入ID或false
*/
public function add_material($data) {
global $wpdb;
$defaults = array(
'material_code' => '',
'material_name' => '',
'category' => '',
'unit' => '个',
'current_stock' => 0,
'safety_stock' => 0,
'lead_time' => 0,
'supplier_info' => ''
);
$data = wp_parse_args($data, $defaults);
// 验证必要字段
if (empty($data['material_code']) || empty($data['material_name'])) {
return false;
}
$result = $wpdb->insert(
$this->table_name,
$data,
array('%s', '%s', '%s', '%s', '%f', '%f', '%d', '%s')
);
return $result ? $wpdb->insert_id : false;
}
/**
* 更新物料信息
* @param int $id 物料ID
* @param array $data 更新数据
* @return bool 是否成功
*/
public function update_material($id, $data) {
global $wpdb;
$result = $wpdb->update(
$this->table_name,
$data,
array('id' => $id),
array('%s', '%s', '%s', '%s', '%f', '%f', '%d', '%s'),
array('%d')
);
return $result !== false;
}
/**
* 获取所有物料
* @param array $args 查询参数
* @return array 物料列表
*/
public function get_materials($args = array()) {
global $wpdb;
$defaults = array(
'per_page' => 20,
'page' => 1,
'search' => '',
'category' => ''
);
$args = wp_parse_args($args, $defaults);
$offset = ($args['page'] - 1) * $args['per_page'];
$where = 'WHERE 1=1';
$params = array();
if (!empty($args['search'])) {
$where .= " AND (material_code LIKE %s OR material_name LIKE %s)";
$search_term = '%' . $wpdb->esc_like($args['search']) . '%';
$params[] = $search_term;
$params[] = $search_term;
}
if (!empty($args['category'])) {
$where .= " AND category = %s";
$params[] = $args['category'];
}
$query = "SELECT * FROM {$this->table_name} {$where}
ORDER BY material_code LIMIT %d OFFSET %d";
$params[] = $args['per_page'];
$params[] = $offset;
if (!empty($params)) {
$query = $wpdb->prepare($query, $params);
}
return $wpdb->get_results($query);
}
/**
* 根据ID获取物料
* @param int $id 物料ID
* @return object|null 物料对象
*/
public function get_material($id) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT * FROM {$this->table_name} WHERE id = %d",
$id
);
return $wpdb->get_row($query);
}
/**
* 检查库存是否充足
* @param int $material_id 物料ID
* @param float $required_qty 需求数量
* @return array 检查结果
*/
public function check_stock($material_id, $required_qty) {
$material = $this->get_material($material_id);
if (!$material) {
return array(
'success' => false,
'message' => '物料不存在'
);
}
$available = $material->current_stock - $material->safety_stock;
if ($available >= $required_qty) {
return array(
'success' => true,
'available' => $available,
'shortage' => 0
);
} else {
return array(
'success' => false,
'available' => $available,
'shortage' => $required_qty - $available,
'message' => sprintf('库存不足,缺料 %.2f %s',
$required_qty - $available,
$material->unit)
);
}
}
}
?>
BOM(物料清单)管理
BOM是MRP的核心,定义了产品与物料的关系。
<?php
// 文件路径: includes/class-bom-manager.php
class BOM_Manager {
private $table_name;
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'mrp_bom';
}
/**
* 创建或更新BOM
* @param string $product_code 产品代码
* @param string $product_name 产品名称
* @param array $materials 物料数组
* @return bool 是否成功
*/
public function save_bom($product_code, $product_name, $materials) {
global $wpdb;
// 删除旧的BOM记录
$this->delete_bom($product_code);
// 插入新的BOM记录
foreach ($materials as $index => $material) {
$data = array(
'product_code' => $product_code,
'product_name' => $product_name,
'material_id' => $material['id'],
'quantity' => $material['quantity'],
'waste_rate' => isset($material['waste_rate']) ? $material['waste_rate'] : 0,
'operation_sequence' => $index + 1
);
$wpdb->insert(
$this->table_name,
$data,
array('%s', '%s', '%d', '%f', '%f', '%d')
);
}
return true;
}
/**
* 获取产品的BOM
* @param string $product_code 产品代码
* @return array BOM列表
*/
public function get_bom($product_code) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT b.*, m.material_code, m.material_name, m.unit
FROM {$this->table_name} b
LEFT JOIN {$wpdb->prefix}mrp_materials m ON b.material_id = m.id
WHERE b.product_code = %s
ORDER BY b.operation_sequence",
$product_code
);
return $wpdb->get_results($query);
}
/**
* 计算物料需求
* @param string $product_code 产品代码
* @param float $quantity 生产数量
* @return array 物料需求列表
*/
public function calculate_material_requirements($product_code, $quantity) {
$bom_items = $this->get_bom($product_code);
$requirements = array();
foreach ($bom_items as $item) {
// 考虑损耗率
$required_qty = $item->quantity * $quantity * (1 + $item->waste_rate / 100);
$requirements[] = array(
'material_id' => $item->material_id,
'material_code' => $item->material_code,
'material_name' => $item->material_name,
'unit' => $item->unit,
'required_qty' => round($required_qty, 2),
'base_qty' => $item->quantity,
'waste_rate' => $item->waste_rate
);
}
return $requirements;
}
/**
* 删除BOM
* @param string $product_code 产品代码
* @return bool 是否成功
*/
private function delete_bom($product_code) {
global $wpdb;
return $wpdb->delete(
$this->table_name,
array('product_code' => $product_code),
array('%s')
);
}
}
?>
MRP计算引擎实现
这是MRP系统的核心计算逻辑。
<?php
// 文件路径: includes/class-mrp-calculator.php
class MRP_Calculator {
private $material_manager;
private $bom_manager;
public function __construct() {
$this->material_manager = new Material_Manager();
$this->bom_manager = new BOM_Manager();
}
/**
* 执行MRP计算
* @param array $production_orders 生产订单数组
* @return array MRP计算结果
*/
public function run_mrp_calculation($production_orders) {
$results = array(
'material_requirements' => array(),
'stock_shortages' => array(),
'purchase_suggestions' => array(),
'summary' => array()
);
// 汇总所有物料需求
$total_requirements = array();
foreach ($production_orders as $order) {
$requirements = $this->bom_manager->calculate_material_requirements(
$order['product_code'],
$order['quantity']
);
foreach ($requirements as $req) {
$material_id = $req['material_id'];
if (!isset($total_requirements[$material_id])) {
$total_requirements[$material_id] = array(
'material_id' => $material_id,
'material_code' => $req['material_code'],
'material_name' => $req['material_name'],
'unit' => $req['unit'],
'total_required' => 0,
'orders' => array()
);
}
$total_requirements[$material_id]['total_required'] += $req['required_qty'];
$total_requirements[$material_id]['orders'][] = array(
'product_code' => $order['product_code'],
'quantity' => $order['quantity'],
'required_qty' => $req['required_qty']
);
}
}
// 检查库存并生成采购建议
foreach ($total_requirements as $material_id => $requirement) {
$stock_check = $this->material_manager->check_stock(
$material_id,
$requirement['total_required']
);
$results['material_requirements'][] = array_merge(
$requirement,
array('stock_check' => $stock_check)
);
if (!$stock_check['success']) {
$material = $this->material_manager->get_material($material_id);
$results['stock_shortages'][] = array(
'material_id' => $material_id,
'material_code' => $requirement['material_code'],
'material_name' => $requirement['material_name'],
'required_qty' => $requirement['total_required'],
'available_qty' => $stock_check['available'],
'shortage_qty' => $stock_check['shortage'],
'unit' => $requirement['unit'],
'lead_time' => $material->lead_time
);
// 生成采购建议(考虑安全库存)
$purchase_qty = max(
$stock_check['shortage'],
$material->safety_stock * 0.5 // 至少采购安全库存的50%
);
$results['purchase_suggestions'][] = array(
'material_id' => $material_id,
'material_code' => $requirement['material_code'],
'material_name' => $requirement['material_name'],
'purchase_qty' => ceil($purchase_qty), // 向上取整
'unit' => $requirement['unit'],
'lead_time' => $material->lead_time,
'suggested_order_date' => date('Y-m-d'),
'suggested_delivery_date' => date('Y-m-d', strtotime("+{$material->lead_time} days"))
);
}
}
// 生成摘要信息
$results['summary'] = array(
'total_materials' => count($total_requirements),
'total_requirements' => array_sum(array_column($results['material_requirements'], 'total_required')),
'shortages_count' => count($results['stock_shortages']),
'purchase_suggestions_count' => count($results['purchase_suggestions']),
'calculation_time' => current_time('mysql')
);
return $results;
}
/**
* 生成MRP报告
* @param array $mrp_results MRP计算结果
* @return string HTML格式的报告
*/
public function generate_mrp_report($mrp_results) {
ob_start();
?>
<div class="mrp-report">
<h2>MRP计算报告</h2>
<p>生成时间: <?php echo $mrp_results['summary']['calculation_time']; ?></p>
<div class="summary">
<h3>摘要</h3>
<ul>
<li>涉及物料种类: <?php echo $mrp_results['summary']['total_materials']; ?></li>
<li>总需求数量: <?php echo $mrp_results['summary']['total_requirements']; ?></li>
<li>缺料数量: <?php echo $mrp_results['summary']['shortages_count']; ?></li>
<li>采购建议: <?php echo $mrp_results['summary']['purchase_suggestions_count']; ?> 项</li>
</ul>
</div>
<?php if (!empty($mrp_results['stock_shortages'])): ?>
<div class="shortages">
<h3>库存短缺明细</h3>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>物料编码</th>
<th>物料名称</th>
<th>需求数量</th>
<th>可用库存</th>
<th>短缺数量</th>
<th>单位</th>
<th>采购提前期</th>
</tr>
</thead>
<tbody>
<?php foreach ($mrp_results['stock_shortages'] as $shortage): ?>
<tr>
<td><?php echo esc_html($shortage['material_code']); ?></td>
<td><?php echo esc_html($shortage['material_name']); ?></td>
<td class="number"><?php echo number_format($shortage['required_qty'], 2); ?></td>
<td class="number"><?php echo number_format($shortage['available_qty'], 2); ?></td>
<td class="number highlight"><?php echo number_format($shortage['shortage_qty'], 2); ?></td>
<td><?php echo esc_html($shortage['unit']); ?></td>
<td><?php echo $shortage['lead_time']; ?> 天</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (!empty($mrp_results['purchase_suggestions'])): ?>
<div class="purchase-suggestions">
<h3>采购建议</h3>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>物料编码</th>
<th>物料名称</th>
<th>建议采购量</th>
<th>单位</th>
<th>建议下单日期</th>
<th>期望到货日期</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($mrp_results['purchase_suggestions'] as $suggestion): ?>
<tr>
<td><?php echo esc_html($suggestion['material_code']); ?></td>
<td><?php echo esc_html($suggestion['material_name']); ?></td>
<td class="number"><?php echo number_format($suggestion['purchase_qty'], 2); ?></td>
<td><?php echo esc_html($suggestion['unit']); ?></td>
<td><?php echo $suggestion['suggested_order_date']; ?></td>
<td><?php echo $suggestion['suggested_delivery_date']; ?></td>
<td>
<button class="button button-small create-po"
data-material-id="<?php echo $suggestion['material_id']; ?>"
data-quantity="<?php echo $suggestion['purchase_qty']; ?>">
创建采购单
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
}
?>
前端界面与用户交互
创建用户友好的管理界面是插件成功的关键。
<?php
// 文件路径: templates/mrp-calculator.php
/**
* MRP计算器界面
*/
// 检查权限
if (!current_user_can('manage_options')) {
wp_die('权限不足');
}
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['mrp_calculate'])) {
$production_orders = array();
if (!empty($_POST['production_orders'])) {
foreach ($_POST['production_orders'] as $order) {
if (!empty($order['product_code']) && !empty($order['quantity'])) {
$production_orders[] = array(
'product_code' => sanitize_text_field($order['product_code']),
'quantity' => floatval($order['quantity'])
);
}
}
}
if (!empty($production_orders)) {
$mrp_calculator = new MRP_Calculator();
$mrp_results = $mrp_calculator->run_mrp_calculation($production_orders);
$report = $mrp_calculator->generate_mrp_report($mrp_results);
}
}
// 获取产品列表
global $wpdb;
$products = $wpdb->get_results(
"SELECT DISTINCT product_code, product_name
FROM {$wpdb->prefix}mrp_bom
ORDER BY product_code"
);
?>
<div class="wrap">
<h1>MRP计算器</h1>
<div class="mrp-container">
<div class="mrp-left">
<form method="post" action="">
<?php wp_nonce_field('mrp_calculate_action', 'mrp_nonce'); ?>
<div class="production-orders">
<h3>生产订单输入</h3>
<div id="order-list">
<div class="order-item">
<div class="order-row">
<div class="order-field">
<label>产品代码:</label>
<select name="production_orders[0][product_code]" class="product-select">
<option value="">选择产品</option>
<?php foreach ($products as $product): ?>
<option value="<?php echo esc_attr($product->product_code); ?>">
<?php echo esc_html($product->product_code . ' - ' . $product->product_name); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="order-field">
<label>生产数量:</label>
<input type="number"
name="production_orders[0][quantity]"
step="0.01"
min="0"
value="1"
class="small-text">
</div>
<div class="order-field">
<button type="button" class="button remove-order" style="display:none;">
删除
</button>
</div>
</div>
<div class="product-details" style="display:none;">
<!-- 产品详情将通过AJAX加载 -->
</div>
</div>
</div>
<button type="button" id="add-order" class="button">
+ 添加生产订单
</button>
</div>
<div class="mrp-actions">
<button type="submit"
name="mrp_calculate"
class="button button-primary button-large">
执行MRP计算
</button>
<button type="button" id="clear-orders" class="button">
清空所有
</button>
</div>
</form>
</div>
<div class="mrp-right">
<?php if (isset($report)): ?>
<?php echo $report; ?>
<div class="export-options">
<h4>导出选项</h4>
<button type="button" id="export-pdf" class="button">
导出为PDF
</button>
<button type="button" id="export-excel" class="button">
导出为Excel
</button>
<button type="button" id="print-report" class="button">
打印报告
</button>
</div>
<?php else: ?>
<div class="mrp-instructions">
<h3>使用说明</h3>
<ol>
<li>在左侧添加生产订单</li>
<li>选择产品代码和输入生产数量</li>
<li>点击"执行MRP计算"按钮</li>
<li>系统将自动计算物料需求并识别短缺</li>
<li>根据采购建议创建采购订单</li>
</ol>
<div class="tips">
<h4>小批量定制生产提示:</h4>
<ul>
<li>对于定制产品,建议先创建BOM再运行MRP</li>
<li>考虑设置较高的安全库存以应对紧急订单</li>
<li>定期更新物料库存信息</li>
<li>及时调整采购提前期数据</li>
</ul>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
let orderCount = 1;
// 添加生产订单
$('#add-order').on('click', function() {
const orderItem = `
<div class="order-item">
<div class="order-row">
<div class="order-field">
<label>产品代码:</label>
<select name="production_orders[${orderCount}][product_code]" class="product-select">
<option value="">选择产品</option>
<?php foreach ($products as $product): ?>
<option value="<?php echo esc_attr($product->product_code); ?>">
<?php echo esc_html($product->product_code . ' - ' . $product->product_name); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="order-field">
<label>生产数量:</label>
<input type="number"
name="production_orders[${orderCount}][quantity]"
step="0.01"
min="0"
value="1"
class="small-text">
</div>
<div class="order-field">
<button type="button" class="button remove-order">
删除
</button>
</div>
</div>
<div class="product-details" style="display:none;"></div>
</div>
`;
$('#order-list').append(orderItem);
orderCount++;
// 显示所有删除按钮(除了第一个)
$('.remove-order').show();
$('#order-list .order-item:first-child .remove-order').hide();
});
// 删除生产订单
$(document).on('click', '.remove-order', function() {
if ($('.order-item').length > 1) {
$(this).closest('.order-item').remove();
}
});
// 产品选择变化时加载BOM详情
$(document).on('change', '.product-select', function() {
const productCode = $(this).val();
const detailsDiv = $(this).closest('.order-item').find('.product-details');
if (productCode) {
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'get_bom_details',
product_code: productCode,
nonce: '<?php echo wp_create_nonce('mrp_ajax_nonce'); ?>'
},
beforeSend: function() {
detailsDiv.html('<p>加载中...</p>').show();
},
success: function(response) {
if (response.success) {
detailsDiv.html(response.data);
} else {
detailsDiv.html('<p class="error">加载失败</p>');
}
}
});
} else {
detailsDiv.hide();
}
});
// 创建采购单
$(document).on('click', '.create-po', function() {
const materialId = $(this).data('material-id');
const quantity = $(this).data('quantity');
if (confirm('确定要创建采购单吗?')) {
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'create_purchase_order',
material_id: materialId,
quantity: quantity,
nonce: '<?php echo wp_create_nonce('mrp_ajax_nonce'); ?>'
},
success: function(response) {
if (response.success) {
alert('采购单创建成功!');
$(this).replaceWith('<span class="dashicons dashicons-yes-alt"></span> 已创建');
} else {
alert('创建失败: ' + response.data);
}
}
});
}
});
// 清空所有订单
$('#clear-orders').on('click', function() {
if (confirm('确定要清空所有生产订单吗?')) {
$('#order-list').html(`
<div class="order-item">
<div class="order-row">
<div class="order-field">
<label>产品代码:</label>
<select name="production_orders[0][product_code]" class="product-select">
<option value="">选择产品</option>
<?php foreach ($products as $product): ?>
<option value="<?php echo esc_attr($product->product_code); ?>">
<?php echo esc_html($product->product_code . ' - ' . $product->product_name); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="order-field">
<label>生产数量:</label>
<input type="number"
name="production_orders[0][quantity]"
step="0.01"
min="0"
value="1"
class="small-text">
</div>
<div class="order-field">
<button type="button" class="button remove-order" style="display:none;">
删除
</button>
</div>
</div>
<div class="product-details" style="display:none;"></div>
</div>
`);
orderCount = 1;
}
});
});
</script>
<style type="text/css">
.mrp-container {
display: flex;
gap: 30px;
margin-top: 20px;
}
.mrp-left {
flex: 1;
min-width: 400px;
}
.mrp-right {
flex: 2;
min-width: 500px;
}
.production-orders {
background: #fff;
padding: 20px;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
.order-item {
margin-bottom: 15px;
padding: 15px;
background: #f9f9f9;
border: 1px solid #e5e5e5;
}
.order-row {
display: flex;
gap: 10px;
align-items: flex-end;
}
.order-field {
flex: 1;
}
.order-field label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}
.product-details {
margin-top: 10px;
padding: 10px;
background: #fff;
border: 1px dashed #ddd;
}
.mrp-actions {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ddd;
}
.mrp-report .summary,
.mrp-report .shortages,
.mrp-report .purchase-suggestions {
margin-bottom: 30px;
}
.mrp-report table {
margin-top: 10px;
}
.mrp-report .number {
text-align: right;
}
.mrp-report .highlight {
color: #d63638;
font-weight: bold;
}
.export-options {
margin-top: 20px;
padding: 15px;
background: #f0f6fc;
border: 1px solid #c3c4c7;
}
.mrp-instructions {
background: #fff;
padding: 20px;
border: 1px solid #ccd0d4;
}
.mrp-instructions ol,
.mrp-instructions ul {
margin-left: 20px;
}
.tips {
margin-top: 20px;
padding: 15px;
background: #f0f6fc;
border-left: 4px solid #72aee6;
}
</style>
AJAX处理与数据交互
<?php
// 文件路径: includes/class-ajax-handler.php
class MRP_Ajax_Handler {
public function __construct() {
add_action('wp_ajax_get_bom_details', array($this, 'get_bom_details'));
add_action('wp_ajax_create_purchase_order', array($this, 'create_purchase_order'));
