Add per-event finance, event team permissions and public door sales state.

Event finance
- Finance tab on the event page: P&L summary with a revenue-to-result
  waterfall, costs and other income in one ledger, and the partner split
  with payouts. Lifecycle stepper (Selling, Adding costs, Ready to close,
  Finalized) with what is left to do; closing the books goes through a
  checklist dialog that freezes the numbers.
- Expense modal shows only the fields each calculation type needs, a live
  preview with the event's real counts, and a category suggested from the
  description. Date inputs follow the UI language.
- Global Finance page: profit per event with outliers clipped and labelled,
  and a "Ready to close" list of past events whose books are still open.
- Calculation service (integer PYG, basis points) with pinned regression
  scenarios; PYG formatting centralised in lib/money with locale-aware
  separators.

Event team permissions
- Per-event members with role presets and requireEventPermission; the
  header stat "Confirmed" is relabelled "Not checked in yet", which is
  what it counts.

Public sales state
- One sales state (online, door, sold out, ended, external, cancelled) for
  the event page, listings and JSON-LD, with the door price and tenders
  shown only while people can still pay at the door.

Frontend unit tests run with vitest (npm test in frontend/).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Michilis
2026-10-03 03:11:46 +00:00
co-authored by Claude Opus 5.5
parent b51c1360e2
commit e203fb6c74
80 changed files with 11326 additions and 971 deletions
+505 -3
View File
@@ -91,12 +91,31 @@
"eventEnded": "Event Ended",
"registrationClosed": "Registration Closed",
"presaleClosesBefore": "Pre-sale registration closes {duration} before the event begins.",
"presaleClosesAtStart": "Pre-sale registration closes when the event begins."
"presaleClosesAtStart": "Pre-sale registration closes when the event begins.",
"eventHasEnded": "This event has ended"
},
"booking": {
"join": "Join Event",
"book": "Book Now",
"register": "Register"
},
"door": {
"onlineClosed": "Online sales closed",
"headline": "Tickets available at the door",
"perPerson": "per person, pay at the door",
"onlinePriceWas": "Online price was {price}",
"spotsLeft": "{n} spots left",
"spotsLeftOne": "1 spot left",
"spotsLeftAtDoor": "{n} spots left (at the door)",
"accepts": "Accepted at the door: {methods}",
"doorsOpen": "Doors open at {time}",
"badge": "Tickets at the door",
"methods": {
"cash": "cash",
"pos": "card",
"bitcoin": "Lightning",
"transfer": "bank transfer"
}
}
},
"booking": {
@@ -174,6 +193,13 @@
"receivedConfirmShortly": "Payment received. We will confirm it shortly.",
"receivedConfirmSoon": "Payment received. We will confirm it shortly.",
"confirmForEvent": "We will confirm your {amount} payment for {event} shortly. Nothing else needed."
},
"myEvents": {
"tab": "My Events",
"empty": "You are not on any event team yet.",
"open": "Open",
"back": "Back to my events",
"noAccess": "You do not have access to this event."
}
},
"community": {
@@ -286,7 +312,8 @@
"emails": "Emails",
"gallery": "Gallery",
"photos": "Photos",
"settings": "Settings"
"settings": "Settings",
"finance": "Finance"
},
"events": {
"title": "Manage Events",
@@ -334,6 +361,481 @@
"refunded": "Refunded",
"failed": "Failed"
}
},
"eventTabs": {
"overview": "Overview",
"attendees": "Attendees",
"tickets": "Tickets",
"email": "Email",
"payments": "Payments",
"finance": "Finance",
"team": "Team"
},
"finance": {
"subnav": {
"summary": "Summary",
"expenses": "Costs & income",
"split": "Split & Payouts"
},
"status": {
"open": "Open",
"finalized": "Finalized",
"paid_out": "Paid out"
},
"frozenNote": "Numbers frozen at finalize on {date}. Unfinalize to make changes.",
"loadError": "Could not load finance data",
"hiddenStats": "Finance numbers are hidden. Use Show Stats to reveal them.",
"kpi": {
"gross": "Gross revenue",
"fees": "Payment fees",
"expenses": "Expenses",
"profit": "Profit",
"loss": "Loss",
"breakEven": "Break-even",
"breakEvenNever": "Not reachable at this price",
"presaleDoor": "Pre-sale {presale} · Door {door}",
"ticketsSold": "{count} tickets sold",
"ticketRevenue": "Ticket revenue",
"revenueDetail": "{count} paid tickets, after refunds",
"afterFees": "After fees and all expenses",
"breakEvenProgress": "{sold} of {total}",
"beyondCapacity": "Needs more tickets than the capacity ({capacity}). Raise the price or cut costs.",
"estimated": "Estimated",
"estimatedHint": "After fees and all costs so far",
"presale": "Pre-sale",
"door": "Door"
},
"waterfall": {
"title": "From revenue to result",
"gross": "Gross revenue",
"refunds": "Refunds",
"fees": "Payment fees",
"otherIncome": "Other income",
"net": "Net revenue",
"expenses": "Expenses",
"profit": "Profit",
"organization": "Organization keeps",
"partners": "Partner payouts",
"legend": {
"in": "Money in",
"out": "Money out",
"total": "Total"
},
"estimatedHint": "Estimated: numbers follow sales and costs until the books are closed."
},
"charts": {
"byCategory": "Expenses by category",
"byMethod": "Revenue by payment method",
"cumulative": "Cumulative sales",
"breakEvenLine": "Break-even",
"revenue": "Revenue",
"plannedVsActual": "Planned vs paid expenses",
"planned": "Planned",
"paid": "Paid",
"empty": "No data yet",
"uncategorized": "Uncategorized",
"breakEvenOffChart": "Break-even is {count} tickets, far above sales so far.",
"tickets": "Tickets",
"addExpensesHint": "Add expenses to see the cost breakdown."
},
"methods": {
"tpago": "TPago",
"bank_transfer": "Bank transfer",
"lightning": "Lightning",
"cash": "Cash",
"pos": "POS",
"bancard": "Bancard"
},
"otherIncome": {
"title": "Other income",
"add": "Add income",
"hint": "Sponsors, venue kickbacks and other money in that is not a ticket.",
"empty": "No other income",
"confirmDelete": "Delete \"{name}\"?",
"placeholder": "e.g. Sponsor: Café Uno",
"edit": "Edit income"
},
"expenses": {
"add": "Add expense",
"applyTemplate": "Apply template",
"applyPack": "Apply pack",
"allCategories": "All categories",
"empty": "Add costs like the venue, instructor or snacks, or add your usual costs from a template.",
"description": "Description",
"category": "Category",
"calcType": "Calculated as",
"amount": "Amount",
"lockedAmount": "Locked amount (PYG)",
"status": "Status",
"paidBy": "Paid by",
"organization": "Organization",
"otherPartner": "Another partner",
"receipt": "Receipt",
"uploadReceipt": "Upload receipt",
"viewReceipt": "View receipt",
"receiptAdminOnly": "Receipt uploads are available to admins and organizers.",
"date": "Date",
"lock": "Lock amount",
"locked": "Locked",
"auto": "Auto",
"lockHint": "Keep this amount fixed instead of following ticket sales.",
"markPaid": "Mark as paid",
"markPlanned": "Mark as not paid yet",
"confirmDelete": "Delete this expense?",
"created": "Expense added",
"saved": "Expense saved",
"deleted": "Expense deleted",
"applied": "{count} expense(s) added",
"edit": "Edit expense",
"new": "New expense",
"pickTemplate": "Choose a template",
"pickPack": "Choose a pack",
"noTemplates": "No templates yet. Create them in Settings → Expense Templates.",
"ownOnly": "You can edit the expenses you added.",
"total": "Total",
"descriptionRequired": "Add a short description",
"descriptionPlaceholder": "e.g. Studio rental",
"paidByHint": "Pick a partner if they paid it themselves; they get it back in their payout.",
"upload": "Upload",
"useTemplate": "Use a template",
"useTemplateHint": "Add your usual costs in one tap",
"packsHeading": "Packs (several costs at once)",
"templatesHeading": "Single costs",
"addAll": "Add {count}",
"addOne": "Add",
"confirmDeleteNamed": "Delete \"{name}\"?",
"emptyTitle": "No expenses yet",
"unlockHint": "Locked. Unlock to follow ticket sales again.",
"frozen": "Finalized: expenses can't be changed.",
"filtered": "filtered",
"amountPerTicket": "Amount per ticket",
"amountPerGuest": "Amount per guest",
"percentOfRevenue": "Percentage of ticket revenue",
"minimumAmount": "Minimum amount",
"categorySuggested": "Suggested from the description"
},
"calcTypes": {
"fixed": "Fixed",
"per_ticket_sold": "Per ticket sold",
"per_checked_in": "Per checked-in guest",
"percent_of_revenue": "Percent of revenue",
"minimum_spend": "Minimum spend"
},
"calcHints": {
"fixed": "One set amount, e.g. instructor fee.",
"per_ticket_sold": "Grows with every paid ticket, e.g. a drink voucher.",
"per_checked_in": "Grows with every guest who shows up, e.g. mat rental.",
"percent_of_revenue": "A share of ticket sales, e.g. platform commission.",
"minimum_spend": "Venue minimum: the higher of a minimum or per-guest spend."
},
"expenseStatus": {
"planned": "Planned",
"paid": "Paid"
},
"split": {
"partners": "Partners",
"addPartner": "Add partner",
"editPartner": "Edit partner",
"empty": "No partners: the organization keeps the whole result.",
"linkedUser": "Linked user",
"searchUser": "Search users by name or email",
"externalName": "Or type a name",
"roleLabel": "Role",
"shareType": "Deal",
"percent": "Percent (%)",
"fixedAmount": "Fixed amount (PYG)",
"threshold": "Profit threshold (PYG)",
"lossRule": "If there is a loss",
"lossCap": "Loss cap (PYG)",
"share": "Share",
"reimbursement": "Reimbursements",
"payout": "Payout",
"owes": "Owes",
"organization": "Organization keeps",
"distributable": "Left after fixed & revenue deals",
"markPaid": "Mark as paid",
"markUnpaid": "Undo payout",
"payoutMethod": "Payout method",
"payoutNote": "Note",
"paidOn": "Paid {date}",
"pending": "Pending",
"statement": "Statement PDF",
"finalize": "Finalize",
"unfinalize": "Unfinalize",
"unfinalizeConfirm": "Unfinalize? Numbers will follow live sales again. This is logged.",
"finalized": "Event finalized",
"unfinalized": "Event reopened",
"finalizeFirst": "Payouts can be recorded after you close the books.",
"ownShareOnly": "You see your own share. The full split is visible to managers.",
"partnerSaved": "Partner saved",
"partnerRemoved": "Partner removed",
"confirmRemove": "Remove this partner?",
"needsNameOrUser": "Link a user or type a name",
"auditLog": "Activity log",
"auditEmpty": "No activity yet",
"who": "Who",
"whoHint": "Link a user so they can log in and see their statement, or just type a name.",
"rolePlaceholder": "e.g. Venue, Co-host, Instructor",
"payoutMethodPlaceholder": "e.g. Bank transfer",
"confirmRemoveNamed": "Remove {name} from the split?",
"distributableHint": "Profit shares are taken from this",
"closeBooks": "Close the books",
"booksClosed": "Books closed",
"closeBooksHint": "Once all costs are in, finalize to freeze the numbers and pay partners. You can reopen later.",
"payoutsProgress": "{paid} of {total} partner payouts recorded.",
"noPartnersTitle": "No partners on this event",
"noPartnersBody": "Add a venue, co-host or instructor who shares in the result. Their payout is calculated for you.",
"calculation": "How the result is split",
"ofDistributable": "{rule}, taken from {base} left after fixed and revenue deals",
"unpaid": "Unpaid",
"includesReimbursement": "Incl. {amount} reimbursed",
"frozenNoPartners": "Numbers are frozen. There are no partners to pay."
},
"shareTypes": {
"percent_profit": "Percent of profit",
"percent_revenue": "Percent of revenue",
"fixed": "Fixed amount",
"fixed_plus_percent_above_threshold": "Fixed + percent above threshold"
},
"lossRules": {
"proportional": "Shares the loss proportionally",
"none": "Does not share losses",
"capped": "Shares the loss up to a cap"
},
"empty": {
"title": "No money in or out yet",
"body": "Ticket sales appear here automatically. Add the event's costs to see profit and break-even."
},
"statement": {
"title": "Profit & loss",
"ticketSales": "Ticket sales ({count})",
"carriesLoss": "Carries part of the loss"
},
"formula": {
"perTicket": "{count} tickets × {amount}",
"perGuest": "{count} guests × {amount}",
"percent": "{percent} of ticket revenue",
"minimum": "min {minimum} or {count} × {amount}"
},
"shareHints": {
"percent_profit": "A percentage of what's left after costs.",
"percent_revenue": "A percentage of ticket sales, paid even if the event loses money.",
"fixed": "A set amount, whatever the result.",
"fixed_plus_percent_above_threshold": "A set amount plus a bonus percentage of profit above a target."
},
"lossRuleTitles": {
"none": "Org covers losses",
"proportional": "Shares losses",
"capped": "Shares up to a cap"
},
"deal": {
"percentProfit": "{percent} of profit",
"percentRevenue": "{percent} of ticket sales",
"fixed": "Fixed {amount}",
"fixedPlus": "{amount} + {percent} of profit above {threshold}"
},
"audit": {
"create": "added",
"update": "changed",
"delete": "deleted",
"apply_template": "added from template",
"apply_pack": "added from pack",
"mark_paid": "payout recorded",
"mark_unpaid": "payout undone",
"finalize": "books closed",
"unfinalize": "books reopened",
"paid_out": "all partners paid",
"finalized": "back to finalized",
"entity": {
"expense": "Expense",
"other_income": "Other income",
"partner": "Partner",
"finance_state": "Finance",
"member": "Team"
}
},
"close": "Close",
"pickDate": "Pick a date",
"clearDate": "Clear date",
"lifecycle": {
"label": "Books status",
"steps": {
"selling": "Selling",
"costs": "Adding costs",
"ready": "Ready to close",
"finalized": "Finalized"
},
"todo": {
"noCosts": "No costs added yet",
"planned_one": "1 planned expense not marked paid",
"planned_other": "{count} planned expenses not marked paid",
"noPartners": "No partners added",
"payoutsPending_one": "1 partner payout to record",
"payoutsPending_other": "{count} partner payouts to record",
"notEnded": "The event has not ended yet"
},
"nudge_one": "This event ended 1 day ago. Add any remaining costs and close the books.",
"nudge_other": "This event ended {count} days ago. Add any remaining costs and close the books.",
"nudgeToday": "This event ended today. Add any remaining costs and close the books.",
"reviewCosts": "Review costs",
"frozen": "Numbers frozen on {date}.",
"paidOut": "Numbers frozen on {date}. Every partner has been paid.",
"nudgeReady_one": "This event ended 1 day ago and every cost is marked paid. You can close the books in Split & Payouts.",
"nudgeReady_other": "This event ended {count} days ago and every cost is marked paid. You can close the books in Split & Payouts.",
"nudgeReadyToday": "This event ended today and every cost is marked paid. You can close the books in Split & Payouts."
},
"finalizeDialog": {
"title": "Close the books",
"subtitle": "Check everything is in before freezing the numbers.",
"notEnded": "The event has not ended yet",
"noExpenses": "No expenses recorded",
"expenses_one": "1 expense recorded",
"expenses_other": "{count} expenses recorded",
"planned_one": "1 planned expense not marked paid",
"planned_other": "{count} planned expenses not marked paid",
"allPaid": "All expenses marked paid",
"income_one": "1 other income entry",
"income_other": "{count} other income entries",
"partners_one": "Payout to 1 partner",
"partners_other": "Payouts to {count} partners",
"noPartners": "No partners: the organization keeps the result",
"finalProfit": "Final profit",
"finalLoss": "Final loss",
"hint": "Numbers stop following sales and costs. You can record partner payouts afterwards, and an admin can reopen the books if needed.",
"confirm": "Finalize and freeze numbers"
},
"breakEven": {
"noCosts": "No costs yet",
"noCostsHint": "Add costs to see how many tickets cover them.",
"reached": "Break-even reached at {at} of {sold} tickets sold",
"needed": "{count} more tickets to break even",
"coveredWithoutTickets": "Costs already covered by other income"
},
"preview": {
"fixed": "Adds {total} to this event's costs",
"perTicket_one": "{amount} × {count} ticket = {total}",
"perTicket_other": "{amount} × {count} tickets = {total}",
"perGuest_one": "{amount} × {count} checked-in guest = {total}",
"perGuest_other": "{amount} × {count} checked-in guests = {total}",
"percent": "{percent} of {sales} ticket revenue = {total}",
"minimum": "Higher of {minimum} or {count} × {amount} = {total}",
"locked": "Locked at {total}"
},
"ledger": {
"income": "Income",
"costs": "Costs",
"allRows": "All costs and income",
"emptyTitle": "No costs or income yet"
}
},
"team": {
"title": "Team",
"hint": "People added here get access to this event only, with the permissions below. Admins always have full access.",
"add": "Add member",
"search": "Search users by name or email",
"noResults": "No users found",
"empty": "No team members yet",
"role": "Role",
"remove": "Remove",
"confirmRemove": "Remove {name} from this event?",
"added": "Member added",
"saved": "Permissions saved",
"removed": "Member removed",
"override": "Changed from the role default",
"reset": "Reset to role defaults",
"permissions": "Permissions",
"permissionsSummary": "{on} of {total} on"
},
"rolePresets": {
"staff": "Staff",
"collaborator": "Collaborator",
"co_manager": "Co-manager"
},
"permissions": {
"view_overview": "View overview",
"check_in": "Check in guests",
"view_attendees_names": "See attendee names",
"view_attendees_pii": "See attendee contact details",
"email_attendees": "Email attendees",
"view_payments": "View payments",
"view_finance": "View finance",
"edit_expenses": "Edit expenses",
"edit_own_expenses_only": "Add and edit own expenses",
"view_full_split": "See the full split",
"edit_event": "Edit event",
"manage_team": "Manage team"
},
"expenseSettings": {
"tab": "Expense Templates",
"categories": "Categories",
"templates": "Templates",
"packs": "Template packs",
"fees": "Payment method fees",
"feesHint": "Charged per paid payment when computing an event's net revenue.",
"addCategory": "Add category",
"addTemplate": "Add template",
"addPack": "Add pack",
"nameEn": "Name (English)",
"nameEs": "Name (Spanish)",
"color": "Color",
"name": "Name",
"description": "Description",
"archived": "Archived",
"archive": "Archive",
"unarchive": "Restore",
"showArchived": "Show archived",
"packTemplates": "Templates in this pack",
"percent": "Percent (%)",
"fixed": "Fixed (PYG)",
"saved": "Saved",
"deleted": "Deleted",
"inUse": "In use. Archive it instead.",
"confirmDelete": "Delete {name}?",
"empty": "Nothing here yet"
},
"financeOverview": {
"title": "Finance",
"subtitle": "Profit across events. Finalized events use their frozen numbers.",
"from": "From",
"to": "To",
"series": "Series",
"venue": "Venue",
"partner": "Partner",
"all": "All",
"noSeries": "No series",
"events": "Events",
"event": "Event",
"date": "Date",
"perEvent": "Profit per event",
"perSeries": "By series",
"perVenue": "By venue",
"perPartner": "Partner totals",
"paid": "Paid",
"pending": "Pending",
"empty": "No events with finance data in this range",
"filters": "Filters",
"readyCount_one": "1 past event",
"readyCount_other": "{count} past events",
"readyHint": "still have open books. Add any last costs and close them.",
"showReady": "Show ready to close",
"showAll": "Show all events",
"readyTitle": "Ready to close",
"readyBadge": "Ready to close",
"clippedHint": "Bars with a break mark are cut off to keep the others readable. The label shows the real value."
},
"eventForm": {
"series": "Series",
"seriesHint": "Groups recurring events (e.g. Morning Club) in the Finance overview."
},
"eventStats": {
"capacity": "Capacity",
"notCheckedIn": "Not checked in yet",
"checkedIn": "Checked in",
"revenue": "Revenue",
"presaleDoor": "Pre-sale {presale} · Door {door}",
"stats": "Stats",
"inShort": "{count} in",
"free": "Free"
}
},
"footer": {
@@ -392,4 +894,4 @@
"downloading": "Downloading…"
}
}
}
}
+505 -3
View File
@@ -91,12 +91,31 @@
"eventEnded": "Evento Finalizado",
"registrationClosed": "Inscripciones Cerradas",
"presaleClosesBefore": "La preventa cierra {duration} antes de que comience el evento.",
"presaleClosesAtStart": "La preventa cierra cuando comienza el evento."
"presaleClosesAtStart": "La preventa cierra cuando comienza el evento.",
"eventHasEnded": "Este evento ya terminó"
},
"booking": {
"join": "Unirse al Evento",
"book": "Reservar Ahora",
"register": "Registrarse"
},
"door": {
"onlineClosed": "Venta online cerrada",
"headline": "Entradas disponibles en la puerta",
"perPerson": "por persona, pago en la puerta",
"onlinePriceWas": "El precio online era {price}",
"spotsLeft": "{n} lugares disponibles",
"spotsLeftOne": "1 lugar disponible",
"spotsLeftAtDoor": "{n} lugares disponibles (en la puerta)",
"accepts": "Aceptamos en la puerta: {methods}",
"doorsOpen": "Las puertas abren a las {time}",
"badge": "Entradas en la puerta",
"methods": {
"cash": "efectivo",
"pos": "tarjeta",
"bitcoin": "Lightning",
"transfer": "transferencia"
}
}
},
"booking": {
@@ -174,6 +193,13 @@
"receivedConfirmShortly": "Pago recibido. Lo confirmaremos en breve.",
"receivedConfirmSoon": "Pago recibido. Lo confirmaremos pronto.",
"confirmForEvent": "Confirmaremos tu pago de {amount} para {event} en breve. No necesitas hacer nada más."
},
"myEvents": {
"tab": "Mis eventos",
"empty": "Todavía no formás parte del equipo de ningún evento.",
"open": "Abrir",
"back": "Volver a mis eventos",
"noAccess": "No tenés acceso a este evento."
}
},
"community": {
@@ -286,7 +312,8 @@
"emails": "Emails",
"gallery": "Galería",
"photos": "Fotos",
"settings": "Configuración"
"settings": "Configuración",
"finance": "Finanzas"
},
"events": {
"title": "Gestionar Eventos",
@@ -334,6 +361,481 @@
"refunded": "Reembolsado",
"failed": "Fallido"
}
},
"eventTabs": {
"overview": "Resumen",
"attendees": "Asistentes",
"tickets": "Entradas",
"email": "Email",
"payments": "Pagos",
"finance": "Finanzas",
"team": "Equipo"
},
"finance": {
"subnav": {
"summary": "Resumen",
"expenses": "Gastos e ingresos",
"split": "Reparto y pagos"
},
"status": {
"open": "Abierto",
"finalized": "Cerrado",
"paid_out": "Pagado"
},
"frozenNote": "Números congelados al cerrar el {date}. Reabrí para hacer cambios.",
"loadError": "No se pudieron cargar las finanzas",
"hiddenStats": "Los números están ocultos. Usá Mostrar estadísticas para verlos.",
"kpi": {
"gross": "Ingresos brutos",
"fees": "Comisiones",
"expenses": "Gastos",
"profit": "Ganancia",
"loss": "Pérdida",
"breakEven": "Punto de equilibrio",
"breakEvenNever": "No alcanzable a este precio",
"presaleDoor": "Preventa {presale} · Puerta {door}",
"ticketsSold": "{count} entradas vendidas",
"ticketRevenue": "Ingresos por entradas",
"revenueDetail": "{count} entradas pagas, sin reembolsos",
"afterFees": "Después de comisiones y gastos",
"breakEvenProgress": "{sold} de {total}",
"beyondCapacity": "Necesita más entradas que la capacidad ({capacity}). Subí el precio o bajá gastos.",
"estimated": "Estimado",
"estimatedHint": "Después de comisiones y los gastos cargados",
"presale": "Preventa",
"door": "Puerta"
},
"waterfall": {
"title": "De los ingresos al resultado",
"gross": "Ingresos brutos",
"refunds": "Reembolsos",
"fees": "Comisiones de pago",
"otherIncome": "Otros ingresos",
"net": "Ingreso neto",
"expenses": "Gastos",
"profit": "Ganancia",
"organization": "Queda para la organización",
"partners": "Pagos a socios",
"legend": {
"in": "Entra",
"out": "Sale",
"total": "Total"
},
"estimatedHint": "Estimado: los números siguen las ventas y los gastos hasta cerrar las cuentas."
},
"charts": {
"byCategory": "Gastos por categoría",
"byMethod": "Ingresos por método de pago",
"cumulative": "Ventas acumuladas",
"breakEvenLine": "Equilibrio",
"revenue": "Ingresos",
"plannedVsActual": "Gastos previstos vs pagados",
"planned": "Previsto",
"paid": "Pagado",
"empty": "Sin datos todavía",
"uncategorized": "Sin categoría",
"breakEvenOffChart": "El equilibrio es {count} entradas, muy por encima de lo vendido.",
"tickets": "Entradas",
"addExpensesHint": "Agregá gastos para ver el desglose de costos."
},
"methods": {
"tpago": "TPago",
"bank_transfer": "Transferencia",
"lightning": "Lightning",
"cash": "Efectivo",
"pos": "POS",
"bancard": "Bancard"
},
"otherIncome": {
"title": "Otros ingresos",
"add": "Agregar ingreso",
"hint": "Sponsors, comisiones del local y otros ingresos que no son entradas.",
"empty": "Sin otros ingresos",
"confirmDelete": "¿Eliminar \"{name}\"?",
"placeholder": "ej. Sponsor: Café Uno",
"edit": "Editar ingreso"
},
"expenses": {
"add": "Agregar gasto",
"applyTemplate": "Aplicar plantilla",
"applyPack": "Aplicar paquete",
"allCategories": "Todas las categorías",
"empty": "Agregá gastos como el lugar, el instructor o snacks, o tus gastos habituales desde una plantilla.",
"description": "Descripción",
"category": "Categoría",
"calcType": "Se calcula",
"amount": "Monto",
"lockedAmount": "Monto fijado (PYG)",
"status": "Estado",
"paidBy": "Pagado por",
"organization": "Organización",
"otherPartner": "Otro socio",
"receipt": "Comprobante",
"uploadReceipt": "Subir comprobante",
"viewReceipt": "Ver comprobante",
"receiptAdminOnly": "La carga de comprobantes está disponible para admins y organizadores.",
"date": "Fecha",
"lock": "Fijar monto",
"locked": "Fijado",
"auto": "Auto",
"lockHint": "Mantené este monto fijo en vez de seguir las ventas.",
"markPaid": "Marcar como pagado",
"markPlanned": "Marcar como no pagado",
"confirmDelete": "¿Eliminar este gasto?",
"created": "Gasto agregado",
"saved": "Gasto guardado",
"deleted": "Gasto eliminado",
"applied": "{count} gasto(s) agregado(s)",
"edit": "Editar gasto",
"new": "Nuevo gasto",
"pickTemplate": "Elegí una plantilla",
"pickPack": "Elegí un paquete",
"noTemplates": "Todavía no hay plantillas. Crealas en Configuración → Plantillas de gastos.",
"ownOnly": "Podés editar los gastos que agregaste.",
"total": "Total",
"descriptionRequired": "Agregá una descripción",
"descriptionPlaceholder": "ej. Alquiler del estudio",
"paidByHint": "Elegí un socio si lo pagó de su bolsillo; se le devuelve en su pago.",
"upload": "Subir",
"useTemplate": "Usar plantilla",
"useTemplateHint": "Agregá tus gastos habituales de una vez",
"packsHeading": "Paquetes (varios gastos juntos)",
"templatesHeading": "Gastos individuales",
"addAll": "Agregar {count}",
"addOne": "Agregar",
"confirmDeleteNamed": "¿Eliminar \"{name}\"?",
"emptyTitle": "Todavía no hay gastos",
"unlockHint": "Fijado. Desbloqueá para volver a seguir las ventas.",
"frozen": "Cerrado: no se pueden cambiar los gastos.",
"filtered": "filtrado",
"amountPerTicket": "Monto por entrada",
"amountPerGuest": "Monto por asistente",
"percentOfRevenue": "Porcentaje de la venta de entradas",
"minimumAmount": "Monto mínimo",
"categorySuggested": "Sugerida según la descripción"
},
"calcTypes": {
"fixed": "Fijo",
"per_ticket_sold": "Por entrada vendida",
"per_checked_in": "Por asistente ingresado",
"percent_of_revenue": "Porcentaje de ingresos",
"minimum_spend": "Consumo mínimo"
},
"calcHints": {
"fixed": "Un monto fijo, ej. honorario del instructor.",
"per_ticket_sold": "Crece con cada entrada pagada, ej. un vale de bebida.",
"per_checked_in": "Crece con cada asistente que llega, ej. alquiler de mat.",
"percent_of_revenue": "Un porcentaje de las ventas, ej. comisión.",
"minimum_spend": "Consumo mínimo del local: el mayor entre el mínimo y el consumo por persona."
},
"expenseStatus": {
"planned": "Previsto",
"paid": "Pagado"
},
"split": {
"partners": "Socios",
"addPartner": "Agregar socio",
"editPartner": "Editar socio",
"empty": "Sin socios: la organización se queda con todo.",
"linkedUser": "Usuario vinculado",
"searchUser": "Buscar usuarios por nombre o email",
"externalName": "O escribí un nombre",
"roleLabel": "Rol",
"shareType": "Acuerdo",
"percent": "Porcentaje (%)",
"fixedAmount": "Monto fijo (PYG)",
"threshold": "Umbral de ganancia (PYG)",
"lossRule": "Si hay pérdida",
"lossCap": "Tope de pérdida (PYG)",
"share": "Participación",
"reimbursement": "Reembolsos",
"payout": "A pagar",
"owes": "Debe",
"organization": "Queda para la organización",
"distributable": "Resto tras acuerdos fijos y de ingresos",
"markPaid": "Marcar como pagado",
"markUnpaid": "Deshacer pago",
"payoutMethod": "Método de pago",
"payoutNote": "Nota",
"paidOn": "Pagado el {date}",
"pending": "Pendiente",
"statement": "Liquidación PDF",
"finalize": "Cerrar",
"unfinalize": "Reabrir",
"unfinalizeConfirm": "¿Reabrir? Los números vuelven a seguir las ventas. Queda registrado.",
"finalized": "Evento cerrado",
"unfinalized": "Evento reabierto",
"finalizeFirst": "Los pagos se registran después de cerrar las cuentas.",
"ownShareOnly": "Ves tu propia participación. El reparto completo lo ven los responsables.",
"partnerSaved": "Socio guardado",
"partnerRemoved": "Socio eliminado",
"confirmRemove": "¿Quitar este socio?",
"needsNameOrUser": "Vinculá un usuario o escribí un nombre",
"auditLog": "Registro de actividad",
"auditEmpty": "Sin actividad todavía",
"who": "Quién",
"whoHint": "Vinculá un usuario para que vea su liquidación, o escribí un nombre.",
"rolePlaceholder": "ej. Local, Co-anfitrión, Instructor",
"payoutMethodPlaceholder": "ej. Transferencia",
"confirmRemoveNamed": "¿Quitar a {name} del reparto?",
"distributableHint": "Las participaciones en la ganancia salen de acá",
"closeBooks": "Cerrar cuentas",
"booksClosed": "Cuentas cerradas",
"closeBooksHint": "Cuando estén todos los gastos, cerrá para congelar los números y pagar a los socios. Se puede reabrir.",
"payoutsProgress": "{paid} de {total} pagos a socios registrados.",
"noPartnersTitle": "No hay socios en este evento",
"noPartnersBody": "Agregá un local, co-anfitrión o instructor que participe del resultado. Su pago se calcula solo.",
"calculation": "Cómo se reparte el resultado",
"ofDistributable": "{rule}, sobre {base} que quedan tras los acuerdos fijos y de ingresos",
"unpaid": "Sin pagar",
"includesReimbursement": "Incluye {amount} de reembolso",
"frozenNoPartners": "Los números están congelados. No hay socios a quienes pagar."
},
"shareTypes": {
"percent_profit": "Porcentaje de la ganancia",
"percent_revenue": "Porcentaje de ingresos",
"fixed": "Monto fijo",
"fixed_plus_percent_above_threshold": "Fijo + porcentaje sobre umbral"
},
"lossRules": {
"proportional": "Comparte la pérdida proporcionalmente",
"none": "No comparte pérdidas",
"capped": "Comparte la pérdida hasta un tope"
},
"empty": {
"title": "Todavía no hay ingresos ni gastos",
"body": "Las ventas aparecen solas. Agregá los gastos del evento para ver la ganancia y el punto de equilibrio."
},
"statement": {
"title": "Resultado",
"ticketSales": "Venta de entradas ({count})",
"carriesLoss": "Asume parte de la pérdida"
},
"formula": {
"perTicket": "{count} entradas × {amount}",
"perGuest": "{count} asistentes × {amount}",
"percent": "{percent} de los ingresos",
"minimum": "mín {minimum} o {count} × {amount}"
},
"shareHints": {
"percent_profit": "Un porcentaje de lo que queda después de los gastos.",
"percent_revenue": "Un porcentaje de las ventas, se paga aunque haya pérdida.",
"fixed": "Un monto fijo, sea cual sea el resultado.",
"fixed_plus_percent_above_threshold": "Un monto fijo más un porcentaje de la ganancia sobre una meta."
},
"lossRuleTitles": {
"none": "La org cubre",
"proportional": "Comparte pérdidas",
"capped": "Comparte hasta un tope"
},
"deal": {
"percentProfit": "{percent} de la ganancia",
"percentRevenue": "{percent} de las ventas",
"fixed": "Fijo {amount}",
"fixedPlus": "{amount} + {percent} de la ganancia sobre {threshold}"
},
"audit": {
"create": "agregado",
"update": "modificado",
"delete": "eliminado",
"apply_template": "agregado desde plantilla",
"apply_pack": "agregado desde paquete",
"mark_paid": "pago registrado",
"mark_unpaid": "pago deshecho",
"finalize": "cuentas cerradas",
"unfinalize": "cuentas reabiertas",
"paid_out": "todos los socios cobraron",
"finalized": "vuelve a cerrado",
"entity": {
"expense": "Gasto",
"other_income": "Otro ingreso",
"partner": "Socio",
"finance_state": "Finanzas",
"member": "Equipo"
}
},
"close": "Cerrar",
"pickDate": "Elegí una fecha",
"clearDate": "Borrar fecha",
"lifecycle": {
"label": "Estado de las cuentas",
"steps": {
"selling": "Vendiendo",
"costs": "Cargando gastos",
"ready": "Listo para cerrar",
"finalized": "Cerrado"
},
"todo": {
"noCosts": "Todavía no hay gastos",
"planned_one": "1 gasto planificado sin marcar como pagado",
"planned_other": "{count} gastos planificados sin marcar como pagados",
"noPartners": "No hay socios",
"payoutsPending_one": "Falta registrar 1 pago a socio",
"payoutsPending_other": "Faltan registrar {count} pagos a socios",
"notEnded": "El evento todavía no terminó"
},
"nudge_one": "Este evento terminó hace 1 día. Agregá los gastos que falten y cerrá las cuentas.",
"nudge_other": "Este evento terminó hace {count} días. Agregá los gastos que falten y cerrá las cuentas.",
"nudgeToday": "Este evento terminó hoy. Agregá los gastos que falten y cerrá las cuentas.",
"reviewCosts": "Revisar gastos",
"frozen": "Números congelados el {date}.",
"paidOut": "Números congelados el {date}. Todos los socios cobraron.",
"nudgeReady_one": "Este evento terminó hace 1 día y todos los gastos están pagados. Podés cerrar las cuentas en Reparto y pagos.",
"nudgeReady_other": "Este evento terminó hace {count} días y todos los gastos están pagados. Podés cerrar las cuentas en Reparto y pagos.",
"nudgeReadyToday": "Este evento terminó hoy y todos los gastos están pagados. Podés cerrar las cuentas en Reparto y pagos."
},
"finalizeDialog": {
"title": "Cerrar cuentas",
"subtitle": "Revisá que esté todo antes de congelar los números.",
"notEnded": "El evento todavía no terminó",
"noExpenses": "No hay gastos cargados",
"expenses_one": "1 gasto cargado",
"expenses_other": "{count} gastos cargados",
"planned_one": "1 gasto planificado sin marcar como pagado",
"planned_other": "{count} gastos planificados sin marcar como pagados",
"allPaid": "Todos los gastos están pagados",
"income_one": "1 ingreso adicional",
"income_other": "{count} ingresos adicionales",
"partners_one": "Pago a 1 socio",
"partners_other": "Pagos a {count} socios",
"noPartners": "Sin socios: la organización se queda con el resultado",
"finalProfit": "Ganancia final",
"finalLoss": "Pérdida final",
"hint": "Los números dejan de seguir las ventas y los gastos. Después podés registrar los pagos a socios, y un admin puede reabrir las cuentas si hace falta.",
"confirm": "Cerrar y congelar números"
},
"breakEven": {
"noCosts": "Todavía sin gastos",
"noCostsHint": "Agregá gastos para ver cuántas entradas los cubren.",
"reached": "Equilibrio alcanzado con {at} de {sold} entradas vendidas",
"needed": "Faltan {count} entradas para cubrir gastos",
"coveredWithoutTickets": "Los gastos ya están cubiertos por otros ingresos"
},
"preview": {
"fixed": "Suma {total} a los gastos del evento",
"perTicket_one": "{amount} × {count} entrada = {total}",
"perTicket_other": "{amount} × {count} entradas = {total}",
"perGuest_one": "{amount} × {count} asistente ingresado = {total}",
"perGuest_other": "{amount} × {count} asistentes ingresados = {total}",
"percent": "{percent} de {sales} en entradas = {total}",
"minimum": "El mayor entre {minimum} o {count} × {amount} = {total}",
"locked": "Fijado en {total}"
},
"ledger": {
"income": "Ingreso",
"costs": "Gastos",
"allRows": "Todos los gastos e ingresos",
"emptyTitle": "Todavía no hay gastos ni ingresos"
}
},
"team": {
"title": "Equipo",
"hint": "Las personas que agregues acá acceden solo a este evento, con los permisos de abajo. Los admins siempre tienen acceso completo.",
"add": "Agregar miembro",
"search": "Buscar usuarios por nombre o email",
"noResults": "No se encontraron usuarios",
"empty": "Todavía no hay miembros",
"role": "Rol",
"remove": "Quitar",
"confirmRemove": "¿Quitar a {name} de este evento?",
"added": "Miembro agregado",
"saved": "Permisos guardados",
"removed": "Miembro quitado",
"override": "Cambiado respecto al rol",
"reset": "Volver a los permisos del rol",
"permissions": "Permisos",
"permissionsSummary": "{on} de {total} activos"
},
"rolePresets": {
"staff": "Staff",
"collaborator": "Colaborador",
"co_manager": "Co-responsable"
},
"permissions": {
"view_overview": "Ver resumen",
"check_in": "Registrar ingresos",
"view_attendees_names": "Ver nombres de asistentes",
"view_attendees_pii": "Ver datos de contacto",
"email_attendees": "Enviar emails a asistentes",
"view_payments": "Ver pagos",
"view_finance": "Ver finanzas",
"edit_expenses": "Editar gastos",
"edit_own_expenses_only": "Agregar y editar sus gastos",
"view_full_split": "Ver el reparto completo",
"edit_event": "Editar evento",
"manage_team": "Gestionar equipo"
},
"expenseSettings": {
"tab": "Plantillas de gastos",
"categories": "Categorías",
"templates": "Plantillas",
"packs": "Paquetes de plantillas",
"fees": "Comisiones por método de pago",
"feesHint": "Se descuentan por cada pago al calcular el ingreso neto de un evento.",
"addCategory": "Agregar categoría",
"addTemplate": "Agregar plantilla",
"addPack": "Agregar paquete",
"nameEn": "Nombre (inglés)",
"nameEs": "Nombre (español)",
"color": "Color",
"name": "Nombre",
"description": "Descripción",
"archived": "Archivado",
"archive": "Archivar",
"unarchive": "Restaurar",
"showArchived": "Mostrar archivados",
"packTemplates": "Plantillas del paquete",
"percent": "Porcentaje (%)",
"fixed": "Fijo (PYG)",
"saved": "Guardado",
"deleted": "Eliminado",
"inUse": "Está en uso. Archivalo.",
"confirmDelete": "¿Eliminar {name}?",
"empty": "Todavía no hay nada"
},
"financeOverview": {
"title": "Finanzas",
"subtitle": "Resultados de todos los eventos. Los eventos cerrados usan sus números congelados.",
"from": "Desde",
"to": "Hasta",
"series": "Serie",
"venue": "Lugar",
"partner": "Socio",
"all": "Todos",
"noSeries": "Sin serie",
"events": "Eventos",
"event": "Evento",
"date": "Fecha",
"perEvent": "Ganancia por evento",
"perSeries": "Por serie",
"perVenue": "Por lugar",
"perPartner": "Totales por socio",
"paid": "Pagado",
"pending": "Pendiente",
"empty": "No hay eventos con datos financieros en este rango",
"filters": "Filtros",
"readyCount_one": "1 evento pasado",
"readyCount_other": "{count} eventos pasados",
"readyHint": "todavía con cuentas abiertas. Agregá los últimos gastos y cerralas.",
"showReady": "Ver listos para cerrar",
"showAll": "Ver todos los eventos",
"readyTitle": "Listos para cerrar",
"readyBadge": "Listo para cerrar",
"clippedHint": "Las barras con una marca de corte están recortadas para que se lean las demás. La etiqueta muestra el valor real."
},
"eventForm": {
"series": "Serie",
"seriesHint": "Agrupa eventos recurrentes (ej. Morning Club) en el resumen de Finanzas."
},
"eventStats": {
"capacity": "Capacidad",
"notCheckedIn": "Sin ingresar todavía",
"checkedIn": "Ingresados",
"revenue": "Ingresos",
"presaleDoor": "Preventa {presale} · Puerta {door}",
"stats": "Datos",
"inShort": "{count} adentro",
"free": "Gratis"
}
},
"footer": {
@@ -392,4 +894,4 @@
"downloading": "Descargando…"
}
}
}
}