@pimia/sdk 0.2.0 → 0.3.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.
package/dist/client.d.ts CHANGED
@@ -13,8 +13,42 @@
13
13
  * - **429**: respeta `Retry-After` y reintenta con espera acotada;
14
14
  * - **errores tipados**: MissingScopeError trae el scope exacto que falta.
15
15
  */
16
+ import type { components } from './api.js';
16
17
  import { OAuth, type OAuthConfig } from './oauth.js';
17
18
  import { type TokenStore } from './tokens.js';
19
+ type Schemas = components['schemas'];
20
+ /** Cliente tal y como lo devuelve la API. */
21
+ export type CustomerResource = Schemas['CustomerResource'];
22
+ /** Factura tal y como la devuelve la API. */
23
+ export type InvoiceResource = Schemas['InvoiceResource'];
24
+ /** Presupuesto tal y como lo devuelve la API. */
25
+ export type EstimateResource = Schemas['EstimateResource'];
26
+ /** Cuerpo de alta/edición de cliente. Incluye `customFields`. */
27
+ export type CustomerRequest = Schemas['CustomerRequest'];
28
+ /** Cuerpo de alta/edición de factura. Incluye `customFields`. */
29
+ export type InvoicesRequest = Schemas['InvoicesRequest'];
30
+ /** Cuerpo de alta/edición de presupuesto. Incluye `customFields`. */
31
+ export type EstimatesRequest = Schemas['EstimatesRequest'];
32
+ /**
33
+ * El sobre `{ data: … }` de Laravel para las escrituras que el spec **no
34
+ * tipa**.
35
+ *
36
+ * Hay 17 operaciones cuyo `200` sale del generador como objeto vacío, y entre
37
+ * ellas están `POST /invoices`, `PUT /invoices/{id}`, `PUT /customers/{id}` y
38
+ * `POST /estimates/{id}/convert-to-invoice`. Usar ahí el tipo generado sería
39
+ * peor que no tipar: `Record<string, never>` afirma que la respuesta **no
40
+ * tiene propiedades**, y el `data` real desaparecería del autocompletado.
41
+ *
42
+ * Así que el sobre se declara aquí y el recurso de dentro sí sale del spec.
43
+ * Está verificado contra los controladores del core, no supuesto: los cuatro
44
+ * devuelven `new XResource($modelo)` con el envoltorio `data` de Laravel
45
+ * activo. La causa del hueco es del generador —un `@return JsonResponse`
46
+ * heredado que le gana a la inferencia—, no del contrato; cuando se arregle
47
+ * en el core, estos tipos pasarán a salir de `Ok<…>` como los demás.
48
+ */
49
+ export interface ResourceEnvelope<T> {
50
+ data: T;
51
+ }
18
52
  export interface PimiaClientOptions extends OAuthConfig {
19
53
  tokens: TokenStore;
20
54
  /** Segundos de margen para refrescar antes de que caduque (default 60). */
@@ -94,21 +128,357 @@ export declare class PimiaClient {
94
128
  /** Cabeceras `X-RateLimit-*` de la última respuesta. */
95
129
  get rateLimit(): RateLimit;
96
130
  get invoices(): {
97
- list: (query?: RequestOptions["query"]) => Promise<unknown>;
98
- get: (id: number | string) => Promise<unknown>;
99
- create: (body: unknown, options?: WriteOptions) => Promise<unknown>;
100
- update: (id: number | string, body: unknown, options?: WriteOptions) => Promise<unknown>;
131
+ list: (query?: RequestOptions["query"]) => Promise<{
132
+ data: components["schemas"]["InvoiceResource"][];
133
+ meta: {
134
+ invoice_total_count: number;
135
+ };
136
+ }>;
137
+ get: (id: number | string) => Promise<{
138
+ data: components["schemas"]["InvoiceResource"] & Record<string, never>;
139
+ }>;
140
+ /**
141
+ * Devuelve `{ data: InvoiceResource }`. El tipo NO sale del spec: el
142
+ * `200` de `invoices.store` está vacío ahí (ver {@link ResourceEnvelope}).
143
+ */
144
+ create: (body: InvoicesRequest, options?: WriteOptions) => Promise<ResourceEnvelope<{
145
+ id: string;
146
+ invoice_date: string;
147
+ due_date: string;
148
+ invoice_number: string;
149
+ reference_number: string;
150
+ status: string;
151
+ paid_status: string;
152
+ tax_per_item: string;
153
+ tax_included: string;
154
+ discount_per_item: string;
155
+ notes: string;
156
+ discount_type: string;
157
+ discount: string;
158
+ discount_val: string;
159
+ sub_total: string;
160
+ total: string;
161
+ effective_total: string;
162
+ tax: string;
163
+ due_amount: string;
164
+ effective_due_amount: string;
165
+ sent: string;
166
+ viewed: string;
167
+ unique_hash: string;
168
+ template_name: string;
169
+ invoice_series_id: string;
170
+ customer_id: string;
171
+ payment_method_id: string;
172
+ recurring_invoice_id: string;
173
+ sequence_number: string;
174
+ exchange_rate: string;
175
+ base_discount_val: string;
176
+ base_sub_total: string;
177
+ base_total: string;
178
+ creator_id: string;
179
+ base_tax: string;
180
+ base_due_amount: string;
181
+ effective_base_total: string;
182
+ effective_base_due_amount: string;
183
+ credited_total: string;
184
+ credited_base_total: string;
185
+ currency_id: string;
186
+ formatted_created_at: string;
187
+ invoice_pdf_url: string;
188
+ formatted_invoice_date: string;
189
+ formatted_due_date: string;
190
+ allow_edit: string;
191
+ payment_module_enabled: string;
192
+ sales_tax_type: string;
193
+ sales_tax_address_type: string;
194
+ overdue: string;
195
+ effective_paid_status: string;
196
+ effective_overdue: string;
197
+ aeat_status: string;
198
+ qr_data: string;
199
+ hash: string;
200
+ aeat_csv: string;
201
+ is_credit_note: string | boolean;
202
+ rectified_invoice_id: string;
203
+ rectified_invoice_number?: string | null;
204
+ rectified_invoice?: {
205
+ id: string;
206
+ invoice_number: string;
207
+ tax_per_item: string;
208
+ tax_included: string;
209
+ sub_total: string;
210
+ discount_val: string;
211
+ tax: string;
212
+ total: string;
213
+ items: components["schemas"]["InvoiceItemResource"][];
214
+ taxes: components["schemas"]["TaxResource"][];
215
+ };
216
+ credit_notes_count: number;
217
+ items?: components["schemas"]["InvoiceItemResource"][];
218
+ payments?: components["schemas"]["PaymentResource"][];
219
+ customer?: components["schemas"]["CustomerResource"];
220
+ invoice_series?: components["schemas"]["InvoiceSeriesResource"];
221
+ payment_method?: components["schemas"]["PaymentMethodResource"];
222
+ creator?: components["schemas"]["UserResource"];
223
+ taxes: components["schemas"]["TaxResource"][];
224
+ fields?: components["schemas"]["CustomFieldValueResource"][];
225
+ company?: components["schemas"]["CompanyResource"];
226
+ currency?: components["schemas"]["CurrencyResource"];
227
+ }>>;
228
+ /** Mismo caso que `create`: el `200` de `invoices.update` no está tipado en el spec. */
229
+ update: (id: number | string, body: InvoicesRequest, options?: WriteOptions) => Promise<ResourceEnvelope<{
230
+ id: string;
231
+ invoice_date: string;
232
+ due_date: string;
233
+ invoice_number: string;
234
+ reference_number: string;
235
+ status: string;
236
+ paid_status: string;
237
+ tax_per_item: string;
238
+ tax_included: string;
239
+ discount_per_item: string;
240
+ notes: string;
241
+ discount_type: string;
242
+ discount: string;
243
+ discount_val: string;
244
+ sub_total: string;
245
+ total: string;
246
+ effective_total: string;
247
+ tax: string;
248
+ due_amount: string;
249
+ effective_due_amount: string;
250
+ sent: string;
251
+ viewed: string;
252
+ unique_hash: string;
253
+ template_name: string;
254
+ invoice_series_id: string;
255
+ customer_id: string;
256
+ payment_method_id: string;
257
+ recurring_invoice_id: string;
258
+ sequence_number: string;
259
+ exchange_rate: string;
260
+ base_discount_val: string;
261
+ base_sub_total: string;
262
+ base_total: string;
263
+ creator_id: string;
264
+ base_tax: string;
265
+ base_due_amount: string;
266
+ effective_base_total: string;
267
+ effective_base_due_amount: string;
268
+ credited_total: string;
269
+ credited_base_total: string;
270
+ currency_id: string;
271
+ formatted_created_at: string;
272
+ invoice_pdf_url: string;
273
+ formatted_invoice_date: string;
274
+ formatted_due_date: string;
275
+ allow_edit: string;
276
+ payment_module_enabled: string;
277
+ sales_tax_type: string;
278
+ sales_tax_address_type: string;
279
+ overdue: string;
280
+ effective_paid_status: string;
281
+ effective_overdue: string;
282
+ aeat_status: string;
283
+ qr_data: string;
284
+ hash: string;
285
+ aeat_csv: string;
286
+ is_credit_note: string | boolean;
287
+ rectified_invoice_id: string;
288
+ rectified_invoice_number?: string | null;
289
+ rectified_invoice?: {
290
+ id: string;
291
+ invoice_number: string;
292
+ tax_per_item: string;
293
+ tax_included: string;
294
+ sub_total: string;
295
+ discount_val: string;
296
+ tax: string;
297
+ total: string;
298
+ items: components["schemas"]["InvoiceItemResource"][];
299
+ taxes: components["schemas"]["TaxResource"][];
300
+ };
301
+ credit_notes_count: number;
302
+ items?: components["schemas"]["InvoiceItemResource"][];
303
+ payments?: components["schemas"]["PaymentResource"][];
304
+ customer?: components["schemas"]["CustomerResource"];
305
+ invoice_series?: components["schemas"]["InvoiceSeriesResource"];
306
+ payment_method?: components["schemas"]["PaymentMethodResource"];
307
+ creator?: components["schemas"]["UserResource"];
308
+ taxes: components["schemas"]["TaxResource"][];
309
+ fields?: components["schemas"]["CustomFieldValueResource"][];
310
+ company?: components["schemas"]["CompanyResource"];
311
+ currency?: components["schemas"]["CurrencyResource"];
312
+ }>>;
101
313
  };
102
314
  get customers(): {
103
- list: (query?: RequestOptions["query"]) => Promise<unknown>;
104
- get: (id: number | string) => Promise<unknown>;
105
- create: (body: unknown, options?: WriteOptions) => Promise<unknown>;
106
- update: (id: number | string, body: unknown, options?: WriteOptions) => Promise<unknown>;
315
+ list: (query?: RequestOptions["query"]) => Promise<{
316
+ data: components["schemas"]["CustomerResource"][];
317
+ meta: {
318
+ customer_total_count: number;
319
+ };
320
+ }>;
321
+ get: (id: number | string) => Promise<{
322
+ data: components["schemas"]["CustomerResource"];
323
+ }>;
324
+ create: (body: CustomerRequest, options?: WriteOptions) => Promise<{
325
+ data: components["schemas"]["CustomerResource"];
326
+ }>;
327
+ /** El `200` de `customers.update` no está tipado en el spec. */
328
+ update: (id: number | string, body: CustomerRequest, options?: WriteOptions) => Promise<ResourceEnvelope<{
329
+ id: string;
330
+ name: string;
331
+ email: string;
332
+ phone: string;
333
+ contact_name: string;
334
+ company_name: string;
335
+ website: string;
336
+ enable_portal: string;
337
+ password_added: boolean;
338
+ currency_id: string;
339
+ payment_method_id: string;
340
+ company_id: string;
341
+ facebook_id: string;
342
+ google_id: string;
343
+ github_id: string;
344
+ created_at: string;
345
+ formatted_created_at: string;
346
+ updated_at: string;
347
+ avatar: string;
348
+ due_amount: string;
349
+ base_due_amount: string;
350
+ prefix: string;
351
+ tax_id: string;
352
+ notes: string;
353
+ iban: string;
354
+ bic: string;
355
+ sepa_mandate_id: string;
356
+ sepa_mandate_date: string;
357
+ billing?: components["schemas"]["AddressResource"];
358
+ shipping?: components["schemas"]["AddressResource"];
359
+ fields?: components["schemas"]["CustomFieldValueResource"][];
360
+ company?: components["schemas"]["CompanyResource"];
361
+ currency?: components["schemas"]["CurrencyResource"];
362
+ payment_method?: components["schemas"]["PaymentMethodResource"];
363
+ }>>;
107
364
  };
108
365
  get estimates(): {
109
- list: (query?: RequestOptions["query"]) => Promise<unknown>;
110
- get: (id: number | string) => Promise<unknown>;
111
- create: (body: unknown, options?: WriteOptions) => Promise<unknown>;
366
+ list: (query?: RequestOptions["query"]) => Promise<{
367
+ data: components["schemas"]["EstimateResource"][];
368
+ meta: {
369
+ estimate_total_count: number;
370
+ };
371
+ }>;
372
+ get: (id: number | string) => Promise<{
373
+ data: components["schemas"]["EstimateResource"];
374
+ }>;
375
+ create: (body: EstimatesRequest, options?: WriteOptions) => Promise<{
376
+ data: components["schemas"]["EstimateResource"];
377
+ }>;
378
+ /**
379
+ * Convierte un presupuesto aceptado en factura.
380
+ *
381
+ * El helper existe porque es el cierre natural del bucle
382
+ * `estimate.accepted` → facturar, y sin él hay que ir por ruta cruda y
383
+ * adivinar la forma de la respuesta.
384
+ *
385
+ * Dos cosas que conviene saber y que el spec no dice:
386
+ *
387
+ * - **la factura nace BORRADOR y sin numerar**: `data.invoice_number` es
388
+ * `null` hasta que la publiques cambiando su estado. No es un fallo;
389
+ * - el id de la factura nueva está en `data.id`. El `r?.data?.id ?? r?.id`
390
+ * defensivo que se ve por ahí sobra: la segunda rama nunca ocurre.
391
+ *
392
+ * Manda `idempotencyKey` —una clave estable por presupuesto, del estilo
393
+ * `estimate:{id}:invoice`— y el reintento tras un timeout no te creará
394
+ * una segunda factura.
395
+ *
396
+ * Exige `estimates:write` **e** `invoices:write`.
397
+ */
398
+ convertToInvoice: (id: number | string, options?: WriteOptions) => Promise<ResourceEnvelope<{
399
+ id: string;
400
+ invoice_date: string;
401
+ due_date: string;
402
+ invoice_number: string;
403
+ reference_number: string;
404
+ status: string;
405
+ paid_status: string;
406
+ tax_per_item: string;
407
+ tax_included: string;
408
+ discount_per_item: string;
409
+ notes: string;
410
+ discount_type: string;
411
+ discount: string;
412
+ discount_val: string;
413
+ sub_total: string;
414
+ total: string;
415
+ effective_total: string;
416
+ tax: string;
417
+ due_amount: string;
418
+ effective_due_amount: string;
419
+ sent: string;
420
+ viewed: string;
421
+ unique_hash: string;
422
+ template_name: string;
423
+ invoice_series_id: string;
424
+ customer_id: string;
425
+ payment_method_id: string;
426
+ recurring_invoice_id: string;
427
+ sequence_number: string;
428
+ exchange_rate: string;
429
+ base_discount_val: string;
430
+ base_sub_total: string;
431
+ base_total: string;
432
+ creator_id: string;
433
+ base_tax: string;
434
+ base_due_amount: string;
435
+ effective_base_total: string;
436
+ effective_base_due_amount: string;
437
+ credited_total: string;
438
+ credited_base_total: string;
439
+ currency_id: string;
440
+ formatted_created_at: string;
441
+ invoice_pdf_url: string;
442
+ formatted_invoice_date: string;
443
+ formatted_due_date: string;
444
+ allow_edit: string;
445
+ payment_module_enabled: string;
446
+ sales_tax_type: string;
447
+ sales_tax_address_type: string;
448
+ overdue: string;
449
+ effective_paid_status: string;
450
+ effective_overdue: string;
451
+ aeat_status: string;
452
+ qr_data: string;
453
+ hash: string;
454
+ aeat_csv: string;
455
+ is_credit_note: string | boolean;
456
+ rectified_invoice_id: string;
457
+ rectified_invoice_number?: string | null;
458
+ rectified_invoice?: {
459
+ id: string;
460
+ invoice_number: string;
461
+ tax_per_item: string;
462
+ tax_included: string;
463
+ sub_total: string;
464
+ discount_val: string;
465
+ tax: string;
466
+ total: string;
467
+ items: components["schemas"]["InvoiceItemResource"][];
468
+ taxes: components["schemas"]["TaxResource"][];
469
+ };
470
+ credit_notes_count: number;
471
+ items?: components["schemas"]["InvoiceItemResource"][];
472
+ payments?: components["schemas"]["PaymentResource"][];
473
+ customer?: components["schemas"]["CustomerResource"];
474
+ invoice_series?: components["schemas"]["InvoiceSeriesResource"];
475
+ payment_method?: components["schemas"]["PaymentMethodResource"];
476
+ creator?: components["schemas"]["UserResource"];
477
+ taxes: components["schemas"]["TaxResource"][];
478
+ fields?: components["schemas"]["CustomFieldValueResource"][];
479
+ company?: components["schemas"]["CompanyResource"];
480
+ currency?: components["schemas"]["CurrencyResource"];
481
+ }>>;
112
482
  };
113
483
  get<T = unknown>(path: string, query?: RequestOptions['query']): Promise<T>;
114
484
  post<T = unknown>(path: string, body?: unknown, options?: WriteOptions): Promise<T>;
@@ -149,3 +519,4 @@ export declare class PimiaClient {
149
519
  private captureRateLimit;
150
520
  private retryDelay;
151
521
  }
522
+ export {};
package/dist/client.js CHANGED
@@ -46,7 +46,12 @@ export class PimiaClient {
46
46
  return {
47
47
  list: (query) => this.get('/invoices', query),
48
48
  get: (id) => this.get(`/invoices/${id}`),
49
+ /**
50
+ * Devuelve `{ data: InvoiceResource }`. El tipo NO sale del spec: el
51
+ * `200` de `invoices.store` está vacío ahí (ver {@link ResourceEnvelope}).
52
+ */
49
53
  create: (body, options) => this.post('/invoices', body, options),
54
+ /** Mismo caso que `create`: el `200` de `invoices.update` no está tipado en el spec. */
50
55
  update: (id, body, options) => this.put(`/invoices/${id}`, body, options),
51
56
  };
52
57
  }
@@ -55,6 +60,7 @@ export class PimiaClient {
55
60
  list: (query) => this.get('/customers', query),
56
61
  get: (id) => this.get(`/customers/${id}`),
57
62
  create: (body, options) => this.post('/customers', body, options),
63
+ /** El `200` de `customers.update` no está tipado en el spec. */
58
64
  update: (id, body, options) => this.put(`/customers/${id}`, body, options),
59
65
  };
60
66
  }
@@ -63,6 +69,27 @@ export class PimiaClient {
63
69
  list: (query) => this.get('/estimates', query),
64
70
  get: (id) => this.get(`/estimates/${id}`),
65
71
  create: (body, options) => this.post('/estimates', body, options),
72
+ /**
73
+ * Convierte un presupuesto aceptado en factura.
74
+ *
75
+ * El helper existe porque es el cierre natural del bucle
76
+ * `estimate.accepted` → facturar, y sin él hay que ir por ruta cruda y
77
+ * adivinar la forma de la respuesta.
78
+ *
79
+ * Dos cosas que conviene saber y que el spec no dice:
80
+ *
81
+ * - **la factura nace BORRADOR y sin numerar**: `data.invoice_number` es
82
+ * `null` hasta que la publiques cambiando su estado. No es un fallo;
83
+ * - el id de la factura nueva está en `data.id`. El `r?.data?.id ?? r?.id`
84
+ * defensivo que se ve por ahí sobra: la segunda rama nunca ocurre.
85
+ *
86
+ * Manda `idempotencyKey` —una clave estable por presupuesto, del estilo
87
+ * `estimate:{id}:invoice`— y el reintento tras un timeout no te creará
88
+ * una segunda factura.
89
+ *
90
+ * Exige `estimates:write` **e** `invoices:write`.
91
+ */
92
+ convertToInvoice: (id, options) => this.post(`/estimates/${id}/convert-to-invoice`, {}, options),
66
93
  };
67
94
  }
