snf_core 0.2.94 → 0.2.95

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/snf_core/auth_controller.rb +12 -2
  3. data/app/models/snf_core/account_transfer.rb +37 -0
  4. data/app/models/snf_core/item_request.rb +27 -0
  5. data/app/models/snf_core/user.rb +7 -6
  6. data/app/models/snf_core/virtual_account.rb +41 -0
  7. data/app/models/snf_core/virtual_account_transaction.rb +52 -0
  8. data/db/migrate/20250310122954_create_snf_core_virtual_accounts.rb +22 -0
  9. data/db/migrate/20250310123127_add_kyc_attributes_to_user.rb +17 -0
  10. data/db/migrate/20250310133249_create_snf_core_virtual_account_transactions.rb +18 -0
  11. data/db/migrate/20250310143604_create_snf_core_item_requests.rb +14 -0
  12. data/db/migrate/20250311000000_create_snf_core_account_transfers.rb +21 -0
  13. data/lib/snf_core/version.rb +1 -1
  14. data/spec/dummy/db/schema.rb +117 -27
  15. data/spec/dummy/log/development.log +1820 -0
  16. data/spec/dummy/log/test.log +99021 -0
  17. data/spec/dummy/tmp/storage/00/ch/00ch4m884d5bnelqgd2ld1qig6ey +0 -0
  18. data/spec/dummy/tmp/storage/0s/ba/0sbase6mg5sgich6or97cg1faxlw +0 -0
  19. data/spec/dummy/tmp/storage/0x/uy/0xuyfqa0t08zr8vfm8p123o0s34t +0 -0
  20. data/spec/dummy/tmp/storage/1o/ts/1otsbrqkd6n4qiht1wabl7fhjnt3 +0 -0
  21. data/spec/dummy/tmp/storage/1v/54/1v542i134jpb9iwbq9m1v801y0ls +0 -0
  22. data/spec/dummy/tmp/storage/1y/46/1y4617uv2nrk9fa58zfv9xyixmfk +0 -0
  23. data/spec/dummy/tmp/storage/24/kq/24kq40q48k7jry3npyah0td4uend +0 -0
  24. data/spec/dummy/tmp/storage/28/ye/28yeusf04wxopqkg0bxki7mqo9ph +0 -0
  25. data/spec/dummy/tmp/storage/2r/hv/2rhvs6uca2ozrojfuub5d4qk0ugy +0 -0
  26. data/spec/dummy/tmp/storage/2w/r1/2wr14buxnrx4cee19gj6sefnbby5 +0 -0
  27. data/spec/dummy/tmp/storage/3k/7z/3k7zlcd9fm2jwn99kinxpahrmm4u +0 -0
  28. data/spec/dummy/tmp/storage/3o/2a/3o2ahddfby8aqgutow91vo4asm4f +0 -0
  29. data/spec/dummy/tmp/storage/46/zj/46zjkljy84qmg4ebes40e2zolzp7 +0 -0
  30. data/spec/dummy/tmp/storage/4f/fl/4fflxsg2pissq4960wfazyv9mgp1 +0 -0
  31. data/spec/dummy/tmp/storage/4l/6a/4l6ap3t705ektu02kabqobaq8huf +0 -0
  32. data/spec/dummy/tmp/storage/4m/3n/4m3n3r5zl2jevvvw3alq0omnnowk +0 -0
  33. data/spec/dummy/tmp/storage/4t/qc/4tqc587gmu2g7hgntrgjnhkw0e1u +0 -0
  34. data/spec/dummy/tmp/storage/4w/76/4w7656628v1p4mp87fqz3okc6ix4 +0 -0
  35. data/spec/dummy/tmp/storage/50/ct/50ctjwt8y93spjaef1b3rfnav9gb +0 -0
  36. data/spec/dummy/tmp/storage/5m/0k/5m0k5zx08ouqos7rs09bpgmalspu +0 -0
  37. data/spec/dummy/tmp/storage/5m/4n/5m4nvbe3vqwrdpy6hujr1oq5nolv +0 -0
  38. data/spec/dummy/tmp/storage/5r/tv/5rtv2agppt9e52eg0oc81svs896e +0 -0
  39. data/spec/dummy/tmp/storage/5s/b6/5sb6kux9jtdj4du9ihu32tc82s12 +0 -0
  40. data/spec/dummy/tmp/storage/5t/84/5t84ncnwqnv83rndaztqz1dsae3v +0 -0
  41. data/spec/dummy/tmp/storage/6h/9a/6h9a6bxecnlwx7zbb4dc33rmftee +0 -0
  42. data/spec/dummy/tmp/storage/6h/pd/6hpdw0r7w6vpcotirwqz1fv640kk +0 -0
  43. data/spec/dummy/tmp/storage/6i/dm/6idmnfdfriqdmvcvgreyeho4xgra +0 -0
  44. data/spec/dummy/tmp/storage/6n/3b/6n3bufdfx1tvls9lzm3g625l1lyk +0 -0
  45. data/spec/dummy/tmp/storage/78/5u/785ub96ra353qygeubpq4vlifjas +0 -0
  46. data/spec/dummy/tmp/storage/7v/55/7v55c4y5xdiw6qrvbdpmd63v4toz +0 -0
  47. data/spec/dummy/tmp/storage/83/yi/83yiflnj2evgqahmzgiavbywle7h +0 -0
  48. data/spec/dummy/tmp/storage/8a/02/8a02q8tcm3lowda8cvcjh18tka7i +0 -0
  49. data/spec/dummy/tmp/storage/8g/4a/8g4aarquz0mos9gxzq6j7xzwli09 +0 -0
  50. data/spec/dummy/tmp/storage/8k/0m/8k0mv7xql5lr89d0172izqplypwx +0 -0
  51. data/spec/dummy/tmp/storage/8n/lz/8nlzdb800dgr9iax1ez2ko4euh02 +0 -0
  52. data/spec/dummy/tmp/storage/8y/y2/8yy2kqz8sa11vh7naiomyofj30ox +0 -0
  53. data/spec/dummy/tmp/storage/92/hz/92hz15r26fs5wymc6tced4b08bh6 +0 -0
  54. data/spec/dummy/tmp/storage/9k/wj/9kwjdz4yw0jyzddj32u97xciot47 +0 -0
  55. data/spec/dummy/tmp/storage/a0/ve/a0veqfafcpy1hl6jvg6jc4qt08kr +0 -0
  56. data/spec/dummy/tmp/storage/ak/g9/akg9bg7fdwisycbclvmikbzl676j +0 -0
  57. data/spec/dummy/tmp/storage/ap/fs/apfsy6cp4mt4jsyiyscutqhv36id +0 -0
  58. data/spec/dummy/tmp/storage/b2/ux/b2uxv1pu0hjqty7779grby8qxzoo +0 -0
  59. data/spec/dummy/tmp/storage/bh/s1/bhs1yc1jo5vhc1u8lz7zme0sowln +0 -0
  60. data/spec/dummy/tmp/storage/c6/29/c6299u91u1tc5bc7eb24yq8k7xgk +0 -0
  61. data/spec/dummy/tmp/storage/cb/dh/cbdhbsxm7irfbsrniqvxnfukotka +0 -0
  62. data/spec/dummy/tmp/storage/dj/zr/djzriitbyq5sebmjs7nf0wqijzl2 +0 -0
  63. data/spec/dummy/tmp/storage/dk/z6/dkz6yvma826vmeofy924nzacyi6a +0 -0
  64. data/spec/dummy/tmp/storage/e6/5h/e65haeyr0sglopfuo5chz55thi9z +0 -0
  65. data/spec/dummy/tmp/storage/ez/kd/ezkdx2jgksxsm9fdwpx5qw8qd6hb +0 -0
  66. data/spec/dummy/tmp/storage/ft/pz/ftpzprhag9awyjvcac1xiqv9qngf +0 -0
  67. data/spec/dummy/tmp/storage/g1/fp/g1fprpwzsoltdd9atheqqbmt75w1 +0 -0
  68. data/spec/dummy/tmp/storage/g6/7r/g67rz6k8b5kmrfsk91e77d37vnfg +0 -0
  69. data/spec/dummy/tmp/storage/ik/r8/ikr86smhkvxy0yrqxjr0iubcuy5s +0 -0
  70. data/spec/dummy/tmp/storage/ip/yy/ipyyodp4f221bzp384ncz3alga7l +0 -0
  71. data/spec/dummy/tmp/storage/jc/jk/jcjkdsojvg0n7abwcfpscbkrlvoj +0 -0
  72. data/spec/dummy/tmp/storage/jd/ik/jdik45lk030wwyyfs9xy6m4q0sv9 +0 -0
  73. data/spec/dummy/tmp/storage/jo/34/jo34sgi1pzpkbryh3z6bmeccbada +0 -0
  74. data/spec/dummy/tmp/storage/jw/gm/jwgmfnb1z5jahut7upw5lwnh6orj +0 -0
  75. data/spec/dummy/tmp/storage/kv/7m/kv7mdo1kvqnagfcp1dit4pzrlugm +0 -0
  76. data/spec/dummy/tmp/storage/kz/ao/kzao8av3jxls3bjeal7bv2qefcq2 +0 -0
  77. data/spec/dummy/tmp/storage/lp/iz/lpizuy9o61mj9a6nsp66uhxmwifn +0 -0
  78. data/spec/dummy/tmp/storage/lu/ge/lugetp9a4lxzimpwttg2bkmrv1tf +0 -0
  79. data/spec/dummy/tmp/storage/m0/hl/m0hl9jcc1ra6ogfep0yxwon1n8e4 +0 -0
  80. data/spec/dummy/tmp/storage/md/94/md943ike1q6c3z19js6h89ghdqpd +0 -0
  81. data/spec/dummy/tmp/storage/mi/y2/miy2op7reeft1521or0hx3sa03qi +0 -0
  82. data/spec/dummy/tmp/storage/n4/6y/n46y5dyl1uf9ihlyp79fzdxny80f +0 -0
  83. data/spec/dummy/tmp/storage/ng/sy/ngsy8siky5us2x8cr54uxlzwoji5 +0 -0
  84. data/spec/dummy/tmp/storage/nq/9r/nq9rfa8upvcoyx92za4fhzrmob0t +0 -0
  85. data/spec/dummy/tmp/storage/nz/h5/nzh5f9vo9icpp0qvxvvm57ivsy85 +0 -0
  86. data/spec/dummy/tmp/storage/o3/3e/o33eo1zb3spyqgqbtugs43olucmj +0 -0
  87. data/spec/dummy/tmp/storage/oc/zy/oczyqb5chp2fvo9kdu62c3y7pl5i +0 -0
  88. data/spec/dummy/tmp/storage/of/zt/ofzt1knekv9ft8direbw6fiaacvc +0 -0
  89. data/spec/dummy/tmp/storage/ol/u8/olu875ti78gguc82spdkt9z8tpii +0 -0
  90. data/spec/dummy/tmp/storage/pe/xm/pexmtjvhiiepbza9928j2j0uo6ub +0 -0
  91. data/spec/dummy/tmp/storage/q4/wp/q4wpnk0ajsmg3nnpj2xja15tc1az +0 -0
  92. data/spec/dummy/tmp/storage/q7/48/q748a4a7d3qk28vajhbkli5tttsj +0 -0
  93. data/spec/dummy/tmp/storage/qg/qu/qgquu27ysijim1jv06k39a3c3opr +0 -0
  94. data/spec/dummy/tmp/storage/qo/zx/qozxdnemk1fwxcvhe1j10ys7mxcq +0 -0
  95. data/spec/dummy/tmp/storage/rg/hw/rghwbui1nfqvuuja4hqvzvwofucm +0 -0
  96. data/spec/dummy/tmp/storage/rj/4a/rj4a0t2xebuf22tyhuitu7x0h0ct +0 -0
  97. data/spec/dummy/tmp/storage/ro/gg/roggtjpn82v1pf5hi9c72ta6uxt7 +0 -0
  98. data/spec/dummy/tmp/storage/sd/b5/sdb5dbqjlz1nng8qaejdmh94h5kh +0 -0
  99. data/spec/dummy/tmp/storage/t6/h0/t6h0whv9denben7ktf5maaohv9dg +0 -0
  100. data/spec/dummy/tmp/storage/u8/dc/u8dc1at72gpsq6d2fqb81rku7xq9 +0 -0
  101. data/spec/dummy/tmp/storage/v8/cs/v8csqmcgvc4z177g5hfmp7twf6ay +0 -0
  102. data/spec/dummy/tmp/storage/vb/z1/vbz124po6xcx6fbh4nefle8r6of3 +0 -0
  103. data/spec/dummy/tmp/storage/vc/0v/vc0vb76xhipq8d5xrmqnnvlkbxhi +0 -0
  104. data/spec/dummy/tmp/storage/vc/eu/vceu8un4vczdune7r9oaihtx3l09 +0 -0
  105. data/spec/dummy/tmp/storage/vg/qn/vgqnfl62z1s3940ei2s9ifrjufiz +0 -0
  106. data/spec/dummy/tmp/storage/vz/qz/vzqz49sc3hfzbcaawqw6yuqzpocv +0 -0
  107. data/spec/dummy/tmp/storage/w1/9e/w19epnmenhmu0y14exloj2oskf53 +0 -0
  108. data/spec/dummy/tmp/storage/w4/k8/w4k8g0iv6qtdhzyw454un1d048hp +0 -0
  109. data/spec/dummy/tmp/storage/wv/8i/wv8iai6dxxh2wqwnj667p5rre1tn +0 -0
  110. data/spec/dummy/tmp/storage/xd/fp/xdfpr9gw2neskzyq6cfxyj2lsa47 +0 -0
  111. data/spec/dummy/tmp/storage/xh/4e/xh4e0tnk5ysrfndrhyu9u0ywueku +0 -0
  112. data/spec/dummy/tmp/storage/xj/d2/xjd2s558zk0eduk4f25ul4t29b2r +0 -0
  113. data/spec/dummy/tmp/storage/yi/r2/yir26w0pcj2268kp8biw0lcrnimz +0 -0
  114. data/spec/dummy/tmp/storage/yj/d7/yjd7k0ea2d8y6yjp8syfudd0qvhr +0 -0
  115. data/spec/dummy/tmp/storage/z0/il/z0ilfyp3hmqqn6alkocrt7y949ee +0 -0
  116. data/spec/dummy/tmp/storage/zc/i3/zci3kt86sywh9q6u7xv7yt38xznu +0 -0
  117. data/spec/examples.txt +192 -128
  118. data/spec/factories/snf_core/account_transfers.rb +27 -0
  119. data/spec/factories/snf_core/addresses.rb +1 -0
  120. data/spec/factories/snf_core/item_requests.rb +26 -0
  121. data/spec/factories/snf_core/users.rb +10 -1
  122. data/spec/factories/snf_core/virtual_account_transactions.rb +33 -0
  123. data/spec/factories/snf_core/virtual_accounts.rb +32 -0
  124. data/spec/models/snf_core/account_transfer_spec.rb +29 -0
  125. data/spec/models/snf_core/item_request_spec.rb +44 -0
  126. data/spec/models/snf_core/user_spec.rb +5 -8
  127. data/spec/models/snf_core/virtual_account_spec.rb +86 -0
  128. data/spec/models/snf_core/virtual_account_transaction_spec.rb +72 -0
  129. data/spec/requests/snf_core/auth_spec.rb +11 -1
  130. metadata +119 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85e0a48d15038fbf0986e432723ef4b5d6789e2795ba1702e81a0b933542df23
