Repository navigation
OM-318: gestión de reclamos de productos - #108
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 39 minutes and 19 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughSe agregan enums y tablas de reportes (productos y reviews) en migraciones y schema Prisma; se incorporan rutas, controlador y servicios para listar, filtrar y actualizar reportes de productos con validaciones de rol, restricciones por tienda y reglas de transición de estado. Changes
Sequence Diagram(s)sequenceDiagram
participant Cliente
participant Controlador
participant Servicio
participant BaseDeDatos
Cliente->>Controlador: PUT /api/reports/products/:reportId\n{ report_status, notes }
Controlador->>Controlador: validar req.user.id_user
Controlador->>Servicio: updateProductReportService(userId, reportId, payload)
Servicio->>BaseDeDatos: users.findUnique(id)
BaseDeDatos-->>Servicio: user
Servicio->>BaseDeDatos: stores.findUnique(fk_user)
BaseDeDatos-->>Servicio: store
Servicio->>BaseDeDatos: productReports.findFirst(filter por reportId y store)
BaseDeDatos-->>Servicio: report
Servicio->>Servicio: validar transición y requerir notes si cierra
Servicio->>BaseDeDatos: productReports.update({...report_status, notes, resolved_by?, resolved_at?})
BaseDeDatos-->>Servicio: updatedReport
Servicio-->>Controlador: updatedReport
Controlador-->>Cliente: HTTP 200 { updatedReport }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/modules/global/reports/product/product-report.service.js (1)
141-145: Validar explícitamente quereport_statusvenga en el body.Si el cliente omite
report_status,allowed.includes(undefined)devuelvefalsey el usuario ve un mensaje poco útil tipo"No podés pasar de PENDING a undefined...". Conviene validar arriba quereport_statussea un valor del enum antes del chequeo de transición, con un mensaje claro (ValidationError("report_status es requerido y debe ser uno de: ...")).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/reports/product/product-report.service.js` around lines 141 - 145, Before checking allowed.includes(report_status) validate that report_status is present and is one of the allowed enum values: if missing or not in the enum throw a ValidationError with a clear message like "report_status es requerido y debe ser uno de: ..." ; update the validation near the block that throws ValidationError (references: report_status, allowed, currentStatus, ValidationError) so the explicit enum check runs first and returns the clearer error before evaluating transition rules.src/modules/global/reports/product/product-report.controller.js (1)
8-71: Chequeo de autenticación redundante y con formato de error inconsistente.Los tres handlers replican el mismo bloque
if (!req.user?.id_user) return res.status(401).json(...). Como las rutas ya corren el middlewareauthenticateantes del controller,req.userdebería estar garantizado; si no lo está, es un bug del middleware y conviene fallar de forma uniforme. Además, esteres.status(401).json(...)inline bypassa elerrorHandlercentral y usa un shape distinto ({ success, message }) al resto de errores del sistema (AppError+errorHandler).Sugerencia: eliminar el chequeo (confiar en el middleware) o, si se quiere mantener defensa en profundidad, lanzar un
UnauthorizedErrorvíanext()para queerrorHandleruniformice la respuesta.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/reports/product/product-report.controller.js` around lines 8 - 71, The three controller functions getProductsReports, updateProductReport, and getProductsReportsFiltered contain redundant inline authentication checks that bypass the central error handler; remove the repeated if (!req.user?.id_user) { return res.status(401)... } blocks and either trust the authenticate middleware or, for defense-in-depth, replace each inline response with next(new UnauthorizedError('Usuario autenticado requerido')) (or the project's equivalent AppError/Unauthorized error constructor) so the centralized errorHandler produces a consistent error shape.src/modules/global/reports/product/product-report.routes.js (1)
7-7:mergeParams: trueinnecesario.Este router se monta en
/api/reports(versrc/app.jslínea 77), que no es una ruta con parámetros padres.mergeParamssolo tiene sentido cuando el router se anida bajo una ruta con:paramsque se quieran heredar. Podés quitarlo para evitar ruido semántico.♻️ Cambio sugerido
-const router = Router({ mergeParams: true }); +const router = Router();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/reports/product/product-report.routes.js` at line 7, Elimina la opción innecesaria mergeParams en la declaración del router: sustituye la creación de router que usa Router({ mergeParams: true }) por una creación simple Router() (identifica la línea con la variable router y la llamada a Router). Asegurate de revisar rápidamente que ninguna lógica en este archivo (rutas dentro de product-report.routes.js) dependa de parámetros heredados antes de removerla; si no hay dependencia, quitar mergeParams reduce ruido semántico.prisma/migrations/20260417000531_add_reports_tables/migration.sql (1)
28-44: Considerá agregar índices sobre columnas FK y sobre el hot path de listado.Postgres no crea índices automáticamente sobre columnas de FK. Dado que el servicio consulta
ProductReportsfiltrando porstatusy ordenando porcreated_at DESC, y que los joins se harán porfk_product,fk_reporteryfk_order, sería conveniente agregar índices desde el schema de Prisma (por ejemplo@@index([fk_product]),@@index([fk_reporter]),@@index([fk_order]), y un@@index([status, created_at])para acelerar el listado paginado). Lo mismo aplica aReviewReports(FKsfk_product_review,fk_reporter,resolved_by).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@prisma/migrations/20260417000531_add_reports_tables/migration.sql` around lines 28 - 44, Agregar índices para acelerar joins y listados: en el modelo ProductReports añade índices para fk_product, fk_reporter y fk_order (p. ej. @@index([fk_product]), @@index([fk_reporter]), @@index([fk_order])) y un índice compuesto para el hot path de listado @@index([status, created_at]) para paginación/ordenamiento; hacer cambios análogos en el modelo ReviewReports creando @@index([fk_product_review]), @@index([fk_reporter]), @@index([resolved_by]) y un @@index([status, created_at]) si aplica. Estos índices corresponden a las columnas y modelos ProductReports (id_product_report, fk_product, fk_reporter, fk_order, status, created_at) y ReviewReports (fk_product_review, fk_reporter, resolved_by, status, created_at) en el schema de Prisma.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/modules/global/reports/product/product-report.service.js`:
- Around line 198-204: The filter validation in
getProductsReportsFilteredService rejects the new IN_PROGRESS enum because
allowedStatuses only contains ["PENDING","RESOLVED","REJECTED"]; update the
allowedStatuses array to include "IN_PROGRESS" and ensure the ValidationError
message (the join of allowedStatuses) reflects the updated list; verify
references to report_status and the related updateProductReportService flow
still accept IN_PROGRESS as a valid intermediate state.
- Around line 10-48: Declare BASE_QUERY as a local constant (e.g., add "const
BASE_QUERY = { ... }") to avoid ReferenceError; remove or replace the invalid
"store" selection inside BASE_QUERY and instead include the store via the
product relation (e.g., use product.select.store or nest product.select to
select store fields) because ProductReports has no direct store relation; update
any filters that use fk_store (references to fk_store in where clauses inside
functions like the store-scoped queries) to filter through the product relation
(e.g., where: { product: { fk_store: store.id_store } }) since ProductReports
doesn't have fk_store; and add "IN_PROGRESS" to the allowedStatuses array so it
matches the ReportStatus enum.
- Around line 35-48: El servicio está usando un campo/relación inexistente
(fk_store / store) en el modelo ProductReports; actualiza BASE_QUERY y todos los
where/filters para usar la relación product en su lugar: eliminar el select raiz
"store" y anidar ese select dentro de product.select (incluyendo store: {
select: { id_store, name } }), y reemplazar cualquier filtro que use fk_store
(en getProductsReportsService, updateProductReportService,
getProductsReportsFilteredService) por product: { fk_store: <valor> } (es decir
aplicar el filtro sobre ProductReports.product.fk_store). Asegúrate de ajustar
BASE_QUERY, el select del filtrado y las cláusulas where mencionadas para
referenciar product en vez de store.
---
Nitpick comments:
In `@prisma/migrations/20260417000531_add_reports_tables/migration.sql`:
- Around line 28-44: Agregar índices para acelerar joins y listados: en el
modelo ProductReports añade índices para fk_product, fk_reporter y fk_order (p.
ej. @@index([fk_product]), @@index([fk_reporter]), @@index([fk_order])) y un
índice compuesto para el hot path de listado @@index([status, created_at]) para
paginación/ordenamiento; hacer cambios análogos en el modelo ReviewReports
creando @@index([fk_product_review]), @@index([fk_reporter]),
@@index([resolved_by]) y un @@index([status, created_at]) si aplica. Estos
índices corresponden a las columnas y modelos ProductReports (id_product_report,
fk_product, fk_reporter, fk_order, status, created_at) y ReviewReports
(fk_product_review, fk_reporter, resolved_by, status, created_at) en el schema
de Prisma.
In `@src/modules/global/reports/product/product-report.controller.js`:
- Around line 8-71: The three controller functions getProductsReports,
updateProductReport, and getProductsReportsFiltered contain redundant inline
authentication checks that bypass the central error handler; remove the repeated
if (!req.user?.id_user) { return res.status(401)... } blocks and either trust
the authenticate middleware or, for defense-in-depth, replace each inline
response with next(new UnauthorizedError('Usuario autenticado requerido')) (or
the project's equivalent AppError/Unauthorized error constructor) so the
centralized errorHandler produces a consistent error shape.
In `@src/modules/global/reports/product/product-report.routes.js`:
- Line 7: Elimina la opción innecesaria mergeParams en la declaración del
router: sustituye la creación de router que usa Router({ mergeParams: true })
por una creación simple Router() (identifica la línea con la variable router y
la llamada a Router). Asegurate de revisar rápidamente que ninguna lógica en
este archivo (rutas dentro de product-report.routes.js) dependa de parámetros
heredados antes de removerla; si no hay dependencia, quitar mergeParams reduce
ruido semántico.
In `@src/modules/global/reports/product/product-report.service.js`:
- Around line 141-145: Before checking allowed.includes(report_status) validate
that report_status is present and is one of the allowed enum values: if missing
or not in the enum throw a ValidationError with a clear message like
"report_status es requerido y debe ser uno de: ..." ; update the validation near
the block that throws ValidationError (references: report_status, allowed,
currentStatus, ValidationError) so the explicit enum check runs first and
returns the clearer error before evaluating transition rules.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 22294b8f-d3a3-4757-bc2c-f8f7fe2feddc
📒 Files selected for processing (7)
prisma/migrations/20260417000531_add_reports_tables/migration.sqlprisma/migrations/20260417115654_add_in_progress_report_status/migration.sqlprisma/schema.prismasrc/app.jssrc/modules/global/reports/product/product-report.controller.jssrc/modules/global/reports/product/product-report.routes.jssrc/modules/global/reports/product/product-report.service.js
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/modules/global/reports/product/product-report.service.js (1)
141-149: Validación explícita dereport_statusmejoraría el mensaje de error.Si el consumidor no envía
report_status(o envía un valor fuera del enum, como"FOO"), se lanza el error genérico de transición indicando "No podés pasar de PENDING a undefined", lo cual resulta confuso para el cliente. Considerar validar presencia y pertenencia al enumReportStatusantes de evaluar la transición, reutilizando la listaallowedStatusesque ya se usa engetProductsReportsFilteredService.♻️ Refactor sugerido
+ const allowedStatuses = ["PENDING", "IN_PROGRESS", "RESOLVED", "REJECTED"]; + if (!report_status || !allowedStatuses.includes(report_status)) { + throw new ValidationError( + `Estado inválido. Los valores permitidos son: ${allowedStatuses.join(", ")}` + ); + } + const currentStatus = report.report_status; const allowed = allowedTransitions[currentStatus];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/reports/product/product-report.service.js` around lines 141 - 149, Validate that report_status is present and a valid enum value before checking allowedTransitions: in the block using currentStatus, allowedTransitions and allowed (and throwing ValidationError), first check if report_status is undefined/null and throw ValidationError like "report_status is required" and then check membership using the existing allowedStatuses array (the same list used by getProductsReportsFilteredService); if report_status is not in allowedStatuses throw a ValidationError listing allowedStatuses, only after those checks perform the existing transition check (if (!allowed.includes(report_status)) ...) to yield the clearer transition error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/modules/global/reports/product/product-report.service.js`:
- Around line 141-149: Validate that report_status is present and a valid enum
value before checking allowedTransitions: in the block using currentStatus,
allowedTransitions and allowed (and throwing ValidationError), first check if
report_status is undefined/null and throw ValidationError like "report_status is
required" and then check membership using the existing allowedStatuses array
(the same list used by getProductsReportsFilteredService); if report_status is
not in allowedStatuses throw a ValidationError listing allowedStatuses, only
after those checks perform the existing transition check (if
(!allowed.includes(report_status)) ...) to yield the clearer transition error.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0f30260f-32ea-4a07-93f9-b6d9159c3772
📒 Files selected for processing (2)
src/modules/global/reports/product/product-report.routes.jssrc/modules/global/reports/product/product-report.service.js
🚧 Files skipped from review as they are similar to previous changes (1)
- src/modules/global/reports/product/product-report.routes.js
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@CodeRabbit help |
ChatThere are 3 ways to chat with CodeRabbit:
CodeRabbit commands
Other keywords and placeholders
CodeRabbit configuration file (
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/modules/global/reports/product/product-report.service.js (1)
64-85: Evitá listados sin límite.Ambos
findManydevuelven todos los reclamos activos. Si el volumen crece, este endpoint puede volverse pesado; conviene reutilizar el flujo paginado o aplicar un límite por defecto.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/modules/global/reports/product/product-report.service.js` around lines 64 - 85, Las llamadas a prisma.productReports.findMany (tanto la rama ADMIN como la SELLER) usan BASE_QUERY pero no aplican paginación ni límite por defecto; cambia ambas invocaciones para reutilizar el flujo paginado o imponer límites seguros: agrega parámetros de paginación (p. ej. take/skip o cursor) a las llamadas a prisma.productReports.findMany y/o extiende BASE_QUERY para incluir valores por defecto (limit/take y offset/skip) si no vienen desde la request. Asegúrate de aplicar la misma lógica de paginado cuando construyas la consulta para SELLER (la que extiende BASE_QUERY con product.fk_store) y documenta/propaga los parámetros de paginado desde la función que ejecuta estas ramas.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/modules/global/reports/product/product-report.service.js`:
- Around line 151-166: Validate that notes is a string before calling trim():
check typeof notes === "string" (or convert non-null values to String only after
verifying) and if notes is provided but not a string, throw a ValidationError
(using the existing ValidationError symbol) with a 400-friendly message; then
use notes.trim() safely in both the mandatory-note check that gates
RESOLVED/REJECTED and in the prisma.productReports.update data spread (the
variables report_status, notes, resolvedReportId and
prisma.productReports.update are the places to change). Ensure the logic still
enforces non-empty trimmed notes for closed statuses and only includes notes in
data when a valid trimmed string exists.
- Around line 116-170: The status transition is vulnerable to race conditions
because you read with prisma.productReports.findFirst and later update with
prisma.productReports.update using only id_product_report; ensure the
read+validation+write is atomic by either wrapping the logic in a Prisma
transaction ($transaction) or by making the update conditional on the current
status (add report_status: currentStatus to the where clause on the update that
uses resolvedReportId), so only the row still in currentStatus is modified
(refer to currentStatus, report_status, resolvedReportId,
prisma.productReports.findFirst and prisma.productReports.update).
---
Nitpick comments:
In `@src/modules/global/reports/product/product-report.service.js`:
- Around line 64-85: Las llamadas a prisma.productReports.findMany (tanto la
rama ADMIN como la SELLER) usan BASE_QUERY pero no aplican paginación ni límite
por defecto; cambia ambas invocaciones para reutilizar el flujo paginado o
imponer límites seguros: agrega parámetros de paginación (p. ej. take/skip o
cursor) a las llamadas a prisma.productReports.findMany y/o extiende BASE_QUERY
para incluir valores por defecto (limit/take y offset/skip) si no vienen desde
la request. Asegúrate de aplicar la misma lógica de paginado cuando construyas
la consulta para SELLER (la que extiende BASE_QUERY con product.fk_store) y
documenta/propaga los parámetros de paginado desde la función que ejecuta estas
ramas.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 68c0e9c4-a98a-4da1-bd8b-2aad948cb0f0
📒 Files selected for processing (1)
src/modules/global/reports/product/product-report.service.js
| const report = await prisma.productReports.findFirst({ | ||
| where: { | ||
| id_product_report: resolvedReportId, | ||
| status: true, | ||
| product: { | ||
| fk_store: store.id_store, | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| if (!report) throw new NotFoundError("Reporte no encontrado"); | ||
|
|
||
| // No se puede modificar un reporte ya cerrado | ||
| if (report.report_status === "RESOLVED" || report.report_status === "REJECTED") | ||
| throw new ValidationError("Este reporte ya fue cerrado y no puede modificarse"); | ||
|
|
||
| // Validar transiciones permitidas | ||
| // PENDING -> IN_PROGRESS | ||
| // IN_PROGRESS -> RESOLVED | ||
| // IN_PROGRESS -> REJECTED | ||
| const allowedTransitions = { | ||
| PENDING: ["IN_PROGRESS"], | ||
| IN_PROGRESS: ["RESOLVED", "REJECTED"], | ||
| }; | ||
|
|
||
| const currentStatus = report.report_status; // estado actual del reporte | ||
| const allowed = allowedTransitions[currentStatus]; // estados permitidos para el siguiente estado | ||
|
|
||
| // si el nuevo estado no esta en los permitidos, lanza error | ||
| if (!allowed.includes(report_status)) { | ||
| throw new ValidationError( | ||
| `No podés pasar de ${currentStatus} a ${report_status}. Transiciones permitidas: ${allowed.join(", ")}` | ||
| ); | ||
| } | ||
|
|
||
| // Notas obligatorias al cerrar el reporte | ||
| if (["RESOLVED", "REJECTED"].includes(report_status) && !notes?.trim()) { | ||
| throw new ValidationError( | ||
| report_status === "RESOLVED" | ||
| ? "Debés agregar una nota explicando cómo se resolvió el reclamo" | ||
| : "Debés agregar una nota explicando por qué se rechazó el reclamo" | ||
| ); | ||
| } | ||
|
|
||
| const isClosed = ["RESOLVED", "REJECTED"].includes(report_status); | ||
|
|
||
| const updatedReport = await prisma.productReports.update({ | ||
| where: { id_product_report: resolvedReportId }, | ||
| data: { | ||
| report_status, | ||
| ...(notes && { notes: notes.trim() }), | ||
| ...(isClosed && { | ||
| resolved_by: resolvedUserId, | ||
| resolved_at: new Date(), | ||
| }), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verifica si el flujo sigue separado en read -> update por id, sin update condicional/transacción.
rg -nP 'productReports\.(findFirst|updateMany|update)\(|\$transaction|report_status:\s*currentStatus' -C4 src/modules/global/reports/product/product-report.service.jsRepository: CrisNAC/BackendMarketplace
Length of output: 661
🏁 Script executed:
#!/bin/bash
# Get complete function context - lines before and after the snippet
sed -n '100,180p' src/modules/global/reports/product/product-report.service.jsRepository: CrisNAC/BackendMarketplace
Length of output: 2729
🏁 Script executed:
#!/bin/bash
# Search for transaction usage around this function
rg -n '\$transaction|prisma\.' src/modules/global/reports/product/product-report.service.js | head -30Repository: CrisNAC/BackendMarketplace
Length of output: 904
🏁 Script executed:
#!/bin/bash
# Get the exact update block to see all where/data conditions
sed -n '162,180p' src/modules/global/reports/product/product-report.service.jsRepository: CrisNAC/BackendMarketplace
Length of output: 570
🏁 Script executed:
#!/bin/bash
# Get the function name and full context - look for async function declaration
sed -n '90,120p' src/modules/global/reports/product/product-report.service.jsRepository: CrisNAC/BackendMarketplace
Length of output: 1279
🏁 Script executed:
#!/bin/bash
# Search for export/function declaration around line 100
rg -n 'export|async.*resolveReport|function' src/modules/global/reports/product/product-report.service.js | head -20Repository: CrisNAC/BackendMarketplace
Length of output: 396
🏁 Script executed:
#!/bin/bash
# Get the end of the function and see where it closes
sed -n '162,200p' src/modules/global/reports/product/product-report.service.jsRepository: CrisNAC/BackendMarketplace
Length of output: 1393
🏁 Script executed:
#!/bin/bash
# Check for any prisma.$transaction usage in the file
rg -n '\$transaction' src/modules/global/reports/product/product-report.service.jsRepository: CrisNAC/BackendMarketplace
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Check for schema constraints on productReports related to state transitions
fd schema.prisma -o cat {} | rg -A50 'model ProductReports|model productReports' | head -80Repository: CrisNAC/BackendMarketplace
Length of output: 195
🏁 Script executed:
#!/bin/bash
# Find the schema.prisma file
fd -name 'schema.prisma' -o cat -n {} \; 2>/dev/null | head -200Repository: CrisNAC/BackendMarketplace
Length of output: 52
🏁 Script executed:
#!/bin/bash
# Search for ProductReport/productReports model in prisma schema
find . -name 'schema.prisma' -o -name '*.prisma' | xargs grep -A30 'model.*[Pp]roduct[Rr]eports' 2>/dev/nullRepository: CrisNAC/BackendMarketplace
Length of output: 1683
Hacé atómica la transición de estado de los reportes.
El flujo es vulnerable a race conditions: dos requests concurrentes pueden leer el reporte en estado IN_PROGRESS, ambas pasar la validación, y luego ambas ejecutar el update. La última escritura gana, violando la regla de "no modificar reportes cerrados".
Línea 162, el update solo filtra por id_product_report. Necesitá agregar la condición report_status: currentStatus en la cláusula where, o envolver todo en una transacción Prisma que incluya el read + validación + update como operación atómica:
where: {
id_product_report: resolvedReportId,
report_status: currentStatus // o envolver con $transaction
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/modules/global/reports/product/product-report.service.js` around lines
116 - 170, The status transition is vulnerable to race conditions because you
read with prisma.productReports.findFirst and later update with
prisma.productReports.update using only id_product_report; ensure the
read+validation+write is atomic by either wrapping the logic in a Prisma
transaction ($transaction) or by making the update conditional on the current
status (add report_status: currentStatus to the where clause on the update that
uses resolvedReportId), so only the row still in currentStatus is modified
(refer to currentStatus, report_status, resolvedReportId,
prisma.productReports.findFirst and prisma.productReports.update).
Summary by CodeRabbit
Notas de Lanzamiento
New Features
Bug Fixes
Tests
Documentation