3c729304db
Corrections verified against actual CMS/BackOffice/FrontOffice source code: - ClubActivationFee: 25,200,000 (not 25,000,000) - Tree depth: no limit (15 is commission calculation depth only) - IPG wallet charge: Balance=56M + Discount=56M - DayaLoan wallet charge: Balance=56M + Discount=112M (2×) - Discount: per-product MaxDiscountPercent (not fixed 30%) - VAT: 10% (ShopVAT) vs 9% (discount store PlaceOrder) - Kavenegar template: 'Afrino' only (not verify-foursat) - SMS sender: 1000001110100 - DayaLoan job: every 20min (not 15min) - Commission job: Sunday 00:05 (not Saturday) - Network tree: on User entity (not separate NetworkNode table) - UserWallets entity (not UserWalletBalances) - OTP: 6 digits, 5 attempts, 2min TTL, 60s cooldown - Removed non-existent constants (ClubJoiningPercentage, ClubActivationThreshold) - Fixed Hangfire Chatika interval: every 5min - Removed InventorySync from recurring jobs list
7.9 KiB
7.9 KiB
🛒 فروشگاه، موجودی و محصولات
منابع ادغامشده:
discount-shop-business.md,DISCOUNT-STORE-STATUS.md,package-purchase-system.md,INVENTORY-IMPROVEMENTS.md,INVENTORY-REFACTORING-STATUS.md,PRODUCT-BUNDLE-FEATURE.md,SHOP-UNIFICATION.md
آخرین بروزرسانی: اسفند ۱۴۰۴
۱. دو فروشگاه FourSat
┌─────────────────────────────────────────────────────────┐
│ FourSat Stores │
├───────────────────────┬─────────────────────────────────┤
│ Regular Store │ Discount Store │
│ (/store) │ (/discount-store) │
├───────────────────────┼─────────────────────────────────┤
│ • همه کاربران │ • فقط اعضای باشگاه │
│ • پرداخت 100% نقدی │ • پرداخت ترکیبی (تخفیف+نقد) │
│ • قیمت عادی │ • تخفیف بر اساس MaxDiscountPercent │
│ • VAT = 10% │ • VAT = 9% (در کد PlaceOrder) │
├───────────────────────┴─────────────────────────────────┤
│ Shared: Products, Categories, │
│ Inventory, ProductImages (1:1 square) │
└─────────────────────────────────────────────────────────┘
۲. Lazy Loading محصولات
۲.۱ API
// ProductService.cs
public record ProductListResult(List<ProductDto> Products, int TotalCount);
public async Task<ProductListResult> GetProductsPagedAsync(
int skip, int take,
Guid? categoryId = null,
string? search = null)
{
var request = new GetProductsRequest {
Pagination = new PaginationState { Skip = skip, Take = take },
CategoryId = categoryId?.ToString() ?? "",
SearchTerm = search ?? ""
};
// gRPC call...
}
۲.۲ پیادهسازی UI (هر دو فروشگاه)
بارگذاری اولیه: 12 محصول
│
▼
اسکرول → نمایش دکمه "نمایش محصولات بیشتر"
│
▼
کلیک → LoadMore() → skip += 12
│
▼
محصولات جدید اضافه به لیست (append)
│
▼
تکرار تا Products.Count >= TotalCount
│
▼
مخفیشدن دکمه
۳. مدیریت موجودی (Inventory)
۳.۱ بهبودهای اخیر
| بهبود | توضیح | وضعیت |
|---|---|---|
| Auto-Create | ایجاد خودکار رکورد موجودی هنگام ساخت محصول | ✅ |
| Hangfire Worker | InventorySyncJob — بررسی دورهای و ایجاد رکوردهای گمشده |
✅ |
| Autocomplete | جستجوی محصول در صفحه موجودی BackOffice با autocomplete | ✅ |
| Lazy Load | بارگذاری تنبل محصولات در هر دو فروشگاه | ✅ |
۳.۲ Entity ها
public class Inventory {
public Guid Id { get; set; }
public Guid ProductId { get; set; } // FK → Product
public int Quantity { get; set; } // موجودی فعلی
public int ReservedQuantity { get; set; } // رزروشده
public int MinimumStock { get; set; } // حداقل موجودی (هشدار)
public bool TrackInventory { get; set; } // آیا موجودی رصد شود؟
}
// فیلد کلیدی در Product:
public int MaxDiscountPercent { get; set; } // 0 تا 100 — درصد تخفیف در فروشگاه تخفیفی
۳.۳ فلوی سفارش و موجودی
سفارش جدید
│
▼
بررسی Quantity - ReservedQuantity >= OrderQuantity?
│
├─→ بله: ReservedQuantity += OrderQuantity
│ پرداخت موفق → Quantity -= OrderQuantity, Reserved -= OrderQuantity
│ پرداخت ناموفق → Reserved -= OrderQuantity (آزادسازی)
│
└─→ خیر: نمایش "موجودی کافی نیست"
۴. تصاویر محصول (۱:۱ مربعی)
AppImage Component (Shared):
• ObjectFit = Cover
• AspectRatio = 1:1 (مربع)
• Fallback = آیکون پیشفرض MudBlazor
• LazyLoading = true
اعمال در:
✅ Regular Store — ProductCard
✅ Discount Store — ProductCard
✅ BackOffice — Product List
✅ Product Detail Pages
۵. باندل محصولات (Product Bundle)
⚠️ وضعیت: طراحی کامل — پیادهسازی نشده
۵.۱ مدل داده
public class ProductBundle {
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal OriginalPrice { get; set; } // مجموع قیمت تکی
public decimal BundlePrice { get; set; } // قیمت باندل
public decimal DiscountPercentage { get; set; }
public bool IsActive { get; set; }
public List<BundleItem> Items { get; set; }
}
public class BundleItem {
public Guid ProductId { get; set; }
public int Quantity { get; set; }
}
۵.۲ فلو
ادمین → ساخت باندل → انتخاب محصولات + تعیین قیمت
│
▼
نمایش در فروشگاه با تگ "باندل"
│
▼
خرید → تمام محصولات باندل یکجا به سبد
│
▼
پرداخت → کسر موجودی هر محصول جداگانه
۶. یکپارچهسازی فروشگاهها (Shop Unification)
۶.۱ اجزای مشترک
| کامپوننت | کاربرد | وضعیت |
|---|---|---|
ProductCard |
کارت محصول (۱:۱) | ✅ مشترک |
AppImage |
نمایش تصویر | ✅ مشترک |
CategoryFilter |
فیلتر دستهبندی | ✅ مشترک |
SearchBar |
جستجوی محصول | ✅ مشترک |
LoadMoreButton |
Lazy loading | ✅ مشترک |
CartSummary |
خلاصه سبد | ⬜ جداگانه |
۶.۲ مسیرهای Navigation
فروشگاه عادی:
/store → لیست محصولات
/store/product/{id} → جزئیات محصول
/store/cart → سبد خرید
/store/checkout → پرداخت
فروشگاه تخفیفی:
/discount-store → لیست محصولات
/discount-store/product/{id} → جزئیات
/discount-store/cart → سبد (ترکیبی)
/discount-store/checkout → پرداخت ترکیبی
۷. دستهبندیها (Categories)
درختی / سلسلهمراتبی
│
├── سلامت و زیبایی
│ ├── مکملها
│ ├── مراقبت پوست
│ └── مراقبت مو
├── تغذیه
│ ├── ارگانیک
│ └── رژیمی
└── ورزشی
مدل: Category (Id, Name, ParentId?, ImageUrl, IsActive, SortOrder)
۸. خلاصه وضعیت
| ماژول | وضعیت | درصد |
|---|---|---|
| فروشگاه عادی | ✅ کامل | 100% |
| فروشگاه تخفیفی | ✅ کامل | 100% |
| Lazy Loading | ✅ کامل | 100% |
| موجودی خودکار | ✅ کامل | 100% |
| تصاویر مربعی | ✅ کامل | 100% |
| باندل محصولات | ⬜ طراحی | 30% |
| مقایسه محصول | ⬜ ایده | 0% |