Ga naar inhoud

Voorraadimport

Atraxion betrekt banden, velgen en accessoires bij meerdere leveranciers. Elke leverancier levert periodiek een stockbestand (CSV via FTP of HTTP-download). De import-pipeline leest dat bestand, koppelt elke regel aan een intern artikel, berekent de verkoopprijs en schrijft het resultaat naar article_stock — de tabel die de webshop gebruikt voor voorraadweergave en bestelbaarheid.


Architectuur

flowchart TD
    A[Leverancier stockbestand\nCSV op FTP of HTTP-URL] -->|Download| B[_externestock/]
    B --> C[UpdatingSupplierStockController\n::importStock]
    C --> D{Guard-checks\nbestand aanwezig?\noud genoeg?\nProcessFile aan?}
    D -- Nee --> E[Stock op 0 zetten\nAlert mail logs@atraxion.com]
    D -- Ja --> F[Parse CSV\nAdminSupplierStockDAO\n::getInfoFromFile]
    F --> G[Batch upsert\nsupplier_stock]
    G --> H[Artikel-koppeling\nAdminLinkSupplierDAO]
    H --> I[Marge/prijs berekening\nAdminMarginDAO]
    I --> J[Batch upsert\narticle_stock]
    J --> K[Validatie-flag\nprijswijziging > drempel?]
    K -- Ja --> L[Validated=0\nReview-wachtrij]
    K -- Nee --> M[Validated=1\nOnmiddellijk zichtbaar]

Hoofdscripts:

Script Doel
_shopupdating/index_checkAllLevs.php Alias → checkAllSupplierStock.php
_shopupdating/cronjobs/atx/suppliers/stock/checkAllSupplierStock.php Alle actieve leveranciers verwerken
_shopupdating/cronjobs/atx/suppliers/stock/checkSupplierStock.php Één leverancier ($argv[1] = Levnr, $argv[2] = updateAnyway)
_shopupdating/index_checkLevFile.php Alias → checkSupplierStock.php
_shopupdating/cronjobs/atx/articles/stock/export.php Verwerkte stock exporteren naar channels/platformen

Stap 1 — Instellingen laden

UpdatingSupplierStockController::importStock leest supplier_import_settings (joined aan supplier) via AdminSupplierImportSettingsDAO::getImportSettings().

Belangrijkste instellingen per leverancier:

Instelling Betekenis
ProcessFile Schakelaar — als false: bestaande stock verwijderen en alert sturen
File Pad relatief aan _externestock/
SourceFile / SourcePriceFile Externe URL om het bestand van te downloaden
MaxLifeTime Maximale bestandsleeftijd in uren (productie only)
Encoding Bestandscodering (standaard ISO-8859-1)
Delimiter / Enclosure / Escape CSV-parseerinstellingen
CountColumnHeadings Aantal headerrijen om over te slaan
Lev_fabpos / Lev_fabpos2 CSV-kolom voor fabrikantcode (primair/secundair)
Lev_eanpos / Lev_eanpos2 CSV-kolom voor EAN (primair/secundair)
Lev_tyre24pos CSV-kolom voor Tyre24-identificator
Lev_artnrpos CSV-kolom voor eigen artikelnummer van de leverancier
Prijspos / Stockpos CSV-kolom voor inkoopprijs respectievelijk voorraadaantal
CheckSizes Valideer maten bij Fabcode-koppeling
ProductGroups JSON-array om te beperken tot bepaalde productgroepen
odooEdiRelay Odoo EDI-relay-vlag

Stap 2 — Bestand downloaden

DownloadRemoteStockFiles::downloadFiles() (_core/model/classes/atx/suppliers/stock/DownloadRemoteStockFiles.php) controleert SourceFile/SourcePriceFile. Als ingesteld: downloaden via file_get_contents (HTTP, time-out 10 s) naar _externestock/<File>.


Stap 3 — Guard-controles

Vóór verwerking worden vier controles uitgevoerd. Bij mislukking: stock op 0, alert e-mail naar logs@atraxion.com:

