update ourchase

This commit is contained in:
kae 2026-05-24 20:05:28 +09:00
parent 4626bf2771
commit 4ba4dc98bf
9 changed files with 844 additions and 0 deletions

View File

@ -65,6 +65,19 @@ export default defineConfig({
{ name: '库存盘点', path: '/inventory/checks', component: './Inventory/Checks' }, { name: '库存盘点', path: '/inventory/checks', component: './Inventory/Checks' },
{ name: '库存调整', path: '/inventory/adjusts', component: './Inventory/Adjusts' }, { name: '库存调整', path: '/inventory/adjusts', component: './Inventory/Adjusts' },
{ name: 'Excel导入', path: '/inventory/import', component: './Inventory/Import' }, { name: 'Excel导入', path: '/inventory/import', component: './Inventory/Import' },
{ name: '库存变动记录', path: '/inventory/logs', component: './Inventory/Logs' },
],
},
{
name: '采购管理',
path: '/purchase',
icon: 'ShoppingCartOutlined',
routes: [
{ name: '供应商管理', path: '/purchase/suppliers', component: './Purchase/Suppliers' },
{ name: '采购订单', path: '/purchase/orders', component: './Purchase/Orders' },
{ name: '采购订单详情', path: '/purchase/orders/:id', component: './Purchase/Orders/detail', hideInMenu: true },
{ name: '入库记录', path: '/purchase/receipts', component: './Purchase/Receipts' },
{ name: '采购报表', path: '/purchase/stats', component: './Purchase/Stats' },
], ],
}, },
{ {

View File

@ -30,7 +30,11 @@ const LoginPage: React.FC = () => {
value: t.id, value: t.id,
})), })),
); );
} else {
message.error('获取公司列表失败,请稍后重试');
} }
} catch {
message.error('获取公司列表失败,请检查网络连接');
} finally { } finally {
setSearching(false); setSearching(false);
} }

View File