68
95
  get(path, query) {
package/dist/index.d.ts CHANGED
@@ -6,12 +6,14 @@
6
6
  * salen los tipos de `./api`.
7
7
  */
8
8
  export { PimiaClient } from './client.js';
9
- export type { PimiaClientOptions, RateLimit, RequestOptions, ResponseMeta, ResponseWithMeta, WriteOptions, } from './client.js';
9
+ export type { CustomerRequest, CustomerResource, EstimateResource, EstimatesRequest, InvoiceResource, InvoicesRequest, PimiaClientOptions, RateLimit, RequestOptions, ResourceEnvelope, ResponseMeta, ResponseWithMeta, WriteOptions, } from './client.js';
10
10
  export { OAuth, createPkceChallenge, createState } from './oauth.js';
11
11
  export type { AuthorizationServerMetadata, AuthorizeUrlOptions, OAuthConfig, PkceChallenge, } from './oauth.js';
12
12
  export { MemoryTokenStore, isExpired, tokenSetFromResponse } from './tokens.js';
13
13
  export type { TokenSet, TokenStore } from './tokens.js';
14
14
  export { ForbiddenError, MissingScopeError, NotAuthenticatedError, NotFoundError, OAuthError, PimiaApiError, PimiaError, RateLimitError, UnauthorizedError, ValidationError, } from './errors.js';
15
+ export { WEBHOOK_DEFAULT_TOLERANCE_SECONDS, WEBHOOK_EVENTS, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_VERSION, WebhookVerificationError, isWebhookEvent, signWebhook, verifyWebhook, } from './webhooks.js';
16
+ export type { ApprovalDecidedPayload, AppRevokedPayload, CustomerPayload, EstimateAcceptedPayload, InvoiceCreatedPayload, InvoicePaidPayload, InvoiceReceivedPayload, IsoDateTime, KnownWebhook, PimiaWebhook, SignWebhookOptions, UnknownWebhook, VerifyWebhookOptions, WebhookBodyInput, WebhookEvent, WebhookHeadersInput, WebhookPayloads, WebhookVerificationReason, } from './webhooks.js';
15
17
  /** Scopes granulares del catálogo de Pimia (paso 4). Pide siempre lo mínimo. */
16
18
  export declare const SCOPES: {
17
19
  readonly invoicesRead: "invoices:read";
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ export { PimiaClient } from './client.js';
9
9
  export { OAuth, createPkceChallenge, createState } from './oauth.js';
10
10
  export { MemoryTokenStore, isExpired, tokenSetFromResponse } from './tokens.js';
11
11
  export { ForbiddenError, MissingScopeError, NotAuthenticatedError, NotFoundError, OAuthError, PimiaApiError, PimiaError, RateLimitError, UnauthorizedError, ValidationError, } from './errors.js';
12
+ export { WEBHOOK_DEFAULT_TOLERANCE_SECONDS, WEBHOOK_EVENTS, WEBHOOK_HEADERS, WEBHOOK_SIGNATURE_VERSION, WebhookVerificationError, isWebhookEvent, signWebhook, verifyWebhook, } from './webhooks.js';
12
13
  /** Scopes granulares del catálogo de Pimia (paso 4). Pide siempre lo mínimo. */
13
14
  export const SCOPES = {
14
15
  invoicesRead: 'invoices:read',