Files
MES/app/controllers/InventoryController.php
2026-08-08 18:28:49 +08:00

727 lines
28 KiB
PHP

<?php
namespace app\controllers;
use core\base\Controller;
use app\models\Supplier;
use app\models\PurchaseOrder;
use app\models\PurchaseItem;
use app\models\Inventory;
use app\models\SalesOrder;
use app\models\SalesItem;
/**
* 进销存管理控制器
* 访问权限:超级管理员 或 进销存类目管理员
*/
class InventoryController extends Controller
{
private function checkCategoryAccess()
{
$this->checkLogin();
$user = $this->getCurrentUser();
if ($user['role'] === self::ROLE_SUPER_ADMIN) {
return $user;
}
if ($user['role'] === self::ROLE_ADMIN && ($user['category'] ?? '') === self::CATEGORY_INVENTORY) {
return $user;
}
header('Location: ' . BASE_URL . '/Front/index');
exit;
}
public function index()
{
$user = $this->checkCategoryAccess();
$supplier = new Supplier();
$purchaseOrder = new PurchaseOrder();
$inventory = new Inventory();
$salesOrder = new SalesOrder();
$this->assign('title', '进销存概览');
$this->assign('user', $user);
$this->assign('activeMenu', 'inventory_dashboard');
$this->assign('supplierCount', count($supplier->getAll()));
$this->assign('purchaseCount', $purchaseOrder->getCount());
$this->assign('stockCount', $inventory->getCount());
$this->assign('salesCount', $salesOrder->getCount());
$this->assign('lowStockItems', $inventory->getLowStock());
$this->render();
}
// ========== 供应商管理 ==========
public function supplier()
{
$user = $this->checkCategoryAccess();
$supplier = new Supplier();
$suppliers = $supplier->getAll();
$this->assign('title', '供应商管理');
$this->assign('user', $user);
$this->assign('activeMenu', 'supplier');
$this->assign('suppliers', $suppliers);
$this->render();
}
public function supplierAdd()
{
$user = $this->checkCategoryAccess();
if ($user['role'] !== self::ROLE_SUPER_ADMIN && $user['role'] !== self::ROLE_ADMIN) {
exit('无权限访问');
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$data = [
'name' => trim($_POST['name'] ?? ''),
'contact_person' => trim($_POST['contact_person'] ?? ''),
'phone' => trim($_POST['phone'] ?? ''),
'address' => trim($_POST['address'] ?? ''),
'remark' => trim($_POST['remark'] ?? ''),
'status' => (int)($_POST['status'] ?? 1),
];
if (empty($data['name'])) {
$this->assign('error', '供应商名称不能为空');
$this->assign('title', '添加供应商');
$this->assign('user', $user);
$this->assign('activeMenu', 'supplier');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
try {
$supplier = new Supplier();
$supplier->add($data);
header('Location: ' . BASE_URL . '/Inventory/supplier');
exit;
} catch (\Throwable $e) {
error_log('[supplierAdd] ' . $e->getMessage());
$this->assign('error', '添加失败:' . $e->getMessage());
$this->assign('title', '添加供应商');
$this->assign('user', $user);
$this->assign('activeMenu', 'supplier');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '添加供应商');
$this->assign('user', $user);
$this->assign('activeMenu', 'supplier');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function supplierEdit()
{
$user = $this->checkCategoryAccess();
$id = (int)($_GET['id'] ?? 0);
$supplier = new Supplier();
$record = $supplier->getById($id);
if (!$record) exit('供应商不存在');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$data = [
'name' => trim($_POST['name'] ?? ''),
'contact_person' => trim($_POST['contact_person'] ?? ''),
'phone' => trim($_POST['phone'] ?? ''),
'address' => trim($_POST['address'] ?? ''),
'remark' => trim($_POST['remark'] ?? ''),
'status' => (int)($_POST['status'] ?? 1),
];
if (empty($data['name'])) {
$this->assign('error', '供应商名称不能为空');
$this->assign('title', '编辑供应商');
$this->assign('user', $user);
$this->assign('activeMenu', 'supplier');
$this->assign('record', $record);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
try {
$supplier->where(['id = :id'], [':id' => $id])->update($data);
header('Location: ' . BASE_URL . '/Inventory/supplier');
exit;
} catch (\Throwable $e) {
error_log('[supplierEdit] ' . $e->getMessage());
$this->assign('error', '更新失败:' . $e->getMessage());
$this->assign('title', '编辑供应商');
$this->assign('user', $user);
$this->assign('activeMenu', 'supplier');
$this->assign('record', $record);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '编辑供应商');
$this->assign('user', $user);
$this->assign('activeMenu', 'supplier');
$this->assign('record', $record);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function supplierDelete()
{
$this->checkLogin();
$this->requireMethod('post');
$this->csrfVerify();
$id = (int)($_POST['id'] ?? 0);
$supplier = new Supplier();
$supplier->delete($id);
header('Location: ' . BASE_URL . '/Inventory/supplier');
exit;
}
// ========== 采购管理 ==========
public function purchase()
{
$user = $this->checkCategoryAccess();
$purchaseOrder = new PurchaseOrder();
$orders = $purchaseOrder->getAll();
$this->assign('title', '采购管理');
$this->assign('user', $user);
$this->assign('activeMenu', 'purchase');
$this->assign('orders', $orders);
$this->render();
}
public function purchaseAdd()
{
$user = $this->checkCategoryAccess();
if ($user['role'] !== self::ROLE_SUPER_ADMIN && $user['role'] !== self::ROLE_ADMIN) {
exit('无权限访问');
}
$supplierModel = new Supplier();
$suppliers = $supplierModel->getActiveList();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$supplierId = (int)($_POST['supplier_id'] ?? 0);
$supplierInfo = $supplierModel->getById($supplierId);
$orderData = [
'order_no' => trim($_POST['order_no'] ?? ''),
'supplier_id' => $supplierId,
'supplier_name' => $supplierInfo['name'] ?? '',
'order_date' => trim($_POST['order_date'] ?? date('Y-m-d')),
'total_amount' => 0,
'status' => trim($_POST['status'] ?? 'pending'),
'remark' => trim($_POST['remark'] ?? ''),
'operator' => $user['emp_name'],
];
if (empty($orderData['order_no'])) {
$this->assign('error', '采购单号不能为空');
$this->assign('title', '添加采购订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'purchase');
$this->assign('suppliers', $suppliers);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
try {
$purchaseOrder = new PurchaseOrder();
$purchaseOrder->add($orderData);
$orderId = $this->pdo->lastInsertId();
$productNames = $_POST['product_name'] ?? [];
$productModels = $_POST['product_model'] ?? [];
$quantities = $_POST['quantity'] ?? [];
$units = $_POST['unit'] ?? [];
$unitPrices = $_POST['unit_price'] ?? [];
$totalAmount = 0;
$purchaseItem = new PurchaseItem();
foreach ($productNames as $i => $pname) {
if (empty(trim($pname))) continue;
$qty = (int)($quantities[$i] ?? 0);
$price = (float)($unitPrices[$i] ?? 0);
$amount = $qty * $price;
$totalAmount += $amount;
$purchaseItem->add([
'order_id' => $orderId,
'product_name' => trim($pname),
'product_model' => trim($productModels[$i] ?? ''),
'quantity' => $qty,
'unit' => trim($units[$i] ?? '个'),
'unit_price' => $price,
'amount' => $amount,
]);
}
$purchaseOrder->where(['id = :id'], [':id' => $orderId])->update(['total_amount' => $totalAmount]);
header('Location: ' . BASE_URL . '/Inventory/purchase');
exit;
} catch (\Throwable $e) {
error_log('[purchaseAdd] ' . $e->getMessage());
$this->assign('error', '添加失败:' . $e->getMessage());
$this->assign('title', '添加采购订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'purchase');
$this->assign('suppliers', $suppliers);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '添加采购订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'purchase');
$this->assign('suppliers', $suppliers);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function purchaseDetail()
{
$user = $this->checkCategoryAccess();
$id = (int)($_GET['id'] ?? 0);
$purchaseOrder = new PurchaseOrder();
$order = $purchaseOrder->getById($id);
if (!$order) exit('订单不存在');
$purchaseItem = new PurchaseItem();
$items = $purchaseItem->getByOrder($id);
$this->assign('title', '采购订单详情');
$this->assign('user', $user);
$this->assign('activeMenu', 'purchase');
$this->assign('order', $order);
$this->assign('items', $items);
$this->render();
}
public function purchaseEdit()
{
$user = $this->checkCategoryAccess();
$id = (int)($_GET['id'] ?? 0);
$purchaseOrder = new PurchaseOrder();
$order = $purchaseOrder->getById($id);
if (!$order) exit('订单不存在');
$supplierModel = new Supplier();
$suppliers = $supplierModel->getActiveList();
$purchaseItem = new PurchaseItem();
$items = $purchaseItem->getByOrder($id);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$supplierId = (int)($_POST['supplier_id'] ?? 0);
$supplierInfo = $supplierModel->getById($supplierId);
$orderData = [
'order_no' => trim($_POST['order_no'] ?? ''),
'supplier_id' => $supplierId,
'supplier_name' => $supplierInfo['name'] ?? '',
'order_date' => trim($_POST['order_date'] ?? ''),
'status' => trim($_POST['status'] ?? 'pending'),
'remark' => trim($_POST['remark'] ?? ''),
];
try {
$purchaseOrder->where(['id = :id'], [':id' => $id])->update($orderData);
$purchaseItem->deleteByOrder($id);
$productNames = $_POST['product_name'] ?? [];
$productModels = $_POST['product_model'] ?? [];
$quantities = $_POST['quantity'] ?? [];
$units = $_POST['unit'] ?? [];
$unitPrices = $_POST['unit_price'] ?? [];
$totalAmount = 0;
foreach ($productNames as $i => $pname) {
if (empty(trim($pname))) continue;
$qty = (int)($quantities[$i] ?? 0);
$price = (float)($unitPrices[$i] ?? 0);
$amount = $qty * $price;
$totalAmount += $amount;
$purchaseItem->add([
'order_id' => $id,
'product_name' => trim($pname),
'product_model' => trim($productModels[$i] ?? ''),
'quantity' => $qty,
'unit' => trim($units[$i] ?? '个'),
'unit_price' => $price,
'amount' => $amount,
]);
}
$purchaseOrder->where(['id = :id'], [':id' => $id])->update(['total_amount' => $totalAmount]);
header('Location: ' . BASE_URL . '/Inventory/purchase');
exit;
} catch (\Throwable $e) {
error_log('[purchaseEdit] ' . $e->getMessage());
$this->assign('error', '更新失败:' . $e->getMessage());
$this->assign('title', '编辑采购订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'purchase');
$this->assign('order', $order);
$this->assign('items', $items);
$this->assign('suppliers', $suppliers);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '编辑采购订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'purchase');
$this->assign('order', $order);
$this->assign('items', $items);
$this->assign('suppliers', $suppliers);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function purchaseDelete()
{
$this->checkLogin();
$this->requireMethod('post');
$this->csrfVerify();
$id = (int)($_POST['id'] ?? 0);
$purchaseItem = new PurchaseItem();
$purchaseItem->deleteByOrder($id);
$purchaseOrder = new PurchaseOrder();
$purchaseOrder->delete($id);
header('Location: ' . BASE_URL . '/Inventory/purchase');
exit;
}
// ========== 库存管理 ==========
public function stock()
{
$user = $this->checkCategoryAccess();
$inventory = new Inventory();
$stocks = $inventory->getAll();
$this->assign('title', '库存管理');
$this->assign('user', $user);
$this->assign('activeMenu', 'stock');
$this->assign('stocks', $stocks);
$this->render();
}
public function stockAdd()
{
$user = $this->checkCategoryAccess();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$data = [
'product_name' => trim($_POST['product_name'] ?? ''),
'product_model' => trim($_POST['product_model'] ?? ''),
'category' => trim($_POST['category'] ?? ''),
'quantity' => (int)($_POST['quantity'] ?? 0),
'unit' => trim($_POST['unit'] ?? '个'),
'unit_price' => (float)($_POST['unit_price'] ?? 0),
'min_stock' => (int)($_POST['min_stock'] ?? 0),
'max_stock' => (int)($_POST['max_stock'] ?? 0),
'warehouse' => trim($_POST['warehouse'] ?? ''),
'location' => trim($_POST['location'] ?? ''),
'remark' => trim($_POST['remark'] ?? ''),
];
if (empty($data['product_name'])) {
$this->assign('error', '产品名称不能为空');
$this->assign('title', '添加库存');
$this->assign('user', $user);
$this->assign('activeMenu', 'stock');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
try {
$inventory = new Inventory();
$inventory->add($data);
header('Location: ' . BASE_URL . '/Inventory/stock');
exit;
} catch (\Throwable $e) {
error_log('[stockAdd] ' . $e->getMessage());
$this->assign('error', '添加失败:' . $e->getMessage());
$this->assign('title', '添加库存');
$this->assign('user', $user);
$this->assign('activeMenu', 'stock');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '添加库存');
$this->assign('user', $user);
$this->assign('activeMenu', 'stock');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function stockEdit()
{
$user = $this->checkCategoryAccess();
$id = (int)($_GET['id'] ?? 0);
$inventory = new Inventory();
$record = $inventory->getById($id);
if (!$record) exit('库存记录不存在');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$data = [
'product_name' => trim($_POST['product_name'] ?? ''),
'product_model' => trim($_POST['product_model'] ?? ''),
'category' => trim($_POST['category'] ?? ''),
'quantity' => (int)($_POST['quantity'] ?? 0),
'unit' => trim($_POST['unit'] ?? '个'),
'unit_price' => (float)($_POST['unit_price'] ?? 0),
'min_stock' => (int)($_POST['min_stock'] ?? 0),
'max_stock' => (int)($_POST['max_stock'] ?? 0),
'warehouse' => trim($_POST['warehouse'] ?? ''),
'location' => trim($_POST['location'] ?? ''),
'remark' => trim($_POST['remark'] ?? ''),
];
try {
$inventory->where(['id = :id'], [':id' => $id])->update($data);
header('Location: ' . BASE_URL . '/Inventory/stock');
exit;
} catch (\Throwable $e) {
error_log('[stockEdit] ' . $e->getMessage());
$this->assign('error', '更新失败:' . $e->getMessage());
$this->assign('title', '编辑库存');
$this->assign('user', $user);
$this->assign('activeMenu', 'stock');
$this->assign('record', $record);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '编辑库存');
$this->assign('user', $user);
$this->assign('activeMenu', 'stock');
$this->assign('record', $record);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function stockDelete()
{
$this->checkLogin();
$this->requireMethod('post');
$this->csrfVerify();
$id = (int)($_POST['id'] ?? 0);
$inventory = new Inventory();
$inventory->delete($id);
header('Location: ' . BASE_URL . '/Inventory/stock');
exit;
}
// ========== 销售管理 ==========
public function sales()
{
$user = $this->checkCategoryAccess();
$salesOrder = new SalesOrder();
$orders = $salesOrder->getAll();
$this->assign('title', '销售管理');
$this->assign('user', $user);
$this->assign('activeMenu', 'sales');
$this->assign('orders', $orders);
$this->render();
}
public function salesAdd()
{
$user = $this->checkCategoryAccess();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$orderData = [
'order_no' => trim($_POST['order_no'] ?? ''),
'customer_name' => trim($_POST['customer_name'] ?? ''),
'order_date' => trim($_POST['order_date'] ?? date('Y-m-d')),
'total_amount' => 0,
'status' => trim($_POST['status'] ?? 'pending'),
'remark' => trim($_POST['remark'] ?? ''),
'operator' => $user['emp_name'],
];
if (empty($orderData['order_no'])) {
$this->assign('error', '销售单号不能为空');
$this->assign('title', '添加销售订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'sales');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
try {
$salesOrder = new SalesOrder();
$salesOrder->add($orderData);
$orderId = $this->pdo->lastInsertId();
$productNames = $_POST['product_name'] ?? [];
$productModels = $_POST['product_model'] ?? [];
$quantities = $_POST['quantity'] ?? [];
$units = $_POST['unit'] ?? [];
$unitPrices = $_POST['unit_price'] ?? [];
$totalAmount = 0;
$salesItem = new SalesItem();
foreach ($productNames as $i => $pname) {
if (empty(trim($pname))) continue;
$qty = (int)($quantities[$i] ?? 0);
$price = (float)($unitPrices[$i] ?? 0);
$amount = $qty * $price;
$totalAmount += $amount;
$salesItem->add([
'order_id' => $orderId,
'product_name' => trim($pname),
'product_model' => trim($productModels[$i] ?? ''),
'quantity' => $qty,
'unit' => trim($units[$i] ?? '个'),
'unit_price' => $price,
'amount' => $amount,
]);
}
$salesOrder->where(['id = :id'], [':id' => $orderId])->update(['total_amount' => $totalAmount]);
header('Location: ' . BASE_URL . '/Inventory/sales');
exit;
} catch (\Throwable $e) {
error_log('[salesAdd] ' . $e->getMessage());
$this->assign('error', '添加失败:' . $e->getMessage());
$this->assign('title', '添加销售订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'sales');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '添加销售订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'sales');
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function salesDetail()
{
$user = $this->checkCategoryAccess();
$id = (int)($_GET['id'] ?? 0);
$salesOrder = new SalesOrder();
$order = $salesOrder->getById($id);
if (!$order) exit('订单不存在');
$salesItem = new SalesItem();
$items = $salesItem->getByOrder($id);
$this->assign('title', '销售订单详情');
$this->assign('user', $user);
$this->assign('activeMenu', 'sales');
$this->assign('order', $order);
$this->assign('items', $items);
$this->render();
}
public function salesEdit()
{
$user = $this->checkCategoryAccess();
$id = (int)($_GET['id'] ?? 0);
$salesOrder = new SalesOrder();
$order = $salesOrder->getById($id);
if (!$order) exit('订单不存在');
$salesItem = new SalesItem();
$items = $salesItem->getByOrder($id);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$this->csrfVerify();
$orderData = [
'order_no' => trim($_POST['order_no'] ?? ''),
'customer_name' => trim($_POST['customer_name'] ?? ''),
'order_date' => trim($_POST['order_date'] ?? ''),
'status' => trim($_POST['status'] ?? 'pending'),
'remark' => trim($_POST['remark'] ?? ''),
];
try {
$salesOrder->where(['id = :id'], [':id' => $id])->update($orderData);
$salesItem->deleteByOrder($id);
$productNames = $_POST['product_name'] ?? [];
$productModels = $_POST['product_model'] ?? [];
$quantities = $_POST['quantity'] ?? [];
$units = $_POST['unit'] ?? [];
$unitPrices = $_POST['unit_price'] ?? [];
$totalAmount = 0;
foreach ($productNames as $i => $pname) {
if (empty(trim($pname))) continue;
$qty = (int)($quantities[$i] ?? 0);
$price = (float)($unitPrices[$i] ?? 0);
$amount = $qty * $price;
$totalAmount += $amount;
$salesItem->add([
'order_id' => $id,
'product_name' => trim($pname),
'product_model' => trim($productModels[$i] ?? ''),
'quantity' => $qty,
'unit' => trim($units[$i] ?? '个'),
'unit_price' => $price,
'amount' => $amount,
]);
}
$salesOrder->where(['id = :id'], [':id' => $id])->update(['total_amount' => $totalAmount]);
header('Location: ' . BASE_URL . '/Inventory/sales');
exit;
} catch (\Throwable $e) {
error_log('[salesEdit] ' . $e->getMessage());
$this->assign('error', '更新失败:' . $e->getMessage());
$this->assign('title', '编辑销售订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'sales');
$this->assign('order', $order);
$this->assign('items', $items);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
return;
}
}
$this->assign('title', '编辑销售订单');
$this->assign('user', $user);
$this->assign('activeMenu', 'sales');
$this->assign('order', $order);
$this->assign('items', $items);
$this->assign('csrfToken', $this->csrfToken());
$this->render();
}
public function salesDelete()
{
$this->checkLogin();
$this->requireMethod('post');
$this->csrfVerify();
$id = (int)($_POST['id'] ?? 0);
$salesItem = new SalesItem();
$salesItem->deleteByOrder($id);
$salesOrder = new SalesOrder();
$salesOrder->delete($id);
header('Location: ' . BASE_URL . '/Inventory/sales');
exit;
}
}