dscf-marketplace 0.2.92 → 0.2.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4cf17e898ac996cc17f10dc5f2297f510d6f2c68e6f0c1d3430db98934106282
4
- data.tar.gz: 5efcbb36d3bfebfb462b92aeea5e386adf805da05f76ad10346e167a04dcf074
3
+ metadata.gz: 40d81c92e7033ba2d583bca456a9fa0114795f713c118e92c1b70d2141c1c3ac
4
+ data.tar.gz: e8f5cca804a50156aff124176f482980628a069a3961c295d0480c395607fbc8
5
5
  SHA512:
6
- metadata.gz: cbf84769b2a1065d9e890f72c84ccd72732489ad1df9d8c1890df6602308c49545680d44eb2a2a89dc99fd737dcf8f42444fb78ee5c8b1b6f28fc5924f2355a2
7
- data.tar.gz: d7c9080cc4109ddd58dc1fb0aa701e8e93511b660079c47a9e2b5016718e513c00d67272c5755ab1717a8788b73f315510a7c228ff5a7a213a80799d743e8faf
6
+ metadata.gz: 75ba3df20836f6e89c20b2a35a01822f132f13fec48c2604807e96ad5b75f675bc6cbf0f72d65acbf40492e4d03d80d8e28532ac1941d6cde80779f4611e208b
7
+ data.tar.gz: 1e3620e360ec311517da1ff4955960a301523b0375801b236b9c3f518dc70ddf97daa7bd576068f9955eed2658f72619ffad90cbca02a13dcccf5e62ffa85fbd
@@ -61,7 +61,7 @@ module Dscf
61
61
 
62
62
  def default_serializer_includes