@ -0,0 +1,211 @@
import { ArrowLeftOutlined, PlusOutlined } from '@ant-design/icons';
import { ProDescriptions, ProTable } from '@ant-design/pro-components';
import { Button, Card, Col, Descriptions, Form, Input, Modal, Row, Space, Tag, Typography, message } from 'antd';
import { useEffect, useState } from 'react';
import { history, useParams } from '@umijs/max';
import {
confirmPurchaseOrder,
createPurchasePayment,
createPurchaseReceipt,
getPurchaseOrder,
listPurchasePayments,
listPurchaseReceipts,
} from '@/services/api';
import type { PurchaseOrder, PurchasePayment, PurchaseReceipt } from '@/types/api';
const STATUS_LABELS: Record<number, string> = { 0: '草稿', 1: '已确认', 2: '已取消' };
const RECEIPT_STATUS_LABELS: Record<number, string> = { 0: '未入库', 1: '部分入库', 2: '已入库' };
const PAYMENT_STATUS_LABELS: Record<number, string> = { 0: '未付款', 1: '部分付款', 2: '已付清' };
const OrderDetailPage: React.FC = () => {
const { id } = useParams<{ id: string }>();
const [order, setOrder] = useState<PurchaseOrder | null>(null);
const [receipts, setReceipts] = useState<PurchaseReceipt[]>([]);
const [payments, setPayments] = useState<PurchasePayment[]>([]);
const [receiptModalOpen, setReceiptModalOpen] = useState(false);
const [paymentModalOpen, setPaymentModalOpen] = useState(false);
const [receiptForm] = Form.useForm();
const [paymentForm] = Form.useForm();
const loadOrder = async () => {
if (!id) return;
const [orderRes, receiptRes, paymentRes] = await Promise.all([
getPurchaseOrder(id),
listPurchaseReceipts({ orderId: id, pageSize: 100 }),
listPurchasePayments({ orderId: id, pageSize: 100 }),
]);
if (orderRes?.code === 0) setOrder(orderRes.data);
if (receiptRes?.code === 0) setReceipts(receiptRes.data?.list || []);
if (paymentRes?.code === 0) setPayments(paymentRes.data?.list || []);
};
useEffect(() => { loadOrder(); }, [id]);
const handleConfirmOrder = async () => {
await confirmPurchaseOrder(id!);
message.success('订单已确认');
loadOrder();
};
const handleCreateReceipt = async () => {
if (!order) return;
const values = await receiptForm.validateFields();
const details = order.details.map((d) => ({
orderDetailId: d.detailId,
productId: d.productId,
actualQty: values[`qty_${d.detailId}`] || '0',
unitCost: d.unitPrice,
}));
await createPurchaseReceipt({ orderId: id!, receiptDate: values.receiptDate, receivedBy: values.receivedBy, details });
message.success('入库单已创建');
setReceiptModalOpen(false);
loadOrder();
};
const handleCreatePayment = async () => {
const values = await paymentForm.validateFields();
await createPurchasePayment({ orderId: id!, ...values });
message.success('付款记录已创建');
setPaymentModalOpen(false);
loadOrder();
};
if (!order) return null;
return (
<Space direction="vertical" style={{ width: '100%' }} size="large">
<Space>
<Button icon={<ArrowLeftOutlined />} onClick={() => history.back()}></Button>
<Typography.Title level={4} style={{ margin: 0 }}> {order.orderNo}</Typography.Title>
</Space>
<Card>
<Descriptions column={3} bordered size="small">
<Descriptions.Item label="供应商">{order.supplierName}</Descriptions.Item>
<Descriptions.Item label="采购日期">{order.orderDate}</Descriptions.Item>
<Descriptions.Item label="采购员">{order.purchaseBy}</Descriptions.Item>
<Descriptions.Item label="合同金额">¥ {order.contractAmount}</Descriptions.Item>
<Descriptions.Item label="已入库数量">{order.receivedQty}</Descriptions.Item>
<Descriptions.Item label="已付款">¥ {order.paidAmount}</Descriptions.Item>
<Descriptions.Item label="入库状态">{RECEIPT_STATUS_LABELS[order.receiptStatus]}</Descriptions.Item>
<Descriptions.Item label="付款状态">{PAYMENT_STATUS_LABELS[order.paymentStatus]}</Descriptions.Item>
<Descriptions.Item label="订单状态">
<Tag color={order.status === 1 ? 'processing' : order.status === 2 ? 'error' : 'default'}>
{STATUS_LABELS[order.status]}
</Tag>
</Descriptions.Item>
{order.remark && <Descriptions.Item label="备注" span={3}>{order.remark}</Descriptions.Item>}
</Descriptions>
{order.status === 0 && (
<Button type="primary" style={{ marginTop: 16 }} onClick={() => Modal.confirm({ title: '确认此采购订单?', onOk: handleConfirmOrder })}>
</Button>
)}
</Card>
<Card title="采购明细">
<ProTable
headerTitle={false}
rowKey="detailId"
search={false}
pagination={false}
dataSource={order.details}
columns={[
{ title: '产品', dataIndex: 'productName' },
{ title: '规格', dataIndex: 'spec' },
{ title: '颜色', dataIndex: 'color' },
{ title: '采购数量', dataIndex: 'quantity' },
{ title: '单价', dataIndex: 'unitPrice' },
{ title: '小计', dataIndex: 'amount' },
{ title: '已入库', dataIndex: 'receivedQty' },
]}
/>
</Card>
<Card
title="入库记录"
extra={order.status === 1 && order.receiptStatus !== 2 && (
<Button icon={<PlusOutlined />} onClick={() => { receiptForm.resetFields(); setReceiptModalOpen(true); }}></Button>
)}
>
<ProTable
headerTitle={false}
rowKey="receiptId"
search={false}
pagination={false}
dataSource={receipts}
columns={[
{ title: '入库单号', dataIndex: 'receiptNo' },
{ title: '入库日期', dataIndex: 'receiptDate' },
{ title: '入库人', dataIndex: 'receivedBy' },
{ title: '状态', dataIndex: 'status', render: (_, r) => r.status === 1 ? <Tag color="success"></Tag> : <Tag>稿</Tag> },
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime' },
]}
/>
</Card>
<Card
title="付款记录"
extra={order.status === 1 && order.paymentStatus !== 2 && (
<Button icon={<PlusOutlined />} onClick={() => { paymentForm.resetFields(); setPaymentModalOpen(true); }}></Button>
)}
>
<ProTable
headerTitle={false}
rowKey="paymentId"
search={false}
pagination={false}
dataSource={payments}
columns={[
{ title: '付款日期', dataIndex: 'paymentDate' },
{ title: '金额', dataIndex: 'amount', render: (v) => `¥ ${v}` },
{ title: '付款方式', dataIndex: 'paymentMethod' },
{ title: '操作人', dataIndex: 'operator' },
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime' },
]}
/>
</Card>
<Modal title="新建入库单" open={receiptModalOpen} onOk={handleCreateReceipt} onCancel={() => setReceiptModalOpen(false)} width={600} destroyOnClose>
<Form form={receiptForm} layout="vertical">
<Row gutter={16}>
<Col span={12}>
<Form.Item name="receiptDate" label="入库日期" rules={[{ required: true }]}>
<Input type="date" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name="receivedBy" label="入库人">
<Input />
</Form.Item>
</Col>
</Row>
{order.details.map((d) => (
<Form.Item key={d.detailId} name={`qty_${d.detailId}`} label={`${d.productName} ${d.spec} ${d.color} 入库数量(采购数量: ${d.quantity}`}>
<Input type="number" min={0} />
</Form.Item>
))}
</Form>
</Modal>
<Modal title="新增付款" open={paymentModalOpen} onOk={handleCreatePayment} onCancel={() => setPaymentModalOpen(false)} destroyOnClose>
<Form form={paymentForm} layout="vertical">
<Form.Item name="paymentDate" label="付款日期" rules={[{ required: true }]}>
<Input type="date" />
</Form.Item>
<Form.Item name="amount" label="金额" rules={[{ required: true }]}>
<Input type="number" min={0} step="0.01" />
</Form.Item>
<Form.Item name="paymentMethod" label="付款方式">
<Input placeholder="现金/银行转账/微信/支付宝" />
</Form.Item>
<Form.Item name="remark" label="备注">
<Input.TextArea rows={2} />
</Form.Item>
</Form>
</Modal>
</Space>
);
};
export default OrderDetailPage;

