Appearance
Customer Service and Payments
This page maps the legacy Symfony menu entries for carts, payment follow-up, refunds, and SAV (after-sales cases) to their controllers and data paths. It describes the source snapshot supplied for this task, identified as axelites/logidav commit 88d9180215a5d489407498fe4905fdd38efd756d; source review does not establish what is currently deployed.
Access and source boundaries
The menu builder creates payment and SAV submenus for the menzzo and meublePromo sources. The non-tree menu is filtered against the user's menu-access list, with a ROLE_ADMIN exception; many page actions also call mz.menu_provider->hasAccess(). That menu filtering does not prove that every AJAX route has the same route-level check, so use the page-specific access path described below rather than treating a hidden menu item as an authorization boundary.
The endpoint examples below use Symfony route names and the path suffixes declared on the controller methods. Their controller-level prefixes are cart, refund, presto, reconciliation-of-payments, sav, stat, and statistic; any deployment-level prefix is not established here.
Carts, contact follow-up, and SMS
| Menu entry | Route and input | What the source does |
|---|---|---|
| Tous les paniers | cart_index, GET /cart/; list data uses paginate_carts | Renders the shared cart table. The default date interval is today; filters include date, cart ID, called flag, call status, and reason. The table is backed by AbandonedCart and its repository. |
| Paniers abandonnées avec téléphone / sans téléphone | cart_with_phone, cart_without_phone; both use the shared cart view and paginate_carts | Selects the phone/no-phone view mode. The default date interval is the previous month through today. The same call-tracking fields are available; the phone page also supplies payment-method labels. |
| Annulation avant paiement | cancellation_before_payment, also the shared cart view and paginator | Uses the cart list with the before-payment mode and the previous-month date default. It remains a cart follow-up screen; the route itself does not issue a payment refund. |
| Annulation avant expédition | cancellation_before_shipment, data via cancellation_before_shipment_paginate | This list is built from Refund rows with autorisation = 1 and the fixed pattern “Annulation avant expédition”. Its update route records call status, failure reason, recall time, caller, and time on the refund row; to_recall creates a Notif. This is follow-up on an authorized refund record, not proof that an order was canceled or money returned. |
| Rapport paniers abandonnées | cancellation_before_shipment_report; data via cancellation_before_shipment_report_paginate | Defaults to the previous seven days. cart_range accepts a displayed d/m/Y - d/m/Y interval; results come from LogReport and include user, order increment ID, amount, action, and date. |
| Sms Paniers abandonnées | cart_with_phone_sms; table via paginate_carts_sms | Lists phone-bearing carts for SMS work. The page defaults to the previous month through today and a grandTotal threshold of 300; available SMS rules come from mz.config. |
| Sms commandes en attente de paiement | sale_pending_payment; table via sale_pending_payment_paginate | Queries pending-payment sales and renders customer, order, products, shipping, coupon, and SMS-history fields. The page also defaults to the previous month through today and a grandTotal threshold of 300. Rows emit a real-shipping-method edit control and the template loads sales.js with the realShipment choices, but it does not define the sale_update_real_shipment route variable used by that handler; the source does not establish that this screen can submit the edit. The backend POST /sale/update-real-shipment updates the sale method/description and log, rejects CSV-exported sales, and may send a retrait email, but that backend behavior alone does not prove this page is wired to it. |
| Résultats d'envois des SMS | cart_to_sale_through_sms; data via paginate_cart_to_sale_through_sms | The repository joins carts to sales through sale_id_after, requires send_sms to be present, and accepts a storeId filter. It is a result view, not a delivery receipt from the SMS provider. |
Cart call updates use update_cart with cart ID, called flag, status, reason, order reference, and optional recall time. The controller saves the cart and an AbandonedCartLog; an order reference also creates a LogReport, and to_recall creates a notification. Marking a cart deleted uses remove_cart; cart_check_order asks the sale service to check for an order and marks the cart deleted only when a match is returned. A cart-log lookup requires a numeric ID and returns HTTP 400 for an invalid query ID; repository errors return HTTP 500.
Selecting carts and submitting cart_send_all_sms is an external communication path. The POST carries cart IDs, rule ID, and a polling token; the controller records “SMS scheduled” logs and starts menzzo:abandonedcarts:sms. That command obtains the configured SMS text and coupon, calls AbandonedCartService::sendSms(), and records the send result on the cart. Missing or invalid phone numbers, missing coupon, or a failed SMS send return an error result. The SMS provider implementation and actual delivery receipt are outside this path. The POST action itself does not call hasAccess() in its method; broader firewall authorization was not reviewed.
When a call is marked do_not_answer, update_cart attempts to enqueue the ChatwootWhatsappTemplateProcessor with the abandoned_cart template key and records a cart log. A missing phone or queue exception produces an error response; enqueueing does not establish that WhatsApp delivered the message.
Refunds and payment operations
| Menu entry | Route and input | What the source does |
|---|---|---|
| Remboursements / Pré-remboursements | refund_index at /refund/index/{source} and refund_pre at /refund/refund-pre/{source}; source branches are menzzo and meublePromo | The corresponding paginators query Refund rows by source, authorization state, and table filters. The main refund list filters for autorisation = 0; the pre-refund list filters for autorisation = 1. List visibility can be narrowed for users with only REFUND_LESS_THAN_100_EURO. See Refunds and SAV for the provider-specific submission and reconciliation paths. |
| Create a pre-refund from an order | refund_add_pre_refund (POST, CSRF required; ROLE_ADMIN or PRE_REFUND_FROM_ORDER) | Resolves the sale from incrementId, records amount/type/reason/information and payment details in a Refund, sets status 0 and authorization 1, creates a linked SaleSav, and writes a SaleLog. Loyalty vouchers are rejected here and must use their dedicated import path. |
| Submit a refund | pre_refund_send (POST) | Loads the refund and sale, rejects missing records, loyalty-voucher rows, unsupported/non-Menzzo payment methods, and some manually paid orders. Supported provider branches call provider-specific code and persist provider-specific results. HTTP/status outcomes are not a single shared contract; see the linked refund page. |
| Resolve or generate a manual refund | resolve_refund, generate_manually_refund | Bulk resolution writes a manual resolution message and sets refund status to 1; manual generation sets the “Remboursement manuel” note, clears authorization, and marks the row complete. These local state changes are not provider confirmation. |
| Statistiques / Alma | stat_payment at /stat/payment; statistic_alma_payment at /statistic/alma | The first reads payment and Payline statistics for a submitted date range. The Alma page uses a date range and a sale-repository aggregate. Both render reports; the code does not submit a payment. |
| Commandes Presto | presto_index at /presto/; data via /presto/paginate | Lists SalePresto records with linked sale date/order status and stored Payline status. The row action status_payllne_presto (POST, id) calls Payline getTransactionDetails(), then stores the response, status text, and isTreated = 1. Provider exceptions and retry behavior are not handled by a common contract in the inspected action. |
| Rapprochement des paiements | reconciliation_of_payments_index and reconciliation_of_payments_import_files_index under /reconciliation-of-payments/ | The order table uses SaleRepository::searchReconciliationOfPaymentsSaleList() and can export visible columns. The import page lists FilesImport records; its inherited upload handler accepts a files collection and stores records under the reconciliation import folder. The import command processes pending/processing records, updates status, and saves line/error data. A successful upload therefore means a file was recorded, not that reconciliation completed. File-format validation, command schedule, and deployed processing status were not established here. |
| Payline / Ezyness | payline_ezynesss_mapping, /sale/payments/payline-ezyness/mapping | The view reads mapping rows through PaylineTransactionLogService, backed by mz_payline_commands_mapping joined to mz_sale; the table can search, sort, and paginate increment/order, transaction, and amount fields. The controller contains two methods declaring the same route name and path; route-registration precedence and which view action runs at runtime are unresolved. |
Payment menus also include source-scoped refund exports and provider-specific status/detail routes. Use the route names from src/AppBundle/Menu/Builder.php and the individual controller methods; do not infer a common refund lifecycle across providers. A refund row, a manual resolution, a local “complete” flag, and a provider's settled transaction are distinct evidence.
SAV case handling
The SAV submenu is also generated for menzzo and meublePromo. Its main route family is sav_index at /sav/index/{source} and shares SaleSav search/pagination with the state-specific screens. Menu variants are grouped below; their repository methods define distinct result sets rather than separate case entities.
| Menu entry | Route family | Source-backed use |
|---|---|---|
| Liste SAV / Tous les SAV | sav_index, sav_all | Paginated case lists from SaleSavRepository::search() and searchAllSav(). The page supplies manufacturer, communication-type, and agent filter choices. |
| SAV validés | sav_validated | Uses searchSavValidated() for the validated subset. |
| Demandes d'annulation avant expédition / SAV annulation avant expédition | sav_cancelation_request, sav_canceled | Separate request and canceled lists use searchCancelationRequests() and searchSavCanceled(). They should not be treated as interchangeable statuses. |
| SAV relances / SAV Prioritaires / Échange SAV à valider | sav_relaunch_request, sav_priority, sav_exchange | Three separate repository queries (searchRelaunchRequests, searchPrioritySav, and searchEchangeSav) drive follow-up, priority, and exchange queues. |
| Pièces détachées → Import des pièces détachées Asir | sale_sav_asir_spare_parts_upload | The upload page is followed by the FilesImportService upload/status routes and AsirSparePartsService line, dimension, order, and SKU actions. Uploading is not proof that imported parts were matched or applied. |
| Export | sav_sav_export | Reads source, date range, date type, SAV status, and problem-type filters; queries SaleSav and exports an XLS result. |
| Export performance SAV | sav_sav_performance_export | Only displayed for ROLE_ADMIN or SEE_SAV_PERFORMANCE_EXPORT. The action validates its date range and starts a console-backed export/polling flow; invalid dates return HTTP 400. Completion is represented by the returned token data, not by the menu click. |
| Planning SAV / Planning SAV tests | sav_planing_dev, sav_planing_test | The legacy menu uses these exact route names. Their event endpoints call SavPlanningService with separate normal and TEST data scopes; create/update/delete actions can change planning records and treated status. The old sav_planing route is commented out in the menu. |
| Chat AI | sav_chat_ai | The screen posts a message to OpenAi::openaiCompletion(), returns text, and stores the request, result, user, and timestamp in ChatAiLog. The report-menu history and stats read these logs. The source does not establish retention, redaction, or an approved policy for customer-identifying text; do not treat this page as a customer-safe channel without that policy. |
| Tous les SAV de tests | v2_index?redirect_to=app.sav.archived_test | This is a Symfony-to-new-Logidav bridge target, not one of the legacy SaleSavController list actions; target behavior is not in the supplied source tree. |
The active list UI also supports bulk agent assignment. It keeps explicitly selected SAV IDs across pages in browser cookies for seven days, and the bundled script submits that ID list to POST /sav/bulk-assign-agent (sav_bulk_assign_agent). The service also accepts an all-selected selection object with filters and exclusions, but that fallback is not sent by the bundled script. The action requires mz.menu_provider->hasAccess('sav') and a valid agent and nonempty selection (400 for missing input, 404 for an unknown agent). SaleSavService::bulkAssign() updates SaleSav.user, skips unchanged assignments, writes a SAV log with the acting user and previous/new agent, and creates a manager-side SaleSavMessage that marks the conversation unresolved. A successful response reports updated and skipped counts; it does not change the SAV status.
The case screens and their mutation endpoints are implemented primarily in SaleSavController and SaleSavService. The refunds and SAV workflow page separately documents the verified fragments for SAV API intake, provider refund submission/reconciliation, and known physical-return/stock gaps. A SAV case, a cancellation request, an exchange queue, and a refund are separate records and transitions in the inspected code.
Evidence limits
The legacy route, controller, and service behavior above was reviewed at the immutable commit recorded in source_refs. New-Logidav destinations, live menu visibility, deployed route registration, provider delivery/settlement, and owner acceptance were not checked. The deeper implementation of payment and SAV actions remains provider- and state-specific as linked above.
Source entry points
- src/AppBundle/Controller/AbandonedCartController.php
- src/AppBundle/Services/AbandonedCartService.php
- src/AppBundle/Controller/RefundController.php
- src/AppBundle/Services/RefundService.php
- src/AppBundle/Controller/Sale/Sav/SaleSavController.php
- src/AppBundle/Controller/ReconciliationOfPaymentsController.php