Conditie Gevolg
ProcessFile = false Stock verwijderen, alert
Bestand bestaat niet op APP_EXTERNESTOCK_PATH/<File> Stock verwijderen, alert
Bestandsleeftijd > MaxLifeTime uur (prod. only) Stock verwijderen, alert
Bestand-mtimesupplier.StockUpdated én geen updateAnyway Stilzwijgend overslaan

Stap 4 — CSV parsen

AdminSupplierStockDAO::getInfoFromFile (_admin/model/dbclasses/atx/suppliers/stock/AdminSupplierStockDAO.php)

Bestand openen met fgetcsv. Codering naar UTF-8 omzetten indien nodig.

Als PriceFile is ingesteld, wordt dit bestand als eerste geparsed in drie opzoektabellen (EAN / Fabcode / leverancierartikelnummer → prijs).

Per CSV-rij maakt newObject() een SupplierStock-object aan met:

  • Levartnr (extern ID): leverancierartikelnummer → EAN → Fabcode → Tyre24Ident (volgorde)
  • Eancode uit Lev_eanpos
  • Fabcode uit Lev_fabpos (of Lev_fabpos2)
  • Tyre24Ident uit Lev_tyre24pos
  • Akprijs uit Prijspos (overschreven door prijsbestand indien aanwezig)
  • Stock uit Stockpos (tekens <, >, + en spaties worden verwijderd)
  • Maatgegevens uit hun respectieve kolomposities
  • Demo-vlag uit Demopos (positieve waarden: y, j, yes, ja, x, 1)

Leveranciersspecifieke uitzonderingen (hardgecodeerd op Levnr in fillSizes()):

Levnr Uitzondering
3 (TD) Bandenmaat parsen uit omschrijvingsstring
6 x/bla opschonen in gaataantal
7 (DELDO) Omschrijving opbouwen uit meerdere kolommen
11 PCD-formaat splitsen (5x130+5x112)
12 Omschrijving verdubbelen
14 / verwijderen uit serie-veld

Stap 5 — Upsert in supplier_stock

AdminSupplierStockDAO::addMultipleRecords — enkele batch INSERT IGNORE … ON DUPLICATE KEY UPDATE. Na de insert zet setStockToZero() alle rijen met importVersion < huidig op Stock = 0, IsDeleted = 1 (rijen die in het huidige bestand niet voorkwamen).


Stap 6 — Artikel-koppeling

AdminLinkSupplierDAO (_admin/model/dbclasses/atx/suppliers/stock/AdminLinkSupplierDAO.php)

Bestaande koppelingen worden eerst gewist: DELETE FROM supplier_link WHERE Levnr = :Levnr.

Koppeling gebeurt via INSERT IGNORE in de volgende prioriteitsvolgorde (de eerste match wint):

Prioriteit Match-type Veld GekoppeldOp
1 Fabcode2 article.Fabcode2 = supplier_stock.Fabcode 'f'
2 Fabcode article.Fabcode = supplier_stock.Fabcode 'f'
3 EAN2 article.Eancode2 = supplier_stock.Eancode 'e'
4 EAN article.Eancode = supplier_stock.Eancode 'e'
5 Tyre24Ident article_tyre.Tyre24Ident = supplier_stock.Tyre24Ident 't'

Bij Fabcode-koppeling worden maten gevalideerd als CheckSizes = 1:

  • Banden: Breedte, Serie en Inch moeten overeenkomen.
  • Velgen: Velgbreedte, Velginch, Gaten, DiamGaten, Et en CTR moeten overeenkomen.

Alle koppelvragen bevatten de filter: article.Active = TRUE, article.CanBeSold = TRUE, article.Dot IS NULL, supplier_stock.IsDeleted = 0.

Na koppeling ruimt deleteRemovedStockLinks verouderde supplier_link-rijen op.


Stap 7 — Marge- en prijsberekening