View File

@ -0,0 +1,101 @@
import { PlusOutlined } from '@ant-design/icons';
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
import { Button, Modal, Tag, message } from 'antd';
import { useRef } from 'react';
import { history } from '@umijs/max';
import { cancelPurchaseOrder, confirmPurchaseOrder, listPurchaseOrders } from '@/services/api';
import type { PurchaseOrder } from '@/types/api';
const STATUS_MAP: Record<number, { text: string; color: string }> = {
0: { text: '草稿', color: 'default' },
1: { text: '已确认', color: 'processing' },
2: { text: '已取消', color: 'error' },
};
const RECEIPT_STATUS_MAP: Record<number, string> = {
0: '未入库',
1: '部分入库',
2: '已入库',
};
const PAYMENT_STATUS_MAP: Record<number, string> = {
0: '未付款',
1: '部分付款',
2: '已付清',
};
const OrdersPage: React.FC = () => {
const actionRef = useRef<ActionType>();
const handleConfirm = async (id: string) => {
await confirmPurchaseOrder(id);
message.success('确认成功');
actionRef.current?.reload();
};
const handleCancel = async (id: string) => {
await cancelPurchaseOrder(id);
message.success('取消成功');
actionRef.current?.reload();
};
const columns: ProColumns<PurchaseOrder>[] = [
{ title: '订单编号', dataIndex: 'orderNo', copyable: true },
{ title: '供应商', dataIndex: 'supplierName', search: false },
{ title: '采购日期', dataIndex: 'orderDate', search: false },
{ title: '合同金额', dataIndex: 'contractAmount', search: false },
{
title: '入库状态',
dataIndex: 'receiptStatus',
search: false,
render: (_, r) => RECEIPT_STATUS_MAP[r.receiptStatus] ?? '-',
},
{
title: '付款状态',
dataIndex: 'paymentStatus',
search: false,
render: (_, r) => PAYMENT_STATUS_MAP[r.paymentStatus] ?? '-',
},
{
title: '状态',
dataIndex: 'status',
search: false,
render: (_, r) => {
const s = STATUS_MAP[r.status] ?? STATUS_MAP[0];
return <Tag color={s.color}>{s.text}</Tag>;
},
},
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime', search: false },
{
title: '操作',
valueType: 'option',
render: (_, r) => [
<a key="view" onClick={() => history.push(`/purchase/orders/${r.orderId}`)}></a>,
r.status === 0 && (
<a key="confirm" onClick={() => Modal.confirm({ title: '确认采购订单?', onOk: () => handleConfirm(r.orderId) })}></a>
),
r.status === 1 && r.receiptStatus === 0 && (
<a key="cancel" style={{ color: '#ff4d4f' }} onClick={() => Modal.confirm({ title: '确认取消?', onOk: () => handleCancel(r.orderId) })}></a>
),
].filter(Boolean),
},
];
return (
<ProTable<PurchaseOrder>
actionRef={actionRef}
headerTitle="采购订单"
rowKey="orderId"
columns={columns}
request={async (params) => {
const res = await listPurchaseOrders({ page: params.current, pageSize: params.pageSize });
return { data: res?.data?.list || [], total: res?.data?.total || 0, success: res?.code === 0 };
}}
toolBarRender={() => [
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={() => history.push('/purchase/orders/new')}></Button>,
]}
/>
);
};
export default OrdersPage;

