From 57a3fcf25fdb4bbd1e96a992ee03188db4b594ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Geonizeli?= Date: Sat, 14 Aug 2021 17:42:19 -0300 Subject: [PATCH] add BuyCryptoOrder and SellCryptoOrder entities --- app/models/buy_crypto_order.rb | 36 +++++ app/models/concerns/processable.rb | 21 +++ app/models/sell_crypto_order.rb | 36 +++++ ...20210814194443_create_buy_crypto_orders.rb | 15 ++ ...0210814194513_create_sell_crypto_orders.rb | 15 ++ db/schema.rb | 30 +++- db/seeds.rb | 2 +- erd.svg | 151 ++++++++++++------ spec/factories/buy_crypto_orders.rb | 34 ++++ spec/factories/sell_crypto_orders.rb | 34 ++++ spec/models/buy_crypto_order_spec.rb | 56 +++++++ spec/models/sell_crypto_order_spec.rb | 56 +++++++ 12 files changed, 437 insertions(+), 49 deletions(-) create mode 100644 app/models/buy_crypto_order.rb create mode 100644 app/models/concerns/processable.rb create mode 100644 app/models/sell_crypto_order.rb create mode 100644 db/migrate/20210814194443_create_buy_crypto_orders.rb create mode 100644 db/migrate/20210814194513_create_sell_crypto_orders.rb create mode 100644 spec/factories/buy_crypto_orders.rb create mode 100644 spec/factories/sell_crypto_orders.rb create mode 100644 spec/models/buy_crypto_order_spec.rb create mode 100644 spec/models/sell_crypto_order_spec.rb diff --git a/app/models/buy_crypto_order.rb b/app/models/buy_crypto_order.rb new file mode 100644 index 0000000..9bbd027 --- /dev/null +++ b/app/models/buy_crypto_order.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: buy_crypto_orders +# +# id :bigint not null, primary key +# paid_amount_cents :integer default(0), not null +# received_amount :decimal(20, 10) +# status :string not null +# created_at :datetime not null +# updated_at :datetime not null +# currency_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_buy_crypto_orders_on_currency_id (currency_id) +# index_buy_crypto_orders_on_user_id (user_id) +# +# Foreign Keys +# +# fk_rails_... (currency_id => currencies.id) +# fk_rails_... (user_id => users.id) +# +class BuyCryptoOrder < ApplicationRecord + include Processable + + belongs_to :user + belongs_to :currency + + monetize :paid_amount_cents + + validates :paid_amount_cents, presence: true, numericality: { greater_than: 0 } + validates :received_amount, presence: true, if: :completed? +end diff --git a/app/models/concerns/processable.rb b/app/models/concerns/processable.rb new file mode 100644 index 0000000..56bde7c --- /dev/null +++ b/app/models/concerns/processable.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +module Processable + extend Enumerize + extend ActiveSupport::Concern + + included do + enumerize :status, in: [:processing, :completed, :canceled], default: :processing + end + + def processing? + status == :processing + end + + def completed? + status == :completed + end + + def canceled? + status == :canceled + end +end diff --git a/app/models/sell_crypto_order.rb b/app/models/sell_crypto_order.rb new file mode 100644 index 0000000..3d0b175 --- /dev/null +++ b/app/models/sell_crypto_order.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: sell_crypto_orders +# +# id :bigint not null, primary key +# paid_amount :decimal(20, 10) default(0.0), not null +# received_amount_cents :integer +# status :string not null +# created_at :datetime not null +# updated_at :datetime not null +# currency_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_sell_crypto_orders_on_currency_id (currency_id) +# index_sell_crypto_orders_on_user_id (user_id) +# +# Foreign Keys +# +# fk_rails_... (currency_id => currencies.id) +# fk_rails_... (user_id => users.id) +# +class SellCryptoOrder < ApplicationRecord + include Processable + + belongs_to :user + belongs_to :currency + + monetize :received_amount_cents + + validates :paid_amount, presence: true, numericality: { greater_than: 0 } + validates :received_amount_cents, presence: true, if: :completed? +end diff --git a/db/migrate/20210814194443_create_buy_crypto_orders.rb b/db/migrate/20210814194443_create_buy_crypto_orders.rb new file mode 100644 index 0000000..ec40edc --- /dev/null +++ b/db/migrate/20210814194443_create_buy_crypto_orders.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true +class CreateBuyCryptoOrders < ActiveRecord::Migration[6.1] + def change + create_table(:buy_crypto_orders) do |t| + t.references(:user, null: false, foreign_key: true) + t.references(:currency, null: false, foreign_key: true) + + t.string(:status, null: false) + t.integer(:paid_amount_cents, null: false, default: 0) + t.decimal(:received_amount, precision: 20, scale: 10, null: true) + + t.timestamps + end + end +end diff --git a/db/migrate/20210814194513_create_sell_crypto_orders.rb b/db/migrate/20210814194513_create_sell_crypto_orders.rb new file mode 100644 index 0000000..2671c06 --- /dev/null +++ b/db/migrate/20210814194513_create_sell_crypto_orders.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true +class CreateSellCryptoOrders < ActiveRecord::Migration[6.1] + def change + create_table(:sell_crypto_orders) do |t| + t.references(:user, null: false, foreign_key: true) + t.references(:currency, null: false, foreign_key: true) + + t.string(:status, null: false) + t.decimal(:paid_amount, precision: 20, scale: 10, null: false, default: 0) + t.integer(:received_amount_cents, null: true) + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 586766c..7af2e1f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_08_12_011039) do +ActiveRecord::Schema.define(version: 2021_08_14_194513) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -65,6 +65,18 @@ ActiveRecord::Schema.define(version: 2021_08_12_011039) do t.index ["user_id"], name: "index_balances_on_user_id" end + create_table "buy_crypto_orders", force: :cascade do |t| + t.bigint "user_id", null: false + t.bigint "currency_id", null: false + t.string "status", null: false + t.integer "paid_amount_cents", default: 0, null: false + t.decimal "received_amount", precision: 20, scale: 10 + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["currency_id"], name: "index_buy_crypto_orders_on_currency_id" + t.index ["user_id"], name: "index_buy_crypto_orders_on_user_id" + end + create_table "currencies", force: :cascade do |t| t.string "name", null: false t.datetime "created_at", precision: 6, null: false @@ -80,6 +92,18 @@ ActiveRecord::Schema.define(version: 2021_08_12_011039) do t.index ["user_id"], name: "index_fiat_balances_on_user_id" end + create_table "sell_crypto_orders", force: :cascade do |t| + t.bigint "user_id", null: false + t.bigint "currency_id", null: false + t.string "status", null: false + t.decimal "paid_amount", precision: 20, scale: 10, default: "0.0", null: false + t.integer "received_amount_cents" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["currency_id"], name: "index_sell_crypto_orders_on_currency_id" + t.index ["user_id"], name: "index_sell_crypto_orders_on_user_id" + end + create_table "user_documents", force: :cascade do |t| t.string "status", null: false t.bigint "user_id", null: false @@ -106,6 +130,10 @@ ActiveRecord::Schema.define(version: 2021_08_12_011039) do add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "balances", "currencies" add_foreign_key "balances", "users" + add_foreign_key "buy_crypto_orders", "currencies" + add_foreign_key "buy_crypto_orders", "users" add_foreign_key "fiat_balances", "users" + add_foreign_key "sell_crypto_orders", "currencies" + add_foreign_key "sell_crypto_orders", "users" add_foreign_key "user_documents", "users" end diff --git a/db/seeds.rb b/db/seeds.rb index 092f13f..8008fe1 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -14,7 +14,7 @@ currency = Currency.create!(name: "CAKE") Balance.create!( user_id: user.id, currency_id: currency.id, - amount: (rand * (10000 - 0) + 0) + amount: rand * 10000 ) FiatBalance.create!(user_id: user.id, amount_cents: 15000) diff --git a/erd.svg b/erd.svg index f083169..966f06c 100644 --- a/erd.svg +++ b/erd.svg @@ -4,12 +4,12 @@ - - + + XStake - -XStake domain model + +XStake domain model m_AdminUser @@ -30,46 +30,92 @@ m_Balance - -Balance - -amount -decimal (20,10) ∗ -currency_id -integer (8) ∗ FK -user_id -integer (8) ∗ FK + +Balance + +amount +decimal (20,10) ∗ +currency_id +integer (8) ∗ FK +user_id +integer (8) ∗ FK + + + +m_BuyCryptoOrder + +BuyCryptoOrder + +currency_id +integer (8) ∗ FK +paid_amount_cents +integer ∗ +received_amount +decimal (20,10) ∗ +status +string ∗ +user_id +integer (8) ∗ FK - + m_Currency - -Currency - -name -string ∗ + +Currency + +name +string ∗ - + m_Currency->m_Balance - - + + + + + +m_Currency->m_BuyCryptoOrder + + + + + +m_SellCryptoOrder + +SellCryptoOrder + +currency_id +integer (8) ∗ FK +paid_amount +decimal (20,10) ∗ +received_amount_cents +integer ∗ +status +string ∗ +user_id +integer (8) ∗ FK + + + +m_Currency->m_SellCryptoOrder + + - + m_FiatBalance - -FiatBalance - -amount_cents -integer ∗ -amount_currency -string ∗ -user_id -integer (8) ∗ FK + +FiatBalance + +amount_cents +integer ∗ +amount_currency +string ∗ +user_id +integer (8) ∗ FK - + m_User User @@ -92,30 +138,41 @@ m_User->m_Balance - - + + + + +m_User->m_BuyCryptoOrder + + m_User->m_FiatBalance - + + + + +m_User->m_SellCryptoOrder + + - + m_UserDocument - -UserDocument - -status -string ∗ -user_id -integer (8) ∗ FK + +UserDocument + +status +string ∗ +user_id +integer (8) ∗ FK m_User->m_UserDocument - - + + diff --git a/spec/factories/buy_crypto_orders.rb b/spec/factories/buy_crypto_orders.rb new file mode 100644 index 0000000..0e05664 --- /dev/null +++ b/spec/factories/buy_crypto_orders.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: buy_crypto_orders +# +# id :bigint not null, primary key +# paid_amount_cents :integer default(0), not null +# received_amount :decimal(20, 10) +# status :string not null +# created_at :datetime not null +# updated_at :datetime not null +# currency_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_buy_crypto_orders_on_currency_id (currency_id) +# index_buy_crypto_orders_on_user_id (user_id) +# +# Foreign Keys +# +# fk_rails_... (currency_id => currencies.id) +# fk_rails_... (user_id => users.id) +# +FactoryBot.define do + factory :buy_crypto_order do + association :user + association :currency + status { :processing } + paid_amount_cents { rand(10000) } + received_amount { rand * 10000 } + end +end diff --git a/spec/factories/sell_crypto_orders.rb b/spec/factories/sell_crypto_orders.rb new file mode 100644 index 0000000..4d9e369 --- /dev/null +++ b/spec/factories/sell_crypto_orders.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: sell_crypto_orders +# +# id :bigint not null, primary key +# paid_amount :decimal(20, 10) default(0.0), not null +# received_amount_cents :integer +# status :string not null +# created_at :datetime not null +# updated_at :datetime not null +# currency_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_sell_crypto_orders_on_currency_id (currency_id) +# index_sell_crypto_orders_on_user_id (user_id) +# +# Foreign Keys +# +# fk_rails_... (currency_id => currencies.id) +# fk_rails_... (user_id => users.id) +# +FactoryBot.define do + factory :sell_crypto_order do + association :user + association :currency + status { :processing } + received_amount_cents { rand(10000) } + paid_amount { rand * 10000 } + end +end diff --git a/spec/models/buy_crypto_order_spec.rb b/spec/models/buy_crypto_order_spec.rb new file mode 100644 index 0000000..9ba1a66 --- /dev/null +++ b/spec/models/buy_crypto_order_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: buy_crypto_orders +# +# id :bigint not null, primary key +# paid_amount_cents :integer default(0), not null +# received_amount :decimal(20, 10) +# status :string not null +# created_at :datetime not null +# updated_at :datetime not null +# currency_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_buy_crypto_orders_on_currency_id (currency_id) +# index_buy_crypto_orders_on_user_id (user_id) +# +# Foreign Keys +# +# fk_rails_... (currency_id => currencies.id) +# fk_rails_... (user_id => users.id) +# +require "rails_helper" + +RSpec.describe(BuyCryptoOrder, type: :model) do + describe "validations" do + context "when status is `processing`" do + subject { build(:buy_crypto_order, status: :processing) } + + it { is_expected.to(validate_presence_of(:paid_amount_cents)) } + it { is_expected.not_to(validate_presence_of(:received_amount)) } + end + + context "when status is `completed`" do + subject { build(:buy_crypto_order, status: :completed) } + + it { is_expected.to(validate_presence_of(:paid_amount_cents)) } + it { is_expected.to(validate_presence_of(:received_amount)) } + end + + context "when status is `canceled`" do + subject { build(:buy_crypto_order, status: :canceled) } + + it { is_expected.to(validate_presence_of(:paid_amount_cents)) } + it { is_expected.not_to(validate_presence_of(:received_amount)) } + end + end + + describe "associations" do + it { is_expected.to(belong_to(:user)) } + it { is_expected.to(belong_to(:currency)) } + end +end diff --git a/spec/models/sell_crypto_order_spec.rb b/spec/models/sell_crypto_order_spec.rb new file mode 100644 index 0000000..651a618 --- /dev/null +++ b/spec/models/sell_crypto_order_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: sell_crypto_orders +# +# id :bigint not null, primary key +# paid_amount :decimal(20, 10) default(0.0), not null +# received_amount_cents :integer +# status :string not null +# created_at :datetime not null +# updated_at :datetime not null +# currency_id :bigint not null +# user_id :bigint not null +# +# Indexes +# +# index_sell_crypto_orders_on_currency_id (currency_id) +# index_sell_crypto_orders_on_user_id (user_id) +# +# Foreign Keys +# +# fk_rails_... (currency_id => currencies.id) +# fk_rails_... (user_id => users.id) +# +require "rails_helper" + +RSpec.describe(SellCryptoOrder, type: :model) do + describe "validations" do + context "when status is `processing`" do + subject { build(:sell_crypto_order, status: :processing) } + + it { is_expected.to(validate_presence_of(:paid_amount)) } + it { is_expected.not_to(validate_presence_of(:received_amount_cents)) } + end + + context "when status is `completed`" do + subject { build(:sell_crypto_order, status: :completed) } + + it { is_expected.to(validate_presence_of(:paid_amount)) } + it { is_expected.to(validate_presence_of(:received_amount_cents)) } + end + + context "when status is `canceled`" do + subject { build(:sell_crypto_order, status: :canceled) } + + it { is_expected.to(validate_presence_of(:paid_amount)) } + it { is_expected.not_to(validate_presence_of(:received_amount_cents)) } + end + end + + describe "associations" do + it { is_expected.to(belong_to(:user)) } + it { is_expected.to(belong_to(:currency)) } + end +end