bscf-core 0.3.4 → 0.3.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8d2f8615fb86130f7abc8d3ee5f5d31bb5848dbb1e0702085137123dc497867
4
- data.tar.gz: 4f139bbbc27a3657b8c0c00e39ed2045be174ff61ef4c7ac372628981e4aca47
3
+ metadata.gz: 0f663a88fd05be1fcf61a27b261cf3705cf813492cf81cb5ca4b995a475335b0
4
+ data.tar.gz: 3027a949d685908b5fa68253b93adf3ab34d36645d643aecd7d73037a04c0bf7
5
5
  SHA512:
6
- metadata.gz: 8ac6729e3c83271a58c765db29ed899be2f846f7620368d52db5de17dab0b528d2a357ab0cc6d08e6e9892bf8e31f07a7b8c18b563a3dd5ef26fc8b557d2b99e
7
- data.tar.gz: 50fd1d3532210e10c0c2b491a64f43d16d05624a5fc6346eb79eee330764a473be133976062b7c0fda22b26fb6ae866df1e64fd8cb4ac4030cb240d78c03f164
6
+ metadata.gz: 1f0e784375d6ceb2d2bb801a9b89788d9c281a68e965dc3b15ba651a2a6f72dbdfc045be1bd4cc4f65450d0cb7d4e7b11e13d6dd4933b11fd750f8a0af3e7388
7
+ data.tar.gz: 40af2d0ffd652e1f8405533d1f8701676c372c907265f9ce6a4e6d9df43e034d27fd8538cfaeca52e5e8ebeb366605ca3aa9743ef14aec3cb0ec004b27278eab
@@ -0,0 +1,40 @@
1
+ module Bscf
2
+ module Core
3
+ class BusinessDocument < ApplicationRecord
4
+ belongs_to :business
5
+ has_one_attached :file
6
+
7
+ before_validation :format_document_name
8
+
9
+ validates :document_number, presence: true
10
+ validates :document_name, presence: true
11
+ validates :business_id, presence: true
12
+ validates :verified_at, presence: true, if: :is_verified?
13
+ validates :file, presence: true
14
+ validate :block_executable_files
15
+
16
+ scope :verified, -> { where(is_verified: true) }
17
+ scope :unverified, -> { where(is_verified: false) }
18
+
19
+ private
20
+
21
+ def format_document_name
22
+ self.document_name = document_name.strip.titleize if document_name.present?
23
+ end
24
+
25
+ def block_executable_files
26
+ return unless file.attached?
27
+
28
+ if file.content_type.in?(%w[
29
+ application/x-msdownload
30
+ application/x-executable
31
+ application/x-msdos-program
32
+ application/x-ms-dos-executable
33
+ application/x-shellscript
34
+ ])
35
+ errors.add(:file, "cannot be an executable file")
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,30 +1,29 @@
1
1
  module Bscf::Core
2
2
  class DeliveryOrder < ApplicationRecord
3
3
  belongs_to :order
4
- belongs_to :delivery_address, class_name: "Bscf::Core::Address"
4
+ belongs_to :pickup_address, class_name: "Bscf::Core::Address"
5
+ belongs_to :dropoff_address, class_name: "Bscf::Core::Address"
6
+ belongs_to :driver, class_name: "Bscf::Core::User", optional: true
5
7
 
6
8
  has_many :delivery_order_items, dependent: :destroy
7
9
  has_many :order_items, through: :delivery_order_items
8
10
  has_many :products, through: :delivery_order_items
9
11
 
10
- attribute :delivery_start_time, :datetime
11
- attribute :delivery_end_time, :datetime
12
- attribute :actual_delivery_time, :datetime
13
-
14
- validates :contact_phone, :status, :estimated_delivery_time, presence: true
12
+ validates :buyer_phone, :seller_phone, :driver_phone,
13
+ :status, :estimated_delivery_time, presence: true
15
14
  validate :end_time_after_start_time, if: -> { delivery_start_time.present? && delivery_end_time.present? }
16
15
 
17
16
  before_save :update_delivery_times
18
17
  before_save :calculate_actual_delivery_time
19
-
20
18
  after_save :sync_items_status, if: :saved_change_to_status?