View File

@ -0,0 +1,55 @@
import { ProColumns, ProTable } from '@ant-design/pro-components';
import { Modal, Tag, message } from 'antd';
import { confirmPurchaseReceipt, listPurchaseReceipts } from '@/services/api';
import type { PurchaseReceipt } from '@/types/api';
import { ActionType } from '@ant-design/pro-components';
import { useRef } from 'react';
const ReceiptsPage: React.FC = () => {
const actionRef = useRef<ActionType>();
const handleConfirm = async (id: string) => {
await confirmPurchaseReceipt(id);
message.success('入库单已确认');
actionRef.current?.reload();
};
const columns: ProColumns<PurchaseReceipt>[] = [
{ title: '入库单号', dataIndex: 'receiptNo', copyable: true },
{ title: '采购订单ID', dataIndex: 'orderId', search: false },
{ title: '入库日期', dataIndex: 'receiptDate', search: false },
{ title: '入库人', dataIndex: 'receivedBy', search: false },
{
title: '状态',
dataIndex: 'status',
search: false,
render: (_, r) => r.status === 1 ? <Tag color="success"></Tag> : <Tag>稿</Tag>,
},
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime', search: false },
{
title: '操作',
valueType: 'option',
render: (_, r) => [
r.status === 0 && (
<a key="confirm" onClick={() => Modal.confirm({ title: '确认入库单? 确认后将写入库存变动记录。', onOk: () => handleConfirm(r.receiptId) })}></a>
),
].filter(Boolean),
},
];
return (
<ProTable<PurchaseReceipt>
actionRef={actionRef}
headerTitle="入库记录"
rowKey="receiptId"
columns={columns}
search={false}
request={async (params) => {
const res = await listPurchaseReceipts({ page: params.current, pageSize: params.pageSize });
return { data: res?.data?.list || [], total: res?.data?.total || 0, success: res?.code === 0 };
}}
/>
);
};
export default ReceiptsPage;

View File

