لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 10/03/24 في كل الموقع
-
عندما اضع ال no-repeat او center تختفي الصورة background-image:url('../imgs/bgss.jpg') no-repeat center;2 نقاط
-
اريد طريقه اطبع الفاتوره في ملف وتكون جميله بنفس التخطيط بالواجهه import sys import traceback import os import sqlite3 from PyQt6.QtWidgets import ( QApplication, QWidget, QVBoxLayout, QTableWidget, QTableWidgetItem, QLabel, QLineEdit, QPushButton, QMessageBox, QSizePolicy, QSplitter, QHBoxLayout, QFrame ) from PyQt6.QtCore import Qt from PyQt6.QtGui import QPixmap, QPainter class FieldsWidget(QWidget): def __init__(self): super().__init__() # تعيين لون الخلفية يميل إلى البياض self.setStyleSheet("background-color: #FFFFFF;") # لون خلفية مائل إلى البياض # إعداد التخطيط العام main_layout = QVBoxLayout(self) self.setLayout(main_layout) # تقسيم النافذة إلى قسمين باستخدام QSplitter splitter = QSplitter(Qt.Orientation.Horizontal) # ---- القسم الأول: الفاتورة ---- right_layout = QVBoxLayout() # إعداد الرأس باستخدام QVBoxLayout self.header_frame = QFrame() # حفظ إطار الرأس في خاصية الكائن self self.header_frame.setStyleSheet("border: 2px solid #4CAF50; border-radius: 10px; padding: 10px;") header_layout = QVBoxLayout(self.header_frame) # الشعار واسم الشركة في صف واحد company_info_layout = QHBoxLayout() self.logo_label = QLabel("الشعار") self.logo_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.logo_label.setFixedSize(80, 80) # حجم مربع مخصص للشعار self.company_info_label = QLabel("اسم الشركة: شركة مثال\nالعنوان: شارع 123، المدينة\nاسم العميل: العميل الافتراضي") self.company_info_label.setAlignment(Qt.AlignmentFlag.AlignLeft) self.company_info_label.setStyleSheet("font-size: 12px;") # إضافة الشعار ومعلومات الشركة إلى التخطيط الأفقي company_info_layout.addWidget(self.logo_label) company_info_layout.addWidget(self.company_info_label) # عنوان الفاتورة self.invoice_title_label = QLabel("فاتورة مبيعات مبسطة") self.invoice_title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.invoice_title_label.setStyleSheet("font-size: 16px; font-weight: bold;") # إضافة العناصر إلى header_layout header_layout.addLayout(company_info_layout) header_layout.addWidget(self.invoice_title_label) # إضافة الإطار إلى التخطيط العام right_layout.addWidget(self.header_frame) # إضافة header_frame إلى التخطيط العام # إعداد جدول التفاصيل self.table_details = QTableWidget() self.table_details.setColumnCount(12) self.table_details.setHorizontalHeaderLabels([ "ID", "رقم الفاتورة", "رقم الصنف", "اسم الصنف", "السعر", "الكمية", "الإجمالي الفرعي", "خصم الصنف", "نسبة الخصم", "الإجمالي بعد الخصم", "قيمة الضريبة", "الإجمالي بعد الضريبة" ]) self.table_details.setColumnHidden(0, True) # إخفاء عمود المعرف self.table_details.setColumnHidden(1, True) # إخفاء عمود رقم الفاتورة self.table_details.setLayoutDirection(Qt.LayoutDirection.RightToLeft) # تغيير الاتجاه إلى اليمين لليسار self.table_details.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) right_layout.addWidget(self.table_details) # إعداد تخطيط الحقول السفلية bottom_frame = QFrame() bottom_frame.setStyleSheet("border: 2px solid #4CAF50; border-radius: 10px; padding: 10px;") bottom_layout = QHBoxLayout(bottom_frame) # إعداد مستطيل الإجماليات totals_widget = QWidget() totals_layout = QVBoxLayout(totals_widget) self.total_label = QLabel("الإجمالي: 0") self.discount_label = QLabel("الخصم: 0") self.vat_label = QLabel("قيمة الضريبة: 0") self.total_after_vat_label = QLabel("الإجمالي بعد الضريبة: 0") # إضافة الحقول إلى التخطيط العمودي totals_layout.addWidget(self.total_label) totals_layout.addWidget(self.discount_label) totals_layout.addWidget(self.vat_label) totals_layout.addWidget(self.total_after_vat_label) # ضبط ارتفاع مستطيل الإجماليات totals_widget.setFixedHeight(170) # تقليل الارتفاع إلى 100 بكسل # إضافة مستطيل الإجماليات إلى التخطيط السفلي bottom_layout.addWidget(totals_widget) # إعداد تخطيط المدفوعات payments_widget = QWidget() payments_layout = QVBoxLayout(payments_widget) self.cash_paid_label = QLabel("المدفوع نقداً: 0") self.bank_paid_label = QLabel("المدفوع بنك: 0") # إضافة حقول المدفوعات payments_layout.addWidget(self.cash_paid_label) payments_layout.addWidget(self.bank_paid_label) # ضبط ارتفاع مستطيل المدفوعات payments_widget.setFixedHeight(100) # تقليل الارتفاع إلى 100 بكسل # إضافة مستطيل المدفوعات إلى التخطيط السفلي bottom_layout.addWidget(payments_widget) # إعداد تخطيط الباركود barcode_widget = QWidget() barcode_layout = QVBoxLayout(barcode_widget) # حقل للباركود self.barcode_label = QLabel() # لا نضع نص هنا self.barcode_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.barcode_label.setFixedSize(150, 100) # تصغير حجم الباركود إلى 150x100 بكسل # إضافة الباركود إلى التخطيط barcode_layout.addWidget(self.barcode_label) # ضبط ارتفاع مستطيل الباركود barcode_widget.setFixedHeight(120) # تقليل الارتفاع إلى 120 بكسل # إضافة مستطيل الباركود إلى التخطيط السفلي bottom_layout.addWidget(barcode_widget) # إضافة الإطار إلى التخطيط العام right_layout.addWidget(bottom_frame) # إضافة القسم الأيمن إلى QSplitter right_widget = QWidget() right_widget.setLayout(right_layout) splitter.addWidget(right_widget) # ---- القسم الثاني: البحث والفواتير ---- left_layout = QVBoxLayout() # حقل البحث self.search_input = QLineEdit(self) self.search_input.setPlaceholderText("ابحث عن رقم الفاتورة...") self.search_input.textChanged.connect(self.filter_invoices) left_layout.addWidget(self.search_input) # إعداد جدول الفواتير self.table_invoices = QTableWidget() self.table_invoices.setColumnCount(13) self.table_invoices.setHorizontalHeaderLabels([ "ID", "رقم الفاتورة", "نوع الفاتورة", "نوع الدفع", "رقم الحساب", "التاريخ", "الإجمالي", "الخصم", "الإجمالي بعد الخصم", "قيمة الضريبة", "الإجمالي بعد الضريبة", "المدفوع نقدا", "المدفوع بنك" ]) self.table_invoices.setColumnHidden(0, True) # إخفاء عمود المعرف self.table_invoices.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) left_layout.addWidget(self.table_invoices) # إضافة القسم الأيسر إلى QSplitter left_widget = QWidget() left_widget.setLayout(left_layout) splitter.addWidget(left_widget) # إضافة QSplitter إلى التخطيط الرئيسي main_layout.addWidget(splitter) # إضافة المتغيرات الجديدة self.selected_invoice_number = None self.load_data() self.table_invoices.cellClicked.connect(self.load_invoice_details) self.setWindowTitle("معاينة الفاتورة") self.setGeometry(300, 300, 1000, 600) # تعديل حجم النافذة def filter_invoices(self): search_text = self.search_input.text().lower() filtered_invoices = [invoice for invoice in self.invoices_data if search_text in invoice[1].lower()] self.populate_invoices_table(filtered_invoices) def populate_invoices_table(self, invoices): self.table_invoices.setRowCount(0) for invoice in invoices: row_position = self.table_invoices.rowCount() self.table_invoices.insertRow(row_position) for column, data in enumerate(invoice): self.table_invoices.setItem(row_position, column, QTableWidgetItem(str(data))) def load_data(self): base_dir = os.path.dirname(os.path.abspath(__file__)) # المجلد الحالي db_path = os.path.join(base_dir, '..', 'data', 'database.db') # المسار إلى قاعدة البيانات if not os.path.exists(db_path): QMessageBox.critical(self, "خطأ", "قاعدة البيانات غير موجودة.") return conn = sqlite3.connect(db_path) cursor = conn.cursor() # جلب جميع الفواتير cursor.execute("SELECT * FROM invoices") self.invoices_data = cursor.fetchall() self.populate_invoices_table(self.invoices_data) conn.close() def load_invoice_details(self, row, column): invoice_number = self.table_invoices.item(row, 1).text() # الحصول على رقم الفاتورة من العمود الثاني self.load_invoice_details_from_db(invoice_number) def load_invoice_details_from_db(self, invoice_number): base_dir = os.path.dirname(os.path.abspath(__file__)) # المجلد الحالي db_path = os.path.join(base_dir, '..', 'data', 'database.db') # المسار إلى قاعدة البيانات if not os.path.exists(db_path): QMessageBox.critical(self, "خطأ", "قاعدة البيانات غير موجودة.") return conn = sqlite3.connect(db_path) cursor = conn.cursor() # جلب تفاصيل الفاتورة من جدول invoice_details cursor.execute("SELECT * FROM invoice_details WHERE invoice_number=?", (invoice_number,)) details_data = cursor.fetchall() # عرض تفاصيل الفاتورة في الجدول self.populate_invoice_details_table(details_data) # جلب المعلومات من جدول الفواتير الأساسي (للحصول على القيم الإجمالية والمدفوعات) cursor.execute(""" SELECT total, total_discount, discount_percentage, total_after_discount, vat_value, total_after_vat, paid_cash, paid_bank FROM invoices WHERE invoice_number=? """, (invoice_number,)) invoice_data = cursor.fetchone() if invoice_data: # تحديث الحقول الإجمالية والمدفوعات self.total_label.setText(f"الإجمالي: {invoice_data[0]}") self.discount_label.setText(f"الخصم: {invoice_data[1]}") self.vat_label.setText(f"قيمة الضريبة: {invoice_data[4]}") self.total_after_vat_label.setText(f"الإجمالي بعد الضريبة: {invoice_data[5]}") self.cash_paid_label.setText(f"المدفوع نقداً: {invoice_data[6]}") self.bank_paid_label.setText(f"المدفوع بنك: {invoice_data[7]}") else: # إذا لم يتم العثور على الفاتورة، نقوم بإعادة ضبط الحقول self.total_label.setText("الإجمالي: 0") self.discount_label.setText("الخصم: 0") self.vat_label.setText("قيمة الضريبة: 0") self.total_after_vat_label.setText("الإجمالي بعد الضريبة: 0") self.cash_paid_label.setText("المدفوع نقداً: 0") self.bank_paid_label.setText("المدفوع بنك: 0") # إعداد الباركود barcode_image_path = os.path.join(base_dir, 'barcodes', f"{invoice_number}.png") if os.path.exists(barcode_image_path): pixmap = QPixmap(barcode_image_path) self.barcode_label.setPixmap(pixmap) self.barcode_label.setScaledContents(True) else: self.barcode_label.setText("لا توجد صورة باركود") conn.close() def populate_invoice_details_table(self, details): self.table_details.setRowCount(0) for detail in details: row_position = self.table_details.rowCount() self.table_details.insertRow(row_position) for column, data in enumerate(detail): self.table_details.setItem(row_position, column, QTableWidgetItem(str(data))) def exception_hook(type, value, tb): print("Unhandled Exception:") print(f"Type: {type.__name__}") print(f"Value: {value}") print("Traceback:") traceback.print_tb(tb) if __name__ == "__main__": sys.excepthook = exception_hook app = QApplication(sys.argv) window = FieldsWidget() window.show() sys.exit(app.exec())1 نقطة
-
1 نقطة
-
1 نقطة
-
1 نقطة
-
ستجد أسفل فيديو الدرس صندوق للتعليقات كما هنا يرجى طرح سؤالك أسفل الدرس وليس هنا حيث هنا قسم الأسئلة العامة ولا نقوم بإجابة الأسئلة الخاصة بمحتوى الدورة أو الدرس، وذلك لمعرفة الدرس الذي توجد به مشكلتك و لمساعدتك بشكل أفضل. أما بخصوص المشكلة فأنت تقوم بتشغيل الكود من ملف أخر غير ملف AHMED_1.py. وبما أن الملف الذي تقوم بتشغيله لا يوجد بنفس المجلد الخاص بملف AHMED_1.py وهو مجلد AHMED فإن المسار الذي سيبحث فيه عن الملف Ah.json سيكون هو المسار الخاص بالملف الذي يتم تشغيله ولهذا تحدث لك المشكلة أنه لا يعثر على على الملف . والحل هو تمرير المسار الكامل للملف كالتالي : import json import os current_dir = os.path.dirname(os.path.abspath(__file__)) file_path = os.path.join(current_dir, 'Ah.json') with open(file_path,"r") as file: data = json.load(file) print(data) وهكذا سيعمل معك سواء تم تشغيل ملف AHMED_1.py مباشرة أو تم تشغيله من أى ملف أخر.1 نقطة
-
1 نقطة
-
دوره البايثون بذكاء الاصطناعي هل بعد انتهاء فترة الدوره هل سوفا احصل على عمل او ان اكادمية حسوب تبحث لي او انا ابحث لم افهم وما هي الوظائف التي سوفا اعمل بها بعد إتقان الدوره البايثون بذكاء الاصطناعي1 نقطة
-
1 نقطة
-
ستجد على اليوتيوب شرح بخصوص ذلك، ابحث عن "شرح system design" وستجد قوائم عن system analysis and design وستجد شرح لـ System Design fundamentals في قناة أحمد الإمام وذلك كافي بالنسبة لك كمطور فما تحتاجه هو الأساسيات وفهم مراحل SDLC أو System Development Life Cycle. لكن مثل تلك المفاهيم ستجدها في المحتوى الأجبني بشكل أفضل، لكونها مفاهيم متقدمة.1 نقطة
-
ما هي اهم المواضيع التي يجب دراستها و معرفتها في data structure في لغة بايثون ؟ و هل يمكن الإكتفاء ب lists و tuples و dictionaries و sets ؟1 نقطة
-
قدمنا في مقال سابق مدخلًا إلى مفاهيم البرمجة كائنية التوجه في جافا سكريبت، وناقشنا مثالًا عن استخدام مبادئها لنمذجة مدرسين وطلاب في مدرسة. كما تحدثنا أيضًا عن إمكانية استخدام الكائنات المجردة prototype والدوال البانية constructor لتنفيذ نماذج مشابهة، والميزات المرتبطة بمفاهيم البرمجة غرضية التوجه التقليدية التي تقدمها جافا سكريبت. سنتوسع في شرح هذه الميزات في مقالنا، وعليك الانتباه إلى أن الميزات التي نشرحها ليست طريقة جديدة لدمج الكائنات classes، وأن الكائنات ستستخدم دائمًا كائنات prototype خلف الكواليس. وكل ما هنالك أنها وسيلة لتسهيل بناء سلسلة الكائنات prototype. ننصحك قبل أن تبدأ العمل معنا في هذه السلسلة أن تطلع على: أساسيات HTML. أساسيات عمل CSS أساسيات جافاسكريبت كما شرحناها في سلسلة المقالات السابقة. أساسيات البرمجة كائنية التوجه في جافا سكربت كما شرحناها في مقال أساسيات العمل مع الكائنات في جافاسكربت، ومقال الوراثة باستخدام الكائنات في جافاسكربت إضافة إلى مفاهيم أساسية في البرمجة كائنية التوجه. اﻷصناف والدوال البانية يمكن التصريح عن صنف باستخدام الكلمة المحجوزة new، إليك كيفية تعريف الصنف Personالذي تعاملنا معه في مقال سابق: class Person { name; constructor(name) { this.name = name; } introduceSelf() { console.log(`Hi! I'm ${this.name}`); } } تُصرّح الشيفرة السابقة صنفًا يُدعى person له: الخاصية name. دالة بانية لها معامل وحيد name ويُستخدم لتهيئة قيمة الخاصية name للكائن الجديد. تابع ()introduceSelf يمكنه اﻹشارة إلى خاصيات الكائن باستخدام التعليمة this. يُعد التصريح ;name في البداية اختياريًا يمكن الاستغناء عنه، إذ يُنشئ السطر ;this.name = name في الدالة البانية الخاصية name قبل تهيئتها. لكن التصريح عمومًا عن الخاصياتن يجعل الشيفرة أسهل قراءة، ويوّضح تمامًا الخاصيات التي يمتلكها الصنف. كما تستطيع أيضًا تهيئة الخاصية بقيمة افتراضية عند التصريح عنها على الشكل ; =name. عّرفت الدالة البانية باستخدام الكلمة المحجوزة constructor، وكما هو الحال عند تعريف الدالة البانية خارج الصنف،سيكون لها المهام التالية: إنشاء كائن جديد. ربط التعليمة this بالكائن الجديد كي تتمكن من استخدام هذه التعليمة في اﻹشارة إلى الكائن ضمن شيفرتها. تنفيذ شيفرة الدالة البانية. إعادة الكائن الجديد. وبالعودة إلى شيفرة التصريح عن الكائن السابق، تستطيع إنشاء واستخدام نسخة جديدة من الكائن Person كالتالي: const giles = new Person("Giles"); giles.introduceSelf(); // Hi! I'm Giles لاحظ كيف نستدعي الدالة البانية باستخدام اسم الصنف، وهو Personفي مثالنا. حذف الدالة البانية إن لم تكن هناك حاجة لتهيئة قيم الخاصيات، تستطيع إهمال الدالة البانية، وعندها ستُولَّد دالة بانية افتراضية: class Animal { sleep() { console.log("zzzzzzz"); } } const spot = new Animal(); spot.sleep(); // 'zzzzzzz' الوراثة Inheritance تسمح الوراثة بإنشاء علاقة تسلسلية بين الكائنات، حيث يمكن للكائنات الفرعية أو الأبناء sub classes وراثة الخاصيات والتوابع من الكائنات الأساسية أو الآباء base classes، وفي نفس الوقت يمكنها تعديلها أو إضافة خصائص جديدة. لنتعرف على كيفية تحقيق مفهوم الوراثة في جافا سكريبت، لذا دعونا نستخدم الصنفPerson السابق في تعريف كائن فرعي أو كائن ابن له باسمprofessor: class Professor extends Person { teaches; constructor(name, teaches) { super(name); this.teaches = teaches; } introduceSelf() { console.log( `My name is ${this.name}, and I will be your ${this.teaches} professor.`, ); } grade(paper) { const grade = Math.floor(Math.random() * (5 - 1) + 1); console.log(grade); } } نستخدم الكلمة المحجوزة extends للدالة إلى أن الصنف الجديد يرث صنفًا آخر. ويضيف الصنف Professor خاصية جديدة هي teaches لهذا نُصرّح عنها. وطالما أننا نريد تهيئة قيمة تلك الخاصية عندما ننشئ كائنًا جديدًا من الصنف Professor، لا بد من تعريف دالة بانية تأخذ معاملين هما name و teaches. وما تفعله الدالة البانية هنا، هو استدعاء الدالة البانية للصنف اﻷب باستخدام التابع ()super ممررة له المعامل name وستتكفل الدالة البانية للصنف اﻷب بضبط قيمة الخاصية name. وبعدها تهيئ الدالة البانية للصنف Professor قيمة الخاصية teaches. ملاحظة: إن كان على الدالة البانية للصنف الابن تهيئة أية قيم خاصة به، عليها أولًا التأكد من تهيئة قيم الصنف الأب لهذا الصنف من خلال استدعاء الدالة ()superوتمرير قيم أية معاملات تحتاجها. كما يتجاوز الكائن الابن التابع ()introduceSelf الخاص بالصنف اﻷب ويقدّم نسخته الخاصة، ويضيف التابع ()grade لتصحيح اﻷوراق (طبعًا في مثالنا يوزّع المدرّس علامات عشوائية على الأوراق). وهكذا سنتمكن اﻵن من إنشاء مدرسين جديد: const walsh = new Professor("Walsh", "Psychology"); walsh.introduceSelf(); // 'My name is Walsh, and I will be your Psychology professor' walsh.grade("my paper"); // some random grade التغليف Encapsulation لنرى أخيرًا كيف ننجز مفهوم التغليف في جافاسكريبت. فقد ناقشنا في مقال سابق كيف أردنا أن تكون الخاصية year للكائن Studentخاصّة، كي نتمكن من تغيير شروط التسجيل في دروس الرماية دون اﻹخلال بالشيفرة التي تستخدم الكائن Student. class Student extends Person { #year; constructor(name, year) { super(name); this.#year = year; } introduceSelf() { console.log(`Hi! I'm ${this.name}, and I'm in year ${this.#year}.`); } canStudyArchery() { return this.#year > 1; } } وما فعلناه في التصريح السابق عن الصنف، أننا جعلنا الخاصية year# خاصّة بالصنف، وهكذا سنتمكن من بناء كائن Student يستخدم الخاصية year# داخليًا، وسيعطي المتصفح رسالة خطأ إن حاولت شيفرة خارج الكائن الوصول إليها. const summers = new Student("Summers", 2); summers.introduceSelf(); // Hi! I'm Summers, and I'm in year 2. summers.canStudyArchery(); // true summers.#year; // SyntaxError ملاحظة: يمكن للشيفرة المكتوبة ضمن طرفية جافا سكريبت في المتصفح الوصول إلى الخاصيات الخاصة حتى لو كانت خارج الصنف. وهذا أمر خاص لتحرير أدوات مطوري ويب فقط من قيود الصياغة اللغوية لجافا سكريبت. إذًا ينبغي أن يبدأ اسم الخاصيات الخاصة بالمحرف # ويجب أن يُصرّح عنها داخل الصنف. التوابع الخاصة Private methods بإمكانك تحديد توابع أيضًا لتكون توابع خاصة بالصنف كما في الخاصيات، بحيث تكون تابعة للكائن نفسه ولا يمكن الوصول إليها من خارجه الدالة خاصة بالكائن نفسه، كما في الخاصيات ولا بد في هذه الحالة أن يبدأ اسم التابع بالمحرف # أيضًا، وعندها ستُستدعى فقط من قبل توابع هذا الصنف كما في المثال التالي: class Example { somePublicMethod() { this.#somePrivateMethod(); } #somePrivateMethod() { console.log("You called me?"); } } const myExample = new Example(); myExample.somePublicMethod(); // 'You called me?' myExample.#somePrivateMethod(); // SyntaxError الخلاصة ناقشنا في هذا المقال الأدوات التي تقدمها جافا سكريبت لكتابة الكائنات والتعامل معها في البرامج كائنية التوجه، وتجدر الإشارة إلى أننا لم نغطي كل النقاط المتعلقة بالموضوع في هذا المقال، لكن ما قدمناه سيكون كافيًا في البداية. ترجمة -وبتصرف- لمقال Classes in JavaScript اقرأ أيضًا المقال السابق: مفاهيم أساسية في البرمجة كائنية التوجه وتحقيقها في جافاسكريبت مدخل إلى جافاسكريبت كائنية التوجه (Object-Oriented JavaScript) لغة البرمجة بالكائنات Object-Oriented Programming برمجة الكائنات Objects في جافاسكريبت مختصر البرمجة كائنية التوجه OOP وتطبيقها في بايثون1 نقطة
-
خلال سنة 2021، وفي حدث خيب بشدة آمال محبي استكشاف الفضاء عبر العالم، أجلت وكالة ناسا NASA رحلةً طال انتظارها إلى القمر بما لا يقل عن سنة كاملة، واعترفت السلطات حينها بأن الموعد المحدد سابقًا بسنة 2024 لهبوط الإنسان على القمر لم يستند إلى دراسة جدوى تقنية. لا تقتصر التقديرات التقنية الخاطئة على مشاريع الفضاء الطموحة المدعومة من الدولة، بل يمكن أن تحدث في حالة أي مبادرة باختلاف حجمها إذا لم تُحلل بعناية. يشرح هذا المثال الجوانب التي ينبغي فحصها قبل إطلاق مشروع برمجة، حتى لا ينتهي المطاف بصاحب المشروع مثل وكالة ناسا، ويطلق مشروعه بنجاح. لماذا نحتاج إلى دراسة الجدوى؟ تقيس دراسة الجدوى أو تحليل الجدوى إمكانية تنفيذ فكرة المشروع من عدة جوانب، وتهدف إلى مساعدة كبار المديرين على تضييق نطاق الخيارات واتخاذ قرارات مستنيرة بالقبول أو بالرفض. تغطي دراسة الجدوى الشاملة خمس جوانب أساسية يرمز لها اختصارًا بالحروف اللاتينية TELOS المعروفة لدى مديري المشاريع والمنتجات. كما تُظهر الصورة أعلاه، يرمز TELOS إلى العناصر التالية: تستكشف الجدوى التقنية ما إذا كان يمكن أصلًا بناء المنتج باستخدام الأدوات والخبراء المتوفرين. تفحص الجدوى الاقتصادية تكاليف المنافع الاقتصادية للمشروع، ولتحديد الجدوى الاقتصادية، كثيرًا ما يستخدم التقدير التقريبي rough order of magnitude ROM. تتأكد الجدوى القانونية من أن المنتج يتوافق مع كل التنظيمات ولا يخترق أية قوانين، على سبيل المثال، إذا كان البرنامج يتعلق ببيانات صحية، فلا بد أن يحترم اللوائح المتعلقة بخصوصية المرضى، كما ينبغي استكشاف المخاطر القانونية الممكنة وكيف يمكن أن تؤثر على المشروع. تستكشف الجدوى العملياتية كيفية تأثير مشروع جديد على العمليات اليومية في الشركة، والإجراءات التي ينبغي تطبيقها، والجهود التي ينبغي بذلها للحفاظ عليها، فإذا افترضنا على سبيل المثال أن الأمر يتعلق بإطلاق منصة عالمية للتجارة الإلكترونية، فسنحتاج تبعًا لذلك إلى مخازن وفرق عمل محلية، وفي بعض الحالات إلى مواقع إنترنت محلية في كل بلد. وعليه، من الصعب جدًا تحقيق هذا المشروع عمليًا، وربما يكون ذلك غير ممكن إطلاقًا، رغم أن الفكرة الأولية تبدوا جذابةً اقتصاديًا وقابلةً للتطبيق تقنيًا. تعطي الجدوى الزمنية للمشروع آجالًا واقعيةً للتنفيذ وتساعد على الالتزام بها. من الواضح أنه لا يمكن إجراء جدوى شاملة دون معرفة عميقة بتخصصات المالية والتقنيات والقانون، لهذا من الطبيعي أن أغلب الدراسات البحثية تُجرى عادة من قبل مستشارين خارجيين. سنركز في هذا المقال على الجانب التقني للجدوى، رغم أننا لا نستطيع اجتزاء الجانب التقني نهائيًا من أوجه التحليل الأخرى لأنها تتحرك جميعًا للأمام معًا، وتؤثر على بعضها البعض. ما هي الجدوى التقنية؟ تُعَد الجدوى التقنية أكثر أجزاء دراسة الجدوى استهلاكًا للوقت في مجال هندسة البرامج وأكثرها تعقيدًا، لذلك، عند الحديث عن جدوى المنتجات الرقمية، فنحن نعني الجدوى التقنية بالدرجة الأولى. وكما ذكرنا سابقًا، تستكشف الجدوى التقنية إمكانية تنفيذ المشروع من زاوية تقنية، غير أنها تأخذ في الحسبان بالضرورة وتتوقف على الميزانية المتوفرة، والأطر الزمنية، والقيود القانونية، وعمليات ما بعد التطوير المتعلقة بالدعم والصيانة. تُقيَّمُ الجدوى خلال مرحلة التفكير في تطوير المنتج، وذلك بعد جمع المتطلبات المبكرة للمشروع والمتمثلة في: المتطلبات الوظيفية التي يضعها محللو الأعمال وتصف الخصائص والحلول. المتطلبات عبر الوظيفية والتي تكون مجموعةً في العادة من طرف مهندس برامج وذات صلة بخصائص النظام، مثل الأداء وقابلية التوسع وغيرها. في نهاية المطاف، إذا تأكدت جدوى المشروع، فسيعمل التحليل المنجز كقاعدة لخطة أعمال تشرح كيفية نقل المشروع من فكرة إلى حقيقة؛ أما إذا تبين أن المروع غير مُجد، فسنحتاج إلى إدارة توقعات العميل وعرض طرق بديلة لتحقيق أهداف الأعمال. لا يمكن إنجاز دراسة جدوى تقنية خلال بضعة أيام قليلة. لهذا، ولتسهيل العمل، نقسم هذه العملية إلى مرحلتين كبيرتين هما الدراسة النظرية والاختبار التطبيقي مع الأشخاص ذوي الصلة. مثال عن دراسة جدوى تقنية دراسة الجدوى التقنية هي فحص متعمق للعوامل التقنية ذات الصلة بالمشروع المعني، وتشمل أمورًا مثل: مكونات الأجهزة والبرامج. المخاطر والقيود التقنية. الملاءمة مع المكونات التقنية الأخرى. قدرات فريق الهندسة. تتضمن هذه الدراسة سلسلةً من الخطوات سنتناولها فيما يلي بتفصيل أكثر. التفكير في خيارات التنفيذ الخبراء المعنيون: مهندس برامج أو مهندس حلول solution architect، خبراء تقنيون آخرون. قبل كل شيء، ينبغي التفكير في خيارات التنفيذ، وهنا، لدينا عادةً عدة بدائل: 1. ألا نفعل شيئا قد يكشف الفحص التقني الشامل أحيانًا أن أفضل خيار في الوضعية الحالية هو الاكتفاء بالنظام الموجود، إذ يمكن أن يتضمن الابتكار أخطارًا غير مبررة وإسرافًا غير ضروري للأموال، مقابل الوعد بالحصول على القليل من التحسينات. 2. تحديد برنامج جاهز وتعديله وفقا لاحتياجاتنا غالبًا ما يكون الخيار الأكثر جدوى هو شراء أداة جاهزة ودمجها مع حل جديد، ومن ثم تعديلها لتتلاءم واحتياجاتنا، وهي ممارسة شائعة خصوصًا لدى التطبيقات واسعة الاستخدام أو البنى التحتية الكبرى والمعقدة التي تستغرق سنوات لبنائها من الصفر. ومن أمثلة المنصات الضخمة صعبة التطوير: أنظمة إدارة الممتلكات الخاصة بالفنادق أنظمة خدمة المسافرين الخاصة بشركات الخطوط الجوية أنظمة السجلات الصحية الإلكترونية الخاصة بالمستشفيات. عند اختيار منتج جاهز للاستخدام ينبغي أن نقيس بحذر الجوانب التالية: الأداء. سهولة التعلم. سهولة الإطلاق. درجة الدعم المقدمة من البائع. التوافق مع التقنيات الأساسية الأخرى التي نستخدمها. قابلية التوسع. خيارات الترخيص. ورغم أننا في هذه الحالة لا نحتاج إلى تصميم كل شيء من العدم، لا يزال هناك الكثير من العمل المطلوب، مثل تطبيق واختبار الواجهات البرمجية API، وتعديل وتحسين تعليمات البرمجة حسب الاحتياجات في حالة برنامج مفتوح المصدر، والتأكد أن كل المكونات تعمل جيدًا كوحدة متكاملة؛ إذ أن الارتباط بجهات خارجيةً يحتاج في بعض الأحيان إلى عدة أشهر ويتطلب مهارةً تقنيةً وخبرةً عميقةً في المجال. 3. بناء نظام مخصص يسمح التطوير المخصص بالاستجابة الصحيحة لكل متطلبات الشركة. وإذا كان مجديًا، أو قابلًا للإكمال ضمن المدة الزمنية والميزانية المحددتان، فالخطوة الموالية ستكون فحص التقنيات والهندسة التي ستستخدم في المشروع. تقييم بيئة الأجهزة والبرامج الخبراء المعنيون هنا هم مهندس برامج أو مهندس حلول solution architect، ومحللي الأنظمة، ومهندسي تقنيات المعلومات. سواءً اخترنا منتجًا جاهزًا أو بناء نظام مخصص، لا بد من جرد أجهزة وبرامج الشركة للإجابة على السؤال: هل يمكن إدارة المشروع باستخدام مكونات البيئة الحالية؟ تُصمم الأنظمة الحديثة مع مراعاة نمو العملاء، مما يعني التعامل مع عدد متزايد يوميًا من المعاملات، ولكن هذا لن يكون قابلًا للتحقيق دون استخدام خوادم إضافية، وشراء مساحات تخزين أكبر، واتخاذ إجراءات أخرى لزيادة قدرة البنية التحتية لتقنية المعلومات. بالنسبة لمقدمي الخدمات على مدار اليوم والأسبوع 24/7، مثل وكالات السفر ومنصات البنوك الرقمية، من المهم إلى جانب امتلاك طاقة معالجة كافية الحفاظ على الاتصال الدائم بشبكة الإنترنت؛ وفي هذه الحالة، لا بد من إنشاء تكرار للشبكة Network redundancy. وإحدى الطرق المباشرة لذلك هي تثبيت نسخة واحدة على الأقل من نظام الأجهزة الحالي، بما في ذلك الخوادم وأجهزة التوجيه وغيرها، وتشغيلها جنبًا إلى جنب. وفي حالة توقف أو فشل مسار المرور الأول، سيتولى المسار الثاني المسؤولية فورًا. زيادةً على الاتصال المستقر بشبكة الإنترنت، أصبحت الكثير من مشاريع الأعمال تعتمد كثيرًا على انتقال الشبكة المنخفض low network latency، ويرتبط ذلك ببث محتوى المستخدمين، والتجارة واللعب على الإنترنت، والتحاضر بتقنية الفيديو، وغيرها من من الخدمات التفاعلية التي يصنع فيها كل جزء من الثانية فارقًا. ومن أجل تحقيق أسرع استجابة ممكنة، ينبغي على الشركة استضافة خوادم تكون قريبةَ ما أمكن إلى المستخدمين النهائيين، ما يمكن أن يتطلب إعادة بناء البنية التحتية لتقنية المعلومات كاملةً. إحدى الطرق الواعدة لحل مشكلات طول وقت الاستجابة في مجال إنترنت الأشياء الطبية Internet of Medical Things وأنظمة إنترنت الأشياء هي حوسبة الحافة Edge computing، والتي توزع بعض وظائف الحوسبة عبر خوادم دقيقة موجودة قرب مصادر البيانات، متمثلةً في المجسات والأجهزة الذكية، بدل الاعتماد الكامل على المعالجات المركزية عن بعد، غير أن هذه التقنية لا تزال غير ناضجة، مما يجعلها تنطوي على مخاطر فشل كبيرة. إضافةً إلى ما سبق، يوجد أمران آخران ينبغي فحصهما عند مراجعة البنية التحتية لتقنية المعلومات. الأول يتمثل في نقاط الضعف الأمنية، والتي تكون ذات أهمية خاصة إذا كنا نخطط لتخزين ومعالجة معلومات حساسة، والثاني هو مقدرة الأنظمة على التفاعل مع معدات وبرامج البائعين الخارجيين. إنشاء عدة تصاميم تقنية الخبراء المعنيون هنا هم مهندس برامج أو مهندس حلول solution architect، إلى جانب الخبراء التقنيين، والمستشارين الخارجيين. تتمثل إحدى القواعد الأساسية لدراسة الجدوى في تقديم عدة تصاميم ممكنة تمثل تقريبًا النظام المزمع بناءه، وينبغي أن يقدم كل تصميم منها نظرةً شاملةَ على الحل وأن يغطي ما يلي: بنية تطبيق عالية المستوى إلى جانب الوحدات الرئيسية والتفاعلات بينها. هندسة البيانات، أو كيفية جمعها ومعالجتها وتخزينها. التكامل مع مصادر البيانات الخارجية والخدمات والأنظمة. آليات الأمان. ينبغي دراسة كل واحد من الخيارات التقنية من كل جوانب الجدوى الاقتصادية والقانونية والعملياتية والزمنية. تحليل المخاطر والقيود التقنية الخبراء المعنيون: مهندس برامج أو مهندس حلول solution architect الخبراء التقنيون. زيادةً على جوانب الجدوى، ينبغي تحليل المخاطر التقنية المرتبطة بكل خيار، والتي تشمل على سبيل المثال لا الحصر: اعتماديات الأطراف الخارجية third-party dependencies. التبني المبكرة للتقنيات غير الناضجة. استخدامات أدوات جديدة على فريق العمل. التعامل مع الأنظمة القديمة. أخيرًا، ينبغي النظر في القيود التقنية والمزالق الممكنة للتصاميم المقترحة، والمتمثلة في: قيود الأداء. التعقيدات المتعلقة بالتطبيق. مخاوف قابلية التوسع: هل سيستطيع النظام تحمل الحجم المتزايد للعمل؟ مخاوف قابلية الاستهلاك: هل سيستطيع النظام دعم وظائف وواجهات جديدة وغيرها؟ التعقيدات المتعلقة بالصيانة والدعم. عند الحصول على كل المعلومات المهمة، لا يبقى إلى اتخاذ القرار واختيار أفضل بديل. المقارنة بين الحلول المقترحة واختيار الأفضل الخبراء المعنيون: كل المختصين المذكورين في العناصر السابقة. للمقارنة بين الحلول واختيار البديل الأفضل، ربما ستحتاج الشركة إلى بناء مصفوفة قرار decision matrix. حسب الجدول الموضح أعلاه، يمكن للحل الأول أن يقدم أداءً أفضل، ولكنه سيكلف ضعف الحل الثاني، كما أنه يطرح تحديات فيما يخص الصيانة؛ مما يتطلب توظيف خبراء أو استخدام خدمات دعم خارجية، وهو ما يجعل الخيار الأول أقل جاذبيةَ من ناحية الجدوى العملياتية. والجدير بالذكر هنا هو أن مصفوفة اتخاذ القرار كما هي موضحة في مثالنا هذا ليست دليلًا إرشاديًا صارمًا ينبغي اتباعه. كتابة تقرير الجدوى الخبراء المعنيون: كل المختصين المذكورين في العناصر السابقة. تتمثل المخرجات المنطقية لدراسة الجدوى في وثيقة تدعى تقرير الجدوى، حيث يمكن أن تختلف من شركة لأخرى، إلا أنها تحتوي عادةً على الأقسام التالية: ملخص يشرح باختصار هدف المشروع، ونطاقه، والمشاكل المتوقع حلها، مع تحديد الجوانب التي لن تُغطى وتبرير ذلك. وصف الوضعية الحالية، ونحدد فيها البنية التحتية الحالية لتقنية المعلومات ومشاكلها، مع وصف الأجهزة، ونسخة نظام التشغيل، وخصائص البرنامج الذي سيُستبدل أو سيُدمج مع النظام الجديد. المتطلبات، ونعني بها تلك الوظيفية وغير الوظيفية، ويمكن وضع أو إنشاء رابط إلى الوثائق المتعلقة بتفاصيل المتطلبات البرمجية المتعلقة بالمشروع حتى يتمكن القارئ من التعرف عليها دون الحاجة إلى قراءة المزيد. تحليل البدائل الممكنة، وهنا نحدد الحلول الممكنة لتحقيق هدف المشروع، بما في ذلك الأدوات الموجودة حاليًا في السوق، وتعريف المعايير الرئيسية لتقييمها، مثل التوافق مع الأهداف المرجوة، والتكاليف، والتلاؤم القانوني، وغيرها، مع تحديد الخيارات الموصى بها وتبرير هذا الاختيار، وتحديد نقاط المنافع المتحصل عليها من خلال تطبيق البديل المختار. تحليل المخاطر الممكنة، مثل تلك المتعلقة بالقيود القانونية المرتبطة بالبديل أو بالبدائل المختارة واقتراح طرق لتجنبها. التوصيات، وتلخص أهم نتائج الدراسة ومسار التحرك الموصى به. اختبار وإثبات الجدوى بعد تحديد التصميم المختار وكتابة تقرير الجدوى، يمكن تخفيف الأخطار المحتملة أكثر من خلال اختبار فرضية المنتج عن طريق: إثبات المفهوم proof of concept. النموذج الأولي prototype. منتج الحد الأدنى MVP. ورغم أنه كثيرًا ما تختلط هذه المصطلحات على الناس -حتى أنهم يستخدمونها للتعبير عن نفس المفهوم-، إلا أن العمليات التي تقف وراءها في الحقيقة تحدث خلال مراحل مختلفة من تطوير المنتج، وتختلف من ناحية الأهداف والموارد المستخدمة. إثبات المفهوم هو الوحيد من بين هذه المفاهيم الذي يرتبط بدراسة الجدوى التقنية، ولكننا سنلقي نظرةً أيضًا على المفهومين الآخرين نظرًا لارتباطهما القريب منه، وأنهما يخدمان نفس الهدف العام والمتمثل في الاختبار قبل الاستثمار. بناء إثبات المفهوم لتأكيد الجدوى التقنية الخبراء المعنيون: محللو الأعمال، ومصممو واجهة وتجربة المستخدم. إثبات المحتوى هو عرض تقريبي للمنتج المستقبلي بناءً على دراسة الجدوى، ويكون موجهًا لأصحاب المصلحة الداخليين والمستثمرين، وهو أرخص وأسرع طريقة لتأكيد أن البديل المختار سيتبع السلوك المتوقع منه في بيئة معينة؛ وإذا تبين عكس ذلك، عندها يمكن اللجوء إلى بديل آخر أو إلغاء الفكرة من أساسها. لا توجد قواعد صارمة تحدد ما يجب أن يبدو عليه إثبات المفهوم، إذ يمكن أن يأخذ شكل وثيقة تقنية، أو عرض تقديمي، أو رسم بياني، أو مخطط هيكلي بمعنى رسم غير وظيفي لواجهة المستخدم المستقبلية، أو خليط من كل هذه الأشياء، وفي غالب الأحيان، لا يتضمن إثبات المفهوم إنشاء تعليمات البرمجة coding. لا يُعَد إعداد إثبات المفهوم أمرًا إلزاميًا، إلا أنه موصى به بشدة عند تطوير منتجات جديدة لا تتوفر حاليًا في السوق، وبعد إثبات المفهوم، يمكن الانتقال إلى الخطوة التالية في عملية الاختبار، وهي إعداد النموذج الأولي. بناء النموذج الأولي لاختبار الواجهة الخبراء المعنيون: محللو الأعمال مصممو واجهة وتجربة المستخدم مطورو البرامج. النموذج الأولي prototype هو نسخة ذات تكلفة منخفضة ووظائف محدودة وهندسة بسيطة للنظام المستقبلي، إذ تحوّل إثبات المفهوم إلى أداة عرض تفاعلية مع تصميم قابل للنقر، مما يظهر تدفق المستخدم بصريًا، ويسمح النموذج التجريبي بأن نعرض على المستثمرين والعملاء النهائيين شكل المنتج والانطباع الذي سيتركه، كما ستساعد الملاحظات من الأشخاص الحقيقيين على التحديد والإصلاح السريعين لعيوب التصميم. بناء منتج الحد الأدنى للتأكد من جدوى السوق الخبراء المعنيون: مديرو المشاريع مطورو البرامج. منتج الحد الادنى هو نسخة قابلة للتشغيل للنظام مبنية على أساس إثبات المفهوم والنموذج الأولي، إضافةً إلى تفاصيل متطلبات البرنامج، التي تحتوي على الخصائص الرئيسية التي تحل مشكلات المستخدم، وتُطلق إلى السوق من أجل جمع تعليقات الجماهير الواسعة، وقياس النتائج؛ وعند الضرورة، نُدخِل تعديلات على الخطة الأولية بناءً على ذلك. متى تكون للجدوى التقنية أهمية قصوى؟ ما هي درجة العمق التي ينبغي أن تبلغها دراسة الجدوى لضمان سير الأمور على ما يرام دون أي مفاجآت غير سارة خلال مرحلة التطوير؟ كلما زادت درجة الابتكار في المنتج، زادت كمية التفاصيل التي ينبغي للدراسة أن تشملها، فقط، وتكون دراسة الجدوى التقنية ذات أهمية خاصة في المجالات التالية: تطوير منتج جديد. إضافة خصائص جديدة. إعادة التصميم الجذري، مثل تجزئة نظام العمل الشامل إلى أنظمة خدمات مصغرة جدًا. بعبارة أخرى، هل توجد حالات لا ضرورة فيها لدراسة الجدوى التقنية؟ بالتأكيد، ويمكن إحصائها على أصابع اليد الواحدة كما يلي: عندما نكون متأكدين من جدوى المشروع. عندما يدير صاحب المشروع مشروعًا مماثلًا أو أنه أجرى دراسةً مماثلةً خلال السنوات الثلاث الماضية. المشروع صغير وبسيط، مع تأثير صغير على أهداف الشركة بعيدة المدى. في كل السيناريوهات الأخرى غير المذكورة هنا، دراسة الجدوى التقنية خطوة ضرورية لفهم ما إذا كان البرنامج جديرًا بالاستثمار أساسًا، وعدد تعديلات الخطة الأولية التي سيكلفنا إياها. ترجمة -وبتصرّف- للمقال Technical Feasibility Study in Software Engineering: Things to Consider Before Development Starts اقرأ أيضًا كيفية إجراء تحليل لجدوى المشروع ما هي إدارة المنتجات؟1 نقطة