21
19
 
22
20
  enum :status, {
23
21
  pending: 0,
24
22
  in_transit: 1,
25
- delivered: 2,
26
- failed: 3,
27
- cancelled: 4
23
+ picked_up: 2,
24
+ delivered: 3,
25
+ failed: 4,
26
+ cancelled: 5
28
27
  }
29
28
 
30
29
  def delivery_duration
@@ -8,7 +8,6 @@ module Bscf
8
8
 
9
9
  enum :listing_type, { buy: 0, sell: 1 }
10
10
  enum :status, { open: 0, partially_matched: 1, matched: 2, completed: 3, cancelled: 4 }
11
-
12
11
  end
13
12
  end
14
13
  end
@@ -2,7 +2,12 @@ module Bscf
2
2
  module Core
3
3
  class Product < ApplicationRecord
4
4
  belongs_to :category
5
+ has_one_attached :thumbnail
6
+ has_many_attached :images
5
7
 
8
+ validates :sku, presence: true, uniqueness: true
9
+ validates :name, presence: true
10
+ validates :description, presence: true
6
11
  has_many :rfq_items
7
12
  has_many :request_for_quotations, through: :rfq_items
8
13
 
@@ -0,0 +1,12 @@
1
+ module Bscf::Core
2
+ class Vehicle < ApplicationRecord
3
+ belongs_to :driver, class_name: "Bscf::Core::User", optional: true
4
+
5
+ validates :plate_number, presence: true, uniqueness: { case_sensitive: false }
6
+ validates :vehicle_type, presence: true
7
+ validates :brand, presence: true
8
+ validates :model, presence: true
9
+ validates :year, presence: true, numericality: { only_integer: true, greater_than: 1900, less_than_or_equal_to: Time.current.year }
10
+ validates :color, presence: true
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ module Bscf::Core
2
+ class WholesalerProduct < ApplicationRecord
3
+ belongs_to :business
4
+ belongs_to :product
5
+
6
+ validates :minimum_order_quantity, presence: true, numericality: { greater_than: 0 }
7
+ validates :wholesale_price, presence: true, numericality: { greater_than: 0 }
8
+ validates :available_quantity, presence: true, numericality: { greater_than_or_equal_to: 0 }
9
+ validates :status, presence: true
10
+
11
+ validate :business_must_be_wholesaler
12
+ validate :unique_product_per_wholesaler
13
+
14
+ enum :status, {
15
+ active: 0,
16
+ inactive: 1,
17
+ out_of_stock: 2
18
+ }
19
+
20
+ private
21
+
22
+ def business_must_be_wholesaler
23
+ if business && !business.wholesaler?
24
+ errors.add(:business, "must be a wholesaler")
25
+ end
26
+ end
27
+
28
+ def unique_product_per_wholesaler
29
+ if WholesalerProduct.where(business: business, product: product)
30
+ .where.not(id: id)
31
+ .exists?
32
+ errors.add(:product, "already exists for this wholesaler")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,14 +1,14 @@
1
1
  class CreateBscfCoreMarketplaceListings < ActiveRecord::Migration[8.0]
2
2
  def change
3
3
  create_table :bscf_core_marketplace_listings do |t|
4
- t.references :user, null: false, foreign_key: {to_table: :bscf_core_users}
4
+ t.references :user, null: false, foreign_key: { to_table: :bscf_core_users }
5
5
  t.integer :listing_type, null: false
6
6
  t.integer :status, null: false
7
7
  t.boolean :allow_partial_match, null: false, default: false
8
8
  t.datetime :preferred_delivery_date
9
9
  t.datetime :expires_at
10
10
  t.boolean :is_active, default: true
11
- t.references :address, null: false, foreign_key: {to_table: :bscf_core_addresses}
11
+ t.references :address, null: false, foreign_key: { to_table: :bscf_core_addresses }
12
12
 
13
13
  t.timestamps
14
14
  end