4
- data.tar.gz: c96837434bf4792ca8d109094aa2d21057dbba027e14f7ca781a215254a1ef63
3
+ metadata.gz: 7ca8bd2392a69ff49850fa101f216f512c1b9f02ebca883ed26ca979ee4b1849
4
+ data.tar.gz: 18480de028a99981ee91e8888d28323ce631abdf25ec99bf1d97d3fffc226790
5
5
  SHA512:
6
- metadata.gz: 22db5c47abc86de6990ba45f4785bd72c1ca7b456258da5e1c2fe503147eaec4df0d98025610c1eaa20c12265b412552e696603cc4d0a3bd2a02fc24039265c1
7
- data.tar.gz: 9275800e26a3904270dd786c221942238689ea060919d5fadfa1e788b6889ae308f5a9eb6e2270287340ad8e105d9e8497dcb1d6c6c37774411c457a4a3ba745
6
+ metadata.gz: b1de2c64f43d2fe35478de2f507b9b6ea887c980fb41f72dbddded1db7e7c2e5a4d252a848625dc9a450b826183c372ac8913357f71459407ed999cf5662c71d
7
+ data.tar.gz: a1952a64ba5b261f4e97629e4028c6417f2983a84cf8e7de5e60809d8eaa0d794302489bbb63187e8336939fc7f5a4813d8c80e88c0bf20657f801314e96f17a
@@ -93,11 +93,21 @@ module SnfCore
93
93
  end
