Proje: Okul.com.tr CRM · Hub: Okul.com.tr CRM — Domain
Tanım
Müşteriye (firma) bir veya birden fazla okul için ürün teklifi oluşturma akışı. CRM tarafında temelde “yeni teklif” sayfası (/sales/checkouts/basket) ve detay (/sales/checkouts/{id}).
API
POST /admin/checkouts— teklif oluşturPOST /admin/checkouts/calculate— anlık hesaplama (debounce’lu, idempotent)GET /admin/checkouts/{id}— detay (include[]: customer, creator, campaign, discountCode, customerUserTitle)
Veri Modeli
Checkout tablosunda items alanı JSON (cast: array). İçeriği CheckoutItemsDTO::toArray() ile snake_case yazılıyor:
{
total_discount, total_discount_percentage, discounted_months,
final_amount, remaining_final_amount,
initial_amount_yearly, initial_amount_monthly,
discount_details: { [key]: { label, discount_rate, amount, amount_yearly } },
school_products: [
{ school_id, school_name, school_type_id, school_type_name,
product_id, product_name, city_id, city_name,
product_price_id, yearly_product_price, monthly_product_price, quantity }
]
}
Calculate endpoint camelCase döner (DTO’yu doğrudan JSON-encode ediyor:
finalAmount,initialAmountMonthly); kayıttakiitemsise snake_case (toArray çağrılıyor). Frontend her iki formu dapick(...)helper’ıyla destekliyor.
Basket Sayfası Akışı
src/pages/checkouts/basket.tsx:
- Müşteri seç —
CustomerService.searchile combobox - Müşterinin okulları yüklenir —
SchoolService.getWithParams({"filter[customer-id]": id}). CustomerSearchRepository response’undaauth_person1_*yok; alıcı bilgileri kullanıcı tarafından doldurulur. - Ürün satırı: okul seç →
school_type_id,province_idiçinSchoolService.getByIdile detay çekilir → şehir+kademe doldurulur → product price listesi yüklenir. - Yeni okul (“Listede yok”):
school_id=null, school_name=<input>ile gönderilir, sunucu yeni okul oluşturur.
Ürün Listeleme Kuralları
src/pages/checkouts/basket.tsx::loadProductPrices filtreleri:
product.product_type === 'listing'(diğerleri: subscription, premium vs. dışlanır)product.is_free !== '1'(promosyon/free ürünler dışlanır)- Ürün adı:
productsMapRef’ten resolve edilir; backend?includes[]=productparametresini şu an yok sayıyor → bkz. 2026-05-07-product-price-includes-yoksayiliyor
Bilinen Buglar
items.school_products[].school_name/city_namemevcut okullar için null kayıt ediliyor → bkz. 2026-05-07-checkout-okul-sehir-bos
Hesaplama Tetiklemesi
Calculate endpoint’i state değişikliklerine debounce’lu reaksiyon vermeli; handler içinden değil, state’i izleyen useEffect’ten tetikle → bkz. debounced-calculate-useeffect
Related
- satis-kampanyasi-modeli — campaign_id, discount_code_id alanları
- okul-entity — School entity, customer_id ilişkisi
- 2026-05-07-checkout-okul-sehir-bos
- 2026-05-07-product-price-includes-yoksayiliyor
- debounced-calculate-useeffect