@@ -0,0 +1,14 @@
1
+ class CreateBscfCoreWholesalerProducts < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :bscf_core_wholesaler_products do |t|
4
+ t.references :business, null: false, foreign_key: { to_table: :bscf_core_businesses }
5
+ t.references :product, null: false, foreign_key: { to_table: :bscf_core_products }
6
+ t.integer :minimum_order_quantity, null: false, default: 1
7
+ t.decimal :wholesale_price
8
+ t.integer :available_quantity, null: false, default: 0
9
+ t.integer :status, null: false, default: 0
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ class CreateBscfCoreBusinessDocuments < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :bscf_core_business_documents do |t|
4
+ t.references :business, null: false, foreign_key: { to_table: :bscf_core_businesses }
5
+ t.string :document_number, null: false
6
+ t.string :document_name, null: false
7
+ t.string :document_description
8
+ t.datetime :verified_at
9
+ t.boolean :is_verified, null: false, default: false
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,57 @@
1
+ # This migration comes from active_storage (originally 20170806125915)
2
+ class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
3
+ def change
4
+ # Use Active Record's configured type for primary and foreign keys
5
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
6
+
7
+ create_table :active_storage_blobs, id: primary_key_type do |t|
8
+ t.string :key, null: false
9
+ t.string :filename, null: false
10
+ t.string :content_type
11
+ t.text :metadata
12
+ t.string :service_name, null: false
13
+ t.bigint :byte_size, null: false
14
+ t.string :checksum
15
+
16
+ if connection.supports_datetime_with_precision?
17
+ t.datetime :created_at, precision: 6, null: false
18
+ else
19
+ t.datetime :created_at, null: false
20
+ end
21
+
22
+ t.index [ :key ], unique: true
23
+ end
24
+
25
+ create_table :active_storage_attachments, id: primary_key_type do |t|
26
+ t.string :name, null: false
27
+ t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28
+ t.references :blob, null: false, type: foreign_key_type
29
+
30
+ if connection.supports_datetime_with_precision?
31
+ t.datetime :created_at, precision: 6, null: false
32
+ else
33
+ t.datetime :created_at, null: false
34
+ end
35
+
36
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
37
+ t.foreign_key :active_storage_blobs, column: :blob_id
38
+ end
39
+
40
+ create_table :active_storage_variant_records, id: primary_key_type do |t|
41
+ t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42
+ t.string :variation_digest, null: false
43
+
44
+ t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
45
+ t.foreign_key :active_storage_blobs, column: :blob_id
46
+ end
47
+ end
48
+
49
+ private
50
+ def primary_and_foreign_key_types
51
+ config = Rails.configuration.generators
52
+ setting = config.options[config.orm][:primary_key_type]
53
+ primary_key_type = setting || :primary_key
54
+ foreign_key_type = setting || :bigint
55
+ [ primary_key_type, foreign_key_type ]
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ class CreateBscfCoreVehicles < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :bscf_core_vehicles do |t|
4
+ t.references :driver, foreign_key: { to_table: :bscf_core_users }, null: true
5
+ t.string :plate_number, null: false
6
+ t.string :vehicle_type, null: false
7
+ t.string :brand, null: false
8
+ t.string :model, null: false
9
+ t.integer :year, null: false
10
+ t.string :color, null: false
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :bscf_core_vehicles, :plate_number, unique: true
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ class AddDeliveryDetailsToDeliveryOrders < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_reference :bscf_core_delivery_orders, :driver, foreign_key: { to_table: :bscf_core_users }
4
+ add_reference :bscf_core_delivery_orders, :pickup_address, null: false, foreign_key: { to_table: :bscf_core_addresses }
5
+ add_column :bscf_core_delivery_orders, :buyer_phone, :string, null: false
6
+ add_column :bscf_core_delivery_orders, :seller_phone, :string, null: false
7
+ rename_column :bscf_core_delivery_orders, :delivery_address_id, :dropoff_address_id
8
+ rename_column :bscf_core_delivery_orders, :contact_phone, :driver_phone
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class AddActualDeliveryTimeToDeliveryOrders < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :bscf_core_delivery_orders, :actual_delivery_time, :datetime
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  module Bscf
2
2
  module Core
3
- VERSION = "0.3.4"
3
+ VERSION = "0.3.6"
4
4
  end
5
5
  end
