✦ Magic Hub
Přihlásit
Changelog historie všech změn v backendu

Seznam je generovaný z git commit historie celého repozitáře — pokrývá všechny moduly (search engine, produkty, překlady, billing, OAuth, platform admin, …). Nejnovější změny nahoře.

  1. 2026-10-30 e74defb0
    fix(alza): seed z vyplněných tabulek ignoruje vzorce a popisné řádky
    Claude-Session: https://claude.ai/code/session_01XkTx2yUrgZDaZiEF5BiexJ
  2. 2026-10-30 43b97c2a
    feat(alza): mapa značka→výrobce, ProductNumber=EAN, váha zásilek v kg, seed z vyplněných tabulek
    Claude-Session: https://claude.ai/code/session_01XkTx2yUrgZDaZiEF5BiexJ
  3. 2026-10-30 a30d1288
    fix(alza): váha g→kg, NameExt z krátkého popisu, Upgates parametry v automapu
    Claude-Session: https://claude.ai/code/session_01XkTx2yUrgZDaZiEF5BiexJ
  4. 2026-10-28 4e102454
    feat(addons): Marketplace pro vybraný e-shop, výběr sdílený se sidebarem
    - session pamatuje vybraný e-shop (sidebar „Tvoje shopy“ / záložky),
      jen pro UI — gating dál podle URL; tenant switch výběr maže
    - Marketplace s víc e-shopy: záložky e-shopů + „Všechny e-shopy“
      místo malého selectu, otevře naposledy vybraný e-shop
    - per-e-shop stránka: neveřejné doplňky jako v Marketplace
    
    Claude-Session: https://claude.ai/code/session_01GJSYrHMVJNiyho5Aj86CYN
  5. 2026-10-26 44818be0
    docs: roadmapa — nasazení rozdělené fakturace (storno, vystavit hned)
    Claude-Session: https://claude.ai/code/session_01GJSYrHMVJNiyho5Aj86CYN
  6. 2026-10-26 ef7b4c54
    feat(split_invoicing): trasy podle zdroje, vystavení hned, storno → dobropis
    - CustomerGroupRoute.key_source: trasy ceníků a skupin uložené zvlášť,
      platí jen trasy aktivního zdroje (migrace převezme aktuální zdroj)
    - InvoicingCompany.issue_unpaid (+ issue_unpaid_since): firma vystavuje
      hned po přijetí objednávky (velkoobchod), jen objednávky od zapnutí;
      úhrada se do Fakturoidu zapíše později, až je objednávka zaplacená
    - Storno objednávky ve Shoptetu → opravný daňový doklad ve Fakturoidu
      (document_type=correction, záporná množství), job „Stornováno“;
      idempotence přes custom_id -storno a vazbu na původní faktuře,
      kontrola typu vzniklého dokladu, backoff, chyby bez těla odpovědi
    - Faktury vystavené před nasazením se automaticky nedobropisují
    
    Claude-Session: https://claude.ai/code/session_01GJSYrHMVJNiyho5Aj86CYN
  7. 2026-10-26 273e2524
    fix(deploy): crontab ukládat do /srv/conf/crontab, ať přežije restart
    Roští /start.sh při startu kontejneru přepíše živý crontab souborem
    /srv/conf/crontab, kde byl jen PATH — restart smazal všechny úlohy.
    
    Claude-Session: https://claude.ai/code/session_013JsY4sBBrS7RzZU2KB2ddw
  8. 2026-10-26 9da62625
    fix(deploy): ručně běžící crony do crontab.managed.txt
    Restart kontejneru 26. 10. smazal crontab; kurzy, rezervace, stale
    překlady a TopTrans sync v managed souboru chyběly, deploy by je neobnovil.
    
    Claude-Session: https://claude.ai/code/session_013JsY4sBBrS7RzZU2KB2ddw
  9. 2026-10-23 9f260371
    docs: roadmapa — živý end-to-end test Alza → Upgates → Alza
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  10. 2026-10-23 72b319a5
    fix(marketplaces): objednávka do Upgates — název položky a fakturační firma
    Živý test (objednávka 62218754): bez products[].title Upgates objednávku
    odmítne (error product.title); firmu/IČO/DIČ uloží jen s company_yn
    (+ vat_payer_yn pro DIČ), jinak je tiše zahodí.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  11. 2026-10-22 5c5733b3
    docs: roadmapa — scénáře napojení e-shopu a živé ověření
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  12. 2026-10-22 3aabdf0e
    fix(marketplaces): kontrola napojení e-shopu hlásí chybějící stav pro storno z tržiště
    Bez cancel_status_id se storno z Alzy po založení objednávky v Upgates odmítne;
    "Načíst z Upgates" ho nově předvyplní stavem typu Canceled.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  13. 2026-10-22 871126ea
    fix(marketplaces): testovací napojení bez uložené volby nezakládá objednávky v e-shopu
    Výchozí hodnota create_shoptet_orders byla True pro všechna napojení —
    testovací napojení bez uloženého klíče by zakládalo objednávky v ostrém
    e-shopu. Nově MarketplaceConnection.creates_eshop_orders(): produkční ano,
    testovací ne. + 30 scénářových testů celého řetězce Alza → Upgates/Shoptet
    → zpět na Alzu (založení, duplicity, 429/422, storno z obou stran, webhook,
    synchronizace stavů, ruční režim, Shoptet API).
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  14. 2026-10-22 84c8f941
    fix(marketplaces): Upgates webhook znovu csrf_exempt + jen POST
    Předchozí commit vložil pomocnou funkci mezi dekorátory a webhook view —
    dekorátory se přesunuly na helper a webhook zůstal bez csrf_exempt.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  15. 2026-10-22 a8b6c3d4
    feat(marketplaces): eviduj příchod Upgates webhooku i při vypnutém napojení
    Bez toho nejde poznat, jestli Upgates webhook vůbec volá, dokud není zakládání
    objednávek zapnuté. Zápis nejvýš 1× za minutu, objednávky se nezpracují.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  16. 2026-10-22 0f667039
    feat(marketplaces): Napojení e-shopu — platforma nahoře, pod ní API vybrané platformy
    Výběr platformy je první karta (uloží se hned po změně). Pod ní se ukáže API
    jen vybrané platformy: Upgates (URL, login, klíč) nebo Shoptet (adresa
    e-shopu + API token do tenantového ShoptetConnection). Shoptet doprava,
    platba a stavy se nastavují tady (EshopConnection.settings); dřívější hodnoty
    v napojení tržiště zůstávají jako fallback, formulář tržiště je už nenabízí.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  17. 2026-10-22 13be11b0
    fix(warehouse_portal): detail objednávky s výdejním místem padal (500)
    Šablona brala parcel_shop.name/id jako argument filtru default — Alza posílá
    parcelShopIdentification + parcelShopBranchCode, chybějící klíč v argumentu
    filtru vyhodí VariableDoesNotExist.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  18. 2026-10-22 1fa6f901
    fix(warehouse_portal): login zaměstnance bez prohlížečového patternu
    Pole loginu mělo HTML pattern jen pro malá písmena — prohlížeč hlásil jen
    "Match the requested format". Pravidla hlídá server (velká písmena převede,
    jinak srozumitelná hláška); u pole je nápověda.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  19. 2026-10-22 9f448a76
    docs: skladový portál nasazen a zapnutý pro Rumako
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  20. 2026-10-22 519c5403
    feat(warehouse_portal): skladový portál pro zaměstnance skladu
    Nový doplněk `warehouse_portal` (Provoz, beta, admin_only, neveřejný, scope
    tenant). Portál běží na app.eshopmagic.cz/<slug>/ (konvertor sedí jen na
    existující portál), správa majitelem na /sklad-portal/.
    
    - Účty zaměstnanců: vždy nový User bez členství v tenantu, login v rámci
      portálu, validátory hesel, výběr e-shopů, oprávnění zásilka/expedice,
      vypnutí a smazání.
    - PortalStaffMiddleware drží účet skladu jen v jeho portálu (i po přihlášení
      přes allauth; `/` ani `/start/` je nepustí do administrace).
    - Portál: objednávky z tržišť (K vyřízení / Odeslané / Vše, hledání), detail
      s položkami a adresou, Vytvořit zásilku → štítky PDF, Expedovat; TEST odznak
      pro alza_test; zámek přihlášení po 10 neúspěšných pokusech.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  21. 2026-10-22 8b05490a
    feat(marketplaces): Upgates API přihlášení přímo v záložce Napojení e-shopu
    API URL, login a klíč (prázdný = beze změny) se dají vyplnit v Napojení
    e-shopu; ukládá se do stejného místa jako nastavení importu
    (source_feed_config['upgates_api']), jazyk cen se nemění. Dřív to šlo jen
    v nastavení importu a pole byla vidět jen při primárním zdroji Upgates.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  22. 2026-10-22 d923225f
    fix(marketplaces): u testovacích objednávek upozornění na ukázkový odkaz štítku
    Testing API Alzy vrací v Create Shipment ukázkovou pdf URL (stejný x= token
    jako příklad v dokumentaci, doména test.alza.cz se nepřeloží) — štítek se
    v testu negeneruje.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  23. 2026-10-22 51a9773a
    feat(marketplaces): kompletní testovací objednávka Alza (doprava, adresa, štítek)
    Tlačítko Testovací objednávka umí zvolit scénář Alzy s dopravou (Zásilkovna,
    dobírka, TOPTRANS na adresu / pobočka Alza, DPD SK, 2 balíky) a projde ho
    na pozadí: dostupnost → Order Insert → Order Confirm (doručovací adresa,
    dopravce) → Create Shipment (štítek PDF), volitelně i Departure. Průběh
    kroků a odkaz na vzniklou objednávku jsou v kartě.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  24. 2026-10-22 d794b21f
    fix(marketplaces): samostatný cron marketplaces_eshop_sync pro webhooky e-shopu
    Deploy doplňuje crontab podle názvu příkazu — řádek
    marketplaces_sync_shoptet_status --only-requested se proto nikdy
    nenainstaloval (příkaz už v crontabu byl). Zpracování objednávek ohlášených
    webhookem má vlastní příkaz (po 5 min, gatuje is_active_for_shop).
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  25. 2026-10-22 0acc3e29
    feat(marketplaces): Napojení e-shopu — ruční režim Shoptet bez API
    Platforma „Shoptet bez API“: objednávka z tržiště se po potvrzení neposílá
    přes API, v detailu je pole pro číslo objednávky, kterou obsluha do e-shopu
    přepsala (drží i rezervace), s upozorněním na ruční přepis a storno. Stavy na
    tržiště se potvrzují stávajícími tlačítky. Záložka u Shoptet API odkazuje na
    nastavení dopravy/platby/stavů v napojení tržiště.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  26. 2026-10-22 5c4dcbe9
    feat(marketplaces): Napojení e-shopu — krok 3 (webhook Upgates, stav zpět na Alzu)
    Webhook /marketplaces/api/eshop/<token>/upgates (Upgates Orders.update, bez
    podpisu → tajný token v URL) jen označí naše objednávky
    (eshop_sync_requested_at) a vrátí 204; neaktivní doplněk/napojení → 204.
    marketplaces_sync_shoptet_status pro Upgates cíl načte stav dávkou
    GET /orders?order_numbers= a promítne: odesláno → Shipment departure
    s trackingem, doručeno → Delivery result Delivered, storno → Canceled
    (akce hlídá stav objednávky — opakovaný webhook nic nezdvojí).
    Cron --only-requested po 5 min (webhooky), plný polling po 15 min.
    Záložka: adresa webhooku + registrace v Upgates (POST /webhooks).
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  27. 2026-10-22 4c4a5713
    feat(marketplaces): Napojení e-shopu — krok 2 (objednávky z tržišť do Upgates)
    Po Order Confirm se objednávka z tržiště založí v Upgates (POST /api/v2/orders),
    když je napojení e-shopu Upgates zapnuté (výchozí vypnuto) a tržiště má
    „Zakládat objednávky v e-shopu“. Před POST hledá objednávku podle
    external_order_number (žádné duplicity), send_emails/sms vždy false,
    odmítnutí Upgates (created_yn false) = trvalá chyba s hláškou u objednávky.
    Storno z tržiště nastaví v Upgates zvolený stav (PUT). Fakturačně Alza,
    doručení zákazník (*_postal). Číslo objednávky a čas jdou do shoptet_order_*
    (obecná „objednávka v e-shopu“, drží rezervace). Shopy bez napojení e-shopu
    beze změny (Shoptet API). Záložka má vypínač a náhled požadavku pro libovolnou
    objednávku — nic neodesílá.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  28. 2026-10-22 3465d8c1
    feat(marketplaces): Napojení e-shopu — krok 1 (nastavení a čtení z Upgates)
    Nový model EshopConnection (jedno napojení e-shopu na shop, sdílené tržišti)
    a samostatná záložka „Napojení e-shopu“ vedle „Napojení Alza Trade“:
    volba platformy (Upgates API / Shoptet API / Shoptet bez API), doprava,
    platba a jazyk objednávky, načtení stavů/doprav/plateb/webhooků z Upgates
    API (na vyžádání, cache 1 h — sdílený rate limit) s předvyplněním mapování
    stavů (Sent → odesláno, mark_delivered_yn → doručeno, Canceled → storno)
    a kontrolou nastavení. Zatím nic do e-shopu nezapisuje.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  29. 2026-10-22 0302a093
    feat(marketplaces): posledních 10 objednávek na stránce tržišť e-shopu
    /trziste/<shop>/ začíná kartou Objednávky (posledních 10 napříč napojeními:
    číslo, tržiště, stav, položky, rezervace do, vytvořeno) s odkazem na všechny.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  30. 2026-10-22 32c684d5
    feat(marketplaces): posledních 10 objednávek nahoře na stránce napojení; JSON v logu sbalený
    Hlavní stránka napojení (Alza) začíná kartou Objednávky — posledních 10
    (číslo, stav, položky, rezervace do, vytvořeno) s odkazem na všechny, nad
    kontrolami a nastavením. Badge stavu sdílený partial _order_state.html.
    V popupu logu jsou požadavek a odpověď sbalené (details).
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  31. 2026-10-22 e1e1cc21
    feat(marketplaces): testovací Alza bez automatické dostupnosti, přepínač na záložce Test
    Cron (Update i noční PartialFull) testovací napojení přeskakuje — dostupnost
    si posílají scénáře a Testovací objednávka jen pro vybrané produkty.
    Nastavení auto_availability (výchozí: test vypnuto, produkce zapnuto) se
    přepíná na záložce Test.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  32. 2026-10-22 d595b9dc
    feat(marketplaces): detail záznamu logu v popupu
    Seznam změněných produktů a celý požadavek/odpověď (JSON) se otevírá
    v modálním okně (.modal-backdrop ze shell.css) místo rozbalování v tabulce.
    Zavírání křížkem, klikem mimo a Escape.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  33. 2026-10-22 7e64ffe4
    docs: confirm tenant switch landing deployment
  34. 2026-10-22 268192c0
    fix(tenants): land on dashboard after tenant and user switches
  35. 2026-10-22 6edecc6b
    docs: confirm invoice and settings tabs deployment
  36. 2026-10-22 3a6ca604
    feat(split_invoicing): invoices first with separate settings tab
  37. 2026-10-22 23734ff0
    docs: confirm Shoptet admin bar snippets deployment
  38. 2026-10-22 4986c0cf
    feat(marketplaces): log Alza aktualizace dostupnosti se seznamem změněných produktů
    Update zpráva ukládá do logu všechny změněné produkty (kód, název, sklad
    a cena bez DPH před → po, „nový“ u prvního odeslání); stránka Log je
    ukazuje v rozbalovací tabulce u záznamu.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  39. 2026-10-22 a2ba8840
    feat(addons): embeddable Shoptet admin bar links for each addon
  40. 2026-10-22 0801dd6a
    feat(marketplaces): tlačítko Vytvořit testovací objednávku (Alza test)
    Karta na záložce Test: výběr skutečného produktu skladem (našeptávač s cenou
    bez DPH a skladem) → testovací Alze se pošle jeho dostupnost a Order Insert
    scénáře T009 na 1 ks. Alza objednávku doručí na testovací Supplier API,
    v e-shopu se nezaloží (create_shoptet_orders u testu vypnuto).
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  41. 2026-10-22 bd058c4d
    fix(status): judge Stripe gateway by client connections, not global key
    The payment_gateway module warned 'Stripe klíč nenastaven' because it
    checked the global platform key, which is intentionally unset — the
    gateway runs on per-workspace StripeConnection keys. Evidence now checks
    connections of workspaces with the active addon; the global Stripe probe
    reports 'none' (untracked) when no global key is configured.
    
    Claude-Session: https://claude.ai/code/session_017Zm4Vmyvn2oAdQwf29wMrf
  42. 2026-10-22 e7953d84
    docs: confirm client addon links deployment
  43. 2026-10-22 f7771b2d
    feat(addons): copyable client links for each active shop addon
  44. 2026-10-22 baafce81
    fix(search_engine): status ignores stale indexes of inactive shops, install AI crons
    Evidence counted SearchIndex errors on shops without the addon (old
    Meilisearch embedders errors, never synced again) as an outage.
    discover_synonyms / enrich_descriptions were only in the reference
    crontab.txt, so they never ran; deploy now installs them (plus
    recompute_popularity) from crontab.managed.txt.
    
    Claude-Session: https://claude.ai/code/session_017Zm4Vmyvn2oAdQwf29wMrf
  45. 2026-10-22 b70d0fa0
    docs: confirm email sender switch deployment
  46. 2026-10-22 8c053a6f
    feat(split_invoicing): explicit Fakturoid or backend email sender switch
  47. 2026-10-22 75bf11a1
    docs: confirm deployed invoice payment and live verification
  48. 2026-10-22 5af4b9eb
    docs: record invoice payment validation
  49. 2026-10-22 025d5e1a
    fix(split_invoicing): register order payment before sending invoice
  50. 2026-10-21 0a470750
    fix(marketplaces): Alza stav dostupnosti — kód bez ohledu na velikost písmen
    Po srovnání kódů s Upgates (CUn184070 → CUN184070) padalo mark_sent na
    IntegrityError 1062 (unikát v MySQL je _ci, Python klíč case-sensitive),
    celá zpráva PartialFull se vrátila a nikdy neodešla. compute_delta a
    mark_sent teď párují kód přes lower(); změna velikosti = aktualizace
    stavu (včetně product_code), ne „zmizel + nový“.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  51. 2026-10-21 9dfd36cc
    fix(marketplaces): Alza dostupnost bez duplicitního EAN ve zprávě, název max 100
    Od 20. 10. 20:25 padaly všechny zprávy /api/v1/availability na HTTP 500
    „Internal API Server error“. Update obsahoval po srovnání kódů s Upgates
    stažení starého kódu (qty 0) i nový kód se stejným EAN (a sady se sdíleným
    EAN), PartialFull název 106 znaků. Ověřeno živě: jednotlivé řádky (starý
    i přejmenovaný kód) Alza přijme.
    
    Odeslání teď dělí zprávu na skupiny bez opakovaného EAN — nuly první, ať
    Alza starý kód stáhne dřív, než dostane sklad nový — a ořízne název na 100.
    Platí i pro zprávy, které už čekají ve frontě.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  52. 2026-10-20 3e8369d4
    fix(import): výhradní zdroj importu zapisuje ceny; CZK ve výpisu bez .00
    Produkty z Upgates neměly Product.price (mapovaný import ceny blokuje kvůli
    souboji se Shoptet importem) — výpis produktů ukazoval „—“ (Rumako:
    UNHXOCAPR a další nové sady), starší měly cenu ze Shoptetu. Když je zdroj
    v nastavení importu jediný (Shop.import_source), nemá s kým soupeřit a ceny
    zapisuje. Ostatní mapované zdroje dál blokované. _HASH_VER 5.
    
    Dry-run na kopii Rumako: cenu má 1572/1572 (dřív 0 u nových), 248 cen
    srovnáno se Upgates. Mironet/Alza berou ceníky, ne Product.price.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  53. 2026-10-20 9fa8aca5
    fix(upgates): ceny produktů bez nastavené jazykové mutace z výchozího jazyka
    Price sync posílal do /products/prices filtr jazyka (Rumako „bi“), takže 303
    produktů bez mutace „bi“ nedostalo žádné ceníky a Alza (ceník ALZA) brala
    starou webovou cenu ze Shoptetu nebo žádnou. API se teď volá bez filtru,
    jazyk vybírá kód: nastavený jazyk, a když ho produkt nemá, výchozí jazyk
    shopu. Doplněné řádky nesou language=cs, takže exporty filtrované přes
    filter_prices_by_language (B2B „bi“) se nemění. ALZA ceny bi vs cs: shoda
    1725/1726.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  54. 2026-10-20 65ae8ba5
    fix(import): Upgates jako zdroj pravdy — párování podle SKU, kód a EAN podle zdroje
    Diagnostika Rumako proti živému Upgates feedu (1572 položek):
    - EAN fallback přebíral produkty, které mají ve feedu vlastní řádek: sady se
      stejným EAN jako lahev (72 skupin) přepisovaly lahev, 11+ sad se nikdy
      nezaložilo, ~40 názvů se každý běh přehazovalo.
    - 58 produktů mělo starý kód — v Upgates změněný, import ho nepřepisoval.
    - EAN smazaný v Upgates zůstával (prázdná hodnota se přeskočila).
    - Nově založený produkt přišel o sdílený EAN v úklidu po běhu.
    
    Teď: kód → PRODUCT_ID (import_code) → EAN jen na produkt bez vlastního
    řádku ve feedu a se stejným/neznámým PRODUCT_ID; jeden produkt = jedna
    položka feedu; primární zdroj přepisuje kód a maže smazaný EAN.
    _HASH_VER 4 → jednorázový plný reimport.
    
    Dry-run na kopii dat Rumako: 1572/1572 živých podle feedu, 0 rozdílů
    v názvech/EAN, 59 přejmenování, 22 nových sad.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  55. 2026-10-20 c4183176
    fix(import): prázdný feed nikdy nemaže katalog; fronta umí Upgates zdroje
    Incident Rumako 20. 10. 2026 13:54: „Importovat teď“ u Upgates zdroje šlo
    přes frontu (run_feed_import) do Shoptet importéru, ten ve feedu nenašel
    nic a před hláškou „0 položek“ označil všech 1549 produktů jako smazané
    ve zdroji (obnoveno ručně).
    
    - run_feed_import: Upgates/generic zdroje jdou přes import_source_feed /
      import_with_mapping (znovupoužije řádek z fronty).
    - Shoptet, generic i crawl importér: běh bez jediné viděné položky
      deleted_in_source nespouští.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  56. 2026-10-20 ea962bda
    feat(import): explicitní zdroj importu v nastavení importu
    Shop.import_source (FK na SourceFeed, prázdné = dosavadní chování). Když je
    nastavený, produkty plní JEN ten zdroj: plánovač ostatní zdroje přeskočí,
    import_source_feed / enqueue_feed_import / worker / CLI import_feed / MCP
    je odmítnou, ruční import z URL a souboru se vypne. UI ukazuje „Zdroj
    importu“ / „Import zakázán“ a skryje zakázaná tlačítka.
    
    Motivace: Rumako importuje z Upgates, ale plánovač každou hodinu tahal
    i pozůstatek Shoptet feedu a míchal data do katalogu.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  57. 2026-10-20 f07a4834
    fix(import): hlavní kategorie z Upgates PRIMARY_YN
    Generický import bral z Upgates jen seznam CATEGORIES, příznak hlavní
    kategorie ignoroval — default_category zůstávala ze starého Shoptet
    importu (Rumako: 958 produktů) nebo prázdná (591), takže tržiště
    nenašla kategorii (např. JRO044370 v Giny). Když mapping nemá
    default_category zvlášť, odvodí se z PRIMARY_YN a synchronizuje
    s kategoriemi.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  58. 2026-10-20 6b52dfc2
    fix(marketplaces): kategorie tržiště i u produktů bez hlavní kategorie
    Shoptet u části produktů neposílá DEFAULT_CATEGORY (Rumako: 591 z 1549),
    mapování se ale hledalo jen podle ní — produkty v namapovaných kategoriích
    (např. JRO044370 v Giny) končily s „Chybí Alza kategorie“. Teď se bere
    hlavní kategorie, a když chybí, první namapovaná z Product.categories.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  59. 2026-10-20 583b8615
    fix(marketplaces): odkaz na Náhled feedu i ze stránky Log
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  60. 2026-10-20 52ba9e6a
    feat(marketplaces): záložka Náhled feedu
    Ukazuje přesně to, co feed tržiště posílá (rozparsované vyrenderované XML):
    kód, název, cena (s/bez DPH podle formátu), kategorie Alzy i e-shopu,
    ostatní elementy v rozbalovacím detailu. Název vede na editaci produktu.
    Přepínač Ve feedu / Vyřazené (s důvodem z validace), hledání, stránkování,
    cache 5 min s tlačítkem Obnovit.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  61. 2026-10-20 7c32a2ed
    feat(marketplaces): Alza NameExt (PARAMETER_0003) z názvu produktu
    Povinný NameExt ze zalistovacích šablon nešel nijak vyplnit (Rumako: 303
    rumů „bez hodnoty“). Listing feed ho teď skládá z názvu produktu bez SEO
    prefixu a výrobce na začátku; ruční hodnota z mapování má přednost.
    Diagnostika ho už nehlásí jako chybějící, nové šablony ho nedávají do
    zdrojů parametrů.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  62. 2026-10-20 f655a012
    fix(marketplaces): Alza /listings/templates bere catId, ne categoryIds
    Živě vracelo 400 „catId: Kolekce nemůže být prázdná“. Ověřeno proti
    portalapi.alza.cz s catId (jedno i víc ID). Dávka sjednocena na 10.
    
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  63. 2026-10-20 a33a5aa6
    fix(revolut_gateway): výpadek Revolutu nezakládá duplicitní platby
    Osiřelá platba bez Revolut objednávky (selhaný create_order) se při
    dalším pokusu znovu použije (attempts/last_error) místo nového řádku.
    Před opakovaným založením dohledání GET /api/orders podle reference
    a metadata.payment_ref — po timeoutu se převezme existující objednávka.
    Neověřitelný stav = 502 bez zakládání. Cron osiřelé po 23 h failed.
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  64. 2026-10-20 5fda706c
    fix(marketplaces): Alza Supplier ID je číslo, textové Client ID patří do portálového API
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  65. 2026-10-20 cf8e83fc
    fix(marketplaces): srozumitelné popisky Alza ID (Supplier ID = Client ID Dropshipment API)
    Claude-Session: https://claude.ai/code/session_018jQeSQoCLp3JkcYt86ftWd
  66. 2026-10-20 2b509eda
    feat(split_invoicing): náhled faktury, ruční vystavení, stránka e-mailu firmy
    - náhled objednávky (i historické) bez zápisu a bez volání Fakturoidu:
      směrování, odběratel, položky, DPH, součty, výsledek kontrol
    - ruční vystavení s potvrzením; e-mail nikomu / jen mně (test) / zákazníkovi,
      po testu tlačítko Poslat zákazníkovi
    - stránka E-mail firmy: náhled textu, příloha, test s PDF poslední faktury
    - příjemce vždy zákazník z objednávky, v nastavení žádná volba příjemce
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  67. 2026-10-20 8a848382
    feat(split_invoicing): odesílání faktur přes vlastní SMTP firmy
    Per firma: Fakturoid odesílá, nebo vlastní SMTP (PDF z Fakturoidu jako
    příloha, šablona předmětu/textu s proměnnými, testovací e-mail).
    Odeslání max 1×, selhání nevrací issued, cron opakuje max 5×.
    smtp_guard přesunut do jádra (editor/), digital_products má shim.
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  68. 2026-10-19 372a93ba
    fix(split_invoicing): načtené skupiny/ceníky vidět i bez založené firmy
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  69. 2026-10-19 c980e4fb
    feat(split_invoicing): Shoptet API jako druhý zdroj dat vedle CSV exportů
    Volba zdroje per e-shop (data_source). API: receiver order_imported jen
    pro zaplacené objednávky, ceník/skupina z customers/{guid}, selhání
    dohledání = retry, nikdy výchozí firma. Návod v katalogu přes 0028.
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  70. 2026-10-19 ee4fc06a
    feat(split_invoicing): zdroj dat z CSV exportů Shoptetu, směrování podle ceníku
    Objednávky a ceník zákazníka (párování přes e-mail) z nativních exportů;
    selhání exportu zákazníků = retry, nikdy výchozí firma. Cron po 10 min,
    round_total podle priceToPay, diag bez PII.
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  71. 2026-10-19 5e82598c
    feat(split_invoicing): fakturovat až po zaplacení objednávky
    Receiver zařadí jen zaplacenou objednávku (i při aktualizaci), worker
    ověří paid v detailu Shoptetu; přeskočený job bez zápisu do Fakturoidu
    se importem zaplacené objednávky vrátí do fronty.
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  72. 2026-10-19 028b145a
    fix(split_invoicing): opravy z review — duplicitní faktury, retence, DPH, veřejné Shoptet API
    - provider_attempted_at: po pokusu o zápis se firma jobu už nemění
    - find_* jen s přesnou shodou custom_id; update_subject před fakturou
    - chybějící sazba DPH / měna = needs_review; DUZP z data objednávky
    - slug účtu zamčený po prvních jobech; neaktivní firma = needs_review
    - purge nemaže vystavené joby (idempotence); logy bez PII
    - payment_gateway.shoptet_api.get_customer / list_customer_groups
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  73. 2026-10-19 eb58f4aa
    feat(split_invoicing): Fakturoid v3 klient, fronta faktur podle zákaznické skupiny, UI nastavení
    - receiver order_imported jen zařadí job (cutoff issue_from, jen Shoptet API)
    - cron issue_split_invoices: detail objednávky + skupina zákazníka -> firma,
      find_invoice před create (idempotence), needs_review při nesouhlasu součtu
    - UI: firmy (šifrované klíče), mapování skupin, cutoff, přehled faktur
    - seed katalogu, crontab, retence 90 dní
    
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  74. 2026-10-19 8c6ddcda
    feat(split_invoicing): kostra doplňku — modely, rozhraní poskytovatele, diag skupin
    Claude-Session: https://claude.ai/code/session_013kwYWMG2URJ8zAmbbnfuyA
  75. 2026-10-16 fb049a7c
    fix(upsell): uložení nesmí tiše smazat zákaznické pravidlo
    save_upsell_data zahodí celé pravidlo, kterému po validaci nezbude
    spouštěč nebo nabízený produkt — a UI pak hlásilo „uloženo“. 15. 10.
    takhle zmizela kategoriová pravidla „Kontaktné šošovky“ na
    happyhairshop SK/HU (obnoveno ze zálohy).
    
    Nový přepínač protect_rules=True: když by validace pravidlo zahodila,
    vyhodí RulesWouldBeDropped a nic nezapíše. Používají ho všechny UI
    cesty (editace pravidla, vzhled pravidla, nastavení, vzhled, starý
    JSON editor) — uživatel dostane hlášku s názvem pravidla a formulář
    s vyplněnými hodnotami.
    
    Claude-Session: https://claude.ai/code/session_01JSBLzGsn9XGVp83ueLoUeF
  76. 2026-10-14 90adac18
    fix(payments): selhaný sync stavu na Shoptet se opakuje
    Status 'failed' byl konečný — cron bral jen 'pending', takže dočasná
    chyba (500 při loginu robota do Shoptet adminu) nechala objednávku
    navždy nezaplacenou. due_for_sync vrací i selhané platby: první den
    po 10 min, pak po hodině, max 7 dní od vytvoření.
    
    Claude-Session: https://claude.ai/code/session_01GtTJ1GYVxq99QJmtu7UdPx
  77. 2026-10-14 19939817
    fix(upsell): košíkové nabídky i na šablonách bez .cart-summary
    fvStudio Samba (ovocnedarky.cz) má v košíku jen .cart-content >
    table.cart-table bez rekapitulace — mount #shoptet-upsell-cart se
    nevytvořil a cart_offers se nikdy nezobrazily. Náhradní kotva pod
    tabulku položek po 2,5 s, onReady jen jednou.
    
    Claude-Session: https://claude.ai/code/session_01V51QrNUueDq2RGTqZgXqkf
  78. 2026-10-14 9de540aa
    fix(upsell): spouštěč = produkt i u katalogu z crawlu webu
    Crawl katalog (ovocnedarky.cz) měl jako Product.code SKU první varianty
    (129/MAL) a o produktu nevěděl nic víc. Crawler teď z dataLayeru bere
    Shoptet ID produktu, guid a kódy variant; config.json posílá
    trigger_product_ids a widget je páruje s dataLayer product.id. Editor
    u spouštěče ukazuje „Produkt ID“ místo kódu varianty.
    
    Widget: šablony bez .buy-box (fvStudio Samba) — náhradní kotva
    #product-detail-form, jinak zůstal widget nad hlavičkou stránky.
    
    Claude-Session: https://claude.ai/code/session_01V51QrNUueDq2RGTqZgXqkf
  79. 2026-10-14 fabb0f85
    fix(upsell): spouštěč sedí na kterýkoli kód varianty produktu
    Katalog z crawlu (ovocnedarky.cz) má jako Product.code kód první
    varianty (129/MAL); widget porovnával jen dataLayer product.id (129),
    takže pravidlo se na detailu nikdy nezobrazilo. Widget teď bere i
    product.codes[] — spouštěč = produkt, kterákoli jeho varianta.
    
    Claude-Session: https://claude.ai/code/session_01V51QrNUueDq2RGTqZgXqkf
  80. 2026-10-14 bdc29fad
    feat(upsell): spouštěč = konkrétní produkt, ne varianta
    Našeptávač spouštěčů (?roots=1) vynechá variantní řádky (is_variant),
    uložený kód varianty v trigger_codes se přemapuje na rodiče. Widget
    stejně matchuje dataLayer product.id bez varianty. Nabízené produkty
    (offer_codes/gift_codes) varianty dál smí.
    
    Claude-Session: https://claude.ai/code/session_01V51QrNUueDq2RGTqZgXqkf
  81. 2026-10-14 83f069a1
    feat(orders): Shoptet import objednávek jen v okně od poslední objednávky −3 dny
    Cron stahoval při každém běhu celou historii exportu (yaknahory 2064
    objednávek od 1/2025). Shoptet export `/export/orders.csv` bere
    dateFrom/dateUntil se stejným hashem (ověřeno živě) — teď se stahuje jen od
    poslední naimportované objednávky minus 3 dny do dneška; pozdě zaplacené
    objednávky okno znovu načte a upsert podle order_code je aktualizuje. Bez
    dosavadních objednávek a u jiných URL se adresa nemění.
    
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  82. 2026-10-14 6c692e1f
    fix(warehouse): cron odpisu komponent z objednávek jde s deployem (yaknahory)
    deduct_orders byl jen v referenčním crontab.txt — po restartu kontejneru
    na serveru chyběl a odpis příplatků/setů neběžel vůbec. Řádek je teď v
    crontab.managed.txt, zatím jen pro shop 27; pivovar (11) má nevyřízenou
    historii od 2023 a zapne se až po mark_orders_deducted.
    
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  83. 2026-10-14 5da00ac7
    fix(warehouse): seznam skladů z dat stránky — novější admin nemá tabulku
    yaknahory má stránku Sklady vykreslenou JavaScriptem: HTML tabulka chybí
    a „Otestovat přihlášení" končilo „seznam skladů v adminu nenalezen".
    Sklady se čtou z JSON propsů `<div data-page>` (props.data.stocks, bez
    smazaných), tabulka zůstává jako záloha pro starší admin.
    
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  84. 2026-10-14 277e6a27
    feat(warehouse): retence fronty robota 90 dní
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  85. 2026-10-14 98412c27
    feat(warehouse): nastavení robota v Shoptet adminu + fronta zápisů v UI
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  86. 2026-10-14 3f2e597e
    feat(warehouse): robot zapisuje frontu odpisů do Shoptet adminu (cron)
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  87. 2026-10-14 c7e4dbdf
    feat(warehouse): klient stránky zásob v Shoptet adminu (zápis rozdílu)
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  88. 2026-10-14 230d882d
    feat(warehouse): našeptávač komponenty podle názvu i kódu, včetně variant
    <datalist> prohlížeče filtrují jen podle hodnoty (kódu) a Safari ho skoro
    neukáže — v párování příplatků nešlo nic vybrat. Nový sdílený našeptávač
    (static/warehouse/code_picker.js) hledá v názvu i kódu bez diakritiky,
    nabízí produkty i jednotlivé varianty („PONOŽKY maniYAK — Velikost: 34-36"),
    ovládá se šipkami a visí s position: fixed, ať ho karta neořízne. Použitý
    v Případkových komponentách i na stránce Sety; ze Skladu odkaz na párování.
    
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  89. 2026-10-14 4a810f93
    feat(warehouse): sety jako varianty + odpis variant
    Model BundleComponent: složení setu (kód produktu nebo varianty → komponenty
    × počet), které Shoptet ve feedu nenese — yaknahory __matrix_286 „SET: popruh
    + pouzdro" se 7 barevnými variantami. Prodaný set strhne komponenty
    (REASON_BUNDLE_DEDUCT) i při --surcharges-only; příplatek namapovaný na kód
    setu se rozbalí na jeho složení (rekurzivně, pojistka proti cyklu).
    
    apply_movement/set_stock umí kód varianty (Product.variants[i]); se zapnutým
    robotem se interní stav neořezává na nule, ať robot odepíše celé množství.
    Stránka Sklad → Sety a výběr komponent nabízí i varianty.
    
    Claude-Session: https://claude.ai/code/session_01GMhyRn135NFA9b2Ym6Qyz9
  90. 2026-10-13 aee46bf2
    fix(marketplaces): stejná hodnota ve dvou tajných polích napojení se neuloží
    7. 10. se HMAC klíč Alzy přepsal hodnotou Client Secretu (prohlížeč
    vyplnil uložené heslo do obou polí) a Alza od té doby vracela
    „Not Authorized“. Formulář teď takové uložení odmítne.
    
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  91. 2026-10-13 afa21b08
    fix(payment widgets): párování platby podle názvu z košíku, ne jen podle relace
    Objednávka 2026005950 (Safari): děkovačka ukazuje jen název platby,
    nastavení má ID (226) a jediný most byla volba z košíku v sessionStorage
    — ta se ztratila a widget nepřesměroval. Widget si teď v košíku trvale
    (localStorage) zapamatuje název volby s ID z nastavení a na děkovačce ho
    porovná se skutečnou platbou objednávky (chrání i před dobírkou). Záznam
    děkovačky nese klíče objednávky z dataLayeru (podklad pro párování
    přímo podle ID). Stripe i Revolut.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  92. 2026-10-13 13298857
    fix(payment widgets): dlouhá návratová URL nesmí shodit platbu
    Objednávka 2026005943: děkovačka s parametry z reklamy přetekla
    max_length návratové URL (200) a checkout vrátil invalid_fields —
    platba nevznikla a zákazník se na Revolut nedostal. Widget teď posílá
    jen adresu stránky (origin + path), server dlouhou query/fragment
    zahodí a chyba invalid_fields nese název pole. Stripe i Revolut.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  93. 2026-10-13 5d2f78e3
    feat(payment widgets): stopa, zda zákazník opravdu odešel na platební stránku
    Revolut u řady nezaplacených objednávek neeviduje žádný pokus o platbu,
    widget přitom přesměrování zadal. Nové události: klik na tlačítko
    platby, odchod ze stránky (pagehide, ms po přesměrování), zaseknuté
    přesměrování (4 s bez odchodu, se stavem stránky) a návrat z platební
    stránky. Tlačítko se po získání platebního odkazu mění na skutečný
    <a href> — projde i v in-app prohlížečích, které JS přesměrování drží.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  94. 2026-10-12 725a0cf2
    docs(roadmap): KetoLinie Allegro stav po doladění
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  95. 2026-10-12 d18f9080
    fix(product_editor): číselníky po importu přes run_task — z cronu se nepřestavěly
    Receivery feed_imported spouštěly přestavbu číselníků a překladů dostupnosti
    v daemon vlákně po commitu. Import z cronu (manage.py) proces hned ukončí,
    vlákno zanikne → param_surcharge zůstal prázdný (yaknahory: nešlo namapovat
    příplatkové komponenty). Teď přes editor.background.run_task (django-q).
    
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  96. 2026-10-12 8f259a66
    feat(marketplaces): Allegro název nabídky s předponou z mapování produktu
    Allegro odmítá krátké názvy (min. 12 znaků / 3 slova, např. „šejkr Emma“).
    seo_prefix mapování produktu se doplní před název jen pro tržiště.
    
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  97. 2026-10-12 295f878b
    refactor(encryption): EncryptedField do jádra editor/ — Sklad nezávisí na platební bráně
    depends_on je aktivační závislost (vypnutý payment_gateway by blokoval
    warehouse), ne jen importní povolení — yaknahory platební bránu nemá.
    EncryptedField (Fernet) se přesouvá z payment_gateway.encryption do
    editor/encryption.py (jádro); payment_gateway/encryption.py zůstává jako
    re-export shim, ať staré migrace (payment_gateway, digital_products,
    marketplaces, revolut_gateway, shipping_integration) dál importují stejnou
    třídu beze změny. warehouse/models.py teď importuje z editor.encryption a
    'payment_gateway' mizí z warehouse/addon.py depends_on. Migrace
    warehouse/0008_admin_robot.py (nemergovaná) regenerována na nový import
    path — makemigrations --check --dry-run beze změn ve všech appkách
    (deconstruct() je shodný pro obě cesty importu, protože jde o tutéž třídu).
    
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  98. 2026-10-12 8573cd5a
    feat(marketplaces): Allegro ruční kategorie bez mapování bere parametry ze šablony
    Produkt přesunutý do Allegro kategorie, kterou nepoužívá žádná kategorie
    e-shopu, dostane zdroje parametrů odhadnuté ze šablony kategorie (cache
    24 h); chybějící povinné hodnoty doplní ruční parametry produktu.
    
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  99. 2026-10-12 aa0c0273
    feat(warehouse): příplatkový odpis do fronty robota, když je zapnutý
    _process_orders načte AdminRobotSettings jednou na celý běh (ne per
    položka). Je-li robot zapnutý a připravený, odpis příplatkové komponenty
    místo StockLevel.surcharge_offset založí AdminStockPush — robot ho zapíše
    přímo do Shoptetu a příští snímek ho uvidí jako běžný stav, takže offset by
    odpis započítal podruhé. Bez robota chování beze změny.
    
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  100. 2026-10-12 fac40fcc
    feat(warehouse): modely robota v Shoptet adminu (nastavení + fronta)
    AdminRobotSettings (přihlášení + sklad pro odpis, heslo šifrováno
    EncryptedField) a AdminStockPush (fronta zápisů) pro robota, který bude
    komponenty setů odepisovat přímo v Shoptet adminu (yaknahory bez API).
    warehouse nově závisí na payment_gateway (sdílené EncryptedField) —
    addons.tests.test_contract_imports zůstává zelený.
    
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  101. 2026-10-12 f4f0ad0f
    fix(marketplaces): Allegro zásoba zastropovaná na 99 999 ks
    Shoptet „neomezeno“ (9 999 9xx ks) Allegro tiše zahodí a aktivace
    nabídky pak selže na NotNullStockViolation.
    
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  102. 2026-10-12 78142025
    refactor(robot): jádro Shoptet admin robota do editor/ (sdílí platby i sklad)
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  103. 2026-10-12 a1ca4ab0
    fix(marketplaces): Allegro ruční kategorie produktu převezme parametry kategorie
    Produkt s ručně zvolenou Allegro kategorií (bez kategorie e-shopu nebo
    z nemapované) neměl načtené zdroje parametrů, a tak nikdy nebyl kompletní.
    Teď je převezme z potvrzeného mapování na stejnou Allegro kategorii.
    
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  104. 2026-10-12 4123f688
    fix(feed_import): stav po skladech jen do warehouse_stock, stock_amount beze změny
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  105. 2026-10-12 42b0f928
    feat(feed_import): stav po skladech z STOCK/WAREHOUSES (yaknahory)
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  106. 2026-10-12 3af84923
    docs(warehouse): implementační plán robota skladu (HTTP jádro sdílené s platbami)
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  107. 2026-10-12 ed6aa39b
    docs(roadmap): KetoLinie nové Allegro nabídky aktivované
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  108. 2026-10-12 503a29d0
    fix(marketplaces): Allegro cena v CZK na celé koruny
    Allegro.cz odmítá haléře: PATCH vrací „Počet desetinných míst pro zadaný
    trh není správný“, POST cenu tiše zahodí a nabídka zůstane bez ceny.
    
    Claude-Session: https://claude.ai/code/session_01FvseBTJ1g3EM3bxK3j4Bkq
  109. 2026-10-12 12c002a0
    docs(warehouse): spec — sety jsou příplatkové parametry, robot zapisuje rozdíl
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  110. 2026-10-12 da340fcd
    docs(warehouse): spec — zmapovaný Shoptet admin (zápis rozdílu, login, log změn)
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  111. 2026-10-12 981a3835
    docs(warehouse): spec — sety yaknahory, odpis komponent robotem v Shoptet adminu
    Claude-Session: https://claude.ai/code/session_01XWny8jZuMZca9ybQu6Ym1X
  112. 2026-10-11 b854f379
    ci: testy paralelně — sériová sada přesahovala timeout jobu
    Po opravě spectacular kroku doběhla Verify application až k testům a ty
    zrušil 35min timeout. Lokálně je sada paralelně bezpečná (6149 testů
    zelených za 3,7 min s --parallel 4 vs 12 min sériově).
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  113. 2026-10-11 ffc8badf
    fix(data_check): OpenAPI schéma pro API Ověření dat — CI padal na spectacular
    Noční Verify application od 8. 10. končil na
    'spectacular --validate --fail-on-warn' (8 function-based endpointů bez
    serializeru) a plná sada testů se vůbec nespustila. Endpointy mají
    extend_schema s query parametry a JSON odpovědí; plná sada lokálně
    zelená (6149 testů).
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  114. 2026-10-11 70b6050d
    fix(payment widgets): objednávka na dobírku nesmí jít na platební bránu
    Zákazník zvolil v košíku kartu, po reloadu košíku přepnul na dobírku —
    widget si pamatoval první volbu (rgw-armed) a objednávku 2026005847
    poslal na Revolut. Volba z košíku si teď drží skupinu a název; přepnutí
    ve stejné skupině ji maže i po reloadu a děkovačka ji použije jen když
    platba/doprava objednávky odpovídá zvolenému názvu. Nesoulad se zapíše
    do záznamu widgetu. Stejně pro Stripe.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  115. 2026-10-11 04552959
    feat(payment_gateway): Stripe brána — okamžité přesměrování, tlačítko k platbě, záznam widgetu
    Stejně jako Revolut: widget na děkovací stránce přesměrovává hned,
    tlačítko po získání platebního odkazu vede rovnou na Stripe Checkout
    a nový GatewayWidgetEvent zaznamená výběr platby v košíku, děkovačku
    (s diagnostikou stránky), přesměrování, chyby widgetu i odmítnuté
    checkouty. Logika záznamu je sdílená v services/widget_events.py,
    tabulka v šabloně _widget_events.html; Revolut ji používá taky.
    Retence 90 dní v purge_old_gateway_payments.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  116. 2026-10-11 c159e085
    feat(revolut_gateway): tlačítko k platbě, když přesměrování neproběhne
    Po získání platebního odkazu vede tlačítko v liště rovnou na Revolut
    (dřív během čekání nic nedělalo) a lišta říká „Pokud se platba neotevře,
    klikněte na tlačítko“ — ve všech jazycích widgetu.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  117. 2026-10-11 ff55cdf3
    fix(revolut_gateway): chyba „objednávka nenalezena“ jen na děkovací stránce
    Zákazník, který po výběru Revolutu odešel z košíku na přihlášení nebo
    produkt, generoval falešnou chybu (objednávka nevznikla). Záznam se teď
    zapisuje jen na skutečné děkovačce (pageType thankYou nebo /dekujeme/).
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  118. 2026-10-10 3d716299
    feat(revolut_gateway): log ukáže, proč objednávka nedošla na Revolut
    Widget zapisuje i výběr Revolutu v košíku a — když po něm na další
    stránce (děkovačce) objednávku nenajde — chybu s diagnostikou stránky
    (číslo šablony, pageType, které zdroje objednávky stránka nabízí).
    Diagnostika jde i k záznamu děkovací stránky. shoptet.thankYouPageOrder
    přidán jako další zdroj objednávky.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  119. 2026-10-10 8a18a20b
    feat(revolut_gateway): okamžité přesměrování + záznam pokusů o platbu
    Widget na děkovací stránce přesměrovává na Revolut hned (dřív po 3 s,
    zákazníci stránku zavírali dřív). Nový RevolutWidgetEvent zaznamená
    každou děkovačku (číslo, platba/doprava, shoda s bránou), přesměrování
    a chyby widgetu (sendBeacon přežije zavření stránky) i checkouty, které
    server odmítl. Přehled plateb ukazuje tabulku „Záznam widgetu“, takže
    je vidět i objednávka, po které nezůstala žádná Revolut platba.
    Retence 90 dní v purge_old_revolut_payments.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  120. 2026-10-10 41648db8
    feat(payment_gateway): Shoptet robot si drží přihlášení (cookies)
    Robot se při každém syncu platby (Stripe i Revolut brána) přihlašoval
    novou session → Shoptet posílal e-mail o novém přihlášení. Cookies se
    teď ukládají šifrovaně do ShoptetConnection.robot_cookies, platnost se
    ověří přes /admin/ a login proběhne jen po vypršení session. Login
    zaškrtne „zapamatovat“; cookies se mažou při chybném loginu a při změně
    loginu/hesla/domény ve formuláři napojení.
    
    Claude-Session: https://claude.ai/code/session_01Kc798W1Aei4W97zV6DwMsv
  121. 2026-10-09 317eceb9
    fix(warehouse): vnořený formulář zveřejnění skladu rozbíjel editor produktu
    Ovladač „Zveřejnit přesný počet kusů“ v panelu Sklad byl <form> uvnitř
    formuláře editoru. Parser vnořený tag zahodí, ale jeho </form> ukončí
    formulář editoru — panely za Skladem vypadly do sloupce navigace
    (rozhozený layout) a jejich pole se neodesílala. Pole se teď váží
    atributem form= na samostatný formulář za editorem.
    
    Claude-Session: https://claude.ai/code/session_01G9oHEfkW1yGXroVFoWFjMv
  122. 2026-10-08 20fc9bcb
    feat(shops): výpisy e-shopů všude abecedně
    accessible_shops má výchozí řazení podle názvu bez ohledu na velikost
    písmen (SHOP_NAME_ORDER) — seznamy v produktech, rezervacích, videích,
    dodavatelích, vyhledávání a normalizaci variant byly v pořadí založení.
    Šablony e-shopů řadí výpis abecedně, výchozí výběr zůstává primární.
    
    Claude-Session: https://claude.ai/code/session_01G9oHEfkW1yGXroVFoWFjMv
  123. 2026-10-08 cc8dc0c1
    feat(warehouse): aktivace Skladu z rozcestníku v god módu, abecední řazení
    Rozcestník ukazoval jen e-shopy s aktivním Skladem; u tenanta s 24 e-shopy
    jich bylo vidět 10 a admin_only doplněk nešlo odtud zapnout. V god módu
    teď rozcestník vypíše e-shopy přepnutého tenanta bez Skladu s tlačítkem
    Aktivovat (přes addons:shop_addon_toggle). Řazení podle jména bez ohledu
    na velikost písmen.
    
    Claude-Session: https://claude.ai/code/session_01G9oHEfkW1yGXroVFoWFjMv
  124. 2026-10-08 df3b95c7
    feat(marketplaces): diagnostika ukazuje název produktu s proklikem
    Řádky chyb a varování měly jen kód produktu. Teď název (odkaz na detail
    produktu) a pod ním kód s odkazem na nastavení produktu pro tržiště.
    
    Claude-Session: https://claude.ai/code/session_01NK53QaYoFZZzdDL6y6LMsp
  125. 2026-10-08 c4ddda3e
    fix(marketplaces): Allegro nabídka až s parametry kategorie, 422 bez opakování
    offer_ready bral kategorii bez načtených parametrů jako kompletní (žádný
    povinný parametr = nic nechybí). 8. 10. tak šlo 6 produktů KetoLinie
    z nově přijatých kategorií bez parametrů; EAN mimo katalog Allegra →
    422 MatchingProductForIdNotFoundException, a outbox každý zkusil 10×.
    
    - nová nabídka jen s načtenými parametry kategorie
    - Allegro 400/422 je trvalá chyba → zpráva hned failed
    - stránka Kategorie: souhrn nahoře, nezařazené kategorie nad namapovanými
      (dřív až pod dlouhou tabulkou, klient je nenašel), řazení podle počtu
      produktů, štítek „návrh odmítnut“
    
    Claude-Session: https://claude.ai/code/session_01NK53QaYoFZZzdDL6y6LMsp
  126. 2026-10-07 de2a56eb
    chore(roadmap): Rumako Alza šablony na produkci
    Claude-Session: https://claude.ai/code/session_01NK53QaYoFZZzdDL6y6LMsp
  127. 2026-10-07 000adb4a
    fix(marketplaces): chyba portálu Alzy neshodí mapování z nahraných šablon
    Rumako má vyplněné, ale neplatné kredence portálu (invalid_client). Pro
    kategorii bez nahrané šablony load_templates sáhl na portál a výjimka
    zahodila i nahrané šablony — nenamapovalo se nic. Kategorie bez šablony
    se teď jen přeskočí.
    
    Claude-Session: https://claude.ai/code/session_01NK53QaYoFZZzdDL6y6LMsp
  128. 2026-10-07 1282ea9c
    feat(marketplaces): import zalistovacích šablon Alzy z XLSX
    Bez Client ID AlzaTrade portálu nešly načíst šablony kategorií, takže
    parametry a SEO prefixy zůstávaly prázdné. Šablony (XLSX nebo ZIP) se
    nově nahrají na stránce Kategorie, uloží k napojení (AlzaCategoryTemplate)
    a load_templates je použije místo portálu. Po nahrání se pro dotčené
    kategorie spustí mapování parametrů.
    
    - SEO prefix: víc možností bez výchozí → výběr podle názvu kategorie
    - párování parametrů ignoruje jednotku a skupinu Alzy
    - fix feedu: výška obalu PARAMETER_7155, hloubka PARAMETER_7152
      (podle šablon Alzy; dřív výška 7152, hloubka 7154)
    
    Claude-Session: https://claude.ai/code/session_01NK53QaYoFZZzdDL6y6LMsp
  129. 2026-10-07 7503058a
    chore(roadmap): Rumako Alza mapování kategorií
    Claude-Session: https://claude.ai/code/session_01Bj3q8N1akFbTXuFKWkfFC1
  130. 2026-10-07 a7c5aa4a
    fix(payment_gateway): robot ověřuje cílový stav objednávky a výsledek uložení
    Neexistující target_status_id (ID z jiného e-shopu) Shoptet admin tiše
    zahodil a robot hlásil synced, přestože se stav nezměnil. ID se nově
    ověřuje proti výběru stavů, při neshodě se použije název, jinak sync
    skončí chybou se seznamem dostupných stavů. Po uložení robot detail
    znovu načte a ověří Placeno i stav.
    
    Claude-Session: https://claude.ai/code/session_01H6XjrttEDDgUvnWrbuEQuZ
  131. 2026-10-06 6d798750
    feat(marketplaces): Allegro sazba DPH v nabídce a pojistka proti sdílenému EAN
    Allegro Business CZ/PL zamítalo všechny nabídky bez taxSettings (VQR012).
    Sazba se bere z feedu, jinak z nové volby v nastavení napojení. Nová
    nabídka se nezakládá pro produkt, jehož EAN má jiný produkt e-shopu —
    Allegro by oba sloučilo do jednoho katalogového produktu.
    
    Claude-Session: https://claude.ai/code/session_01Bj3q8N1akFbTXuFKWkfFC1
  132. 2026-10-05 c00609c4
    feat(revolut_gateway): Revolut platební brána jako doplněk
    Port Revolut brány ze starého backendu (djangoAdmin) jako samostatný
    doplněk, který funguje stejně jako Stripe (payment_gateway): snippet do
    hlavičky Shoptetu, přesměrování na Revolut Hosted Checkout z děkovací
    stránky, webhook s ověřeným podpisem, self-healing status endpoint,
    ověření částky proti Shoptet API a propsání „zaplaceno“ cronem.
    
    - Revolut Merchant API ověřené proti oficiální OpenAPI specifikaci
      (merchant-2026-08-17): /api/orders, /api/webhooks (automatická
      registrace webhooku po uložení klíče), podpis v1 HMAC-SHA256.
    - Webhook nevěří payloadu: stav objednávky vždy z Revolut API, cizí
      objednávky účtu potvrdí 204, chyby vrací 4xx (Revolut opakuje jen 4xx).
    - Ochrana proti dvojí platbě: zaplacená objednávka → 409 already_paid,
      rozpracovaná se znovu použije, neznámý stav → 502 místo nové.
    - Cron process_revolut_status_sync: rekonciliace pending plateb (<48 h)
      + propsání na Shoptet přes sdílené status_sync.sync_payment.
    - Retence 90 dní (purge_old_revolut_payments), crony v crontab.managed.
    - Sdílené checkout helpery vytažené ze Stripe api.py do
      payment_gateway/services/checkout_common.py (chování Stripe 1:1),
      preserve_secrets veřejně, Shoptet karta jako sdílený include.
    - Katalog: beta, admin_only, zdarma (addons 0026) + návod.
    
    Claude-Session: https://claude.ai/code/session_01EJ3DCNUSZexNWL66nfVKs4
  133. 2026-10-05 b86cb5bf
    feat(marketplaces): Allegro nabídky a objednávky automaticky v cronu
    - push nabídek každých 15 min, polling objednávek každých 5 min
      (crontab.managed.txt, crontab.txt, crons v addon.py)
    - nová nabídka jen pro kompletní produkt (kategorie + povinné parametry);
      stejná data, která Allegro definitivně odmítlo, se neposílají dokola
    - aktualizace PATCH bez productSet — schválený nebo sloučený katalogový
      produkt by jinak blokoval i cenu a sklad
    - vyprodaný / vyřazený / smazaný produkt: příkaz END; po naskladnění
      PATCH + ACTIVATE (MarketplaceProductState.quantity = poslední odeslaný
      sklad, 0 = ukončeno synchronizací)
    - každý příkaz publikace má vlastní commandId — Allegro na stejné ID
      vrátí už provedený příkaz
    
    Claude-Session: https://claude.ai/code/session_01AeoGm9YGdWcs9nNcLtGXHM
  134. 2026-10-05 bedd0774
    chore(roadmap): Allegro KetoLinie — 67 nabídek aktivních
    Claude-Session: https://claude.ai/code/session_01AeoGm9YGdWcs9nNcLtGXHM
  135. 2026-10-05 df83a34b
    chore(roadmap): Allegro KetoLinie — 69 nabídek odesláno jako koncepty
    Claude-Session: https://claude.ai/code/session_01AeoGm9YGdWcs9nNcLtGXHM
  136. 2026-10-05 d01a891b
    fix(marketplaces): Allegro vlastní hodnota z názvu produktu max. 50 znaků
    Šablona kategorie limit u slovníku s vlastními hodnotami neuvádí, Allegro
    ale delší hodnotu odmítne (422 „Hodnota v parametru Název je příliš
    dlouhá“ — živě 50 znaků prošlo, 52 ne). Název se zkracuje na hranici slova.
    
    Claude-Session: https://claude.ai/code/session_01AeoGm9YGdWcs9nNcLtGXHM
  137. 2026-10-05 d1caf4a6
    fix(marketplaces): Allegro parametr podle názvu bez shody neposílá „jiná“
    Allegro u nového produktu odmítá ambiguousValueId bez vlastního návrhu
    hodnoty (422 „Pro nejednoznačnou hodnotu … nebyl zadán vlastní návrh“,
    u slovníků bez vlastních hodnot „Neplatné hodnoty parametru“). Bez shody
    v názvu teď parametr chybí — diagnostika ho ukáže a produkt se neposílá
    do jisté chyby.
    
    Claude-Session: https://claude.ai/code/session_01AeoGm9YGdWcs9nNcLtGXHM
  138. 2026-10-04 0362ccff
    feat(reservations): zavření dne pro všechny dopravy najednou
    Výjimka ve formuláři Výjimky jde zaškrtnutím „Pro všechny dopravy“
    uložit u všech profilů e-shopu (dovolená, den bez expedice). Uložené
    výjimky sdílené všemi profily mají štítek a tlačítko „Smazat u všech“.
    Při zavření dne s existujícími rezervacemi panel varuje, že zůstávají.
    
    Claude-Session: https://claude.ai/code/session_015nkXGR3eCs3NcKbqvXsoSe
  139. 2026-10-04 3cdd30c1
    fix(marketplaces): Allegro popis s mezerou za tučným popiskem, název bez zdvojených mezer
    Ostrý koncept KetoLinie (nabídka 18986337203) prošel validací Allegra bez chyb.
    
    Claude-Session: https://claude.ai/code/session_01RYA9ZVQ6815Pj45yyCpLwp
  140. 2026-10-04 e8b05116
    feat(marketplaces): parametry Allegro kategorií, popis nabídky, test jednoho produktu
    - parameters.py: šablona /sale/categories/{id}/parameters (cache 24 h),
      automatické zdroje (Stav, EAN, váha, porce/kusy z názvu, příchuť/druh
      podle názvu se stemem a fallbackem na „jiná“, Značka podle výrobce),
      ruční zdroje z UI, dělení parametrů produkt vs. nabídka, valuesIds
    - description.py: HTML popis → jen h2/p/ul/ol/li/b
    - payload podle OpenAPI: vlastní EAN mimo katalog jako parametr (bez
      id/idType), GPSR na prvku productSet, language, název 75 znaků,
      aktualizace PATCH bez publication, 202 → ID z Location
    - diagnostika chybějících povinných parametrů
    - marketplaces_allegro_push_offers --product KOD --preview|--now [--no-publish]
    
    Claude-Session: https://claude.ai/code/session_01RYA9ZVQ6815Pj45yyCpLwp
  141. 2026-10-03 3c7d6395
    chore(roadmap): portál bez cen a ve vzhledu eshopmagic.cz
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  142. 2026-10-03 847a64cf
    feat(portal): logo EshopMagic i na přihlášení a registraci
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  143. 2026-10-03 9a627432
    feat(portal): vzhled podle webu eshopmagic.cz
    Veřejný portál přechází ze shell.css administrace na design webu
    EshopMagic (převzaté em2026.css): krémové pozadí, Geist, fialová/žlutá,
    pilulková tlačítka, karty se stínem, fialový a tmavý pruh. Vlastní kostra
    stránky s navigací a patičkou jako web, logo EshopMagic + Magic Hub.
    Doplňky v jedné mřížce s kategorií jako štítkem, detail s panelem vpravo,
    /start/ jako em-form. Přihlášení a registrace ve stejných barvách.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  144. 2026-10-03 6fcbafbd
    fix(addons): návod Platební brány bez ceny doplňku
    Návody se zobrazují i na veřejném portálu, kde se ceny zatím neuvádějí.
    Sekce „Cena doplňku“ (300 Kč / měsíc ze seedu 0008, navíc nesouhlasila
    s katalogem) se nahrazuje jen informací o poplatcích Stripe; ručně
    upravený obsah návodu zůstává.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  145. 2026-10-03 2560a69f
    fix(portal): na veřejném portálu zatím neuvádět ceny
    Doplňky na portálu ukazují jen zkušební dobu („14 dní zdarma“), žádné
    částky; tlačítko po vyčerpané zkoušce je „Zapnout doplněk“ (cena je až
    na potvrzovací stránce v administraci). Odkaz na ceník kreditů se
    nepřihlášeným nezobrazuje.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  146. 2026-10-02 652ac035
    chore(roadmap): portál a fakturace doplňků — stav
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  147. 2026-10-02 14015168
    fix(addons): roční interval jen superadmin, zkouška klientů agentury zvlášť
    Roční předplatné neumí poměrnou část roku od aktivace ani přepnutí
    intervalu uprostřed roku — klient by si přepnutím na roční vyrobil měsíce
    zdarma, nebo naopak zaplatil dvakrát. Dokud to účtování neumí, roční
    interval nastavuje jen superadmin (po domluvě); klientovi se volba
    nezobrazuje a požadavek na roční se změní na měsíční / 403.
    
    Otisk zkušební doby už nezapisuje IČO plátce (agentury) — jinak by jedna
    zkouška vyčerpala nárok všem klientům, za které agentura platí.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  148. 2026-10-02 991221b7
    docs(addons): accrual předem, otisk zkoušky a otevřené body v roadmapě
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  149. 2026-10-02 d92b8f62
    fix(addons): zkušební doba — otisk zákazníka, plátce, 14 dní včetně startu
    - AddonTrial.tenant SET_NULL (anti-abuse ledger přežije smazání
      zákazníka) + AddonTrialMark: všechny normalizované domény e-shopů, IČO
      z fakturačních údajů a Shoptet projectId. Nárok padá na shodě
      kterékoli hodnoty v jiném účtu; prázdná doména kontrolu IČO/projectId
      nepřeskočí (migrace 0024).
    - Fakturační údaje se kontrolují u PLÁTCE (billing_tenant || tenant), kam
      jdou faktury; chybí-li agentuře, zákazník dostane srozumitelnou hlášku.
    - Zkouška = 14 dní včetně dne startu (2. 10. → do 15. 10.).
    - Badge „Vypnuto — nezaplacená faktura“ podle dluhu.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  150. 2026-10-02 d5e56d7c
    fix(addons): denní accrual předem, robustní poplatky a upomínky
    - addons_daily_billing zakládá poplatek za AKTUÁLNÍ měsíc (roční interval:
      rok, jen řádky aktivní od 1. 1.) pro každého plátce pod zámkem
      (invoicing.process_accruals → billing.accrue_current_period), bez
      e-mailů; nespoléhá na generate_monthly_invoices a sdílí s ním klíč
      (source_shop_addon, kind, period_start). Řádky ve zkušební době se
      přeskočí, měsíc konce zkoušky se nezdvojí s konverzí.
    - _accrue přes filter().first() + create pod zámkem plátce místo
      get_or_create (MySQL podmíněné UniqueConstrainty ignoruje).
    - Každá zkouška / faktura / plátce v denním běhu zvlášť (try/except +
      logger.exception) — chyba jedné položky nezastaví ostatní.
    - Zámek samoobsluhy podle dluhu (nezaplacená faktura po vypnutí), ne podle
      ShopAddon.suspended_at — nový e-shop ho neobejde.
    - Splatnost faktury v minulosti odmítnuta; vypnutí nikdy bez upomínky
      po splatnosti, upomínka uvádí skutečné ráno vypnutí.
    - --date jen s --dry-run; předmět e-mailu bez zalomení řádku.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  151. 2026-10-02 d8f64461
    feat(portal): tlačítko placeného doplňku vede na potvrzení zkušební doby
    Placený měsíční doplněk má na portálu „Vyzkoušet 14 dní zdarma“ (nebo
    „Zapnout za X Kč / měsíc“, když už zkoušku využil) s odkazem na
    addons:trial_start — tam čeká potvrzení podmínek a fakturační údaje.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  152. 2026-10-02 12e75b11
    docs(addons): zkušební doba a fakturace v kontraktu doplňků + roadmapa
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  153. 2026-10-02 6b0a5211
    feat(addons): 14denní zkušební doba, ruční fakturace a upomínky doplňků
    Zkušební doba (services/trials.py, AddonTrial): placený měsíční doplněk,
    který si zákazník zapne sám, jde přes potvrzení addons:trial_start
    (/addons/zkouska/<slug>/ — cíl i z veřejného portálu): 14 dní zdarma
    (AddonCatalog.trial_days), 1× na zákazníka i doménu e-shopu, vyžaduje
    fakturační údaje. Po konci doplněk běží dál a naúčtuje se zbytek měsíce;
    zrušit jde do konce zkoušky (addons:trial_cancel). Bez nároku = placená
    aktivace. Superadmin dál zapíná jedním klikem bez zkoušky.
    
    Fakturace (services/invoicing.py): superadmin dashboard:admin_billing —
    fronta K fakturaci s fakturačními údaji, zápis čísla faktury a splatnosti,
    Čeká na zaplacení (Označit zaplaceno / Odpustit). Upomínky v den
    splatnosti a +3 dny, +7 dní se doplněk vypne (ShopAddon.suspended_at,
    data zůstávají), po zaplacení se zapne. Denní cron addons_daily_billing
    07:00 (--dry-run, --date) + heartbeat, JobSpec, crontab.managed.txt.
    
    Platba kartou u doplňků vypnutá (ADDON_PAYMENT_METHOD=invoice, karta jen
    kredity); nezaplacená zapsaná faktura se nepurguje.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  154. 2026-10-02 35ca4b88
    fix(addons): měsíc konce zkušební doby se účtuje poměrně
    Accrual vynechával každé období, ve kterém zkouška (free_until) končí —
    celý měsíc konce zkoušky byl zdarma. Teď se vynechá jen období zkouškou
    pokryté celé, zbytek se naúčtuje poměrně (amount_after_trial, poznámka
    „Poměrná část po zkušební době“); měsíčně i ročně.
    
    accrue_trial_remainder založí poplatek za zbytek období hned při konci
    zkoušky — stejný klíč (source_shop_addon, kind, period_start) jako
    accrual 1. dne, takže ho ten nezdvojí.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  155. 2026-10-02 fbc3c10b
    feat(tenants): fakturační údaje workspace pro fakturaci doplňků
    BillingProfile (OneToOne Tenant): firma, IČO, DIČ, adresa, e-mail pro
    faktury, is_complete. Stránka tenants:billing_profile
    (/tenants/fakturacni-udaje/) pro owner/admin, ?next= jen lokální cíl.
    Odkaz v sidebaru Účet.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  156. 2026-10-02 12af4a97
    test(tenants): pre-hijack test registrace na nový formulář (e-mail + heslo + souhlas)
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  157. 2026-10-02 da746f68
    feat(portal): registrace ve vzhledu Magic Hub se souhlasem s podmínkami
    Vlastní šablona allauth registrace (e-mail, heslo, souhlas s odkazy na
    obchodní podmínky a zásady OÚ, Google tlačítko, odkaz na přihlášení),
    stejný vzhled jako přihlašovací stránka; `next` (návrat na /start/)
    se přenáší i přes Google.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  158. 2026-10-02 f377bee1
    feat(portal): superadmin editace veřejné stránky doplňku
    Na detailu doplňku v superadminu tlačítko „Veřejná stránka“ → editor
    textů portálu (adresa, název, claim, co umí, popis a FAQ v Quillu, SEO,
    hlavní služba na homepage). Bez uložené stránky se adresa odvozuje
    z českého názvu doplňku.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  159. 2026-10-02 ab53d189
    feat(portal): veřejný portál Magic Hub na kořeni s registrací a stránkami doplňků
    Nepřihlášený návštěvník na `/` vidí portál místo přihlašovacího formuláře:
    hlavní služby (Platební brána, Vyhledávač, Ověření dat), všechny veřejné
    doplňky podle kategorií, jak to funguje, návody a přihlášení vpravo nahoře.
    Každý veřejný doplněk má stránku /doplnky/<slug>/ s cenou, návodem
    z katalogu a tlačítkem podle stavu návštěvníka (vyzkoušet / zapnout přes
    Marketplace / otevřít nastavení / požádat o zapnutí). Texty přepisuje
    `portal.AddonPage`, pro tři hlavní služby naseedované podle toho, co kód
    skutečně umí.
    
    Registrace jen e-mail + heslo se souhlasem s podmínkami, pak `/start/`
    založí workspace i první e-shop a vrátí návštěvníka k doplňku, kvůli
    kterému přišel. Logika zakládání vytažená do `tenants.services.create_workspace`
    a `product_editor.services.shops.create_shop`. Přihlášení a allauth stránky
    přebrandované z G-Hub na Magic Hub.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  160. 2026-10-02 e30ddea5
    feat(tenants): pozvánka i heslo vedle sebe při přidání člověka
    Formulář „Přidat“ má dvě viditelná tlačítka: Poslat pozvánku a Vytvořit
    účet s heslem (heslo jen superadmin, účet vznikne hned bez e-mailu).
    Čekající pozvánky ukazují Poslat znovu i pole pro heslo bez rozbalování.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  161. 2026-10-02 83e128b7
    fix(feed_editor): název a popis upravený v editoru se propisuje do exportu
    get_effective_value preferoval *_original (text ze zdroje) před živým
    sloupcem — port z TS, kde edity šly do edits overlay. Editor primárního
    jazyka i AI přepis popisků ale ukládají do sloupce, takže ruční úprava
    názvu/popisu do Shoptet feedu nikdy nedorazila. Teď živý sloupec, *_original
    jen jako fallback. Oba importéry drží sloupec aktuální three-way mergem,
    needitované produkty exportují beze změny (Mironet golden test zelený).
    
    Claude-Session: https://claude.ai/code/session_01Mx1VJCc8ZNWa6Qje647pAf
  162. 2026-10-02 e1b8ca2a
    feat(product_editor): hromadné úpravy produktů s náhledem a vrácením zpět
    Výpis produktů má menu Hromadné úpravy: rozsah zaškrtnuté řádky nebo
    všechny produkty odpovídající filtru výpisu, náhled změn před provedením,
    velký rozsah běží na pozadí s průběhem. Operace jen nad poli, která
    Shoptet export opravdu bere z produktu: ceny (±%, ± částka, nastavit,
    zaokrouhlit ,90/9/5/10), doporučená cena, měna (přepsat / přepočet ČNB /
    vlastní kurz + volitelně výchozí měna e-shopu), sklad, jednotka,
    zobrazit/skrýt, vyřadit/zařadit do feedu, výrobce, štítky, kategorie
    srovnávačů.
    
    Historie úprav s Vrátit zpět — po celých produktech (vše nebo nic),
    varianty párované podle kódu, přeskočí produkty, které mezitím změnil
    import nebo ruční úprava. Retence 90 dní v purge_old_product_changes.
    Hromadná úprava nenastavuje edited (u mapovaného importu by zmrazil
    texty/obrázky), u polí přepisovaných importem maže otisk importu.
    
    Filtr výpisu podle měny a výrobce; filtry vytažené do služby sdílené
    s hromadnými úpravami. Starý endpoint vyřazení z feedu nahrazen operací.
    
    Import: matrix rodič bez <CURRENCY> přebírá měnu variant místo natvrdo
    CZK (luxurycardesign.sk měl eurové rodiče označené jako koruny).
    
    Claude-Session: https://claude.ai/code/session_01Mx1VJCc8ZNWa6Qje647pAf
  163. 2026-10-02 fdc415ce
    fix(data_check): našeptávač ulic — ulice zvlášť, obce jen jako doplněk
    Ve společném dotazu Mapy.com vracelo pro „mas“ 15× hlavně obce a
    Masarykovu v různých městech, po sloučení zbyla 1 ulice. Teď nejdřív
    jen ulice (15 kandidátů → 5 různých), obce a části obcí se dotazují
    až když řádků zbude míň než limit.
    
    Claude-Session: https://claude.ai/code/session_01Y8ABthqdb8Y8yp2p6rNR6T
  164. 2026-10-02 49d0a000
    feat(data_check): našeptávač adres ve stylu Foxentry — nejdřív ulice, pak čísla domů
    Řádek menu: kulatá vlajka CZ/SK, tučný název, vpravo „ulice“ nebo obec.
    Bez čísla domu widget nabízí ulice, části obcí a obce (Mapy.com
    regional.street/municipality_part/municipality); stejná ulice ve víc
    obcích je jeden řádek „ulice“. Výběr ulice v jedné obci doplní obec
    a hned nabídne čísla domů (t=address), u nejednoznačné ulice nápověda.
    Widget vrací 10 výsledků, patička s atribucí Mapy.com drží dole.
    Veřejné API se nemění (streets jen pro widget).
    
    Oprava: mousedown na řádku už nepropadne do document listeneru, který
    zavíral nově otevřené menu.
    
    Claude-Session: https://claude.ai/code/session_01Y8ABthqdb8Y8yp2p6rNR6T
  165. 2026-10-02 6cb4b7bd
    fix(product_editor): editor produktu se drží šířky displeje
    Sloupec panelů měl grid-template-columns 1fr = minmax(auto, 1fr), takže
    řada obrázků, široká tabulka v popisku nebo dlouhá URL roztáhly celý
    editor do boku. Teď minmax(0, 1fr): široký obsah scrolluje uvnitř bloku.
    Vizuální editor popisku zalamuje dlouhá slova, zmenšuje obrázky na šířku
    boxu a širokou tabulku scrolluje (dřív ji overflow:hidden ořízl).
    
    Claude-Session: https://claude.ai/code/session_01Mx1VJCc8ZNWa6Qje647pAf
  166. 2026-10-02 022ec1e4
    feat(tenants): pozvánka e-mailem jako primární přihlášení, heslo jen alternativa
    Přidání člověka (detail klienta i detail e-shopu) rovnou pošle e-mail
    s pozvánkou: jednorázový přihlašovací odkaz na 48 h, potom kód z e-mailu
    nebo Google. Tenant bez e-shopu dostane návod na přihlášení kódem,
    existující uživatel jen oznámení o přístupu. Čekající pozvánky ukazují
    stav odeslání (Invite.sent_at) a tlačítko Poslat znovu; heslo za klienta
    je schované pod „Nebo heslo…“.
    
    Bezpečnost: bootstrap_invited_user a resolve_user_by_email už nevrátí
    Google ani bootstrap účet, který si e-mail klienta jen napsal do profilu
    (pre-hijack — odkaz z pozvánky by přihlásil klienta do útočníkova účtu,
    resp. útočník by dostal členství rovnou). mark_email_verified maže cizí
    neověřené nároky na adresu. Limit 20 pozvánek/h na odesílatele.
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  167. 2026-10-02 e9e5e3a4
    fix(widgets): Videa a Ověření dat načítají tlačítko do Shoptet admin lišty
    Storefront widgety Videí k produktům a Ověření dat nenačítaly sdílený
    loader „Externí nastavení“ (window.__aiBridgeAuto), takže klient neměl
    v admin liště e-shopu proklik do administrace. Loader běží na každé
    stránce. Static obou doplňků dostal krátkou cache v nginx jako ostatní
    widgety; kontraktní test hlídá loader u všech widget.js.
  168. 2026-10-02 3b2f4f2d
    feat(addons): tlačítko Nastavení u každého aktivního doplňku e-shopu
    V Marketplace e-shopu i v superadmin seznamu „Doplňky e-shopu“ má aktivní
    doplněk proklik do své stránky pro daný e-shop. Cíl dává registry.shop_url():
    nové pole manifestu shop_url_name, jinak settings link, jinak vstupní
    stránka. Kontraktní test hlídá, že ho má každý doplněk.
  169. 2026-10-02 9d9c3c47
    chore(roadmap): videa k produktům zapnutá pro Happy Hair
  170. 2026-10-02 3e98e71a
    feat(product_videos): doplněk Videa k produktům
    Videa (YouTube, Vimeo, MP4) v galerii produktu na e-shopu — místo
    parametrů video_N (Happy Hair) se zadávají v administraci. Panel e-shopu
    se snippetem pro Shoptet a hledáním produktu, stránka produktu s výběrem
    obrázku, který se změní na video, pořadím a úpravami; hromadné převzetí
    parametrů video_N. Widget: miniatura -> video ve vlastním lightboxu
    (YouTube nocookie, Vimeo, <video>), veřejný token, gate přes doplněk.
  171. 2026-10-02 c50878cc
    feat(dashboard): editace názvu a domény e-shopu z přehledu e-shopů
    V god-mód přehledu „E-shopy a klienti“ jde název i doména měnit přímo
    v řádku tabulky; totéž v kartě Základní údaje na detailu e-shopu.
    Nový superadmin endpoint admin_shop_set_identity (ShopIdentityForm,
    doména volitelná, next redirect jen na vlastní host).
    
    Claude-Session: https://claude.ai/code/session_01Y1y1G33VRJafVHai5hvVGp
  172. 2026-10-01 3d367c13
    fix(marketplaces): diagnostika Allegra (dřív 404 — adaptér bez feedu)
    Diagnostika počítala jen s feed formáty; API-only adaptér má jedinou
    diagnostiku pod prázdným klíčem. Allegro hlásí stav připojení, chybějící
    cenník dopravy/podmínky/GPSR a per-produkt EAN, kategorii a cenu.
    
    Claude-Session: https://claude.ai/code/session_01UYpK6CTFSn3ePLmNdDAnda
  173. 2026-10-01 cc613efc
    feat(bridge): odkaz z pozvánky odeslaný adminem platí 48 hodin
    Pozvaný často otevře e-mail až druhý den — 15 minut nestačilo. Odkaz
    z pozvánky (admin_invite_send_link) nese příznak a platí 48 h; odkaz, o který
    si řekne kdokoli z e-shopu (bridge_go), zůstává 15 min. Oba jednorázové
    (váží se na last_login). E-mail uvádí skutečnou platnost.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  174. 2026-10-01 e4061017
    fix(marketplaces): neutrální nadpis sloupce návrhu kategorie (i pro Allegro)
    Claude-Session: https://claude.ai/code/session_01UYpK6CTFSn3ePLmNdDAnda
  175. 2026-10-01 738cec6d
    feat(marketplaces): výběr Allegro kategorie ze stromu s ID
    Stránka mapování kategorií Allegra nabízí „Vybrat ze stromu“ (stejný
    picker jako Alza): strom Supermarket/Zdraví/Sport a cestování/Krása
    v jazyce domény, vybrat jde jen listovou kategorii. Strom se staví na
    pozadí (stovky GETů po patrech) a drží se v cache 7 dní.
    
    Claude-Session: https://claude.ai/code/session_01UYpK6CTFSn3ePLmNdDAnda
  176. 2026-10-01 be754fbb
    feat(marketplaces): návrhy kategorií jde přijmout i u Allegra
    Přijetí/odmítnutí/hromadné přijetí návrhu bylo gatované na Alza portál.
    U Allegra je přijetí jen zkopírování návrhu do potvrzeného mapování.
    
    Claude-Session: https://claude.ai/code/session_01UYpK6CTFSn3ePLmNdDAnda
  177. 2026-10-01 2d1b7e65
    feat(marketplaces): Allegro nabídka s dopravou, poprodejními podmínkami a GPSR
    Cenník dopravy, doba odeslání, podmínky vrácení/reklamace/záruka,
    výrobce a odpovědná osoba (GPSR) a typ faktury se načítají z účtu
    prodejce (po připojení nebo tlačítkem) a vybírají ve formuláři napojení.
    Jediná možnost se předvyplní. Bez nich Allegro nabídku nezveřejní.
    Formulář Allegro už neukazuje Alza-only sekce.
    
    Claude-Session: https://claude.ai/code/session_01UYpK6CTFSn3ePLmNdDAnda
  178. 2026-10-01 1b7de790
    fix(marketplaces): Allegro User-Agent s názvem registrované aplikace + scope sale:settings:read
    Allegro od 30. 6. 2026 odmítá requesty bez User-Agentu ve tvaru
    Název/verze (+URL), kde název sedí s registrovanou aplikací; hrozí
    zablokování klíče. Název se zadává v napojení (pole app_name).
    
    Claude-Session: https://claude.ai/code/session_01UYpK6CTFSn3ePLmNdDAnda
  179. 2026-10-01 39bde765
    feat(data_check): zelená fajfka u polí vyplněných z registru a ověřených
    Adresa z našeptávače, údaje firmy podle IČO, platné IČO a ověřený e-mail
    dostanou v poli vpravo zelenou fajfku (title „Ověřeno z registru“).
    Ruční přepsání hodnoty fajfku zruší.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  180. 2026-10-01 17580b9c
    feat(data_check): patička našeptávače — vpravo logo EshopMagic, vlevo drobně Mapy.com
    Logo (klobouk) a nápis EshopMagic ve stylu eshopmagic.cz (em2026 .em-logo);
    povinná atribuce Mapy.com zůstává malým logem vlevo.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  181. 2026-10-01 c465b67a
    feat(data_check): rychlejší našeptávač, záložní zdroje při výpadku, značka eshopmagic.cz
    - widget: dotaz 100 ms po klávese (dřív 250), rušení přepsaných dotazů
      (AbortController), okamžité přefiltrování posledních návrhů bez čekání
      na server; země i ze skrytého ID Shoptetu přes výběr předvolby
    - server: keep-alive session na zdroje, timeout Mapy.com 3 s
    - výpadek / vyčerpané kredity Mapy.com → RÚIAN (ARES); výpadek ARES →
      firma z VIES (plátci DPH, adresa rozparsovaná); spadlý zdroj se 60 s
      přeskakuje rovnou na zálohu
    - patička našeptávače: eshopmagic.cz + povinné logo Mapy.com s odkazem
      (podmínky atribuce Mapy.com)
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  182. 2026-10-01 2be8aedd
    feat(data_check): klíč Mapy.com jen v nastavení platformy pro superadmina
    Formulář klíče zmizel ze stránky doplňku, kterou vidí klient. Nová stránka
    /overeni-dat/nastaveni-platformy/ (jen superadmin), odkaz z Nastavení
    platformy → Rychlé akce; superadmin na stránce doplňku vidí jen stav.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  183. 2026-10-01 cf116b47
    feat(data_check): widget je pro každý e-shop připravený, API klíče spravuje jen admin
    Klient nic nezakládá — kód widgetu se zobrazí rovnou ke zkopírování,
    nastavení (funkce, další domény) je volitelné ve sbaleném bloku.
    Tlačítko na API klíče klienta vidí jen superadmin.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  184. 2026-10-01 5b69f450
    feat(data_check): widget do košíku — našeptávač adresy, firma podle IČO, kontrola e-mailu
    Skript pro objednávku/registraci e-shopu (Shoptet i obecný web):
    - našeptávač u fakturační i doručovací ulice (doručovací Shoptet má číslo
      domu zvlášť), doplní město a PSČ
    - IČO → název firmy, adresa sídla a DIČ; když e-shop nemá zapnuté pole IČ,
      widget přidá vlastní řádek ve stylu šablony; zvolená země je jen
      preference (SK firma na CZ e-shopu se najde)
    - e-mail: návrh opravy překlepu domény, upozornění na neexistující doménu
    - vlastní vyplnění nespouští další dotazy (neubírá limit)
    
    Veřejný token per e-shop (DataCheckWidget), požadavky jen z domény
    e-shopu (Origin + CORS politika doplňku), limit 60/min na IP, čerpá
    měsíční limity klienta, odpovědi jen s poli pro formulář. Správa widgetu
    a kód k vložení na stránce doplňku. Pole ověřena na skutečném HTML
    kroku 2 objednávky Shoptetu.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  185. 2026-10-01 e3339c66
    feat(data_check): doplněk Ověření dat — API firem, adres, e-mailů a telefonů pro ČR a SR
    Bezplatné zdroje za jednotným API klíčovaným public_api.TenantApiKey:
    - firmy: ARES (CZ), RPO (SK), hledání podle názvu, DIČ přes VIES;
      kontrola IČO (mod 11) bez volání zdroje
    - adresy: našeptávač a ověření — Mapy.com (klíč v DB, superadmin), bez
      klíče RÚIAN přes standardizaci ARES (jen ČR)
    - e-maily: formát, DNS MX / null MX, 9 189 jednorázových domén, role,
      návrh opravy překlepu domény
    - telefony: libphonenumber (platnost, typ, E.164, původní operátor)
    
    Měsíční limity per tenant (DataCheckPlan, superadmin navyšuje), 429 po
    vyčerpání; chybný vstup a výpadek zdroje se nepočítají. Cache odpovědí
    zdrojů (dcheck:). Katalog beta/admin_only, 299 Kč/měs globálně za klienta.
    Retence čerpání 90 dní (purge_data_check_usage).
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  186. 2026-10-01 db3a1017
    fix(upgates): sklad a dostupnost z Upgates API každých 15 min, ne z ranního XML
    Upgates generuje XML export jednou denně (Rumako 04:21), takže import
    feedu každých 15 min celý den stahoval stejný snímek a Alza po 04:35
    nedostala žádnou změnu dostupnosti (1. 10.: 93 produktů se skladem
    jiným než v Upgates). Plánovač teď po feedech a cenách načte sklad
    z API /products/simple (celý katalog ~3 s) a zapíše jen změny.
    
    Push dostupnosti čeká na zámek importu, aby neposlal mezistav, kdy feed
    editovaným produktům na chvíli vrátí ranní sklad.
    
    Claude-Session: https://claude.ai/code/session_01A8EwLXsDcGamWrsozphQh5
  187. 2026-10-01 5ece3313
    fix(deploy): deploy doplňuje běžící cron úlohy, restart kontejneru je už nesmaže
    Restart kontejneru při deployi 1a457077 smazal ručně instalované řádky
    crontabu: Tržiště (dostupnost Alze, noční PartialFull), kupóny a
    retenční purge ~3 h neběžely. Deploy teď doplní každou úlohu
    z deploy/rosti/crontab.managed.txt, jejíž příkaz v crontabu chybí;
    existující řádky nemění.
    
    Claude-Session: https://claude.ai/code/session_01XCPdVcfaYdN1ch4NX5FH5n
  188. 2026-09-30 22a779ee
    feat(marketplaces): e-shop s dostupností přes API tržiště se aktualizuje každých 15 min
    Zprávy o dostupnosti Alze chodí jen po změně dat, ta se ale z e-shopu
    (Upgates) stahovala jen jednou za hodinu. Jádro dostalo rozšiřovací bod
    import_hooks.register_sync_interval_provider (interval jen zkracuje,
    chyba providera je izolovaná); Tržiště přes něj u aktivního produkčního
    napojení s API push chce 15 min.
    
    Claude-Session: https://claude.ai/code/session_01XCPdVcfaYdN1ch4NX5FH5n
  189. 2026-09-30 657fa67f
    feat(admin): log přihlášení u superadmina ukáže štítek místo všech tenantů
    Superadmin je členem skoro všech klientů a řádek v logu pak vypisoval
    celý seznam firem.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  190. 2026-09-30 a93323c6
    chore(roadmap): EAN Rumako srovnán s Upgates
    Claude-Session: https://claude.ai/code/session_01Xxr8XJYm9x5t3Qog4RxZ9h
  191. 2026-09-30 90dd3e2d
    ci: plná testovací sada jen denně v noci nebo ručně
    Verify application (~30 min) běžela po každém pushi a zbytečně čerpala
    omezený čas CI. Teď schedule 03:00 (přeskočí, když za 25 h nepřibyl
    commit) + workflow_dispatch. Deploy dál čeká na rychlé kontroly;
    úprava ci.yml deploy nespouští.
    
    Claude-Session: https://claude.ai/code/session_01XCPdVcfaYdN1ch4NX5FH5n
  192. 2026-09-30 be43ddf8
    feat(import): primární zdroj je pro EAN zdroj pravdy
    Primární feed (Rumako = Upgates) přepíše EAN i ručně upraveným produktům;
    nová volba SourceFeed.protect_manual_ean (přepínač v mapování feedu) je
    může chránit. Po běhu se EAN, který zdroj přiřadil, vezme všem ostatním
    produktům (starý kód, produkt smazaný ve zdroji) — i když byla položka
    hash-skipnutá. Prohozené EANy se srovnají v jednom běhu, duplicitní EAN
    ve zdroji dostanou oba produkty. Sekundární zdroj dál jen doplňuje.
    
    Claude-Session: https://claude.ai/code/session_01Xxr8XJYm9x5t3Qog4RxZ9h
  193. 2026-09-30 275d5ccb
    chore(roadmap): EAN z Upgates ověřen na produkci
    Claude-Session: https://claude.ai/code/session_01Xxr8XJYm9x5t3Qog4RxZ9h
  194. 2026-09-30 556be218
    fix(import): primární zdroj přepisuje EAN i existujícím produktům
    Primární feed (Rumako = Upgates) je pro data autorita: EAN přepíše i
    rozdílnou hodnotu ze Shoptetu (175 produktů). Sekundární zdroj EAN jen
    doplňuje, ručně editované produkty zůstávají chráněné. EAN držený jiným
    produktem se nepřebírá a položka nedostane otisk, takže ji další běh
    zkusí znovu (prohozené EANy se srovnají). _HASH_VER 3.
    
    Claude-Session: https://claude.ai/code/session_01Xxr8XJYm9x5t3Qog4RxZ9h
  195. 2026-09-30 c8b33e33
    chore(roadmap): změřené zrychlení administrace po deployi
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  196. 2026-09-30 1a457077
    perf: rychlejší administrace — N+1 ceníků, diagnostika na pozadí, paralelní raqeto, gunicorn preload
    - get_available_price_lists: shop.products.only() bez 'shop' dělal dotaz
      na každý produkt (Alza napojení 1 695 dotazů / 26 s na produkci);
      stejná chyba opravena ve 3 dalších místech
    - diagnostika tržiště: stránka se otevře hned, výsledek se dopočítá přes
      fetch; cache posledního výsledku per napojení s verzí katalogu
      (feed_version), všechny formáty jedním průchodem, Přepočítat, zámek
    - raqeto: nezávislá GET čtení paralelně (detail požadavku, přehled
      klienta, karty dashboardu)
    - gunicorn --preload --max-requests: kontejner má 0,5 CPU a workery
      padají na OOM — nový worker se jen forkne místo desítek sekund startu
    - login: test povoluje odkaz na registraci (záměrná změna z e8015243)
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  197. 2026-09-30 66fa44c0
    fix(marketplaces): token AlzaTrade API se nevyžaduje opakovaně
    Alza blacklistuje klienty, kteří nerecyklují token nebo opakují špatné
    přihlašovací údaje. Test připojení bral nový token na každý klik; teď
    i on používá token z cache. Přibyl zámek proti souběžnému vyžádání,
    15min pauza po zamítnutých kredencích a otisk kredencí v cache klíči.
    
    Claude-Session: https://claude.ai/code/session_01XCPdVcfaYdN1ch4NX5FH5n
  198. 2026-09-30 6d571ee9
    fix(import): Upgates import doplní chybějící EAN existujícím produktům
    Mapovaný import při UPDATE ean nikdy nezapisoval, takže produkty založené
    Shoptet feedem bez EAN ho z Upgates nedostaly (Rumako: 386 produktů).
    Chybějící EAN se nově doplní; existující se nepřepisuje a EAN jiného
    produktu se nepřebírá. _HASH_VER 2 vynutí jednorázový plný průchod.
    
    Claude-Session: https://claude.ai/code/session_01Xxr8XJYm9x5t3Qog4RxZ9h
  199. 2026-09-30 e8015243
    feat(accounts): superadmin vytvoří pozvanému účet s heslem; odkazy na registraci a reset hesla
    U čekající pozvánky (detail klienta / e-shopu) může superadmin zadat heslo:
    založí se (nebo použije bootstrapovaný) účet, e-mail se označí ověřený
    a pozvánka se propáruje. Účet s už nastaveným heslem se nikdy nepřepisuje
    (pre-hijack ochrana). Login stránka odkazuje na allauth registraci
    a zapomenuté heslo.
    
    Claude-Session: https://claude.ai/code/session_01DUbUvYzEasuYQgREkuLcRE
  200. 2026-09-30 f87a030e
    feat(marketplaces): provoz tržiště na první pohled na stránce e-shopu
    Karta napojení ukazuje stav dostupnosti, frontu a 10 posledních zpráv
    s lidským popisem (jen pro správce). Log komunikace čte i zkrácený
    záznam dostupnosti (productCount), dřív tam byl u Alzy prázdný Obsah.
    
    Claude-Session: https://claude.ai/code/session_01XCPdVcfaYdN1ch4NX5FH5n