From b22132b0f2f7d6a4040420f5b239c72f6ed23156 Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Thu, 23 Jul 2026 21:51:12 +0200 Subject: [PATCH] Add permissions and types for invoice recipients --- app/Service/PermissionStore.php | 12 ++ extensions/manifest.json | 4 +- resources/js/packages/api/src/index.ts | 2 + .../packages/api/src/openapi.json.client.ts | 202 +++++++++++++++--- 4 files changed, 186 insertions(+), 34 deletions(-) diff --git a/app/Service/PermissionStore.php b/app/Service/PermissionStore.php index 32072868..3d4af1b9 100644 --- a/app/Service/PermissionStore.php +++ b/app/Service/PermissionStore.php @@ -80,6 +80,10 @@ class PermissionStore 'invoices:update', 'invoices:download', 'invoices:delete', + 'invoice-recipients:view', + 'invoice-recipients:create', + 'invoice-recipients:update', + 'invoice-recipients:delete', 'invoice-settings:view', 'invoice-settings:update', ], @@ -147,6 +151,10 @@ class PermissionStore 'invoices:update', 'invoices:download', 'invoices:delete', + 'invoice-recipients:view', + 'invoice-recipients:create', + 'invoice-recipients:update', + 'invoice-recipients:delete', 'invoice-settings:view', 'invoice-settings:update', ], @@ -203,6 +211,10 @@ class PermissionStore 'invoices:update', 'invoices:download', 'invoices:delete', + 'invoice-recipients:view', + 'invoice-recipients:create', + 'invoice-recipients:update', + 'invoice-recipients:delete', 'invoice-settings:view', 'invoice-settings:update', ], diff --git a/extensions/manifest.json b/extensions/manifest.json index 2a02ddc3..50507b03 100644 --- a/extensions/manifest.json +++ b/extensions/manifest.json @@ -1,7 +1,7 @@ { "Billing": { "repository": "solidtime-io/extension-billing", - "ref": "v0.0.3" + "ref": "v0.0.4" }, "Services": { "repository": "solidtime-io/extension-services", @@ -9,6 +9,6 @@ }, "Invoicing": { "repository": "solidtime-io/extension-invoicing", - "ref": "v0.0.1" + "ref": "feature/recipients" } } diff --git a/resources/js/packages/api/src/index.ts b/resources/js/packages/api/src/index.ts index ce846c92..f033f36f 100644 --- a/resources/js/packages/api/src/index.ts +++ b/resources/js/packages/api/src/index.ts @@ -117,6 +117,8 @@ export type DetailedInvoiceResponse = ZodiosResponseByAlias['data'][0]; +export type InvoiceRecipient = ZodiosResponseByAlias['data'][0]; +export type InvoiceRecipientBody = ZodiosBodyByAlias; export type UpdateInvoiceSettings = ZodiosBodyByAlias; diff --git a/resources/js/packages/api/src/openapi.json.client.ts b/resources/js/packages/api/src/openapi.json.client.ts index 823b7eb6..91baa238 100644 --- a/resources/js/packages/api/src/openapi.json.client.ts +++ b/resources/js/packages/api/src/openapi.json.client.ts @@ -45,14 +45,54 @@ const InvitationResource = z const InvitationStoreRequest = z .object({ email: z.string().email(), role: z.enum(['admin', 'manager', 'employee']) }) .passthrough(); +const InvoiceRecipientResource = z + .object({ + id: z.string(), + organization_id: z.string(), + name: z.string(), + vatin: z.union([z.string(), z.null()]), + address_line_1: z.union([z.string(), z.null()]), + address_line_2: z.union([z.string(), z.null()]), + address_line_3: z.union([z.string(), z.null()]), + address_post_code: z.union([z.string(), z.null()]), + address_city: z.union([z.string(), z.null()]), + address_country: z.union([z.string(), z.null()]), + phone: z.union([z.string(), z.null()]), + email: z.union([z.string(), z.null()]), + is_archived: z.boolean(), + archived_at: z.union([z.string(), z.null()]), + invoices_count: z.number().int(), + has_non_draft_invoices: z.boolean(), + created_at: z.union([z.string(), z.null()]), + updated_at: z.union([z.string(), z.null()]), + }) + .passthrough(); +const InvoiceRecipientCollection = z.array(InvoiceRecipientResource); +const InvoiceRecipientRequest = z + .object({ + name: z.string(), + vatin: z.union([z.string(), z.null()]).optional(), + address_line_1: z.union([z.string(), z.null()]).optional(), + address_line_2: z.union([z.string(), z.null()]).optional(), + address_line_3: z.union([z.string(), z.null()]).optional(), + address_post_code: z.union([z.string(), z.null()]).optional(), + address_city: z.union([z.string(), z.null()]).optional(), + address_country: z.union([z.string(), z.null()]).optional(), + phone: z.union([z.string(), z.null()]).optional(), + email: z.union([z.string(), z.null()]).optional(), + is_archived: z.boolean().optional(), + }) + .passthrough(); const InvoiceResource = z .object({ id: z.string(), organization_id: z.string(), + invoice_recipient_id: z.string(), reference: z.string(), seller_name: z.string(), - buyer_name: z.string(), + recipient: z.string(), status: z.string(), + status_label: z.string(), date: z.string(), due_at: z.string(), paid_date: z.string(), @@ -76,16 +116,7 @@ const InvoiceStoreRequest = z seller_address_country: z.union([z.string(), z.null()]).optional(), seller_phone: z.union([z.string(), z.null()]).optional(), seller_email: z.union([z.string(), z.null()]).optional(), - buyer_name: z.string(), - buyer_vatin: z.union([z.string(), z.null()]).optional(), - buyer_address_line_1: z.union([z.string(), z.null()]).optional(), - buyer_address_line_2: z.union([z.string(), z.null()]).optional(), - buyer_address_line_3: z.union([z.string(), z.null()]).optional(), - buyer_address_post_code: z.union([z.string(), z.null()]).optional(), - buyer_address_city: z.union([z.string(), z.null()]).optional(), - buyer_address_country: z.union([z.string(), z.null()]).optional(), - buyer_phone: z.union([z.string(), z.null()]).optional(), - buyer_email: z.union([z.string(), z.null()]).optional(), + invoice_recipient_id: z.string(), date: z.string(), billing_period_start: z.union([z.string(), z.null()]).optional(), billing_period_end: z.union([z.string(), z.null()]).optional(), @@ -130,6 +161,7 @@ const DetailedInvoiceResource = z .object({ id: z.string(), organization_id: z.string(), + invoice_recipient_id: z.string(), reference: z.string(), seller_name: z.string(), seller_vatin: z.string(), @@ -141,16 +173,7 @@ const DetailedInvoiceResource = z seller_address_country: z.string(), seller_phone: z.string(), seller_email: z.string(), - buyer_name: z.string(), - buyer_vatin: z.string(), - buyer_address_line_1: z.string(), - buyer_address_line_2: z.string(), - buyer_address_line_3: z.string(), - buyer_address_post_code: z.string(), - buyer_address_city: z.string(), - buyer_address_country: z.string(), - buyer_phone: z.string(), - buyer_email: z.string(), + recipient: InvoiceRecipientResource, paid_date: z.string(), due_at: z.string(), discount_type: z.string(), @@ -171,7 +194,7 @@ const DetailedInvoiceResource = z entries: z.array(InvoiceEntryResource), }) .passthrough(); -const InvoiceStatus = z.enum(['draft', 'sent', 'cancelled']); +const InvoiceStatus = z.enum(['draft', 'sent', 'paid', 'cancelled']); const InvoiceUpdateRequest = z .object({ status: InvoiceStatus, @@ -187,16 +210,7 @@ const InvoiceUpdateRequest = z seller_address_country: z.union([z.string(), z.null()]), seller_phone: z.union([z.string(), z.null()]), seller_email: z.union([z.string(), z.null()]), - buyer_name: z.string(), - buyer_vatin: z.union([z.string(), z.null()]), - buyer_address_line_1: z.union([z.string(), z.null()]), - buyer_address_line_2: z.union([z.string(), z.null()]), - buyer_address_line_3: z.union([z.string(), z.null()]), - buyer_address_post_code: z.union([z.string(), z.null()]), - buyer_address_city: z.union([z.string(), z.null()]), - buyer_address_country: z.union([z.string(), z.null()]), - buyer_phone: z.union([z.string(), z.null()]), - buyer_email: z.union([z.string(), z.null()]), + invoice_recipient_id: z.string(), date: z.string(), billing_period_start: z.union([z.string(), z.null()]), billing_period_end: z.union([z.string(), z.null()]), @@ -1885,6 +1899,125 @@ const endpoints = makeApi([ }, ], }, + { + method: 'get', + path: '/v1/organizations/:organization/invoice-recipients', + alias: 'getInvoiceRecipients', + requestFormat: 'json', + parameters: [ + { + name: 'organization', + type: 'Path', + schema: z.string(), + }, + ], + response: z.object({ data: InvoiceRecipientCollection }).passthrough(), + }, + { + method: 'post', + path: '/v1/organizations/:organization/invoice-recipients', + alias: 'createInvoiceRecipient', + requestFormat: 'json', + parameters: [ + { + name: 'body', + type: 'Body', + schema: InvoiceRecipientRequest, + }, + { + name: 'organization', + type: 'Path', + schema: z.string(), + }, + ], + response: z.object({ data: InvoiceRecipientResource }).passthrough(), + }, + { + method: 'get', + path: '/v1/organizations/:organization/invoice-recipients/:invoiceRecipient', + alias: 'getInvoiceRecipient', + requestFormat: 'json', + parameters: [ + { + name: 'organization', + type: 'Path', + schema: z.string(), + }, + { + name: 'invoiceRecipient', + type: 'Path', + schema: z.string(), + }, + ], + response: z.object({ data: InvoiceRecipientResource }).passthrough(), + }, + { + method: 'put', + path: '/v1/organizations/:organization/invoice-recipients/:invoiceRecipient', + alias: 'updateInvoiceRecipient', + requestFormat: 'json', + parameters: [ + { + name: 'body', + type: 'Body', + schema: InvoiceRecipientRequest, + }, + { + name: 'organization', + type: 'Path', + schema: z.string(), + }, + { + name: 'invoiceRecipient', + type: 'Path', + schema: z.string(), + }, + ], + response: z.object({ data: InvoiceRecipientResource }).passthrough(), + }, + { + method: 'post', + path: '/v1/organizations/:organization/invoice-recipients/:invoiceRecipient/duplicate', + alias: 'duplicateInvoiceRecipient', + requestFormat: 'json', + parameters: [ + { + name: 'body', + type: 'Body', + schema: InvoiceRecipientRequest, + }, + { + name: 'organization', + type: 'Path', + schema: z.string(), + }, + { + name: 'invoiceRecipient', + type: 'Path', + schema: z.string(), + }, + ], + response: z.object({ data: InvoiceRecipientResource }).passthrough(), + }, + { + method: 'delete', + path: '/v1/organizations/:organization/invoice-recipients/:invoiceRecipient', + alias: 'deleteInvoiceRecipient', + requestFormat: 'json', + parameters: [ + { + name: 'organization', + type: 'Path', + schema: z.string(), + }, + { + name: 'invoiceRecipient', + type: 'Path', + schema: z.string(), + }, + ], + response: z.void(), + }, { method: 'get', path: '/v1/organizations/:organization/invoices', @@ -1901,6 +2034,11 @@ const endpoints = makeApi([ type: 'Query', schema: z.number().int().gte(1).lte(2147483647).optional(), }, + { + name: 'status', + type: 'Query', + schema: InvoiceStatus.optional(), + }, ], response: z.object({ data: InvoiceCollection }).passthrough(), errors: [