@@ -0,0 +1,23 @@
1
+ FactoryBot.define do
2
+ factory :business_document, class: 'Bscf::Core::BusinessDocument' do
3
+ association :business, factory: :business
4
+ document_number { "DOC-#{SecureRandom.hex(4).upcase}" }
5
+ document_name { "Business License" }
6
+ document_description { "Official business license document" }
7
+ is_verified { false }
8
+
9
+ after(:build) do |document|
10
+ document.file.attach(
11
+ io: StringIO.new("test content"),
12
+ filename: 'sample.pdf',
13
+ content_type: 'application/pdf'
14
+ )
15
+ end
16
+
17
+ trait :verified do
18
+ is_verified { true }
19
+ verified_at { Time.current }
20
+ association :business, factory: :business
21
+ end
22
+ end
23
+ end
@@ -1,11 +1,34 @@
1
1
  FactoryBot.define do
2
2
  factory :delivery_order, class: 'Bscf::Core::DeliveryOrder' do
3
3
  association :order
4
- association :delivery_address, factory: :address
5
- contact_phone { Faker::PhoneNumber.phone_number }
4
+ association :pickup_address, factory: :address
5
+ association :dropoff_address, factory: :address
6
+ driver { nil }
7
+
8
+ buyer_phone { Faker::PhoneNumber.phone_number }
9
+ seller_phone { Faker::PhoneNumber.phone_number }
10
+ driver_phone { Faker::PhoneNumber.phone_number }
6
11
  delivery_notes { Faker::Lorem.paragraph }
7
12
  estimated_delivery_time { 2.days.from_now }
8
- actual_delivery_time { nil }
13
+ delivery_start_time { nil }
14
+ delivery_end_time { nil }
9
15
  status { :pending }
16
+
17
+ trait :with_driver do
18
+ association :driver, factory: :user
19
+ end
20
+
21
+ trait :in_transit do
22
+ with_driver
23
+ status { :in_transit }
24
+ delivery_start_time { Time.current }
25
+ end
26
+
27
+ trait :delivered do
28
+ with_driver
29
+ status { :delivered }
30
+ delivery_start_time { 2.hours.ago }
31
+ delivery_end_time { Time.current }
32
+ end
10
33
  end
11
34
  end
@@ -1,8 +1,28 @@
1
1
  FactoryBot.define do
2
2
  factory :product, class: 'Bscf::Core::Product' do
3
+ category
4
+ sequence(:sku) { |n| "SKU-#{n}" }
3
5
  name { Faker::Commerce.product_name }
4
6
  description { Faker::Lorem.paragraph }
5
- association :category, factory: :category
6
- base_price { Faker::Commerce.price(range: 0..1000.0) }
7
+
8
+ after(:build) do |product|
9
+ product.thumbnail.attach(
10
+ io: StringIO.new("test image"),
11
+ filename: 'thumbnail.jpg',
12
+ content_type: 'image/jpeg'
13
+ )
14
+ end
15
+
16
+ trait :with_images do
17
+ after(:build) do |product|
18
+ 2.times do |i|
19
+ product.images.attach(
20
+ io: StringIO.new("test image #{i}"),
21
+ filename: "image_#{i}.jpg",
22
+ content_type: 'image/jpeg'
23
+ )
24
+ end
25
+ end
26
+ end
7
27
  end
8
28
  end
@@ -0,0 +1,11 @@
1
+ FactoryBot.define do
2
+ factory :vehicle, class: 'Bscf::Core::Vehicle' do
3
+ association :driver, factory: :user
4
+ sequence(:plate_number) { |n| "ABC#{n}123" }
5
+ vehicle_type { [ 'Truck', 'Van', 'Pickup' ].sample }
6
+ brand { [ 'Toyota', 'Ford', 'Mercedes', 'Volvo' ].sample }
7
+ model { [ 'Hilux', 'Transit', 'Actros', 'FH16' ].sample }
8
+ year { rand(2015..Time.current.year) }
9
+ color { [ 'White', 'Black', 'Silver', 'Blue' ].sample }
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ FactoryBot.define do
2
+ factory :wholesaler_product, class: 'Bscf::Core::WholesalerProduct' do
3
+ association :business, :wholesaler
4
+ association :product
5
+ minimum_order_quantity { Faker::Number.between(from: 5, to: 100) }
6
+ wholesale_price { Faker::Commerce.price(range: 50..1000.0) }
7
+ available_quantity { Faker::Number.between(from: 100, to: 1000) }
8
+ status { :active }
9
+ end
10
+
11
+ trait :inactive do
12
+ status { :inactive }
13
+ end
14
+
15
+ trait :out_of_stock do
16
+ status { :out_of_stock }
17
+ available_quantity { 0 }
18
+ end
19
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bscf-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Asrat
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-07 00:00:00.000000000 Z
10
+ date: 2025-04-10 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: active_model_serializers
@@ -289,6 +289,7 @@ files:
289
289
  - app/models/bscf/core/address.rb
