ايمن ميلاد نشر 19 يناير أرسل تقرير نشر 19 يناير السلام عليكم استفسار بخصوص حفظ فاتورة في حالة يعمل علي نظام أكثر من شخص بحيث يتم اصدار فاتورة أكثر من شخص في وقت واحد اعتماد رصيد صنف علي حركة صنف وليس تحديث مخزن بعد كل عملية بيع او تعديل فاتورة او فاتورة ترجيع هل كود صحيح بالنسبة للنظام يعمل عليه اكثر من شخص 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); } } 1 اقتباس
0 Mustafa Suleiman نشر 19 يناير أرسل تقرير نشر 19 يناير منطق الكود الحالي غير مناسب للعمل في بيئة متعددة المستخدمين، فأنت تعتمد على توليد رقم الفاتورة وعرضه في 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); } } 1 اقتباس
0 ايمن ميلاد نشر 19 يناير الكاتب أرسل تقرير نشر 19 يناير في كل عمليات إدخال تكون هكدا بالنسبة لتوليد رقم فاتورة اقتباس
0 ايمن ميلاد نشر 21 يناير الكاتب أرسل تقرير نشر 21 يناير ممكن توضيح اكثر من حيث تعديل علي جدول مخزن اقتباس
السؤال
ايمن ميلاد
السلام عليكم
استفسار بخصوص حفظ فاتورة في حالة يعمل علي نظام أكثر من شخص بحيث يتم اصدار فاتورة أكثر من شخص في وقت واحد
اعتماد رصيد صنف علي حركة صنف وليس تحديث مخزن بعد كل عملية بيع او تعديل فاتورة او فاتورة ترجيع
هل كود صحيح بالنسبة للنظام يعمل عليه اكثر من شخص
3 أجوبة على هذا السؤال
Recommended Posts
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.