cover/Elixir.DaProductApp.Transactions.ReqChkTxn.html

1 defmodule DaProductApp.Transactions.ReqChkTxn do
2 @moduledoc """
3 ReqChkTxn aggregate for UPI transaction status check requests.
4
5 Stores normalized data from ReqChkTxn/RespChkTxn with transaction status information.
6 Handles both domestic and international transaction status checks.
7 """
8 use Ecto.Schema
9 import Ecto.Changeset
10
11 alias DaProductApp.Transactions.{Transaction, ReqChkTxnEvent}
12 alias DaProductApp.Partners.{Partner, Merchant}
13
14 8 schema "req_chk_txns" do
15 # Minimal persisted columns (matching migration)
16 field :msg_id, :string
17 field :org_id, :string
18 field :original_txn_id, :string
19 field :status, :string
20 field :validation_type, :string
21 field :checked_at, :utc_datetime
22 # Additional fields referenced by validations and business logic
23 # These are not persisted in the current migration and should be treated as virtual
24 # to avoid Ecto attempting to select non-existent columns from the DB.
25 field :transaction_status, :string, virtual: true
26 field :payer_addr, :string, virtual: true
27 field :payee_addr, :string, virtual: true
28 field :corridor, :string, virtual: true
29 field :fx_rate, :decimal, virtual: true
30 field :base_currency, :string, virtual: true
31 field :base_amount, :decimal, virtual: true
32
33 # Raw XML hashes for audit
34 field :req_xml_hash, :binary
35 field :resp_xml_hash, :binary
36 # Store the full parsed payload for flexibility and future indexing
37 field :payload, :map
38
39 belongs_to :transaction, Transaction, foreign_key: :transaction_id
40 belongs_to :partner, Partner, foreign_key: :partner_id
41 belongs_to :merchant, Merchant, foreign_key: :merchant_id
42
43 # Per-aggregate events
44 has_many :events, ReqChkTxnEvent, foreign_key: :req_chk_txn_id
45
46 timestamps(type: :utc_datetime)
47 end
48
49 @required ~w(msg_id org_id original_txn_id status validation_type transaction_id)a
50 @optional ~w(checked_at req_xml_hash resp_xml_hash partner_id merchant_id payload transaction_status payer_addr payee_addr corridor fx_rate base_currency base_amount)a
51
52 def changeset(struct, attrs) do
53 struct
54 |> cast(attrs, @required ++ @optional)
55 |> validate_required(@required)
56 |> validate_inclusion(:status, ~w(PENDING PROCESSED FAILED))
57 |> validate_inclusion(:validation_type, ~w(DOMESTIC INTERNATIONAL))
58 |> validate_inclusion(:transaction_status, ~w(PENDING SUCCESS FAILURE EXPIRED DEEMED REVERSED))
59 |> validate_length(:msg_id, max: 35)
60 |> validate_length(:original_txn_id, max: 35)
61 |> validate_format(:payer_addr, ~r/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+$/, message: "Invalid UPI ID format")
62 |> validate_format(:payee_addr, ~r/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+$/, message: "Invalid UPI ID format")
63 1 |> unique_constraint(:msg_id)
64 end
65
66 @doc """
67 Changeset for international transaction checks with FX validation
68 """
69 def international_changeset(struct, attrs) do
70 struct
71 |> changeset(attrs)
72 |> put_change(:validation_type, "INTERNATIONAL")
73
:-(
|> validate_international_fields()
74 end
75
76 @doc """
77 Changeset for domestic transaction checks
78 """
79 def domestic_changeset(struct, attrs) do
80 struct
81 |> changeset(attrs)
82 |> put_change(:validation_type, "DOMESTIC")
83 |> put_change(:corridor, nil)
84
:-(
|> put_change(:fx_rate, nil)
85 end
86
87 # Private validation functions
88
89 defp validate_international_fields(changeset) do
90 changeset
91 |> validate_required([:corridor])
92 |> validate_inclusion(:corridor, ~w(SINGAPORE UAE USA))
93
:-(
|> validate_currency_fields()
94 end
95
96 defp validate_currency_fields(changeset) do
97 changeset
98 |> validate_inclusion(:base_currency, ~w(USD SGD AED EUR GBP))
99 |> validate_number(:fx_rate, greater_than: 0)
100
:-(
|> validate_number(:base_amount, greater_than: 0)
101 end
102
103 @doc """
104 Get status counts for analytics
105 """
106 def status_counts do
107
:-(
%{
108 "pending" => 0,
109 "processed" => 0,
110 "failed" => 0
111 }
112 end
113
114 @doc """
115 Get validation type counts
116 """
117 def validation_type_counts do
118
:-(
%{
119 "domestic" => 0,
120 "international" => 0
121 }
122 end
123
124 @doc """
125 Get transaction status counts
126 """
127 def transaction_status_counts do
128
:-(
%{
129 "pending" => 0,
130 "success" => 0,
131 "failure" => 0,
132 "expired" => 0,
133 "deemed" => 0,
134 "reversed" => 0
135 }
136 end
137 end
Line Hits Source