80 lines
12 KiB
JavaScript
80 lines
12 KiB
JavaScript
// Fictional training recipes and prices. No customer or Solnce catalog data.
|
||
const suppliers=[
|
||
['fresh','Демо: овощи и молочные продукты'],['protein','Демо: мясо и рыба'],
|
||
['bakery','Демо: пекарня'],['grocery','Демо: бакалея'],['pack','Демо: упаковка']
|
||
].map(([key,name])=>({id:`demo-v1-supplier-${key}`,name,active:true,category:'Учебный поставщик',contact:'',phone:'',email:'',address:'',deliveryDays:'По договорённости',minOrder:0,note:'Вымышленный поставщик для знакомства с приложением.'}));
|
||
// key, product name, purchase unit price, supplier, unit
|
||
const products=[
|
||
['baguette','Багет',240,'bakery'],['tomato','Томаты',260,'fresh'],['basil','Базилик',1500,'fresh'],
|
||
['oil','Масло оливковое',1100,'grocery'],['rye','Хлеб ржаной',220,'bakery'],
|
||
['salmon','Лосось слабосолёный',2400,'protein'],['cream','Сыр сливочный',850,'fresh'],
|
||
['cucumber','Огурцы',220,'fresh'],['dill','Укроп',700,'fresh'],['toast','Хлеб тостовый',240,'bakery'],
|
||
['chicken','Филе курицы готовое',850,'protein'],['lettuce','Салат листовой',700,'fresh'],
|
||
['mozzarella','Моцарелла мини',950,'fresh'],['cherry','Томаты черри',450,'fresh'],
|
||
['tartlet','Тарталетки готовые',750,'bakery'],['mushroom','Шампиньоны',340,'fresh'],
|
||
['cooking-cream','Сливки',500,'fresh'],['gouda','Сыр гауда',900,'fresh'],['onion','Лук репчатый',80,'fresh'],
|
||
['tortilla','Тортилья',450,'bakery'],['turkey','Ветчина из индейки',1000,'protein'],['pepper','Перец сладкий',380,'fresh'],
|
||
['brie','Сыр бри',1600,'fresh'],['blue','Сыр с голубой плесенью',1700,'fresh'],['grape','Виноград',420,'fresh'],
|
||
['walnut','Орех грецкий очищенный',1000,'grocery'],['honey','Мёд',800,'grocery'],['cracker','Крекер',500,'bakery'],
|
||
['salami','Салями',1400,'protein'],['beef','Ростбиф готовый',2000,'protein'],['pickle','Корнишоны',400,'grocery'],
|
||
['olive','Маслины без косточек',650,'grocery'],['mustard','Горчица',350,'grocery'],['carrot','Морковь',90,'fresh'],
|
||
['hummus','Хумус готовый',600,'grocery'],['biscuit','Крошка бисквитная',500,'bakery'],
|
||
['strawberry','Клубника',750,'fresh'],['blueberry','Голубика',1300,'fresh'],['sugar','Пудра сахарная',160,'grocery'],
|
||
['box','Коробка для кейтеринга',65,'pack','шт.'],['skewer','Шпажка бамбуковая',2,'pack','шт.'],
|
||
['cup','Стаканчик десертный',9,'pack','шт.'],['sauce-cup','Соусник',7,'pack','шт.']
|
||
].map(([key,name,cost,supplier,unit='кг'])=>({key,id:`demo-v1-stock-${key}`,name,cost,unit,supplierId:`demo-v1-supplier-${supplier}`}));
|
||
const byKey=new Map(products.map(p=>[p.key,p]));
|
||
// Product quantities are gross/net in grams; packaging is in pieces.
|
||
const recipes=[
|
||
{id:'bruschetta-tomato',name:'Брускетты с томатами и базиликом',price:2200,pieces:24,
|
||
rows:[['baguette',480,480],['tomato',330,300],['basil',26,24],['oil',36,36]],
|
||
composition:['24 брускетты с томатами, базиликом и оливковым маслом'],allergens:['Глютен'],
|
||
steps:['Нарезать багет на 24 одинаковых ломтика и подсушить.','Подготовить томаты и базилик, смешать с маслом.','Распределить начинку по ломтикам, уложить в коробку.']},
|
||
{id:'salmon-cream',name:'Канапе с лососем и сливочным сыром',price:3200,pieces:16,
|
||
rows:[['rye',240,240],['salmon',240,240],['cream',160,160],['cucumber',84,72],['dill',10,8]],
|
||
composition:['16 канапе на ржаном хлебе с лососем, сливочным сыром и огурцом'],allergens:['Рыба','Молоко','Глютен'],
|
||
steps:['Вырезать из ржаного хлеба 16 основ.','Нанести сливочный сыр, добавить ломтики огурца и лосося.','Украсить укропом и уложить в коробку.']},
|
||
{id:'chicken-sandwich',name:'Мини-сэндвичи с курицей',price:2400,pieces:12,
|
||
rows:[['toast',360,360],['chicken',240,240],['cream',120,120],['tomato',92,84],['lettuce',45,36]],
|
||
composition:['12 мини-сэндвичей с готовым куриным филе, томатами, салатом и сливочным сыром'],allergens:['Глютен','Молоко'],
|
||
steps:['Нарезать готовое куриное филе, подготовить овощи и салат.','Собрать сэндвичи из тостового хлеба, сыра, курицы и овощей.','Нарезать на 12 одинаковых мини-сэндвичей и упаковать.']},
|
||
{id:'caprese',name:'Канапе «Капрезе»',price:2100,pieces:20,
|
||
rows:[['mozzarella',360,360],['cherry',308,280],['basil',22,20],['oil',40,40],['skewer',20,20]],
|
||
composition:['20 шпажек с моцареллой, черри и базиликом'],allergens:['Молоко'],
|
||
steps:['Подготовить черри и базилик, обсушить моцареллу.','Собрать 20 шпажек, распределив ингредиенты поровну.','Добавить оливковое масло, уложить в коробку.']},
|
||
{id:'mushroom-tartlet',name:'Тарталетки с грибами и сыром',price:2300,pieces:20,
|
||
rows:[['tartlet',200,200],['mushroom',440,300],['cooking-cream',100,100],['gouda',120,120],['onion',88,60],['oil',20,20]],
|
||
composition:['20 тарталеток с шампиньонами, сливками и сыром'],allergens:['Глютен','Молоко'],
|
||
steps:['Подготовить и обжарить лук с грибами на масле; в расчёте учтены потери массы.','Добавить сливки и распределить начинку по 20 тарталеткам.','Добавить сыр, запечь до расплавления и упаковать после охлаждения.']},
|
||
{id:'turkey-wrap',name:'Роллы из тортильи с индейкой',price:2700,pieces:25,
|
||
rows:[['tortilla',320,320],['turkey',320,320],['cream',180,180],['lettuce',75,60],['pepper',150,120]],
|
||
composition:['25 мини-роллов с ветчиной из индейки, сливочным сыром, перцем и салатом'],allergens:['Глютен','Молоко'],
|
||
steps:['Подготовить салат и перец, нарезать ветчину.','Нанести сыр на тортильи, распределить начинку и свернуть плотные рулеты.','Нарезать на 25 порций и уложить срезом вверх.']},
|
||
{id:'cheese-fruit',name:'Сырный бокс с виноградом и орехами',price:2900,pieces:0,
|
||
rows:[['brie',200,200],['gouda',180,180],['blue',120,120],['grape',176,160],['walnut',50,50],['honey',40,40],['cracker',50,50],['sauce-cup',1,1]],
|
||
composition:['Бри, гауда и сыр с голубой плесенью','Виноград, грецкий орех, мёд и крекер'],allergens:['Молоко','Орехи','Глютен'],
|
||
steps:['Нарезать сыры порционными кусочками.','Подготовить виноград, переложить мёд в соусник.','Разложить сыры, виноград, орехи и крекер отдельными секциями.']},
|
||
{id:'meat-assortment',name:'Мясное ассорти с корнишонами',price:3200,pieces:0,
|
||
rows:[['salami',180,180],['beef',220,220],['turkey',150,150],['pickle',90,80],['olive',45,40],['mustard',30,30],['sauce-cup',1,1]],
|
||
composition:['Салями, готовый ростбиф и ветчина из индейки','Корнишоны, маслины и горчица'],allergens:['Горчица'],
|
||
steps:['Нарезать готовые мясные продукты тонкими ломтиками.','Обсушить корнишоны и маслины, переложить горчицу в соусник.','Разложить ассорти секциями, добавить гарниры.']},
|
||
{id:'vegetables-hummus',name:'Овощной бокс с хумусом',price:2100,pieces:0,
|
||
rows:[['carrot',300,250],['cucumber',275,250],['pepper',250,200],['cherry',165,150],['hummus',150,150],['sauce-cup',1,1]],
|
||
composition:['Морковь, огурцы, сладкий перец и черри','Хумус в отдельном соуснике'],allergens:['Кунжут в составе хумуса'],
|
||
steps:['Вымыть и подготовить овощи; очистить морковь и перец.','Нарезать овощи палочками, черри оставить целыми.','Переложить хумус в соусник и разложить овощи вокруг него.']},
|
||
{id:'berry-dessert',name:'Ягодные мини-десерты',price:2800,pieces:16,
|
||
rows:[['biscuit',160,160],['cream',320,320],['cooking-cream',160,160],['strawberry',176,160],['blueberry',84,80],['sugar',80,80],['cup',16,16]],
|
||
composition:['16 десертов со сливочным кремом, бисквитом, клубникой и голубикой'],allergens:['Молоко','Глютен','Яйцо в составе бисквита'],
|
||
steps:['Подготовить ягоды, разделить готовый бисквит на крошку.','Смешать сливочный сыр, сливки и сахарную пудру в крем.','Собрать 16 стаканчиков слоями: бисквит, крем, ягоды.']}
|
||
];
|
||
const round=n=>Math.round(n*1000)/1000;
|
||
const totals=new Map();
|
||
export const boxes=recipes.map((r,index)=>{
|
||
const rows=[...r.rows,['box',1,1]].map(([key,gross,net])=>{const p=byKey.get(key),scale=p.unit==='кг'?1000:1;const row={productId:p.id,name:p.name,unit:p.unit,gross:round(gross/scale),net:round(net/scale),unitCost:p.cost};totals.set(key,round((totals.get(key)||0)+row.gross));return row});
|
||
const outputGrams=Math.round(rows.filter(p=>p.unit==='кг').reduce((s,p)=>s+p.net*1000,0));
|
||
return {id:`demo-v1-${r.id}`,name:r.name,price:r.price,category:0,catalogSection:'Демонстрационные боксы',pieces:r.pieces,weight:`${outputGrams} г`,photo:`demo/images/${r.id}.png`,composition:r.composition,ingredients:rows.map(p=>[p.name,p.gross,p.unit]),demo:true,ttk:{number:`ДЕМО-${String(index+1).padStart(2,'0')}`,basis:'На 1 готовый бокс',outputGrams,pieces:r.pieces,rows,steps:r.steps,allergens:r.allergens,ingredientCost:Math.round(rows.reduce((s,p)=>s+p.gross*p.unitCost,0)*100)/100,note:'Учебные нормы и цены для знакомства с приложением.'}};
|
||
});
|
||
export const stock=products.map((p,index)=>({id:p.id,name:p.name,unit:p.unit,cost:p.cost,qty:(p.unit==='шт.'?Math.floor:round)((totals.get(p.key)||0)*[0,0.45,2,0.7][index%4]),min:p.unit==='кг'?0.1:2,supplierId:p.supplierId,lastPurchaseDate:'',lastPurchasePrice:0}));
|
||
export {suppliers};
|
||
export const demo={version:1,batch:'trial-demo-v1',boxes,stock,suppliers,scenario:{lines:[{id:'demo-v1-bruschetta-tomato',qty:2},{id:'demo-v1-salmon-cream',qty:1},{id:'demo-v1-caprese',qty:1}]}};
|