94
94
 
95
95
  def signup_params
96
- params.require(:user).permit(:first_name, :middle_name, :last_name, :password, :phone_number, :business_name, :tin_number, :business_type)
96
+ params.require(:user).permit(
97
+ :first_name, :middle_name, :last_name, :password,
98
+ :phone_number, :business_name, :tin_number, :business_type,
99
+ :date_of_birth, :nationality, :occupation, :source_of_funds, :kyc_status,
100
+ :gender, :verified_at, :verified_by_id, :address_id
101
+ )
97
102
  end
98
103
 
99
104
  def user_params
100
- signup_params.slice(:first_name, :middle_name, :last_name, :password, :phone_number)
105
+ signup_params.slice(
106
+ :first_name, :middle_name, :last_name, :password,
107
+ :phone_number, :date_of_birth, :nationality,
108
+ :occupation, :source_of_funds, :kyc_status,
109
+ :gender, :verified_at, :verified_by_id, :address_id
110
+ )
101
111
  end
102
112
  end
103
113
  end
@@ -0,0 +1,37 @@
1
+ module SnfCore
2
+ class AccountTransfer < ApplicationRecord
3
+ belongs_to :source_account, polymorphic: true
4
+ belongs_to :destination_account, polymorphic: true
5
+ belongs_to :user
6
+
7
+ enum :status, {
8
+ initiated: 0,
9
+ pending: 1,
10
+ processing: 2,
11
+ completed: 3,
12
+ failed: 4,
13
+ cancelled: 5,
14
+ reversed: 6
15
+ }
16
+
17
+ enum :transfer_type, {
18
+ virtual_to_cbs: 0,
19
+ cbs_to_virtual: 1,
20
+ virtual_to_virtual: 2
21
+ }
22
+
23
+ validates :amount, presence: true, numericality: { greater_than: 0 }
24
+ validates :reference_number, presence: true, uniqueness: true
25
+ validates :status, presence: true
26
+ validates :transfer_type, presence: true
27
+
28
+ before_validation :generate_reference_number, on: :create
29
+
30
+
31
+ private
32
+
33
+ def generate_reference_number
34
+ self.reference_number ||= "TRF-#{SecureRandom.hex(8).upcase}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ module SnfCore
2
+ class ItemRequest < ApplicationRecord
3
+ belongs_to :user
4
+ belongs_to :product
5
+
6
+ validates :quantity, presence: true, numericality: { greater_than: 0 }
7
+ validates :requested_delivery_date, presence: true
8
+ validates :status, presence: true
9
+ validate :delivery_date_cannot_be_in_past
10
+
11
+ enum :status, {
12
+ pending: 0,
13
+ approved: 1,
14
+ rejected: 2,
15
+ fulfilled: 3,
16
+ cancelled: 4
17
+ }
18
+
19
+ private
20
+
21
+ def delivery_date_cannot_be_in_past
22
+ if requested_delivery_date.present? && requested_delivery_date < Date.current
23
+ errors.add(:requested_delivery_date, "can't be in the past")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -2,17 +2,18 @@ module SnfCore
2
2
  class User < ApplicationRecord
