dscf-marketplace 0.1.7 → 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 +26 -0
- data/app/models/dscf/marketplace/order_item.rb +8 -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/order_items.rb +1 -1
- data/spec/factories/dscf/marketplace/orders.rb +10 -1
- metadata +16 -3
- data/README.md +0 -28
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
|
@@ -2,14 +2,17 @@ module Dscf::Marketplace
|
|
2
2
|
class Order < ApplicationRecord
|
3
3
|
enum :order_type, {rfq_based: 0, direct_listing: 1}
|
4
4
|
enum :status, {pending: 0, confirmed: 1, processing: 2, completed: 3, cancelled: 4}
|
5
|
+
enum :fulfillment_type, {self_pickup: 0, delivery: 1}
|
5
6
|
|
6
7
|
belongs_to :quotation, optional: true
|
7
8
|
belongs_to :listing, optional: true
|
8
9
|
belongs_to :user, class_name: "Dscf::Core::User"
|
10
|
+
belongs_to :delivery_order, optional: true
|
9
11
|
has_many :order_items, dependent: :destroy
|
10
12
|
|
11
13
|
validates :order_type, presence: true
|
12
14
|
validates :status, presence: true
|
15
|
+
validates :fulfillment_type, presence: true
|
13
16
|
validates :user, presence: true
|
14
17
|
validate :quotation_or_listing_present
|
15
18
|
|
@@ -81,6 +84,29 @@ module Dscf::Marketplace
|
|
81
84
|
end
|
82
85
|
end
|
83
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
|
+
|
84
110
|
def confirm!
|
85
111
|
return false unless pending?
|
86
112
|
|
@@ -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,6 +1,6 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :dscf_marketplace_order_item, class: "Dscf::Marketplace::OrderItem" do
|
3
|
-
|
3
|
+
association :order, factory: :dscf_marketplace_order
|
4
4
|
quantity { 5 }
|
5
5
|
unit_price { 10.0 }
|
6
6
|
status { :pending }
|
@@ -2,8 +2,9 @@ FactoryBot.define do
|
|
2
2
|
factory :dscf_marketplace_order, class: "Dscf::Marketplace::Order" do
|
3
3
|
order_type { :rfq_based }
|
4
4
|
status { :pending }
|
5
|
+
fulfillment_type { :self_pickup }
|
5
6
|
association :user, factory: :dscf_core_user
|
6
|
-
quotation
|
7
|
+
association :quotation, factory: :dscf_marketplace_quotation
|
7
8
|
listing { nil }
|
8
9
|
|
9
10
|
trait :rfq_based do
|
@@ -17,6 +18,14 @@ FactoryBot.define do
|
|
17
18
|
quotation { nil }
|
18
19
|
end
|
19
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
|
+
|
20
29
|
trait :confirmed do
|
21
30
|
status { :confirmed }
|
22
31
|
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,13 +415,15 @@ 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
|
426
428
|
- app/models/dscf/marketplace/order.rb
|
427
429
|
- app/models/dscf/marketplace/order_item.rb
|
@@ -449,14 +451,25 @@ files:
|
|
449
451
|
- db/migrate/20250828090500_change_quotation_status_to_integer.rb
|
450
452
|
- db/migrate/20250828115509_create_dscf_marketplace_orders.rb
|
451
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
|
452
461
|
- lib/dscf/marketplace.rb
|
453
462
|
- lib/dscf/marketplace/engine.rb
|
454
463
|
- lib/dscf/marketplace/version.rb
|
455
464
|
- lib/tasks/dscf/marketplace_tasks.rake
|
465
|
+
- spec/factories/dscf/core/addresses.rb
|
456
466
|
- spec/factories/dscf/core/business_types.rb
|
457
467
|
- spec/factories/dscf/core/businesses.rb
|
458
468
|
- spec/factories/dscf/core/users.rb
|
459
469
|
- spec/factories/dscf/marketplace/categories.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
|
461
474
|
- spec/factories/dscf/marketplace/order_items.rb
|
462
475
|
- spec/factories/dscf/marketplace/orders.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).
|