Appearance
Meduse product publication bridge
An inbound Logidav product file enters Meduse through the new-product FTP folder. A scheduled command queues ProcessNewProduct, which creates or updates catalog rows and requests a Menzzo export. The Meduse product grid can also change a product's Activé status; the UI has no separate Publish action. Menzzo's exporter turns versioned catalog changes into JSONL and puts the file in its Magento FTP pending folder. Magento's corresponding import and updated_<input>.csv response are traced in the Magento catalog import guide.
The schedules described here are source declarations. The checked-in modules file enables Logidav and Menzzo and their providers register schedule providers, but this review did not inspect a deployed scheduler, queue worker, FTP contents, or Magento state. For the broader module schedule inventory and queue behavior, see scheduled jobs, queues, and the connector map.
View diagram source
flowchart LR
FTPin["Logidav FTP<br/>/meduse/connectors/logidav/new-product/pending/{basename}"] -->|every-minute source schedule| Scan["meduse:module:logidav:generate-sync-jobs"]
Scan -->|copy local; move FTP pending to processing| NewJob["ProcessNewProduct<br/>queue: process-new-product"]
NewJob -->|create/enrich by SKU; success moves file| Catalog["Meduse CatalogProduct<br/>raw_values, channels, categories, media"]
NewJob -->|queue after successful import| Export["Menzzo ExportProducts"]
UI["Products2.vue<br/>select product → Modifier → edit Activé → Enregistrer"] --> Update["UpdateProductsExcel<br/>queue: processing"]
Update -->|ExportProductsEvent| Export
Sweep["menzzo:products-export<br/>every-minute declaration; dispatchSync"] --> Export
Export -->|JSONL FTP put| FTPout["/meduse/connectors/menzzo/export/pending/<br/>{timestamp}_{random}_export-products-{timestamp}.jsonl"]
FTPout --> Magento["Magento catalog import<br/>see linked guide"]
Magento -->|completion CSV| Ack["/meduse/connectors/menzzo/export/complete/<br/>updated_{input}.csv"]
Ack -->|every-minute source schedule| Sync["meduse:sync-after-magento-update"]
Sync -->|rewrite feedback; move same basename| Finished["/meduse/connectors/logidav/new-product/finished/{basename}"]
Logidav file intake
The input folder is meduse/connectors/logidav/new-product/pending on the configured FTP disk. The Logidav schedule provider declares meduse:module:logidav:generate-sync-jobs every minute, guarded by a process-list check. GenerateSyncJobsForCommand scans that folder, creates local directories, and copies each accepted file to modules/logidav/new-product/processing/{basename} on the default Storage disk (storage/app/modules/logidav/new-product/processing/{basename} when the default local disk is used). Before copying, it writes a local lock at modules/logidav/new-product/processing/lock/{basename}.lock. After the copy, it attempts to move the FTP file from pending to processing and pushes ProcessNewProduct onto the process-new-product queue. Its counter reaches ten after a successful FTP move, just before the queue push; a nearby comment still says two. Schedule and intake command · scan, lock, copy, move, and queue push · default Storage root
The command accepts every file it finds in pending; this source does not enforce a filename pattern. The job expects JSON with top-level attribute_set_id, new_product_id, and a products array. Each product entry supplies a SKU, name, categories, website IDs, store ID, custom attributes, and image data; product links, weight, price, and file attributes can also be processed. The following is a schematic example with invented identifiers, not a runnable import:
json
{
"attribute_set_id": 90001,
"new_product_id": "SYN-IMPORT-01",
"products": [
{
"sku": "SYN-CHAIR-001",
"name": "Synthetic chair",
"categories": [{ "category_id": 90002 }],
"websiteIds": [90003],
"store_id": 90004,
"customAttributes": [
{ "attribute_code": "color", "value": "synthetic-blue" }
],
"productLinks": [],
"images": []
}
]
}Create, enrich, and publish
ProcessNewProduct reads the local copy and loops over products. It looks up each SKU in catalog_product; a missing SKU is created with the incoming family and new_product_id, and is_enabled set to 1. An existing SKU is reused, with its family changed when the incoming family is nonempty. The job writes the supplied name, price, weight, mapped custom attributes, visibility, category/channel links, associations, and media paths. Website IDs are translated to catalog channels by the Logidav helper; select and multiselect values use the Logidav-to-Meduse option map. Image URLs are downloaded into Meduse public catalog media storage. This is an upsert by SKU, so replaying a renamed or manually requeued file can overwrite supplied fields and replace nonempty category or website/channel associations; absent fields generally remain from the existing raw values. Product job and file transitions · SKU create/update and default enabled state · catalog fields and save
After the file's product loop succeeds, the job attempts to move the FTP file from new-product/processing to new-product/complete and moves its local copy from processing to complete. If the remote move throws, the job logs it and continues to the export dispatch and finished status, so a finished tracked job alone does not prove the FTP file left processing. It queues the Menzzo ExportProducts job on queue export when no tenant is active; in the meuble_promo tenant context it queues that tenant's exporter instead. A caught processing exception marks the tracked job failed, tries to move the FTP file to new-product/failed, and rethrows. The local copy is not moved to a local failed folder in this catch path. processProduct has its transaction start commented out, so a later error does not guarantee rollback of earlier product writes. Success/failure handling and export dispatch · transaction boundary and product save · FTP helper
For an optional status or content edit, open Products, select the product, click Modifier, edit the Activé column, then click Enregistrer. The page submits changed rows to /api/bulk-update-products-excel; the API queues UpdateProductsExcel on processing. That job writes the is_enabled field and flat row, saves catalog raw values, then dispatches ExportProductsEvent; the Menzzo event provider queues ExportProducts on export-products. The UI's success toast is the API response; whether the job has completed or is still queued depends on the deployed queue driver, so follow its tracked job. The exporter serializes enabled status as 1 and disabled status as 2. A new product from the Logidav import is enabled by default, so this UI step is optional rather than a required publish action. Grid controls and save · Activé field and save action · Excel update endpoint and queue · status update and export event · event dispatch and listener · Menzzo export listener
Menzzo export and Magento feedback
The Menzzo module declares menzzo:products-export every minute. That command calls ExportProducts::dispatchSync(). Product updates can also dispatch the same job through ExportProductsEvent, and the Logidav product job explicitly queues it after intake. These are source-level triggers, not evidence that an application scheduler or queue worker is active. config/queue.php defaults to the sync driver unless QUEUE_CONNECTION overrides it, so a queue name alone does not prove asynchronous execution. Menzzo schedule and command · command calls the exporter synchronously · queue default
Modules/Menzzo/Jobs/ExportProducts exits without exporting if the Menzzo connector/resource is disabled or its last_sync cursor is empty. Otherwise it selects VersioningVersion rows newer than last_sync, advances the cursor to the newest selected version, and serializes the corresponding products. Products without a name are skipped. Each JSONL line carries SKU, status, family, categories, channels, associations, attributes, and image references; the FTP consumer receives a file at /meduse/connectors/menzzo/export/pending/{YYYYMMDDHHMMSS}_{0-to-1000}_export-products-{YYYYMMDDHHMMSS}.jsonl. The local staging name is storage/app/connectors/menzzo/export/pending/export-products-{YYYYMMDDHHMMSS}.jsonl when the default local disk is used. The tracked job is marked finished after the FTP put call; this is Meduse's handoff boundary, not Magento acceptance. Connector guard and version cursor · serialization · remote filename and upload
Magento reads the JSONL handoff and returns updated_<input>.csv in /meduse/connectors/menzzo/export/complete; the receiving-side importer and its file transition are covered in the Magento catalog import guide. Meduse declares meduse:sync-after-magento-update every minute. That command checks for the enabled Menzzo connector, reads completion files as CSV, consumes fields such as SKU, magento_id, feedId, availability, shipping amount, and product-type metadata, and rewrites the file with URL, image, and delivery-coefficient columns. The header uses str_getcsv, but data rows are split with explode(',', ...); a quoted comma in a value can shift subsequent columns and misread feedback. On success it moves the same basename to meduse/connectors/logidav/new-product/finished/{basename} relative to the configured FTP root and may call meduse:module:logidav:sync-product-to-logidav for products whose IDs changed. Kernel schedule · feedback parsing · rewrite, resource handling, and move
The feedback handler has two limits worth carrying into acceptance: its call to the helper that would populate connector_resource_product is commented out, and its resource-data lookup uses literal connector_id = 'product' while the create branch stores the numeric Menzzo connector ID. A successful file move therefore does not prove that Magento platform IDs were linked in that mapping table or that the connector resource row was updated as intended. An exception leaves the CSV in export/complete, where the next scheduled invocation can read it again. The Magento-side item-level result is described in the linked receiver guide.
Failure, retry, and duplicate boundaries
- Inbound lock and move: the generator skips an FTP basename when its local
.lockexists. No lock removal is present in the inspected intake path, so a later file reusing that basename can be skipped until something outside this flow clears the lock. Itsfile_exists($dist)check uses the local filesystem before the FTP move; if that local path exists, the command skips the remote move and still queues the job. If product processing throws, the FTP file is moved tonew-product/failed; the inspected schedule does not move these inbound failures back to pending. A queue retry may still replay the local processing copy, depending on the deployed queue configuration. - Partial intake: product/category/channel writes occur during the file loop, while the transaction start is commented. If a later product fails, earlier writes may remain even though the tracked job is failed and the FTP file is under
failed. Re-import by SKU reuses the row and rewrites supplied data; it is not a create-only operation. - Export cursor and serialization:
ExportProductsdeclares uniqueness for one hour, but itsuniqueId()returns a freshuniqid(), so separate dispatches do not share a stable product/cursor lock. The cursor is advanced before serialization. An exception in the inner JSONL loop marks the tracked job finished with the error text and returns before FTP upload; an exception reaching the outer catch marks it failed and restores the prior cursor. Do not usefinishedalone as proof that a JSONL file exists. Cursor, error handling, and unique ID · serialization error branch · outer error and unique ID · unique key - Magento processing retry:
menzzo:retry-failed-filesis declared hourly and moves files older than one hour from/meduse/connectors/menzzo/export/processingback topending. It only recycles stale processing files; the code does not move files fromfailed. If the Magento consumer processed a file but did not complete its state transition, this retry can present the same filename again. Retry schedule · processing-to-pending move - Feedback replay: if feedback processing throws, the CSV remains in
export/completefor another scheduled attempt. Before a successful move to Logidavfinished, the handler deletes an existing destination file with the same basename, so a repeated acknowledgement can replace the earlier finished file. The feedback path should be checked for its row-level result and destination state, not only the command's next invocation.
For the adjacent product form, batch-edit, and job-polling behavior, continue to product editing. The workflow map covers neighboring stock, price, and product-update feeds.
Evidence boundary
The Meduse and frontend claims above were checked against backend commit 5d3de6cb63b976aaeef739a8a7f288c4b46509ad and frontend commit ca4a9364974f25715715b002f1ee463c8d53d31f. The Magento consumer is linked separately. No Artisan command, queue worker, FTP connection, database, deployment, or storefront was inspected or run; this page describes code paths and configured schedule declarations, not live execution or product acceptance.