63
63
  {
64
- index: [ :user, :ordered_by, :ordered_to, :quotation, order_items: [:product] ],
64
+ index: [ :user, :ordered_by, :ordered_to, :quotation, order_items: [ :product ] ],
65
65
  show: [ :user, :ordered_by, :ordered_to, :quotation, :listing, :delivery_order, :order_items ],
66
66
  create: [ :user, :ordered_by, :ordered_to, :quotation, :listing ],
67
67
  update: [ :user, :ordered_by, :ordered_to, :quotation, :listing ]
@@ -34,6 +34,18 @@ module Dscf
34
34
  render_success("request_for_quotations.success.index", data: rfqs, serializer_options: options)
35
35
  end
36
36
 
37
+ def respond
38
+ @rfq = Dscf::Marketplace::RequestForQuotation.find(params[:id])
39
+ service = RfqResponseService.new(current_user)
40
+ result = service.respond_to_rfq(@rfq, params.permit(:notes, :valid_until, :delivery_date, quotation_items_attributes: [ :rfq_item_id, :unit_id, :quantity, :unit_price, :notes ]))
41
+
42
+ if result[:success]
43
+ render_success("request_for_quotations.success.responded", data: result[:quotation], serializer_options: {include: [ :business, quotation_items: [ :product, :unit, :rfq_item ] ]})
44
+ else
45
+ render_error("request_for_quotations.errors.respond_failed", errors: result[:errors])
46
+ end
47
+ end
48
+
37
49
  private
38
50
 
39
51
  def model_params
@@ -61,18 +61,6 @@ module Dscf
61
61
  status == "draft"
62
62
  end
63
63
 
64
- def sent?
65
- status == "sent"
66
- end
67
-
68
- def accepted?
69
- status == "accepted"
70
- end
71
-
72
- def rejected?
73
- status == "rejected"
74
- end
75
-
76
64
  def within_validity_period?
77
65
  valid_until > Time.current && !expired?
78
66
  end
@@ -162,6 +150,23 @@ module Dscf
162
150
  order
163
151
  end
164
152
 
153
+ def build_from_rfq_items(items_attributes)
154
+ items_attributes.each do |item_attrs|
155
+ rfq_item = request_for_quotation.rfq_items.find_by(id: item_attrs[:rfq_item_id])
156
+ next unless rfq_item
157
+
158
+ quotation_items.build(
159
+ rfq_item: rfq_item,
160
+ product: rfq_item.product,
161
+ unit: item_attrs[:unit_id] ? Dscf::Marketplace::Unit.find(item_attrs[:unit_id]) : rfq_item.unit,
162
+ quantity: [ item_attrs[:quantity].to_i, rfq_item.quantity ].min,
163
+ unit_price: item_attrs[:unit_price].to_f,
164
+ notes: item_attrs[:notes] || ""
165
+ )
166
+ end
167
+ valid? # To trigger validations
168
+ end
169
+
165
170
  private
166
171
 
167
172
  def calculate_total_price
@@ -25,7 +25,7 @@ module Dscf
25
25
 
26
26
  # Ransack configuration for secure filtering
27
27
  def self.ransackable_attributes(_auth_object = nil)
28
- %w[id quotation_id rfq_item_id product_id unit_id quantity unit_price subtotal created_at updated_at]
28
+ %w[id quotation_id rfq_item_id product_id unit_id quantity unit_price subtotal notes created_at updated_at]
29
29
  end
30
30
 
31
31
  def self.ransackable_associations(_auth_object = nil)
@@ -0,0 +1,55 @@
1
+ module Dscf
2
+ module Marketplace
3
+ class RfqResponseService
4
+ def initialize(current_user)
5
+ @current_user = current_user
6
+ end
7
+
8
+ def respond_to_rfq(rfq, params = {})
9
+ return {success: false, errors: [ "RFQ not found or invalid" ]} unless rfq.present? && rfq.sent?
10
+
11
+ business = Dscf::Core::Business.find_by(user: @current_user)
12
+ return {success: false, errors: [ "No business associated with user" ]} unless business.present?
13
+
14
+ quotation = Dscf::Marketplace::Quotation.new(
15
+ request_for_quotation: rfq,
16
+ business: business,
17
+ status: :draft,
18
+ notes: params[:notes] || "",
19
+ valid_until: params[:valid_until] || 7.days.from_now,
20
+ delivery_date: params[:delivery_date] || (params[:valid_until] || 7.days.from_now).to_date
21
+ )
22
+
23
+ if quotation.save
24
+ create_quotation_items(quotation, params[:quotation_items_attributes] || [])
25
+ rfq.update(status: :responded) if rfq.sent?
26
+ {success: true, quotation: quotation}
27
+ else
28
+ {success: false, errors: quotation.errors.full_messages}
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def create_quotation_items(quotation, items_attributes)
35
+ items_attributes.each do |item_attrs|
36
+ rfq_item = quotation.request_for_quotation.rfq_items.find_by(id: item_attrs[:rfq_item_id])
37
+ next unless rfq_item
38
+
39
+ unit_id = item_attrs[:unit_id] || rfq_item.unit_id
40
+ unit = Dscf::Marketplace::Unit.find_by(id: unit_id)
41
+
42
+ quotation.quotation_items.build(
43
+ rfq_item: rfq_item,
44
+ product: rfq_item.product,
45
+ unit: unit,
46
+ quantity: item_attrs[:quantity] || rfq_item.quantity,
47
+ unit_price: item_attrs[:unit_price],
48
+ notes: item_attrs[:notes] || ""
49
+ )
50
+ end
51
+ quotation.save
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,6 +1,7 @@
1
- # DSCF Marketplace Locale File
2
- # This file contains I18n messages for automatic message resolution
3
- # Pattern: {model_name}.{success|errors}.{action_name}
1
+ # Updated DSCF Marketplace Locale File
2
+ # Added missing keys for request_for_quotations (plural) to match controller usage
3
+ # Ensures full error messages are displayed instead of "Translation missing"
4
+ # Pattern: {model_name}.{success|errors}.{action_name} - using plural for controller consistency
4
5
 
5
6
  en:
6
7
  # Core Models
@@ -106,6 +107,28 @@ en:
106
107
  update: "Failed to update listing"
107
108
  destroy: "Failed to delete listing"
108
109
 
110
+ # RFQ Models (Added plural keys to match controller)
111
+ request_for_quotations:
112
+ success:
113
+ index: "RFQs retrieved successfully"
114
+ show: "RFQ details retrieved successfully"
115
+ create: "RFQ created successfully"
116
+ update: "RFQ updated successfully"
117
+ destroy: "RFQ deleted successfully"
118
+ sent: "RFQ sent successfully"
119
+ closed: "RFQ closed successfully"
120
+ responded: "RFQ response sent successfully"
121
+ errors:
122
+ index: "Failed to retrieve RFQs"
123
+ show: "Failed to retrieve RFQ details"
124
+ create_failed: "Failed to create RFQ" # Key fix: Matches render_error("request_for_quotations.errors.create_failed")
125
+ update: "Failed to update RFQ"
126
+ destroy: "Failed to delete RFQ"
127
+ send_failed: "Failed to send RFQ"
128
+ close_failed: "Failed to close RFQ"
129
+ respond_failed: "Failed to respond to RFQ"
130
+
131
+ # Singular fallback for consistency
109
132
  request_for_quotation:
110
133
  success:
111
134
  index: "RFQs retrieved successfully"
@@ -115,6 +138,7 @@ en:
115
138
  destroy: "RFQ deleted successfully"
116
139
  sent: "RFQ sent successfully"
117
140
  closed: "RFQ closed successfully"
141
+ responded: "RFQ response sent successfully"
118
142
  errors:
119
143
  index: "Failed to retrieve RFQs"
120
144
  show: "Failed to retrieve RFQ details"
@@ -123,6 +147,7 @@ en:
123
147
  destroy: "Failed to delete RFQ"
124
148
  sent: "Failed to send RFQ"
125
149
  closed: "Failed to close RFQ"
150
+ responded: "Failed to respond to RFQ"
126
151
 
127
152
  rfq_item:
128
153
  success:
@@ -351,3 +376,34 @@ en:
351
376
  operation_failed: "Operation failed"
352
377
  record_not_found: "Record not found"
353
378
  supplier_not_found: "Supplier not found"
379
+ # Added for common validation errors to show full messages
380
+ activerecord:
381
+ errors:
382
+ messages:
383
+ record_invalid: "Validation failed: %{errors}"
384
+ inclusion: "is not included in the list"
385
+ confirmation: "doesn't match %{attribute}"
386
+ accepted: "must be accepted"
387
+ empty: "can't be empty"
388
+ blank: "can't be blank"
389
+ too_short: "is too short (minimum is %{count} characters)"
390
+ too_long: "is too long (maximum is %{count} characters)"
391
+ invalid: "is invalid"
392
+ not_a_number: "is not a number"
393
+ not_an_integer: "is not an integer"
394
+ greater_than: "must be greater than %{count}"
395
+ greater_than_or_equal_to: "must be greater than or equal to %{count}"
396
+ equal_to: "must be equal to %{count}"
397
+ less_than: "must be less than %{count}"
398
+ less_than_or_equal_to: "must be less than or equal to %{count}"
399
+ other_than: "must be other than %{count}"
400
+ excluded: "should not be %{object}"
401
+ taken: "has already been taken"
402
+ unique: "has already been taken"
403
+ uniqueness: "has already been taken"
404
+ present: "must be present"
405
+ absence: "must be blank"
406
+ url: "is not a valid URL"
407
+ email: "is not a valid email"
408
+ format: "is invalid"
409
+ required: "can't be blank"
data/config/routes.rb CHANGED
@@ -50,6 +50,7 @@ Dscf::Marketplace::Engine.routes.draw do
50
50
  member do
51
51
  post "send_rfq"
52
52
  post "close"
53
+ post "respond"
53
54
  end
54
55
  collection do
55
56
  get "my_rfqs"
@@ -0,0 +1,5 @@
1
+ class AddNotesToDscfMarketplaceQuotationItems < ActiveRecord::Migration[8.0]
2
+ def change
3
+ add_column :dscf_marketplace_quotation_items, :notes, :text
4
+ end
5
+ end
@@ -1,5 +1,5 @@
1
1
  module Dscf
2
2
  module Marketplace
3
- VERSION = "0.2.92".freeze
3
+ VERSION = "0.2.93".freeze
4
4
  end
5
5
  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.2.92
4
+ version: 0.2.93
5
5
  platform: ruby
6
6
  authors:
7
7
  - Asrat
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-09-09 00:00:00.000000000 Z
10
+ date: 2025-09-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -470,6 +470,7 @@ files:
470
470
  - app/serializers/dscf/marketplace/unit_conversion_serializer.rb
471
471
  - app/serializers/dscf/marketplace/unit_serializer.rb
472
472
  - app/services/dscf/marketplace/my_resource_service.rb
473
+ - app/services/dscf/marketplace/rfq_response_service.rb
473
474
  - config/environments/production.rb
474
475
  - config/locales/en.yml
475
476
  - config/routes.rb
@@ -497,6 +498,7 @@ files:
497
498
  - db/migrate/20250901080134_add_fulfillment_type_to_orders.rb
498
499
  - db/migrate/20250903061154_add_ordered_fields_to_orders.rb
499
500
  - db/migrate/20250903061306_populate_ordered_fields_for_existing_orders.rb
501
+ - db/migrate/20250909092700_add_notes_to_dscf_marketplace_quotation_items.rb
500
502
  - lib/dscf/marketplace.rb
501
503
  - lib/dscf/marketplace/engine.rb
502
504
  - lib/dscf/marketplace/version.rb