Skip to main content

Validarea deținerii — workshop-uri și add-on-uri

Un participant poate reveni la pasul de selecție a workshop-urilor/add-on-urilor de mai multe ori (de exemplu la schimbarea abonamentului). Pluginul trebuie să se asigure că workshop-urile la care participantul e deja înscris și add-on-urile deja cumpărate rămân dezactivate în formular și nu ajung, prin nicio cale, în coș a doua oară.

Problema identificată și rezolvată

La pasul de selecție, cardurile de workshop/add-on deja deținute sunt afișate dezactivate (disabled) — corect din punct de vedere vizual. Însă trei din cele patru layout-uri de card pentru workshop-uri (modal v2, panou dual/dp, orar/tt) și toate cele patru layout-uri pentru add-on-uri mai adăugau, în plus, un <input type="hidden"> necondiționat cu același name="selected_workshops[]" / selected_addons[]. Rolul acestui input ascuns era să „forțeze" trimiterea ID-ului chiar dacă checkbox-ul dezactivat nu e trimis de browser — util pentru add-on-urile incluse gratuit cu pass-ul, dar aplicat din greșeală și pentru elementele deja deținute.

Rezultat: la fiecare trimitere a formularului (inclusiv la „Înapoi"), ID-urile workshop-urilor/add-on-urilor deja deținute erau retrimise către handler-ul de checkout, care încerca să le adauge în coș.

Nu a fost posibilă o achiziție dublă

Stratul de validare de la nivel de coș (dentisity_validate_cart_product(), agățat pe woocommerce_add_to_cart_validation și re-rulat pe woocommerce_check_cart_items) bloca deja aceste adăugări. Niciun participant nu a putut plăti a doua oară pentru același workshop sau add-on. Problema reală era experiența de utilizator: aceste încercări blocate generau notificări de eroare confuze („Ești deja înscris la acest workshop.", „Ai cumpărat deja acest add-on.") pe pagina de checkout, pentru acțiuni pe care participantul nu le inițiase.

A doua cauză, mai vizibilă pentru participant, era în calculul totalului afișat pe ecran în timpul selecției: checkbox-ul de add-on (.ao-hidden-checkbox) era bifat (checked) automat nu doar pentru add-on-urile gratuite incluse cu pass-ul, ci și pentru cele deja deținute — și purta data-price cu prețul real, nu 0. Funcția JS computeTotals() însumează toate checkbox-urile .ao-hidden-checkbox:checked, indiferent dacă sunt disabled, deci totalul afișat și eticheta „N add-on-uri selectate" includeau, în mod vizibil, prețul unor add-on-uri deja cumpărate — exact senzația de „a fost adăugat în coș" resimțită la apăsarea „Continuă la plată", chiar dacă coșul WooCommerce real nu conținea niciodată acel element a doua oară.

Fix aplicat

  • Input-ul ascuns „owned" a fost eliminat complet din cele 3 layout-uri de workshop afectate — un workshop deținut se comportă acum la fel ca în lista simplă (reg_days): doar dezactivat, fără retrimitere.
  • Condiția input-ului ascuns pentru add-on-uri a fost restrânsă să se aplice doar add-on-urilor incluse gratuit cu pass-ul ($_ao_is_free), nu și celor pur și simplu deja cumpărate ($_ao_owned).
  • Bifarea automată ($_ao_precheck) a checkbox-ului de add-on a fost, la rândul ei, restrânsă la același criteriu — doar $_ao_is_free. Un add-on deja deținut apare acum nebifat și dezactivat, la fel ca un workshop deja deținut, deci nu mai contribuie la totalul sau la numărătoarea calculate de computeTotals().
  • În bucla de adăugare în coș din handler-ul de checkout a fost adăugată o verificare explicită de deținere (workshop-uri prin dentisity_get_user_workshop_group_ids(), add-on-uri prin dentisity_user_addon_order_id()), astfel încât orice ID retrimis din greșeală este ignorat silențios, în loc să declanșeze add_to_cart() și o notificare de eroare.

Tabel comparativ al condițiilor

Pentru fiecare condiție de eligibilitate, tabelul arată unde este verificată: la pasul de selecție din formularul de înregistrare, la adăugarea efectivă în coș (woocommerce_add_to_cart_validation), și la re-validarea din checkout (woocommerce_check_cart_items).

CondițiePasul de selecție (formular înregistrare)Coș (add_to_cart)Checkout (woocommerce_check_cart_items)
Cont aprobat (identity check)Pasul e vizibil doar după aprobareWorkshop: identity_check_status === 'approved'dentisity_validate_cart_product()Stare cont re-verificată general (email neconfirmat / în așteptare / respins / informații necesare)
Pass activ deținut (pentru workshop-uri)Pass-ul e adăugat primul, în aceeași cereredentisity_user_pass_product_id() sau pass în coșWorkshop-urile sunt eliminate din coș dacă nu există niciun pass deținut/în coș
Pass activ deținut (pentru add-on-uri)La felAceeași regulă ca la workshop-uriAdd-on-urile sunt eliminate din coș dacă nu există niciun pass
Deja înscris la workshopdentisity_get_user_workshop_group_ids($uid) dezactivează checkbox-ul (is_already_enrolled)in_array($product_id, $enrolled) blochează adăugarea + notificareRe-validat pe fiecare linie din coș prin aceeași funcție; acum filtrat și proactiv înainte de add_to_cart() în handler-ul de checkout
Workshop cumpărabildentisity_workshop_purchasable === 'yes' controlează is_unavailableAceeași verificare pe metadatăEliminat din coș + notificare dacă devine necumpărabil după ce a fost adăugat
Workshop epuizat (stoc)slots_left <= 0is_soldout(nu e re-verificat explicit aici — se bazează pe gestiunea de stoc WooCommerce)(nu există re-verificare explicită per linie — se bazează pe WooCommerce)
Workshop permis pentru acest pass (mod „specific")Lista de workshop-uri permise filtrează ce e vizibil(nu e verificat în dentisity_validate_cart_product — doar în bucla de adăugare din handler-ul de checkout)(nu e re-validat per linie de coș — vezi nota de mai jos)
Add-on deja cumpăratdentisity_user_addon_order_id($uid, $id) → dezactivează + bifează implicitBlochează adăugarea + notificareRe-validat prin aceeași funcție; acum filtrat și proactiv în handler-ul de checkout
Add-on cumpărabildentisity_addon_purchasable === 'yes' (ignorat dacă e gratuit cu pass-ul)Aceeași regulă, ignorată pentru add-on-uri gratuite cu pass-ul(nu există re-verificare specifică de „cumpărabil" în hook-ul de eliminare din coș — doar cerința de pass, mai sus)
Add-on epuizat (stoc)_stock <= 0disabled(nu e re-verificat explicit — se bazează pe stocul WooCommerce)(aceeași lipsă ca la workshop-uri)
Add-on permis pentru tip participant / specializare / passFiltrează vizibilitatea în lista de add-on-uri afișatăVerificare completă, ignorată dacă add-on-ul e gratuit cu pass-ul curentAcoperit indirect — aceeași funcție de validare rulează pe fiecare linie din coș la checkout
Add-on gratuit inclus cu pass-ulBifat automat + input ascuns (singurul caz legitim rămas pentru input-ul necondiționat)Ignoră verificările de tip/specializare/passPrețul e adus la zero prin dentisity_free_product_ids() la calculul totalurilor coșului
Credite gratuite de workshop din passNu blochează selecția — se aplică ulteriorN/A la momentul adăugăriiCele mai scumpe workshop-uri eligibile sunt aduse la zero, până la epuizarea creditelor rămase
Verificare rămasă neacoperită la checkout

Modul „specific" de workshop-uri permise pentru un pass și starea de epuizare a stocului nu sunt re-validate pentru elemente aflate deja în coș la checkout — doar la momentul adăugării și în bucla proprie a formularului de înregistrare. Impactul e limitat, deoarece pluginul nu oferă nicio altă cale de adăugare a workshop-urilor în coș în afara acestui flux controlat (nu există pagină de coș vizibilă participanților). Rămâne un candidat pentru întărire ulterioară, dar nu afectează integritatea achizițiilor în fluxul actual.