Saltar al contenido principal

Disputas de la tienda

Los administradores arbitran las disputas que los compradores abren sobre pedidos de la tienda (ver Tienda) — resolviéndolas como un reembolso completo o denegando la disputa, con una nota. Esto no estaba documentado anteriormente en el lado de administración — documento nuevo.

Checklist de implementación

  • Listar disputas de productos abiertas (adminOpenProductDisputes)
  • Resolver una disputa — reembolso o denegación, con notas del administrador (adminResolveProductDispute)

Dónde vive esto

Backendgraphql/types/product-dispute.type.js, graphql/resolvers/product-dispute.resolver.js, managers/product-managers/product-dispute.manager.js. Nota: adminOpenProductDisputes / adminResolveProductDispute fueron renombrados este ciclo (de openProductDisputes/resolveProductDispute) para llevar el prefijo admin, así que la división de esquema de apps/backend/api/server.js (solo quedan en la instancia admin los campos que coinciden con /^admin[A-Z]/) ahora los enruta al esquema de administración (/admin/graphql) — estructuralmente inalcanzables por el cliente /web/graphql. El archivo de resolver sigue fuera de la carpeta admin/, y cada resolver además exige context.admin + el permiso MODERATE_CONTENT.

Frontendapps/frontend-admin/src/app/moderation/disputes/page.tsx. Lista las disputas abiertas vinculadas a pedidos de la tienda; un administrador (requiere MODERATE_CONTENT, según el documento de Tienda) resuelve cada una como un reembolso o una denegación con notas. La query/mutation son operaciones admin generadas en @closegram/apollo-admin (AdminGetOpenProductDisputesInline / AdminResolveProductDisputeInline), importadas por la página como Documents generados.

Referencia de GraphQL

query AdminGetOpenProductDisputesInline($limit: Int, $offset: Int) {
adminOpenProductDisputes(limit: $limit, offset: $offset) {
id orderId reason status createdAt
raisedBy { id username profilePicture }
order { id quantity totalPriceCoins status product { id name } seller { id username } }
}
}

mutation AdminResolveProductDisputeInline($disputeId: ID!, $resolution: ProductDisputeResolution!, $adminNotes: String) {
adminResolveProductDispute(disputeId: $disputeId, resolution: $resolution, adminNotes: $adminNotes) {
id status
}
}

adminResolveProductDispute devuelve el ProductDispute actualizado (no un payload de tipo success/message). El comprador que abrió la disputa se resuelve como raisedBy, no como buyer — no existe un campo buyer/seller directamente en ProductDispute; al vendedor se llega mediante order.seller.

ProductDisputeResolution es un enum con dos valores: resolved_refund y resolved_denied. Elegir resolved_refund revierte la transferencia de monedas (acredita al comprador, debita el saldo y las ganancias de por vida del vendedor) de la misma forma que lo hace cancelOrder, pero no restaura el inventario ni cambia el status propio del pedido — el pedido permanece como shipped/delivered a modo de registro de envío, y la fila de la disputa es lo que rastrea el reembolso. resolved_denied simplemente cierra la disputa sin ningún movimiento de monedas. En cualquier caso, el comprador recibe una notificación dispute_resolved (best-effort, se ignora si falla).