printavo-ruby 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72653b7d55ebd17fbbc5331c50e530cbb15655175c35190ba3c740e5f53fad0f
4
- data.tar.gz: 2f0bd39de81b2862289510ec8b282e3009165d86aa8c32c27a96b6f03228fc9e
3
+ metadata.gz: 3563777c538165a00ca4888be9d489c6a0e9b14542dac6d02a18b60894826f7d
4
+ data.tar.gz: ceb47bf69ff0dc7f48888d4fee3f601aa621015f8028e6dd0af31e2037b36749
5
5
  SHA512:
6
- metadata.gz: 8dd07daab212f1a7cf4a137fd3890f099422f7fc24cb15a2368f30864d7d9c701c83c7d4d0d27a59dcec3ec11444ddb4f11acae4edf035d4033ab03523ad4127
7
- data.tar.gz: 013ea7ed343e5ee3915f9092609e756b4f917d0f1d24dd6c04b4a4c837dc0dbd5d2a3d475ac67e5bde40e9ff335b219c1b352627ae8e925ac857ac9fba17951f
6
+ metadata.gz: b266887065b5f727b6f9660557ce37d7ab64b53f23033f5c64c678cae919f00cd21e169fee17177bce7e873fdcc9d978307a3254e20de0224aab4b8e9c462d97
7
+ data.tar.gz: 4d1c944ca7283bab4e45dfe3cde0ddc6acf3ea35a167ee7b49574b6f7e367fff77a833253976605e2f8219aea932d5056b04004fcb55553cd4975bdfe853b3fe
data/docs/CHANGELOG.md CHANGED
@@ -6,6 +6,31 @@ All notable changes to this project will be documented in this file.
6
6
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
7
7
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
+ ## [0.6.0] - 2026-03-30
10
+
11
+ ### Added
12
+ - `Printavo::Contact` model (`id`, `first_name`, `last_name`, `full_name`, `email`, `phone`, `fax`)
13
+ - `Contacts` resource: `find(id)`, `create(**input)`, `contactCreate` mutation,
14
+ `update(id, **input)`, `contactUpdate` mutation
15
+ - `Printavo::Invoice` model (`id`, `nickname`, `total`, `amount_paid`,
16
+ `amount_outstanding`, `paid_in_full?`, `invoice_at`, `payment_due_at`,
17
+ `status`, `status_key`, `status?`, `contact`)
18
+ - `Invoices` resource: `all`, `find(id)`, `update(id, **input)` (`invoiceUpdate` mutation);
19
+ note: invoices are promoted from quotes, so no `create` — use `client.orders.create`
20
+ - `Printavo::Account` model (`id`, `company_name`, `company_email`, `phone`,
21
+ `website`, `logo_url`, `locale`)
22
+ - `Account` resource: singleton `find` — no ID argument, returns the account
23
+ associated with the current API credentials
24
+ - `client.contacts`, `client.invoices`, `client.account` entry points on `Printavo::Client`
25
+ - 10 new `.graphql` files under `lib/printavo/graphql/` (contacts/find, create, update;
26
+ invoices/all, find, update; account/find)
27
+
28
+ ### Changed
29
+ - `spec/support/factories.rb` split into 9 domain files under `spec/support/factories/`
30
+ (`account`, `contact`, `customer`, `graphql`, `inquiry`, `invoice`, `job`, `order`, `status`)
31
+ - `spec/spec_helper.rb` loads factory files via `Dir` glob; `config.include Factories` moved here
32
+ - Removed `Metrics/ModuleLength` exclusion from `.rubocop.yml` — no longer needed
33
+
9
34
  ## [0.5.2] - 2026-03-30
10
35
 
11
36
  ### Changed
data/docs/TODO.md CHANGED
@@ -69,6 +69,7 @@ Full task list for `printavo-ruby` across all versions. Checked items are shippe
69
69
  - [x] Extract 17 GraphQL heredocs into external `.graphql` files (`lib/printavo/graphql/<resource>/<op>.graphql`)
70
70
  - [x] `docs/` consolidation — `CHANGELOG.md`, `CONTRIBUTING.md`, `FUTURE.md`, `CACHING.md`
71
71
  - [x] `docs/CACHING.md` — nine caching strategy patterns for rate-limit-aware consumers