@ -0,0 +1,112 @@
import { ProTable } from '@ant-design/pro-components';
import { Card, Col, DatePicker, Row, Statistic, Typography } from 'antd';
import { useEffect, useState } from 'react';
import {
getPurchaseStatsByProduct,
getPurchaseStatsBySupplier,
getPurchaseStatsSummary,
} from '@/services/api';
import type { PurchaseStatsByProductItem, PurchaseStatsBySupplierItem, PurchaseStatsSummary } from '@/types/api';
const { RangePicker } = DatePicker;
const StatsPage: React.FC = () => {
const [summary, setSummary] = useState<PurchaseStatsSummary | null>(null);
const [bySupplier, setBySupplier] = useState<PurchaseStatsBySupplierItem[]>([]);
const [byProduct, setByProduct] = useState<PurchaseStatsByProductItem[]>([]);
const [dateRange, setDateRange] = useState<[string, string]>(['', '']);
const loadData = async (startDate = '', endDate = '') => {
const [sumRes, supRes, proRes] = await Promise.all([
getPurchaseStatsSummary({ startDate, endDate }),
getPurchaseStatsBySupplier({ startDate, endDate }),
getPurchaseStatsByProduct({ startDate, endDate }),
]);
if (sumRes?.code === 0) setSummary(sumRes.data);
if (supRes?.code === 0) setBySupplier(supRes.data?.list || []);
if (proRes?.code === 0) setByProduct(proRes.data?.list || []);
};
useEffect(() => { loadData(); }, []);
return (
<div>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={24}>
<Typography.Title level={4}></Typography.Title>
<RangePicker
onChange={(_, dateStrings) => {
const [start, end] = dateStrings;
setDateRange([start, end]);
loadData(start, end);
}}
/>
</Col>
</Row>
<Row gutter={16} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card>
<Statistic title="采购订单数" value={summary?.totalOrders ?? 0} />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="采购总额" value={summary?.totalAmount ?? '0.00'} prefix="¥" />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="已付款" value={summary?.totalPaidAmount ?? '0.00'} prefix="¥" />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="未付款" value={summary?.unpaidAmount ?? '0.00'} prefix="¥" valueStyle={{ color: '#cf1322' }} />
</Card>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Card title="按供应商统计">
<ProTable<PurchaseStatsBySupplierItem>
headerTitle={false}
rowKey="supplierId"
search={false}
pagination={false}
dataSource={bySupplier}
columns={[
{ title: '供应商', dataIndex: 'supplierName' },
{ title: '订单数', dataIndex: 'orderCount' },
{ title: '采购总额', dataIndex: 'totalAmount', render: (v) => `¥ ${v}` },
{ title: '已付款', dataIndex: 'paidAmount', render: (v) => `¥ ${v}` },
]}
/>
</Card>
</Col>
<Col span={12}>
<Card title="按产品统计">
<ProTable<PurchaseStatsByProductItem>
headerTitle={false}
rowKey="productId"
search={false}
pagination={false}
dataSource={byProduct}
columns={[
{ title: '产品', dataIndex: 'productName' },
{ title: '规格', dataIndex: 'spec' },
{ title: '颜色', dataIndex: 'color' },
{ title: '采购数量', dataIndex: 'totalQty' },
{ title: '均价', dataIndex: 'avgUnitPrice', render: (v) => `¥ ${v}` },
{ title: '采购额', dataIndex: 'totalAmount', render: (v) => `¥ ${v}` },
]}
/>
</Card>
</Col>
</Row>
</div>
);
};
export default StatsPage;

View File

