العميل بيوصف الفلو اللي عايزه ويسأل هل موجود وصحيح ومقبول وإيه التعديلات:
«الوقتي فين نظام الشركة الحالي — لو عملت فاتورة بيع مباشرة بتعمل إذن صرف من المخزن. فالمندوب لما يبعت عرض السعر يتحوّل على طول لفاتورة بيع، يعني المحاسب أو المسؤول بيحوّله مباشر لفاتورة بيع. هل ده صحيح ومقبول؟ وإيه هي التعديلات اللي محتاجينها علشان نعمل كده؟»
الفلو المطلوب باختصار: مندوب يعمل عرض سعر → مسؤول/محاسب يحوّله لفاتورة بيع → الفاتورة عند ترحيلها تعمل إذن صرف من المخزن.
الإجابة المختصرة: الفلو ده صحيح ومقبول محاسبيًا ومخزنيًا، ومعظمه موجود ومبني بالفعل — بس فيه حلقة واحدة ناقصة في الواجهة بتخلّي الفلو مش قابل للوصول من الشاشة، + شويّة قرارات صلاحيات.
BE الـendpoint POST /api/sales/quotations/{id}/convert-to-invoice شغّال ومكتمل:
SalesInvoiceController@createFromQuotation SalesInvoiceController.php:753-834.salesperson_id)، المخزن، البنود (صنف/كمية/سعر/وحدة)، خصم البند، الضريبة، العملة، التواريخ، الملاحظات/الشروط — وبيعيد حساب الإجماليات recalculateTotals() :816.sales.invoices.create :45.BE PostSalesInvoice::execute PostSalesInvoice.php:38-117:
sales.auto_create_stock_issue_on_invoice :352.sales.auto_approve_stock_issue_on_invoice :415 (الصلاحية inventory.issues.approve).stock_issue_status SalesInvoiceResource.php:95.sales.cogs_recognition_point :62.FE ده جوهر المشكلة. في شاشة عروض الأسعار الموجود بس زر «تحويل لأمر بيع» (Convert to Order) — quotations.component.html:106-115 (يظهر للعرض «المقبول»، صلاحية sales.orders.create).
أما «تحويل لفاتورة» فالكود التحتي موجود (الخدمة sales-quotation.service.ts:98 + الـeffect quotations.effects.ts:179 + الـreducer) لكن مفيش أي زر أو شاشة بتنده عليه — كود ميت. فالمستخدم مش قادر يوصل للفلو ده من الشاشة.
توضيح للسجل: فيتشر 9122 (WP1) ضاف الـendpoint في الباك فقط — الزر في الواجهة ما اتعملش. رسالة تحقّق 9122 السابقة قالت «الزر بقى شغّال» وده كان غير دقيق؛ التذكرة دي بتكمّل الحلقة الناقصة.
BE من RolePermissionSeeder، خارج الصندوق:
| الدور | يحوّل (create) | يرحّل (post) | يعتمد الصرف (issues.approve) |
|---|---|---|---|
| المالك / super-admin | ✔ | ✔ | ✔ |
| المدير (manager) | ✘ | ✘ | ✘ |
| المحاسب (accountant) | ✘ | ✘ | ✘ |
| الكاشير / مندوب المبيعات | ✔ | ✘ | ✘ |
يعني «المحاسب/المسؤول بيحوّله» زي ما العميل بيقول — المحاسب حاليًا مالوش الصلاحية لا يحوّل ولا يرحّل. ده قرار توزيع صلاحيات محتاج تأكيد (قسم 8).
| البند | المطلوب | الموجود اليوم | اللازم يتعمل |
|---|---|---|---|
| endpoint تحويل عرض→فاتورة | موجود | ✅ كامل (BE) | لا شيء |
| زر «تحويل لفاتورة» في الواجهة | موجود | ❌ مفقود (كود ميت) | بناء الزر + التنقّل للمسودة (FE) |
| فاتورة → إذن صرف | موجود | ✅ 9121 | إعداد فقط (تفعيل التوجّلات) |
| تحويل بخطوة واحدة لفاتورة نهائية مرحّلة | ؟ (غامض) | ❌ (خطوتين: تحويل→مسودة ثم ترحيل) | قرار مالك (اختياري WP) |
| صلاحية المحاسب للتحويل/الترحيل | «المحاسب يحوّل» | ❌ المحاسب مالوش | قرار مالك (توزيع صلاحيات) |
| ترحيل خصومات الرأس المركّبة d1/d2/d3 (9123) | — | ❌ عرض السعر مفهوش الأعمدة دي | غير مطبّق (تُدخل على المسودة لو لزم) |
الخلاصة: الفجوة الحقيقية صغيرة وواضحة = زر واحد في الواجهة يربط الـendpoint الموجود بالمستخدم، + قرارَي صلاحيات/خطوة-واحدة. مفيش أي إعادة بناء للباك.
| الطبقة | الملف | التغيير |
|---|---|---|
| FE | features/sales/quotations/quotations.component.html | زر «تحويل لفاتورة» في عمود الإجراءات (للعرض المقبول، صلاحية sales.invoices.create) |
| FE | features/sales/quotations/quotations.component.ts | هاندلر يدِسباتش convertQuotationToInvoice (موجود) + تأكيد + توست + تنقّل للمسودة الجديدة |
| FE | store/sales/quotations/quotations.effects.ts:179 | غالبًا لا تغيير (الـeffect موجود)؛ ممكن نضيف تنقّل بعد النجاح |
| FE | quotation-view/quotation-view.component.html | (اختياري) نفس الزر في شاشة العرض التفصيلية |
| FE | assets/i18n/ar.json · en.json | مفتاح SALES.CONVERT_TO_INVOICE |
| BE | RolePermissionSeeder.php | (لو المالك وافق) منح المحاسب/المدير sales.invoices.create/post — منح غير كاسر |
اعتماديات موجودة تُعاد استخدامها كما هي: الـendpoint، الـPostSalesInvoice، شاشة الفاتورة (زر الترحيل + بادج «غير مصروف»)، إعدادات «التسليم» (9121).
| الحالة | السلوك الحالي |
|---|---|
| عرض سعر مش «مقبول» | الـendpoint يرفض بـ422؛ الزر لازم يظهر للمقبول فقط (زي زر «تحويل لأمر»). |
| عرض سعر اتحوّل قبل كده | يبقى Converted → يفشل شرط «المقبول» → مايتحوّلش مرتين. (نخفي الزر بعد التحويل.) |
| مندوب محصور (own-scoped) | يقدر يحوّل عروضه هو بس (الباك بيفرض ده :761). |
| عرض السعر مخزنه فاضي | الفاتورة تاخد مخزن الرأس؛ لو فاضي → يقع على الإعداد sales.default_warehouse_id عند الترحيل/الصرف. (سؤال 3.) |
| عرض فيه أصناف خدمية (بلا مخزون) | إذن الصرف بيتعامل معاها زي أي فاتورة عادية (سلوك 9121 القائم). |
| خصومات مركّبة على الرأس | عرض السعر مفهوش d1/d2/d3؛ تُدخل يدويًا على المسودة قبل الترحيل لو محتاجها. |
| الترحيل غير قابل للتراجع | لذلك الخطوتين (تحويل→مسودة، مراجعة، ترحيل) هي التصميم الأأمن — الترحيل يثبّت قيود + مخزون. |
WP1 — زر «تحويل لفاتورة» في الواجهة FE — جوهر التذكرة. زر في عمود إجراءات عرض السعر المقبول، gated sales.invoices.create، يدِسباتش الأكشن الموجود، وبعد النجاح يفتح مسودة الفاتورة الجديدة (زي ما «تحويل لأمر» بيفتح الأمر). اختبار: build أخضر + جربة حيّة عرض→مسودة.
WP2 — توزيع الصلاحيات BE (قرار مالك) — لو المالك حدّد إن المحاسب/المدير يحوّل/يرحّل، نمنحهم الصلاحيات في السيدر (منح غير كاسر). اختبار: seeder + تأكيد الأدوار.
WP3 (اختياري) — زر «تحويل وترحيل» بخطوة واحدة FE+BE — لو المالك عايز «مباشر» فعلاً: زر بيحوّل ثم يرحّل في خطوة (يستدعي endpointين موجودين بالتتابع، أو endpoint مجمّع). [FIN] لأنه بيثبّت قيود+مخزون فورًا — يحتاج مراجعة. توصيتنا: نبدأ بالخطوتين (WP1) والـone-click اختياري لو طُلب.
sales.default_warehouse_id ولا نطلب اختيار المخزن قبل التحويل؟
التغيير الوحيد في الواجهة = زر «تحويل لفاتورة» في عمود إجراءات عرض السعر المقبول (واختياريًا في شاشة العرض). تحت: قبل → بعد.