using System.ComponentModel;
using trakker.Models;
namespace trakker.Data
{
///
/// Provides data access methods for the entity.
/// This class encapsulates database operations such as upsert, delete and ad-hoc
/// SQL execution for clients. It inherits from which
/// provides connection management.
///
internal class ClientData(string connectionString) : DataAccess(connectionString)
{
public BindingList Get(string? clientId = null)
{
var results = new BindingList();
string whereClause = "1 = 1";
if (clientId != null)
{
whereClause = "client_id = $client_id";
}
string sql = $@"
SELECT
client_id,
name,
company,
email,
phone,
address_street,
address_city,
address_state,
address_postal,
notes,
is_active
FROM
clients
WHERE
{whereClause}
ORDER BY
name ASC
;
";
using var conn = OpenConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = sql;
if (clientId != null)
{
cmd.Parameters.AddWithValue("$client_id", clientId);
}
using var reader = cmd.ExecuteReader();
var _var1 = reader.GetOrdinal("client_id");
var _var2 = reader.GetOrdinal("name");
var _var3 = reader.GetOrdinal("company");
var _var4 = reader.GetOrdinal("email");
var _var5 = reader.GetOrdinal("phone");
var _var6 = reader.GetOrdinal("address_street");
var _var7 = reader.GetOrdinal("address_city");
var _var8 = reader.GetOrdinal("address_state");
var _var9 = reader.GetOrdinal("address_postal");
var _var10 = reader.GetOrdinal("notes");
var _var11 = reader.GetOrdinal("is_active");
while (reader.Read())
{
results.Add(new Client
{
ClientId = reader.GetString(_var1),
Name = reader.GetString(_var2),
Company = reader.GetString(_var3),
Email = reader.GetString(_var4),
Phone = reader.GetString(_var5),
AddressStreet = reader.GetString(_var6),
AddressCity = reader.GetString(_var7),
AddressState = reader.GetString(_var8),
AddressPostal = reader.GetString(_var9),
Notes = reader.GetString(_var10),
IsActive = reader.GetString(_var11),
});
}
return results;
}
///
/// Inserts a new client record or updates an existing one (upsert) using
/// the provided model. This method executes
/// a single SQL statement inside a transaction and will commit on
/// success or roll back on failure.
///
/// The model to insert or update. Must not be null.
///
/// The SQL statement uses an ON CONFLICT clause to perform the update when a
/// matching client_id already exists. Parameter names correspond to the
/// client model property names.
///
public void Upsert(Client client)
{
const string sql = @"
INSERT INTO clients (
client_id,
name,
company,
email,
phone,
address_street,
address_city,
address_state,
address_postal,
notes,
is_active
)
VALUES (
$client_id,
$name,
$company,
$email,
$phone,
$address_street,
$address_city,
$address_state,
$address_postal,
$notes,
$is_active
)
ON CONFLICT(client_id) DO UPDATE SET
name = excluded.name,
company = excluded.company,
email = excluded.email,
phone = excluded.phone,
address_street = excluded.address_street,
address_city = excluded.address_city,
address_state = excluded.address_state,
address_postal = excluded.address_postal,
notes = excluded.notes,
is_active = excluded.is_active,
updated_at = CURRENT_TIMESTAMP;
";
using var conn = OpenConnection();
using var tx = conn.BeginTransaction();
try
{
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = tx;
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("$client_id", client.ClientId);
cmd.Parameters.AddWithValue("$name", client.Name);
cmd.Parameters.AddWithValue("$company", client.Company);
cmd.Parameters.AddWithValue("$email", client.Email);
cmd.Parameters.AddWithValue("$phone", client.Phone);
cmd.Parameters.AddWithValue("$address_street", client.AddressStreet);
cmd.Parameters.AddWithValue("$address_city", client.AddressCity);
cmd.Parameters.AddWithValue("$address_state", client.AddressState);
cmd.Parameters.AddWithValue("$address_postal", client.AddressPostal);
cmd.Parameters.AddWithValue("$notes", client.Notes);
cmd.Parameters.AddWithValue("$is_active", client.IsActive);
cmd.ExecuteNonQuery();
}
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
}
///
/// Deletes the client with the specified from the
/// database.
///
/// The identifier of the client to delete.
/// An optional integer representing any scalar value returned by the
/// command executed after deletion (if applicable). May be null.
///
/// The method executes within a transaction. The current implementation attempts
/// to read a scalar value after the delete; that value depends on surrounding
/// database triggers or commands and may be null.
///
public int? Delete(string clientId)
{
const string sql = @"
DELETE FROM
clients
WHERE
client_id = $client_id
;
";
using var conn = OpenConnection();
using var tx = conn.BeginTransaction();
int? result = 0;
try
{
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = tx;
cmd.CommandText = sql;
cmd.Parameters.AddWithValue("$client_id", clientId);
cmd.ExecuteNonQuery();
}
using var idCmd = conn.CreateCommand();
idCmd.Transaction = tx;
result = (int?)idCmd.ExecuteScalar() ;
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
return result;
}
}
}