spree_packeta 0.1.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.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,307 @@
1
+ # Spree Packeta
2
+
3
+ Packeta (Zasilkovna) shipping integration for Spree Commerce. This gem provides SOAP API integration for packet creation, tracking, and label generation.
4
+
5
+ ## Features
6
+
7
+ - **Phase 1 (Current)**:
8
+ - SOAP client wrapper for Packeta API
9
+ - Packet creation and tracking
10
+ - Shipping method and calculator integration
11
+ - Database tracking fields for shipments
12
+
13
+ - **Planned Features**:
14
+ - Dynamic rate calculation from Packeta API
15
+ - Label generation (PDF, PNG, ZPL)
16
+ - COD (Cash on Delivery) support
17
+ - Customs declarations
18
+ - Admin interface for packet management
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'spree_packeta', path: '/Users/oezr/Projects/spree_packeta'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ ```bash
31
+ bundle install
32
+ ```
33
+
34
+ Run the installation generator:
35
+
36
+ ```bash
37
+ bundle exec rails g spree_packeta:install
38
+ ```
39
+
40
+ This will:
41
+ - Copy the migration files
42
+ - Create an initializer with configuration
43
+ - Optionally run migrations
44
+ - Optionally install the Packeta shipping method
45
+
46
+ ### Manual Installation
47
+
48
+ If you prefer to install components separately:
49
+
50
+ ```bash
51
+ # Copy and run migrations
52
+ bundle exec rails spree_packeta:install:migrations
53
+ bundle exec rails db:migrate
54
+
55
+ # Install Packeta shipping method
56
+ bundle exec rake spree_packeta:install
57
+
58
+ # Check status
59
+ bundle exec rake spree_packeta:status
60
+ ```
61
+
62
+ ## Configuration
63
+
64
+ Configure the gem in `config/initializers/spree_packeta.rb`:
65
+
66
+ ```ruby
67
+ SpreePacketa.configure do |config|
68
+ # API credentials
69
+ config.api_password = ENV['PACKETA_API_PASSWORD']
70
+
71
+ # SOAP endpoint (production vs sandbox)
72
+ config.soap_endpoint = ENV.fetch('PACKETA_SOAP_ENDPOINT',
73
+ 'http://www.zasilkovna.cz/api/soap')
74
+
75
+ # WSDL path (can be local file or URL)
76
+ config.wsdl_path = ENV.fetch('PACKETA_WSDL_PATH',
77
+ 'http://www.zasilkovna.cz/api/soap.wsdl')
78
+
79
+ # Default eshop identifier
80
+ config.eshop = ENV['PACKETA_ESHOP']
81
+
82
+ # Sender information for return labels
83
+ config.sender_name = ENV['PACKETA_SENDER_NAME']
84
+ config.sender_email = ENV['PACKETA_SENDER_EMAIL']
85
+ config.sender_phone = ENV['PACKETA_SENDER_PHONE']
86
+
87
+ # Rate caching duration (in seconds)
88
+ config.rate_cache_duration = 3600 # 1 hour
89
+
90
+ # Auto-create packets on shipment
91
+ config.auto_create_packets = true
92
+
93
+ # Sync tracking status interval
94
+ config.tracking_sync_interval = 1.hour
95
+ end
96
+ ```
97
+
98
+ ### Environment Variables
99
+
100
+ Add these to your `.env` file:
101
+
102
+ ```env
103
+ PACKETA_API_PASSWORD=your_api_password_here
104
+ PACKETA_ESHOP=your_eshop_identifier
105
+ PACKETA_SENDER_NAME=Your Store Name
106
+ PACKETA_SENDER_EMAIL=shipping@yourstore.com
107
+ PACKETA_SENDER_PHONE=+420123456789
108
+
109
+ # Optional: Use local WSDL file
110
+ PACKETA_WSDL_PATH=file:///path/to/soap.wsdl
111
+ ```
112
+
113
+ ## Usage
114
+
115
+ ### Quick Start with Rake Tasks
116
+
117
+ The easiest way to set up Packeta:
118
+
119
+ ```bash
120
+ # Install shipping method with default configuration
121
+ bundle exec rake spree_packeta:install
122
+
123
+ # Check installation status and configuration
124
+ bundle exec rake spree_packeta:status
125
+
126
+ # Remove shipping method if needed
127
+ bundle exec rake spree_packeta:uninstall
128
+ ```
129
+
130
+ The `install` task will:
131
+ - Create "Packeta" shipping method
132
+ - Set up Czech Republic and EU zones
133
+ - Configure the Packeta calculator with default rates (99 CZK)
134
+ - Assign shipping categories and zones
135
+
136
+ ### Manual Setup via Admin Panel
137
+
138
+ If you prefer manual setup or need to customize:
139
+
140
+ 1. Go to Settings → Shipping Methods
141
+ 2. Click "New Shipping Method"
142
+ 3. Fill in:
143
+ - **Name**: Packeta
144
+ - **Admin Name**: Packeta (Zásilkovna)
145
+ - **Code**: packeta
146
+ - **Display**: Both (or your preference)
147
+ - **Tracking URL**: `https://tracking.packeta.com/:tracking`
148
+ - **Shipping Categories**: Select applicable categories
149
+ - **Calculator**: Choose "Packeta Shipping Calculator"
150
+ 4. Configure calculator preferences:
151
+ - **Amount**: Base shipping rate (e.g., 99.00)
152
+ - **Currency**: CZK (or your currency)
153
+ 5. Assign to zones (Czech Republic, EU, etc.)
154
+
155
+ ### Creating a Packet
156
+
157
+ The gem provides services for packet management:
158
+
159
+ ```ruby
160
+ # Create a packet from a shipment
161
+ service = SpreePacketa::Services::PacketCreator.new(shipment)
162
+ result = service.create_packet
163
+
164
+ # This will:
165
+ # - Extract shipment details
166
+ # - Call Packeta API to create packet
167
+ # - Store packet ID and barcode on shipment
168
+ # - Return packet details
169
+ ```
170
+
171
+ ### Tracking
172
+
173
+ ```ruby
174
+ # Get current status
175
+ tracker = SpreePacketa::Services::Tracker.new(shipment)
176
+ status = tracker.current_status
177
+ # => { date_time: ..., status_code: ..., status_text: ..., branch_id: ... }
178
+
179
+ # Get full tracking history
180
+ history = tracker.tracking_history
181
+ # => [{ date_time: ..., status_code: ..., ... }, ...]
182
+ ```
183
+
184
+ ### Direct SOAP API Access
185
+
186
+ For advanced use cases, you can use the SOAP operations directly:
187
+
188
+ ```ruby
189
+ # Initialize operations
190
+ ops = SpreePacketa::Soap::Operations.new
191
+
192
+ # Create a packet
193
+ packet = ops.create_packet(
194
+ number: 'ORDER-12345',
195
+ name: 'John',
196
+ surname: 'Doe',
197
+ email: 'john@example.com',
198
+ phone: '+420123456789',
199
+ address_id: 1234, # Packeta pickup point ID
200
+ value: 1000.0,
201
+ weight: 2.5,
202
+ currency: 'CZK'
203
+ )
204
+ # => { id: 123456, barcode: '...', barcode_text: 'Z...' }
205
+
206
+ # Get packet status
207
+ status = ops.packet_status(packet[:id])
208
+ # => { status_code: 1, status_text: 'Packet created', ... }
209
+
210
+ # Get tracking history
211
+ history = ops.packet_tracking(packet[:id])
212
+ # => [{ date_time: ..., status_code: ..., ... }, ...]
213
+ ```
214
+
215
+ ## Database Schema
216
+
217
+ The gem adds the following columns to `spree_shipments`:
218
+
219
+ | Column | Type | Description |
220
+ |---------------------------|----------|--------------------------------------|
221
+ | `packeta_packet_id` | bigint | Packeta packet ID |
222
+ | `packeta_barcode` | string | Packet barcode |
223
+ | `packeta_tracking_url` | string | Full tracking URL |
224
+ | `packeta_status_code` | integer | Latest status code |
225
+ | `packeta_branch_id` | integer | Current branch/pickup point ID |
226
+ | `packeta_last_sync_at` | datetime | Last tracking sync timestamp |
227
+
228
+ ## Error Handling
229
+
230
+ The gem defines custom exceptions for different error scenarios:
231
+
232
+ ```ruby
233
+ begin
234
+ ops.create_packet(attributes)
235
+ rescue SpreePacketa::AuthenticationError => e
236
+ # Invalid API password
237
+ rescue SpreePacketa::ValidationError => e
238
+ # Invalid packet attributes
239
+ rescue SpreePacketa::NotFoundError => e
240
+ # Packet not found
241
+ rescue SpreePacketa::NetworkError => e
242
+ # Network/connectivity issues
243
+ rescue SpreePacketa::ApiError => e
244
+ # Other API errors
245
+ end
246
+ ```
247
+
248
+ ## Development
249
+
250
+ After checking out the repo, run:
251
+
252
+ ```bash
253
+ bundle install
254
+ ```
255
+
256
+ ### Running Tests
257
+
258
+ ```bash
259
+ bundle exec rspec
260
+ ```
261
+
262
+ ### Console
263
+
264
+ ```bash
265
+ bundle exec bin/console
266
+ ```
267
+
268
+ ## Roadmap
269
+
270
+ ### Phase 2: Rate Calculation
271
+ - [ ] Dynamic rate calculation via Packeta API
272
+ - [ ] Zone-based pricing
273
+ - [ ] Service level support
274
+ - [ ] Rate caching
275
+
276
+ ### Phase 3: Label Generation
277
+ - [ ] PDF label generation
278
+ - [ ] Multiple format support (PNG, ZPL)
279
+ - [ ] Batch label generation
280
+ - [ ] Label storage
281
+
282
+ ### Phase 4: Advanced Features
283
+ - [ ] COD support
284
+ - [ ] Customs declarations
285
+ - [ ] Pickup point widget
286
+ - [ ] Return shipments
287
+ - [ ] Packet updates
288
+
289
+ ### Phase 5: Admin Interface
290
+ - [ ] Packeta settings page
291
+ - [ ] Packet management
292
+ - [ ] Bulk operations
293
+ - [ ] Tracking dashboard
294
+
295
+ ## Contributing
296
+
297
+ Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/spree_packeta.
298
+
299
+ ## License
300
+
301
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
302
+
303
+ ## Support
304
+
305
+ For issues specific to this gem, please open an issue on GitHub.
306
+
307
+ For Packeta API documentation and support, visit: https://www.zasilkovna.cz/api/
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpreePacketa
4
+ module OrderDecorator
5
+ def self.prepended(base)
6
+ base.class_eval do
7
+ set_callback :updating_from_params, :before, :update_packeta_pickup_point
8
+
9
+ # Store Packeta pickup point data in order metadata
10
+ store_accessor :public_metadata, :packeta_pickup_point_id
11
+ store_accessor :public_metadata, :packeta_pickup_point_name
12
+ store_accessor :public_metadata, :packeta_pickup_point_address
13
+ end
14
+ end
15
+
16
+ def update_packeta_pickup_point
17
+ return unless @updating_params[:order] && @updating_params[:order][:packeta_pickup_point].present?
18
+ set_packeta_pickup_point(@updating_params[:order].delete(:packeta_pickup_point))
19
+ end
20
+
21
+ # Get the Packeta pickup point ID for this order
22
+ #
23
+ # @return [Integer, nil] The pickup point ID
24
+ def packeta_address_id
25
+ packeta_pickup_point_id&.to_i
26
+ end
27
+
28
+ # Set Packeta pickup point from widget data
29
+ #
30
+ # @param point_data [Hash] Pickup point data from Packeta widget
31
+ def set_packeta_pickup_point(point_data)
32
+ return unless point_data
33
+
34
+ self.packeta_pickup_point_id = point_data[:id]
35
+ self.packeta_pickup_point_name = point_data[:name]
36
+
37
+ address_parts = [point_data[:street], point_data[:city], point_data[:zip]].compact
38
+
39
+ self.packeta_pickup_point_address = address_parts.join(', ')
40
+ end
41
+
42
+ Spree::Order.prepend self
43
+ end
44
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpreePacketa
4
+ module ShipmentDecorator
5
+ def self.prepended(base)
6
+ base.class_eval do
7
+ # Hook into state machine - after successful transition to 'ready'
8
+ state_machine.after_transition to: :ready, do: :auto_create_packeta_packet
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ # Automatically create Packeta packet when shipment becomes ready
15
+ # This method is called after state transition to 'ready' is committed
16
+ # Errors are caught and logged but never block the shipment workflow
17
+ def auto_create_packeta_packet
18
+ # Early returns for conditions that prevent auto-creation
19
+ return unless should_auto_create_packet?
20
+
21
+ # Delegate to service for actual creation
22
+ packet_creator = SpreePacketa::Services::PacketCreator.new(self)
23
+ result = packet_creator.create_packet
24
+
25
+ Rails.logger.info("Auto-created Packeta packet #{result[:id]} for shipment #{id}")
26
+ rescue SpreePacketa::ValidationError => e
27
+ Rails.logger.warn("Automatic Packeta packet creation skipped for shipment #{id}: #{e.message}")
28
+ rescue SpreePacketa::NetworkError, SpreePacketa::ApiError => e
29
+ Rails.logger.error("Packeta API error during auto-creation for shipment #{id}: #{e.message}")
30
+ rescue StandardError => e
31
+ Rails.logger.error("Unexpected error during Packeta auto-creation for shipment #{id}: #{e.class} - #{e.message}")
32
+ Rails.logger.error(e.backtrace.first(10).join("\n"))
33
+ end
34
+
35
+ # Determine if automatic packet creation should proceed
36
+ def should_auto_create_packet?
37
+ # Check global configuration
38
+ return false unless SpreePacketa.configuration.auto_create_packets
39
+
40
+ # Check if packet already exists
41
+ return false if packeta_packet_id.present?
42
+
43
+ # Check if this is a Packeta shipment
44
+ return false unless packeta_shipping_method?
45
+
46
+ # Check if pickup point is selected
47
+ return false unless order.packeta_pickup_point_id.present?
48
+
49
+ true
50
+ end
51
+
52
+ # Check if the shipping method is a Packeta shipping method
53
+ def packeta_shipping_method?
54
+ return false unless shipping_method
55
+
56
+ # Check if it's our Packeta shipping method class
57
+ return true if shipping_method.is_a?(SpreePacketa::Models::ShippingMethod)
58
+
59
+ # Fallback: check carrier name for admin-configured methods
60
+ shipping_method.carrier == 'Packeta'
61
+ end
62
+
63
+ Spree::Shipment.prepend self
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddPacketaTrackingToShipments < ActiveRecord::Migration[7.0]
4
+ def change
5
+ add_column :spree_shipments, :packeta_packet_id, :bigint
6
+ add_column :spree_shipments, :packeta_barcode, :string
7
+ add_column :spree_shipments, :packeta_tracking_url, :string
8
+ add_column :spree_shipments, :packeta_status_code, :integer
9
+ add_column :spree_shipments, :packeta_branch_id, :integer
10
+ add_column :spree_shipments, :packeta_last_sync_at, :datetime
11
+
12
+ add_index :spree_shipments, :packeta_packet_id, unique: true, name: 'index_shipments_on_packeta_packet_id'
13
+ end
14
+ end