dscf-marketplace 0.1.6 → 0.1.8
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/models/dscf/marketplace/delivery_order.rb +107 -0
- data/app/models/dscf/marketplace/delivery_order_item.rb +192 -0
- data/app/models/dscf/marketplace/delivery_vehicle.rb +61 -0
- data/app/models/dscf/marketplace/order.rb +131 -0
- data/app/models/dscf/marketplace/order_item.rb +38 -0
- data/app/models/dscf/marketplace/quotation.rb +37 -0
- data/db/migrate/20250828115509_create_dscf_marketplace_orders.rb +19 -0
- data/db/migrate/20250828115525_create_dscf_marketplace_order_items.rb +22 -0
- data/db/migrate/20250831110907_create_dscf_marketplace_delivery_orders.rb +21 -0
- data/db/migrate/20250831110921_create_dscf_marketplace_delivery_order_items.rb +27 -0
- data/db/migrate/20250831110933_create_dscf_marketplace_delivery_vehicles.rb +17 -0
- data/db/migrate/20250901074509_add_delivery_handoff_fields_to_delivery_order_items.rb +16 -0
- data/db/migrate/20250901074746_add_delivery_order_to_orders.rb +7 -0
- data/db/migrate/20250901075511_add_delivery_vehicle_to_delivery_orders.rb +7 -0
- data/db/migrate/20250901080134_add_fulfillment_type_to_orders.rb +6 -0
- data/lib/dscf/marketplace/version.rb +1 -1
- data/spec/factories/dscf/core/addresses.rb +15 -0
- data/spec/factories/dscf/marketplace/delivery_order_items.rb +46 -0
- data/spec/factories/dscf/marketplace/delivery_orders.rb +14 -0
- data/spec/factories/dscf/marketplace/delivery_vehicles.rb +37 -0
- data/spec/factories/dscf/marketplace/listings.rb +16 -4
- data/spec/factories/dscf/marketplace/order_items.rb +49 -0
- data/spec/factories/dscf/marketplace/orders.rb +45 -0
- data/spec/factories/dscf/marketplace/{dscf/marketplace/quotation_items.rb → quotation_items.rb} +1 -1
- data/spec/factories/dscf/marketplace/{dscf/marketplace/quotations.rb → quotations.rb} +4 -4
- metadata +26 -7
- data/README.md +0 -28
- /data/spec/factories/dscf/marketplace/{dscf/marketplace/request_for_quotations.rb → request_for_quotations.rb} +0 -0
- /data/spec/factories/dscf/marketplace/{dscf/marketplace/rfq_items.rb → rfq_items.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bbdcc2258fee7a63ff11ba8c87e73a47923e8dbb6d9737e55d6e3e9d7002822
|
4
|
+
data.tar.gz: 184a09306eb3c9de61c2e9edc82cdd49ed735e7bf91acae09b5ace7cd7e9f3a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 503e6dcdb6f209cbc278219572beb1deadbbeff8910e979914b4cd316e176528bc91130b4917bc874e3de36afb9e0f4c56978ecc3aa0dd54f4e175ee8cd2a071
|
7
|
+
data.tar.gz: e2790cb38d3a76cff511631c8b4fc7ae4b277e8faa2459241379ba3127438b242449438d363e5976dda72aa90a4d3b354e091ebf3d699bf4afb3e48a504689ef
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Dscf::Marketplace
|
2
|
+
class DeliveryOrder < ApplicationRecord
|
3
|
+
enum :status, {assigned: 0, picked_up: 1, in_transit: 2, delivered: 3, failed: 4}
|
4
|
+
enum :vehicle_type, {motorcycle: 0, car: 1, van: 2, truck: 3}
|
5
|
+
|
6
|
+
belongs_to :driver, class_name: "Dscf::Core::User"
|
7
|
+
belongs_to :pickup_address, class_name: "Dscf::Core::Address"
|
8
|
+
belongs_to :delivery_vehicle, optional: true
|
9
|
+
has_many :delivery_order_items, dependent: :destroy
|
10
|
+
has_many :marketplace_orders, class_name: "Dscf::Marketplace::Order", dependent: :nullify
|
11
|
+
|
12
|
+
validates :driver_id, presence: true
|
13
|
+
validates :pickup_address_id, presence: true
|
14
|
+
validates :status, presence: true
|
15
|
+
validates :vehicle_type, presence: true
|
16
|
+
validates :estimated_delivery_price, numericality: {greater_than_or_equal_to: 0}, allow_nil: true
|
17
|
+
validates :actual_delivery_price, numericality: {greater_than_or_equal_to: 0}, allow_nil: true
|
18
|
+
|
19
|
+
before_save :calculate_actual_delivery_price, if: :status_changed?
|
20
|
+
|
21
|
+
def pickup!
|
22
|
+
return false unless assigned?
|
23
|
+
update!(status: :picked_up, actual_delivery_time: Time.current)
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def start_delivery!
|
28
|
+
return false unless picked_up?
|
29
|
+
update!(status: :in_transit)
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def complete_delivery!
|
34
|
+
return false unless in_transit?
|
35
|
+
update!(status: :delivered, actual_delivery_time: Time.current)
|
36
|
+
update_associated_orders
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def mark_failed!(reason = nil)
|
41
|
+
update!(status: :failed, delivery_notes: reason)
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def total_items
|
46
|
+
delivery_order_items.sum(:quantity)
|
47
|
+
end
|
48
|
+
|
49
|
+
def total_orders
|
50
|
+
marketplace_orders.count
|
51
|
+
end
|
52
|
+
|
53
|
+
def delivery_duration
|
54
|
+
return nil unless actual_delivery_time && estimated_delivery_time
|
55
|
+
(actual_delivery_time - estimated_delivery_time).to_i
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_time?
|
59
|
+
return nil unless actual_delivery_time && estimated_delivery_time
|
60
|
+
actual_delivery_time <= estimated_delivery_time
|
61
|
+
end
|
62
|
+
|
63
|
+
# Handoff tracking methods
|
64
|
+
def handoff_completion_status
|
65
|
+
items = delivery_order_items
|
66
|
+
{
|
67
|
+
total_items: items.count,
|
68
|
+
confirmed_items: items.count(&:receiver_confirmed?),
|
69
|
+
pending_confirmation: items.count { |item| item.delivered? && !item.receiver_confirmed? },
|
70
|
+
issues_reported: items.count(&:requires_resolution?),
|
71
|
+
completion_percentage: items.present? ? (items.count(&:receiver_confirmed?).to_f / items.count * 100).round(2) : 0
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def all_handoffs_confirmed?
|
76
|
+
delivery_order_items.all?(&:receiver_confirmed?)
|
77
|
+
end
|
78
|
+
|
79
|
+
def items_requiring_attention
|
80
|
+
delivery_order_items.select(&:requires_resolution?)
|
81
|
+
end
|
82
|
+
|
83
|
+
def generate_handoff_summary_report
|
84
|
+
{
|
85
|
+
delivery_order_id: id,
|
86
|
+
driver: driver&.name,
|
87
|
+
vehicle: vehicle_type,
|
88
|
+
total_destinations: delivery_order_items.distinct.pluck(:dropoff_address_id).count,
|
89
|
+
handoff_status: handoff_completion_status,
|
90
|
+
issues: items_requiring_attention.map(&:comprehensive_damage_report),
|
91
|
+
generated_at: Time.current
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def calculate_actual_delivery_price
|
98
|
+
self.actual_delivery_price ||= estimated_delivery_price
|
99
|
+
end
|
100
|
+
|
101
|
+
def update_associated_orders
|
102
|
+
marketplace_orders.each do |order|
|
103
|
+
order.update!(status: :completed)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module Dscf::Marketplace
|
2
|
+
class DeliveryOrderItem < ApplicationRecord
|
3
|
+
enum :status, {pending: 0, picked_up: 1, delivered: 2, receiver_confirmed: 3, damaged: 4, missing: 5, disputed: 6}
|
4
|
+
|
5
|
+
belongs_to :delivery_order
|
6
|
+
belongs_to :order_item, class_name: "Dscf::Marketplace::OrderItem"
|
7
|
+
belongs_to :pickup_address, class_name: "Dscf::Core::Address"
|
8
|
+
belongs_to :dropoff_address, class_name: "Dscf::Core::Address"
|
9
|
+
|
10
|
+
validates :delivery_order_id, presence: true
|
11
|
+
validates :order_item_id, presence: true
|
12
|
+
validates :quantity, presence: true, numericality: {greater_than: 0}
|
13
|
+
validates :status, presence: true
|
14
|
+
validates :pickup_verified_quantity, numericality: {greater_than_or_equal_to: 0}, allow_nil: true
|
15
|
+
validates :dropoff_verified_quantity, numericality: {greater_than_or_equal_to: 0}, allow_nil: true
|
16
|
+
validates :damaged_quantity, numericality: {greater_than_or_equal_to: 0}, allow_nil: true
|
17
|
+
validates :missing_quantity, numericality: {greater_than_or_equal_to: 0}, allow_nil: true
|
18
|
+
validates :receiver_confirmed_by, presence: true, if: :receiver_action_taken?
|
19
|
+
validates :receiver_confirmed_at, presence: true, if: :receiver_action_taken?
|
20
|
+
|
21
|
+
before_save :calculate_verification_totals
|
22
|
+
|
23
|
+
def pending?
|
24
|
+
status == "pending"
|
25
|
+
end
|
26
|
+
|
27
|
+
def picked_up?
|
28
|
+
status == "picked_up"
|
29
|
+
end
|
30
|
+
|
31
|
+
def delivered?
|
32
|
+
status == "delivered"
|
33
|
+
end
|
34
|
+
|
35
|
+
def damaged?
|
36
|
+
status == "damaged"
|
37
|
+
end
|
38
|
+
|
39
|
+
def missing?
|
40
|
+
status == "missing"
|
41
|
+
end
|
42
|
+
|
43
|
+
def receiver_confirmed?
|
44
|
+
status == "receiver_confirmed"
|
45
|
+
end
|
46
|
+
|
47
|
+
def disputed?
|
48
|
+
status == "disputed"
|
49
|
+
end
|
50
|
+
|
51
|
+
def pickup!
|
52
|
+
return false unless pending?
|
53
|
+
update!(status: :picked_up, pickup_verified_quantity: quantity)
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def deliver!
|
58
|
+
return false unless picked_up?
|
59
|
+
update!(status: :delivered, dropoff_verified_quantity: pickup_verified_quantity.to_f - (damaged_quantity || 0) - (missing_quantity || 0))
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
# Handoff workflow methods
|
64
|
+
def receiver_confirm!(confirmed_quantity, notes = nil, confirmed_by = nil)
|
65
|
+
return false unless delivered? || picked_up?
|
66
|
+
|
67
|
+
update!(
|
68
|
+
status: :receiver_confirmed,
|
69
|
+
dropoff_verified_quantity: confirmed_quantity,
|
70
|
+
receiver_notes: notes,
|
71
|
+
receiver_confirmed_at: Time.current,
|
72
|
+
receiver_confirmed_by: confirmed_by,
|
73
|
+
handoff_checklist_completed: true
|
74
|
+
)
|
75
|
+
notify_receiver_confirmation
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def report_issue!(issue_type, quantity, description, reported_by)
|
80
|
+
return false unless delivered? || picked_up?
|
81
|
+
|
82
|
+
update!(
|
83
|
+
status: issue_type.to_sym, # :damaged or :missing
|
84
|
+
damaged_quantity: ((issue_type == "damaged") ? quantity : 0),
|
85
|
+
missing_quantity: ((issue_type == "missing") ? quantity : 0),
|
86
|
+
receiver_notes: description,
|
87
|
+
receiver_confirmed_at: Time.current,
|
88
|
+
receiver_confirmed_by: reported_by,
|
89
|
+
handoff_checklist_completed: true
|
90
|
+
)
|
91
|
+
notify_platform_of_issue
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def dispute_delivery!(reason, disputed_by)
|
96
|
+
return false unless delivered? || receiver_confirmed?
|
97
|
+
|
98
|
+
update!(
|
99
|
+
status: :disputed,
|
100
|
+
receiver_notes: reason,
|
101
|
+
receiver_confirmed_at: Time.current,
|
102
|
+
receiver_confirmed_by: disputed_by
|
103
|
+
)
|
104
|
+
create_dispute_record
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
def mark_damaged!(quantity)
|
109
|
+
update!(status: :damaged, damaged_quantity: quantity)
|
110
|
+
true
|
111
|
+
end
|
112
|
+
|
113
|
+
def mark_missing!(quantity)
|
114
|
+
update!(status: :missing, missing_quantity: quantity)
|
115
|
+
true
|
116
|
+
end
|
117
|
+
|
118
|
+
def verification_complete?
|
119
|
+
pickup_verified_quantity.present? && dropoff_verified_quantity.present?
|
120
|
+
end
|
121
|
+
|
122
|
+
def has_discrepancy?
|
123
|
+
return false unless verification_complete?
|
124
|
+
pickup_verified_quantity != dropoff_verified_quantity
|
125
|
+
end
|
126
|
+
|
127
|
+
def discrepancy_amount
|
128
|
+
return 0 unless has_discrepancy?
|
129
|
+
(pickup_verified_quantity - dropoff_verified_quantity).abs
|
130
|
+
end
|
131
|
+
|
132
|
+
def product_name
|
133
|
+
order_item&.product_name
|
134
|
+
end
|
135
|
+
|
136
|
+
def unit_name
|
137
|
+
order_item&.unit_name
|
138
|
+
end
|
139
|
+
|
140
|
+
# Loss/Damage Reporting Methods
|
141
|
+
def comprehensive_damage_report
|
142
|
+
{
|
143
|
+
item_id: id,
|
144
|
+
product_name: order_item&.product&.name,
|
145
|
+
expected_quantity: quantity,
|
146
|
+
received_quantity: dropoff_verified_quantity,
|
147
|
+
damaged_quantity: damaged_quantity,
|
148
|
+
missing_quantity: missing_quantity,
|
149
|
+
total_discrepancy: discrepancy_amount,
|
150
|
+
issue_description: receiver_notes,
|
151
|
+
reported_at: receiver_confirmed_at,
|
152
|
+
reported_by: receiver_confirmed_by,
|
153
|
+
requires_resolution: requires_resolution?
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
def requires_resolution?
|
158
|
+
has_discrepancy? || damaged? || missing? || disputed?
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def receiver_action_taken?
|
164
|
+
receiver_confirmed? || damaged? || missing? || disputed?
|
165
|
+
end
|
166
|
+
|
167
|
+
def notify_receiver_confirmation
|
168
|
+
# Placeholder for notification logic
|
169
|
+
# This will be implemented when we add notification system
|
170
|
+
Rails.logger.info("Receiver confirmation completed for delivery order item #{id}")
|
171
|
+
end
|
172
|
+
|
173
|
+
def notify_platform_of_issue
|
174
|
+
# Placeholder for platform notification
|
175
|
+
# This will be implemented when we add notification system
|
176
|
+
Rails.logger.info("Issue reported for delivery order item #{id}: #{status}")
|
177
|
+
end
|
178
|
+
|
179
|
+
def create_dispute_record
|
180
|
+
# Placeholder for dispute record creation
|
181
|
+
# This will be implemented when we add dispute management
|
182
|
+
Rails.logger.info("Dispute created for delivery order item #{id}")
|
183
|
+
end
|
184
|
+
|
185
|
+
def calculate_verification_totals
|
186
|
+
if pickup_verified_quantity.present? && damaged_quantity.present? && missing_quantity.present?
|
187
|
+
expected_dropoff = pickup_verified_quantity - damaged_quantity - missing_quantity
|
188
|
+
self.dropoff_verified_quantity ||= expected_dropoff
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Dscf
|
2
|
+
module Marketplace
|
3
|
+
class DeliveryVehicle < ApplicationRecord
|
4
|
+
enum :vehicle_type, {motorcycle: 0, car: 1, van: 2, truck: 3}
|
5
|
+
|
6
|
+
belongs_to :driver, class_name: "Dscf::Core::User"
|
7
|
+
has_many :delivery_orders, dependent: :nullify
|
8
|
+
|
9
|
+
validates :driver_id, presence: true
|
10
|
+
validates :plate_number, presence: true, uniqueness: true
|
11
|
+
validates :vehicle_type, presence: true
|
12
|
+
validates :year, numericality: {greater_than: 1900, less_than_or_equal_to: Time.current.year + 1}, allow_nil: true
|
13
|
+
validates :brand, length: {maximum: 50}, allow_blank: true
|
14
|
+
validates :model, length: {maximum: 50}, allow_blank: true
|
15
|
+
validates :color, length: {maximum: 30}, allow_blank: true
|
16
|
+
|
17
|
+
def motorcycle?
|
18
|
+
vehicle_type == "motorcycle"
|
19
|
+
end
|
20
|
+
|
21
|
+
def car?
|
22
|
+
vehicle_type == "car"
|
23
|
+
end
|
24
|
+
|
25
|
+
def van?
|
26
|
+
vehicle_type == "van"
|
27
|
+
end
|
28
|
+
|
29
|
+
def truck?
|
30
|
+
vehicle_type == "truck"
|
31
|
+
end
|
32
|
+
|
33
|
+
def display_name
|
34
|
+
brand_and_model = [ brand.presence, model.presence ].compact.join(" ")
|
35
|
+
brand_and_model = nil if brand_and_model.blank?
|
36
|
+
"#{brand_and_model} (#{plate_number})".strip
|
37
|
+
end
|
38
|
+
|
39
|
+
def active_deliveries
|
40
|
+
delivery_orders.where.not(status: [ :delivered, :failed ])
|
41
|
+
end
|
42
|
+
|
43
|
+
def completed_deliveries
|
44
|
+
delivery_orders.where(status: :delivered)
|
45
|
+
end
|
46
|
+
|
47
|
+
def failed_deliveries
|
48
|
+
delivery_orders.where(status: :failed)
|
49
|
+
end
|
50
|
+
|
51
|
+
def total_deliveries
|
52
|
+
delivery_orders.count
|
53
|
+
end
|
54
|
+
|
55
|
+
def success_rate
|
56
|
+
return 0 if total_deliveries.zero?
|
57
|
+
(completed_deliveries.count.to_f / total_deliveries * 100).round(2)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module Dscf::Marketplace
|
2
|
+
class Order < ApplicationRecord
|
3
|
+
enum :order_type, {rfq_based: 0, direct_listing: 1}
|
4
|
+
enum :status, {pending: 0, confirmed: 1, processing: 2, completed: 3, cancelled: 4}
|
5
|
+
enum :fulfillment_type, {self_pickup: 0, delivery: 1}
|
6
|
+
|
7
|
+
belongs_to :quotation, optional: true
|
8
|
+
belongs_to :listing, optional: true
|
9
|
+
belongs_to :user, class_name: "Dscf::Core::User"
|
10
|
+
belongs_to :delivery_order, optional: true
|
11
|
+
has_many :order_items, dependent: :destroy
|
12
|
+
|
13
|
+
validates :order_type, presence: true
|
14
|
+
validates :status, presence: true
|
15
|
+
validates :fulfillment_type, presence: true
|
16
|
+
validates :user, presence: true
|
17
|
+
validate :quotation_or_listing_present
|
18
|
+
|
19
|
+
before_save :calculate_total_amount
|
20
|
+
|
21
|
+
def self.create_from_quotation(quotation)
|
22
|
+
return nil unless quotation.accepted?
|
23
|
+
|
24
|
+
order = create!(
|
25
|
+
order_type: :rfq_based,
|
26
|
+
status: :pending,
|
27
|
+
quotation: quotation,
|
28
|
+
user: quotation.request_for_quotation.user,
|
29
|
+
total_amount: quotation.total_price
|
30
|
+
)
|
31
|
+
|
32
|
+
quotation.quotation_items.each do |item|
|
33
|
+
order.order_items.create!(
|
34
|
+
quotation_item: item,
|
35
|
+
product: item.product,
|
36
|
+
unit: item.unit,
|
37
|
+
quantity: item.quantity,
|
38
|
+
unit_price: item.unit_price,
|
39
|
+
status: :pending
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
order
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.create_from_listing(listing, user, quantity)
|
47
|
+
return nil unless listing.status == "active" && quantity <= listing.quantity
|
48
|
+
|
49
|
+
order = create!(
|
50
|
+
order_type: :direct_listing,
|
51
|
+
status: :pending,
|
52
|
+
listing: listing,
|
53
|
+
user: user,
|
54
|
+
total_amount: listing.price * quantity
|
55
|
+
)
|
56
|
+
|
57
|
+
order.order_items.create!(
|
58
|
+
listing: listing,
|
59
|
+
product: listing.supplier_product.product,
|
60
|
+
unit: listing.supplier_product.product.unit,
|
61
|
+
quantity: quantity,
|
62
|
+
unit_price: listing.price,
|
63
|
+
status: :pending
|
64
|
+
)
|
65
|
+
|
66
|
+
order
|
67
|
+
end
|
68
|
+
|
69
|
+
def total_amount
|
70
|
+
# Return stored value if it exists and is greater than 0, otherwise calculate
|
71
|
+
stored_value = super
|
72
|
+
calculated_value = order_items.sum { |item| item.quantity * item.unit_price }
|
73
|
+
|
74
|
+
# Return stored value if it's set and valid, otherwise return calculated value
|
75
|
+
return stored_value if stored_value.present? && stored_value > 0
|
76
|
+
calculated_value
|
77
|
+
end
|
78
|
+
|
79
|
+
def supplier
|
80
|
+
if rfq_based?
|
81
|
+
quotation&.business
|
82
|
+
elsif direct_listing?
|
83
|
+
listing&.business
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Fulfillment type methods
|
88
|
+
def self_pickup?
|
89
|
+
fulfillment_type == "self_pickup"
|
90
|
+
end
|
91
|
+
|
92
|
+
def delivery?
|
93
|
+
fulfillment_type == "delivery"
|
94
|
+
end
|
95
|
+
|
96
|
+
def requires_delivery_order?
|
97
|
+
delivery? && !completed?
|
98
|
+
end
|
99
|
+
|
100
|
+
def can_be_completed?
|
101
|
+
if self_pickup?
|
102
|
+
# Self-pickup orders can be completed immediately after confirmation
|
103
|
+
confirmed? || processing?
|
104
|
+
else
|
105
|
+
# Delivery orders require delivery_order association
|
106
|
+
delivery_order.present? && delivery_order.delivered?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def confirm!
|
111
|
+
return false unless pending?
|
112
|
+
|
113
|
+
update!(status: :confirmed)
|
114
|
+
# Update order items without reloading to avoid association issues
|
115
|
+
order_items.update_all(status: OrderItem.statuses[:confirmed])
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def calculate_total_amount
|
120
|
+
self.total_amount = order_items.sum { |item| item.quantity * item.unit_price }
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def quotation_or_listing_present
|
126
|
+
unless quotation.present? || listing.present?
|
127
|
+
errors.add(:base, "Either quotation or listing must be present")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Dscf::Marketplace
|
2
|
+
class OrderItem < ApplicationRecord
|
3
|
+
enum :status, {pending: 0, confirmed: 1, processing: 2, fulfilled: 3, cancelled: 4}
|
4
|
+
|
5
|
+
belongs_to :order, optional: true
|
6
|
+
belongs_to :quotation_item, optional: true
|
7
|
+
belongs_to :listing, optional: true
|
8
|
+
belongs_to :product
|
9
|
+
belongs_to :unit
|
10
|
+
|
11
|
+
validates :quantity, presence: true, numericality: {greater_than: 0}
|
12
|
+
validates :unit_price, presence: true, numericality: {greater_than_or_equal_to: 0}
|
13
|
+
validates :status, presence: true
|
14
|
+
validates :product, presence: true
|
15
|
+
validates :unit, presence: true
|
16
|
+
validate :quotation_item_or_listing_present
|
17
|
+
|
18
|
+
def subtotal
|
19
|
+
quantity * unit_price
|
20
|
+
end
|
21
|
+
|
22
|
+
def product_name
|
23
|
+
product&.name
|
24
|
+
end
|
25
|
+
|
26
|
+
def unit_name
|
27
|
+
unit&.name
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def quotation_item_or_listing_present
|
33
|
+
unless quotation_item.present? || listing.present?
|
34
|
+
errors.add(:base, "Either quotation_item or listing must be present")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -5,6 +5,7 @@ module Dscf
|
|
5
5
|
belongs_to :business, class_name: "Dscf::Core::Business", optional: true if defined?(Dscf::Core)
|
6
6
|
|
7
7
|
has_many :quotation_items, class_name: "Dscf::Marketplace::QuotationItem", dependent: :destroy
|
8
|
+
has_one :order, class_name: "Dscf::Marketplace::Order", dependent: :destroy
|
8
9
|
|
9
10
|
validates :request_for_quotation_id, presence: true
|
10
11
|
validates :business_id, presence: true
|
@@ -79,9 +80,19 @@ module Dscf
|
|
79
80
|
|
80
81
|
update_columns(status: "accepted")
|
81
82
|
request_for_quotation.update!(status: "selected", selected_quotation: self)
|
83
|
+
|
84
|
+
# Create order from accepted quotation
|
85
|
+
create_order_from_quotation
|
86
|
+
|
82
87
|
true
|
83
88
|
end
|
84
89
|
|
90
|
+
def create_order_from_quotation
|
91
|
+
return if order.present?
|
92
|
+
|
93
|
+
Order.create_from_quotation(self)
|
94
|
+
end
|
95
|
+
|
85
96
|
def reject!
|
86
97
|
return false unless sent?
|
87
98
|
|
@@ -113,6 +124,32 @@ module Dscf
|
|
113
124
|
quotation_items.exists? && quotation_items.all? { |item| item.unit_price.present? }
|
114
125
|
end
|
115
126
|
|
127
|
+
def create_order_from_quotation
|
128
|
+
return if order.present?
|
129
|
+
|
130
|
+
order = Dscf::Marketplace::Order.create!(
|
131
|
+
order_type: :rfq_based,
|
132
|
+
status: :pending,
|
133
|
+
quotation: self,
|
134
|
+
user: request_for_quotation.user,
|
135
|
+
total_amount: total_price
|
136
|
+
)
|
137
|
+
|
138
|
+
quotation_items.each do |item|
|
139
|
+
Dscf::Marketplace::OrderItem.create!(
|
140
|
+
order: order,
|
141
|
+
quotation_item: item,
|
142
|
+
product: item.product,
|
143
|
+
unit: item.unit,
|
144
|
+
quantity: item.quantity,
|
145
|
+
unit_price: item.unit_price,
|
146
|
+
status: :pending
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
order
|
151
|
+
end
|
152
|
+
|
116
153
|
private
|
117
154
|
|
118
155
|
def calculate_total_price
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateDscfMarketplaceOrders < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_marketplace_orders do |t|
|
4
|
+
t.integer :order_type
|
5
|
+
t.integer :status
|
6
|
+
t.references :quotation, null: true, foreign_key: {to_table: :dscf_marketplace_quotations}
|
7
|
+
t.references :listing, null: true, foreign_key: {to_table: :dscf_marketplace_listings}
|
8
|
+
t.references :user, null: false, foreign_key: {to_table: :dscf_core_users}
|
9
|
+
t.decimal :total_amount
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :dscf_marketplace_orders, :user_id, name: "user_id_on_dm_orders_idx"
|
15
|
+
add_index :dscf_marketplace_orders, :quotation_id, name: "quotation_id_on_dm_orders_idx"
|
16
|
+
add_index :dscf_marketplace_orders, :listing_id, name: "listing_id_on_dm_orders_idx"
|
17
|
+
add_index :dscf_marketplace_orders, :status, name: "status_on_dm_orders_idx"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateDscfMarketplaceOrderItems < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_marketplace_order_items do |t|
|
4
|
+
t.references :order, null: false, foreign_key: {to_table: :dscf_marketplace_orders}
|
5
|
+
t.references :quotation_item, null: true, foreign_key: {to_table: :dscf_marketplace_quotation_items}
|
6
|
+
t.references :listing, null: true, foreign_key: {to_table: :dscf_marketplace_listings}
|
7
|
+
t.references :product, null: false, foreign_key: {to_table: :dscf_marketplace_products}
|
8
|
+
t.references :unit, null: false, foreign_key: {to_table: :dscf_marketplace_units}
|
9
|
+
t.decimal :quantity, precision: 15, scale: 6
|
10
|
+
t.decimal :unit_price
|
11
|
+
t.integer :status
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index :dscf_marketplace_order_items, :order_id, name: "order_id_on_dm_order_items_idx"
|
17
|
+
add_index :dscf_marketplace_order_items, :quotation_item_id, name: "quotation_item_id_on_dm_order_items_idx"
|
18
|
+
add_index :dscf_marketplace_order_items, :listing_id, name: "listing_id_on_dm_order_items_idx"
|
19
|
+
add_index :dscf_marketplace_order_items, :product_id, name: "product_id_on_dm_order_items_idx"
|
20
|
+
add_index :dscf_marketplace_order_items, :unit_id, name: "unit_id_on_dm_order_items_idx"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateDscfMarketplaceDeliveryOrders < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_marketplace_delivery_orders do |t|
|
4
|
+
t.bigint :driver_id, null: false
|
5
|
+
t.bigint :pickup_address_id, null: false
|
6
|
+
t.integer :status
|
7
|
+
t.text :delivery_notes
|
8
|
+
t.datetime :estimated_delivery_time
|
9
|
+
t.datetime :actual_delivery_time
|
10
|
+
t.decimal :estimated_delivery_price
|
11
|
+
t.decimal :actual_delivery_price
|
12
|
+
t.json :optimized_route
|
13
|
+
t.integer :vehicle_type
|
14
|
+
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
|
18
|
+
add_foreign_key :dscf_marketplace_delivery_orders, :dscf_core_users, column: :driver_id
|
19
|
+
add_foreign_key :dscf_marketplace_delivery_orders, :dscf_core_addresses, column: :pickup_address_id
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CreateDscfMarketplaceDeliveryOrderItems < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_marketplace_delivery_order_items do |t|
|
4
|
+
t.bigint :delivery_order_id, null: false
|
5
|
+
t.bigint :order_item_id, null: false
|
6
|
+
t.bigint :pickup_address_id, null: false
|
7
|
+
t.bigint :dropoff_address_id, null: false
|
8
|
+
t.decimal :quantity
|
9
|
+
t.integer :status
|
10
|
+
t.text :notes
|
11
|
+
t.decimal :pickup_verified_quantity
|
12
|
+
t.decimal :dropoff_verified_quantity
|
13
|
+
t.decimal :damaged_quantity
|
14
|
+
t.decimal :missing_quantity
|
15
|
+
t.text :pickup_notes
|
16
|
+
t.text :dropoff_notes
|
17
|
+
t.integer :position
|
18
|
+
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
|
22
|
+
add_foreign_key :dscf_marketplace_delivery_order_items, :dscf_marketplace_delivery_orders, column: :delivery_order_id
|
23
|
+
add_foreign_key :dscf_marketplace_delivery_order_items, :dscf_marketplace_order_items, column: :order_item_id
|
24
|
+
add_foreign_key :dscf_marketplace_delivery_order_items, :dscf_core_addresses, column: :pickup_address_id
|
25
|
+
add_foreign_key :dscf_marketplace_delivery_order_items, :dscf_core_addresses, column: :dropoff_address_id
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateDscfMarketplaceDeliveryVehicles < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_marketplace_delivery_vehicles do |t|
|
4
|
+
t.bigint :driver_id, null: false
|
5
|
+
t.string :plate_number
|
6
|
+
t.integer :vehicle_type
|
7
|
+
t.string :brand
|
8
|
+
t.string :model
|
9
|
+
t.integer :year
|
10
|
+
t.string :color
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
add_foreign_key :dscf_marketplace_delivery_vehicles, :dscf_core_users, column: :driver_id
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class AddDeliveryHandoffFieldsToDeliveryOrderItems < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
# Add new handoff fields
|
4
|
+
add_column :dscf_marketplace_delivery_order_items, :receiver_confirmed_at, :datetime
|
5
|
+
add_column :dscf_marketplace_delivery_order_items, :receiver_confirmed_by, :integer
|
6
|
+
add_column :dscf_marketplace_delivery_order_items, :receiver_notes, :text
|
7
|
+
add_column :dscf_marketplace_delivery_order_items, :handoff_checklist_completed, :boolean, default: false
|
8
|
+
|
9
|
+
# Add indexes for performance
|
10
|
+
add_index :dscf_marketplace_delivery_order_items, :receiver_confirmed_by
|
11
|
+
add_index :dscf_marketplace_delivery_order_items, :handoff_checklist_completed
|
12
|
+
|
13
|
+
# Note: Status enum migration handled separately to avoid data loss
|
14
|
+
# The status values will be updated in the model after this migration
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class AddDeliveryOrderToOrders < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
add_column :dscf_marketplace_orders, :delivery_order_id, :bigint
|
4
|
+
add_foreign_key :dscf_marketplace_orders, :dscf_marketplace_delivery_orders, column: :delivery_order_id
|
5
|
+
add_index :dscf_marketplace_orders, :delivery_order_id
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class AddDeliveryVehicleToDeliveryOrders < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
add_column :dscf_marketplace_delivery_orders, :delivery_vehicle_id, :bigint
|
4
|
+
add_foreign_key :dscf_marketplace_delivery_orders, :dscf_marketplace_delivery_vehicles, column: :delivery_vehicle_id
|
5
|
+
add_index :dscf_marketplace_delivery_orders, :delivery_vehicle_id
|
6
|
+
end
|
7
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :dscf_core_address, class: "Dscf::Core::Address" do
|
3
|
+
user
|
4
|
+
address_type { 0 } # home
|
5
|
+
country { "Ethiopia" }
|
6
|
+
city { "Addis Ababa" }
|
7
|
+
sub_city { "Bole" }
|
8
|
+
woreda { "03" }
|
9
|
+
kebele { "07" }
|
10
|
+
house_numbers { "123" }
|
11
|
+
po_box { "1234" }
|
12
|
+
latitude { 9.1450 }
|
13
|
+
longitude { 38.7525 }
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :dscf_marketplace_delivery_order_item, class: "Dscf::Marketplace::DeliveryOrderItem" do
|
3
|
+
association :delivery_order, factory: :dscf_marketplace_delivery_order
|
4
|
+
association :order_item, factory: :dscf_marketplace_order_item
|
5
|
+
pickup_address { create(:dscf_core_address) }
|
6
|
+
dropoff_address { create(:dscf_core_address) }
|
7
|
+
quantity { 10.0 }
|
8
|
+
status { :pending }
|
9
|
+
notes { "Test delivery item" }
|
10
|
+
pickup_verified_quantity { nil }
|
11
|
+
dropoff_verified_quantity { nil }
|
12
|
+
damaged_quantity { nil }
|
13
|
+
missing_quantity { nil }
|
14
|
+
pickup_notes { nil }
|
15
|
+
dropoff_notes { nil }
|
16
|
+
position { 1 }
|
17
|
+
|
18
|
+
trait :picked_up do
|
19
|
+
status { :picked_up }
|
20
|
+
pickup_verified_quantity { 10.0 }
|
21
|
+
end
|
22
|
+
|
23
|
+
trait :delivered do
|
24
|
+
status { :delivered }
|
25
|
+
pickup_verified_quantity { 10.0 }
|
26
|
+
dropoff_verified_quantity { 10.0 }
|
27
|
+
end
|
28
|
+
|
29
|
+
trait :receiver_confirmed do
|
30
|
+
status { :receiver_confirmed }
|
31
|
+
pickup_verified_quantity { 10.0 }
|
32
|
+
dropoff_verified_quantity { 10.0 }
|
33
|
+
receiver_confirmed_at { Time.current }
|
34
|
+
receiver_confirmed_by { create(:dscf_core_user).id }
|
35
|
+
handoff_checklist_completed { true }
|
36
|
+
end
|
37
|
+
|
38
|
+
trait :with_damage do
|
39
|
+
status { :damaged }
|
40
|
+
pickup_verified_quantity { 10.0 }
|
41
|
+
dropoff_verified_quantity { 8.0 }
|
42
|
+
damaged_quantity { 2.0 }
|
43
|
+
receiver_notes { "Package damaged during transport" }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :dscf_marketplace_delivery_order, class: "Dscf::Marketplace::DeliveryOrder" do
|
3
|
+
association :driver, factory: :dscf_core_user
|
4
|
+
association :pickup_address, factory: :dscf_core_address
|
5
|
+
status { :assigned }
|
6
|
+
delivery_notes { "Test delivery order" }
|
7
|
+
estimated_delivery_time { 2.hours.from_now }
|
8
|
+
actual_delivery_time { nil }
|
9
|
+
estimated_delivery_price { 50.0 }
|
10
|
+
actual_delivery_price { nil }
|
11
|
+
optimized_route { {} }
|
12
|
+
vehicle_type { :motorcycle }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :dscf_marketplace_delivery_vehicle, class: "Dscf::Marketplace::DeliveryVehicle" do
|
3
|
+
association :driver, factory: :dscf_core_user
|
4
|
+
sequence(:plate_number) { |n| "ABC#{n.to_s.rjust(3, "0")}" }
|
5
|
+
vehicle_type { :motorcycle }
|
6
|
+
brand { "Honda" }
|
7
|
+
model { "CBR" }
|
8
|
+
year { 2020 }
|
9
|
+
color { "Red" }
|
10
|
+
|
11
|
+
trait :car do
|
12
|
+
vehicle_type { :car }
|
13
|
+
brand { "Toyota" }
|
14
|
+
model { "Corolla" }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :van do
|
18
|
+
vehicle_type { :van }
|
19
|
+
brand { "Ford" }
|
20
|
+
model { "Transit" }
|
21
|
+
end
|
22
|
+
|
23
|
+
trait :truck do
|
24
|
+
vehicle_type { :truck }
|
25
|
+
brand { "Volvo" }
|
26
|
+
model { "FH16" }
|
27
|
+
end
|
28
|
+
|
29
|
+
trait :old_vehicle do
|
30
|
+
year { 2010 }
|
31
|
+
end
|
32
|
+
|
33
|
+
trait :future_vehicle do
|
34
|
+
year { Time.current.year + 2 }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,10 +1,22 @@
|
|
1
1
|
FactoryBot.define do
|
2
|
-
factory :dscf_marketplace_listing, class:
|
3
|
-
business
|
4
|
-
supplier_product
|
2
|
+
factory :dscf_marketplace_listing, class: "Dscf::Marketplace::Listing" do
|
3
|
+
association :business, factory: :dscf_core_business
|
4
|
+
association :supplier_product, factory: :dscf_marketplace_supplier_product
|
5
5
|
price { 500.00 }
|
6
6
|
quantity { 100.0 }
|
7
7
|
description { "High quality product listing" }
|
8
|
-
status
|
8
|
+
# status defaults to :draft from model
|
9
|
+
|
10
|
+
trait :active do
|
11
|
+
status { :active }
|
12
|
+
end
|
13
|
+
|
14
|
+
trait :draft do
|
15
|
+
status { :draft }
|
16
|
+
end
|
17
|
+
|
18
|
+
trait :paused do
|
19
|
+
status { :paused }
|
20
|
+
end
|
9
21
|
end
|
10
22
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :dscf_marketplace_order_item, class: "Dscf::Marketplace::OrderItem" do
|
3
|
+
association :order, factory: :dscf_marketplace_order
|
4
|
+
quantity { 5 }
|
5
|
+
unit_price { 10.0 }
|
6
|
+
status { :pending }
|
7
|
+
quotation_item { nil } # Don't create by default
|
8
|
+
listing { nil } # Don't create by default
|
9
|
+
|
10
|
+
# Set product and unit based on quotation_item or listing
|
11
|
+
after(:build) do |item|
|
12
|
+
if item.quotation_item
|
13
|
+
item.product ||= item.quotation_item.product
|
14
|
+
item.unit ||= item.quotation_item.unit
|
15
|
+
elsif item.listing
|
16
|
+
item.product ||= item.listing.supplier_product.product
|
17
|
+
item.unit ||= item.listing.supplier_product.product.unit
|
18
|
+
else
|
19
|
+
item.product ||= create(:dscf_marketplace_product)
|
20
|
+
item.unit ||= create(:dscf_marketplace_unit)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
trait :from_quotation do
|
25
|
+
association :quotation_item, factory: :dscf_marketplace_quotation_item
|
26
|
+
end
|
27
|
+
|
28
|
+
trait :from_listing do
|
29
|
+
association :listing, factory: :dscf_marketplace_listing
|
30
|
+
quotation_item { nil }
|
31
|
+
end
|
32
|
+
|
33
|
+
trait :confirmed do
|
34
|
+
status { :confirmed }
|
35
|
+
end
|
36
|
+
|
37
|
+
trait :processing do
|
38
|
+
status { :processing }
|
39
|
+
end
|
40
|
+
|
41
|
+
trait :fulfilled do
|
42
|
+
status { :fulfilled }
|
43
|
+
end
|
44
|
+
|
45
|
+
trait :cancelled do
|
46
|
+
status { :cancelled }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :dscf_marketplace_order, class: "Dscf::Marketplace::Order" do
|
3
|
+
order_type { :rfq_based }
|
4
|
+
status { :pending }
|
5
|
+
fulfillment_type { :self_pickup }
|
6
|
+
association :user, factory: :dscf_core_user
|
7
|
+
association :quotation, factory: :dscf_marketplace_quotation
|
8
|
+
listing { nil }
|
9
|
+
|
10
|
+
trait :rfq_based do
|
11
|
+
order_type { :rfq_based }
|
12
|
+
association :quotation, factory: :dscf_marketplace_quotation
|
13
|
+
end
|
14
|
+
|
15
|
+
trait :direct_listing do
|
16
|
+
order_type { :direct_listing }
|
17
|
+
association :listing, factory: :dscf_marketplace_listing
|
18
|
+
quotation { nil }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :self_pickup do
|
22
|
+
fulfillment_type { :self_pickup }
|
23
|
+
end
|
24
|
+
|
25
|
+
trait :delivery do
|
26
|
+
fulfillment_type { :delivery }
|
27
|
+
end
|
28
|
+
|
29
|
+
trait :confirmed do
|
30
|
+
status { :confirmed }
|
31
|
+
end
|
32
|
+
|
33
|
+
trait :processing do
|
34
|
+
status { :processing }
|
35
|
+
end
|
36
|
+
|
37
|
+
trait :completed do
|
38
|
+
status { :completed }
|
39
|
+
end
|
40
|
+
|
41
|
+
trait :cancelled do
|
42
|
+
status { :cancelled }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/factories/dscf/marketplace/{dscf/marketplace/quotation_items.rb → quotation_items.rb}
RENAMED
@@ -1,5 +1,5 @@
|
|
1
1
|
FactoryBot.define do
|
2
|
-
factory :dscf_marketplace_quotation_item, class:
|
2
|
+
factory :dscf_marketplace_quotation_item, class: "Dscf::Marketplace::QuotationItem" do
|
3
3
|
association :quotation, factory: :dscf_marketplace_quotation
|
4
4
|
association :rfq_item, factory: :dscf_marketplace_rfq_item
|
5
5
|
product { rfq_item.product }
|
@@ -1,11 +1,11 @@
|
|
1
1
|
FactoryBot.define do
|
2
|
-
factory :dscf_marketplace_quotation, class:
|
2
|
+
factory :dscf_marketplace_quotation, class: "Dscf::Marketplace::Quotation" do
|
3
3
|
association :request_for_quotation, factory: :dscf_marketplace_request_for_quotation
|
4
4
|
association :business, factory: :dscf_core_business
|
5
5
|
total_price { nil }
|
6
6
|
delivery_date { 5.days.from_now.to_date }
|
7
7
|
valid_until { 15.days.from_now }
|
8
|
-
status {
|
8
|
+
status { :draft }
|
9
9
|
notes { "Sample quotation notes" }
|
10
10
|
|
11
11
|
trait :expired do
|
@@ -15,11 +15,11 @@ FactoryBot.define do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
trait :sent do
|
18
|
-
status {
|
18
|
+
status { :sent }
|
19
19
|
end
|
20
20
|
|
21
21
|
trait :accepted do
|
22
|
-
status {
|
22
|
+
status { :accepted }
|
23
23
|
end
|
24
24
|
end
|
25
25
|
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.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Asrat
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-09-01 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -415,14 +415,18 @@ extensions: []
|
|
415
415
|
extra_rdoc_files: []
|
416
416
|
files:
|
417
417
|
- MIT-LICENSE
|
418
|
-
- README.md
|
419
418
|
- Rakefile
|
420
419
|
- app/controllers/dscf/marketplace/application_controller.rb
|
421
420
|
- app/jobs/dscf/marketplace/application_job.rb
|
422
421
|
- app/mailers/dscf/marketplace/application_mailer.rb
|
423
422
|
- app/models/dscf/marketplace/application_record.rb
|
424
423
|
- app/models/dscf/marketplace/category.rb
|
424
|
+
- app/models/dscf/marketplace/delivery_order.rb
|
425
|
+
- app/models/dscf/marketplace/delivery_order_item.rb
|
426
|
+
- app/models/dscf/marketplace/delivery_vehicle.rb
|
425
427
|
- app/models/dscf/marketplace/listing.rb
|
428
|
+
- app/models/dscf/marketplace/order.rb
|
429
|
+
- app/models/dscf/marketplace/order_item.rb
|
426
430
|
- app/models/dscf/marketplace/product.rb
|
427
431
|
- app/models/dscf/marketplace/quotation.rb
|
428
432
|
- app/models/dscf/marketplace/quotation_item.rb
|
@@ -445,20 +449,35 @@ files:
|
|
445
449
|
- db/migrate/20250828090300_create_dscf_marketplace_quotation_items.rb
|
446
450
|
- db/migrate/20250828090400_change_rfq_status_to_integer.rb
|
447
451
|
- db/migrate/20250828090500_change_quotation_status_to_integer.rb
|
452
|
+
- db/migrate/20250828115509_create_dscf_marketplace_orders.rb
|
453
|
+
- db/migrate/20250828115525_create_dscf_marketplace_order_items.rb
|
454
|
+
- db/migrate/20250831110907_create_dscf_marketplace_delivery_orders.rb
|
455
|
+
- db/migrate/20250831110921_create_dscf_marketplace_delivery_order_items.rb
|
456
|
+
- db/migrate/20250831110933_create_dscf_marketplace_delivery_vehicles.rb
|
457
|
+
- db/migrate/20250901074509_add_delivery_handoff_fields_to_delivery_order_items.rb
|
458
|
+
- db/migrate/20250901074746_add_delivery_order_to_orders.rb
|
459
|
+
- db/migrate/20250901075511_add_delivery_vehicle_to_delivery_orders.rb
|
460
|
+
- db/migrate/20250901080134_add_fulfillment_type_to_orders.rb
|
448
461
|
- lib/dscf/marketplace.rb
|
449
462
|
- lib/dscf/marketplace/engine.rb
|
450
463
|
- lib/dscf/marketplace/version.rb
|
451
464
|
- lib/tasks/dscf/marketplace_tasks.rake
|
465
|
+
- spec/factories/dscf/core/addresses.rb
|
452
466
|
- spec/factories/dscf/core/business_types.rb
|
453
467
|
- spec/factories/dscf/core/businesses.rb
|
454
468
|
- spec/factories/dscf/core/users.rb
|
455
469
|
- spec/factories/dscf/marketplace/categories.rb
|
456
|
-
- spec/factories/dscf/marketplace/
|
457
|
-
- spec/factories/dscf/marketplace/
|
458
|
-
- spec/factories/dscf/marketplace/
|
459
|
-
- spec/factories/dscf/marketplace/dscf/marketplace/rfq_items.rb
|
470
|
+
- spec/factories/dscf/marketplace/delivery_order_items.rb
|
471
|
+
- spec/factories/dscf/marketplace/delivery_orders.rb
|
472
|
+
- spec/factories/dscf/marketplace/delivery_vehicles.rb
|
460
473
|
- spec/factories/dscf/marketplace/listings.rb
|
474
|
+
- spec/factories/dscf/marketplace/order_items.rb
|
475
|
+
- spec/factories/dscf/marketplace/orders.rb
|
461
476
|
- spec/factories/dscf/marketplace/products.rb
|
477
|
+
- spec/factories/dscf/marketplace/quotation_items.rb
|
478
|
+
- spec/factories/dscf/marketplace/quotations.rb
|
479
|
+
- spec/factories/dscf/marketplace/request_for_quotations.rb
|
480
|
+
- spec/factories/dscf/marketplace/rfq_items.rb
|
462
481
|
- spec/factories/dscf/marketplace/supplier_products.rb
|
463
482
|
- spec/factories/dscf/marketplace/unit_conversions.rb
|
464
483
|
- spec/factories/dscf/marketplace/units.rb
|
data/README.md
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# Dscf::Marketplace
|
2
|
-
Short description and motivation.
|
3
|
-
|
4
|
-
## Usage
|
5
|
-
How to use my plugin.
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
Add this line to your application's Gemfile:
|
9
|
-
|
10
|
-
```ruby
|
11
|
-
gem "dscf-marketplace"
|
12
|
-
```
|
13
|
-
|
14
|
-
And then execute:
|
15
|
-
```bash
|
16
|
-
$ bundle
|
17
|
-
```
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
```bash
|
21
|
-
$ gem install dscf-marketplace
|
22
|
-
```
|
23
|
-
|
24
|
-
## Contributing
|
25
|
-
Contribution directions go here.
|
26
|
-
|
27
|
-
## License
|
28
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
File without changes
|
File without changes
|