// Конфигурация калькулятора мыла
import { CalculatorConfig } from '@/lib/calculator-types';
import { calculateSoapStep, calculateSoapSubtotal, round } from './calc';
export const soapCalculatorConfig: CalculatorConfig = {
id: 'soap',
name: 'Калькулятор мыла',
description: 'Расчет себестоимости мыла ручной работы',
icon: '🧼',
fields: [
{
id: 'soapName',
type: 'text',
label: 'Название мыла',
placeholder: 'Введите название',
defaultValue: '',
gridCols: 1,
required: false,
},
{
id: 'photo',
type: 'file',
label: 'Фото мыла (необязательно)',
accept: 'image/*',
gridCols: 1,
required: false,
},
{
id: 'weight',
type: 'number',
label: 'Вес мыла, г',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'base',
showStepAfter: 'base',
},
{
id: 'basePrice',
type: 'number',
label: 'Цена основы, руб',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'base',
},
{
id: 'aromaPrice',
type: 'number',
label: 'Цена отдушки, руб',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'aroma',
showStepAfter: 'aroma',
},
{
id: 'aromaWeight',
type: 'number',
label: 'Фасовка отдушки, г',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'aroma',
},
{
id: 'pigmentPrice',
type: 'number',
label: 'Цена пигмента, руб',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'pigment',
showStepAfter: 'pigment',
},
{
id: 'pigmentWeight',
type: 'number',
label: 'Фасовка пигмента, г',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'pigment',
},
{
id: 'moldPrice',
type: 'number',
label: 'Цена формы, руб',
defaultValue: '',
gridCols: 1,
required: false,
groupName: 'mold',
showStepAfter: 'mold',
},
{
id: 'box',
type: 'number',
label: 'Пакет/коробка, руб',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'packaging',
showStepAfter: 'packaging',
},
{
id: 'filler',
type: 'number',
label: 'Наполнитель, руб',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'packaging',
},
{
id: 'ribbon',
type: 'number',
label: 'Лента, руб',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'packaging',
},
{
id: 'label',
type: 'number',
label: 'Наклейка, руб',
defaultValue: '',
gridCols: 2,
required: false,
groupName: 'packaging',
},
{
id: 'markup',
type: 'number',
label: 'Наценка, %',
defaultValue: '',
gridCols: 2,
required: false,
},
],
calculationSteps: [
{
id: 'base',
name: 'Себестоимость основы',
formula: (values) => round(calculateSoapStep('base', values)),
formulaDescription: '(вес / 1000) * цена_основы',
},
{
id: 'aroma',
name: 'Себестоимость отдушки (1 %)',
formula: (values) => round(calculateSoapStep('aroma', values)),
formulaDescription: '((вес * 0.01) / фасовка_отдушки) * цена_отдушки',
},
{
id: 'pigment',
name: 'Себестоимость пигмента (0.5 %)',
formula: (values) => round(calculateSoapStep('pigment', values)),
formulaDescription: '((вес * 0.005) / фасовка_пигмента) * цена_пигмента',
},
{
id: 'mold',
name: 'Себестоимость формы',
formula: (values) => round(calculateSoapStep('mold', values)),
formulaDescription: 'цена_формы / 100',
},
{
id: 'packaging',
name: 'Стоимость упаковки',
formula: (values) => round(calculateSoapStep('packaging', values)),
formulaDescription: 'пакет + наполнитель + лента + наклейка',
},
],
subtotals: [
{
id: 'operational',
name: 'Операционные расходы (5 %)',
formula: (values, steps) => round(calculateSoapSubtotal('operational', values, steps)),
formulaDescription: '(основа + отдушка + пигмент + форма + упаковка) * 0.05',
},
{
id: 'total',
name: 'Итого себестоимость',
formula: (values, steps) => round(calculateSoapSubtotal('total', values, steps)),
highlight: true,
formulaDescription: 'основа + отдушка + пигмент + форма + упаковка + операционные',
},
],
additionalCalculations: [
{
id: 'finalPrice',
name: 'Итоговая цена с наценкой',
formula: (values, steps, subtotals) => {
const total = subtotals.total || 0;
const markup = values.markup || 0;
return round(total * (1 + markup / 100));
},
formulaDescription: 'итого_себестоимость * (1 + наценка / 100)',
},
{
id: 'pricePer100g',
name: 'Цена за 100 г',
formula: (values, steps, subtotals, additional) => {
const weight = values.weight || 0;
const finalPrice = additional?.finalPrice || 0;
if (weight > 0) {
return round((finalPrice / weight) * 100);
}
return 0;
},
formulaDescription: '(итоговая_цена / вес) * 100',
},
],
formatTelegramMessage: (values, steps, subtotals, additional) => {
const soapName = values.soapName || 'Без названия';
const weight = values.weight || 0;
const box = values.box || 0;
const filler = values.filler || 0;
const ribbon = values.ribbon || 0;
const label = values.label || 0;
const markup = values.markup || 0;
const totalCost = subtotals.total || 0;
const finalPrice = additional?.finalPrice || 0;
const pricePer100g = additional?.pricePer100g || 0;
let text = `🧼 Расчёт мыла: ${soapName}\n\n`;
text += `⚖️ Вес мыла: ${weight} г\n\n`;
text += `📦 Упаковка:\n`;
text += ` 📥 Пакет/коробка: ${box} ₽\n`;
text += ` 🌾 Наполнитель: ${filler} ₽\n`;
text += ` 🎀 Лента: ${ribbon} ₽\n`;
text += ` 🏷️ Наклейка: ${label} ₽\n\n`;
text += `💹 Наценка: ${markup}%\n\n`;
text += `📊 Итоги расчёта:\n`;
text += ` 💵 Себестоимость: ${totalCost.toFixed(1)} ₽\n`;
text += ` 🎯 Итоговая цена с наценкой: ${finalPrice.toFixed(1)} ₽\n`;
text += ` ⚗️ Цена за 100 г: ${pricePer100g.toFixed(1)} ₽`;
return text;
},
};