3
3
  has_secure_password
4
4
 
5
+ belongs_to :verified_by, class_name: 'SnfCore::User', optional: true
6
+ belongs_to :address
7
+
5
8
  has_one :wallet, dependent: :destroy
6
9
 
7
- validates :first_name, :middle_name, :last_name, :phone_number, presence: true
10
+ validates :first_name, :middle_name, :last_name, :phone_number, :date_of_birth, :nationality, presence: true
8
11
  validates :phone_number, :email, uniqueness: true
9
12
 
10
- after_create :create_default_wallet
13
+ enum :kyc_status, { pending: 0, approved: 1, rejected: 2 }
14
+ enum :gender, { male: 0, female: 1}
11
15
 
12
- private
16
+ has_one :virtual_account, dependent: :destroy
13
17
 
14
- def create_default_wallet
15
- create_wallet(balance: 0.0, is_active: true)
16
- end
17
18
  end
18
19
  end
@@ -0,0 +1,41 @@
1
+ module SnfCore
2
+ class VirtualAccount < ApplicationRecord
3
+ belongs_to :user
4
+
5
+ enum :interest_type, {
6
+ conventional: 0, # Standard interest type
7
+ interest_free: 1, # No interest (IFB)
8
+ preferential: 2, # Special rates for specific customer segments
9
+ custom: 3 # Negotiated rates for specific cases
10
+ }
11
+
12
+ validates :account_number, presence: true, uniqueness: {case_sensitive: true}
13
+ validates :cbs_account_number, presence: true, uniqueness: true
14
+ validates :balance, presence: true, numericality: { greater_than_or_equal_to: 0 }
15
+ validates :interest_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }
16
+ validates :interest_type, presence: true
17
+ validates :branch_code, presence: true, length: { is: 3 }
18
+ validates :product_scheme, presence: true, length: { is: 1 }
19
+ validates :voucher_type, presence: true, length: { is: 1 }
20
+ validates :active, presence:true, inclusion: { in: [true, false] }
21
+
22
+ before_validation :generate_account_number, on: :create
23
+ validate :validate_interest_free_rate
24
+
25
+ private
26
+
27
+ def validate_interest_free_rate
28
+ if interest_free? && !interest_rate.zero?
29
+ errors.add(:interest_rate, 'must be 0 for interest-free accounts')
30
+ end
31
+ end
32
+
33
+ def generate_account_number
34
+ return if account_number.present?
35
+
36
+ last_seq = self.class.maximum(:account_number).to_s[-6..-1].to_i
37
+ seq = (last_seq + 1).to_s.rjust(6, '0')
38
+ self.account_number = "#{branch_code}#{product_scheme}#{voucher_type}#{seq}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,52 @@
1
+ module SnfCore
2
+ class VirtualAccountTransaction < ApplicationRecord
3
+ belongs_to :from_account, class_name: 'SnfCore::VirtualAccount', optional: true
4
+ belongs_to :to_account, class_name: 'SnfCore::VirtualAccount', optional: true
5
+
6
+ enum :transaction_type, {
7
+ transfer: 0,
8
+ deposit: 1,
9
+ withdrawal: 2
10
+ }
11
+
12
+ enum :status, {
13
+ pending: 0,
14
+ completed: 1,
15
+ failed: 2,
16
+ reversed: 3
17
+ }
18
+
19
+ validates :amount, presence: true, numericality: { greater_than: 0 }
20
+ validates :reference_number, presence: true, uniqueness: true
21
+ validates :transaction_type, presence: true
22
+ validates :status, presence: true
23
+
24
+ validate :validate_accounts
25
+ validate :validate_sufficient_balance, if: -> { amount.present? && transfer_or_withdrawal? }
26
+
27
+ private
28
+
29
+ def validate_sufficient_balance
30
+ return unless from_account
31
+ return if from_account.balance >= amount
32
+
33
+ errors.add(:amount, 'Insufficient balance')
34
+ end
35
+
36
+ def validate_accounts
37
+ case transaction_type.to_s
38
+ when 'transfer'
39
+ errors.add(:base, 'Transfer requires both from and to accounts') unless from_account && to_account
40
+ errors.add(:base, 'Cannot transfer to same account') if from_account == to_account
41
+ when 'deposit'
42
+ errors.add(:base, 'Deposit requires to_account') unless to_account
43
+ when 'withdrawal'
44
+ errors.add(:base, 'Withdrawal requires from_account') unless from_account
45
+ end
46
+ end
47
+
48
+ def transfer_or_withdrawal?
49
+ %w[transfer withdrawal].include?(transaction_type.to_s)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ class CreateSnfCoreVirtualAccounts < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :snf_core_virtual_accounts do |t|
4
+ t.references :user, null: false, foreign_key: { to_table: :snf_core_users }
5
+ t.string :account_number, null: false, limit: 11
6
+ t.string :cbs_account_number, null: false
7
+ t.decimal :balance, null: false, default: 0.0
8
+ t.decimal :interest_rate, null: false, default: 0.0
9
+ t.integer :interest_type, null: false, default: 0
10
+ t.boolean :active, null: false, default: true
11
+ t.string :branch_code, null: false, limit: 3
12
+ t.string :product_scheme, null: false, limit: 1
13
+ t.string :voucher_type, null: false, limit: 1
14
+
15
+ t.timestamps
16
+
17
+ t.index :account_number, unique: true
18
+ t.index :cbs_account_number, unique: true
19
+ t.index [:branch_code, :product_scheme, :voucher_type]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ class AddKycAttributesToUser < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :snf_core_users, :date_of_birth, :date, null: false
4
+ add_column :snf_core_users, :nationality, :string, null: false
5
+ add_column :snf_core_users, :occupation, :string
6
+ add_column :snf_core_users, :source_of_funds, :string
7
+ add_column :snf_core_users, :kyc_status, :integer
8
+ add_column :snf_core_users, :gender, :integer
9
+ add_column :snf_core_users, :verified_at, :datetime
10
+ add_column :snf_core_users, :verified_by_id, :bigint
11
+ add_column :snf_core_addresses, :house_number, :string
12
+ add_column :snf_core_users, :address_id, :bigint
13
+
14
+ add_foreign_key :snf_core_users, :snf_core_users, column: :verified_by_id, primary_key: :id, on_delete: :nullify
15
+ add_foreign_key :snf_core_users, :snf_core_addresses, column: :address_id, primary_key: :id
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ class CreateSnfCoreVirtualAccountTransactions < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :snf_core_virtual_account_transactions do |t|
4
+ t.references :from_account, foreign_key: { to_table: :snf_core_virtual_accounts }
5
+ t.references :to_account, foreign_key: { to_table: :snf_core_virtual_accounts }
6
+ t.decimal :amount, null: false
7
+ t.integer :transaction_type, null: false
8
+ t.integer :status, null: false, default: 0
9
+ t.string :reference_number, null: false
10
+ t.text :description
11
+
12
+ t.timestamps
13
+
14
+ t.index :reference_number, unique: true
15
+ t.index :created_at
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ class CreateSnfCoreItemRequests < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :snf_core_item_requests do |t|
4
+ t.references :user, null: false, foreign_key: { to_table: :snf_core_users}
5
+ t.references :product, null: false, foreign_key: {to_table: :snf_core_products}
6
+ t.integer :quantity, null:false
7
+ t.date :requested_delivery_date, null:false
8
+ t.text :notes
9
+ t.integer :status, null:false, default: 0
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ class CreateSnfCoreAccountTransfers < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :snf_core_account_transfers do |t|
4
+ t.references :source_account, polymorphic: true, null: false
5
+ t.references :destination_account, polymorphic: true, null: false
6
+ t.references :user, null: false, foreign_key: { to_table: :snf_core_users }
7
+ t.decimal :amount, precision: 15, scale: 2, null: false
8
+ t.string :reference_number, null: false, index: { unique: true }
9
+ t.integer :status, null: false, default: 0
10
+ t.integer :transfer_type, null: false
11
+ t.text :description
12
+ t.text :failure_reason
13
+ t.datetime :completed_at
14
+ t.bigint :reversal_transfer_id
15
+ t.jsonb :cbs_response_data
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :snf_core_account_transfers, :reversal_transfer_id
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module SnfCore
2
- VERSION = "0.2.94"
2
+ VERSION = "0.2.95"
3
3
  end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
