文件还在测试中
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
namespace App\Controllers\PSI;
|
||||
|
||||
use App\Controllers\Controller;
|
||||
use App\Models\PSI\Outbound;
|
||||
use App\Models\PSI\OutboundItem;
|
||||
use App\Models\PSI\SalesOrder;
|
||||
use App\Models\PSI\SalesOrderItem;
|
||||
use App\Models\PSI\Product;
|
||||
use App\Controllers\PSI\StockHelper;
|
||||
|
||||
/**
|
||||
* 出库单(交付):关联销售订单,扣减成品库存,回写销售订单已交付数量。
|
||||
* 编辑/删除会先回冲原库存与已交付量,再重新应用,保证数据一致。
|
||||
*/
|
||||
class OutboundsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$list = array_reverse((new Outbound())->all());
|
||||
$itemM = new OutboundItem();
|
||||
foreach ($list as &$o) { $o['_items'] = $itemM->whereAll('ob_id', $o['id']); }
|
||||
return $this->renderSubsys('psi', 'psi/outbounds', ['list' => $list], \psi_nav(), 'outbounds');
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$soNo = $this->get('so');
|
||||
$so = $soNo ? (new SalesOrder())->where('order_no', $soNo) : null;
|
||||
$soItems = $so ? (new SalesOrderItem())->whereAll('so_id', $so['id']) : [];
|
||||
return $this->renderSubsys('psi', 'psi/outbound_form', [
|
||||
'o' => null, 'items' => [], 'so' => $so, 'soItems' => $soItems, 'products' => (new Product())->all(),
|
||||
], \psi_nav(), 'outbounds');
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_check()) return $this->redirect('PSI/outbounds/create');
|
||||
$items = $this->parseItems();
|
||||
if (empty($items)) { $this->flash('请至少添加一条出库明细', 'err'); return $this->redirect('PSI/outbounds/create'); }
|
||||
|
||||
$soNo = trim($this->post('so_no'));
|
||||
$so = $soNo ? (new SalesOrder())->where('order_no', $soNo) : null;
|
||||
$orderNo = \psi_gen_no('OUT');
|
||||
$id = (new Outbound())->insert([
|
||||
'order_no' => $orderNo,
|
||||
'so_no' => $soNo,
|
||||
'customer' => trim($this->post('customer')) ?: ($so['customer'] ?? ''),
|
||||
'salesman' => trim($this->post('salesman')) ?: ($so['salesman'] ?? ($_SESSION['admin_name'] ?? '')),
|
||||
'warehouse' => trim($this->post('warehouse')),
|
||||
'status' => 'delivered',
|
||||
'delivery_date' => $this->post('delivery_date') ?: null,
|
||||
'remark' => trim($this->post('remark')),
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
$this->applyItems($id, $items, $soNo, $orderNo);
|
||||
if ($so) {
|
||||
\psi_recompute_so($so['id']);
|
||||
$st = (new SalesOrder())->find($so['id'])['status'];
|
||||
(new Outbound())->update($id, ['status' => $st === 'delivered' ? 'delivered' : 'partial']);
|
||||
}
|
||||
$this->flash('出库单已保存', 'ok');
|
||||
if ($this->post('auto_print', '1') !== '0') {
|
||||
return $this->redirect('PSI/outbounds/print/' . $id);
|
||||
}
|
||||
return $this->redirect('PSI/outbounds/show/' . $id);
|
||||
}
|
||||
|
||||
public function show($id)
|
||||
{
|
||||
$o = (new Outbound())->find($id);
|
||||
if (!$o) { \Core\App::notFound(); return; }
|
||||
$items = (new OutboundItem())->whereAll('ob_id', $id);
|
||||
return $this->renderSubsys('psi', 'psi/outbound_show', ['o' => $o, 'items' => $items], \psi_nav(), 'outbounds');
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$o = (new Outbound())->find($id);
|
||||
if (!$o) { \Core\App::notFound(); return; }
|
||||
$items = (new OutboundItem())->whereAll('ob_id', $id);
|
||||
$so = $o['so_no'] ? (new SalesOrder())->where('order_no', $o['so_no']) : null;
|
||||
$soItems = $so ? (new SalesOrderItem())->whereAll('so_id', $so['id']) : [];
|
||||
return $this->renderSubsys('psi', 'psi/outbound_form', [
|
||||
'o' => $o, 'items' => $items, 'so' => $so, 'soItems' => $soItems, 'products' => (new Product())->all(),
|
||||
], \psi_nav(), 'outbounds');
|
||||
}
|
||||
|
||||
public function update($id)
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_check()) return $this->redirect('PSI/outbounds/edit/' . $id);
|
||||
$o = (new Outbound())->find($id);
|
||||
if (!$o) { \Core\App::notFound(); return; }
|
||||
$items = $this->parseItems();
|
||||
if (empty($items)) { $this->flash('请至少添加一条出库明细', 'err'); return $this->redirect('PSI/outbounds/edit/' . $id); }
|
||||
|
||||
$this->reverseItems($id); // 回冲库存与已交付
|
||||
(new Outbound())->update($id, [
|
||||
'customer' => trim($this->post('customer')),
|
||||
'salesman' => trim($this->post('salesman')) ?: ($_SESSION['admin_name'] ?? ''),
|
||||
'warehouse' => trim($this->post('warehouse')),
|
||||
'delivery_date' => $this->post('delivery_date') ?: null,
|
||||
'remark' => trim($this->post('remark')),
|
||||
]);
|
||||
$this->applyItems($id, $items, $o['so_no'], $o['order_no']);
|
||||
if ($o['so_no']) {
|
||||
$so = (new SalesOrder())->where('order_no', $o['so_no']);
|
||||
if ($so) \psi_recompute_so((int)$so['id']);
|
||||
}
|
||||
$this->flash('出库单已更新', 'ok');
|
||||
return $this->redirect('PSI/outbounds/show/' . $id);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_check()) {
|
||||
$this->reverseItems($id);
|
||||
(new OutboundItem())->deleteRaw('ob_id', $id);
|
||||
(new Outbound())->delete($id);
|
||||
$this->flash('出库单已删除', 'ok');
|
||||
}
|
||||
return $this->redirect('PSI/outbounds');
|
||||
}
|
||||
|
||||
public function printDoc($id)
|
||||
{
|
||||
$o = (new Outbound())->find($id);
|
||||
if (!$o) { \Core\App::notFound(); return; }
|
||||
$items = (new OutboundItem())->whereAll('ob_id', $id);
|
||||
$body = \App\Core\View::render('psi/outbound_print', ['o' => $o, 'items' => $items]);
|
||||
echo \psi_print_shell('出库单 ' . $o['order_no'], $body);
|
||||
}
|
||||
|
||||
/** 应用出库明细:写明细 + 扣库存 + 回写销售订单已交付量 */
|
||||
private function applyItems(int $obId, array $items, string $soNo, string $orderNo): void
|
||||
{
|
||||
$itemM = new OutboundItem();
|
||||
$so = $soNo ? (new SalesOrder())->where('order_no', $soNo) : null;
|
||||
foreach ($items as $it) {
|
||||
$it['ob_id'] = $obId;
|
||||
$itemM->insert($it);
|
||||
if ((int)($it['product_id'] ?? 0) > 0) {
|
||||
StockHelper::adjustStock((int)$it['product_id'], (int)$it['qty'], 'out', '销售出库', $orderNo);
|
||||
}
|
||||
if ((int)($it['so_item_id'] ?? 0) > 0) {
|
||||
$si = (new SalesOrderItem())->find($it['so_item_id']);
|
||||
if ($si) {
|
||||
(new SalesOrderItem())->update($si['id'], ['delivered_qty' => (int)$si['delivered_qty'] + (int)$it['qty']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 回冲:把出库明细的库存与销售订单已交付量还原 */
|
||||
private function reverseItems(int $obId): void
|
||||
{
|
||||
$old = (new OutboundItem())->whereAll('ob_id', $obId);
|
||||
foreach ($old as $oi) {
|
||||
if ((int)($oi['product_id'] ?? 0) > 0) {
|
||||
StockHelper::adjustStock((int)$oi['product_id'], (int)$oi['qty'], 'in', '出库冲正', $oi['ob_id'] ?? '');
|
||||
}
|
||||
if ((int)($oi['so_item_id'] ?? 0) > 0) {
|
||||
$si = (new SalesOrderItem())->find($oi['so_item_id']);
|
||||
if ($si) {
|
||||
$back = max(0, (int)$si['delivered_qty'] - (int)$oi['qty']);
|
||||
(new SalesOrderItem())->update($si['id'], ['delivered_qty' => $back]);
|
||||
}
|
||||
}
|
||||
}
|
||||
(new OutboundItem())->deleteRaw('ob_id', $obId);
|
||||
}
|
||||
|
||||
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[] = [
|
||||
'so_item_id' => (int)($row['so_item_id'] ?? 0),
|
||||
'product_id' => (int)($row['product_id'] ?? 0),
|
||||
'name' => $name,
|
||||
'spec' => trim((string)($row['spec'] ?? '')),
|
||||
'unit' => trim((string)($row['unit'] ?? '')),
|
||||
'qty' => $qty,
|
||||
'price' => $price,
|
||||
'amount' => round($qty * $price, 2),
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user