1) Tu tiempo y sueldo

Define tus horas y sueldo mensual objetivo para calcular tu costo por hora.

Horas trabajadas/mes0 h
2) Gastos operativos mensuales

Agrega gastos fijos. Si son compartidos, indica el % de uso del negocio.

ConceptoMonto (MXN)% negocioCosto real
Ej.: internet, luz, renta, SaaS, envíos, publicidad…

Desgaste de equipo (se prorratea mensual)
EquipoCosto (MXN)Años de vida
Computadora, impresora, herramientas, etc.

Subtotal operativos$0
Desgaste equipo (mensual)$0
Operativos + desgaste$0
+ Extras (%)$0
Total gastos operativos mensuales$0
4) Materiales por producto
Escalas de descuento (si rellenas una, se ignora el % simple de ese campo)

Insumos: costo total del paquete y fracción usada por unidad (0.5 = mitad, 0.02 = 1/50, etc.).

InsumoCosto total (MXN)Fracción para 1 udCosto p/ ud
Incluye etiquetas, empaque, DTF, foil, etc.
Servicios (cantidad × precio)$0
Descuento por cantidad-$0
Servicios por hojas$0
Descuento por hojas-$0
Servicios por impresiones$0
Descuento por impresiones-$0
Corte manual (fijo + por unidad)$0
Descuento corte manual-$0
Corte ploteo (fijo + por unidad)$0
Descuento corte ploteo-$0
Servicios extra por unidad$0
Materiales + servicios por unidad$0
5) Tiempo y prorrateos
Costo de tu trabajo p/ud$0
Costo colaborador p/ud$0
Gasto operativo p/ud (prorrateo)$0
Costo total p/ud$0
6) Precio final al público
7) Historial de precios

Se guardan tus precios por unidad. Puedes copiar, exportar o borrar.