13
+ ActiveRecord::Schema[8.0].define(version: 2025_03_11_000000) do
14
14
  # These are extensions that must be enabled in order to support this database
15
15
  enable_extension "pg_catalog.plpgsql"
16
16
 
@@ -20,8 +20,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
20
20
  t.bigint "record_id", null: false
21
21
  t.bigint "blob_id", null: false
22
22
  t.datetime "created_at", null: false
23
- t.index [ "blob_id" ], name: "index_active_storage_attachments_on_blob_id"
24
- t.index [ "record_type", "record_id", "name", "blob_id" ], name: "index_active_storage_attachments_uniqueness", unique: true
23
+ t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
24
+ t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
25
25
  end
26
26
 
27
27
  create_table "active_storage_blobs", force: :cascade do |t|
@@ -33,13 +33,37 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
33
33
  t.bigint "byte_size", null: false
34
34
  t.string "checksum"
35
35
  t.datetime "created_at", null: false
36
- t.index [ "key" ], name: "index_active_storage_blobs_on_key", unique: true
36
+ t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
37
37
  end
38
38
 
39
39
  create_table "active_storage_variant_records", force: :cascade do |t|
40
40
  t.bigint "blob_id", null: false
41
41
  t.string "variation_digest", null: false
42
- t.index [ "blob_id", "variation_digest" ], name: "index_active_storage_variant_records_uniqueness", unique: true
42
+ t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
43
+ end
44
+
45
+ create_table "snf_core_account_transfers", force: :cascade do |t|
46
+ t.string "source_account_type", null: false
47
+ t.bigint "source_account_id", null: false
48
+ t.string "destination_account_type", null: false
49
+ t.bigint "destination_account_id", null: false
50
+ t.bigint "user_id", null: false
51
+ t.decimal "amount", precision: 15, scale: 2, null: false
52
+ t.string "reference_number", null: false
53
+ t.integer "status", default: 0, null: false
54
+ t.integer "transfer_type", null: false
55
+ t.text "description"
56
+ t.text "failure_reason"
57
+ t.datetime "completed_at"
58
+ t.bigint "reversal_transfer_id"
59
+ t.jsonb "cbs_response_data"
60
+ t.datetime "created_at", null: false
61
+ t.datetime "updated_at", null: false
62
+ t.index ["destination_account_type", "destination_account_id"], name: "index_snf_core_account_transfers_on_destination_account"
63
+ t.index ["reference_number"], name: "index_snf_core_account_transfers_on_reference_number", unique: true
64
+ t.index ["reversal_transfer_id"], name: "index_snf_core_account_transfers_on_reversal_transfer_id"
65
+ t.index ["source_account_type", "source_account_id"], name: "index_snf_core_account_transfers_on_source_account"
66
+ t.index ["user_id"], name: "index_snf_core_account_transfers_on_user_id"
43
67
  end
