Merge the OrderReturn endpoints into one domain PR - #430
PrestaEdit wants to merge 8 commits into
Conversation
Adds:
- DELETE /order-returns/{orderReturnId} (DeleteOrderReturnCommand)
- DELETE /order-returns/bulk-delete (BulkDeleteOrderReturnsCommand)
Single-delete op added to existing OrderReturn.php; bulk op lives in a
new BulkOrderReturns.php file.
The DeleteOrderReturn / BulkDeleteOrderReturns commands were introduced
in PS 9.1+/develop and do not exist at the 9.0.3 tag — 9.0.3 CI matrix
legs will fail here until PR PrestaShop#220 (drop 9.0.3 from CI) lands.
Related to PrestaShop/PrestaShop#39630
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds:
- GET /order-returns/{orderReturnId}/products (GetOrderReturnProducts —
returns per-line rows: orderDetailId, customizationId, reference,
productName, quantity, customization)
- DELETE /order-returns/{orderReturnId}/products/{orderDetailId}
(DeleteProductFromOrderReturnCommand — optional customizationId query
parameter for customized lines)
The core OrderReturn product-mgmt commands were introduced in PS 9.1+/
develop and do not exist at the 9.0.3 tag — 9.0.3 CI matrix legs will
fail here until PR PrestaShop#220 (drop 9.0.3 from CI) lands.
The tests only cover scope protection; happy-path coverage would need
to seed a full OrderReturn + OrderReturnDetail row set, which the
existing test infrastructure does not currently expose.
Related to PrestaShop/PrestaShop#39630
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds DELETE /order-returns/{orderReturnId}/products/bulk-delete using
CQRSDelete with BulkDeleteProductsFromOrderReturnCommand. Body:
{stagedProductRows: [{order_detail_id, customization_id?}, ...]} —
snake_case matches the Command's ctor read shape ($row['order_detail_id'],
$row['customization_id']). The Command builds OrderReturnProductId VOs
internally from each row.
Complements PrestaShop#372 (single-product delete + list).
Scope-protection test only — happy-path coverage would need seeding an
OrderReturn with actual OrderReturnDetail rows which the existing test
infrastructure does not currently expose.
Depends on OrderReturn (PS 9.1+/develop) — 9.0.3 CI matrix legs will
fail until PR PrestaShop#220 (drop 9.0.3 from CI) lands.
Related to PrestaShop/PrestaShop#39630
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidates PrestaShop#371 (delete + bulk-delete), PrestaShop#372 (products list + delete) and PrestaShop#386 (bulk delete products). Two of the three shipped with no test method at all: PrestaShop#372 and PrestaShop#386 only declared their endpoints in getProtectedEndpoints(), so the scope protection was covered and the behaviour was not. They could not do much better on their own — PrestaShop#372's listing is the only read side of PrestaShop#386's bulk delete, and PrestaShop#371's delete had nothing to check against either. One OrderReturnActionsEndpointTest now covers the five endpoints: - the products listing asserts the complete row structure and the seeded lines - deleting a product, and bulk deleting them, are asserted through that listing - deleting an order return, and bulk deleting them, are asserted by GET /order-returns/{orderReturnId} answering 404, instead of SELECT COUNT(*) FROM ps_order_return Order returns are created by the customer from the front office and the domain has only Delete, BulkDelete and UpdateState commands, so seeding stays SQL — in one documented helper that also creates the order_return_detail rows the product endpoints need. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI reported DELETE /order-returns/999999 answering 405 (Allow: GET, PATCH) on 9.0.3 and 9.1.4, which cascaded into ten failures. Every command and query these five endpoints use — DeleteOrderReturnCommand, BulkDeleteOrderReturnsCommand, GetOrderReturnProducts, DeleteProductFromOrderReturnCommand and BulkDeleteProductsFromOrderReturnCommand — landed in 9.2. ApiResourceScopesExtractor::skipCQRSNotFound() silently drops any operation whose CQRS class is missing, so on 9.0.3 and 9.1.4 the routes were never registered and only the GET and PATCH the module already had answered. The five operations now declare minVersion 9.2.0, and the test class skips as a whole below that version — the scopes do not exist there either, so createApiClient() could not run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… on DELETE
testBulkDeleteProductsFromOrderReturn deleted every product of the return; the
core refuses to empty a merchandise return, so the request answered 500. The
test now keeps the last row, and a second test pins the refusal — mapped to 422
through CannotDeleteLastProductFromOrderReturnException instead of a 500.
DELETE /order-returns/{id} answered 422 on "orderReturnStateId should not be
null": ApiPlatform validates the resource on the delete too, and the state is
only ever sent on the update. The constraint moves to an Update group the
partial update declares.
The delete and bulk operations declare minVersion 9.2.0, so their CQRS classes are legitimately absent on the older cores the matrix still runs. Same treatment as the TaxRule create/delete layer.
GetOrderReturnProducts is 9.2-only like the commands.
📋 Summary of changesThis PR adds five new Admin API endpoints covering the OrderReturn domain: ⏱️ Estimated review time15–20 minutes — five new resource classes plus one test class, a pre-9.2 PHPStan suppression block, and two targeted changes to an existing resource. Main work is verifying Core 9.2 field names against the collection DTO. 🎯 Scope
🧱 API Platform / CQRS architecture compliance✅ What is correct
Scopes — URI conventions — All paths are plural, lowercase, kebab-case; parent path correctly prefixes sub-resource paths;
No forbidden patterns — No custom normalizers, processors, or Value Object properties detected.
|
nicosomb
left a comment
There was a problem hiding this comment.
Two more things worth noting, not really actionable in this PR:
Bulk delete isn't atomic. AbstractBulkCommandHandler::handleBulkAction only catches exceptions matching the type passed to it (DeleteOrderReturnException here), but OrderReturnRepository::delete() throws OrderReturnNotFoundException on a missing id, a sibling exception, not a subtype. Hitting a missing id partway through a batch re-throws immediately and abandons the rest of the array, with no rollback of what was already deleted. This is shared Core behavior, not specific to this PR, and no test here exercises a mixed valid/invalid batch.
Two items from the automated pre-review don't hold up. allowEmptyBody: false being absent on BulkOrderReturns/BulkOrderReturnProducts has no effect, false is already the framework default (CQRSApiSerializer::isEmptyBodyAllowed). And OrderReturnProductList's field mapping is fine as is, every property, including the isCustomization getter mapping to customization, matches GetOrderReturnProducts's result by name.
| ), | ||
| ], | ||
| exceptionToStatus: [ | ||
| OrderReturnNotFoundException::class => Response::HTTP_NOT_FOUND, |
There was a problem hiding this comment.
Only OrderReturnNotFoundException is mapped. BulkDeleteOrderReturnsCommand's constructor throws OrderReturnConstraintException for an invalid id (OrderReturnId::assertIsIntegerGreaterThanZero), unmapped here so it falls through to 500. Needs OrderReturnConstraintException mapped to 422.
| { | ||
| public int $orderReturnId; | ||
|
|
||
| public int $orderDetailId; |
There was a problem hiding this comment.
No ApiProperty(identifier: true) on orderDetailId, unlike every other non-bulk single-target resource in the module (CategoryDelete, TaxRule, SearchEngine/DeleteSearchEngine, Address all mark theirs). Worth aligning for consistency.
|
|
||
| public int $quantity; | ||
|
|
||
| public bool $customization; |
There was a problem hiding this comment.
GetOrderReturnProductsHandler builds each row from OrderReturnProductForEditing, which also exposes getCustomizationFields() (the actual customization content, e.g. text/file inputs), not exposed here. The DTO says a line is customized but never what the customization contains.
What this PR does
Merges #371, #372 and #386 into one PR, following the mutualisation done on the Product domain in #410.
Endpoints
/order-returns/{orderReturnId}DeleteOrderReturnCommandorder_return_write/order-returns/bulk-deleteBulkDeleteOrderReturnsCommandorder_return_write/order-returns/{orderReturnId}/productsGetOrderReturnProductsorder_return_read/order-returns/{orderReturnId}/products/{orderDetailId}DeleteProductFromOrderReturnCommandorder_return_write/order-returns/{orderReturnId}/products/bulk-deleteBulkDeleteProductsFromOrderReturnCommandorder_return_writeTwo of the three had no behavioural test
#372 and #386 shipped with a
getProtectedEndpoints()declaration and nothing else — scope protection covered, behaviour not. That was hard to avoid one PR at a time: #372's products listing is the only read side of #386's bulk delete, and #371's delete had nothing to check against either, so it fell back toSELECT COUNT(*) FROM ps_order_return.One
OrderReturnActionsEndpointTestnow covers the five endpoints:GET /order-returns/{orderReturnId}answering404GET, for each idOn fixtures: order returns are created by the customer from the front office. The
OrderReturndomain exposes onlyDeleteOrderReturnCommand,BulkDeleteOrderReturnsCommandandUpdateOrderReturnStateCommand— there is no way to create one through the Admin API, so the seeding stays SQL. It is now a single documented helper, and it also creates theorder_return_detailrows the product endpoints need, which is what makes those tests possible at all.How to test
Covered by
OrderReturnActionsEndpointTest.Supersedes
All three will be closed once the CI is green here.