+
+
void
- onCreateSupply: () => void
- canCreateSupply: boolean
- isCreatingSupply: boolean
- onCanCreateSupplyChange?: (canCreate: boolean) => void
+ onComplete: () => void;
+ onCreateSupply: () => void;
+ canCreateSupply: boolean;
+ isCreatingSupply: boolean;
+ onCanCreateSupplyChange?: (canCreate: boolean) => void;
}
-export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupply, isCreatingSupply, onCanCreateSupplyChange }: DirectSupplyCreationProps) {
- const { user } = useAuth()
-
- // Состояние для товаров
- const [searchTerm, setSearchTerm] = useState('')
- const [loading, setLoading] = useState(false)
- const [wbCards, setWbCards] = useState([])
- const [supplyItems, setSupplyItems] = useState([])
-
- // Общие настройки
- const [deliveryDate, setDeliveryDate] = useState(undefined)
- const [selectedFulfillmentOrg, setSelectedFulfillmentOrg] = useState('')
- const [selectedServices, setSelectedServices] = useState([])
- const [selectedConsumables, setSelectedConsumables] = useState([])
-
+export function DirectSupplyCreation({
+ onComplete,
+ onCreateSupply,
+ canCreateSupply,
+ isCreatingSupply,
+ onCanCreateSupplyChange,
+}: DirectSupplyCreationProps) {
+ const { user } = useAuth();
+
+ // Новые состояния для блока создания поставки
+ const [deliveryDate, setDeliveryDate] = useState("");
+ const [selectedFulfillment, setSelectedFulfillment] = useState("");
+ const [goodsQuantity, setGoodsQuantity] = useState(1200);
+ const [goodsVolume, setGoodsVolume] = useState(0);
+ const [cargoPlaces, setCargoPlaces] = useState(0);
+ const [goodsPrice, setGoodsPrice] = useState(0);
+ const [fulfillmentServicesPrice, setFulfillmentServicesPrice] =
+ useState(0);
+ const [logisticsPrice, setLogisticsPrice] = useState(0);
+
+ // Оригинальные состояния для товаров
+ const [searchTerm, setSearchTerm] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [wbCards, setWbCards] = useState([]);
+ const [supplyItems, setSupplyItems] = useState([]);
+
+ // Общие настройки (оригинальные)
+ const [deliveryDateOriginal, setDeliveryDateOriginal] = useState<
+ Date | undefined
+ >(undefined);
+ const [selectedFulfillmentOrg, setSelectedFulfillmentOrg] =
+ useState("");
+ const [selectedServices, setSelectedServices] = useState([]);
+ const [selectedConsumables, setSelectedConsumables] = useState([]);
+
// Поставщики
- const [suppliers, setSuppliers] = useState([])
- const [showSupplierModal, setShowSupplierModal] = useState(false)
+ const [suppliers, setSuppliers] = useState([]);
+ const [showSupplierModal, setShowSupplierModal] = useState(false);
const [newSupplier, setNewSupplier] = useState({
- name: '',
- contactName: '',
- phone: '',
- market: '',
- address: '',
- place: '',
- telegram: ''
- })
-
+ name: "",
+ contactName: "",
+ phone: "",
+ market: "",
+ address: "",
+ place: "",
+ telegram: "",
+ });
+
// Данные для фулфилмента
- const [organizationServices, setOrganizationServices] = useState<{[orgId: string]: FulfillmentService[]}>({})
- const [organizationSupplies, setOrganizationSupplies] = useState<{[orgId: string]: FulfillmentService[]}>({})
+ const [organizationServices, setOrganizationServices] = useState<{
+ [orgId: string]: FulfillmentService[];
+ }>({});
+ const [organizationSupplies, setOrganizationSupplies] = useState<{
+ [orgId: string]: FulfillmentService[];
+ }>({});
// Загружаем контрагентов-фулфилментов
- const { data: counterpartiesData } = useQuery(GET_MY_COUNTERPARTIES)
-
+ const { data: counterpartiesData } = useQuery(GET_MY_COUNTERPARTIES);
+
// Мутация для создания поставки
- const [createSupply, { loading: creatingSupply }] = useMutation(CREATE_WILDBERRIES_SUPPLY, {
- onCompleted: (data) => {
- if (data.createWildberriesSupply.success) {
- toast.success(data.createWildberriesSupply.message)
- onComplete()
- } else {
- toast.error(data.createWildberriesSupply.message)
- }
- },
- onError: (error) => {
- toast.error('Ошибка при создании поставки')
- console.error('Error creating supply:', error)
+ const [createSupply, { loading: creatingSupply }] = useMutation(
+ CREATE_WILDBERRIES_SUPPLY,
+ {
+ onCompleted: (data) => {
+ if (data.createWildberriesSupply.success) {
+ toast.success(data.createWildberriesSupply.message);
+ onComplete();
+ } else {
+ toast.error(data.createWildberriesSupply.message);
+ }
+ },
+ onError: (error) => {
+ toast.error("Ошибка при создании поставки");
+ console.error("Error creating supply:", error);
+ },
}
- })
+ );
// Моковые данные товаров для демонстрации
const getMockCards = (): WildberriesCard[] => [
{
nmID: 123456789,
- vendorCode: 'SKU001',
- title: 'Платье летнее розовое',
- description: 'Легкое летнее платье из натурального хлопка',
- brand: 'Fashion',
- object: 'Платья',
- parent: 'Одежда',
- countryProduction: 'Россия',
- supplierVendorCode: 'SUPPLIER-001',
- mediaFiles: ['/api/placeholder/400/400'],
- sizes: [{ chrtID: 123456, techSize: 'M', wbSize: 'M Розовый', price: 2500, discountedPrice: 2000, quantity: 50 }]
+ vendorCode: "SKU001",
+ title: "Платье летнее розовое",
+ description: "Легкое летнее платье из натурального хлопка",
+ brand: "Fashion",
+ object: "Платья",
+ parent: "Одежда",
+ countryProduction: "Россия",
+ supplierVendorCode: "SUPPLIER-001",
+ mediaFiles: ["/api/placeholder/400/400"],
+ sizes: [
+ {
+ chrtID: 123456,
+ techSize: "M",
+ wbSize: "M Розовый",
+ price: 2500,
+ discountedPrice: 2000,
+ quantity: 50,
+ },
+ ],
},
{
nmID: 987654321,
- vendorCode: 'SKU002',
- title: 'Платье черное вечернее',
- description: 'Элегантное вечернее платье для особых случаев',
- brand: 'Fashion',
- object: 'Платья',
- parent: 'Одежда',
- countryProduction: 'Россия',
- supplierVendorCode: 'SUPPLIER-002',
- mediaFiles: ['/api/placeholder/400/403'],
- sizes: [{ chrtID: 987654, techSize: 'M', wbSize: 'M Черный', price: 3500, discountedPrice: 3000, quantity: 30 }]
+ vendorCode: "SKU002",
+ title: "Платье черное вечернее",
+ description: "Элегантное вечернее платье для особых случаев",
+ brand: "Fashion",
+ object: "Платья",
+ parent: "Одежда",
+ countryProduction: "Россия",
+ supplierVendorCode: "SUPPLIER-002",
+ mediaFiles: ["/api/placeholder/400/403"],
+ sizes: [
+ {
+ chrtID: 987654,
+ techSize: "M",
+ wbSize: "M Черный",
+ price: 3500,
+ discountedPrice: 3000,
+ quantity: 30,
+ },
+ ],
},
{
nmID: 555666777,
- vendorCode: 'SKU003',
- title: 'Блузка белая офисная',
- description: 'Классическая белая блузка для офиса',
- brand: 'Office',
- object: 'Блузки',
- parent: 'Одежда',
- countryProduction: 'Турция',
- supplierVendorCode: 'SUPPLIER-003',
- mediaFiles: ['/api/placeholder/400/405'],
- sizes: [{ chrtID: 555666, techSize: 'L', wbSize: 'L Белый', price: 1800, discountedPrice: 1500, quantity: 40 }]
+ vendorCode: "SKU003",
+ title: "Блузка белая офисная",
+ description: "Классическая белая блузка для офиса",
+ brand: "Office",
+ object: "Блузки",
+ parent: "Одежда",
+ countryProduction: "Турция",
+ supplierVendorCode: "SUPPLIER-003",
+ mediaFiles: ["/api/placeholder/400/405"],
+ sizes: [
+ {
+ chrtID: 555666,
+ techSize: "L",
+ wbSize: "L Белый",
+ price: 1800,
+ discountedPrice: 1500,
+ quantity: 40,
+ },
+ ],
},
{
nmID: 444333222,
- vendorCode: 'SKU004',
- title: 'Джинсы женские синие',
- description: 'Классические женские джинсы прямого кроя',
- brand: 'Denim',
- object: 'Джинсы',
- parent: 'Одежда',
- countryProduction: 'Бангладеш',
- supplierVendorCode: 'SUPPLIER-004',
- mediaFiles: ['/api/placeholder/400/408'],
- sizes: [{ chrtID: 444333, techSize: '30', wbSize: '30 Синий', price: 2800, discountedPrice: 2300, quantity: 25 }]
+ vendorCode: "SKU004",
+ title: "Джинсы женские синие",
+ description: "Классические женские джинсы прямого кроя",
+ brand: "Denim",
+ object: "Джинсы",
+ parent: "Одежда",
+ countryProduction: "Бангладеш",
+ supplierVendorCode: "SUPPLIER-004",
+ mediaFiles: ["/api/placeholder/400/408"],
+ sizes: [
+ {
+ chrtID: 444333,
+ techSize: "30",
+ wbSize: "30 Синий",
+ price: 2800,
+ discountedPrice: 2300,
+ quantity: 25,
+ },
+ ],
},
{
nmID: 111222333,
- vendorCode: 'SKU005',
- title: 'Кроссовки женские белые',
- description: 'Удобные женские кроссовки для повседневной носки',
- brand: 'Sport',
- object: 'Кроссовки',
- parent: 'Обувь',
- countryProduction: 'Вьетнам',
- supplierVendorCode: 'SUPPLIER-005',
- mediaFiles: ['/api/placeholder/400/410'],
- sizes: [{ chrtID: 111222, techSize: '37', wbSize: '37 Белый', price: 3200, discountedPrice: 2800, quantity: 35 }]
+ vendorCode: "SKU005",
+ title: "Кроссовки женские белые",
+ description: "Удобные женские кроссовки для повседневной носки",
+ brand: "Sport",
+ object: "Кроссовки",
+ parent: "Обувь",
+ countryProduction: "Вьетнам",
+ supplierVendorCode: "SUPPLIER-005",
+ mediaFiles: ["/api/placeholder/400/410"],
+ sizes: [
+ {
+ chrtID: 111222,
+ techSize: "37",
+ wbSize: "37 Белый",
+ price: 3200,
+ discountedPrice: 2800,
+ quantity: 35,
+ },
+ ],
},
{
nmID: 777888999,
- vendorCode: 'SKU006',
- title: 'Сумка женская черная',
- description: 'Стильная женская сумка из экокожи',
- brand: 'Accessories',
- object: 'Сумки',
- parent: 'Аксессуары',
- countryProduction: 'Китай',
- supplierVendorCode: 'SUPPLIER-006',
- mediaFiles: ['/api/placeholder/400/411'],
- sizes: [{ chrtID: 777888, techSize: 'Универсальный', wbSize: 'Черный', price: 1500, discountedPrice: 1200, quantity: 60 }]
- }
- ]
+ vendorCode: "SKU006",
+ title: "Сумка женская черная",
+ description: "Стильная женская сумка из экокожи",
+ brand: "Accessories",
+ object: "Сумки",
+ parent: "Аксессуары",
+ countryProduction: "Китай",
+ supplierVendorCode: "SUPPLIER-006",
+ mediaFiles: ["/api/placeholder/400/411"],
+ sizes: [
+ {
+ chrtID: 777888,
+ techSize: "Универсальный",
+ wbSize: "Черный",
+ price: 1500,
+ discountedPrice: 1200,
+ quantity: 60,
+ },
+ ],
+ },
+ ];
// Загружаем товары при инициализации
useEffect(() => {
- loadCards()
- }, [user])
+ loadCards();
+ }, [user]);
const loadCards = async () => {
- setLoading(true)
+ setLoading(true);
try {
- const wbApiKey = user?.organization?.apiKeys?.find(key => key.marketplace === 'WILDBERRIES')
-
+ const wbApiKey = user?.organization?.apiKeys?.find(
+ (key) => key.marketplace === "WILDBERRIES"
+ );
+
if (wbApiKey?.isActive) {
- const validationData = wbApiKey.validationData as Record
- const apiToken = validationData?.token ||
- validationData?.apiKey ||
- validationData?.key ||
- (wbApiKey as { apiKey?: string }).apiKey
-
+ const validationData = wbApiKey.validationData as Record<
+ string,
+ string
+ >;
+ const apiToken =
+ validationData?.token ||
+ validationData?.apiKey ||
+ validationData?.key ||
+ (wbApiKey as { apiKey?: string }).apiKey;
+
if (apiToken) {
- console.log('Загружаем карточки из WB API...')
- const cards = await WildberriesService.getAllCards(apiToken, 20)
- setWbCards(cards)
- console.log('Загружено карточек из WB API:', cards.length)
- return
+ console.log("Загружаем карточки из WB API...");
+ const cards = await WildberriesService.getAllCards(apiToken, 20);
+ setWbCards(cards);
+ console.log("Загружено карточек из WB API:", cards.length);
+ return;
}
}
-
+
// Если API ключ не настроен, показываем моковые данные
- console.log('API ключ WB не настроен, показываем моковые данные')
- setWbCards(getMockCards())
+ console.log("API ключ WB не настроен, показываем моковые данные");
+ setWbCards(getMockCards());
} catch (error) {
- console.error('Ошибка загрузки карточек WB:', error)
+ console.error("Ошибка загрузки карточек WB:", error);
// При ошибке API показываем моковые данные
- setWbCards(getMockCards())
+ setWbCards(getMockCards());
} finally {
- setLoading(false)
+ setLoading(false);
}
- }
+ };
const searchCards = async () => {
if (!searchTerm.trim()) {
- loadCards()
- return
+ loadCards();
+ return;
}
-
- setLoading(true)
+
+ setLoading(true);
try {
- const wbApiKey = user?.organization?.apiKeys?.find(key => key.marketplace === 'WILDBERRIES')
-
+ const wbApiKey = user?.organization?.apiKeys?.find(
+ (key) => key.marketplace === "WILDBERRIES"
+ );
+
if (wbApiKey?.isActive) {
- const validationData = wbApiKey.validationData as Record
- const apiToken = validationData?.token ||
- validationData?.apiKey ||
- validationData?.key ||
- (wbApiKey as { apiKey?: string }).apiKey
-
+ const validationData = wbApiKey.validationData as Record<
+ string,
+ string
+ >;
+ const apiToken =
+ validationData?.token ||
+ validationData?.apiKey ||
+ validationData?.key ||
+ (wbApiKey as { apiKey?: string }).apiKey;
+
if (apiToken) {
- console.log('Поиск в WB API:', searchTerm)
- const cards = await WildberriesService.searchCards(apiToken, searchTerm, 20)
- setWbCards(cards)
- console.log('Найдено карточек в WB API:', cards.length)
- return
+ console.log("Поиск в WB API:", searchTerm);
+ const cards = await WildberriesService.searchCards(
+ apiToken,
+ searchTerm,
+ 20
+ );
+ setWbCards(cards);
+ console.log("Найдено карточек в WB API:", cards.length);
+ return;
}
}
-
+
// Если API ключ не настроен, ищем в моковых данных
- console.log('API ключ WB не настроен, поиск в моковых данных:', searchTerm)
- const mockCards = getMockCards()
- const filteredCards = mockCards.filter(card =>
- card.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
- card.brand.toLowerCase().includes(searchTerm.toLowerCase()) ||
- card.nmID.toString().includes(searchTerm.toLowerCase()) ||
- card.object?.toLowerCase().includes(searchTerm.toLowerCase())
- )
- setWbCards(filteredCards)
- console.log('Найдено моковых товаров:', filteredCards.length)
+ console.log(
+ "API ключ WB не настроен, поиск в моковых данных:",
+ searchTerm
+ );
+ const mockCards = getMockCards();
+ const filteredCards = mockCards.filter(
+ (card) =>
+ card.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ card.brand.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ card.nmID.toString().includes(searchTerm.toLowerCase()) ||
+ card.object?.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+ setWbCards(filteredCards);
+ console.log("Найдено моковых товаров:", filteredCards.length);
} catch (error) {
- console.error('Ошибка поиска карточек WB:', error)
+ console.error("Ошибка поиска карточек WB:", error);
// При ошибке ищем в моковых данных
- const mockCards = getMockCards()
- const filteredCards = mockCards.filter(card =>
- card.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
- card.brand.toLowerCase().includes(searchTerm.toLowerCase()) ||
- card.nmID.toString().includes(searchTerm.toLowerCase()) ||
- card.object?.toLowerCase().includes(searchTerm.toLowerCase())
- )
- setWbCards(filteredCards)
- console.log('Найдено моковых товаров (fallback):', filteredCards.length)
+ const mockCards = getMockCards();
+ const filteredCards = mockCards.filter(
+ (card) =>
+ card.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ card.brand.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ card.nmID.toString().includes(searchTerm.toLowerCase()) ||
+ card.object?.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+ setWbCards(filteredCards);
+ console.log("Найдено моковых товаров (fallback):", filteredCards.length);
} finally {
- setLoading(false)
+ setLoading(false);
}
- }
+ };
// Функции для работы с услугами и расходниками
const loadOrganizationServices = async (organizationId: string) => {
- if (organizationServices[organizationId]) return
-
+ if (organizationServices[organizationId]) return;
+
try {
const response = await apolloClient.query({
query: GET_COUNTERPARTY_SERVICES,
- variables: { organizationId }
- })
-
+ variables: { organizationId },
+ });
+
if (response.data?.counterpartyServices) {
- setOrganizationServices(prev => ({
+ setOrganizationServices((prev) => ({
...prev,
- [organizationId]: response.data.counterpartyServices
- }))
+ [organizationId]: response.data.counterpartyServices,
+ }));
}
} catch (error) {
- console.error('Ошибка загрузки услуг организации:', error)
+ console.error("Ошибка загрузки услуг организации:", error);
}
- }
+ };
const loadOrganizationSupplies = async (organizationId: string) => {
- if (organizationSupplies[organizationId]) return
-
+ if (organizationSupplies[organizationId]) return;
+
try {
const response = await apolloClient.query({
query: GET_COUNTERPARTY_SUPPLIES,
- variables: { organizationId }
- })
-
+ variables: { organizationId },
+ });
+
if (response.data?.counterpartySupplies) {
- setOrganizationSupplies(prev => ({
+ setOrganizationSupplies((prev) => ({
...prev,
- [organizationId]: response.data.counterpartySupplies
- }))
+ [organizationId]: response.data.counterpartySupplies,
+ }));
}
} catch (error) {
- console.error('Ошибка загрузки расходников организации:', error)
+ console.error("Ошибка загрузки расходников организации:", error);
}
- }
+ };
// Работа с товарами поставки
const addToSupply = (card: WildberriesCard) => {
- const existingItem = supplyItems.find(item => item.card.nmID === card.nmID)
+ const existingItem = supplyItems.find(
+ (item) => item.card.nmID === card.nmID
+ );
if (existingItem) {
- toast.info('Товар уже добавлен в поставку')
- return
+ toast.info("Товар уже добавлен в поставку");
+ return;
}
const newItem: SupplyItem = {
@@ -353,114 +488,132 @@ export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupp
quantity: 1200,
pricePerUnit: 0,
totalPrice: 0,
- supplierId: ''
- }
+ supplierId: "",
+ };
- setSupplyItems(prev => [...prev, newItem])
- toast.success('Товар добавлен в поставку')
- }
+ setSupplyItems((prev) => [...prev, newItem]);
+ toast.success("Товар добавлен в поставку");
+ };
const removeFromSupply = (nmID: number) => {
- setSupplyItems(prev => prev.filter(item => item.card.nmID !== nmID))
- }
+ setSupplyItems((prev) => prev.filter((item) => item.card.nmID !== nmID));
+ };
- const updateSupplyItem = (nmID: number, field: keyof SupplyItem, value: string | number) => {
- setSupplyItems(prev => prev.map(item => {
- if (item.card.nmID === nmID) {
- const updatedItem = { ...item, [field]: value }
- if (field === 'quantity' || field === 'pricePerUnit') {
- updatedItem.totalPrice = updatedItem.quantity * updatedItem.pricePerUnit
+ const updateSupplyItem = (
+ nmID: number,
+ field: keyof SupplyItem,
+ value: string | number
+ ) => {
+ setSupplyItems((prev) =>
+ prev.map((item) => {
+ if (item.card.nmID === nmID) {
+ const updatedItem = { ...item, [field]: value };
+ if (field === "quantity" || field === "pricePerUnit") {
+ updatedItem.totalPrice =
+ updatedItem.quantity * updatedItem.pricePerUnit;
+ }
+ return updatedItem;
}
- return updatedItem
- }
- return item
- }))
- }
+ return item;
+ })
+ );
+ };
// Работа с поставщиками
const handleCreateSupplier = () => {
if (!newSupplier.name || !newSupplier.contactName || !newSupplier.phone) {
- toast.error('Заполните обязательные поля')
- return
+ toast.error("Заполните обязательные поля");
+ return;
}
const supplier: Supplier = {
id: Date.now().toString(),
- ...newSupplier
- }
+ ...newSupplier,
+ };
- setSuppliers(prev => [...prev, supplier])
+ setSuppliers((prev) => [...prev, supplier]);
setNewSupplier({
- name: '',
- contactName: '',
- phone: '',
- market: '',
- address: '',
- place: '',
- telegram: ''
- })
- setShowSupplierModal(false)
- toast.success('Поставщик создан')
- }
+ name: "",
+ contactName: "",
+ phone: "",
+ market: "",
+ address: "",
+ place: "",
+ telegram: "",
+ });
+ setShowSupplierModal(false);
+ toast.success("Поставщик создан");
+ };
- // Расчеты
+ // Расчеты для нового блока
+ const getTotalSum = () => {
+ return goodsPrice + fulfillmentServicesPrice + logisticsPrice;
+ };
+
+ // Оригинальные расчеты
const getTotalQuantity = () => {
- return supplyItems.reduce((sum, item) => sum + item.quantity, 0)
- }
+ return supplyItems.reduce((sum, item) => sum + item.quantity, 0);
+ };
const getTotalItemsCost = () => {
- return supplyItems.reduce((sum, item) => sum + item.totalPrice, 0)
- }
+ return supplyItems.reduce((sum, item) => sum + item.totalPrice, 0);
+ };
const getServicesCost = () => {
- if (!selectedFulfillmentOrg || selectedServices.length === 0) return 0
-
- const services = organizationServices[selectedFulfillmentOrg] || []
- return selectedServices.reduce((sum, serviceId) => {
- const service = services.find(s => s.id === serviceId)
- return sum + (service ? service.price : 0)
- }, 0) * getTotalQuantity()
- }
+ if (!selectedFulfillmentOrg || selectedServices.length === 0) return 0;
+
+ const services = organizationServices[selectedFulfillmentOrg] || [];
+ return (
+ selectedServices.reduce((sum, serviceId) => {
+ const service = services.find((s) => s.id === serviceId);
+ return sum + (service ? service.price : 0);
+ }, 0) * getTotalQuantity()
+ );
+ };
const getConsumablesCost = () => {
- if (!selectedFulfillmentOrg || selectedConsumables.length === 0) return 0
-
- const supplies = organizationSupplies[selectedFulfillmentOrg] || []
- return selectedConsumables.reduce((sum, supplyId) => {
- const supply = supplies.find(s => s.id === supplyId)
- return sum + (supply ? supply.price : 0)
- }, 0) * getTotalQuantity()
- }
+ if (!selectedFulfillmentOrg || selectedConsumables.length === 0) return 0;
+
+ const supplies = organizationSupplies[selectedFulfillmentOrg] || [];
+ return (
+ selectedConsumables.reduce((sum, supplyId) => {
+ const supply = supplies.find((s) => s.id === supplyId);
+ return sum + (supply ? supply.price : 0);
+ }, 0) * getTotalQuantity()
+ );
+ };
const formatCurrency = (amount: number) => {
- return new Intl.NumberFormat('ru-RU', {
- style: 'currency',
- currency: 'RUB',
- minimumFractionDigits: 0
- }).format(amount)
- }
+ return new Intl.NumberFormat("ru-RU", {
+ style: "currency",
+ currency: "RUB",
+ minimumFractionDigits: 0,
+ }).format(amount);
+ };
// Создание поставки
const handleCreateSupplyInternal = async () => {
if (supplyItems.length === 0) {
- toast.error('Добавьте товары в поставку')
- return
+ toast.error("Добавьте товары в поставку");
+ return;
}
- if (!deliveryDate) {
- toast.error('Выберите дату поставки')
- return
+ if (!deliveryDateOriginal) {
+ toast.error("Выберите дату поставки");
+ return;
}
- if (supplyItems.some(item => item.quantity <= 0 || item.pricePerUnit <= 0)) {
- toast.error('Укажите количество и цену для всех товаров')
- return
+ if (
+ supplyItems.some((item) => item.quantity <= 0 || item.pricePerUnit <= 0)
+ ) {
+ toast.error("Укажите количество и цену для всех товаров");
+ return;
}
try {
const supplyInput = {
- deliveryDate: deliveryDate.toISOString().split('T')[0],
- cards: supplyItems.map(item => ({
+ deliveryDate: deliveryDateOriginal.toISOString().split("T")[0],
+ cards: supplyItems.map((item) => ({
nmId: item.card.nmID.toString(),
vendorCode: item.card.vendorCode,
title: item.card.title,
@@ -471,426 +624,846 @@ export function DirectSupplyCreation({ onComplete, onCreateSupply, canCreateSupp
selectedFulfillmentServices: selectedServices,
selectedConsumableOrg: selectedFulfillmentOrg,
selectedConsumableServices: selectedConsumables,
- deliveryDate: deliveryDate.toISOString().split('T')[0],
- mediaFiles: item.card.mediaFiles
- }))
- }
+ deliveryDate: deliveryDateOriginal.toISOString().split("T")[0],
+ mediaFiles: item.card.mediaFiles,
+ })),
+ };
- await createSupply({ variables: { input: supplyInput } })
- toast.success('Поставка успешно создана!')
- onComplete()
+ await createSupply({ variables: { input: supplyInput } });
+ toast.success("Поставка успешно создана!");
+ onComplete();
} catch (error) {
- console.error('Error creating supply:', error)
- toast.error('Ошибка при создании поставки')
+ console.error("Error creating supply:", error);
+ toast.error("Ошибка при создании поставки");
}
- }
+ };
// Обработка внешнего вызова создания поставки
React.useEffect(() => {
if (isCreatingSupply) {
- handleCreateSupplyInternal()
+ handleCreateSupplyInternal();
}
- }, [isCreatingSupply])
+ }, [isCreatingSupply]);
// Обновление статуса возможности создания поставки
React.useEffect(() => {
- const canCreate = supplyItems.length > 0 &&
- deliveryDate !== null &&
- supplyItems.every(item => item.quantity > 0 && item.pricePerUnit > 0)
-
- if (onCanCreateSupplyChange) {
- onCanCreateSupplyChange(canCreate)
- }
- }, [supplyItems, deliveryDate, onCanCreateSupplyChange])
+ const canCreate =
+ supplyItems.length > 0 &&
+ deliveryDateOriginal !== null &&
+ supplyItems.every((item) => item.quantity > 0 && item.pricePerUnit > 0);
- const fulfillmentOrgs = (counterpartiesData?.myCounterparties || []).filter((org: Organization) => org.type === 'FULFILLMENT')
+ if (onCanCreateSupplyChange) {
+ onCanCreateSupplyChange(canCreate);
+ }
+ }, [supplyItems, deliveryDateOriginal, onCanCreateSupplyChange]);
+
+ const fulfillmentOrgs = (counterpartiesData?.myCounterparties || []).filter(
+ (org: Organization) => org.type === "FULFILLMENT"
+ );
const markets = [
- { value: 'sadovod', label: 'Садовод' },
- { value: 'tyak-moscow', label: 'ТЯК Москва' }
- ]
+ { value: "sadovod", label: "Садовод" },
+ { value: "tyak-moscow", label: "ТЯК Москва" },
+ ];
return (
-
- {/* Основные настройки */}
-
-
-
-
- {/* Поиск и карточки */}
-
-
-
- {/* Услуги и расходники в одной строке */}
- {selectedFulfillmentOrg && (
-
-
- {/* Дата поставки */}
-
-
-
-
-
-
-
- setDeliveryDate(date || undefined)}
- minDate={new Date()}
- inline
- locale="ru"
- />
-
-
-
-
- {/* Фулфилмент */}
-
-
-
-
- {/* Показатели */}
-
-
- Товаров
- {getTotalQuantity()}
-
-
- Стоимость
- {formatCurrency(getTotalItemsCost()).replace(' ₽', '₽')}
-
-
- Услуги ФФ
- {formatCurrency(getServicesCost() + getConsumablesCost()).replace(' ₽', '₽')}
-
- setSearchTerm(e.target.value)}
- className="bg-white/5 border-white/20 text-white placeholder-white/50 h-7 text-xs flex-1"
- onKeyPress={(e) => e.key === 'Enter' && searchCards()}
- />
-
-
-
-
- {loading ? (
- [...Array(6)].map((_, i) => (
-
- ))
- ) : (
- wbCards.map((card) => {
- const isInSupply = supplyItems.some(item => item.card.nmID === card.nmID)
- return (
-
- addToSupply(card)}
- >
-
- {isInSupply && (
-
- )
- })
- )}
-
- ✓
-
- )}
-
+ <>
+
+
+ {/* НОВЫЙ БЛОК СОЗДАНИЯ ПОСТАВКИ */}
+
+ {/* Первая строка */}
+
+ {/* 1. Модуль выбора даты */}
-
-
Услуги фулфилмента:
-
- {organizationServices[selectedFulfillmentOrg] ? (
- organizationServices[selectedFulfillmentOrg].map((service) => (
-
- ))
- ) : (
- Загрузка...
- )}
+
+
+ {/* 2. Модуль выбора фулфилмента */}
+ setDeliveryDate(e.target.value)}
+ className="w-full h-8 rounded-lg border-0 bg-white/20 backdrop-blur px-2 py-1 text-white placeholder:text-white/50 focus:bg-white/30 focus:outline-none focus:ring-1 focus:ring-white/20 text-xs font-medium"
+ min={new Date().toISOString().split("T")[0]}
+ />
-
+
+ {/* Вторая строка */}
+ Расходные материалы:
-
- {organizationSupplies[selectedFulfillmentOrg] ? (
- organizationSupplies[selectedFulfillmentOrg].map((supply) => (
-
- ))
- ) : (
- Загрузка...
- )}
+
+
+
+
+ {/* 3. Объём товаров */}
+
+
+
+ setGoodsVolume(parseFloat(e.target.value) || 0)
+ }
+ placeholder="м³"
+ className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
+ />
+
+
+ {/* 4. Грузовые места */}
+
+
+ setCargoPlaces(parseInt(e.target.value) || 0)}
+ placeholder="шт"
+ className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
+ />
+
+
+ {/* 5. Цена товаров */}
+
- )}
- {/* Компактная таблица товаров */}
-
+
+ setGoodsPrice(parseFloat(e.target.value) || 0)}
+ placeholder="₽"
+ className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
+ />
+
+
+ {/* 6. Цена услуг фулфилмента */}
+
+
+
+ setFulfillmentServicesPrice(parseFloat(e.target.value) || 0)
+ }
+ placeholder="₽"
+ className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
+ />
+
+
+ {/* 7. Цена логистики */}
+
+
+
+ setLogisticsPrice(parseFloat(e.target.value) || 0)
+ }
+ placeholder="₽"
+ className="h-8 bg-white/20 border-0 text-white placeholder:text-white/50 focus:bg-white/30 focus:ring-1 focus:ring-white/20 text-xs"
+ />
+
+
+ {/* 8. Итоговая сумма */}
+
+
+
+
+ {formatCurrency(getTotalSum()).replace(" ₽", " ₽")}
+
- Товары в поставке
-
-
-
- {supplyItems.length === 0 ? (
-
-
-
+
Добавьте товары из карточек выше
+ {/* Блок поиска товаров - оптимизированное расположение */} +
+
+
- ) : (
+
+ {/* Карточки товаров - увеличенный размер и единообразность */}
+ setSearchTerm(e.target.value)}
+ className="bg-white/20 border-0 text-white placeholder-white/60 h-8 text-xs flex-1 focus:bg-white/30 focus:ring-1 focus:ring-white/20"
+ onKeyPress={(e) => e.key === "Enter" && searchCards()}
+ />
+
+
- {supplyItems.map((item) => (
-
- {/* Товар */}
-
-
-
+
+ )}
+
+ {/* Модуль товаров в поставке - новый дизайн */}
+
+
+
+ {/* Модальное окно создания поставщика */}
+
-
+
+
+ {/* Услуги и расходники в одной строке */}
+ {selectedFulfillmentOrg && (
+ {item.card.title}
- Арт: {item.card.vendorCode}
+
+
+ Найдено товаров: {wbCards.length}
+
+ {supplyItems.length > 0 && (
+
+ В поставке: {supplyItems.length}
+
+ )}
+
+
+
+ {loading
+ ? [...Array(12)].map((_, i) => (
+
+ ))
+ : wbCards.map((card) => {
+ const isInSupply = supplyItems.some(
+ (item) => item.card.nmID === card.nmID
+ );
+ return (
+
+
+ {!loading && wbCards.length === 0 && (
+ addToSupply(card)}
+ >
+
+ );
+ })}
+
+
+
+ {/* Оверлей с информацией */}
+
+
+
+
+ {/* Индикатор добавления в поставку */}
+ {isInSupply && (
+
+
+
+ {card.title}
+
+
+ Арт: {card.vendorCode}
+
+ {card.sizes && card.sizes[0] && (
+
+ от{" "}
+ {card.sizes[0].discountedPrice ||
+ card.sizes[0].price}{" "}
+ ₽
+
+ )}
+
+ ✓
+
+ )}
+
+ {/* Индикатор при наведении */}
+
+
+
+
+
+
+ )}
+ + {searchTerm + ? "Товары не найдены" + : "Введите запрос для поиска товаров"} +
+ {searchTerm && ( ++ Попробуйте изменить условия поиска +
+ )} +
+
+
+
+
+ Услуги фулфилмента:
+
+ {organizationServices[selectedFulfillmentOrg] ? (
+ organizationServices[selectedFulfillmentOrg].map(
+ (service) => (
+
+ )
+ )
+ ) : (
+ Загрузка...
+ )}
+
+
+
+ Расходные материалы:
+
+ {organizationSupplies[selectedFulfillmentOrg] ? (
+ organizationSupplies[selectedFulfillmentOrg].map(
+ (supply) => (
+
+ )
+ )
+ ) : (
+ Загрузка...
+ )}
+
+
+
+ Товары в поставке
+
+
+
+
+ {supplyItems.length === 0 ? (
+
+
+
+ ) : (
+ + Добавьте товары из карточек выше +
+
+ {supplyItems.map((item) => (
+
+ {/* Заголовок товара с кнопкой удаления */}
+
- {/* Количество */}
-
+
-
+
+
+ {item.card.title}
+
+
+ Арт: {item.card.vendorCode}
+
+
- updateSupplyItem(item.card.nmID, 'quantity', parseInt(e.target.value) || 0)}
- className="bg-purple-500 border-0 text-white text-center h-6 text-xs font-bold"
- min="1"
- />
-
-
- {/* Цена */}
-
- updateSupplyItem(item.card.nmID, 'pricePerUnit', parseFloat(e.target.value) || 0)}
- className="bg-white/10 border-white/20 text-white text-center h-6 text-xs"
- placeholder="Цена"
- />
-
-
- {/* Поставщик */}
-
-
+ )}
+