44
68
 
45
69
  create_table "snf_core_addresses", force: :cascade do |t|
@@ -51,6 +75,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
51
75
  t.decimal "longitude", null: false
52
76
  t.datetime "created_at", null: false
53
77
  t.datetime "updated_at", null: false
78
+ t.string "house_number"
54
79
  end
55
80
 
56
81
  create_table "snf_core_business_documents", force: :cascade do |t|
@@ -62,8 +87,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
62
87
  t.boolean "is_verified", default: false, null: false
63
88
  t.datetime "created_at", null: false
64
89
  t.datetime "updated_at", null: false
65
- t.index [ "business_id" ], name: "index_snf_core_business_documents_on_business_id"
66
- t.index [ "verified_by_id" ], name: "index_snf_core_business_documents_on_verified_by_id"
90
+ t.index ["business_id"], name: "index_snf_core_business_documents_on_business_id"
91
+ t.index ["verified_by_id"], name: "index_snf_core_business_documents_on_verified_by_id"
67
92
  end
68
93
 
69
94
  create_table "snf_core_businesses", force: :cascade do |t|
@@ -75,7 +100,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
75
100
  t.integer "verification_status", default: 0, null: false
76
101
  t.datetime "created_at", null: false
77
102
  t.datetime "updated_at", null: false
78
- t.index [ "user_id" ], name: "index_snf_core_businesses_on_user_id"
103
+ t.index ["user_id"], name: "index_snf_core_businesses_on_user_id"
79
104
  end
80
105
 
81
106
  create_table "snf_core_categories", force: :cascade do |t|
@@ -84,7 +109,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
84
109
  t.bigint "parent_id"
85
110
  t.datetime "created_at", null: false
86
111
  t.datetime "updated_at", null: false
87
- t.index [ "parent_id" ], name: "index_snf_core_categories_on_parent_id"
112
+ t.index ["parent_id"], name: "index_snf_core_categories_on_parent_id"
88
113
  end
89
114
 
90
115
  create_table "snf_core_customer_groups", force: :cascade do |t|
@@ -95,8 +120,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
95
120
  t.bigint "customer_id", null: false
96
121
  t.datetime "created_at", null: false
97
122
  t.datetime "updated_at", null: false
98
- t.index [ "customer_id" ], name: "index_snf_core_customer_groups_on_customer_id"
99
- t.index [ "group_id" ], name: "index_snf_core_customer_groups_on_group_id"
123
+ t.index ["customer_id"], name: "index_snf_core_customer_groups_on_customer_id"
124
+ t.index ["group_id"], name: "index_snf_core_customer_groups_on_group_id"
100
125
  end
