优化采购单
This commit is contained in:
@@ -16,8 +16,7 @@ use Maatwebsite\Excel\Concerns\WithStyles;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
|
||||
/**
|
||||
* 采购单导出(C2 全品类按分类 sort 排序 / C3 仅蔬果分类)
|
||||
* 数据源为订货明细按商品聚合(无独立采购明细表),每门店一列显示数量
|
||||
* 采购单导出
|
||||
*/
|
||||
class PurchaseOrderExport implements FromCollection, WithHeadings, WithMapping, WithStyles
|
||||
{
|
||||
@@ -135,7 +134,7 @@ class PurchaseOrderExport implements FromCollection, WithHeadings, WithMapping,
|
||||
|
||||
return array_merge(
|
||||
['序号', '分类', '品名', '供应商', '包规', '单位', '成本', '单价', '数量', '实际称重', '金额'],
|
||||
array_map(static fn (string $name): string => $name . '(数量)', array_values($this->storeNames)),
|
||||
array_values($this->storeNames),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class StoreOrderController extends BaseController
|
||||
$query = StoreOrderModel::query()->with([
|
||||
'store:id,name,address,contact,phone',
|
||||
'items:id,order_id,product_id,product_name,product_spec,unit,price,quantity,amount,image_ids',
|
||||
'purchase:id,purchase_no,purchase_date,status'
|
||||
]);
|
||||
|
||||
// 按包含的商品名称搜索:任一明细品名包含关键字即命中
|
||||
|
||||
@@ -26,8 +26,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 采购单管理(C1 汇总生成 / C2-C3 导出 / C4 修改)
|
||||
* 采购单无独立明细表,明细直接溯源门店订货明细(store_order_item.purchase_id)
|
||||
* 采购单管理
|
||||
*/
|
||||
#[RequestAttribute('/purchase/order', 'purchase.order')]
|
||||
class PurchaseOrderController extends BaseController
|
||||
@@ -144,26 +143,61 @@ class PurchaseOrderController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
/** C4 修改采购单头信息(采购日期、状态、备注) */
|
||||
/** 修改采购单信息(采购日期、实际金额、备注) */
|
||||
#[PutRoute(route: '/{id}', authorize: 'update', where: ['id' => '[0-9]+'])]
|
||||
public function update(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'purchase_date' => 'nullable|date_format:Y-m-d',
|
||||
'status' => 'nullable|integer|in:' . PurchaseOrderModel::STATUS_PENDING . ',' . PurchaseOrderModel::STATUS_COMPLETED,
|
||||
'actual_amount' => 'required|numeric|min:0',
|
||||
'remark' => 'nullable|string|max:255',
|
||||
], [
|
||||
'purchase_date.date_format' => '采购日期格式为 Y-m-d',
|
||||
'status.in' => '采购单状态不正确',
|
||||
'actual_amount.required' => '实际金额不能为空',
|
||||
'actual_amount.numeric' => '实际金额格式错误',
|
||||
'actual_amount.min' => '实际金额不能小于0',
|
||||
'remark.max' => '备注超过最大长度',
|
||||
]);
|
||||
$purchase = PurchaseOrderModel::find($id);
|
||||
if (empty($purchase)) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
if ($purchase->status !== PurchaseOrderModel::STATUS_PENDING) {
|
||||
throw new RepositoryException('采购单已完成,不允许修改信息');
|
||||
}
|
||||
$purchase->update(array_filter($data, static fn ($v) => $v !== null));
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成采购单
|
||||
* @throws
|
||||
*/
|
||||
#[PutRoute(route: '/{id}/finish', authorize: 'update', where: ['id' => '[0-9]+'])]
|
||||
public function finish(int $id): JsonResponse
|
||||
{
|
||||
$purchase = PurchaseOrderModel::find($id);
|
||||
if (empty($purchase)) {
|
||||
throw new RepositoryException('采购单不存在');
|
||||
}
|
||||
if ($purchase->status !== PurchaseOrderModel::STATUS_PENDING) {
|
||||
throw new RepositoryException('采购单已完成,不允许修改信息');
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($purchase) {
|
||||
// 修改采购单状态
|
||||
$purchase->status = PurchaseOrderModel::STATUS_COMPLETED;
|
||||
$purchase->save();
|
||||
// 修改所有订单状态为配送中
|
||||
StoreOrderModel::where('purchase_id', $purchase->id)->update([
|
||||
'status' => StoreOrderModel::STATUS_DISTRIBUTION
|
||||
]);
|
||||
// 生成并发送账单
|
||||
|
||||
return $this->success();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成采购单
|
||||
*/
|
||||
@@ -194,7 +228,9 @@ class PurchaseOrderController extends BaseController
|
||||
}
|
||||
|
||||
/**
|
||||
* C2/C3 导出采购单(Excel 表格):?type=all 全品类 / category 仅蔬果分类
|
||||
* 导出采购单 Excel 表格
|
||||
*
|
||||
* @throws
|
||||
*/
|
||||
#[GetRoute(route: '/{id}/export', authorize: 'export', where: ['id' => '[0-9]+'])]
|
||||
public function export(int $id, Request $request): Response
|
||||
@@ -267,8 +303,6 @@ class PurchaseOrderController extends BaseController
|
||||
'amount' => (string) $item->amount,
|
||||
'remark' => (string) $item->remark,
|
||||
'image_ids' => $item->image_ids,
|
||||
'editable' => $purchase->status === PurchaseOrderModel::STATUS_PENDING
|
||||
&& in_array((int) $item->order_status, StoreOrderItemModel::ITEM_EDITABLE_STATUS, true),
|
||||
];
|
||||
}
|
||||
app(ItemImageResolver::class)->resolve($rows);
|
||||
|
||||
@@ -43,6 +43,7 @@ class PurchaseOrderModel extends Model
|
||||
'estimate_amount' => 'decimal:2',
|
||||
'actual_amount' => 'decimal:2',
|
||||
'operator_id' => 'integer',
|
||||
'created_at' => 'datetime:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -88,4 +88,12 @@ class StoreOrderModel extends Model
|
||||
{
|
||||
return $this->hasMany(StoreOrderItemModel::class, 'order_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 采购单
|
||||
*/
|
||||
public function purchase(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(PurchaseOrderModel::class, 'purchase_id', 'id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ export interface IPurchaseCellItem {
|
||||
remark: string;
|
||||
/** 首图 */
|
||||
image?: string;
|
||||
/** 是否可编辑/同步(采购单进行中且订货单未锁定) */
|
||||
editable: boolean;
|
||||
}
|
||||
|
||||
/** 单元格下钻数据(门店 + 商品 + 全部订货明细) */
|
||||
|
||||
@@ -73,6 +73,12 @@ export default interface IStoreOrder {
|
||||
remark?: string;
|
||||
items?: IStoreOrderItem[];
|
||||
created_at?: string;
|
||||
purchase?: {
|
||||
id: number;
|
||||
purchase_no: string;
|
||||
purchase_date: string;
|
||||
status: number;
|
||||
}
|
||||
}
|
||||
|
||||
export const STORE_ORDER_STATUS_MAP: Record<number, { text: string; color: string }> = {
|
||||
@@ -83,13 +89,3 @@ export const STORE_ORDER_STATUS_MAP: Record<number, { text: string; color: strin
|
||||
4: { text: '已完成', color: 'success' },
|
||||
9: { text: '已取消', color: 'error' },
|
||||
};
|
||||
|
||||
/** 待汇总预览行 */
|
||||
export interface IOrderSummaryRow {
|
||||
product_id: number;
|
||||
product_name: string;
|
||||
product_spec: string;
|
||||
unit: string;
|
||||
total_quantity: string;
|
||||
store_count: number;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import { getStoreOptions } from '@/api/customer/store.ts';
|
||||
import type IStore from '@/domain/iStore.ts';
|
||||
import AuthButton from '@/components/AuthButton';
|
||||
import { DeleteOutlined, SettingOutlined, UnorderedListOutlined } from '@ant-design/icons';
|
||||
import {PURCHASE_STATUS_MAP} from "@/domain/iPurchaseOrder.ts";
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
@@ -344,10 +345,28 @@ const StoreOrderPage: React.FC = () => {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '采购单ID',
|
||||
title: '采购单',
|
||||
dataIndex: 'purchase_id',
|
||||
valueType: 'digit',
|
||||
hideInForm: true,
|
||||
render: (_, record) => record.purchase ? (
|
||||
<Space orientation={'vertical'}>
|
||||
<div>
|
||||
<Text type={'secondary'}>采购单号:</Text>
|
||||
{record.purchase.purchase_no}
|
||||
</div>
|
||||
<div>
|
||||
<Text type={'secondary'}>采购单日期:</Text>
|
||||
{record.purchase.purchase_date}
|
||||
</div>
|
||||
<div>
|
||||
<Text type={'secondary'}>采购单状态:</Text>
|
||||
<Tag color={PURCHASE_STATUS_MAP[record.purchase.status ?? 0]?.color}>
|
||||
{PURCHASE_STATUS_MAP[record.purchase.status ?? 0]?.text}
|
||||
</Tag>
|
||||
</div>
|
||||
</Space>
|
||||
) : '-'
|
||||
},
|
||||
{
|
||||
title: '账单ID',
|
||||
|
||||
+139
-107
@@ -20,7 +20,7 @@ import {
|
||||
Tag,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { CheckOutlined, DownloadOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import {DownloadOutlined, EditOutlined, UnorderedListOutlined} from '@ant-design/icons';
|
||||
import type { TableProps } from 'antd';
|
||||
import XinTable from '@/components/XinTable';
|
||||
import type {
|
||||
@@ -242,15 +242,11 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleComplete = async () => {
|
||||
if (!detail) {
|
||||
return;
|
||||
}
|
||||
const handleComplete = async (id: number) => {
|
||||
setCompleting(true);
|
||||
try {
|
||||
await Update(`/purchase/order/${detail.purchase.id}`, { status: 3 });
|
||||
await Update(`/purchase/order/${id}/finish`, { status: 3 });
|
||||
message.success('采购单已标记完成');
|
||||
await loadDetail(detail.purchase.id!);
|
||||
await tableRef.current?.reload();
|
||||
} finally {
|
||||
setCompleting(false);
|
||||
@@ -310,16 +306,20 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
fixed: 'right',
|
||||
align: 'center',
|
||||
render: (_, row) => (
|
||||
<AuthButton auth="purchase.order.update">
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEdit(row)}
|
||||
>
|
||||
修改
|
||||
</Button>
|
||||
</AuthButton>
|
||||
<>
|
||||
{ detail?.purchase.status === 0 ? (
|
||||
<AuthButton auth="purchase.order.update">
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => openEdit(row)}
|
||||
>
|
||||
修改
|
||||
</Button>
|
||||
</AuthButton>
|
||||
) : '-' }
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
@@ -426,14 +426,16 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
dataIndex: 'purchase_no',
|
||||
valueType: 'text',
|
||||
hideInForm: true,
|
||||
align: 'center',
|
||||
width: 260,
|
||||
render: (_, record) => <Text copyable={{ text: record.purchase_no }}>{record.purchase_no}</Text>,
|
||||
},
|
||||
{
|
||||
title: '采购日期',
|
||||
dataIndex: 'purchase_date',
|
||||
valueType: 'dateRange',
|
||||
hideInForm: true,
|
||||
valueType: 'date',
|
||||
align: 'center',
|
||||
hideInForm: true,
|
||||
render: (_, record) => record.purchase_date,
|
||||
},
|
||||
{
|
||||
@@ -441,15 +443,20 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
dataIndex: 'estimate_amount',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'right',
|
||||
align: 'center',
|
||||
render: (_, record) => `¥${record.estimate_amount}`,
|
||||
},
|
||||
{
|
||||
title: '实际成本',
|
||||
dataIndex: 'actual_amount',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'right',
|
||||
valueType: "digit",
|
||||
fieldProps: {
|
||||
min: 0,
|
||||
precision: 3,
|
||||
placeholder: "请输入单价"
|
||||
},
|
||||
align: 'center',
|
||||
render: (_, record) =>
|
||||
Number(record.actual_amount) > 0 ? (
|
||||
<Text strong>¥{record.actual_amount}</Text>
|
||||
@@ -457,6 +464,28 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
<Text type="secondary">未录入</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '总件数',
|
||||
dataIndex: 'total_quantity',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
valueType: "textarea",
|
||||
hideInTable: true,
|
||||
hideInSearch: true
|
||||
},
|
||||
{
|
||||
title: '总重量',
|
||||
dataIndex: 'total_weight',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => `${record.total_weight}斤`,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
@@ -484,24 +513,18 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const operateRender: XinTableProps<IPurchaseOrder>['operateRender'] = (record) => [
|
||||
<Button key="detail" size="small" onClick={() => openDetail(record.id!)}>
|
||||
详情
|
||||
</Button>,
|
||||
<AuthButton key="export" auth="purchase.order.export">
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{ key: 'all', label: '导出全品类', onClick: () => exportPurchase(record.id!, 'all') },
|
||||
{ key: 'category', label: '仅导出蔬果分类', onClick: () => exportPurchase(record.id!, 'category') },
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Button size="small" type="primary" ghost icon={<DownloadOutlined />}>
|
||||
导出
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</AuthButton>,
|
||||
const operateRender: XinTableProps<IPurchaseOrder>['operateRender'] = (record, dom) => [
|
||||
record.status === 0 ? dom.edit : null,
|
||||
<Button key="detail" size="small" icon={<UnorderedListOutlined />} type={'primary'} onClick={() => openDetail(record.id!)} />,
|
||||
record.status === 0 ? (
|
||||
<AuthButton auth="purchase.order.update">
|
||||
<Popconfirm title="确认该采购单已完成?" description="已完成采购单将锁定,不能再修改订单信息" onConfirm={() => handleComplete(record.id!)}>
|
||||
<Button type={'primary'} size="small" variant={'solid'} color={'green'} loading={completing}>
|
||||
完成采购
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</AuthButton>
|
||||
) : null
|
||||
];
|
||||
|
||||
const tableProps: XinTableProps<IPurchaseOrder> = {
|
||||
@@ -511,6 +534,9 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
accessName: 'purchase.order',
|
||||
tableRef,
|
||||
operateRender,
|
||||
operateProps: {
|
||||
width: 300
|
||||
},
|
||||
formProps: false,
|
||||
};
|
||||
|
||||
@@ -531,19 +557,10 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
onClose={() => setDetailOpen(false)}
|
||||
size={1200}
|
||||
loading={detailLoading}
|
||||
extra={detail?.purchase.status === 0 && (
|
||||
<AuthButton auth="purchase.order.update">
|
||||
<Popconfirm title="确认标记该采购单为已完成?" onConfirm={handleComplete}>
|
||||
<Button size="small" icon={<CheckOutlined />} loading={completing}>
|
||||
标记完成
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</AuthButton>
|
||||
)}
|
||||
>
|
||||
{detail && (
|
||||
<>
|
||||
<Title level={5} className="mt-5! mb-3!">
|
||||
<Title level={5} className="mb-3!">
|
||||
采购单信息
|
||||
</Title>
|
||||
<Descriptions column={3} size="small" bordered>
|
||||
@@ -556,9 +573,12 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
<Descriptions.Item label="制单人">
|
||||
{detail.purchase.operator?.nickname ?? '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="预估金额">¥{detail.purchase.estimate_amount}</Descriptions.Item>
|
||||
<Descriptions.Item label="实际金额">¥{detail.purchase.actual_amount}</Descriptions.Item>
|
||||
<Descriptions.Item label="预估成本">¥{detail.purchase.estimate_amount}</Descriptions.Item>
|
||||
<Descriptions.Item label="实际成本">¥{detail.purchase.actual_amount}</Descriptions.Item>
|
||||
<Descriptions.Item label="总件数">{detail.purchase.total_quantity}</Descriptions.Item>
|
||||
<Descriptions.Item label="总重量">{detail.purchase.total_weight}</Descriptions.Item>
|
||||
<Descriptions.Item label="创建时间" span={2}>{detail.purchase.created_at}</Descriptions.Item>
|
||||
<Descriptions.Item label="备注" span={3}>{detail.purchase.remark}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Tabs
|
||||
@@ -566,65 +586,77 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
onChange={setDetailTab}
|
||||
className="mt-3!"
|
||||
items={[
|
||||
{
|
||||
key: 'items',
|
||||
label: '商品明细',
|
||||
children: (
|
||||
<Table<IPurchaseDetailRow>
|
||||
{ key: 'items', label: '商品明细' },
|
||||
{ key: 'stores', label: '门店购买详情' },
|
||||
]}
|
||||
/>
|
||||
|
||||
{ detailTab === 'stores' ? (
|
||||
<>
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<Text>选择门店:</Text>
|
||||
<Select
|
||||
value={storeId || undefined}
|
||||
onChange={(value) => setStoreId(value)}
|
||||
placeholder="选择门店"
|
||||
className="w-60!"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
options={detail.stores.map((s) => ({ value: s.id, label: s.name }))}
|
||||
/>
|
||||
</div>
|
||||
<Spin spinning={storeLoading}>
|
||||
{storeSummary && storeSummary.items.length > 0 ? (
|
||||
<Table<IPurchaseStoreItem>
|
||||
rowKey="product_id"
|
||||
size="small"
|
||||
bordered
|
||||
columns={buildItemColumns()}
|
||||
dataSource={detail.items}
|
||||
columns={storeColumns}
|
||||
dataSource={storeSummary.items}
|
||||
pagination={false}
|
||||
scroll={{ x: 'max-content' }}
|
||||
summary={renderSummary}
|
||||
summary={renderStoreTotal}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'stores',
|
||||
label: '门店购买详情',
|
||||
children: (
|
||||
<>
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<Text>选择门店:</Text>
|
||||
<Select
|
||||
value={storeId || undefined}
|
||||
onChange={(value) => setStoreId(value)}
|
||||
placeholder="选择门店"
|
||||
className="w-60!"
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
options={detail.stores.map((s) => ({ value: s.id, label: s.name }))}
|
||||
/>
|
||||
</div>
|
||||
<Spin spinning={storeLoading}>
|
||||
{storeSummary && storeSummary.items.length > 0 ? (
|
||||
<Table<IPurchaseStoreItem>
|
||||
rowKey="product_id"
|
||||
size="small"
|
||||
bordered
|
||||
columns={storeColumns}
|
||||
dataSource={storeSummary.items}
|
||||
pagination={false}
|
||||
summary={renderStoreTotal}
|
||||
/>
|
||||
) : (
|
||||
!storeLoading && (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="该门店在此采购单中无采购商品"
|
||||
className="py-8!"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Spin>
|
||||
</>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : (
|
||||
!storeLoading && (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="该门店在此采购单中无采购商品"
|
||||
className="py-8!"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Spin>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Space style={{ marginBottom: 20 }}>
|
||||
<AuthButton key="export" auth="purchase.order.export">
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{ key: 'all', label: '导出全品类', onClick: () => detail && exportPurchase(detail.purchase.id!, 'all') },
|
||||
{ key: 'category', label: '仅导出蔬果分类', onClick: () => detail && exportPurchase(detail.purchase.id!, 'category') },
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Button type="primary" ghost icon={<DownloadOutlined />}>
|
||||
导出
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</AuthButton>
|
||||
</Space>
|
||||
<Table<IPurchaseDetailRow>
|
||||
rowKey="product_id"
|
||||
size="small"
|
||||
bordered
|
||||
columns={buildItemColumns()}
|
||||
dataSource={detail.items}
|
||||
pagination={false}
|
||||
scroll={{ x: 'max-content' }}
|
||||
summary={renderSummary}
|
||||
/>
|
||||
</>
|
||||
) }
|
||||
</>
|
||||
)}
|
||||
</Drawer>
|
||||
@@ -766,7 +798,7 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
{item.weight ?? '-'} 斤
|
||||
</div>
|
||||
<div className="w-40 shrink-0 text-center">
|
||||
{item.editable ? (
|
||||
{detail?.purchase.status === 0 ? (
|
||||
<Space size={0}>
|
||||
<AuthButton auth="purchase.order.update">
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user