DoSoapCalc/frontend/calculators/soap/config.ts
DosAi 02c7520c90 Refactor: Modular calculator architecture
Created modular system for calculators, added soap and candles calculators, universal components, updated backend
2025-11-02 15:45:07 +03:00

247 lines
7.2 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Конфигурация калькулятора мыла
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 = `🧼 <b>Расчёт мыла:</b> <i>${soapName}</i>\n\n`;
text += `⚖️ <b>Вес мыла:</b> ${weight} г\n\n`;
text += `📦 <b>Упаковка:</b>\n`;
text += ` 📥 Пакет/коробка: ${box}\n`;
text += ` 🌾 Наполнитель: ${filler}\n`;
text += ` 🎀 Лента: ${ribbon}\n`;
text += ` 🏷️ Наклейка: ${label}\n\n`;
text += `💹 <b>Наценка:</b> ${markup}%\n\n`;
text += `📊 <b>Итоги расчёта:</b>\n`;
text += ` 💵 Себестоимость: ${totalCost.toFixed(1)}\n`;
text += ` 🎯 Итоговая цена с наценкой: ${finalPrice.toFixed(1)}\n`;
text += ` ⚗️ Цена за 100 г: ${pricePer100g.toFixed(1)}`;
return text;
},
};