all()); $itemM = new PurchaseOrderItem(); foreach ($orders as &$o) { $o['_items'] = $itemM->whereAll('po_id', $o['id']); } return $this->renderSubsys('psi', 'psi/purchase_orders', ['orders' => $orders], \psi_nav(), 'purchase_orders'); } public function create() { return $this->renderSubsys('psi', 'psi/purchase_order_form', [ 'o' => null, 'items' => [], 'materials' => (new Material())->all(), 'products' => (new Product())->all(), ], \psi_nav(), 'purchase_orders'); } public function store() { if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_check()) return $this->redirect('PSI/purchase_orders/create'); $items = $this->parseItems(); if (empty($items)) { $this->flash('请至少添加一条采购明细', 'err'); return $this->redirect('PSI/purchase_orders/create'); } $id = (new PurchaseOrder())->insert([ 'order_no' => $oNo = \psi_gen_no('PO'), 'supplier_id' => (int)$this->post('supplier_id'), 'supplier_name' => $supp = trim($this->post('supplier_name')), 'salesman' => $buyer = trim($this->post('salesman')) ?: ($_SESSION['admin_name'] ?? ''), 'status' => 'pending', 'expected_at' => $this->post('expected_at') ?: null, 'remark' => trim($this->post('remark')), 'created_at' => date('Y-m-d H:i:s'), ]); $itemM = new PurchaseOrderItem(); foreach ($items as $it) { $it['po_id'] = $id; $itemM->insert($it); } \Core\Notify::newPurchaseOrder($oNo, $supp, $buyer, $id); $this->flash('采购订单已创建', 'ok'); if ($this->post('auto_print', '1') !== '0') { return $this->redirect('PSI/purchase_orders/print/' . $id); } return $this->redirect('PSI/purchase_orders/show/' . $id); } public function show($id) { $o = (new PurchaseOrder())->find($id); if (!$o) { \Core\App::notFound(); return; } $items = (new PurchaseOrderItem())->whereAll('po_id', $id); return $this->renderSubsys('psi', 'psi/purchase_order_show', ['o' => $o, 'items' => $items], \psi_nav(), 'purchase_orders'); } public function edit($id) { $o = (new PurchaseOrder())->find($id); if (!$o) { \Core\App::notFound(); return; } if (in_array($o['status'], ['received'], true)) { $this->flash('已收货的采购订单不可编辑', 'err'); return $this->redirect('PSI/purchase_orders/show/' . $id); } $items = (new PurchaseOrderItem())->whereAll('po_id', $id); return $this->renderSubsys('psi', 'psi/purchase_order_form', [ 'o' => $o, 'items' => $items, 'materials' => (new Material())->all(), 'products' => (new Product())->all(), ], \psi_nav(), 'purchase_orders'); } public function update($id) { if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_check()) return $this->redirect('PSI/purchase_orders/edit/' . $id); $o = (new PurchaseOrder())->find($id); if (!$o) { \Core\App::notFound(); return; } $items = $this->parseItems(); if (empty($items)) { $this->flash('请至少添加一条采购明细', 'err'); return $this->redirect('PSI/purchase_orders/edit/' . $id); } (new PurchaseOrder())->update($id, [ 'supplier_id' => (int)$this->post('supplier_id'), 'supplier_name' => trim($this->post('supplier_name')), 'salesman' => trim($this->post('salesman')) ?: ($_SESSION['admin_name'] ?? ''), 'expected_at' => $this->post('expected_at') ?: null, 'remark' => trim($this->post('remark')), ]); $old = (new PurchaseOrderItem())->whereAll('po_id', $id); $recv = []; foreach ($old as $oi) { $recv[($oi['item_type'] ?? '') . '|' . ($oi['item_id'] ?? 0) . '|' . $oi['name']] = (int)($oi['received_qty'] ?? 0); } (new PurchaseOrderItem())->deleteRaw('po_id', $id); foreach ($items as $it) { $key = ($it['item_type'] ?? '') . '|' . ($it['item_id'] ?? 0) . '|' . $it['name']; $it['received_qty'] = $recv[$key] ?? 0; $it['po_id'] = $id; (new PurchaseOrderItem())->insert($it); } $this->flash('采购订单已更新', 'ok'); return $this->redirect('PSI/purchase_orders/show/' . $id); } /** 收货入库:成品增加库存 + 写入采购入库流水;更新已收数量与状态 */ public function receive($id) { if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_check()) return $this->redirect('PSI/purchase_orders/show/' . $id); $o = (new PurchaseOrder())->find($id); if (!$o) { \Core\App::notFound(); return; } if ($o['status'] === 'received') { $this->flash('该采购订单已收货,不可重复操作', 'err'); return $this->redirect('PSI/purchase_orders/show/' . $id); } $items = (new PurchaseOrderItem())->whereAll('po_id', $id); $purchaseM = new Purchase(); foreach ($items as $it) { if ((int)$it['qty'] <= 0) continue; if ($it['item_type'] === 'product' && (int)$it['item_id'] > 0) { StockHelper::adjustStock((int)$it['item_id'], (int)$it['qty'], 'in', '采购入库', $o['order_no']); $purchaseM->insert([ 'product_id' => (int)$it['item_id'], 'qty' => (int)$it['qty'], 'price' => (float)$it['price'], 'amount' => (float)$it['amount'], 'supplier' => $o['supplier_name'], 'order_no' => $o['order_no'], 'created_at' => date('Y-m-d H:i:s'), ]); } (new PurchaseOrderItem())->update($it['id'], ['received_qty' => (int)$it['qty']]); } (new PurchaseOrder())->update($id, ['status' => 'received']); $this->flash('采购订单已收货入库', 'ok'); return $this->redirect('PSI/purchase_orders/show/' . $id); } public function destroy($id) { if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_check()) { (new PurchaseOrderItem())->deleteRaw('po_id', $id); (new PurchaseOrder())->delete($id); $this->flash('采购订单已删除', 'ok'); } return $this->redirect('PSI/purchase_orders'); } public function printDoc($id) { $o = (new PurchaseOrder())->find($id); if (!$o) { \Core\App::notFound(); return; } $items = (new PurchaseOrderItem())->whereAll('po_id', $id); $body = \App\Core\View::render('psi/purchase_order_print', ['o' => $o, 'items' => $items]); echo \psi_print_shell('采购订单 ' . $o['order_no'], $body); } private function parseItems(): array { $raw = $_POST['items'] ?? []; $out = []; foreach ($raw as $row) { $name = trim((string)($row['name'] ?? '')); $qty = (int)($row['qty'] ?? 0); $price = (float)($row['price'] ?? 0); if ($name === '' || $qty <= 0) continue; $out[] = [ 'item_type' => $row['item_type'] === 'product' ? 'product' : 'material', 'item_id' => (int)($row['item_id'] ?? 0), 'name' => $name, 'spec' => trim((string)($row['spec'] ?? '')), 'unit' => trim((string)($row['unit'] ?? '')), 'qty' => $qty, 'price' => $price, 'amount' => round($qty * $price, 2), 'received_qty' => 0, ]; } return $out; } }