mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-05 03:12:34 -06:00
Перевел на multi-tenant
Добавил поставщиков Накладные успешно создаются из фронта
This commit is contained in:
@@ -24,8 +24,14 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
const [updatingItems, setUpdatingItems] = useState<Set<string>>(new Set());
|
||||
|
||||
// --- ЗАПРОСЫ ---
|
||||
const storesQuery = useQuery({ queryKey: ['stores'], queryFn: api.getStores });
|
||||
const suppliersQuery = useQuery({ queryKey: ['suppliers'], queryFn: api.getSuppliers });
|
||||
|
||||
// Получаем сразу все справочники одним запросом
|
||||
const dictQuery = useQuery({
|
||||
queryKey: ['dictionaries'],
|
||||
queryFn: api.getDictionaries,
|
||||
staleTime: 1000 * 60 * 5 // Кэшируем на 5 минут
|
||||
});
|
||||
|
||||
const recommendationsQuery = useQuery({ queryKey: ['recommendations'], queryFn: api.getRecommendations });
|
||||
|
||||
const draftQuery = useQuery({
|
||||
@@ -39,8 +45,9 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
});
|
||||
|
||||
const draft = draftQuery.data;
|
||||
const stores = dictQuery.data?.stores || [];
|
||||
const suppliers = dictQuery.data?.suppliers || [];
|
||||
|
||||
// ... (МУТАЦИИ оставляем без изменений) ...
|
||||
const updateItemMutation = useMutation({
|
||||
mutationFn: (vars: { itemId: string; payload: UpdateDraftItemRequest }) =>
|
||||
api.updateDraftItem(id!, vars.itemId, vars.payload),
|
||||
@@ -115,11 +122,14 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
|
||||
const handleCommit = async () => {
|
||||
try {
|
||||
// Валидируем форму (включая нового поставщика)
|
||||
const values = await form.validateFields();
|
||||
|
||||
if (invalidItemsCount > 0) {
|
||||
message.warning(`Осталось ${invalidItemsCount} нераспознанных товаров!`);
|
||||
return;
|
||||
}
|
||||
|
||||
commitMutation.mutate({
|
||||
date_incoming: values.date_incoming.format('YYYY-MM-DD'),
|
||||
store_id: values.store_id,
|
||||
@@ -127,7 +137,7 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
comment: values.comment || '',
|
||||
});
|
||||
} catch {
|
||||
message.error('Заполните обязательные поля');
|
||||
message.error('Заполните обязательные поля (Склад, Поставщик)');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,12 +172,11 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div style={{ paddingBottom: 60 }}>
|
||||
{/* Header: Уплотненный, без переноса слов */}
|
||||
{/* Header */}
|
||||
<div style={{ marginBottom: 12, display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/invoices')} size="small" />
|
||||
|
||||
{/* Контейнер заголовка и бейджа */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
|
||||
<span style={{ fontSize: 18, fontWeight: 'bold', whiteSpace: 'nowrap' }}>
|
||||
{draft.document_number ? `№${draft.document_number}` : 'Черновик'}
|
||||
@@ -189,7 +198,7 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Form: Compact margins */}
|
||||
{/* Form: Склады и Поставщики */}
|
||||
<div style={{ background: '#fff', padding: 12, borderRadius: 8, marginBottom: 12, opacity: isCanceled ? 0.6 : 1 }}>
|
||||
<Form form={form} layout="vertical" initialValues={{ date_incoming: dayjs() }}>
|
||||
<Row gutter={10}>
|
||||
@@ -199,22 +208,27 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="Склад" name="store_id" rules={[{ required: true }]} style={{ marginBottom: 8 }}>
|
||||
<Form.Item label="Склад" name="store_id" rules={[{ required: true, message: 'Выберите склад' }]} style={{ marginBottom: 8 }}>
|
||||
<Select
|
||||
placeholder="Куда?"
|
||||
loading={storesQuery.isLoading}
|
||||
options={storesQuery.data?.map(s => ({ label: s.name, value: s.id }))}
|
||||
loading={dictQuery.isLoading}
|
||||
options={stores.map(s => ({ label: s.name, value: s.id }))}
|
||||
size="middle"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item label="Поставщик" name="supplier_id" rules={[{ required: true }]} style={{ marginBottom: 8 }}>
|
||||
{/* Поле Поставщика (Обязательное) */}
|
||||
<Form.Item label="Поставщик" name="supplier_id" rules={[{ required: true, message: 'Выберите поставщика' }]} style={{ marginBottom: 8 }}>
|
||||
<Select
|
||||
placeholder="От кого?"
|
||||
loading={suppliersQuery.isLoading}
|
||||
options={suppliersQuery.data?.map(s => ({ label: s.name, value: s.id }))}
|
||||
loading={dictQuery.isLoading}
|
||||
options={suppliers.map(s => ({ label: s.name, value: s.id }))}
|
||||
size="middle"
|
||||
showSearch
|
||||
filterOption={(input, option) =>
|
||||
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="Комментарий" name="comment" style={{ marginBottom: 0 }}>
|
||||
@@ -243,14 +257,14 @@ export const InvoiceDraftPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Footer Actions */}
|
||||
<Affix offsetBottom={60} /* Высота нижнего меню */>
|
||||
<Affix offsetBottom={60}>
|
||||
<div style={{
|
||||
background: '#fff',
|
||||
padding: '8px 16px',
|
||||
borderTop: '1px solid #eee',
|
||||
boxShadow: '0 -2px 10px rgba(0,0,0,0.05)',
|
||||
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
|
||||
borderRadius: '8px 8px 0 0' // Скругление сверху
|
||||
borderRadius: '8px 8px 0 0'
|
||||
}}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<span style={{ fontSize: 11, color: '#888', lineHeight: 1 }}>Итого:</span>
|
||||
|
||||
Reference in New Issue
Block a user