72
+ - [x] Split `spec/support/factories.rb` monolith into 9 domain files under `spec/support/factories/`
72
73
 
73
74
  ---
74
75
 
@@ -86,38 +87,166 @@ Resources and mutations present in the Printavo V2 GraphQL API that are not yet
86
87
  wrapped by a resource class. Raw GraphQL access works for all of these today via
87
88
  `client.graphql.query(...)` — these tasks add first-class resource support.
88
89
 
89
- ### Contacts
90
+ ### Embedded / Infrastructure Types (no standalone resource needed)
91
+
92
+ These types appear in the schema but are sub-objects, pagination wrappers, or
93
+ return values — they are exposed via model field accessors, not separate resources.
94
+
95
+ - `*Connection` / `*Edge` (all variants) — GraphQL pagination wrappers; handled by `Printavo::Page`
96
+ - `Address`, `BillingAddress`, `CustomerAddress`, `MerchAddress`, `CustomAddress` — embedded address shapes
97
+ - `Avatar` — embedded in `User`
98
+ - `CatalogInformation`, `InvoiceInformation` — embedded in `Account`
99
+ - `DeletedID` — return type for delete mutations
100
+ - `EmailMessage`, `TextMessage` — message subtypes under `Thread`
101
+ - `Feature`, `FeatureRestriction` — embedded in `Account.features`
102
+ - `LineItemEnabledColumns`, `LineItemGroupSize`, `LineItemPriceReceipt`, `LineItemSizeCount` — embedded in `LineItemGroup` / `LineItem`
103
+ - `LoggedIn` — auth response type
104
+ - `MerchOrderDelivery`, `MerchStoreSummary` — embedded in `MerchOrder` / `MerchStore`
105
+ - `MessageAttachment` — embedded in `Thread` messages
106
+ - `ObjectTimestamps` — `createdAt` / `updatedAt` on all objects
107
+ - `OrderUnionConnection` / `OrderUnionEdge` — union pagination for `Quote | Invoice`; handled via inline fragments
108
+ - `PageInfo` — cursor pagination metadata; handled by `Printavo::Page`
109
+ - `PaymentRequestDetail` — embedded in `PaymentRequest`
110
+ - `Permission` — embedded in `User`
111
+ - `Personalization` — size/color breakdown embedded in `LineItem` / `Imprint`
112
+ - `PricingMatrixCell`, `PricingMatrixColumn` — sub-objects of `PricingMatrix`
113
+ - `ProductCatalog` — configuration embedded in `Account`
114
+ - `Social` — social media links embedded in `Account`
115
+ - `ThreadSummary` — preview summary embedded on orders; not a standalone query
116
+ - `TransactionDetails`, `TransactionUnionConnection`, `TransactionUnionEdge` — embedded / pagination wrappers
90
117
 