290
290
  - app/models/bscf/core/application_record.rb
291
291
  - app/models/bscf/core/business.rb
292
+ - app/models/bscf/core/business_document.rb
292
293
  - app/models/bscf/core/category.rb
293
294
  - app/models/bscf/core/delivery_order.rb
294
295
  - app/models/bscf/core/delivery_order_item.rb
@@ -304,9 +305,11 @@ files:
304
305
  - app/models/bscf/core/user.rb
305
306
  - app/models/bscf/core/user_profile.rb
306
307
  - app/models/bscf/core/user_role.rb
308
+ - app/models/bscf/core/vehicle.rb
307
309
  - app/models/bscf/core/virtual_account.rb
308
310
  - app/models/bscf/core/virtual_account_transaction.rb
309
- - app/services/token_service.rb
311
+ - app/models/bscf/core/wholesaler_product.rb
312
+ - app/services/bscf/core/token_service.rb
310
313
  - config/database.yml
311
314
  - config/routes.rb
312
315
  - db/migrate/20250326065606_create_bscf_core_users.rb
@@ -328,6 +331,12 @@ files:
328
331
  - db/migrate/20250330181143_create_bscf_core_delivery_orders.rb
329
332
  - db/migrate/20250330190145_create_bscf_core_delivery_order_items.rb
330
333
  - db/migrate/20250331113323_create_bscf_core_marketplace_listings.rb
334
+ - db/migrate/20250402120554_create_bscf_core_wholesaler_products.rb
335
+ - db/migrate/20250403190620_create_bscf_core_business_documents.rb
336
+ - db/migrate/20250403194629_create_active_storage_tables.active_storage.rb
337
+ - db/migrate/20250410115524_create_bscf_core_vehicles.rb
338
+ - db/migrate/20250410181144_add_delivery_details_to_delivery_orders.rb
339
+ - db/migrate/20250410181145_add_actual_delivery_time_to_delivery_orders.rb
331
340
  - lib/bscf/core.rb
332
341
  - lib/bscf/core/engine.rb
333
342
  - lib/bscf/core/version.rb
@@ -335,6 +344,7 @@ files:
335
344
  - lib/tasks/bscf/core_tasks.rake
336
345
  - lib/tasks/release.rake
337
346
  - spec/factories/bscf/core/addresses.rb
347
+ - spec/factories/bscf/core/business_documents.rb
338
348
  - spec/factories/bscf/core/businesses.rb
339
349
  - spec/factories/bscf/core/categories.rb
340
350
  - spec/factories/bscf/core/delivery_order_items.rb
@@ -351,8 +361,10 @@ files:
351
361
  - spec/factories/bscf/core/user_profiles.rb
352
362
  - spec/factories/bscf/core/user_roles.rb
353
363
  - spec/factories/bscf/core/users.rb
364
+ - spec/factories/bscf/core/vehicles.rb
354
365
  - spec/factories/bscf/core/virtual_account_transactions.rb
355
366
  - spec/factories/bscf/core/virtual_accounts.rb
367
+ - spec/factories/bscf/core/wholesaler_products.rb
356
368
  homepage: https://mksaddis.com/
357
369
  licenses:
358
370
  - MIT
@@ -374,7 +386,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
374
386
  - !ruby/object:Gem::Version
375
387
  version: '0'
376
388
  requirements: []
377
- rubygems_version: 3.6.5
389
+ rubygems_version: 3.6.2
378
390
  specification_version: 4
379
391
  summary: An Engine for Supply Chain Financing
380
392
  test_files: []