using System; using System.Windows.Input; namespace FMS_ERP.Common { public class RelayCommand : ICommand { private readonly Action _execute; private readonly Func _canExecute; public RelayCommand(Action execute, Func canExecute = null) { _execute = execute; _canExecute = canExecute; } public bool CanExecute(object parameter) => _canExecute == null || _canExecute(); public void Execute(object parameter) => _execute(); public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } } } using System.Data.SqlClient; using System.Configuration; namespace FMS_ERP.Data { public static class Db { public static SqlConnection Open() { var cs = ConfigurationManager.ConnectionStrings["FmsConn"].ConnectionString; var con = new SqlConnection(cs); con.Open(); return con; } } } using System; using System.Collections.ObjectModel; using System.Configuration; using System.Data.SqlClient; using System.Linq; using System.Threading.Tasks; using FMS_ERP.Domain.Accounting; namespace FMS_ERP.Domain.Accounting { public enum AccountType { All = -1, Asset = 0, Liability = 1, Equity = 2, Revenue = 3, Expense = 4, Other = 5 } public class Account { public long AccountId { get; set; } public string Code { get; set; } public string Name { get; set; } public AccountType Type { get; set; } public bool IsActive { get; set; } = true; public long? ParentId { get; set; } public int Level { get; set; } } public class AccountNode { public AccountNode(Account acc) { Account = acc; Children = new ObservableCollection(); } public Account Account { get; } public ObservableCollection Children { get; } public string Code { get { return Account.Code; } } public string Name { get { return Account.Name; } } public AccountType Type { get { return Account.Type; } } public bool IsActive { get { return Account.IsActive; } } public decimal TotalDebit { get; set; } public decimal TotalCredit { get; set; } public decimal Balance => TotalDebit - TotalCredit; public System.Collections.Generic.IEnumerable Flatten() { yield return this; foreach (var c in Children) foreach (var x in c.Flatten()) yield return x; } public AccountNode Filter(string query) { var q = (query ?? "").ToLowerInvariant(); bool selfMatch = ((Code ?? "").ToLowerInvariant().Contains(q)) || ((Name ?? "").ToLowerInvariant().Contains(q)); var filteredChildren = Children .Select(c => c.Filter(query)) .Where(c => c != null) .ToList(); if (selfMatch || filteredChildren.Any()) { var copy = new AccountNode(new Account { AccountId = Account.AccountId, Code = Account.Code, Name = Account.Name, Type = Account.Type, IsActive = Account.IsActive }); foreach (var fc in filteredChildren) copy.Children.Add(fc); return copy; } return null; } public void ApplyFrom(Account other) { Account.Code = other.Code; Account.Name = other.Name; Account.Type = other.Type; Account.IsActive = other.IsActive; } public static async Task CreateVendorAsync(Vendor v, long? parentAccountId, AccountType accType) { using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["FmsConn"].ConnectionString)) { await con.OpenAsync().ConfigureAwait(false); using (var tx = con.BeginTransaction()) { try { // 1) أنشئ حساب فرعي في accounts string parentCode = null; using (var getParent = new SqlCommand( "SELECT code FROM dbo.accounts WHERE account_id=@pid", con, tx)) { getParent.Parameters.AddWithValue("@pid", (object)parentAccountId ?? DBNull.Value); var obj = await getParent.ExecuteScalarAsync().ConfigureAwait(false); parentCode = obj == null ? null : Convert.ToString(obj); } if (string.IsNullOrEmpty(parentCode)) throw new InvalidOperationException("Parent account not found."); // هات أكبر رقم ابن مباشر تحت الأب (آخر 3 أرقام) int baseNum = 100; // أول ابن: ..100 int nextNum; using (var cmdMax = new SqlCommand(@" SELECT MAX(CAST(RIGHT(code,3) AS INT)) FROM dbo.accounts WHERE parent_id = @pid;", con, tx)) { cmdMax.Parameters.AddWithValue("@pid", parentAccountId); var mx = await cmdMax.ExecuteScalarAsync().ConfigureAwait(false); int maxChild = (mx == DBNull.Value || mx == null) ? 0 : Convert.ToInt32(mx); nextNum = (maxChild == 0) ? baseNum : (maxChild + 1); } var childCode = parentCode + nextNum.ToString("000"); long newAccountId; using (var insAcc = new SqlCommand(@" INSERT INTO dbo.accounts(code,name,parent_id,type,is_active,level) OUTPUT INSERTED.account_id VALUES(@code,@name,@pid,@type,1, (SELECT ISNULL(level,0)+1 FROM dbo.accounts WHERE account_id=@pid));", con, tx)) { insAcc.Parameters.AddWithValue("@code", childCode); insAcc.Parameters.AddWithValue("@name", v.Name); insAcc.Parameters.AddWithValue("@pid", parentAccountId); insAcc.Parameters.AddWithValue("@type", (int)accType); newAccountId = (long)await insAcc.ExecuteScalarAsync().ConfigureAwait(false); } // 2) أضف المورد ويرتبط بالحساب long newVendorId; using (var insVend = new SqlCommand(@" INSERT INTO dbo.vendors (name,mobile,phone,email,tax_no,cr_no,address,city,notes,is_active,account_id,created_at,created_by) OUTPUT INSERTED.vendor_id VALUES(@name,@mobile,@phone,@email,@tax,@cr,@addr,@city,@notes,1,@acc,GETDATE(),NULL);", con, tx)) { insVend.Parameters.AddWithValue("@name", (object)v.Name ?? DBNull.Value); insVend.Parameters.AddWithValue("@mobile", (object)v.Mobile ?? DBNull.Value); insVend.Parameters.AddWithValue("@phone", (object)v.Phone ?? DBNull.Value); insVend.Parameters.AddWithValue("@email", (object)v.Email ?? DBNull.Value); insVend.Parameters.AddWithValue("@tax", (object)v.TaxNo ?? DBNull.Value); insVend.Parameters.AddWithValue("@cr", (object)v.CrNo ?? DBNull.Value); insVend.Parameters.AddWithValue("@addr", (object)v.Address ?? DBNull.Value); insVend.Parameters.AddWithValue("@city", (object)v.City ?? DBNull.Value); insVend.Parameters.AddWithValue("@notes", (object)v.Notes ?? DBNull.Value); insVend.Parameters.AddWithValue("@acc", newAccountId); newVendorId = (long)await insVend.ExecuteScalarAsync().ConfigureAwait(false); } tx.Commit(); return newVendorId; } catch { tx.Rollback(); throw; } } } } } } using System; namespace FMS_ERP.Domain.Accounting { public class FixedAssetCategory { // ===== Identity ===== public long CategoryId { get; set; } public string CategoryCode { get; set; } public string CategoryName { get; set; } // ===== Accounts Mapping ===== public long AssetAccountId { get; set; } public long AccumulatedDepAccountId { get; set; } public long ExpenseAccountId { get; set; } // ===== Depreciation Settings ===== public int UsefulLifeYears { get; set; } public string DepreciationMethod { get; set; } public string DepreciationFrequency { get; set; } public bool AllowLifeOverride { get; set; } // ===== Status ===== public bool IsActive { get; set; } // ===== Audit ===== public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } // ===== Notes ===== public string Notes { get; set; } public string AssetAccountName { get; set; } public string AccumulatedDepAccountName { get; set; } public string ExpenseAccountName { get; set; } } } using System; namespace FMS_ERP.Domain.Models { public class WorkOrder { public long WoId { get; set; } public string WoNo { get; set; } public string WoType { get; set; } = "SERVICE"; // حسب اتفاقنا public long CustomerId { get; set; } public string CustomerName { get; set; } // للعرض public string Status { get; set; } = "OPEN"; // OPEN/IN_PROGRESS/DONE/CLOSED/CANCELLED public DateTime CreatedAt { get; set; } public DateTime? DueDate { get; set; } public string Notes { get; set; } } } using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using FMS_ERP.Data; using FMS_ERP.Domain.Models; namespace FMS_ERP.Services { public class WorkOrderRepository { public IList GetAll(string statusFilter = null, long? customerId = null) { var list = new List(); var sql = @" SELECT wo.wo_id, wo.wo_no, wo.wo_type, wo.customer_id, c.name AS customer_name, wo.status, wo.created_at, wo.due_date, wo.notes FROM dbo.work_orders wo JOIN dbo.customers c ON c.customer_id = wo.customer_id WHERE 1=1"; if (!string.IsNullOrWhiteSpace(statusFilter)) sql += " AND wo.status = @s"; if (customerId.HasValue) sql += " AND wo.customer_id = @cid"; sql += " ORDER BY wo.wo_id DESC;"; using (var cn = Db.Open()) using (var cmd = new SqlCommand(sql, cn)) { if (!string.IsNullOrWhiteSpace(statusFilter)) cmd.Parameters.AddWithValue("@s", statusFilter); if (customerId.HasValue) cmd.Parameters.AddWithValue("@cid", customerId.Value); using (var rd = cmd.ExecuteReader()) { while (rd.Read()) { list.Add(new WorkOrder { WoId = Convert.ToInt64(rd["wo_id"]), WoNo = rd["wo_no"].ToString(), WoType = rd["wo_type"].ToString(), CustomerId = Convert.ToInt64(rd["customer_id"]), CustomerName = rd["customer_name"].ToString(), Status = rd["status"].ToString(), CreatedAt = Convert.ToDateTime(rd["created_at"]), DueDate = rd["due_date"] == DBNull.Value ? (DateTime?)null : Convert.ToDateTime(rd["due_date"]), Notes = rd["notes"]?.ToString() }); } } } return list; } public WorkOrder Insert(WorkOrder wo) { using (var cn = Db.Open()) using (var tx = cn.BeginTransaction()) { // رقم WO تلقائي var woNo = NumberingService.GetNext("WO", cn, tx); var sql = @" INSERT INTO dbo.work_orders(wo_no, wo_type, customer_id, status, due_date, notes, created_at, created_by) VALUES(@no, @type, @cid, @status, @due, @notes, SYSDATETIME(), SUSER_SNAME()); SELECT SCOPE_IDENTITY();"; long newId; using (var cmd = new SqlCommand(sql, cn, tx)) { cmd.Parameters.AddWithValue("@no", woNo); cmd.Parameters.AddWithValue("@type", string.IsNullOrWhiteSpace(wo.WoType) ? "SERVICE" : wo.WoType); cmd.Parameters.AddWithValue("@cid", wo.CustomerId); cmd.Parameters.AddWithValue("@status", string.IsNullOrWhiteSpace(wo.Status) ? "OPEN" : wo.Status); cmd.Parameters.AddWithValue("@due", (object)wo.DueDate ?? DBNull.Value); cmd.Parameters.AddWithValue("@notes", (object)wo.Notes ?? DBNull.Value); newId = Convert.ToInt64(Convert.ToDecimal(cmd.ExecuteScalar())); } tx.Commit(); wo.WoId = newId; wo.WoNo = woNo; wo.CreatedAt = DateTime.Now; // جلب اسم العميل للعرض using (var cmd = new SqlCommand("SELECT name FROM dbo.customers WHERE customer_id=@id", cn)) { cmd.Parameters.AddWithValue("@id", wo.CustomerId); wo.CustomerName = (cmd.ExecuteScalar() ?? "").ToString(); } return wo; } } public void Update(WorkOrder wo) { const string sql = @" UPDATE dbo.work_orders SET customer_id = @cid, due_date = @due, notes = @notes, updated_at = SYSDATETIME(), updated_by = SUSER_SNAME() WHERE wo_id = @id;"; using (var cn = Db.Open()) using (var cmd = new SqlCommand(sql, cn)) { cmd.Parameters.AddWithValue("@cid", wo.CustomerId); cmd.Parameters.AddWithValue("@due", (object)wo.DueDate ?? DBNull.Value); cmd.Parameters.AddWithValue("@notes", (object)wo.Notes ?? DBNull.Value); cmd.Parameters.AddWithValue("@id", wo.WoId); cmd.ExecuteNonQuery(); } } public void ChangeStatus(long woId, string newStatus) { const string sql = @" UPDATE dbo.work_orders SET status = @st, updated_at = SYSDATETIME(), updated_by = SUSER_SNAME() WHERE wo_id = @id;"; using (var cn = Db.Open()) using (var cmd = new SqlCommand(sql, cn)) { cmd.Parameters.AddWithValue("@st", newStatus); cmd.Parameters.AddWithValue("@id", woId); cmd.ExecuteNonQuery(); } } } } namespace FMS_ERP.Domain { public class AccountBalanceDto { public long AccountId { get; set; } public string Code { get; set; } public string Name { get; set; } public decimal TotalDebit { get; set; } public decimal TotalCredit { get; set; } public decimal Balance => TotalDebit - TotalCredit; } } namespace FMS_ERP.Domain { public class AccountLookup { public long AccountId { get; set; } public string Name { get; set; } } } namespace FMS_ERP.Domain { public class AccountMapping { public long? MappingId { get; set; } public string EntityType { get; set; } // SYSTEM / CUSTOMER / VENDOR / WAREHOUSE / ITEM / ... public long? EntityId { get; set; } // null للثوابت public string Purpose { get; set; } // INVENTORY_PARENT / AP_PARENT / ... public long AccountId { get; set; } public string AccountName { get; set; } // للعرض فقط (code - name) public string Note { get; set; } } } using System.Collections.Generic; using FMS_ERP.Domain.Accounting; namespace FMS_ERP.Domain { public static class AccountTypeHelper { public static readonly Dictionary DisplayNames = new Dictionary { { AccountType.Asset, "الأصول" }, { AccountType.Liability, "الخصوم" }, { AccountType.Equity, "حقوق الملكية" }, { AccountType.Revenue, "الإيرادات" }, { AccountType.Expense, "المصروفات" }, { AccountType.Other, "أخرى" } }; public static IEnumerable> DisplayList { get { return DisplayNames; } } } } namespace FMS_ERP.Domain { public class Customer { public long CustomerId { get; set; } public string Name { get; set; } // اسم العميل public string CompanyName { get; set; } // اسم الشركة public string ContactName { get; set; } // اسم المسؤول public string Mobile { get; set; } public string Phone { get; set; } public string Email { get; set; } public string TaxNo { get; set; } public string CrNo { get; set; } public string Address { get; set; } public string City { get; set; } public string Notes { get; set; } public bool IsActive { get; set; } = true; public long? AccountId { get; set; } public System.DateTime? CreatedAt { get; set; } public long? CreatedBy { get; set; } public System.DateTime? UpdatedAt { get; set; } public long? UpdatedBy { get; set; } } } using System.Collections.Generic; namespace FMS_ERP.Domain { public class GLAccount { // ===== Identity ===== public long AccountId { get; set; } // ===== Basic Info ===== public string Code { get; set; } public string Name { get; set; } // ===== Hierarchy ===== public long? ParentId { get; set; } // ===== Flags ===== public bool IsPosting { get; set; } public bool IsActive { get; set; } // ===== Tree Support (UI only) ===== public List Children { get; set; } = new List(); } } public class Item { public long ItemId { get; set; } public string Code { get; set; } public string Name { get; set; } public string Unit { get; set; } public decimal? MinQty { get; set; } public string Category { get; set; } public string Notes { get; set; } public bool IsActive { get; set; } public long? CreatedBy { get; set; } public long? UpdatedBy { get; set; } } using System; namespace FMS_ERP.Domain { /// /// سطر قيد يومية (يرتبط بجدول dbo.journal_entry_lines) /// أعمدة معتمدة من عندك: line_id, entry_id, line_no, account_id, debit, credit, description, details /// public class JournalLineRow { public long? LineId { get; set; } // line_id (PK) public long? EntryId { get; set; } // entry_id (FK -> journal_entries) public int? LineNo { get; set; } // line_no public long AccountId { get; set; } // account_id public decimal Debit { get; set; } // debit public decimal Credit { get; set; } // credit public string Description { get; set; } // description (قصير) public string Details { get; set; } // details (طويل/اختياري) } } using System; namespace FMS_ERP.Domain { public class LedgerEntryDto { public DateTime Date { get; set; } public string VoucherNo { get; set; } public string AccountCode { get; set; } public string AccountName { get; set; } public decimal Debit { get; set; } public decimal Credit { get; set; } public decimal Balance { get; set; } // رصيد تراكمي public string Description { get; set; } } } namespace FMS_ERP.Domain { public class Partner { public long PartnerId { get; set; } public string Name { get; set; } public bool IsActive { get; set; } = true; public string Notes { get; set; } public long? AccountId { get; set; } // الحساب المرتبط برأس مال الشريك } } using System; namespace FMS_ERP.Domain { public class PartnerContribution { public long ContribId { get; set; } public long PartnerId { get; set; } public DateTime ContribDate { get; set; } public decimal Amount { get; set; } public string Memo { get; set; } } } namespace FMS_ERP.Domain { public class User { public long user_id { get; set; } public string username { get; set; } public string password_hash { get; set; } public string full_name { get; set; } public string email { get; set; } public bool is_active { get; set; } public int failed_attempts { get; set; } public System.DateTime? locked_until { get; set; } } } using System; namespace FMS_ERP.Domain { public class Vendor { public long VendorId { get; set; } public string Name { get; set; } public string Mobile { get; set; } public string Phone { get; set; } public string Email { get; set; } public string TaxNo { get; set; } public string CrNo { get; set; } public string Address { get; set; } public string City { get; set; } public string Notes { get; set; } public bool IsActive { get; set; } = true; public long? AccountId { get; set; } // لأغراض التتبّع public DateTime? CreatedAt { get; set; } public long? CreatedBy { get; set; } public DateTime? UpdatedAt { get; set; } public long? UpdatedBy { get; set; } } public class VendorContract { public long ContractId { get; set; } public long VendorId { get; set; } public string Title { get; set; } public string ContractNo { get; set; } public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } public decimal? Amount { get; set; } public string Notes { get; set; } } } // Domain/Warehouse.cs using System; namespace FMS_ERP.Domain { public class Warehouse { public long WarehouseId { get; set; } public string Code { get; set; } // فريد public string Name { get; set; } public string Type { get; set; } // Main/Store/3rdParty... public string Address { get; set; } public string Notes { get; set; } public bool IsActive { get; set; } = true; public long? CreatedBy { get; set; } public long? UpdatedBy { get; set; } } }