dscf-marketplace 0.7.6 → 0.8.0
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 +4 -4
- data/app/controllers/dscf/marketplace/aggregator_listings_controller.rb +40 -0
- data/app/controllers/dscf/marketplace/orders_controller.rb +18 -1
- data/app/controllers/dscf/marketplace/products_controller.rb +1 -1
- data/app/controllers/dscf/marketplace/supplier_products_controller.rb +7 -8
- data/app/controllers/dscf/marketplace/suppliers_controller.rb +28 -0
- data/app/models/dscf/marketplace/aggregator_listing.rb +40 -0
- data/app/models/dscf/marketplace/order.rb +1 -0
- data/app/models/dscf/marketplace/order_invoice.rb +39 -0
- data/app/models/dscf/marketplace/product.rb +4 -4
- data/app/models/dscf/marketplace/supplier.rb +42 -0
- data/app/models/dscf/marketplace/supplier_product.rb +4 -2
- data/app/policies/dscf/marketplace/aggregator_listing_policy.rb +29 -0
- data/app/policies/dscf/marketplace/supplier_policy.rb +25 -0
- data/app/serializers/dscf/marketplace/aggregator_listing_serializer.rb +27 -0
- data/app/serializers/dscf/marketplace/product_serializer.rb +1 -1
- data/app/serializers/dscf/marketplace/supplier_product_serializer.rb +3 -2
- data/app/serializers/dscf/marketplace/supplier_serializer.rb +11 -0
- data/app/services/dscf/marketplace/invoice_pdf_generator.rb +110 -0
- data/config/routes.rb +16 -0
- data/db/migrate/20260514000001_create_dscf_marketplace_suppliers.rb +20 -0
- data/db/migrate/20260514000002_add_supplier_id_to_supplier_products.rb +12 -0
- data/db/migrate/20260514000003_create_dscf_marketplace_order_invoices.rb +19 -0
- data/db/migrate/20260514000004_replace_base_price_with_gross_weight_on_products.rb +6 -0
- data/db/migrate/20260514000005_create_dscf_marketplace_aggregator_listings.rb +22 -0
- data/db/migrate/20260514000006_add_supplier_id_to_addresses.rb +9 -0
- data/lib/dscf/marketplace/version.rb +1 -1
- data/spec/factories/dscf/marketplace/aggregator_listings.rb +9 -0
- data/spec/factories/dscf/marketplace/order_invoices.rb +11 -0
- data/spec/factories/dscf/marketplace/products.rb +1 -1
- data/spec/factories/dscf/marketplace/suppliers.rb +10 -0
- metadata +23 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f0f591983cea40d4d2f6a5c4467b7d545de79760fc7911ab344859f7511c77a
|
|
4
|
+
data.tar.gz: 99b10ebe989e7152e2727da4c789dc857a33bf348192f706618f041c22bddc71
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 81582f57d99751874955a157a46e73588352f38e1c93ae2ac08a11f2f5b7419df0cbd16076a4d0653aa0026d219a9e183b58ee7cb432077aed6365a6bc79dfc5
|
|
7
|
+
data.tar.gz: fe8a10f50d36184274dceeb2d36d09406d5beda9665b064d0a45b6858a6e4088fd6ad5bcf053f56d8e3cba9f7fa37a9e860a23c5e3d71645f8a3dcf6facb281e
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class AggregatorListingsController < ApplicationController
|
|
4
|
+
include Dscf::Core::Common
|
|
5
|
+
|
|
6
|
+
def feed
|
|
7
|
+
authorize @clazz.new, :index?
|
|
8
|
+
listings = @clazz.active.includes(supplier_product: :product)
|
|
9
|
+
|
|
10
|
+
options = {
|
|
11
|
+
include: default_serializer_includes[:index] || [],
|
|
12
|
+
meta: { resource_type: "aggregator_feed" }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
render_success(data: listings, serializer_options: options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def my_listings
|
|
19
|
+
authorize @clazz.new, :my_listings?
|
|
20
|
+
businesses = current_user.businesses.pluck(:id)
|
|
21
|
+
listings = @clazz.where(aggregator_id: businesses).includes(supplier_product: :product)
|
|
22
|
+
|
|
23
|
+
options = {
|
|
24
|
+
include: default_serializer_includes[:index] || [],
|
|
25
|
+
meta: { resource_type: "my_aggregator_listings" }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
render_success(data: listings, serializer_options: options)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def model_params
|
|
34
|
+
params.require(:aggregator_listing).permit(
|
|
35
|
+
:aggregator_id, :supplier_product_id, :price, :quantity, :status
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -68,12 +68,29 @@ module Dscf
|
|
|
68
68
|
authorize @obj, :complete?
|
|
69
69
|
if @obj.can_be_completed? && @obj.update(status: :completed)
|
|
70
70
|
@obj.order_items.update_all(status: OrderItem.statuses[:fulfilled])
|
|
71
|
-
|
|
71
|
+
begin
|
|
72
|
+
invoice = Dscf::Marketplace::InvoicePdfGenerator.new(@obj).generate!
|
|
73
|
+
render_success("orders.success.completed", data: @obj, meta: { invoice_id: invoice.id })
|
|
74
|
+
rescue => e
|
|
75
|
+
Rails.logger.error("Invoice generation failed for order #{@obj.id}: #{e.message}")
|
|
76
|
+
render_success("orders.success.completed", data: @obj)
|
|
77
|
+
end
|
|
72
78
|
else
|
|
73
79
|
render_error("orders.errors.complete_failed")
|
|
74
80
|
end
|
|
75
81
|
end
|
|
76
82
|
|
|
83
|
+
def invoice
|
|
84
|
+
@obj = find_record
|
|
85
|
+
authorize @obj, :show?
|
|
86
|
+
invoice = @obj.order_invoice
|
|
87
|
+
if invoice&.pdf_file&.attached?
|
|
88
|
+
redirect_to rails_blob_url(invoice.pdf_file, disposition: "attachment")
|
|
89
|
+
else
|
|
90
|
+
render_error("orders.errors.no_invoice")
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
77
94
|
def my_orders
|
|
78
95
|
authorize @clazz.new, :my_orders?
|
|
79
96
|
service = MyResourceService.new(current_user)
|
|
@@ -71,22 +71,21 @@ module Dscf
|
|
|
71
71
|
|
|
72
72
|
def model_params
|
|
73
73
|
params.require(:supplier_product).permit(
|
|
74
|
-
:business_id, :product_id, :
|
|
75
|
-
:minimum_order_quantity, :status
|
|
76
|
-
:supplier_name, :supplier_address, :supplier_contact_phone
|
|
74
|
+
:business_id, :product_id, :supplier_id, :supplier_price,
|
|
75
|
+
:available_quantity, :minimum_order_quantity, :status
|
|
77
76
|
)
|
|
78
77
|
end
|
|
79
78
|
|
|
80
79
|
def eager_loaded_associations
|
|
81
|
-
[ :business, :product, :listings ]
|
|
80
|
+
[ :business, :product, :supplier, :listings ]
|
|
82
81
|
end
|
|
83
82
|
|
|
84
83
|
def default_serializer_includes
|
|
85
84
|
{
|
|
86
|
-
index: [ :business, :product ],
|
|
87
|
-
show: [ :business, :product, :listings ],
|
|
88
|
-
create: [ :business, :product ],
|
|
89
|
-
update: [ :business, :product ]
|
|
85
|
+
index: [ :business, :product, :supplier ],
|
|
86
|
+
show: [ :business, :product, :supplier, :listings ],
|
|
87
|
+
create: [ :business, :product, :supplier ],
|
|
88
|
+
update: [ :business, :product, :supplier ]
|
|
90
89
|
}
|
|
91
90
|
end
|
|
92
91
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class SuppliersController < ApplicationController
|
|
4
|
+
include Dscf::Core::Common
|
|
5
|
+
|
|
6
|
+
def my_suppliers
|
|
7
|
+
authorize @clazz.new, :index?
|
|
8
|
+
businesses = current_user.businesses.pluck(:id)
|
|
9
|
+
suppliers = @clazz.where(business_id: businesses)
|
|
10
|
+
|
|
11
|
+
options = {
|
|
12
|
+
include: default_serializer_includes[:index] || [],
|
|
13
|
+
meta: { resource_type: "my_suppliers" }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
render_success("suppliers.success.index", data: suppliers, serializer_options: options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def model_params
|
|
22
|
+
params.require(:supplier).permit(
|
|
23
|
+
:name, :address, :contact_phone, :business_type, :business_id
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Dscf::Marketplace
|
|
2
|
+
class AggregatorListing < ApplicationRecord
|
|
3
|
+
belongs_to :aggregator, class_name: "Dscf::Core::Business"
|
|
4
|
+
belongs_to :supplier_product, class_name: "Dscf::Marketplace::SupplierProduct"
|
|
5
|
+
|
|
6
|
+
has_many :order_items, class_name: "Dscf::Marketplace::OrderItem", as: :source, dependent: :nullify
|
|
7
|
+
|
|
8
|
+
delegate :name, :description, :thumbnail_url, :images_urls, to: :product, allow_nil: true
|
|
9
|
+
|
|
10
|
+
enum :status, { active: 0, draft: 1, paused: 2, sold_out: 3 }, default: :draft
|
|
11
|
+
|
|
12
|
+
validates :price, numericality: { greater_than: 0 }, presence: true
|
|
13
|
+
validates :quantity, numericality: { greater_than: 0 }, presence: true
|
|
14
|
+
validates :aggregator, presence: true
|
|
15
|
+
validates :supplier_product_id, presence: true, uniqueness: { scope: :aggregator_id }
|
|
16
|
+
|
|
17
|
+
scope :active, -> { where(status: :active) }
|
|
18
|
+
scope :by_aggregator, ->(aggregator_id) { where(aggregator_id: aggregator_id) }
|
|
19
|
+
|
|
20
|
+
def self.ransackable_attributes(_auth_object = nil)
|
|
21
|
+
%w[id aggregator_id supplier_product_id price quantity status created_at updated_at]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.ransackable_associations(_auth_object = nil)
|
|
25
|
+
%w[aggregator supplier_product]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def product
|
|
29
|
+
supplier_product&.product
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def available?
|
|
33
|
+
active? && quantity > 0
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def total_value
|
|
37
|
+
price * quantity
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -13,6 +13,7 @@ module Dscf::Marketplace
|
|
|
13
13
|
belongs_to :delivery_order, optional: true
|
|
14
14
|
belongs_to :dropoff_address, class_name: "Dscf::Core::Address", optional: true
|
|
15
15
|
has_many :order_items, dependent: :destroy, autosave: true
|
|
16
|
+
has_one :order_invoice, class_name: "Dscf::Marketplace::OrderInvoice", dependent: :destroy
|
|
16
17
|
accepts_nested_attributes_for :order_items, allow_destroy: true
|
|
17
18
|
|
|
18
19
|
validates :order_type, presence: true
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Dscf::Marketplace
|
|
2
|
+
class OrderInvoice < ApplicationRecord
|
|
3
|
+
belongs_to :order, class_name: "Dscf::Marketplace::Order"
|
|
4
|
+
|
|
5
|
+
enum :status, { generated: 0 }
|
|
6
|
+
|
|
7
|
+
before_validation :generate_invoice_number, on: :create
|
|
8
|
+
before_validation :set_issued_at, on: :create
|
|
9
|
+
|
|
10
|
+
validates :invoice_number, presence: true, uniqueness: { case_sensitive: false }
|
|
11
|
+
validates :subtotal, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
|
12
|
+
validates :total, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
|
13
|
+
validates :vat_amount, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
|
14
|
+
validates :status, presence: true
|
|
15
|
+
validates :issued_at, presence: true
|
|
16
|
+
validates :order, presence: true
|
|
17
|
+
|
|
18
|
+
has_one_attached :pdf_file
|
|
19
|
+
|
|
20
|
+
def self.ransackable_attributes(_auth_object = nil)
|
|
21
|
+
%w[id order_id invoice_number subtotal vat_amount total status issued_at created_at updated_at]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.ransackable_associations(_auth_object = nil)
|
|
25
|
+
%w[order]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def generate_invoice_number
|
|
31
|
+
year = Date.current.year.to_s
|
|
32
|
+
self.invoice_number ||= "INV-#{year}-#{SecureRandom.hex(4).upcase}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def set_issued_at
|
|
36
|
+
self.issued_at ||= Time.current
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -17,7 +17,7 @@ module Dscf
|
|
|
17
17
|
validates :name, presence: true, length: {maximum: 200}
|
|
18
18
|
validates :description, length: {maximum: 2000}, allow_blank: true
|
|
19
19
|
validates :category_id, presence: true
|
|
20
|
-
validates :
|
|
20
|
+
validates :gross_weight, presence: true, numericality: {greater_than: 0}
|
|
21
21
|
validates :business_only, inclusion: {in: [ true, false ]}
|
|
22
22
|
validates :unit_id, presence: true
|
|
23
23
|
validates :base_quantity, presence: true, numericality: {greater_than: 0}
|
|
@@ -29,7 +29,7 @@ module Dscf
|
|
|
29
29
|
|
|
30
30
|
# Ransack configuration for secure filtering
|
|
31
31
|
def self.ransackable_attributes(_auth_object = nil)
|
|
32
|
-
%w[id sku name description category_id unit_id
|
|
32
|
+
%w[id sku name description category_id unit_id gross_weight base_quantity business_only created_at updated_at]
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def self.ransackable_associations(_auth_object = nil)
|
|
@@ -41,8 +41,8 @@ module Dscf
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def price_per_unit
|
|
44
|
-
return nil unless
|
|
45
|
-
|
|
44
|
+
return nil unless base_quantity && base_quantity > 0
|
|
45
|
+
nil
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def category_path
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Dscf::Marketplace
|
|
2
|
+
class Supplier < ApplicationRecord
|
|
3
|
+
belongs_to :business, class_name: "Dscf::Core::Business"
|
|
4
|
+
has_many :supplier_products, class_name: "Dscf::Marketplace::SupplierProduct",
|
|
5
|
+
dependent: :nullify
|
|
6
|
+
has_many :addresses, class_name: "Dscf::Core::Address", dependent: :nullify
|
|
7
|
+
|
|
8
|
+
enum :business_type, {
|
|
9
|
+
retailer: 0,
|
|
10
|
+
wholesaler: 1,
|
|
11
|
+
distributor: 2,
|
|
12
|
+
manufacturer: 3,
|
|
13
|
+
importer: 4,
|
|
14
|
+
exporter: 5,
|
|
15
|
+
service_provider: 6,
|
|
16
|
+
aggregator: 7
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
before_validation :generate_code, on: :create
|
|
20
|
+
|
|
21
|
+
validates :name, presence: true
|
|
22
|
+
validates :code, presence: true, uniqueness: true
|
|
23
|
+
validates :business_type, presence: true
|
|
24
|
+
validates :business, presence: true
|
|
25
|
+
|
|
26
|
+
scope :by_business, ->(business_id) { where(business_id: business_id) }
|
|
27
|
+
|
|
28
|
+
def self.ransackable_attributes(_auth_object = nil)
|
|
29
|
+
%w[id name code address contact_phone business_type business_id created_at updated_at]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.ransackable_associations(_auth_object = nil)
|
|
33
|
+
%w[business supplier_products addresses]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def generate_code
|
|
39
|
+
self.code ||= "SUP-#{SecureRandom.hex(4).upcase}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -2,8 +2,10 @@ module Dscf::Marketplace
|
|
|
2
2
|
class SupplierProduct < ApplicationRecord
|
|
3
3
|
belongs_to :business, class_name: "Dscf::Core::Business"
|
|
4
4
|
belongs_to :product, class_name: "Dscf::Marketplace::Product"
|
|
5
|
+
belongs_to :supplier, class_name: "Dscf::Marketplace::Supplier", optional: true
|
|
5
6
|
|
|
6
7
|
delegate :name, :description, :thumbnail_url, :images_urls, to: :product, allow_nil: true
|
|
8
|
+
delegate :name, :address, :contact_phone, to: :supplier, prefix: true, allow_nil: true
|
|
7
9
|
|
|
8
10
|
has_many :listings, class_name: "Dscf::Marketplace::Listing"
|
|
9
11
|
has_many :order_items, class_name: "Dscf::Marketplace::OrderItem"
|
|
@@ -20,11 +22,11 @@ module Dscf::Marketplace
|
|
|
20
22
|
scope :by_product, ->(product_id) { where(product_id: product_id) }
|
|
21
23
|
|
|
22
24
|
def self.ransackable_attributes(_auth_object = nil)
|
|
23
|
-
%w[id business_id product_id supplier_price available_quantity minimum_order_quantity status created_at updated_at
|
|
25
|
+
%w[id business_id product_id supplier_id supplier_price available_quantity minimum_order_quantity status created_at updated_at]
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def self.ransackable_associations(_auth_object = nil)
|
|
27
|
-
%w[business product listings order_items]
|
|
29
|
+
%w[business product supplier listings order_items]
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
# Custom methods
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class AggregatorListingPolicy < Dscf::Core::ApplicationPolicy
|
|
4
|
+
def index?
|
|
5
|
+
true
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def show?
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create?
|
|
13
|
+
user.present?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def update?
|
|
17
|
+
user.present? && record.aggregator_id.in?(user.businesses.pluck(:id))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def feed?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def my_listings?
|
|
25
|
+
user.present?
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class SupplierPolicy < Dscf::Core::ApplicationPolicy
|
|
4
|
+
def index?
|
|
5
|
+
true
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def show?
|
|
9
|
+
true
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create?
|
|
13
|
+
user.present?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def update?
|
|
17
|
+
user.present? && record.business_id.in?(user.businesses.pluck(:id))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def my_suppliers?
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class AggregatorListingSerializer < ActiveModel::Serializer
|
|
4
|
+
attributes :id, :aggregator_id, :supplier_product_id, :price, :quantity,
|
|
5
|
+
:status, :created_at, :updated_at, :total_value, :available?,
|
|
6
|
+
:product_name, :product_description, :thumbnail_url, :images_urls
|
|
7
|
+
|
|
8
|
+
belongs_to :aggregator
|
|
9
|
+
|
|
10
|
+
def product_name
|
|
11
|
+
object.product&.name
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def product_description
|
|
15
|
+
object.product&.description
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def thumbnail_url
|
|
19
|
+
object.product&.thumbnail_url
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def images_urls
|
|
23
|
+
object.product&.images_urls
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -2,7 +2,7 @@ module Dscf
|
|
|
2
2
|
module Marketplace
|
|
3
3
|
class ProductSerializer < ActiveModel::Serializer
|
|
4
4
|
attributes :id, :sku, :name, :description, :category_id, :unit_id,
|
|
5
|
-
:
|
|
5
|
+
:gross_weight, :base_quantity, :business_only, :created_at, :updated_at,
|
|
6
6
|
:display_name, :price_per_unit, :thumbnail_url, :image_urls
|
|
7
7
|
|
|
8
8
|
belongs_to :category
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
module Dscf
|
|
2
2
|
module Marketplace
|
|
3
3
|
class SupplierProductSerializer < ActiveModel::Serializer
|
|
4
|
-
attributes :id, :business_id, :product_id, :
|
|
5
|
-
:minimum_order_quantity, :status, :created_at, :updated_at,
|
|
4
|
+
attributes :id, :business_id, :product_id, :supplier_id, :supplier_price,
|
|
5
|
+
:available_quantity, :minimum_order_quantity, :status, :created_at, :updated_at,
|
|
6
6
|
:in_stock?, :price_per_unit, :display_name, :name, :description,
|
|
7
7
|
:thumbnail_url, :images_urls,
|
|
8
8
|
:supplier_name, :supplier_address, :supplier_contact_phone
|
|
9
9
|
|
|
10
10
|
belongs_to :business
|
|
11
11
|
belongs_to :product
|
|
12
|
+
belongs_to :supplier
|
|
12
13
|
has_many :listings
|
|
13
14
|
end
|
|
14
15
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class SupplierSerializer < ActiveModel::Serializer
|
|
4
|
+
attributes :id, :name, :code, :address, :contact_phone, :business_type,
|
|
5
|
+
:business_id, :created_at, :updated_at
|
|
6
|
+
|
|
7
|
+
belongs_to :business
|
|
8
|
+
has_many :addresses
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Dscf
|
|
2
|
+
module Marketplace
|
|
3
|
+
class InvoicePdfGenerator
|
|
4
|
+
attr_reader :order, :order_invoice
|
|
5
|
+
|
|
6
|
+
def initialize(order)
|
|
7
|
+
@order = order
|
|
8
|
+
@business = order.ordered_to
|
|
9
|
+
@customer = order.ordered_by
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate!
|
|
13
|
+
@order_invoice = build_invoice
|
|
14
|
+
ActiveRecord::Base.transaction do
|
|
15
|
+
@order_invoice.save!
|
|
16
|
+
generate_and_attach_pdf
|
|
17
|
+
end
|
|
18
|
+
@order_invoice
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def build_invoice
|
|
24
|
+
Dscf::Marketplace::OrderInvoice.new(
|
|
25
|
+
order: order,
|
|
26
|
+
subtotal: calculate_subtotal,
|
|
27
|
+
vat_amount: calculate_vat,
|
|
28
|
+
total: calculate_total,
|
|
29
|
+
status: :generated
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def calculate_subtotal
|
|
34
|
+
order.order_items.sum(&:subtotal)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def calculate_vat
|
|
38
|
+
(calculate_subtotal * 0.15).round(2)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def calculate_total
|
|
42
|
+
calculate_subtotal + calculate_vat
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def generate_and_attach_pdf
|
|
46
|
+
pdf = render_pdf
|
|
47
|
+
@order_invoice.pdf_file.attach(
|
|
48
|
+
io: StringIO.new(pdf.render),
|
|
49
|
+
filename: "#{@order_invoice.invoice_number}.pdf",
|
|
50
|
+
content_type: "application/pdf"
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render_pdf
|
|
55
|
+
invoice = @order_invoice
|
|
56
|
+
business = @business
|
|
57
|
+
customer = @customer
|
|
58
|
+
order_items = order.order_items.includes(:product, :unit)
|
|
59
|
+
|
|
60
|
+
Prawn::Document.new do |pdf|
|
|
61
|
+
pdf.text "INVOICE", size: 24, style: :bold, align: :center
|
|
62
|
+
pdf.move_down 10
|
|
63
|
+
|
|
64
|
+
pdf.text business.name, size: 16, style: :bold
|
|
65
|
+
pdf.text "TIN: #{business.tin_number}" if business.tin_number.present?
|
|
66
|
+
pdf.text business.contact_phone if business.contact_phone.present?
|
|
67
|
+
pdf.move_down 10
|
|
68
|
+
|
|
69
|
+
pdf.text "Invoice #: #{invoice.invoice_number}"
|
|
70
|
+
pdf.text "Date: #{invoice.issued_at.strftime('%B %d, %Y')}"
|
|
71
|
+
pdf.text "Order #: #{order.id}"
|
|
72
|
+
pdf.move_down 10
|
|
73
|
+
|
|
74
|
+
pdf.text "Bill To:", style: :bold
|
|
75
|
+
pdf.text customer.name
|
|
76
|
+
pdf.move_down 15
|
|
77
|
+
|
|
78
|
+
table_data = [["Item", "Qty", "Unit", "Unit Price", "Total"]]
|
|
79
|
+
order_items.each do |item|
|
|
80
|
+
table_data << [
|
|
81
|
+
item.product_name,
|
|
82
|
+
item.quantity.to_s,
|
|
83
|
+
item.unit_name,
|
|
84
|
+
format_currency(item.unit_price),
|
|
85
|
+
format_currency(item.subtotal)
|
|
86
|
+
]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
pdf.table(table_data, header: true, width: pdf.bounds.width) do
|
|
90
|
+
row(0).font_style = :bold
|
|
91
|
+
cells.padding = 5
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
pdf.move_down 15
|
|
95
|
+
|
|
96
|
+
pdf.text "Subtotal: #{format_currency(invoice.subtotal)}", align: :right
|
|
97
|
+
pdf.text "VAT (15%): #{format_currency(invoice.vat_amount)}", align: :right
|
|
98
|
+
pdf.text "Total: #{format_currency(invoice.total)}", align: :right, size: 14, style: :bold
|
|
99
|
+
|
|
100
|
+
pdf.move_down 30
|
|
101
|
+
pdf.text "Thank you for your business!", align: :center, style: :italic
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def format_currency(amount)
|
|
106
|
+
"ETB #{format('%.2f', amount)}"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/config/routes.rb
CHANGED
|
@@ -19,6 +19,21 @@ Dscf::Marketplace::Engine.routes.draw do
|
|
|
19
19
|
|
|
20
20
|
resources :unit_conversions
|
|
21
21
|
|
|
22
|
+
# Supplier Management
|
|
23
|
+
resources :suppliers do
|
|
24
|
+
collection do
|
|
25
|
+
get "my_suppliers"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Aggregator Feed
|
|
30
|
+
resources :aggregator_listings do
|
|
31
|
+
collection do
|
|
32
|
+
get "feed"
|
|
33
|
+
get "my_listings"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
22
37
|
# Trading Models
|
|
23
38
|
resources :supplier_products do
|
|
24
39
|
resources :listings, only: [ :index ]
|
|
@@ -82,6 +97,7 @@ Dscf::Marketplace::Engine.routes.draw do
|
|
|
82
97
|
post "confirm"
|
|
83
98
|
post "cancel"
|
|
84
99
|
post "complete"
|
|
100
|
+
get "invoice"
|
|
85
101
|
end
|
|
86
102
|
collection do
|
|
87
103
|
get "my_orders"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class CreateDscfMarketplaceSuppliers < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :dscf_marketplace_suppliers do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :code, null: false
|
|
6
|
+
t.text :address
|
|
7
|
+
t.string :contact_phone
|
|
8
|
+
t.integer :business_type, null: false, default: 0
|
|
9
|
+
t.references :business,
|
|
10
|
+
null: false,
|
|
11
|
+
index: { name: "business_on_dm_suppliers_indx" },
|
|
12
|
+
foreign_key: { to_table: :dscf_core_businesses }
|
|
13
|
+
|
|
14
|
+
t.timestamps
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
add_index :dscf_marketplace_suppliers, :code, unique: true
|
|
18
|
+
add_index :dscf_marketplace_suppliers, :business_type
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class AddSupplierIdToSupplierProducts < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_reference :dscf_marketplace_supplier_products, :supplier,
|
|
4
|
+
null: true,
|
|
5
|
+
index: { name: "supplier_on_dm_sp_indx" },
|
|
6
|
+
foreign_key: { to_table: :dscf_marketplace_suppliers }
|
|
7
|
+
|
|
8
|
+
remove_column :dscf_marketplace_supplier_products, :supplier_name, :string if column_exists?(:dscf_marketplace_supplier_products, :supplier_name)
|
|
9
|
+
remove_column :dscf_marketplace_supplier_products, :supplier_address, :text if column_exists?(:dscf_marketplace_supplier_products, :supplier_address)
|
|
10
|
+
remove_column :dscf_marketplace_supplier_products, :supplier_contact_phone, :string if column_exists?(:dscf_marketplace_supplier_products, :supplier_contact_phone)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class CreateDscfMarketplaceOrderInvoices < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :dscf_marketplace_order_invoices do |t|
|
|
4
|
+
t.references :order, null: false,
|
|
5
|
+
foreign_key: { to_table: :dscf_marketplace_orders },
|
|
6
|
+
index: { name: "order_on_dm_oi_indx" }
|
|
7
|
+
t.string :invoice_number, null: false
|
|
8
|
+
t.decimal :subtotal, precision: 15, scale: 2, null: false
|
|
9
|
+
t.decimal :vat_amount, precision: 15, scale: 2, null: false, default: 0
|
|
10
|
+
t.decimal :total, precision: 15, scale: 2, null: false
|
|
11
|
+
t.integer :status, null: false, default: 0
|
|
12
|
+
t.datetime :issued_at, null: false
|
|
13
|
+
|
|
14
|
+
t.timestamps
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
add_index :dscf_marketplace_order_invoices, :invoice_number, unique: true
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
class ReplaceBasePriceWithGrossWeightOnProducts < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
remove_column :dscf_marketplace_products, :base_price, :decimal
|
|
4
|
+
add_column :dscf_marketplace_products, :gross_weight, :decimal, precision: 15, scale: 6, null: false, default: 0
|
|
5
|
+
end
|
|
6
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class CreateDscfMarketplaceAggregatorListings < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :dscf_marketplace_aggregator_listings do |t|
|
|
4
|
+
t.references :aggregator, null: false,
|
|
5
|
+
foreign_key: { to_table: :dscf_core_businesses },
|
|
6
|
+
index: { name: "aggregator_on_dm_al_indx" }
|
|
7
|
+
t.references :supplier_product, null: false,
|
|
8
|
+
foreign_key: { to_table: :dscf_marketplace_supplier_products },
|
|
9
|
+
index: { name: "sp_on_dm_al_indx" }
|
|
10
|
+
t.decimal :price, precision: 15, scale: 2, null: false
|
|
11
|
+
t.decimal :quantity, precision: 15, scale: 6, null: false, default: 0
|
|
12
|
+
t.integer :status, null: false, default: 0
|
|
13
|
+
|
|
14
|
+
t.timestamps
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
add_index :dscf_marketplace_aggregator_listings,
|
|
18
|
+
[:aggregator_id, :supplier_product_id],
|
|
19
|
+
unique: true,
|
|
20
|
+
name: "unique_aggregator_sp_indx"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class AddSupplierIdToAddresses < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_reference :dscf_core_addresses, :supplier, null: true,
|
|
4
|
+
foreign_key: { to_table: :dscf_marketplace_suppliers },
|
|
5
|
+
index: { name: "supplier_on_dca_indx" }
|
|
6
|
+
|
|
7
|
+
change_column_null :dscf_core_addresses, :user_id, true
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
factory :dscf_marketplace_aggregator_listing, class: "Dscf::Marketplace::AggregatorListing" do
|
|
3
|
+
association :aggregator, factory: :dscf_core_business
|
|
4
|
+
association :supplier_product, factory: :dscf_marketplace_supplier_product
|
|
5
|
+
price { 250.00 }
|
|
6
|
+
quantity { 50.0 }
|
|
7
|
+
status { :active }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
factory :dscf_marketplace_order_invoice, class: "Dscf::Marketplace::OrderInvoice" do
|
|
3
|
+
invoice_number { "INV-#{Date.current.year}-#{SecureRandom.hex(4).upcase}" }
|
|
4
|
+
subtotal { 1000.00 }
|
|
5
|
+
vat_amount { 150.00 }
|
|
6
|
+
total { 1150.00 }
|
|
7
|
+
status { :generated }
|
|
8
|
+
issued_at { Time.current }
|
|
9
|
+
association :order, factory: :dscf_marketplace_order
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -4,7 +4,7 @@ FactoryBot.define do
|
|
|
4
4
|
sequence(:name) { |n| "Product #{n}" }
|
|
5
5
|
description { "Product description" }
|
|
6
6
|
category { create(:dscf_marketplace_category) }
|
|
7
|
-
|
|
7
|
+
gross_weight { 1.5 }
|
|
8
8
|
business_only { false }
|
|
9
9
|
unit { create(:dscf_marketplace_unit) }
|
|
10
10
|
base_quantity { 1.0 }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
FactoryBot.define do
|
|
2
|
+
factory :dscf_marketplace_supplier, class: "Dscf::Marketplace::Supplier" do
|
|
3
|
+
name { Faker::Company.name }
|
|
4
|
+
code { "SUP-#{SecureRandom.hex(4).upcase}" }
|
|
5
|
+
address { Faker::Address.full_address }
|
|
6
|
+
contact_phone { Faker::PhoneNumber.phone_number }
|
|
7
|
+
business_type { :retailer }
|
|
8
|
+
association :business, factory: :dscf_core_business
|
|
9
|
+
end
|
|
10
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dscf-marketplace
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Asrat
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-05-
|
|
10
|
+
date: 2026-05-24 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|
|
@@ -35,14 +35,14 @@ dependencies:
|
|
|
35
35
|
requirements:
|
|
36
36
|
- - "~>"
|
|
37
37
|
- !ruby/object:Gem::Version
|
|
38
|
-
version: 0.3.
|
|
38
|
+
version: 0.3.3
|
|
39
39
|
type: :runtime
|
|
40
40
|
prerelease: false
|
|
41
41
|
version_requirements: !ruby/object:Gem::Requirement
|
|
42
42
|
requirements:
|
|
43
43
|
- - "~>"
|
|
44
44
|
- !ruby/object:Gem::Version
|
|
45
|
-
version: 0.3.
|
|
45
|
+
version: 0.3.3
|
|
46
46
|
- !ruby/object:Gem::Dependency
|
|
47
47
|
name: active_model_serializers
|
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -417,6 +417,7 @@ files:
|
|
|
417
417
|
- MIT-LICENSE
|
|
418
418
|
- Rakefile
|
|
419
419
|
- app/controllers/concerns/dscf/marketplace/demo_permission_bypass.rb
|
|
420
|
+
- app/controllers/dscf/marketplace/aggregator_listings_controller.rb
|
|
420
421
|
- app/controllers/dscf/marketplace/application_controller.rb
|
|
421
422
|
- app/controllers/dscf/marketplace/categories_controller.rb
|
|
422
423
|
- app/controllers/dscf/marketplace/delivery_order_items_controller.rb
|
|
@@ -433,10 +434,12 @@ files:
|
|
|
433
434
|
- app/controllers/dscf/marketplace/request_for_quotations_controller.rb
|
|
434
435
|
- app/controllers/dscf/marketplace/rfq_items_controller.rb
|
|
435
436
|
- app/controllers/dscf/marketplace/supplier_products_controller.rb
|
|
437
|
+
- app/controllers/dscf/marketplace/suppliers_controller.rb
|
|
436
438
|
- app/controllers/dscf/marketplace/unit_conversions_controller.rb
|
|
437
439
|
- app/controllers/dscf/marketplace/units_controller.rb
|
|
438
440
|
- app/jobs/dscf/marketplace/application_job.rb
|
|
439
441
|
- app/mailers/dscf/marketplace/application_mailer.rb
|
|
442
|
+
- app/models/dscf/marketplace/aggregator_listing.rb
|
|
440
443
|
- app/models/dscf/marketplace/application_record.rb
|
|
441
444
|
- app/models/dscf/marketplace/category.rb
|
|
442
445
|
- app/models/dscf/marketplace/delivery_order.rb
|
|
@@ -445,15 +448,18 @@ files:
|
|
|
445
448
|
- app/models/dscf/marketplace/delivery_vehicle.rb
|
|
446
449
|
- app/models/dscf/marketplace/listing.rb
|
|
447
450
|
- app/models/dscf/marketplace/order.rb
|
|
451
|
+
- app/models/dscf/marketplace/order_invoice.rb
|
|
448
452
|
- app/models/dscf/marketplace/order_item.rb
|
|
449
453
|
- app/models/dscf/marketplace/product.rb
|
|
450
454
|
- app/models/dscf/marketplace/quotation.rb
|
|
451
455
|
- app/models/dscf/marketplace/quotation_item.rb
|
|
452
456
|
- app/models/dscf/marketplace/request_for_quotation.rb
|
|
453
457
|
- app/models/dscf/marketplace/rfq_item.rb
|
|
458
|
+
- app/models/dscf/marketplace/supplier.rb
|
|
454
459
|
- app/models/dscf/marketplace/supplier_product.rb
|
|
455
460
|
- app/models/dscf/marketplace/unit.rb
|
|
456
461
|
- app/models/dscf/marketplace/unit_conversion.rb
|
|
462
|
+
- app/policies/dscf/marketplace/aggregator_listing_policy.rb
|
|
457
463
|
- app/policies/dscf/marketplace/category_policy.rb
|
|
458
464
|
- app/policies/dscf/marketplace/delivery_order_item_policy.rb
|
|
459
465
|
- app/policies/dscf/marketplace/delivery_order_policy.rb
|
|
@@ -468,9 +474,11 @@ files:
|
|
|
468
474
|
- app/policies/dscf/marketplace/quotation_policy.rb
|
|
469
475
|
- app/policies/dscf/marketplace/request_for_quotation_policy.rb
|
|
470
476
|
- app/policies/dscf/marketplace/rfq_item_policy.rb
|
|
477
|
+
- app/policies/dscf/marketplace/supplier_policy.rb
|
|
471
478
|
- app/policies/dscf/marketplace/supplier_product_policy.rb
|
|
472
479
|
- app/policies/dscf/marketplace/unit_conversion_policy.rb
|
|
473
480
|
- app/policies/dscf/marketplace/unit_policy.rb
|
|
481
|
+
- app/serializers/dscf/marketplace/aggregator_listing_serializer.rb
|
|
474
482
|
- app/serializers/dscf/marketplace/category_serializer.rb
|
|
475
483
|
- app/serializers/dscf/marketplace/delivery_order_item_serializer.rb
|
|
476
484
|
- app/serializers/dscf/marketplace/delivery_order_serializer.rb
|
|
@@ -486,12 +494,14 @@ files:
|
|
|
486
494
|
- app/serializers/dscf/marketplace/request_for_quotation_serializer.rb
|
|
487
495
|
- app/serializers/dscf/marketplace/rfq_item_serializer.rb
|
|
488
496
|
- app/serializers/dscf/marketplace/supplier_product_serializer.rb
|
|
497
|
+
- app/serializers/dscf/marketplace/supplier_serializer.rb
|
|
489
498
|
- app/serializers/dscf/marketplace/unit_conversion_serializer.rb
|
|
490
499
|
- app/serializers/dscf/marketplace/unit_serializer.rb
|
|
491
500
|
- app/serializers/dscf/marketplace/user_serializer.rb
|
|
492
501
|
- app/services/dscf/marketplace/delivery_order_service.rb
|
|
493
502
|
- app/services/dscf/marketplace/dispute_service.rb
|
|
494
503
|
- app/services/dscf/marketplace/gebeta_service.rb
|
|
504
|
+
- app/services/dscf/marketplace/invoice_pdf_generator.rb
|
|
495
505
|
- app/services/dscf/marketplace/my_resource_service.rb
|
|
496
506
|
- app/services/dscf/marketplace/rfq_response_service.rb
|
|
497
507
|
- app/services/dscf/marketplace/role_service.rb
|
|
@@ -534,6 +544,12 @@ files:
|
|
|
534
544
|
- db/migrate/20260310122000_add_missing_fields_to_dscf_core_user_roles.rb
|
|
535
545
|
- db/migrate/20260501000002_add_supplier_contact_fields_to_supplier_products.rb
|
|
536
546
|
- db/migrate/20260506100000_add_payment_references_to_dscf_marketplace_orders.rb
|
|
547
|
+
- db/migrate/20260514000001_create_dscf_marketplace_suppliers.rb
|
|
548
|
+
- db/migrate/20260514000002_add_supplier_id_to_supplier_products.rb
|
|
549
|
+
- db/migrate/20260514000003_create_dscf_marketplace_order_invoices.rb
|
|
550
|
+
- db/migrate/20260514000004_replace_base_price_with_gross_weight_on_products.rb
|
|
551
|
+
- db/migrate/20260514000005_create_dscf_marketplace_aggregator_listings.rb
|
|
552
|
+
- db/migrate/20260514000006_add_supplier_id_to_addresses.rb
|
|
537
553
|
- db/seeds.rb
|
|
538
554
|
- lib/dscf/marketplace.rb
|
|
539
555
|
- lib/dscf/marketplace/engine.rb
|
|
@@ -545,12 +561,14 @@ files:
|
|
|
545
561
|
- spec/factories/dscf/core/roles.rb
|
|
546
562
|
- spec/factories/dscf/core/user_roles.rb
|
|
547
563
|
- spec/factories/dscf/core/users.rb
|
|
564
|
+
- spec/factories/dscf/marketplace/aggregator_listings.rb
|
|
548
565
|
- spec/factories/dscf/marketplace/categories.rb
|
|
549
566
|
- spec/factories/dscf/marketplace/delivery_order_items.rb
|
|
550
567
|
- spec/factories/dscf/marketplace/delivery_orders.rb
|
|
551
568
|
- spec/factories/dscf/marketplace/delivery_stops.rb
|
|
552
569
|
- spec/factories/dscf/marketplace/delivery_vehicles.rb
|
|
553
570
|
- spec/factories/dscf/marketplace/listings.rb
|
|
571
|
+
- spec/factories/dscf/marketplace/order_invoices.rb
|
|
554
572
|
- spec/factories/dscf/marketplace/order_items.rb
|
|
555
573
|
- spec/factories/dscf/marketplace/orders.rb
|
|
556
574
|
- spec/factories/dscf/marketplace/products.rb
|
|
@@ -559,6 +577,7 @@ files:
|
|
|
559
577
|
- spec/factories/dscf/marketplace/request_for_quotations.rb
|
|
560
578
|
- spec/factories/dscf/marketplace/rfq_items.rb
|
|
561
579
|
- spec/factories/dscf/marketplace/supplier_products.rb
|
|
580
|
+
- spec/factories/dscf/marketplace/suppliers.rb
|
|
562
581
|
- spec/factories/dscf/marketplace/unit_conversions.rb
|
|
563
582
|
- spec/factories/dscf/marketplace/units.rb
|
|
564
583
|
homepage: https://mksaddis.com/
|