Producto$ / UdLoteFechaAcciones
Tip: usa títulos claros para buscar luego.
`; const w=window.open('','_blank'); w.document.open(); w.document.write(html); w.document.close(); }catch(e){ alert('No se pudo abrir la ficha para imprimir.'); } } }; function saveMain(){ try{ localStorage.setItem(STORE_MAIN, JSON.stringify(state)); }catch(e){} } function loadMain(){ try{ const raw=localStorage.getItem(STORE_MAIN); if(raw) Object.assign(state, JSON.parse(raw)); }catch(e){} } function saveHistory(){ try{ localStorage.setItem(STORE_HISTORY, JSON.stringify(history)); }catch(e){} } function loadHistory(){ try{ const raw=localStorage.getItem(STORE_HISTORY); history = raw? JSON.parse(raw):[]; }catch(e){ history=[]; } } function renderOps(){ const c=document.getElementById('opsList'); c.innerHTML=''; state.ops.forEach((op,i)=>{ const row=document.createElement('div'); row.className='mqs-row'; row.innerHTML=`
$0
`; c.appendChild(row); }); c.querySelectorAll('input[data-t="op"]').forEach(el=>{ el.oninput=()=>{ const i=+el.dataset.i, k=el.dataset.k; state.ops[i][k]= (k==='name')? el.value : toNum(el.value); calc(); saveMain(); }; }); } function renderEquip(){ const c=document.getElementById('equipList'); c.innerHTML=''; state.equip.forEach((eq,i)=>{ const row=document.createElement('div'); row.className='mqs-row'; row.style.gridTemplateColumns='1fr 140px 120px'; row.innerHTML=`
`; c.appendChild(row); }); c.querySelectorAll('input[data-t="equip"]').forEach(el=>{ el.oninput=()=>{ const i=+el.dataset.i, k=el.dataset.k; state.equip[i][k]= (k==='name')? el.value : toNum(el.value); calc(); saveMain(); }; }); } function renderMats(){ const c=document.getElementById('matList'); c.innerHTML=''; state.mats.forEach((m,i)=>{ const row=document.createElement('div'); row.className='mqs-row'; row.innerHTML=`
$0
`; c.appendChild(row); }); c.querySelectorAll('input[data-t="mat"]').forEach(el=>{ el.oninput=()=>{ const i=+el.dataset.i, k=el.dataset.k; state.mats[i][k]= (k==='name')? el.value : toNum(el.value); calc(); saveMain(); }; }); } function renderHistory(){ const c=document.getElementById('historyList'); c.innerHTML=''; if(history.length===0){ const empty=document.createElement('div'); empty.className='mqs-mini'; empty.textContent='Aún no hay elementos. Calcula un precio y pulsa “Guardar en historial”.'; c.appendChild(empty); return; } history.forEach(it=>{ const row=document.createElement('div'); row.className='mqs-history-row'; const d=new Date(it.timestamp); const when=d.toLocaleString('es-MX',{year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'}); const extras=[ it.qtyTotal?`Cant: ${it.qtyTotal}`:'', it.numSheets?`Hojas: ${it.numSheets}`:'', it.numPrints?`Imp: ${it.numPrints}`:'', it.corteManual?'Corte manual':'', it.cortePloteo?'Corte ploteo':'' ].filter(Boolean).join(' · '); row.innerHTML=`
${esc(it.name)}
${it.tag?`
#${esc(it.tag)}
`:''} ${it.note?`
${esc(it.note)}
`:''} ${extras?`
${esc(extras)}
`:''}
${mxn(it.unitPrice)}
${it.batchSize}
${when}
`; c.appendChild(row); }); } function renderAll(){ renderOps(); renderEquip(); renderMats(); renderHistory(); } function bindFromState(){ document.getElementById('daysPerWeek').value=state.daysPerWeek; document.getElementById('hoursPerDay').value=state.hoursPerDay; document.getElementById('weeksPerMonth').value=state.weeksPerMonth; document.getElementById('monthlySalary').value=state.monthlySalary; document.getElementById('opsAdsPct').value=state.opsAdsPct; document.getElementById('opsImprevPct').value=state.opsImprevPct; document.getElementById('opsTaxPct').value=state.opsTaxPct; document.getElementById('productTitle').value=state.productTitle||''; document.getElementById('productNote').value=state.productNote||''; document.getElementById('qtyTotal').value=state.qtyTotal||0; document.getElementById('numSheets').value=state.numSheets||0; document.getElementById('numPrints').value=state.numPrints||0; document.getElementById('priceQtyPerUnit').value=state.priceQtyPerUnit||0; document.getElementById('pricePerSheet').value=state.pricePerSheet||0; document.getElementById('pricePerPrint').value=state.pricePerPrint||0; document.getElementById('discQtyPct').value=state.discQtyPct||0; document.getElementById('discSheetsPct').value=state.discSheetsPct||0; document.getElementById('discPrintsPct').value=state.discPrintsPct||0; document.getElementById('discQtyThreshold').value=state.discQtyThreshold||0; document.getElementById('discSheetsThreshold').value=state.discSheetsThreshold||0; document.getElementById('discPrintsThreshold').value=state.discPrintsThreshold||0; document.getElementById('discQtyScale').value=state.discQtyScale||''; document.getElementById('discSheetsScale').value=state.discSheetsScale||''; document.getElementById('discPrintsScale').value=state.discPrintsScale||''; document.getElementById('corteManual').checked=!!state.corteManual; document.getElementById('cortePloteo').checked=!!state.cortePloteo; document.getElementById('priceManualFixed').value=state.priceManualFixed||0; document.getElementById('priceManualPerUnit').value=state.priceManualPerUnit||0; document.getElementById('pricePlotterFixed').value=state.pricePlotterFixed||0; document.getElementById('pricePlotterPerUnit').value=state.pricePlotterPerUnit||0; document.getElementById('discManualPct').value=state.discManualPct||0; document.getElementById('discPlotterPct').value=state.discPlotterPct||0; document.getElementById('minutesPerUnit').value=state.minutesPerUnit; document.getElementById('minutesPerUnitCollab').value=state.minutesPerUnitCollab; document.getElementById('unitsPerMonth').value=state.unitsPerMonth; document.getElementById('feesPct').value=state.feesPct; document.getElementById('marginPct').value=state.marginPct; document.getElementById('batchSize').value=state.batchSize; const pn=document.getElementById('productName'); if(pn && !pn.value && state.productTitle) pn.value=state.productTitle; } function calc(){ const hoursPerMonth = state.daysPerWeek*state.hoursPerDay*state.weeksPerMonth; document.getElementById('hoursPerMonth').textContent = (hoursPerMonth||0).toFixed(0)+' h'; const hourly = hoursPerMonth>0 ? (state.monthlySalary/hoursPerMonth) : 0; document.getElementById('hourlyRate').value = fmt(hourly); // Operativos let opsRealSum=0; state.ops.forEach((op,i)=>{ const real=(Number(op.amount)||0)*(Math.min(Math.max(Number(op.pct)||0,0),100)/100); opsRealSum+=real; const cell=document.querySelector(`[data-op-real="${i}"]`); if(cell) cell.textContent=fmt(real); }); let equipMonthly=0; state.equip.forEach(eq=>{ const months=Math.max(1,Number(eq.years)||0)*12; equipMonthly += (Number(eq.cost)||0)/months; }); const opsBasePlusEquip = opsRealSum + equipMonthly; const extras = opsBasePlusEquip * ((state.opsAdsPct + state.opsImprevPct + state.opsTaxPct)/100); const opsTotal = opsBasePlusEquip + extras; document.getElementById('opsSubtotal').textContent=fmt(opsRealSum); document.getElementById('equipMonthly').textContent=fmt(equipMonthly); document.getElementById('opsBasePlusEquip').textContent=fmt(opsBasePlusEquip); document.getElementById('opsExtras').textContent=fmt(extras); document.getElementById('opsTotal').textContent=fmt(opsTotal); const collHourly = (state.collHoursMonth>0)? (state.collMonthly/state.collHoursMonth):0; // Materiales p/ud (insumos) let matsPerUnit=0; state.mats.forEach((m,i)=>{ const perUnit=(Number(m.total)||0)*(Number(m.frac)||0); matsPerUnit+=perUnit; const cell=document.querySelector(`[data-mat-real="${i}"]`); if(cell) cell.textContent=fmt(perUnit); }); // ===== Servicios extra con descuentos y escalas ===== const qtyUnits = Math.max(1, Number(state.qtyTotal)||1); // Cantidad: usar escala si existe, si no, % simple con umbral const pctQtyScale = scalePctFor(state.qtyTotal||0, state.discQtyScale); const appliedQtyPct = (pctQtyScale!==null) ? pctQtyScale : ((state.qtyTotal||0) >= (state.discQtyThreshold||0) ? (state.discQtyPct||0) : 0); const svcQtyBase = (state.priceQtyPerUnit||0) * (state.qtyTotal||0); const svcQtyDisc = svcQtyBase * (appliedQtyPct/100); const svcQtyNet = svcQtyBase - svcQtyDisc; // Hojas const pctSheetsScale = scalePctFor(state.numSheets||0, state.discSheetsScale); const appliedSheetsPct = (pctSheetsScale!==null) ? pctSheetsScale : ((state.numSheets||0) >= (state.discSheetsThreshold||0) ? (state.discSheetsPct||0) : 0); const svcSheetsBase = (state.pricePerSheet||0) * (state.numSheets||0); const svcSheetsDisc = svcSheetsBase * (appliedSheetsPct/100); const svcSheetsNet = svcSheetsBase - svcSheetsDisc; // Impresiones const pctPrintsScale = scalePctFor(state.numPrints||0, state.discPrintsScale); const appliedPrintsPct = (pctPrintsScale!==null) ? pctPrintsScale : ((state.numPrints||0) >= (state.discPrintsThreshold||0) ? (state.discPrintsPct||0) : 0); const svcPrintsBase = (state.pricePerPrint||0) * (state.numPrints||0); const svcPrintsDisc = svcPrintsBase * (appliedPrintsPct/100); const svcPrintsNet = svcPrintsBase - svcPrintsDisc; // Cortes const svcManualBase = state.corteManual ? ((state.priceManualFixed||0) + (state.priceManualPerUnit||0)*(state.qtyTotal||0)) : 0; const svcManualDisc = state.corteManual ? (svcManualBase*(state.discManualPct||0)/100) : 0; const svcManualNet = svcManualBase - svcManualDisc; const svcPlotterBase = state.cortePloteo ? ((state.pricePlotterFixed||0) + (state.pricePlotterPerUnit||0)*(state.qtyTotal||0)) : 0; const svcPlotterDisc = state.cortePloteo ? (svcPlotterBase*(state.discPlotterPct||0)/100) : 0; const svcPlotterNet = svcPlotterBase - svcPlotterDisc; const svcTotal = svcQtyNet + svcSheetsNet + svcPrintsNet + svcManualNet + svcPlotterNet; const svcPerUnit = qtyUnits>0 ? (svcTotal/qtyUnits) : svcTotal; // Desglose UI document.getElementById('svcQtyTotal').textContent = fmt(svcQtyNet); document.getElementById('svcQtyDisc').textContent = '-'+fmt(svcQtyDisc); document.getElementById('svcQtyDiscLbl').textContent = `Descuento por cantidad (${(appliedQtyPct||0)}%)`; document.getElementById('svcSheetsTotal').textContent = fmt(svcSheetsNet); document.getElementById('svcSheetsDisc').textContent = '-'+fmt(svcSheetsDisc); document.getElementById('svcSheetsDiscLbl').textContent= `Descuento por hojas (${(appliedSheetsPct||0)}%)`; document.getElementById('svcPrintsTotal').textContent = fmt(svcPrintsNet); document.getElementById('svcPrintsDisc').textContent = '-'+fmt(svcPrintsDisc); document.getElementById('svcPrintsDiscLbl').textContent= `Descuento por impresiones (${(appliedPrintsPct||0)}%)`; document.getElementById('svcManualTotal').textContent = fmt(svcManualNet); document.getElementById('svcManualDisc').textContent = '-'+fmt(svcManualDisc); document.getElementById('svcManualDiscLbl').textContent= `Descuento corte manual (${(state.discManualPct||0)}%)`; document.getElementById('svcPlotterTotal').textContent= fmt(svcPlotterNet); document.getElementById('svcPlotterDisc').textContent = '-'+fmt(svcPlotterDisc); document.getElementById('svcPlotterDiscLbl').textContent= `Descuento corte ploteo (${(state.discPlotterPct||0)}%)`; document.getElementById('svcPerUnit').textContent = fmt(svcPerUnit); // Total materiales+servicios p/ud const matTotal = matsPerUnit + svcPerUnit; document.getElementById('matTotal').textContent=fmt(matTotal); // Labor y operativos const laborOwn=(state.minutesPerUnit/60)*hourly; const laborCollab=(state.minutesPerUnitCollab/60)*collHourly; document.getElementById('laborOwn').textContent=fmt(laborOwn); document.getElementById('laborCollab').textContent=fmt(laborCollab); const units=Math.max(1,state.unitsPerMonth); const opsPerUnit=opsTotal/units; document.getElementById('opsPerUnit').textContent=fmt(opsPerUnit); const costPerUnit = matTotal + laborOwn + laborCollab + opsPerUnit; document.getElementById('costPerUnit').textContent=fmt(costPerUnit); const priceNoFees = costPerUnit*(1+state.marginPct/100); const priceWithFees = priceNoFees*(1+state.feesPct/100); document.getElementById('priceNoFees').textContent=fmt(priceNoFees); document.getElementById('priceWithFees').textContent=fmt(priceWithFees); document.getElementById('finalPerUnit').textContent=fmt(priceWithFees); const finalBatch = priceWithFees * Math.max(1,state.batchSize); document.getElementById('finalBatch').textContent=fmt(finalBatch); state._computed={ hourly,collHourly,matsPerUnit,svcPerUnit,svcTotal, svcQtyNet,svcQtyDisc,svcSheetsNet,svcSheetsDisc,svcPrintsNet,svcPrintsDisc, svcManualNet,svcManualDisc,svcPlotterNet,svcPlotterDisc, opsPerUnit,costPerUnit,priceNoFees,priceWithFees,finalBatch, matTotalPU:matTotal,laborOwn,laborCollab, appliedQtyPct:appliedQtyPct||0, appliedSheetsPct:appliedSheetsPct||0, appliedPrintsPct:appliedPrintsPct||0 }; } function init(){ loadMain(); loadHistory(); bindFromState(); renderAll(); calc(); } if(document.readyState==='loading'){ document.addEventListener('DOMContentLoaded', init); } else { init(); }
Scroll al inicio