91
- - [ ] `Printavo::Contact` model (`id`, `firstName`, `lastName`, `email`, `phone`)
92
- - [ ] `Contacts` resource: `find(id)`
93
- - [ ] `contact` query (contacts are distinct from the customer's `primaryContact`)
118
+ ---
119
+
120
+ ### Account
121
+
122
+ - [x] `Printavo::Account` model (shop-level info: name, address, phone, logo)
123
+ - [x] `Account` resource: `find` (singleton — no ID needed)
124
+ - [x] `account` query
125
+
126
+ ### Contacts ✅
127
+
128
+ - [x] `Printavo::Contact` model (`id`, `firstName`, `lastName`, `email`, `phone`)
129
+ - [x] `Contacts` resource: `find(id)`, `create`, `update`
130
+ - [x] `contact` query (contacts are distinct from the customer's `primaryContact`)
131
+
132
+ ### Invoices ✅
133
+
134
+ - [x] `Printavo::Invoice` model (mirrors Order; has `amountPaid`, `amountOutstanding`, `paidInFull?`)
135
+ - [x] `Invoices` resource: `all`, `find(id)`, `update`
136
+ - [x] `client.invoices` entry point on `Printavo::Client`
137
+
138
+ ### Approvals
94
139
 
95
- ### Invoices
140
+ - [ ] `Printavo::Approval` model (`id`, `status`, `approvedAt`, `contact`)
141
+ - [ ] `Printavo::ApprovalRequest` model (`id`, `sentAt`, `expiresAt`, `approval`)
142
+ - [ ] `Approvals` resource: `all(order_id:)`, `find(id)`
143
+ - [ ] `approvalCreate`, `approvalUpdate` mutations
96
144
 
97
- - [ ] `Printavo::Invoice` model (mirrors Order; has `invoiceNumber`, `paidAt`, `balanceDue`)
98
- - [ ] `Invoices` resource: `all`, `find(id)`
99
- - [ ] `client.invoices` entry point on `Printavo::Client`
145
+ ### Categories
146
+
147
+ - [ ] `Printavo::Category` model (`id`, `name`)
148
+ - [ ] `Categories` resource: `all`, `find(id)` — reference data for products/line items
149
+
150
+ ### Contractor Profiles
151
+
152
+ - [ ] `Printavo::ContractorProfile` model (`id`, `name`, `email`)
153
+ - [ ] `ContractorProfiles` resource: `all`, `find(id)` — contractors assignable to invoices
154
+
155
+ ### Delivery Methods
156
+
157
+ - [ ] `Printavo::DeliveryMethod` model (`id`, `name`)
158
+ - [ ] `DeliveryMethods` resource: `all` — reference data (pickup, ship, etc.)
159
+
160
+ ### Delete Mutations
161
+
162
+ - [ ] `contactDelete(id:)`
163
+ - [ ] `customerDelete(id:)`
164
+ - [ ] `quoteDelete(id:)` / `invoiceDelete(id:)`
165
+ - [ ] `lineItemDelete(id:)`
166
+ - [ ] `taskDelete(id:)`
167
+
168
+ ### Email Templates
169
+
170
+ - [ ] `Printavo::EmailTemplate` model (`id`, `name`, `subject`, `body`)
171
+ - [ ] `EmailTemplates` resource: `all`, `find(id)`
172
+
173
+ ### Expenses
174
+
175
+ - [ ] `Printavo::Expense` model (`id`, `name`, `amount`, `category`)
176
+ - [ ] `Expenses` resource: `all(order_id:)`, `find(id)`, `create`, `update`
177
+ - [ ] `expenseCreate`, `expenseUpdate` mutations
178
+
179
+ ### Fees
180
+
181
+ - [ ] `Printavo::Fee` model (`id`, `name`, `amount`, `taxable`)
182
+ - [ ] `Fees` resource: `all(order_id:)`, `find(id)`, `create`, `update`
183
+ - [ ] `feeCreate`, `feeUpdate` mutations
184
+
185
+ ### Imprints
186
+
187
+ - [ ] `Printavo::Imprint` model (`id`, `name`, `position`, `colors`, `personalization`)
188
+ - [ ] `Imprints` resource: `all(line_item_group_id:)`, `find(id)`, `create`, `update`
189
+ - [ ] `imprintCreate`, `imprintUpdate` mutations
100
190
 
101
191
  ### Line Item Groups
102
192
 
103
- - [ ] `Printavo::LineItemGroup` model (`id`, `name`, `description`)
104
- - [ ] `LineItemGroups` resource: `all(order_id:)`, `find(id)`
105
- - [ ] `lineItemGroup` / `lineItemGroups` queries
193
+ - [ ] `Printavo::LineItemGroup` model (`id`, `name`, `description`, `sizes`, `enabled_columns`)
194
+ - [ ] `LineItemGroups` resource: `all(order_id:)`, `find(id)`, `create`, `update`
195
+ - [ ] `lineItemGroupCreate`, `lineItemGroupUpdate` mutations
106
196
 
107
197
  ### Merch
108
198
 
109
- - [ ] `Printavo::MerchStore` model
110
- - [ ] `Printavo::MerchOrder` model
199
+ - [ ] `Printavo::MerchStore` model (`id`, `name`, `url`, `summary`)
200
+ - [ ] `Printavo::MerchOrder` model (`id`, `status`, `delivery`, `contact`)
111
201
  - [ ] `MerchStores` resource: `all`, `find(id)`
112
202
  - [ ] `MerchOrders` resource: `all`, `find(id)`
113
203
  - [ ] `client.merch_stores` / `client.merch_orders` entry points
114
204
 
115
- ### Transactions & Payments
205
+ ### Mockups & Production Files
116
206
 
117
- - [ ] `Printavo::Transaction` model (`id`, `amount`, `kind`, `createdAt`)
118
- - [ ] `Transactions` resource: `all(order_id:)`, `find(id)`
119
- - [ ] Payment request mutations
120
- - [ ] Payment dispute handling
207
+ - [ ] `Printavo::Mockup` model (`id`, `url`, `position`, `createdAt`)
208
+ - [ ] `Printavo::ProductionFile` model (`id`, `url`, `filename`, `createdAt`)
209
+ - [ ] `Mockups` resource: `all(order_id:)`, `find(id)`, `create`
210
+ - [ ] `ProductionFiles` resource: `all(order_id:)`, `find(id)`, `create`
211
+
212
+ ### Payments
213
+
214
+ - [ ] `Printavo::Payment` model (`id`, `amount`, `method`, `paidAt`)
215
+ - [ ] `Printavo::PaymentDispute` model (`id`, `amount`, `reason`, `status`)
216
+ - [ ] `Printavo::PaymentRequest` model (`id`, `amount`, `sentAt`, `paidAt`, `details`)
217
+ - [ ] `Printavo::PaymentTerm` model (`id`, `name`, `netDays`) — reference data
218
+ - [ ] `Payments` resource: `all(order_id:)`, `find(id)`
219
+ - [ ] `PaymentRequests` resource: `all(order_id:)`, `find(id)`, `create`
220
+ - [ ] `PaymentTerms` resource: `all` — reference data
221
+ - [ ] `paymentRequestCreate` mutation
222
+
223
+ ### Preset Tasks
224
+
225
+ - [ ] `Printavo::PresetTask` model (`id`, `body`, `dueOffsetDays`, `assignee`)
226
+ - [ ] `Printavo::PresetTaskGroup` model (`id`, `name`, `tasks`)
227
+ - [ ] `PresetTaskGroups` resource: `all`, `find(id)`, `create`, `update`
228
+ - [ ] `presetTaskGroupCreate`, `presetTaskGroupUpdate` mutations
229
+
230
+ ### Product Catalog & Pricing
231
+
232
+ - [ ] `Printavo::Product` model (`id`, `name`, `sku`, `description`)
233
+ - [ ] `Printavo::PricingMatrix` model (`id`, `name`, `cells`, `columns`)
234
+ - [ ] `Products` resource: `all`, `find(id)`
235
+ - [ ] `PricingMatrices` resource: `all`, `find(id)`
236
+
237
+ ### Purchase Orders
238
+
239
+ - [ ] `Printavo::PurchaseOrder` model (`id`, `vendorName`, `total`, `sentAt`)
240
+ - [ ] `Printavo::PoLineItem` model (`id`, `name`, `quantity`, `price`)
241
+ - [ ] `PurchaseOrders` resource: `all`, `find(id)`, `create`, `update`
242
+ - [ ] `purchaseOrderCreate`, `purchaseOrderUpdate` mutations
243
+
244
+ ### Refunds & Returns
245
+
246
+ - [ ] `Printavo::Refund` model (`id`, `amount`, `reason`, `createdAt`)
247
+ - [ ] `Printavo::Return` model (`id`, `quantity`, `reason`, `createdAt`)
248
+ - [ ] `Refunds` resource: `all(order_id:)`, `find(id)`, `create`
249
+ - [ ] `refundCreate` mutation
121
250
 
122
251
  ### Tasks
123
252
 
@@ -127,62 +256,96 @@ wrapped by a resource class. Raw GraphQL access works for all of these today via
127
256
 
128
257
  ### Threads (Messages)
129
258
 
130
- - [ ] `Printavo::Thread` model (`id`, `body`, `author`, `createdAt`)
259
+ - [ ] `Printavo::Thread` model (`id`, `body`, `author`, `createdAt`, `attachments`)
131
260
  - [ ] `Threads` resource: `all(order_id:)`, `find(id)`, `create`
132
261
  - [ ] `threadCreate` mutation
133
262
 
134
- ### Account
263
+ ### Transactions
135
264
 
136
- - [ ] `Printavo::Account` model (shop-level info: name, address, phone, logo)
137
- - [ ] `Account` resource: `find` (singleton — no ID needed)
138
- - [ ] `account` query
265
+ - [ ] `Printavo::Transaction` model (`id`, `amount`, `kind`, `createdAt`, `details`)
266
+ - [ ] `Transactions` resource: `all(order_id:)`, `find(id)`
139
267
 
140
- ### Delete Mutations
268
+ ### Types of Work
141
269
 
142
- - [ ] `customerDelete(id:)`
143
- - [ ] `quoteDelete(id:)` / `invoiceDelete(id:)`
144
- - [ ] `lineItemDelete(id:)`
145
- - [ ] `taskDelete(id:)`
270
+ - [ ] `Printavo::TypeOfWork` model (`id`, `name`)
271
+ - [ ] `TypesOfWork` resource: `all` — reference data for order categorization
146
272
 
147
- ### Product Catalog & Pricing
273
+ ### Users
148
274
 
149
- - [ ] `Printavo::Product` model
150
- - [ ] `Products` resource: `all`, `find(id)`
151
- - [ ] Pricing matrix queries
275
+ - [ ] `Printavo::User` model (`id`, `firstName`, `lastName`, `email`, `avatar`, `permissions`)
276
+ - [ ] `Users` resource: `all`, `find(id)` — shop staff members
277
+
278
+ ### Vendors
279
+
280
+ - [ ] `Printavo::Vendor` model (`id`, `name`, `email`, `phone`)
281
+ - [ ] `Vendors` resource: `all`, `find(id)`, `create`, `update`
282
+ - [ ] `vendorCreate`, `vendorUpdate` mutations
152
283
 
153
284
  ---
154
285
 
155
286
  ## Planned Versions
156
287
 
157
- ### v0.6.0 — Invoices, Contacts & Analytics
288
+ ### v0.6.0 — Invoices, Contacts & Account ✅
158
289
 
159
- - [ ] `Invoices` resource (`all`, `find`)
160
- - [ ] `Contacts` resource (`find`)
161
- - [ ] `Account` resource (singleton)
162
- - [ ] Analytics/Reporting resource: revenue, job counts, customer activity, turnaround times
163
- - [ ] Community burn-in — gather feedback on 0.5.x API surface
290
+ - [x] `Invoices` resource (`all`, `find`, `update`)
291
+ - [x] `Contacts` resource (`find`, `create`, `update`)
292
+ - [x] `Account` resource (singleton `find`)
164
293
 
165
294
  ### v0.7.0 — Transactions, Tasks & Threads
166
295
 
167
296
  - [ ] `Transactions` resource (`all`, `find`)
168
297
  - [ ] `Tasks` resource (`all`, `find`, `create`, `update`, `complete`)
169
298
  - [ ] `Threads` resource (`all`, `find`, `create`)
170
- - [ ] Delete mutations for Customer, Order, LineItem, Task
299
+ - [ ] Delete mutations: `contactDelete`, `customerDelete`, `quoteDelete`, `invoiceDelete`, `lineItemDelete`, `taskDelete`
300
+
301
+ ### v0.8.0 — Order Structure: Line Item Groups, Imprints & Fees
171
302
 
172
- ### v0.8.0 Merch & Products
303
+ - [ ] `LineItemGroups` resource (`all`, `find`, `create`, `update`)
304
+ - [ ] `Imprints` resource (`all`, `find`, `create`, `update`)
305
+ - [ ] `Fees` resource (`all`, `find`, `create`, `update`)
306
+ - [ ] `Expenses` resource (`all`, `find`, `create`, `update`)
307
+
308
+ ### v0.9.0 — Merch, Products & Pricing
173
309
 
174
310
  - [ ] `MerchStores` + `MerchOrders` resources
175
- - [ ] `LineItemGroups` resource
176
- - [ ] `Products` resource + pricing matrix queries
311
+ - [ ] `Products` resource + `PricingMatrices` resource
312
+ - [ ] `Categories` resource (reference data)
313
+
314
+ ### v0.10.0 — Financial: Payments, Purchase Orders & Refunds
315
+
316
+ - [ ] `Payments` resource (`all`, `find`)
317
+ - [ ] `PaymentRequests` resource (`all`, `find`, `create`)
318
+ - [ ] `PaymentTerms` resource (`all`) — reference data
319
+ - [ ] `PurchaseOrders` + `PoLineItems` resources
320
+ - [ ] `Refunds` + `Returns` resources
321
+
322
+ ### v0.11.0 — Workflow: Approvals & Preset Tasks
323
+
324
+ - [ ] `Approvals` resource (`all`, `find`, `create`)
325
+ - [ ] `PresetTaskGroups` resource (`all`, `find`, `create`, `update`)
326
+
327
+ ### v0.12.0 — Files, Media & Communication
328
+
329
+ - [ ] `ProductionFiles` resource (`all`, `find`, `create`)
330
+ - [ ] `Mockups` resource (`all`, `find`, `create`)
331
+ - [ ] `EmailTemplates` resource (`all`, `find`)
332
+
333
+ ### v0.13.0 — People, Orgs & Reference Data
334
+
335
+ - [ ] `Users` resource (`all`, `find`)
336
+ - [ ] `Vendors` resource (`all`, `find`, `create`, `update`)
337
+ - [ ] `ContractorProfiles` resource (`all`, `find`)
338
+ - [ ] `DeliveryMethods` resource (`all`) — reference data
339
+ - [ ] `TypesOfWork` resource (`all`) — reference data
177
340
 
178
- ### v0.9.0 — Retry / Backoff & CLI
341
+ ### v0.14.0 — Retry / Backoff & CLI
179
342
 
180
343
  - [ ] Configurable `max_retries` on `Printavo::Client`
181
344
  - [ ] Exponential backoff with jitter on 429 responses
182
345
  - [ ] `retry_on_rate_limit: true/false` client option
183
346
  - [ ] Thor-based `printavo` CLI (`customers`, `orders`, `orders find <id>`)
184
347
 
185
- ### v0.10.0 — API Freeze
348
+ ### v0.99.0 — API Freeze
186
349
 
187
350
  - [ ] Community feedback integration
188
351
  - [ ] Deprecation notices for any renamed methods
@@ -25,10 +25,22 @@ module Printavo
25
25
  @graphql = GraphqlClient.new(connection)
26
26
  end
27
27
 
28
+ def account
29
+ Resources::Account.new(@graphql)
30
+ end
31
+
32
+ def contacts
33
+ Resources::Contacts.new(@graphql)
34
+ end
35
+
28
36
  def customers
29
37
  Resources::Customers.new(@graphql)
30
38
  end
31
39
 
40
+ def invoices
41
+ Resources::Invoices.new(@graphql)
42
+ end
43
+
32
44
  def statuses
33
45
  Resources::Statuses.new(@graphql)
34
46
  end
@@ -0,0 +1,14 @@
1
+ # lib/printavo/models/account.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Printavo
5
+ class Account < Models::Base
6
+ def id = self['id']
7
+ def company_name = self['companyName']
8
+ def company_email = self['companyEmail']
9
+ def phone = self['phone']
10
+ def website = self['website']
11
+ def logo_url = self['logoUrl']
12
+ def locale = self['locale']
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ # lib/printavo/models/contact.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Printavo
5
+ class Contact < Models::Base
6
+ def id = self['id']
7
+ def first_name = self['firstName']
8
+ def last_name = self['lastName']
9
+ def email = self['email']
10
+ def phone = self['phone']
11
+ def fax = self['fax']
12
+
13
+ def full_name
14
+ self['fullName'] || [first_name, last_name].compact.join(' ').strip
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ # lib/printavo/models/invoice.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Printavo
5
+ class Invoice < Models::Base
6
+ def id = self['id']
7
+ def nickname = self['nickname']
8
+ def total = self['total']
9
+ def amount_paid = self['amountPaid']
10
+ def amount_outstanding = self['amountOutstanding']
11
+ def paid_in_full? = self['paidInFull']
12
+ def invoice_at = self['invoiceAt']
13
+ def payment_due_at = self['paymentDueAt']
14
+
15
+ def status
16
+ dig('status', 'name')
17
+ end
18
+
19
+ def status_id
20
+ dig('status', 'id')
21
+ end
22
+
23
+ def status_color
24
+ dig('status', 'color')
25
+ end
26
+
27
+ def status_key
28
+ return nil if status.nil?
29
+
30
+ status.downcase.gsub(/\s+/, '_').to_sym
31
+ end
32
+
33
+ def status?(key)
34
+ status_key == key.to_sym
35
+ end
36
+
37
+ def contact
38
+ attrs = self['contact']
39
+ Contact.new(attrs) if attrs
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ # lib/printavo/resources/account.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Printavo
5
+ module Resources
6
+ # Singleton resource — Printavo exposes one account per API credential.
7
+ # Use +client.account.find+ (no ID argument required).
8
+ class Account < Base
9
+ FIND_QUERY = File.read(File.join(__dir__, '../graphql/account/find.graphql')).freeze
10
+
11
+ # Returns the account associated with the current API credentials.
12
+ #
13
+ # @return [Printavo::Account]
14
+ #
15
+ # @example
16
+ # account = client.account.find
17
+ # puts account.company_name # => "Texas Embroidery Ranch"
18
+ # puts account.locale # => "en-US"
19
+ def find
20
+ data = @graphql.query(FIND_QUERY)
21
+ Printavo::Account.new(data['account'])
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,57 @@
1
+ # lib/printavo/resources/contacts.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Printavo
5
+ module Resources
6
+ class Contacts < Base
7
+ FIND_QUERY = File.read(File.join(__dir__, '../graphql/contacts/find.graphql')).freeze
8
+ CREATE_MUTATION = File.read(File.join(__dir__, '../graphql/contacts/create.graphql')).freeze
9
+ UPDATE_MUTATION = File.read(File.join(__dir__, '../graphql/contacts/update.graphql')).freeze
10
+
11
+ # Finds a contact by ID.
12
+ #
13
+ # @param id [String, Integer]
14
+ # @return [Printavo::Contact]
15
+ #
16
+ # @example
17
+ # client.contacts.find("123")
18
+ def find(id)
19
+ data = @graphql.query(FIND_QUERY, variables: { id: id.to_s })
20
+ Printavo::Contact.new(data['contact'])
21
+ end
22
+
23
+ # Creates a new contact. Requires at minimum +email:+.
24
+ #
25
+ # @return [Printavo::Contact]
26
+ #
27
+ # @example
28
+ # client.contacts.create(first_name: "Jane", last_name: "Smith",
29
+ # email: "jane@example.com", phone: "555-867-5309")
30
+ def create(**input)
31
+ data = @graphql.mutate(CREATE_MUTATION, variables: { input: camelize_keys(input) })
32
+ Printavo::Contact.new(data['contactCreate'])
33
+ end
34
+
35
+ # Updates an existing contact by ID.
36
+ #
37
+ # @param id [String, Integer]
38
+ # @return [Printavo::Contact]
39
+ #
40
+ # @example
41
+ # client.contacts.update("123", phone: "555-999-0000")
42
+ def update(id, **input)
43
+ data = @graphql.mutate(UPDATE_MUTATION,
44
+ variables: { id: id.to_s, input: camelize_keys(input) })
45
+ Printavo::Contact.new(data['contactUpdate'])
46
+ end
47
+
48
+ private
49
+
50
+ def camelize_keys(hash)
51
+ hash.transform_keys do |key|
52
+ key.to_s.gsub(/_([a-z])/) { ::Regexp.last_match(1).upcase }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,61 @@
1
+ # lib/printavo/resources/invoices.rb
2
+ # frozen_string_literal: true
3
+
4
+ module Printavo
5
+ module Resources
6
+ class Invoices < Base
7
+ ALL_QUERY = File.read(File.join(__dir__, '../graphql/invoices/all.graphql')).freeze
8
+ FIND_QUERY = File.read(File.join(__dir__, '../graphql/invoices/find.graphql')).freeze
9
+ UPDATE_MUTATION = File.read(File.join(__dir__, '../graphql/invoices/update.graphql')).freeze
10
+
11
+ def all(first: 25, after: nil)
12
+ fetch_page(first: first, after: after).records
13
+ end
14
+
15
+ # Finds an invoice by ID.
16
+ #
17
+ # @param id [String, Integer]
18
+ # @return [Printavo::Invoice]
19
+ #
20
+ # @example
21
+ # client.invoices.find("456")
22
+ def find(id)
23
+ data = @graphql.query(FIND_QUERY, variables: { id: id.to_s })
24
+ Printavo::Invoice.new(data['invoice'])
25
+ end
26
+
27
+ # Updates an existing invoice by ID.
28
+ # Note: invoices are promoted from quotes — use +client.orders.create+ to originate.
29
+ #
30
+ # @param id [String, Integer]
31
+ # @return [Printavo::Invoice]
32
+ #
33
+ # @example
34
+ # client.invoices.update("456", nickname: "Final Invoice", payment_due_at: "2026-05-01")
35
+ def update(id, **input)
36
+ data = @graphql.mutate(UPDATE_MUTATION,
37
+ variables: { id: id.to_s, input: camelize_keys(input) })
38
+ Printavo::Invoice.new(data['invoiceUpdate'])
39
+ end
40
+
41
+ private
42
+
43
+ def fetch_page(first: 25, after: nil, **)
44
+ data = @graphql.query(ALL_QUERY, variables: { first: first, after: after })
45
+ nodes = data['invoices']['nodes'].map { |attrs| Printavo::Invoice.new(attrs) }
46
+ page_info = data['invoices']['pageInfo']
47
+ Printavo::Page.new(
48
+ records: nodes,
49
+ has_next_page: page_info['hasNextPage'],
50
+ end_cursor: page_info['endCursor']
51
+ )
52
+ end
53
+
54
+ def camelize_keys(hash)
55
+ hash.transform_keys do |key|
56
+ key.to_s.gsub(/_([a-z])/) { ::Regexp.last_match(1).upcase }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Printavo
5
- VERSION = '0.5.2'
5
+ VERSION = '0.6.0'
6
6
  end
data/lib/printavo.rb CHANGED
@@ -12,13 +12,19 @@ require_relative 'printavo/connection'
12
12
  require_relative 'printavo/graphql_client'
13
13
  require_relative 'printavo/page'
14
14
  require_relative 'printavo/models/base'
15
+ require_relative 'printavo/models/account'
16
+ require_relative 'printavo/models/contact'
15
17
  require_relative 'printavo/models/customer'
18
+ require_relative 'printavo/models/invoice'
16
19
  require_relative 'printavo/models/status'
17
20
  require_relative 'printavo/models/order'
18
21
  require_relative 'printavo/models/job'
19
22
  require_relative 'printavo/models/inquiry'
20
23
  require_relative 'printavo/resources/base'
24
+ require_relative 'printavo/resources/account'
25
+ require_relative 'printavo/resources/contacts'
21
26
  require_relative 'printavo/resources/customers'
27
+ require_relative 'printavo/resources/invoices'
22
28
  require_relative 'printavo/resources/statuses'
23
29
  require_relative 'printavo/resources/orders'
24
30
  require_relative 'printavo/resources/jobs'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: printavo-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stan Carver II
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-30 00:00:00.000000000 Z
11
+ date: 2026-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -272,16 +272,22 @@ files:
272
272
  - lib/printavo/connection.rb
273
273
  - lib/printavo/errors.rb
274
274
  - lib/printavo/graphql_client.rb
275
+ - lib/printavo/models/account.rb
275
276
  - lib/printavo/models/base.rb
277
+ - lib/printavo/models/contact.rb
276
278
  - lib/printavo/models/customer.rb
277
279
  - lib/printavo/models/inquiry.rb
280
+ - lib/printavo/models/invoice.rb
278
281
  - lib/printavo/models/job.rb
279
282
  - lib/printavo/models/order.rb
280
283
  - lib/printavo/models/status.rb
281
284
  - lib/printavo/page.rb
285
+ - lib/printavo/resources/account.rb
282
286
  - lib/printavo/resources/base.rb
287
+ - lib/printavo/resources/contacts.rb
283
288
  - lib/printavo/resources/customers.rb
284
289
  - lib/printavo/resources/inquiries.rb
290
+ - lib/printavo/resources/invoices.rb
285
291
  - lib/printavo/resources/jobs.rb
286
292
  - lib/printavo/resources/orders.rb
287
293
  - lib/printavo/resources/statuses.rb