[{"data":1,"prerenderedAt":212},["ShallowReactive",2],{"project-ru-multi-warehouse-carts":3,"recommendation-mention-sources":150},{"id":4,"title":5,"body":6,"category":120,"context":121,"cover":122,"coverAlt":123,"demo":124,"description":125,"extension":126,"featured":127,"locale":128,"meta":129,"navigation":130,"order":131,"path":132,"published":130,"repository":124,"results":133,"role":134,"seo":135,"slug":136,"stack":137,"stem":147,"year":148,"__hash__":149},"projects\u002Fru\u002Fprojects\u002Fmulti-warehouse-carts.md","Корзины по складам для оптовых покупателей",{"type":7,"value":8,"toc":105},"minimark",[9,14,18,21,25,28,32,37,40,44,47,50,54,57,60,64,67,71,74,78,94],[10,11,13],"h2",{"id":12},"задача","Задача",[15,16,17],"p",{},"Некоторые магазины на inSales продают оптом сразу из нескольких источников: со своего склада и со склада партнёра, в одном каталоге. Оптовику нужно заказать один и тот же товар так: 15 штук с основного склада и 8 с партнёрского. Корзина должна держать эти выборы раздельно, у каждого склада свои количества и своя сумма.",[15,19,20],{},"Корзина витрины была рассчитана на один список товаров. Все операции с ней в CommonJS, общей JavaScript-библиотеке витрин inSales, исходили из того, что корзина одна. Вызывают библиотеку отовсюду: из карточки товара, каталога, главной, блоков сопутствующих товаров. Любая правка в ней затрагивает множество магазинов, в том числе тех, у кого один склад и кому эта функция не нужна.",[10,22,24],{"id":23},"моя-роль","Моя роль",[15,26,27],{},"Проект был на мне от реализации на витрине до production. Я дорабатывал общую библиотеку, формулировал нужные фронтенду изменения API и согласовывал формат ответа с backend-разработчиком, который делал серверную часть. С продуктовым менеджером уточнял приоритеты и открытые продуктовые вопросы, при тестировании разбирал сбои в корзинах по складам. Начал в августе 2023 года, к ноябрю функция уже работала в production.",[10,29,31],{"id":30},"что-я-сделал","Что я сделал",[33,34,36],"h3",{"id":35},"склад-передаётся-в-каждой-операции-с-корзиной","Склад передаётся в каждой операции с корзиной",[15,38,39],{},"В операциях добавления, изменения и удаления товара нельзя было указать, к какому складу относится изменение. Я добавил выбранный склад во все три, и карточка товара с двумя полями количества стала обновлять группу нужного склада. Заодно договорился с бэкендом, на какие поля ответа опирается интерфейс. Например, склад на верхнем уровне ответа означает склад основной корзины, и это не обязательно склад последней операции. Под эти договорённости я и переделал обработку ответа в библиотеке.",[33,41,43],{"id":42},"расчёт-количества-начинается-с-выбранного-склада","Расчёт количества начинается с выбранного склада",[15,45,46],{},"Когда покупатель вводит новое количество, библиотека считает, сколько штук добавить или убрать. Исходное значение она брала не из той корзины, и изменение для одного склада могло считаться от количества на другом. Я исправил расчёт: текущее количество берётся из группы выбранного склада. Если там 15 штук, а покупатель просит 5, библиотека убирает 10.",[15,48,49],{},"Доступный остаток и заказанное количество тоже разные данные. Я запросил у бэкенда остаток по каждому товару внутри группы склада: прежнее поле количества говорило только о том, сколько заказал покупатель.",[33,51,53],{"id":52},"пересекающиеся-запросы-теряли-выбор","Пересекающиеся запросы теряли выбор",[15,55,56],{},"На тестировании я заметил, что быстрые изменения по разным складам могут перезаписывать друг друга. Покупатель выбирал товары на обоих складах, а в корзине оставался выбор только по одному. Библиотека и раньше объединяла накопленные задачи в один запрос, но старый API принимал один склад на запрос. Изменения по двум складам уходили отдельно и могли пересечься.",[15,58,59],{},"Я описал сбой и сформулировал требование: библиотека собирает изменения по нескольким складам и отправляет их группами в одном запросе, а сервер раскладывает каждую группу в корзину своего склада. Серверную часть сделал backend-разработчик.",[33,61,63],{"id":62},"обычная-корзина-работает-как-раньше","Обычная корзина работает как раньше",[15,65,66],{},"Запрос без склада выключал режим корзин по складам, а одновременно два режима работать не могли. Один и тот же товар можно добавить из разных виджетов на странице, поэтому я проверял, сохраняется ли выбор складов при добавлении из каталога, с главной и из сопутствующих товаров. Данные обычной корзины обрабатывал отдельно от сгруппированных по складам. Магазины с одним складом сохранили привычные поля количества и свой код.",[33,68,70],{"id":69},"карточка-товара-и-корзина-показывают-одни-и-те-же-числа","Карточка товара и корзина показывают одни и те же числа",[15,72,73],{},"Я связал обновления корзин по складам с полями количества в карточке товара: и там, и в корзине видно, сколько выбрано на каждом складе. Ещё разбирал случай, когда покупатель меняет количество и сразу переходит к оформлению. На сервер уходили два запроса, последний ответ был верным, а виджет показывал результат первого. Я воспроизвёл сценарий и записал его для команды.",[10,75,77],{"id":76},"результат","Результат",[79,80,81,85,88,91],"ul",{},[82,83,84],"li",{},"К ноябрю 2023 года функция работала в production. Работу я начал в августе 2023-го.",[82,86,87],{},"Оптовый покупатель выбирает количество отдельно для каждого склада, а корзина показывает по каждому складу свои количества и сумму.",[82,89,90],{},"Групповое обновление вышло в production: изменения по нескольким складам уходят на сервер одним запросом. Так закрылся найденный мной на тестировании случай с потерей выбора.",[82,92,93],{},"Магазины с одним складом остались на обычной корзине, их код менять не пришлось.",[15,95,96,97,104],{},"Своих цифр по использованию или выручке от этой функции у меня нет. Зачем вообще продавать партнёрские остатки, видно по ",[98,99,103],"a",{"href":100,"rel":101},"https:\u002F\u002Fwww.insales.ru\u002Fblogs\u002Fcases\u002Fhited",[102],"nofollow","кейсу, который опубликовал inSales",": у одного магазина с каталогом на 45 000 SKU партнёрские склады дали 21% онлайн-продаж за 2024 год. Это показатель самого магазина, а не результат моей работы.",{"title":106,"searchDepth":107,"depth":107,"links":108},"",2,[109,110,111,119],{"id":12,"depth":107,"text":13},{"id":23,"depth":107,"text":24},{"id":30,"depth":107,"text":31,"children":112},[113,115,116,117,118],{"id":35,"depth":114,"text":36},3,{"id":42,"depth":114,"text":43},{"id":52,"depth":114,"text":53},{"id":62,"depth":114,"text":63},{"id":69,"depth":114,"text":70},{"id":76,"depth":107,"text":77},"Платформа витрин","inSales","\u002Fimages\u002Fwork\u002Fmulti-warehouse-carts\u002Fgrouped-cart-update.svg","Схема: изменения покупателя по двум складам собираются в очередь библиотеки корзины и уходят одним запросом с группировкой по складам",null,"Корзина для оптовых витрин inSales, где покупатель выбирает количество отдельно для каждого склада. Общая библиотека витрин отправляет изменения по нескольким складам одним запросом. В production — к ноябрю 2023 года.","md",false,"ru",{},true,9,"\u002Fru\u002Fprojects\u002Fmulti-warehouse-carts",[],"Отвечал за реализацию на витрине и согласование API",{"title":5,"description":125},"multi-warehouse-carts",[138,139,140,141,142,143,144,145,146],"JavaScript","API Development","AJAX","State Management","E-Commerce","Debugging","Front-End Development","API Integration","Requirements Analysis","ru\u002Fprojects\u002Fmulti-warehouse-carts","Август – ноябрь 2023","yQEzCsBjxtnN0SG2-bQmQBX6qcHt3P7yJTXhMMtnITY",[151,162,167,182,192,197,202,207],{"stem":152,"name":153,"date":154,"featured":107,"published":130,"mentions":155},"recommendations\u002Fandrey-stikheev","Andrey Stikheev","2026-09-08",[156,159],{"project":157,"quote":158},"product-image-editor","Among his most notable projects was a sophisticated browser-based image editor built with TypeScript and Fabric.js, now used by thousands of merchants.",{"project":160,"quote":161},"abandoned-carts","He also developed the frontend for a paid abandoned-cart product and contributed to the production migration from Vue 2 to Vue 3.",{"stem":163,"name":164,"date":165,"featured":124,"published":130,"mentions":166},"recommendations\u002Fdaniil-churakov","Daniil Churakov","2026-08-27",[],{"stem":168,"name":169,"date":170,"featured":114,"published":130,"mentions":171},"recommendations\u002Fdmitry-sadovsky","Dmitry Sadovsky","2026-08-22",[172,175,177,180],{"project":173,"quote":174},"visual-storefront","For years he was the driving force behind our visual storefront editor — a large, complex application where merchants build their online stores.",{"project":157,"quote":176},"It became the tool merchants use to design product images right inside the admin panel — the kind of component companies usually buy rather than build.",{"project":178,"quote":179},"image-editor","What impressed me most was the infographic editor he built essentially from scratch: a full canvas-based graphic editor with artboards, shapes, text styling, templates, and undo\u002Fredo history, powered by an editor core he developed himself.",{"project":160,"quote":181},"Our abandoned carts functionality — with multi-channel customer messaging, communication history, and analytics — was largely his work, from UI down to the details of how requests are cancelled and states stay in sync.",{"stem":183,"name":184,"date":185,"featured":186,"published":130,"mentions":187},"recommendations\u002Fegor-fedkin","Egor Fedkin","2026-08-31",1,[188,190],{"project":157,"quote":189},"Today, the tool is actively used by around 3,000 users.",{"project":160,"quote":191},"It has already generated approximately twice the revenue needed to cover its development costs.",{"stem":193,"name":194,"date":195,"featured":124,"published":130,"mentions":196},"recommendations\u002Femmanuel-stephen","Emmanuel Stephen","2026-10-11",[],{"stem":198,"name":199,"date":200,"featured":124,"published":130,"mentions":201},"recommendations\u002Fevgeny-lonshakov","Евгений Лоншаков","2026-09-21",[],{"stem":203,"name":204,"date":205,"featured":124,"published":130,"mentions":206},"recommendations\u002Froman-gorbachev","Роман Горбачев","2026-09-17",[],{"stem":208,"name":209,"date":210,"featured":124,"published":130,"mentions":211},"recommendations\u002Fvladimir-bedarev","Bedarev Vladimir","2026-09-16",[],1793195987412]