اذهب إلى المحتوى

السؤال

نشر

السلام عليكم 

استفسار بخصوص حفظ فاتورة في حالة يعمل علي نظام أكثر من شخص بحيث يتم اصدار فاتورة أكثر من شخص في وقت واحد 

اعتماد رصيد صنف علي حركة صنف وليس تحديث مخزن بعد كل عملية بيع او تعديل فاتورة او فاتورة ترجيع 

هل كود صحيح بالنسبة للنظام يعمل عليه اكثر من شخص 

    if (comboBox1.SelectedIndex == -1)
    {
        MessageBox.Show("الرجاء إدخال اسم زبون", "تنبيه", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        return;
    }
    if (dataGridView4.Rows.Count == 0)
    {
        MessageBox.Show("الرجاء إدخال أصناف في الفاتورة", "تنبيه", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        return;
    }
    string sql = "";
    SqlDataAdapter adp;
    DataTable dt;
    SqlCommand sqlcmd;
    SqlCommandBuilder commandBuilder;
    if (Class1.sqlCon.State != ConnectionState.Open)
    {
        Class1.sqlCon.Open();
    }
    SqlTransaction trans = Class1.sqlCon.BeginTransaction();
    try
    {
        sql = "select *from Invoices ";
        sqlcmd = new SqlCommand(sql, Class1.sqlCon);
        sqlcmd.Transaction = trans;
        adp = new SqlDataAdapter(sqlcmd);
        dt = new DataTable();
        adp.Fill(dt);

        DataRow dr = dt.NewRow();
        dr["invoice_number"] = textBox1.Text;

        dr["invoice_date"] = dateTimePicker1.Value;
        dr["customer_name"] = comboBox1.Text;
        decimal total = 0;

        for (int i = 0; i <= dataGridView4.Rows.Count - 1; i++)
        {
            total += Convert.ToDecimal(dataGridView4.Rows[i].Cells[3].Value);
        }

        textBox2.Text = total.ToString("N2");
        label9.Text = Class1.NumberToWords(Convert.ToDouble(textBox2.Text), "دينار ", "درهم");

        dr["total_amount"] = textBox2.Text;
        dr["total_ar"] = label9.Text;

        dt.Rows.Add(dr);
        commandBuilder = new SqlCommandBuilder(adp);
        adp.Update(dt);
        //حركة زبون 
        string des = " مبيعات ";
        Class1.OPER_CUSTOMERS(textBox1.Text, DateTime.Today, des, total, 0, comboBox1.Text, trans, label10.Text);
        /////حفظ تفاصيل فاتورة المبيعات
        sql = "select *from InvoiceDetails ";
        sqlcmd = new SqlCommand(sql, Class1.sqlCon);
        sqlcmd.Transaction = trans;
        adp = new SqlDataAdapter(sqlcmd);
        dt = new DataTable();
        adp.Fill(dt);

        for (int i = 0; i <= dataGridView4.Rows.Count - 1; i++)
        {
            dr = dt.NewRow();
            dr["invoice_number"] = textBox1.Text;
            dr["item_name"] = dataGridView4.Rows[i].Cells[0].Value;
            dr["item_id"] = dataGridView4.Rows[i].Cells[5].Value; // نفترض أن العمود السادس يحتوي على item_id
            dr["inv_date"] = dateTimePicker1.Value;
            dr["quantity"] = dataGridView4.Rows[i].Cells[1].Value;
            dr["unit_price"] = dataGridView4.Rows[i].Cells[2].Value;
            dr["total_price"] = textBox2.Text;
            dr["store_number"] = dataGridView4.Rows[i].Cells[4].Value;
            dt.Rows.Add(dr);
            commandBuilder = new SqlCommandBuilder(adp);
            adp.Update(dt);
        }
        for (int i = 0; i <= dataGridView4.Rows.Count - 1; i++)
        {
            int item_id = Convert.ToInt32(dataGridView4.Rows[i].Cells[5].Value);
            int quantitySold = Convert.ToInt32(dataGridView4.Rows[i].Cells[1].Value);
            DateTime movementDate = dateTimePicker1.Value;
            string dayName = movementDate.ToString("dddd", new CultureInfo("ar-LY")); // اسم اليوم بالعربية
            string invoiceNumber = textBox1.Text;
            string updateInventorySql = "UPDATE inventory SET quantity = quantity - @qty WHERE item_id = @item_id";
            SqlCommand updateCmd = new SqlCommand(updateInventorySql, Class1.sqlCon, trans);
            updateCmd.Parameters.Add("@qty", quantitySold).Value = quantitySold;
            updateCmd.Parameters.Add("@item_id", item_id).Value = item_id;
            updateCmd.ExecuteNonQuery();
            /// تسجيل حركة صنف يوميه
            string type = "مبيعات";
            string insertMovementSql = @"INSERT INTO ItemDailyMovement (item_id, movement_date, day_name, quantity_sold, invoice_number,item_name,name_cus,type,invotry_num)
                         VALUES (@item_id, @date, @day, @qty, @inv,@item_name,@name_cus,@type,@invotry_num)";
            SqlCommand movementCmd = new SqlCommand(insertMovementSql, Class1.sqlCon, trans);
            movementCmd.Parameters.Add("@item_id", item_id).Value = item_id;
            movementCmd.Parameters.Add("@date", movementDate).Value = movementDate;
            movementCmd.Parameters.Add("@day", dayName).Value = dayName;
            movementCmd.Parameters.Add("@qty", quantitySold).Value = quantitySold;
            movementCmd.Parameters.Add("@inv", invoiceNumber).Value = invoiceNumber;
            movementCmd.Parameters.Add("@item_name", dataGridView4.Rows[i].Cells[0].Value.ToString()).Value = dataGridView4.Rows[i].Cells[0].Value.ToString();
            movementCmd.Parameters.Add("@name_cus", comboBox1.Text).Value = comboBox1.Text;
            movementCmd.Parameters.Add("@type", type).Value = type;
            movementCmd.Parameters.Add("@invotry_num", dataGridView4.Rows[i].Cells[4].Value.ToString()).Value = dataGridView4.Rows[i].Cells[4].Value.ToString();
            movementCmd.ExecuteNonQuery();
        }



        trans.Commit();
        MessageBox.Show("تم حفظ الفاتورة وتحديث كمية مخزن بنجاح", "تاكيد", MessageBoxButtons.OK, MessageBoxIcon.Information);
        fill_dgv3();
        fill_dgv2_inv();
        dataGridView4.Rows.Clear();
        comboBox1.SelectedIndex = -1;
        textBox2.Clear();
        textBox1.Text = "" + (Class1.CODE_GENE("Invoices", "invoice_id") + 1).ToString("");
        label9.Text = "";



    }
    catch (Exception ex)
    {
        trans.Rollback();
        MessageBox.Show("فشل الاتصال بقاعدة البيانات: " + ex.Message, "خطأ", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

 

Recommended Posts

  • 0
نشر

منطق الكود الحالي غير مناسب للعمل في بيئة متعددة المستخدمين، فأنت تعتمد على توليد رقم الفاتورة وعرضه في TextBox قبل الحفظ، ولو فتح موظف ما شاشة البيع وحصل على رقم فاتورة 105، وفتح موظف آخر الشاشة في نفس اللحظة وحصل أيضاً على 105، فأول من يحفظ سينجح، والثاني سيحصل على خطأ Primary Key Violation أو سيقوم بتحديث فاتورة زميله حسب التصميم.

لذا رقم الفاتورة يجب أن يولد لحظة الحفظ داخل قاعدة البيانات أو داخل الـ Transaction، ولا يؤخذ من الشاشة.

كذلك استخدمت SqlDataAdapter لجلب كل الفواتير Select * ثم إضافة سطر واحد وحفظ الجدول بالكامل هو أسلوب يقتل سرعة البرنامج، فتخيل لو يوجد 100 ألف فاتورة، البرنامج سيقوم بتحميلها كلها للذاكرة فقط لإضافة فاتورة واحدة.

الصحيح هو استخدام جملة INSERT INTO مباشرة عبر SqlCommand.

وفي بيئة متعددة المستخدمين وحتى المستخدم الواحد، استخدام كائن اتصال static أو عام ومشارك، أمرخطير، فقد تتداخل الـ Transactions بين مستخدمين مختلفين إن لم يتم التعامل معها بحذر شديد.

الصحيح هو إنشاء اتصال جديد new SqlConnection داخل جملة using لكل عملية حفظ.

أيضًا قمت بالخصم من المخزون، لكن لو كانت الكمية المتبقية 5، وقام موظفان ببيع 3 قطع في نفس الثانية، فالكود الحالي سيسمح بذلك ويصبح الرصيد بالسالب (-1).

وذكرت أنك تريد الاعتماد على حركة الصنف، لا مشكلة في ذلك، لكن من ناحية الأداء فلو أردت معرفة الرصيد الحالي، فلا يجب أن تقوم بجمع كل الحركات (مبيعات - مشتريات) من أول يوم للنظام في كل مرة تبيع فيها، لأن ذاك سيجعل النظام بطيئ مع الوقت.

يجب أن تحتفظ بجدول Inventory به الرصيد الحالي لسرعة الاستعلام، وجدول ItemMovement لتسجيل التاريخ للتقارير والمراجعة، وقمت بذلك بالفعل لكن طريقة التنفيذ تحتاج تعديل، بحيث جدول Inventory يحتوي الرصيد الحالي اللحظي، ويتم التعديل عليه بالزيادة والنقصان لسرعة معرفة كم يوجد الآن؟ عند فتح فاتورة البيع.

وجدول ItemDailyMovement هو سجل تاريخي لا يُحذف منه شيء، ولمعرفة رصيد الصنف بتاريخ قديم، أو مراجعة الحسابات، تقوم بجمع الحركات من ذلك الجدول.

if (comboBox1.SelectedIndex == -1)
{
    MessageBox.Show("الرجاء إدخال اسم زبون", "تنبيه", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
}
if (dataGridView4.Rows.Count == 0)
{
    MessageBox.Show("الرجاء إدخال أصناف في الفاتورة", "تنبيه", MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
}

decimal totalInvoice = 0;
foreach (DataGridViewRow row in dataGridView4.Rows)
{
    if (row.Cells[3].Value != null)
        totalInvoice += Convert.ToDecimal(row.Cells[3].Value);
}

textBox2.Text = totalInvoice.ToString("N2");
string totalAr = Class1.NumberToWords(Convert.ToDouble(textBox2.Text), "دينار ", "درهم");

using (SqlConnection con = new SqlConnection(Class1.sqlCon.ConnectionString))
{
    con.Open();
    SqlTransaction trans = con.BeginTransaction();

    try
    {
        string sqlInsertInvoice = @"INSERT INTO Invoices (invoice_date, customer_name, total_amount, total_ar) 
                                    VALUES (@date, @cus, @total, @total_ar);
                                    SELECT SCOPE_IDENTITY();";

        SqlCommand cmdInvoice = new SqlCommand(sqlInsertInvoice, con, trans);
        cmdInvoice.Parameters.AddWithValue("@date", dateTimePicker1.Value);
        cmdInvoice.Parameters.AddWithValue("@cus", comboBox1.Text);
        cmdInvoice.Parameters.AddWithValue("@total", totalInvoice);
        cmdInvoice.Parameters.AddWithValue("@total_ar", totalAr);

        object result = cmdInvoice.ExecuteScalar();
        string newInvoiceID = result.ToString();

        foreach (DataGridViewRow row in dataGridView4.Rows)
        {
            if (row.IsNewRow) continue;

            int itemId = Convert.ToInt32(row.Cells[5].Value);
            decimal qty = Convert.ToDecimal(row.Cells[1].Value);
            decimal price = Convert.ToDecimal(row.Cells[2].Value);
            decimal rowTotal = Convert.ToDecimal(row.Cells[3].Value);
            string itemName = row.Cells[0].Value.ToString();
            string storeNum = row.Cells[4].Value.ToString();

            string sqlDetails = @"INSERT INTO InvoiceDetails (invoice_number, item_name, item_id, inv_date, quantity, unit_price, total_price, store_number)
                                  VALUES (@invNo, @name, @id, @date, @qty, @price, @tot, @store)";

            SqlCommand cmdDetails = new SqlCommand(sqlDetails, con, trans);
            cmdDetails.Parameters.AddWithValue("@invNo", newInvoiceID);
            cmdDetails.Parameters.AddWithValue("@name", itemName);
            cmdDetails.Parameters.AddWithValue("@id", itemId);
            cmdDetails.Parameters.AddWithValue("@date", dateTimePicker1.Value);
            cmdDetails.Parameters.AddWithValue("@qty", qty);
            cmdDetails.Parameters.AddWithValue("@price", price);
            cmdDetails.Parameters.AddWithValue("@tot", rowTotal);
            cmdDetails.Parameters.AddWithValue("@store", storeNum);
            cmdDetails.ExecuteNonQuery();

            string sqlUpdateStock = @"UPDATE inventory SET quantity = quantity - @qty 
                                      WHERE item_id = @id AND quantity >= @qty";

            SqlCommand cmdStock = new SqlCommand(sqlUpdateStock, con, trans);
            cmdStock.Parameters.AddWithValue("@qty", qty);
            cmdStock.Parameters.AddWithValue("@id", itemId);

            int rowsAffected = cmdStock.ExecuteNonQuery();
            if (rowsAffected == 0)
            {
                throw new Exception("الكمية غير متوفرة حالياً للصنف: " + itemName);
            }

            string dayName = dateTimePicker1.Value.ToString("dddd", new CultureInfo("ar-LY"));
            string sqlMove = @"INSERT INTO ItemDailyMovement (item_id, movement_date, day_name, quantity_sold, invoice_number, item_name, name_cus, type, invotry_num)
                               VALUES (@id, @date, @day, @qty, @inv, @iname, @cus, 'مبيعات', @store)";

            SqlCommand cmdMove = new SqlCommand(sqlMove, con, trans);
            cmdMove.Parameters.AddWithValue("@id", itemId);
            cmdMove.Parameters.AddWithValue("@date", dateTimePicker1.Value);
            cmdMove.Parameters.AddWithValue("@day", dayName);
            cmdMove.Parameters.AddWithValue("@qty", qty);
            cmdMove.Parameters.AddWithValue("@inv", newInvoiceID);
            cmdMove.Parameters.AddWithValue("@iname", itemName);
            cmdMove.Parameters.AddWithValue("@cus", comboBox1.Text);
            cmdMove.Parameters.AddWithValue("@store", storeNum);
            cmdMove.ExecuteNonQuery();
        }

        Class1.OPER_CUSTOMERS(newInvoiceID, DateTime.Today, "مبيعات", totalInvoice, 0, comboBox1.Text, trans, label10.Text);

        trans.Commit();
        MessageBox.Show("تم حفظ الفاتورة رقم " + newInvoiceID + " بنجاح", "تأكيد", MessageBoxButtons.OK, MessageBoxIcon.Information);

        fill_dgv3();
        fill_dgv2_inv();
        dataGridView4.Rows.Clear();
        comboBox1.SelectedIndex = -1;
        textBox2.Clear();
        label9.Text = "";
    }
    catch (Exception ex)
    {
        trans.Rollback();
        MessageBox.Show("فشل العملية: " + ex.Message, "خطأ", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

 

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...