101
126
 
102
127
  create_table "snf_core_delivery_orders", force: :cascade do |t|
@@ -109,7 +134,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
109
134
  t.integer "status", null: false
110
135
  t.datetime "created_at", null: false
111
136
  t.datetime "updated_at", null: false
112
- t.index [ "order_id" ], name: "index_snf_core_delivery_orders_on_order_id"
137
+ t.index ["order_id"], name: "index_snf_core_delivery_orders_on_order_id"
113
138
  end
114
139
 
115
140
  create_table "snf_core_groups", force: :cascade do |t|
@@ -117,7 +142,20 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
117
142
  t.bigint "business_id", null: false
118
143
  t.datetime "created_at", null: false
119
144
  t.datetime "updated_at", null: false
120
- t.index [ "business_id" ], name: "index_snf_core_groups_on_business_id"
145
+ t.index ["business_id"], name: "index_snf_core_groups_on_business_id"
146
+ end
147
+
148
+ create_table "snf_core_item_requests", force: :cascade do |t|
149
+ t.bigint "user_id", null: false
150
+ t.bigint "product_id", null: false
151
+ t.integer "quantity", null: false
152
+ t.date "requested_delivery_date", null: false
153
+ t.text "notes"
154
+ t.integer "status", default: 0, null: false
155
+ t.datetime "created_at", null: false
156
+ t.datetime "updated_at", null: false
157
+ t.index ["product_id"], name: "index_snf_core_item_requests_on_product_id"
158
+ t.index ["user_id"], name: "index_snf_core_item_requests_on_user_id"
121
159
  end
122
160
 
123
161
  create_table "snf_core_order_items", force: :cascade do |t|
@@ -128,8 +166,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
128
166
  t.decimal "subtotal", null: false
129
167
  t.datetime "created_at", null: false
130
168
  t.datetime "updated_at", null: false
131
- t.index [ "order_id" ], name: "index_snf_core_order_items_on_order_id"
132
- t.index [ "store_inventory_id" ], name: "index_snf_core_order_items_on_store_inventory_id"
169
+ t.index ["order_id"], name: "index_snf_core_order_items_on_order_id"
170
+ t.index ["store_inventory_id"], name: "index_snf_core_order_items_on_store_inventory_id"
133
171
  end
134
172
 
135
173
  create_table "snf_core_orders", force: :cascade do |t|
@@ -139,8 +177,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
139
177
  t.decimal "total_amount", default: "0.0", null: false
140
178
  t.datetime "created_at", null: false
141
179
  t.datetime "updated_at", null: false
142
- t.index [ "store_id" ], name: "index_snf_core_orders_on_store_id"
143
- t.index [ "user_id" ], name: "index_snf_core_orders_on_user_id"
180
+ t.index ["store_id"], name: "index_snf_core_orders_on_store_id"
181
+ t.index ["user_id"], name: "index_snf_core_orders_on_user_id"
144
182
  end
145
183
 
146
184
  create_table "snf_core_products", force: :cascade do |t|
@@ -151,7 +189,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
151
189
  t.float "base_price"
152
190
  t.datetime "created_at", null: false
153
191
  t.datetime "updated_at", null: false
154
- t.index [ "category_id" ], name: "index_snf_core_products_on_category_id"
192
+ t.index ["category_id"], name: "index_snf_core_products_on_category_id"
155
193
  end
156
194
 
157
195
  create_table "snf_core_roles", force: :cascade do |t|
@@ -167,9 +205,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
167
205
  t.datetime "created_at", null: false
168
206
  t.datetime "updated_at", null: false
169
207
  t.integer "status", default: 0, null: false
170
- t.index [ "product_id" ], name: "index_snf_core_store_inventories_on_product_id"
171
- t.index [ "store_id", "product_id" ], name: "index_snf_core_store_inventories_on_store_id_and_product_id", unique: true
172
- t.index [ "store_id" ], name: "index_snf_core_store_inventories_on_store_id"
208
+ t.index ["product_id"], name: "index_snf_core_store_inventories_on_product_id"
209
+ t.index ["store_id", "product_id"], name: "index_snf_core_store_inventories_on_store_id_and_product_id", unique: true
210
+ t.index ["store_id"], name: "index_snf_core_store_inventories_on_store_id"
173
211
  end
174
212
 
175
213
  create_table "snf_core_stores", force: :cascade do |t|
@@ -180,8 +218,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
180
218
  t.bigint "address_id", null: false
181
219
  t.datetime "created_at", null: false
182
220
  t.datetime "updated_at", null: false
183
- t.index [ "address_id" ], name: "index_snf_core_stores_on_address_id"
184
- t.index [ "business_id" ], name: "index_snf_core_stores_on_business_id"
221
+ t.index ["address_id"], name: "index_snf_core_stores_on_address_id"
222
+ t.index ["business_id"], name: "index_snf_core_stores_on_business_id"
185
223
  end
186
224
 
187
225
  create_table "snf_core_user_roles", force: :cascade do |t|
@@ -189,8 +227,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
189
227
  t.bigint "role_id", null: false
190
228
  t.datetime "created_at", null: false
191
229
  t.datetime "updated_at", null: false
192
- t.index [ "role_id" ], name: "index_snf_core_user_roles_on_role_id"
193
- t.index [ "user_id" ], name: "index_snf_core_user_roles_on_user_id"
230
+ t.index ["role_id"], name: "index_snf_core_user_roles_on_role_id"
231
+ t.index ["user_id"], name: "index_snf_core_user_roles_on_user_id"
194
232
  end
195
233
 
196
234
  create_table "snf_core_users", force: :cascade do |t|
@@ -204,6 +242,50 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
204
242
  t.string "password_digest", null: false