@ -0,0 +1,124 @@
import { PlusOutlined } from '@ant-design/icons';
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
import { Button, Form, Input, Modal, Space, Tag, message } from 'antd';
import { useRef, useState } from 'react';
import { createSupplier, deleteSupplier, listSuppliers, updateSupplier } from '@/services/api';
import type { Supplier } from '@/types/api';
const STATUS_MAP: Record<number, { text: string; color: string }> = {
0: { text: '停用', color: 'default' },
1: { text: '正常', color: 'success' },
};
const SuppliersPage: React.FC = () => {
const actionRef = useRef<ActionType>();
const [form] = Form.useForm();
const [modalOpen, setModalOpen] = useState(false);
const [editing, setEditing] = useState<Supplier | null>(null);
const openCreate = () => {
setEditing(null);
form.resetFields();
setModalOpen(true);
};
const openEdit = (record: Supplier) => {
setEditing(record);
form.setFieldsValue({ supplierName: record.supplierName, phone: record.phone, remark: record.remark });
setModalOpen(true);
};
const handleSubmit = async () => {
const values = await form.validateFields();
if (editing) {
await updateSupplier(editing.supplierId, values);
message.success('更新成功');
} else {
await createSupplier(values);
message.success('创建成功');
}
setModalOpen(false);
actionRef.current?.reload();
};
const handleDelete = async (id: string) => {
await deleteSupplier(id);
message.success('删除成功');
actionRef.current?.reload();
};
const handleToggleStatus = async (record: Supplier) => {
await updateSupplier(record.supplierId, { status: record.status === 1 ? 0 : 1 });
message.success('状态已更新');
actionRef.current?.reload();
};
const columns: ProColumns<Supplier>[] = [
{ title: '供应商名称', dataIndex: 'supplierName' },
{ title: '联系电话', dataIndex: 'phone', search: false },
{ title: '采购次数', dataIndex: 'purchaseCount', search: false },
{ title: '累计入库数量', dataIndex: 'deliveredQty', search: false },
{ title: '应结金额', dataIndex: 'payableAmount', search: false },
{ title: '实结金额', dataIndex: 'paidAmount', search: false },
{
title: '状态',
dataIndex: 'status',
search: false,
render: (_, r) => {
const s = STATUS_MAP[r.status] ?? STATUS_MAP[1];
return <Tag color={s.color}>{s.text}</Tag>;
},
},
{ title: '创建时间', dataIndex: 'createdAt', valueType: 'dateTime', search: false },
{
title: '操作',
valueType: 'option',
render: (_, r) => [
<a key="edit" onClick={() => openEdit(r)}></a>,
<a key="toggle" onClick={() => handleToggleStatus(r)}>
{r.status === 1 ? '停用' : '启用'}
</a>,
<a key="del" style={{ color: '#ff4d4f' }} onClick={() => Modal.confirm({ title: '确认删除?', onOk: () => handleDelete(r.supplierId) })}></a>,
],
},
];
return (
<>
<ProTable<Supplier>
actionRef={actionRef}
headerTitle="供应商管理"
rowKey="supplierId"
columns={columns}
request={async (params) => {
const res = await listSuppliers({ page: params.current, pageSize: params.pageSize, supplierName: params.supplierName });
return { data: res?.data?.list || [], total: res?.data?.total || 0, success: res?.code === 0 };
}}
toolBarRender={() => [
<Button key="add" type="primary" icon={<PlusOutlined />} onClick={openCreate}></Button>,
]}
/>
<Modal
title={editing ? '编辑供应商' : '新增供应商'}
open={modalOpen}
onOk={handleSubmit}
onCancel={() => setModalOpen(false)}
destroyOnClose
>
<Form form={form} layout="vertical">
<Form.Item name="supplierName" label="供应商名称" rules={[{ required: true, message: '请输入供应商名称' }]}>
<Input />
</Form.Item>
<Form.Item name="phone" label="联系电话">
<Input />
</Form.Item>
<Form.Item name="remark" label="备注">
<Input.TextArea rows={2} />
</Form.Item>
</Form>
</Modal>
</>
);
};
export default SuppliersPage;

View File

