回筐逻辑修改
This commit is contained in:
@@ -1,26 +1,23 @@
|
||||
/** 门店回筐记录(压筐=生成账单自动写入只读;回筐=门店退回手动登记) */
|
||||
/** 门店压回筐记录(随账单生成:数量正数=压筐附加金额,负数=回筐抵扣金额) */
|
||||
export default interface IContainerReturn {
|
||||
id?: number;
|
||||
store_id?: number;
|
||||
/** 关联账单ID(0=手动回筐登记) */
|
||||
/** 关联账单ID */
|
||||
bill_id?: number;
|
||||
/** 类型:1压筐 2回筐 */
|
||||
type?: number;
|
||||
/** 压(回)筐数量(正数=压筐,负数=回筐) */
|
||||
box_num?: number;
|
||||
/** 压(回)托盘数量(正数=压筐,负数=回筐) */
|
||||
tray_num?: number;
|
||||
/** 记录日期(压筐=账单日期,回筐=退回日期) */
|
||||
return_date?: string;
|
||||
/** 周转筐单价(生成账单时快照) */
|
||||
box_price?: string;
|
||||
/** 周转托盘单价(生成账单时快照) */
|
||||
tray_price?: string;
|
||||
/** 抵扣(附加)金额(正=压筐附加,负=回筐抵扣) */
|
||||
amount?: string;
|
||||
operator_id?: number;
|
||||
remark?: string;
|
||||
created_at?: string;
|
||||
/** 列表接口附带 */
|
||||
store?: { id: number; name: string; pending_box_num?: number; pending_tray_num?: number } | null;
|
||||
bill?: { id: number; bill_no: string } | null;
|
||||
store?: { id: number; name: string } | null;
|
||||
bill?: { id: number; bill_no: string; bill_date?: string } | null;
|
||||
operator?: { id: number; nickname: string } | null;
|
||||
}
|
||||
|
||||
/** 回筐记录类型映射 */
|
||||
export const CONTAINER_TYPE_MAP: Record<number, { text: string; color: string }> = {
|
||||
1: { text: '压筐', color: 'processing' },
|
||||
2: { text: '回筐', color: 'success' },
|
||||
};
|
||||
|
||||
@@ -14,10 +14,6 @@ export default interface IStore {
|
||||
address?: string;
|
||||
/** 回款周期(天) */
|
||||
payment_cycle_days?: number;
|
||||
/** 待回筐数量(生成账单压筐累加,回筐登记扣减) */
|
||||
pending_box_num?: number;
|
||||
/** 待回托盘数量 */
|
||||
pending_tray_num?: number;
|
||||
/** 总采购金额(只统计商品金额,账单支付后累加) */
|
||||
total_purchase_amount?: string;
|
||||
status?: number;
|
||||
|
||||
@@ -52,8 +52,8 @@ const CustomerLevelPage: React.FC = () => {
|
||||
return (
|
||||
<Image
|
||||
src={url}
|
||||
width={60}
|
||||
height={60}
|
||||
width={30}
|
||||
height={30}
|
||||
style={{ objectFit: 'cover', borderRadius: 4 }}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -86,30 +86,6 @@ const StorePage: React.FC = () => {
|
||||
fieldProps: { min: 0, precision: 0 },
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '待回筐数量',
|
||||
dataIndex: 'pending_box_num',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => (
|
||||
<Text strong type={Number(record.pending_box_num) > 0 ? 'warning' : undefined}>
|
||||
{record.pending_box_num ?? 0}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '待回托盘数量',
|
||||
dataIndex: 'pending_tray_num',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => (
|
||||
<Text strong type={Number(record.pending_tray_num) > 0 ? 'warning' : undefined}>
|
||||
{record.pending_tray_num ?? 0}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '总采购金额',
|
||||
dataIndex: 'total_purchase_amount',
|
||||
|
||||
@@ -96,6 +96,7 @@ const SupplierPage: React.FC = () => {
|
||||
grid: true,
|
||||
colProps: { span: 12 },
|
||||
layout: 'vertical',
|
||||
rowProps: {gutter: 24}
|
||||
},
|
||||
modalProps: { width: 720 },
|
||||
};
|
||||
|
||||
@@ -329,10 +329,6 @@ const StoreOrderPage: React.FC = () => {
|
||||
{record.bill.pay_state_name ?? BILL_PAY_STATE_MAP[record.bill.pay_state ?? 0]?.text}
|
||||
</Tag>
|
||||
</div>
|
||||
<div>
|
||||
<Text type={'secondary'}>账单金额:</Text>
|
||||
<span className={'text-[red]'}>{record.bill.total_amount} ¥</span>
|
||||
</div>
|
||||
</Space>
|
||||
) : '-'
|
||||
},
|
||||
@@ -524,17 +520,6 @@ const StoreOrderPage: React.FC = () => {
|
||||
{detail.bill.pay_state_name ?? BILL_PAY_STATE_MAP[detail.bill.pay_state ?? 0]?.text}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="商品金额">¥{detail.bill.product_amount ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="配送费">¥{detail.bill.delivery_fee ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="附加金额">
|
||||
¥{detail.bill.added_amount ?? '0.00'}
|
||||
<Text type={'secondary'} className={'ml-1 text-[12px]'}>
|
||||
(筐{detail.bill.box_num ?? 0}/托盘{detail.bill.tray_num ?? 0})
|
||||
</Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="账单总金额">
|
||||
<Text strong className={'text-[red]'}>¥{detail.bill.total_amount}</Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="付款时间">{detail.bill.paid_at ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="付款备注">{detail.bill.pay_remark || '-'}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
@@ -1094,7 +1094,7 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
{billPrepare && (
|
||||
<>
|
||||
<div className="py-2 text-gray-500">
|
||||
每个门店单独生成一张账单;商品金额由订单汇总不可修改,请填写各门店的配送费与周转筐/托盘数量。
|
||||
每个门店单独生成一张账单;商品金额由订单汇总不可修改,请填写各门店的配送费与周转筐/托盘数量(正数=压筐附加金额,负数=回筐抵扣金额)。
|
||||
{billAllGenerated ? '该采购单已全部生成账单,仅可查看。' : '生成后采购单中的全部订单将关联到对应门店账单。'}
|
||||
</div>
|
||||
<Form form={billForm} layout="vertical" onFinish={handleBillSave}>
|
||||
@@ -1105,8 +1105,8 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
<div className="w-40 shrink-0">门店</div>
|
||||
<div className="w-28 shrink-0 text-center">商品金额</div>
|
||||
<div className="w-32 shrink-0 text-center">配送费(元)</div>
|
||||
<div className="w-32 shrink-0 text-center">周转筐(¥{billPrepare.stores[0]?.box_price ?? '0.00'})</div>
|
||||
<div className="w-32 shrink-0 text-center">周转托盘(¥{billPrepare.stores[0]?.tray_price ?? '0.00'})</div>
|
||||
<div className="w-32 shrink-0 text-center">周转筐(¥{billPrepare.stores[0]?.box_price ?? '0.00'}/个)</div>
|
||||
<div className="w-32 shrink-0 text-center">周转托盘(¥{billPrepare.stores[0]?.tray_price ?? '0.00'}/个)</div>
|
||||
<div className="w-28 shrink-0 text-center">附加金额</div>
|
||||
<div className="flex-1 text-center">账单总金额</div>
|
||||
</div>
|
||||
@@ -1148,10 +1148,9 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
min={0}
|
||||
precision={0}
|
||||
disabled={billed}
|
||||
placeholder="周转筐数量"
|
||||
placeholder="正压负回"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
@@ -1161,10 +1160,9 @@ const PurchaseOrderPage: React.FC = () => {
|
||||
>
|
||||
<InputNumber
|
||||
className="w-full"
|
||||
min={0}
|
||||
precision={0}
|
||||
disabled={billed}
|
||||
placeholder="周转托盘数量"
|
||||
placeholder="正压负回"
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="w-28 shrink-0 text-center">
|
||||
|
||||
@@ -1,18 +1,40 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { message, Tag, Typography } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { Typography } from 'antd';
|
||||
import XinTable from '@/components/XinTable';
|
||||
import type { XinTableColumn, XinTableProps } from '@/components/XinTable/typings.ts';
|
||||
import type IContainerReturn from '@/domain/iContainerReturn.ts';
|
||||
import { CONTAINER_TYPE_MAP } from '@/domain/iContainerReturn.ts';
|
||||
import { getStoreOptions } from '@/api/customer/store.ts';
|
||||
import { Create } from '@/api/common/table.ts';
|
||||
import type IStore from '@/domain/iStore.ts';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
/** 有符号数量展示:正数=压筐(橙色 +N),负数=回筐(绿色 N),0 置灰 */
|
||||
const SignedNum: React.FC<{ value?: number }> = ({ value }) => {
|
||||
const num = Number(value ?? 0);
|
||||
if (num > 0) {
|
||||
return <Text strong type="warning">+{num}</Text>;
|
||||
}
|
||||
if (num < 0) {
|
||||
return <Text strong type="success">{num}</Text>;
|
||||
}
|
||||
return <Text type="secondary">0</Text>;
|
||||
};
|
||||
|
||||
/** 有符号金额展示:正=压筐附加(橙),负=回筐抵扣(绿) */
|
||||
const SignedAmount: React.FC<{ value?: string }> = ({ value }) => {
|
||||
const num = Number(value ?? 0);
|
||||
if (num > 0) {
|
||||
return <Text strong type="warning">+¥{num.toFixed(2)}</Text>;
|
||||
}
|
||||
if (num < 0) {
|
||||
return <Text strong type="success">-¥{Math.abs(num).toFixed(2)}</Text>;
|
||||
}
|
||||
return <Text type="secondary">¥0.00</Text>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 回筐记录(压筐=生成账单时自动写入,只读;回筐=门店退回手动登记,删除后恢复门店待回数量)
|
||||
* 压回筐记录(周转筐/托盘跟账单走:生成账单时自动写入完整快照,只读)
|
||||
* 数量正数=压筐(附加金额),负数=回筐(抵扣金额)
|
||||
*/
|
||||
const ContainerReturnPage: React.FC = () => {
|
||||
const [stores, setStores] = useState<IStore[]>([]);
|
||||
@@ -26,7 +48,7 @@ const ContainerReturnPage: React.FC = () => {
|
||||
title: '门店',
|
||||
dataIndex: 'store_id',
|
||||
valueType: 'select',
|
||||
rules: [{ required: true, message: '请选择门店' }],
|
||||
hideInForm: true,
|
||||
fieldProps: {
|
||||
options: stores.map((s) => ({ label: s.name, value: s.id })),
|
||||
showSearch: true,
|
||||
@@ -34,81 +56,57 @@ const ContainerReturnPage: React.FC = () => {
|
||||
},
|
||||
render: (_, record) => record.store?.name ?? `门店#${record.store_id}`,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
valueType: 'select',
|
||||
hideInForm: true,
|
||||
align: 'center',
|
||||
fieldProps: {
|
||||
options: Object.entries(CONTAINER_TYPE_MAP).map(([value, item]) => ({
|
||||
value: Number(value),
|
||||
label: item.text,
|
||||
})),
|
||||
},
|
||||
render: (_, record) => {
|
||||
const item = CONTAINER_TYPE_MAP[record.type ?? 0];
|
||||
return <Tag color={item?.color}>{item?.text ?? '-'}</Tag>;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '关联账单',
|
||||
dataIndex: 'bill_id',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) =>
|
||||
record.bill ? <Text copyable={{ text: record.bill.bill_no }}>{record.bill.bill_no}</Text> : '-',
|
||||
render: (_, record) => record.bill ? (
|
||||
<>
|
||||
<Text copyable={{ text: record.bill.bill_no }}>{record.bill.bill_no}</Text>
|
||||
<div className={'text-[12px] text-[#999]'}>{record.bill.bill_date}</div>
|
||||
</>
|
||||
) : '-',
|
||||
},
|
||||
{
|
||||
title: '周转筐数量',
|
||||
title: '压(回)筐数量',
|
||||
dataIndex: 'box_num',
|
||||
valueType: 'digit',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
initialValue: 0,
|
||||
rules: [{ required: true, message: '请输入周转筐数量' }],
|
||||
fieldProps: { min: 0, precision: 0 },
|
||||
align: 'center',
|
||||
render: (_, record) => <Text strong>{record.box_num}</Text>,
|
||||
render: (_, record) => <SignedNum value={record.box_num} />,
|
||||
},
|
||||
{
|
||||
title: '周转托盘数量',
|
||||
title: '压(回)托盘数量',
|
||||
dataIndex: 'tray_num',
|
||||
valueType: 'digit',
|
||||
hideInSearch: true,
|
||||
initialValue: 0,
|
||||
rules: [{ required: true, message: '请输入周转托盘数量' }],
|
||||
fieldProps: { min: 0, precision: 0 },
|
||||
align: 'center',
|
||||
render: (_, record) => <Text strong>{record.tray_num}</Text>,
|
||||
},
|
||||
{
|
||||
title: '当前待回(筐/托盘)',
|
||||
key: 'pending',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) =>
|
||||
record.store
|
||||
? `${record.store.pending_box_num ?? 0} / ${record.store.pending_tray_num ?? 0}`
|
||||
: '-',
|
||||
render: (_, record) => <SignedNum value={record.tray_num} />,
|
||||
},
|
||||
{
|
||||
title: '退回日期',
|
||||
dataIndex: 'return_date',
|
||||
valueType: 'dateRange',
|
||||
hideInTable: true,
|
||||
title: '筐单价',
|
||||
dataIndex: 'box_price',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => `¥${Number(record.box_price ?? 0).toFixed(2)}`,
|
||||
},
|
||||
{
|
||||
title: '退回日期',
|
||||
dataIndex: 'return_date',
|
||||
valueType: 'date',
|
||||
title: '托盘单价',
|
||||
dataIndex: 'tray_price',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
initialValue: dayjs(),
|
||||
rules: [{ required: true, message: '请选择退回日期' }],
|
||||
align: 'center',
|
||||
render: (_, record) => `¥${Number(record.tray_price ?? 0).toFixed(2)}`,
|
||||
},
|
||||
{
|
||||
title: '抵扣(附加)金额',
|
||||
dataIndex: 'amount',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => <SignedAmount value={record.amount} />,
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
@@ -119,20 +117,20 @@ const ContainerReturnPage: React.FC = () => {
|
||||
render: (_, record) => record.operator?.nickname ?? '-',
|
||||
},
|
||||
{
|
||||
title: '登记时间',
|
||||
title: '记录时间',
|
||||
dataIndex: 'created_at',
|
||||
valueType: 'dateRange',
|
||||
hideInForm: true,
|
||||
hideInTable: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '记录时间',
|
||||
dataIndex: 'created_at',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
valueType: 'textarea',
|
||||
hideInSearch: true,
|
||||
fieldProps: { rows: 2 },
|
||||
render: (v) => v || '-',
|
||||
},
|
||||
];
|
||||
|
||||
const tableProps: XinTableProps<IContainerReturn> = {
|
||||
@@ -140,25 +138,11 @@ const ContainerReturnPage: React.FC = () => {
|
||||
columns,
|
||||
rowKey: 'id',
|
||||
accessName: 'recon.containerReturn',
|
||||
// 压筐记录由账单生成,仅回筐记录可删除;均不允许编辑
|
||||
// 记录随账单生成,只读:不新增、不编辑、不删除
|
||||
addShow: false,
|
||||
editShow: false,
|
||||
deleteShow: (record) => record.type === 2,
|
||||
// 自定义提交:DatePicker 值为 dayjs 对象,格式化为 Y-m-d 后再提交
|
||||
handleFinish: async (values) => {
|
||||
await Create('/recon/container-return', {
|
||||
...values,
|
||||
return_date: dayjs(values.return_date).format('YYYY-MM-DD'),
|
||||
});
|
||||
message.success('回筐已登记');
|
||||
return true;
|
||||
},
|
||||
formProps: {
|
||||
grid: true,
|
||||
colProps: { span: 12 },
|
||||
rowProps: { gutter: 20 },
|
||||
layout: 'vertical',
|
||||
},
|
||||
modalProps: { width: 640, title: '回筐登记' },
|
||||
deleteShow: false,
|
||||
formProps: false,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -166,7 +150,7 @@ const ContainerReturnPage: React.FC = () => {
|
||||
<div className="mb-5">
|
||||
<Title level={3}>回筐记录</Title>
|
||||
<Text type="secondary">
|
||||
生成账单时自动写入「压筐」记录并累加门店待回数量;门店退回周转筐/托盘时手动新增「回筐」登记并扣减待回,删除登记将恢复待回数量。
|
||||
周转筐/托盘跟账单走:生成账单时按填写数量自动写入;正数=压筐(附加金额),负数=回筐(抵扣金额),数量为 0 不生成记录。
|
||||
</Text>
|
||||
</div>
|
||||
<XinTable<IContainerReturn> {...tableProps} />
|
||||
|
||||
Reference in New Issue
Block a user