AdminLinkSupplierDAO::getExtArticlesWithStock haalt gekoppelde artikelen op met Stock >= MinimumStock (uit supplier.MinimumStock). Per artikel:

  • netMargins uit article_stock_margin WHERE Marge > 0 OR Nettoprijs > 0
  • tarifMargins uit article_stock_margin WHERE VasteKorting > 0
  • Netprijs berekend als:
  • Inkoopprijs > 0 + nettomarge gevonden: Akprijs × marge + cte
  • Anders tariefmarge: (StandardPrice − StandardPrice × VasteKorting%) + cte
  • Speciale regel: als Akprijs = 0 én Prijspos > 0, wordt de voorraad geforceerd op 0 (Latexhurt-workaround).
  • Gewichtsgebaseerde toeslagen worden op prijs opgeteld als er overeenkomende WeightBasedPrice-regels bestaan.

Stap 8 — Upsert in article_stock

AdminStockDAO::addMultipleRecords (_admin/model/dbclasses/atx/articles/stock/AdminStockDAO.php)

Batch INSERT … ON DUPLICATE KEY UPDATE naar article_stock:

  • Velden: ArtnrLevnr, Artnr, Levnr, HasStock, Stock, Akprijs, PurchasePrice, Netprijs, Updated, Validated, LastValidatedPrice.

Prijsvalidatiemechanisme:

Het veld Validated bepaalt of een artikel direct zichtbaar is of in de review-wachtrij belandt:

  • ATX_PERCENTAGE_CHANGE(nieuwe_prijs, LastValidatedPrice) <= AllowedPercentageChangeValidated = 1 (direct zichtbaar).
  • Prijssprong te groot → Validated = 0 → artikel belandt in de review-wachtrij; een medewerker beoordeelt de prijs manueel.

Auto-validatie cross-check: na de upsert valideert een tweede query ongevalideerde rijen waarbij een gevalideerde concurrent een prijs heeft binnen −15%.

setStockToZero zet article_stock-rijen op nul die hun koppeling verloren of onder MinimumStock zijn gezakt.


Stap 9 — Tijdstempel bijwerken

AdminSupplierDAO::saveFileTime($supplierNumber, $dtFile) schrijft de mtime van het bestand naar supplier.StockUpdated.


Datamodel

Tabel Inhoud
supplier Leverancier-stamdata: Levnr, Levnaam, MinimumStock, MaxAllowedPriceDifference, StockUpdated, importVersion
supplier_import_settings Import-instellingen per leverancier: bestandspaden, CSV-kolomposities, drempelwaarden
supplier_stock Ruwe importregels per leverancier (staging)
supplier_link Koppeling supplier_stock.Levartnrarticle.Artnr (veld GekoppeldOp: f/e/t)
article_stock Definitieve voorraad + prijs per artikel per leverancier (gebruikt door de webshop)
article_stock_margin Margeregels per leverancier, categorie, merk, type en maat
article_stock_history Historische snapshots per leverancier en artikelnummer

Stockbestanden staan op schijf in _externestock/ (APP_EXTERNESTOCK_PATH).


Belangrijkste klassen

Klasse Locatie
UpdatingSupplierStockController _admin/controller/classes/atx/suppliers/stock/
UpdatingSupplierStockWorker _admin/controller/classes/atx/suppliers/stock/
AdminSupplierImportSettingsDAO _admin/model/dbclasses/atx/suppliers/stock/
AdminSupplierStockDAO _admin/model/dbclasses/atx/suppliers/stock/
AdminLinkSupplierDAO _admin/model/dbclasses/atx/suppliers/stock/
AdminStockDAO _admin/model/dbclasses/atx/articles/stock/
AdminMarginDAO _admin/model/dbclasses/atx/articles/stock/
SupplierImportSettings _core/model/classes/atx/suppliers/stock/SupplierImportSettings.php
SupplierStock _core/model/classes/atx/suppliers/stock/SupplierStock.php
DownloadRemoteStockFiles _core/model/classes/atx/suppliers/stock/DownloadRemoteStockFiles.php

  • Externe platformen — verwerkte stock wordt daarna geëxporteerd naar platform-channels
  • Productcatalogusarticle_stock bepaalt wat zichtbaar en bestelbaar is in de catalogus
  • Crons & queue — cron-frequentie en queue-verwerking voor importscripts
  • Admin & backoffice — validatie-review en prijsbeheer in AdminSupplierController en AdminStockController