@ -4,6 +4,7 @@ import type {
BoltInfo, BoltInfo,
ColorDetailItem, ColorDetailItem,
CurrentUser, CurrentUser,
InventoryLog,
LoginParams, LoginParams,
LoginResult, LoginResult,
Menu, Menu,
@ -15,6 +16,12 @@ import type {
Product, Product,
ProductSummaryItem, ProductSummaryItem,
ProductTree, ProductTree,
PurchaseOrder,
PurchasePayment,
PurchaseReceipt,
PurchaseStatsByProductItem,
PurchaseStatsBySupplierItem,
PurchaseStatsSummary,
Relation, Relation,
RelationHistory, RelationHistory,
Role, Role,
@ -22,6 +29,7 @@ import type {
StockCheck, StockCheck,
StockGroup, StockGroup,
StockSummary, StockSummary,
Supplier,
SystemConfig, SystemConfig,
User, User,
} from '@/types/api'; } from '@/types/api';
@ -234,3 +242,96 @@ export const updateRelation = (id: string, data: { status: number; validFrom?: s
export const listRelationHistory = (params?: PaginationParams) => export const listRelationHistory = (params?: PaginationParams) =>
request<ApiResponse<PaginatedData<RelationHistory>>>('/api/v1/crm/relationships/history', { method: 'GET', params }); request<ApiResponse<PaginatedData<RelationHistory>>>('/api/v1/crm/relationships/history', { method: 'GET', params });
// ── Suppliers ─────────────────────────────────────
export const listSuppliers = (params: PaginationParams & { supplierName?: string; status?: number }) =>
request<ApiResponse<PaginatedData<Supplier>>>('/api/v1/purchase/suppliers', { method: 'GET', params });
export const getSupplier = (id: string) =>
request<ApiResponse<Supplier>>(`/api/v1/purchase/suppliers/${id}`, { method: 'GET' });
export const createSupplier = (data: { supplierName: string; phone?: string; remark?: string }) =>
request<ApiResponse<{ id: string }>>('/api/v1/purchase/suppliers', { method: 'POST', data });
export const updateSupplier = (id: string, data: { supplierName?: string; phone?: string; status?: number; remark?: string }) =>
request<ApiResponse>(`/api/v1/purchase/suppliers/${id}`, { method: 'PUT', data });
export const deleteSupplier = (id: string) =>
request<ApiResponse>(`/api/v1/purchase/suppliers/${id}`, { method: 'DELETE' });
// ── Purchase Orders ───────────────────────────────
export const listPurchaseOrders = (params: PaginationParams & { supplierId?: string; status?: number; startDate?: string; endDate?: string }) =>
request<ApiResponse<PaginatedData<PurchaseOrder>>>('/api/v1/purchase/orders', { method: 'GET', params });
export const getPurchaseOrder = (id: string) =>
request<ApiResponse<PurchaseOrder>>(`/api/v1/purchase/orders/${id}`, { method: 'GET' });
export const createPurchaseOrder = (data: {
supplierId: string;
orderDate: string;
purchaseBy?: string;
remark?: string;
details: Array<{ productId: string; productName: string; spec?: string; color?: string; quantity: string; unitPrice: string; remark?: string }>;
}) =>
request<ApiResponse<{ id: string }>>('/api/v1/purchase/orders', { method: 'POST', data });
export const updatePurchaseOrder = (id: string, data: object) =>
request<ApiResponse>(`/api/v1/purchase/orders/${id}`, { method: 'PUT', data });
export const confirmPurchaseOrder = (id: string) =>
request<ApiResponse>(`/api/v1/purchase/orders/${id}/confirm`, { method: 'POST' });
export const cancelPurchaseOrder = (id: string) =>
request<ApiResponse>(`/api/v1/purchase/orders/${id}/cancel`, { method: 'POST' });
// ── Purchase Receipts ─────────────────────────────
export const listPurchaseReceipts = (params: PaginationParams & { orderId?: string; status?: number; startDate?: string; endDate?: string }) =>
request<ApiResponse<PaginatedData<PurchaseReceipt>>>('/api/v1/purchase/receipts', { method: 'GET', params });
export const getPurchaseReceipt = (id: string) =>
request<ApiResponse<PurchaseReceipt>>(`/api/v1/purchase/receipts/${id}`, { method: 'GET' });
export const createPurchaseReceipt = (data: {
orderId: string;
receiptDate: string;
receivedBy?: string;
remark?: string;
details: Array<{ orderDetailId: string; productId: string; actualQty: string; unitCost?: string; remark?: string }>;
}) =>
request<ApiResponse<{ id: string }>>('/api/v1/purchase/receipts', { method: 'POST', data });
export const confirmPurchaseReceipt = (id: string) =>
request<ApiResponse>(`/api/v1/purchase/receipts/${id}/confirm`, { method: 'POST' });
// ── Purchase Payments ─────────────────────────────
export const listPurchasePayments = (params: PaginationParams & { orderId?: string; startDate?: string; endDate?: string }) =>
request<ApiResponse<PaginatedData<PurchasePayment>>>('/api/v1/purchase/payments', { method: 'GET', params });
export const createPurchasePayment = (data: {
orderId: string;
paymentDate: string;
amount: string;
paymentMethod?: string;
remark?: string;
}) =>
request<ApiResponse<{ id: string }>>('/api/v1/purchase/payments', { method: 'POST', data });
// ── Purchase Stats ────────────────────────────────
export const getPurchaseStatsSummary = (params: { startDate?: string; endDate?: string }) =>
request<ApiResponse<PurchaseStatsSummary>>('/api/v1/purchase/stats/summary', { method: 'GET', params });
export const getPurchaseStatsBySupplier = (params: { startDate?: string; endDate?: string }) =>
request<ApiResponse<{ list: PurchaseStatsBySupplierItem[] }>>('/api/v1/purchase/stats/by-supplier', { method: 'GET', params });
export const getPurchaseStatsByProduct = (params: { startDate?: string; endDate?: string }) =>
request<ApiResponse<{ list: PurchaseStatsByProductItem[] }>>('/api/v1/purchase/stats/by-product', { method: 'GET', params });
// ── Inventory Logs ────────────────────────────────
export const listInventoryLogs = (params: PaginationParams & { productId?: string; changeType?: number; startDate?: string; endDate?: string }) =>
request<ApiResponse<PaginatedData<InventoryLog>>>('/api/v1/inventory/logs', { method: 'GET', params });

View File

@ -246,3 +246,126 @@ export interface PaginationParams {
page?: number; page?: number;
pageSize?: number; pageSize?: number;
} }
// ── Purchase ──────────────────────────────────────
export interface Supplier {
supplierId: string;
supplierName: string;
phone: string;
purchaseCount: number;
deliveredQty: string;
payableAmount: string;
paidAmount: string;
status: number;
remark?: string;
createdAt: string;
updatedAt: string;
}
export interface PurchaseOrderDetail {
detailId: string;
productId: string;
productName: string;
spec: string;
color: string;
quantity: string;
unitPrice: string;
amount: string;
receivedQty: string;
remark?: string;
}
export interface PurchaseOrder {
orderId: string;
orderNo: string;
supplierId: string;
supplierName: string;
orderDate: string;
contractAmount: string;
receivedQty: string;
paidAmount: string;
paymentStatus: number;
receiptStatus: number;
purchaseBy: string;
creator: string;
operator: string;
status: number;
remark?: string;
createdAt: string;
updatedAt: string;
details: PurchaseOrderDetail[];
}
export interface PurchaseReceiptDetail {
detailId: string;
orderDetailId: string;
productId: string;
actualQty: string;
unitCost: string;
remark?: string;
}
export interface PurchaseReceipt {
receiptId: string;
receiptNo: string;
orderId: string;
receiptDate: string;
receivedBy: string;
status: number;
remark?: string;
createdAt: string;
details: PurchaseReceiptDetail[];
}
export interface PurchasePayment {
paymentId: string;
orderId: string;
supplierId: string;
paymentDate: string;
amount: string;
paymentMethod: string;
operator: string;
remark?: string;
createdAt: string;
}
export interface InventoryLog {
logId: string;
productId: string;
changeQty: string;
balanceAfter: string;
changeType: number;
refType: string;
refId: string;
contactId: string;
logDate: string;
operator: string;
remark?: string;
createdAt: string;
}
export interface PurchaseStatsSummary {
totalOrders: number;
totalAmount: string;
totalPaidAmount: string;
unpaidAmount: string;
}
export interface PurchaseStatsBySupplierItem {
supplierId: string;
supplierName: string;
orderCount: number;
totalAmount: string;
paidAmount: string;
}
export interface PurchaseStatsByProductItem {
productId: string;
productName: string;
spec: string;
color: string;
totalQty: string;
avgUnitPrice: string;
totalAmount: string;
}