defmodule DaProductAppWeb.TransactionsController do use DaProductAppWeb, :controller require Logger alias DaProductApp.Groups.Group alias DaProductApp.Brands.Brand alias DaProductApp.Stores.Store alias DaProductApp.PosTerminals.PosTerminal alias DaProductApp.Transactions.Transaction alias DaProductApp.Repo import Ecto.Query def get_transactions(conn, %{"Code" => code} = _params) do Logger.info("Fetching transactions for Code: #{code}") query = from g in Group, where: g.code == ^code, join: b in Brand, on: b.group_id == g.id, join: s in Store, on: s.brand_id == b.id, join: pt in PosTerminal, on: pt.store_id == s.id, join: t in Transaction, on: fragment("? COLLATE utf8mb4_unicode_ci = ? COLLATE utf8mb4_unicode_ci", t.device_id, pt.serial_number), order_by: [desc: t.inserted_at], select: %{ id: t.id, email: t.email, user_id: t.user_id, payment_status: t.status, transaction_amount: t.transaction_amount, transaction_ref_number: t.transaction_ref_number, m_ref_num: t.m_ref_num, name: t.name, provider_id: t.provider_id, device_id: t.device_id, merchant_id: t.merchant_id, additional_data: t.additional_data, payment_reference_id: t.payment_reference_id, payload: t.payload, transaction_id: t.transaction_id, settlement_date_time: t.settlement_date_time, inserted_at: t.inserted_at, updated_at: t.updated_at, patient_name: t.patient_name, uhid: t.uhid, mobile_no: t.mobile_no, processing_id: t.processing_id, pay_mode: t.pay_mode, location_id: t.location_id, transaction_location: t.transaction_location } case Repo.all(query) do [] -> conn |> put_status(:not_found) |> json(%{ status: "error", message: "No transactions found for the provided Code", data: %{ transactions: [] } }) transactions -> conn |> put_status(:ok) |> json(%{ status: "success", message: "Transactions retrieved successfully", data: %{ transactions: transactions } }) end end def get_transactions(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", message: "Code is required", data: %{ transactions: [] } }) end def get_device_ids(conn, %{"Code" => code} = _params) do Logger.info("Fetching device IDs for Code: #{code}") query = from g in Group, where: g.code == ^code, join: b in Brand, on: b.group_id == g.id, join: s in Store, on: s.brand_id == b.id, join: pt in PosTerminal, on: pt.store_id == s.id, select: pt.serial_number case Repo.all(query) do [] -> conn |> put_status(:not_found) |> json(%{ status: "error", message: "No devices found for the provided Code", data: %{ device_ids: [] } }) device_ids -> conn |> put_status(:ok) |> json(%{ status: "success", message: "Device IDs retrieved successfully", data: %{ device_ids: device_ids } }) end end def get_device_ids(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", message: "Code is required", data: %{ device_ids: [] } }) end def get_store_details(conn, %{"merchantRefId" => code} = _params) do Logger.info("Fetching all store details for Code: #{code}") query = from g in Group, where: g.code == ^code, join: b in Brand, on: b.group_id == g.id, join: s in Store, on: s.brand_id == b.id, join: a in DaProductApp.Addresses.Address, on: a.id == s.address_id, select: %{ store_id: s.id, store_name: s.name, store_code: s.code, store_neo_merchant_id: s.neo_merchant_id, address_id: s.address_id, brand_id: s.brand_id, brand_name: b.name, brand_code: b.code, created_at: s.inserted_at, city: a.city, country: a.country, state: a.state, zip_code: a.zipcode } stores = Repo.all(query) Logger.info("Found #{length(stores)} stores for Code: #{code}") case stores do [] -> conn |> put_status(:not_found) |> json(%{ status: "error", message: "No stores found for the provided merchantRefId", data: %{ stores: [] } }) stores -> conn |> put_status(:ok) |> json(%{ status: "success", message: "#{length(stores)} store(s) retrieved successfully", data: %{ stores: stores } }) end end def get_store_details(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", message: "merchantRefId is required", data: %{ stores: [] } }) end def get_device_details(conn, %{"merchantRefId" => code} = _params) do Logger.info("Fetching device details for Code: #{code}") query = from g in Group, where: g.code == ^code, join: b in Brand, on: b.group_id == g.id, join: s in Store, on: s.brand_id == b.id, join: pt in PosTerminal, on: pt.store_id == s.id, select: %{ device_id: pt.id, serial_number: pt.serial_number, name: pt.name, terminal_id: pt.terminal_id, device_type: pt.device_type, store: %{ id: s.id, name: s.name, code: s.code }, brand: %{ id: b.id, name: b.name, code: b.code }, providers: pt.provider_id, inserted_at: pt.inserted_at, updated_at: pt.updated_at } devices = Repo.all(query) Logger.info("Found #{length(devices)} devices for Code: #{code}") case devices do [] -> conn |> put_status(:not_found) |> json(%{ status: "error", message: "No devices found for the provided merchantRefId", data: %{ devices: [] } }) devices -> conn |> put_status(:ok) |> json(%{ status: "success", message: "#{length(devices)} device(s) retrieved successfully", data: %{ devices: devices } }) end end def get_device_details(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", message: "merchantRefId is required", data: %{ devices: [] } }) end def get_transaction_by_email(conn, %{"email" => email} = _params) do Logger.info("Fetching transaction details for email: #{email}") query = from t in Transaction, where: t.email == ^email, order_by: [desc: t.inserted_at], select: %{ id: t.id, email: t.email, user_id: t.user_id, payment_status: t.status, transaction_amount: t.transaction_amount, transaction_ref_number: t.transaction_ref_number, m_ref_num: t.m_ref_num, name: t.name, provider_id: t.provider_id, device_id: t.device_id, merchant_id: t.merchant_id, additional_data: t.additional_data, payment_reference_id: t.payment_reference_id, payload: t.payload, transaction_id: t.transaction_id, settlement_date_time: t.settlement_date_time, patient_name: t.patient_name, uhid: t.uhid, mobile_no: t.mobile_no, processing_id: t.processing_id, pay_mode: t.pay_mode, location_id: t.location_id, transaction_location: t.transaction_location, created_at: t.inserted_at, updated_at: t.updated_at } case Repo.all(query) do [] -> conn |> put_status(:not_found) |> json(%{ status: "error", message: "No transactions found for the provided email", data: %{ transactions: [] } }) transactions -> # Convert created_at to UAE time transactions = Enum.map(transactions, fn tx -> tx |> Map.update!(:created_at, &to_uae_datetime/1) # Optionally, also convert updated_at: |> Map.update!(:updated_at, &to_uae_datetime/1) end) conn |> put_status(:ok) |> json(%{ status: "success", count: length(transactions), message: "#{length(transactions)} transaction(s) retrieved successfully", data: %{ transactions: transactions } }) end end def get_transaction_by_email(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", message: "Email is required", data: %{ transactions: [] } }) end def get_custom_log_from_txn_id(conn, %{"transaction_id" => transaction_id} = _params) do Logger.info("Fetching custom event log for transaction_id: #{transaction_id}") query = from cel in DaProductApp.Activity.CustomEventLog, where: cel.transaction_id == ^transaction_id, order_by: [desc: cel.id], select: %{ id: cel.id, transaction_id: cel.transaction_id, event_name: cel.event_name, event_value: cel.event_value, event_id: cel.event_id, transaction_ref_number: cel.refrence_id, payload: cel.payload, device_id: cel.device_id, created_at: cel.inserted_at, updated_at: cel.updated_at } case Repo.all(query) do [] -> conn |> put_status(:not_found) |> json(%{ status: "error", count: 0, message: "No custom event logs found for the provided transaction_id", data: %{ custom_logs: [] } }) logs -> conn |> put_status(:ok) |> json(%{ status: "success", count: length(logs), message: "#{length(logs)} custom event log(s) retrieved successfully", data: %{ custom_logs: logs } }) end end def get_custom_log_from_txn_id(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", count: 0, message: "transaction_id is required", data: %{ custom_logs: [] } }) end def get_device_by_device_id(conn, %{"device_id" => device_id} = _params) do Logger.info("Fetching device details for device_id: #{device_id}") query = from pt in PosTerminal, where: pt.serial_number == ^device_id, select: %{ device_id: pt.id, name: pt.name, serial_number: pt.serial_number, device_type: pt.device_type, terminal_id: pt.terminal_id, store_id: pt.store_id, provider_id: pt.provider_id, created_at: pt.inserted_at, updated_at: pt.updated_at } case Repo.one(query) do nil -> conn |> put_status(:not_found) |> json(%{ status: "error", message: "No device found for the provided device_id", data: %{ device: nil } }) device -> conn |> put_status(:ok) |> json(%{ status: "success", message: "Device details retrieved successfully", data: %{ device: device } }) end end def get_device_by_device_id(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", message: "device_id is required", data: %{ device: nil } }) end def get_store_by_store_id(conn, %{"store_id" => store_id} = _params) do Logger.info("Fetching store details for store_id: #{store_id}") query = from s in Store, where: s.id == ^store_id, select: %{ store_id: s.id, store_name: s.name, store_code: s.code, neo_merchant_id: s.neo_merchant_id, address_id: s.address_id, created_at: s.inserted_at, updated_at: s.updated_at } case Repo.one(query) do nil -> conn |> put_status(:not_found) |> json(%{ status: "error", message: "No store found for the provided store_id", data: %{ store: nil } }) store -> conn |> put_status(:ok) |> json(%{ status: "success", message: "Store details retrieved successfully", data: %{ store: store } }) end end def get_store_by_store_id(conn, _params) do conn |> put_status(:bad_request) |> json(%{ status: "error", message: "store_id is required", data: %{ store: nil } }) end defp to_uae_datetime(nil), do: "" defp to_uae_datetime(%NaiveDateTime{} = naive_dt) do dt_utc = DateTime.from_naive!(naive_dt, "Etc/UTC") dt_uae = DateTime.add(dt_utc, 4 * 3600, :second) "#{dt_uae.year}-#{pad2(dt_uae.month)}-#{pad2(dt_uae.day)} #{pad2(dt_uae.hour)}:#{pad2(dt_uae.minute)}:#{pad2(dt_uae.second)}" end defp to_uae_datetime(%DateTime{} = dt_utc) do dt_uae = DateTime.add(dt_utc, 4 * 3600, :second) "#{dt_uae.year}-#{pad2(dt_uae.month)}-#{pad2(dt_uae.day)} #{pad2(dt_uae.hour)}:#{pad2(dt_uae.minute)}:#{pad2(dt_uae.second)}" end defp to_uae_datetime(datetime) when is_binary(datetime) do case NaiveDateTime.from_iso8601(datetime) do {:ok, naive_dt} -> to_uae_datetime(naive_dt) _ -> datetime end end defp pad2(n) when n < 10, do: "0#{n}" defp pad2(n), do: "#{n}" end