205
243
  t.boolean "password_changed", default: false
206
244
  t.string "reset_password_token"
245
+ t.date "date_of_birth", null: false
246
+ t.string "nationality", null: false
247
+ t.string "occupation"
248
+ t.string "source_of_funds"
249
+ t.integer "kyc_status"
250
+ t.integer "gender"
251
+ t.datetime "verified_at"
252
+ t.bigint "verified_by_id"
253
+ t.bigint "address_id"
254
+ end
255
+
256
+ create_table "snf_core_virtual_account_transactions", force: :cascade do |t|
257
+ t.bigint "from_account_id"
258
+ t.bigint "to_account_id"
259
+ t.decimal "amount", null: false
260
+ t.integer "transaction_type", null: false
261
+ t.integer "status", default: 0, null: false
262
+ t.string "reference_number", null: false
263
+ t.text "description"
264
+ t.datetime "created_at", null: false
265
+ t.datetime "updated_at", null: false
266
+ t.index ["created_at"], name: "index_snf_core_virtual_account_transactions_on_created_at"
267
+ t.index ["from_account_id"], name: "index_snf_core_virtual_account_transactions_on_from_account_id"
268
+ t.index ["reference_number"], name: "idx_on_reference_number_bd9be00f20", unique: true
269
+ t.index ["to_account_id"], name: "index_snf_core_virtual_account_transactions_on_to_account_id"
270
+ end
271
+
272
+ create_table "snf_core_virtual_accounts", force: :cascade do |t|
273
+ t.bigint "user_id", null: false
274
+ t.string "account_number", limit: 11, null: false
275
+ t.string "cbs_account_number", null: false
276
+ t.decimal "balance", default: "0.0", null: false
277
+ t.decimal "interest_rate", default: "0.0", null: false
278
+ t.integer "interest_type", default: 0, null: false
279
+ t.boolean "active", default: true, null: false
280
+ t.string "branch_code", limit: 3, null: false
281
+ t.string "product_scheme", limit: 1, null: false
282
+ t.string "voucher_type", limit: 1, null: false
283
+ t.datetime "created_at", null: false
284
+ t.datetime "updated_at", null: false
285
+ t.index ["account_number"], name: "index_snf_core_virtual_accounts_on_account_number", unique: true
286
+ t.index ["branch_code", "product_scheme", "voucher_type"], name: "idx_on_branch_code_product_scheme_voucher_type_b7038b3d5f"
287
+ t.index ["cbs_account_number"], name: "index_snf_core_virtual_accounts_on_cbs_account_number", unique: true
288
+ t.index ["user_id"], name: "index_snf_core_virtual_accounts_on_user_id"
207
289
  end
208
290
 
209
291
  create_table "snf_core_wallets", force: :cascade do |t|
@@ -213,12 +295,13 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
213
295
  t.boolean "is_active", default: true, null: false
214
296
  t.datetime "created_at", null: false
215
297
  t.datetime "updated_at", null: false
216
- t.index [ "user_id" ], name: "index_snf_core_wallets_on_user_id"
217
- t.index [ "wallet_number" ], name: "index_snf_core_wallets_on_wallet_number", unique: true
298
+ t.index ["user_id"], name: "index_snf_core_wallets_on_user_id"
299
+ t.index ["wallet_number"], name: "index_snf_core_wallets_on_wallet_number", unique: true
218
300
  end
219
301
 
220
302
  add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
221
303
  add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
304
+ add_foreign_key "snf_core_account_transfers", "snf_core_users", column: "user_id"
222
305
  add_foreign_key "snf_core_business_documents", "snf_core_businesses", column: "business_id"
223
306
  add_foreign_key "snf_core_business_documents", "snf_core_users", column: "verified_by_id"
224
307
  add_foreign_key "snf_core_businesses", "snf_core_users", column: "user_id"
@@ -227,6 +310,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
227
310
  add_foreign_key "snf_core_customer_groups", "snf_core_users", column: "customer_id"
228
311
  add_foreign_key "snf_core_delivery_orders", "snf_core_orders", column: "order_id"
229
312
  add_foreign_key "snf_core_groups", "snf_core_businesses", column: "business_id"
313
+ add_foreign_key "snf_core_item_requests", "snf_core_products", column: "product_id"
314
+ add_foreign_key "snf_core_item_requests", "snf_core_users", column: "user_id"
230
315
  add_foreign_key "snf_core_order_items", "snf_core_orders", column: "order_id"
231
316
  add_foreign_key "snf_core_order_items", "snf_core_store_inventories", column: "store_inventory_id"
232
317
  add_foreign_key "snf_core_orders", "snf_core_stores", column: "store_id"
@@ -238,5 +323,10 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_06_074713) do
238
323
  add_foreign_key "snf_core_stores", "snf_core_businesses", column: "business_id"
239
324
  add_foreign_key "snf_core_user_roles", "snf_core_roles", column: "role_id"
240
325
  add_foreign_key "snf_core_user_roles", "snf_core_users", column: "user_id"
326
+ add_foreign_key "snf_core_users", "snf_core_addresses", column: "address_id"
327
+ add_foreign_key "snf_core_users", "snf_core_users", column: "verified_by_id", on_delete: :nullify
328
+ add_foreign_key "snf_core_virtual_account_transactions", "snf_core_virtual_accounts", column: "from_account_id"
329
+ add_foreign_key "snf_core_virtual_account_transactions", "snf_core_virtual_accounts", column: "to_account_id"
330
+ add_foreign_key "snf_core_virtual_accounts", "snf_core_users", column: "user_id"
241
331
  add_foreign_key "snf_core_wallets", "snf_core_users", column: "user_id"
242
332
  end