diff --git a/Client/SdkRequestExtensions.cs b/Client/SdkRequestExtensions.cs new file mode 100644 index 0000000..279e350 --- /dev/null +++ b/Client/SdkRequestExtensions.cs @@ -0,0 +1,32 @@ +using IdokladSdk.Clients; +using IdokladSdk.Requests.Core; + +namespace Idoklad.Client; + +/// +/// Generic helpers over the SDK's list/detail request builders so the read endpoints of the many +/// agendas can be expressed in a single line. The concrete payload type is inferred by the SDK and +/// returned boxed (full data is preserved at runtime; only the Swagger schema is generic). +/// +public static class SdkRequestExtensions +{ + public static async Task ToPageAsync( + this BaseList list, + int page, + int pageSize, + CancellationToken ct) + where TList : BaseList + where TClient : BaseClient + where TFilter : new() + where TSort : new() + where TGetModel : new() + => (await list.Page(page).PageSize(pageSize).GetAsync(ct)).Unwrap(); + + public static async Task ToDetailAsync( + this BaseDetail detail, + CancellationToken ct) + where TDetail : BaseDetail + where TClient : BaseClient + where TGetModel : new() + => (await detail.GetAsync(ct)).Unwrap(); +} diff --git a/Controllers/CatalogController.cs b/Controllers/CatalogController.cs new file mode 100644 index 0000000..1e0bb82 --- /dev/null +++ b/Controllers/CatalogController.cs @@ -0,0 +1,101 @@ +using IdokladSdk.Models.PriceListItem; +using IdokladSdk.Models.StockMovement; +using IdokladSdk.Models.Tag; +using Microsoft.AspNetCore.Mvc; +using Idoklad.Services; + +namespace Idoklad.Controllers; + +/// +/// Price list items, stock movements and tags. Requires iDoklad credentials (headers or environment defaults). +/// +[ApiController] +[Produces("application/json")] +[Tags("Catalog")] +public sealed class CatalogController : ControllerBase +{ + private readonly CatalogService _service; + + public CatalogController(CatalogService service) => _service = service; + + // ---- Price list items ---- + + /// List price list items (paged). + [HttpGet("price-list-items")] + public async Task ListPriceListItems(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListPriceListItemsAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new price list item. + [HttpGet("price-list-items/default")] + public async Task PriceListItemDefault(CancellationToken ct) + => Ok(await _service.PriceListItemDefaultAsync(ct)); + + /// Get a price list item detail by id. + [HttpGet("price-list-items/{id:int}")] + public async Task PriceListItemDetail(int id, CancellationToken ct) + => Ok(await _service.PriceListItemDetailAsync(id, ct)); + + /// Create a new price list item. + [HttpPost("price-list-items")] + public async Task CreatePriceListItem([FromBody] PriceListItemPostModel model, CancellationToken ct) + => Ok(await _service.CreatePriceListItemAsync(model, ct)); + + /// Update an existing price list item (the model id identifies the item). + [HttpPatch("price-list-items")] + public async Task UpdatePriceListItem([FromBody] PriceListItemPatchModel model, CancellationToken ct) + => Ok(await _service.UpdatePriceListItemAsync(model, ct)); + + // ---- Stock movements ---- + + /// List stock movements (paged). + [HttpGet("stock-movements")] + public async Task ListStockMovements(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListStockMovementsAsync(page, pageSize, ct)); + + /// Get a pre-filled default stock movement model for the given price list item id. + [HttpGet("stock-movements/default/{priceListItemId:int}")] + public async Task StockMovementDefault(int priceListItemId, CancellationToken ct) + => Ok(await _service.StockMovementDefaultAsync(priceListItemId, ct)); + + /// Get a stock movement detail by id. + [HttpGet("stock-movements/{id:int}")] + public async Task StockMovementDetail(int id, CancellationToken ct) + => Ok(await _service.StockMovementDetailAsync(id, ct)); + + /// Create a new stock movement. + [HttpPost("stock-movements")] + public async Task CreateStockMovement([FromBody] StockMovementPostModel model, CancellationToken ct) + => Ok(await _service.CreateStockMovementAsync(model, ct)); + + /// Update an existing stock movement (the model id identifies the movement). + [HttpPatch("stock-movements")] + public async Task UpdateStockMovement([FromBody] StockMovementPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateStockMovementAsync(model, ct)); + + /// Delete a stock movement by id. + [HttpDelete("stock-movements/{id:int}")] + public async Task DeleteStockMovement(int id, CancellationToken ct) + => Ok(await _service.DeleteStockMovementAsync(id, ct)); + + // ---- Tags ---- + + /// List tags (paged). + [HttpGet("tags")] + public async Task ListTags(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListTagsAsync(page, pageSize, ct)); + + /// Create a new tag. + [HttpPost("tags")] + public async Task CreateTag([FromBody] TagPostModel model, CancellationToken ct) + => Ok(await _service.CreateTagAsync(model, ct)); + + /// Update an existing tag (the model id identifies the tag). + [HttpPatch("tags")] + public async Task UpdateTag([FromBody] TagPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateTagAsync(model, ct)); + + /// Delete a tag by id. + [HttpDelete("tags/{id:int}")] + public async Task DeleteTag(int id, CancellationToken ct) + => Ok(await _service.DeleteTagAsync(id, ct)); +} diff --git a/Controllers/CodeListsController.cs b/Controllers/CodeListsController.cs new file mode 100644 index 0000000..33ca7ba --- /dev/null +++ b/Controllers/CodeListsController.cs @@ -0,0 +1,118 @@ +using Microsoft.AspNetCore.Mvc; +using Idoklad.Services; + +namespace Idoklad.Controllers; + +/// +/// Read-only iDoklad code lists / registers. Requires iDoklad credentials (headers or environment +/// defaults). +/// +[ApiController] +[Produces("application/json")] +[Tags("CodeLists")] +public sealed class CodeListsController : ControllerBase +{ + private readonly CodeListsService _service; + + public CodeListsController(CodeListsService service) => _service = service; + + /// List banks (paged). + [HttpGet("code-lists/banks")] + public async Task Banks(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListBanksAsync(page, pageSize, ct)); + + /// Get a bank detail by id. + [HttpGet("code-lists/banks/{id:int}")] + public async Task BankDetail(int id, CancellationToken ct) + => Ok(await _service.BankDetailAsync(id, ct)); + + /// List countries (paged). + [HttpGet("code-lists/countries")] + public async Task Countries(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListCountriesAsync(page, pageSize, ct)); + + /// Get a country detail by id. + [HttpGet("code-lists/countries/{id:int}")] + public async Task CountryDetail(int id, CancellationToken ct) + => Ok(await _service.CountryDetailAsync(id, ct)); + + /// List currencies (paged). + [HttpGet("code-lists/currencies")] + public async Task Currencies(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListCurrenciesAsync(page, pageSize, ct)); + + /// Get a currency detail by id. + [HttpGet("code-lists/currencies/{id:int}")] + public async Task CurrencyDetail(int id, CancellationToken ct) + => Ok(await _service.CurrencyDetailAsync(id, ct)); + + /// List constant symbols (paged). + [HttpGet("code-lists/constant-symbols")] + public async Task ConstantSymbols(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListConstantSymbolsAsync(page, pageSize, ct)); + + /// Get a constant symbol detail by id. + [HttpGet("code-lists/constant-symbols/{id:int}")] + public async Task ConstantSymbolDetail(int id, CancellationToken ct) + => Ok(await _service.ConstantSymbolDetailAsync(id, ct)); + + /// List exchange rates (paged). + [HttpGet("code-lists/exchange-rates")] + public async Task ExchangeRates(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListExchangeRatesAsync(page, pageSize, ct)); + + /// Get an exchange rate detail by id. + [HttpGet("code-lists/exchange-rates/{id:int}")] + public async Task ExchangeRateDetail(int id, CancellationToken ct) + => Ok(await _service.ExchangeRateDetailAsync(id, ct)); + + /// List payment options (paged). + [HttpGet("code-lists/payment-options")] + public async Task PaymentOptions(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListPaymentOptionsAsync(page, pageSize, ct)); + + /// Get a payment option detail by id. + [HttpGet("code-lists/payment-options/{id:int}")] + public async Task PaymentOptionDetail(int id, CancellationToken ct) + => Ok(await _service.PaymentOptionDetailAsync(id, ct)); + + /// List VAT codes (paged). + [HttpGet("code-lists/vat-codes")] + public async Task VatCodes(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListVatCodesAsync(page, pageSize, ct)); + + /// Get a VAT code detail by id. + [HttpGet("code-lists/vat-codes/{id:int}")] + public async Task VatCodeDetail(int id, CancellationToken ct) + => Ok(await _service.VatCodeDetailAsync(id, ct)); + + /// List VAT reverse-charge codes (paged). + [HttpGet("code-lists/vat-reverse-charge-codes")] + public async Task VatReverseChargeCodes(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListVatReverseChargeCodesAsync(page, pageSize, ct)); + + /// Get a VAT reverse-charge code detail by id. + [HttpGet("code-lists/vat-reverse-charge-codes/{id:int}")] + public async Task VatReverseChargeCodeDetail(int id, CancellationToken ct) + => Ok(await _service.VatReverseChargeCodeDetailAsync(id, ct)); + + /// List sales offices (paged). + [HttpGet("code-lists/sales-offices")] + public async Task SalesOffices(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListSalesOfficesAsync(page, pageSize, ct)); + + /// Get a sales office detail by id. + [HttpGet("code-lists/sales-offices/{id:int}")] + public async Task SalesOfficeDetail(int id, CancellationToken ct) + => Ok(await _service.SalesOfficeDetailAsync(id, ct)); + + /// List sales POS equipment (paged). + [HttpGet("code-lists/sales-pos-equipment")] + public async Task SalesPosEquipment(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListSalesPosEquipmentAsync(page, pageSize, ct)); + + /// Get a sales POS equipment detail by id. + [HttpGet("code-lists/sales-pos-equipment/{id:int}")] + public async Task SalesPosEquipmentDetail(int id, CancellationToken ct) + => Ok(await _service.SalesPosEquipmentDetailAsync(id, ct)); +} diff --git a/Controllers/IntegrationController.cs b/Controllers/IntegrationController.cs new file mode 100644 index 0000000..c5dcca4 --- /dev/null +++ b/Controllers/IntegrationController.cs @@ -0,0 +1,122 @@ +using IdokladSdk.Enums; +using IdokladSdk.Models.Attachment; +using IdokladSdk.Models.Webhook; +using Microsoft.AspNetCore.Mvc; +using Idoklad.Services; + +namespace Idoklad.Controllers; + +/// +/// Integration / utility agendas: webhooks, notifications, change log, registered sales, +/// unpaired documents, attachments and system code books. Requires iDoklad credentials. +/// +[ApiController] +[Produces("application/json")] +[Tags("Integration")] +public sealed class IntegrationController : ControllerBase +{ + private readonly IntegrationService _service; + + public IntegrationController(IntegrationService service) => _service = service; + + // ---- Webhooks ---- + + /// List configured webhooks (paged). + [HttpGet("webhooks")] + public async Task ListWebhooks(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListWebhooksAsync(page, pageSize, ct)); + + /// Get a webhook detail by id. + [HttpGet("webhooks/{id:int}")] + public async Task WebhookDetail(int id, CancellationToken ct) + => Ok(await _service.WebhookDetailAsync(id, ct)); + + /// Create (register) a new webhook subscription. + [HttpPost("webhooks")] + public async Task CreateWebhook([FromBody] AgendaWebhookSettingsPostModel model, CancellationToken ct) + => Ok(await _service.CreateWebhookAsync(model, ct)); + + /// Delete a webhook by id. + [HttpDelete("webhooks/{id:int}")] + public async Task DeleteWebhook(int id, CancellationToken ct) + => Ok(await _service.DeleteWebhookAsync(id, ct)); + + // ---- Notifications ---- + + /// List notifications (paged). + [HttpGet("notifications")] + public async Task ListNotifications(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListNotificationsAsync(page, pageSize, ct)); + + /// Delete (dismiss) a notification by id. + [HttpDelete("notifications/{id:int}")] + public async Task DeleteNotification(int id, CancellationToken ct) + => Ok(await _service.DeleteNotificationAsync(id, ct)); + + // ---- Change log ---- + + /// List the agenda change log (paged). + [HttpGet("logs")] + public async Task ListLog(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListLogAsync(page, pageSize, ct)); + + // ---- Registered sales (EET) ---- + + /// List registered sales / EET records (paged). + [HttpGet("registered-sales")] + public async Task ListRegisteredSales(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListRegisteredSalesAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new registered sale. + [HttpGet("registered-sales/default")] + public async Task RegisteredSaleDefault(CancellationToken ct) + => Ok(await _service.RegisteredSaleDefaultAsync(ct)); + + // ---- Unpaired documents ---- + + /// List unpaired documents for a movement type and pairing document type. + [HttpGet("unpaired-documents/{movementType}/{pairingDocumentType}")] + public async Task ListUnpairedDocuments( + MovementType movementType, PairingDocumentType pairingDocumentType, int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListUnpairedDocumentsAsync(movementType, pairingDocumentType, page, pageSize, ct)); + + // ---- Attachments ---- + + /// Get a single attachment by its id. + [HttpGet("attachments/{attachmentId:int}")] + public async Task GetAttachment(int attachmentId, bool compressed = false, CancellationToken ct = default) + => Ok(await _service.GetAttachmentAsync(attachmentId, compressed, ct)); + + /// Get all attachments of a document. + [HttpGet("attachments/document/{documentId:int}/{documentType}")] + public async Task GetDocumentAttachments( + int documentId, AttachmentDocumentType documentType, bool compressed = false, CancellationToken ct = default) + => Ok(await _service.GetDocumentAttachmentsAsync(documentId, documentType, compressed, ct)); + + /// Upload an attachment for a document. + [HttpPost("attachments")] + public async Task UploadAttachment([FromBody] AttachmentUploadModel model, CancellationToken ct) + => Ok(await _service.UploadAttachmentAsync(model, ct)); + + /// Delete a single attachment by its id. + [HttpDelete("attachments/{attachmentId:int}")] + public async Task DeleteAttachment(int attachmentId, CancellationToken ct) + => Ok(await _service.DeleteAttachmentAsync(attachmentId, ct)); + + /// Delete all attachments of a document. + [HttpDelete("attachments/document/{documentId:int}/{documentType}")] + public async Task DeleteDocumentAttachments(int documentId, AttachmentDocumentType documentType, CancellationToken ct) + => Ok(await _service.DeleteDocumentAttachmentsAsync(documentId, documentType, ct)); + + // ---- System code books ---- + + /// Get the iDoklad system code books. + [HttpGet("system/code-books")] + public async Task CodeBooks(CancellationToken ct) + => Ok(await _service.CodeBooksAsync(ct)); + + /// Get changes to the code books since the given timestamp. + [HttpGet("system/code-books/changes")] + public async Task CodeBooksChanges([FromQuery] DateTime lastCheck, CancellationToken ct = default) + => Ok(await _service.CodeBooksChangesAsync(lastCheck, ct)); +} diff --git a/Controllers/PaymentsController.cs b/Controllers/PaymentsController.cs new file mode 100644 index 0000000..5242ca9 --- /dev/null +++ b/Controllers/PaymentsController.cs @@ -0,0 +1,112 @@ +using IdokladSdk.Models.BankStatement.Patch; +using IdokladSdk.Models.BankStatement.Post; +using IdokladSdk.Models.IssuedDocumentPayment; +using IdokladSdk.Models.ReceivedDocumentPayments; +using Microsoft.AspNetCore.Mvc; +using Idoklad.Services; + +namespace Idoklad.Controllers; + +/// +/// Bank statements and document payments. Requires iDoklad credentials (headers or environment defaults). +/// +[ApiController] +[Produces("application/json")] +[Tags("Payments")] +public sealed class PaymentsController : ControllerBase +{ + private readonly PaymentsService _service; + + public PaymentsController(PaymentsService service) => _service = service; + + // ---- Bank statements ---- + + /// List bank statements (paged). + [HttpGet("bank-statements")] + public async Task ListBankStatements(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListBankStatementsAsync(page, pageSize, ct)); + + /// Get a bank statement detail by id. + [HttpGet("bank-statements/{id:int}")] + public async Task BankStatementDetail(int id, CancellationToken ct) + => Ok(await _service.BankStatementDetailAsync(id, ct)); + + /// Create a new bank statement. + [HttpPost("bank-statements")] + public async Task CreateBankStatement([FromBody] BankStatementPostModel model, CancellationToken ct) + => Ok(await _service.CreateBankStatementAsync(model, ct)); + + /// Update an existing bank statement (the model id identifies the statement). + [HttpPatch("bank-statements")] + public async Task UpdateBankStatement([FromBody] BankStatementPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateBankStatementAsync(model, ct)); + + /// Delete a bank statement by id. + [HttpDelete("bank-statements/{id:int}")] + public async Task DeleteBankStatement(int id, CancellationToken ct) + => Ok(await _service.DeleteBankStatementAsync(id, ct)); + + // ---- Issued document payments ---- + + /// List issued document payments (paged). + [HttpGet("issued-payments")] + public async Task ListIssuedPayments(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListIssuedPaymentsAsync(page, pageSize, ct)); + + /// Get a pre-filled default issued payment model for the given issued invoice id. + [HttpGet("issued-payments/default/{invoiceId:int}")] + public async Task IssuedPaymentDefault(int invoiceId, CancellationToken ct) + => Ok(await _service.IssuedPaymentDefaultAsync(invoiceId, ct)); + + /// Get an issued document payment detail by id. + [HttpGet("issued-payments/{id:int}")] + public async Task IssuedPaymentDetail(int id, CancellationToken ct) + => Ok(await _service.IssuedPaymentDetailAsync(id, ct)); + + /// Create (register) a new payment for an issued document. + [HttpPost("issued-payments")] + public async Task CreateIssuedPayment([FromBody] IssuedDocumentPaymentPostModel model, CancellationToken ct) + => Ok(await _service.CreateIssuedPaymentAsync(model, ct)); + + /// Delete an issued document payment by id. + [HttpDelete("issued-payments/{id:int}")] + public async Task DeleteIssuedPayment(int id, CancellationToken ct) + => Ok(await _service.DeleteIssuedPaymentAsync(id, ct)); + + /// Fully unpay an issued invoice (removes all its payments). + [HttpPost("issued-payments/fully-unpay/{invoiceId:int}")] + public async Task FullyUnpayIssued(int invoiceId, CancellationToken ct) + => Ok(await _service.FullyUnpayIssuedAsync(invoiceId, ct)); + + // ---- Received document payments ---- + + /// List received document payments (paged). + [HttpGet("received-payments")] + public async Task ListReceivedPayments(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListReceivedPaymentsAsync(page, pageSize, ct)); + + /// Get a pre-filled default received payment model for the given received invoice id. + [HttpGet("received-payments/default/{invoiceId:int}")] + public async Task ReceivedPaymentDefault(int invoiceId, CancellationToken ct) + => Ok(await _service.ReceivedPaymentDefaultAsync(invoiceId, ct)); + + /// Get a received document payment detail by id. + [HttpGet("received-payments/{id:int}")] + public async Task ReceivedPaymentDetail(int id, CancellationToken ct) + => Ok(await _service.ReceivedPaymentDetailAsync(id, ct)); + + /// Create (register) a new payment for a received document. + [HttpPost("received-payments")] + public async Task CreateReceivedPayment([FromBody] ReceivedDocumentPaymentPostModel model, CancellationToken ct) + => Ok(await _service.CreateReceivedPaymentAsync(model, ct)); + + /// Delete a received document payment by id. + [HttpDelete("received-payments/{id:int}")] + public async Task DeleteReceivedPayment(int id, CancellationToken ct) + => Ok(await _service.DeleteReceivedPaymentAsync(id, ct)); + + /// Fully unpay a received invoice (removes all its payments). + [HttpPost("received-payments/fully-unpay/{invoiceId:int}")] + public async Task FullyUnpayReceived(int invoiceId, CancellationToken ct) + => Ok(await _service.FullyUnpayReceivedAsync(invoiceId, ct)); +} diff --git a/Controllers/PurchaseCashController.cs b/Controllers/PurchaseCashController.cs new file mode 100644 index 0000000..fd273fd --- /dev/null +++ b/Controllers/PurchaseCashController.cs @@ -0,0 +1,107 @@ +using IdokladSdk.Models.CashRegister; +using IdokladSdk.Models.CashVoucher; +using IdokladSdk.Models.ReceivedReceipt.Patch; +using IdokladSdk.Models.ReceivedReceipt.Post; +using Microsoft.AspNetCore.Mvc; +using Idoklad.Services; + +namespace Idoklad.Controllers; + +/// +/// Received receipts and cash agendas. Requires iDoklad credentials (headers or environment defaults). +/// +[ApiController] +[Produces("application/json")] +[Tags("PurchaseAndCash")] +public sealed class PurchaseCashController : ControllerBase +{ + private readonly PurchaseCashService _service; + + public PurchaseCashController(PurchaseCashService service) => _service = service; + + // ---- Received receipts ---- + + /// List received receipts (paged). + [HttpGet("received-receipts")] + public async Task ListReceivedReceipts(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListReceivedReceiptsAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new received receipt. + [HttpGet("received-receipts/default")] + public async Task ReceivedReceiptDefault(CancellationToken ct) + => Ok(await _service.ReceivedReceiptDefaultAsync(ct)); + + /// Get a received receipt detail by id. + [HttpGet("received-receipts/{id:int}")] + public async Task ReceivedReceiptDetail(int id, CancellationToken ct) + => Ok(await _service.ReceivedReceiptDetailAsync(id, ct)); + + /// Create a new received receipt. + [HttpPost("received-receipts")] + public async Task CreateReceivedReceipt([FromBody] ReceivedReceiptPostModel model, CancellationToken ct) + => Ok(await _service.CreateReceivedReceiptAsync(model, ct)); + + /// Update an existing received receipt (the model id identifies the document). + [HttpPatch("received-receipts")] + public async Task UpdateReceivedReceipt([FromBody] ReceivedReceiptPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateReceivedReceiptAsync(model, ct)); + + /// Delete a received receipt by id. + [HttpDelete("received-receipts/{id:int}")] + public async Task DeleteReceivedReceipt(int id, CancellationToken ct) + => Ok(await _service.DeleteReceivedReceiptAsync(id, ct)); + + // ---- Cash vouchers ---- + + /// List cash vouchers (paged). + [HttpGet("cash-vouchers")] + public async Task ListCashVouchers(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListCashVouchersAsync(page, pageSize, ct)); + + /// Get a cash voucher detail by id. + [HttpGet("cash-vouchers/{id:int}")] + public async Task CashVoucherDetail(int id, CancellationToken ct) + => Ok(await _service.CashVoucherDetailAsync(id, ct)); + + /// Create a new cash voucher. + [HttpPost("cash-vouchers")] + public async Task CreateCashVoucher([FromBody] CashVoucherPostModel model, CancellationToken ct) + => Ok(await _service.CreateCashVoucherAsync(model, ct)); + + /// Update an existing cash voucher (the model id identifies the document). + [HttpPatch("cash-vouchers")] + public async Task UpdateCashVoucher([FromBody] CashVoucherPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateCashVoucherAsync(model, ct)); + + /// Delete a cash voucher by id. + [HttpDelete("cash-vouchers/{id:int}")] + public async Task DeleteCashVoucher(int id, CancellationToken ct) + => Ok(await _service.DeleteCashVoucherAsync(id, ct)); + + // ---- Cash registers ---- + + /// List cash registers (paged). + [HttpGet("cash-registers")] + public async Task ListCashRegisters(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListCashRegistersAsync(page, pageSize, ct)); + + /// Get a cash register detail by id. + [HttpGet("cash-registers/{id:int}")] + public async Task CashRegisterDetail(int id, CancellationToken ct) + => Ok(await _service.CashRegisterDetailAsync(id, ct)); + + /// Create a new cash register. + [HttpPost("cash-registers")] + public async Task CreateCashRegister([FromBody] CashRegisterPostModel model, CancellationToken ct) + => Ok(await _service.CreateCashRegisterAsync(model, ct)); + + /// Update an existing cash register (the model id identifies the register). + [HttpPatch("cash-registers")] + public async Task UpdateCashRegister([FromBody] CashRegisterPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateCashRegisterAsync(model, ct)); + + /// Delete a cash register by id. + [HttpDelete("cash-registers/{id:int}")] + public async Task DeleteCashRegister(int id, CancellationToken ct) + => Ok(await _service.DeleteCashRegisterAsync(id, ct)); +} diff --git a/Controllers/SalesDocumentsController.cs b/Controllers/SalesDocumentsController.cs new file mode 100644 index 0000000..d23bebe --- /dev/null +++ b/Controllers/SalesDocumentsController.cs @@ -0,0 +1,270 @@ +using IdokladSdk.Models.CreditNote; +using IdokladSdk.Models.IssuedDocumentTemplate.Patch; +using IdokladSdk.Models.IssuedDocumentTemplate.Post; +using IdokladSdk.Models.IssuedTaxDocument.Patch; +using IdokladSdk.Models.IssuedTaxDocument.Post; +using IdokladSdk.Models.ProformaInvoice; +using IdokladSdk.Models.RecurringInvoice; +using IdokladSdk.Models.SalesOrder; +using IdokladSdk.Models.SalesReceipt; +using Microsoft.AspNetCore.Mvc; +using Idoklad.Services; + +namespace Idoklad.Controllers; + +/// +/// Sales-side documents. Requires iDoklad credentials (headers or environment defaults). +/// +[ApiController] +[Produces("application/json")] +[Tags("SalesDocuments")] +public sealed class SalesDocumentsController : ControllerBase +{ + private readonly SalesDocumentsService _service; + + public SalesDocumentsController(SalesDocumentsService service) => _service = service; + + // ---- Proforma invoices ---- + + /// List proforma invoices (paged). + [HttpGet("proforma-invoices")] + public async Task ListProforma(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListProformaAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new proforma invoice. + [HttpGet("proforma-invoices/default")] + public async Task ProformaDefault(CancellationToken ct) + => Ok(await _service.ProformaDefaultAsync(ct)); + + /// Get a proforma invoice detail by id. + [HttpGet("proforma-invoices/{id:int}")] + public async Task ProformaDetail(int id, CancellationToken ct) + => Ok(await _service.ProformaDetailAsync(id, ct)); + + /// Create a new proforma invoice. + [HttpPost("proforma-invoices")] + public async Task CreateProforma([FromBody] ProformaInvoicePostModel model, CancellationToken ct) + => Ok(await _service.CreateProformaAsync(model, ct)); + + /// Update an existing proforma invoice (the model id identifies the document). + [HttpPatch("proforma-invoices")] + public async Task UpdateProforma([FromBody] ProformaInvoicePatchModel model, CancellationToken ct) + => Ok(await _service.UpdateProformaAsync(model, ct)); + + /// Create a copy (draft) of an existing proforma invoice. + [HttpPost("proforma-invoices/{id:int}/copy")] + public async Task CopyProforma(int id, CancellationToken ct) + => Ok(await _service.ProformaCopyAsync(id, ct)); + + /// Delete a proforma invoice by id. + [HttpDelete("proforma-invoices/{id:int}")] + public async Task DeleteProforma(int id, CancellationToken ct) + => Ok(await _service.DeleteProformaAsync(id, ct)); + + // ---- Credit notes ---- + + /// List credit notes (paged). + [HttpGet("credit-notes")] + public async Task ListCreditNotes(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListCreditNotesAsync(page, pageSize, ct)); + + /// Get a pre-filled default credit note model for the given (issued) invoice id. + [HttpGet("credit-notes/default/{invoiceId:int}")] + public async Task CreditNoteDefault(int invoiceId, CancellationToken ct) + => Ok(await _service.CreditNoteDefaultAsync(invoiceId, ct)); + + /// Get a credit note detail by id. + [HttpGet("credit-notes/{id:int}")] + public async Task CreditNoteDetail(int id, CancellationToken ct) + => Ok(await _service.CreditNoteDetailAsync(id, ct)); + + /// Create a new credit note. + [HttpPost("credit-notes")] + public async Task CreateCreditNote([FromBody] CreditNotePostModel model, CancellationToken ct) + => Ok(await _service.CreateCreditNoteAsync(model, ct)); + + /// Update an existing credit note (the model id identifies the document). + [HttpPatch("credit-notes")] + public async Task UpdateCreditNote([FromBody] CreditNotePatchModel model, CancellationToken ct) + => Ok(await _service.UpdateCreditNoteAsync(model, ct)); + + /// Delete a credit note by id. + [HttpDelete("credit-notes/{id:int}")] + public async Task DeleteCreditNote(int id, CancellationToken ct) + => Ok(await _service.DeleteCreditNoteAsync(id, ct)); + + // ---- Sales receipts ---- + + /// List sales receipts (paged). + [HttpGet("sales-receipts")] + public async Task ListSalesReceipts(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListSalesReceiptsAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new sales receipt. + [HttpGet("sales-receipts/default")] + public async Task SalesReceiptDefault(CancellationToken ct) + => Ok(await _service.SalesReceiptDefaultAsync(ct)); + + /// Get a sales receipt detail by id. + [HttpGet("sales-receipts/{id:int}")] + public async Task SalesReceiptDetail(int id, CancellationToken ct) + => Ok(await _service.SalesReceiptDetailAsync(id, ct)); + + /// Create a new sales receipt. + [HttpPost("sales-receipts")] + public async Task CreateSalesReceipt([FromBody] SalesReceiptPostModel model, CancellationToken ct) + => Ok(await _service.CreateSalesReceiptAsync(model, ct)); + + /// Update an existing sales receipt (the model id identifies the document). + [HttpPatch("sales-receipts")] + public async Task UpdateSalesReceipt([FromBody] SalesReceiptPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateSalesReceiptAsync(model, ct)); + + /// Create a copy (draft) of an existing sales receipt. + [HttpPost("sales-receipts/{id:int}/copy")] + public async Task CopySalesReceipt(int id, CancellationToken ct) + => Ok(await _service.SalesReceiptCopyAsync(id, ct)); + + /// Delete a sales receipt by id. + [HttpDelete("sales-receipts/{id:int}")] + public async Task DeleteSalesReceipt(int id, CancellationToken ct) + => Ok(await _service.DeleteSalesReceiptAsync(id, ct)); + + // ---- Sales orders ---- + + /// List sales orders (paged). + [HttpGet("sales-orders")] + public async Task ListSalesOrders(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListSalesOrdersAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new sales order. + [HttpGet("sales-orders/default")] + public async Task SalesOrderDefault(CancellationToken ct) + => Ok(await _service.SalesOrderDefaultAsync(ct)); + + /// Get a sales order detail by id. + [HttpGet("sales-orders/{id:int}")] + public async Task SalesOrderDetail(int id, CancellationToken ct) + => Ok(await _service.SalesOrderDetailAsync(id, ct)); + + /// Create a new sales order. + [HttpPost("sales-orders")] + public async Task CreateSalesOrder([FromBody] SalesOrderPostModel model, CancellationToken ct) + => Ok(await _service.CreateSalesOrderAsync(model, ct)); + + /// Update an existing sales order (the model id identifies the document). + [HttpPatch("sales-orders")] + public async Task UpdateSalesOrder([FromBody] SalesOrderPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateSalesOrderAsync(model, ct)); + + /// Create a copy (draft) of an existing sales order. + [HttpPost("sales-orders/{id:int}/copy")] + public async Task CopySalesOrder(int id, CancellationToken ct) + => Ok(await _service.SalesOrderCopyAsync(id, ct)); + + /// Delete a sales order by id. + [HttpDelete("sales-orders/{id:int}")] + public async Task DeleteSalesOrder(int id, CancellationToken ct) + => Ok(await _service.DeleteSalesOrderAsync(id, ct)); + + // ---- Recurring invoices ---- + + /// List recurring invoices (paged). + [HttpGet("recurring-invoices")] + public async Task ListRecurring(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListRecurringAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new recurring invoice. + [HttpGet("recurring-invoices/default")] + public async Task RecurringDefault(CancellationToken ct) + => Ok(await _service.RecurringDefaultAsync(ct)); + + /// Get a recurring invoice detail by id. + [HttpGet("recurring-invoices/{id:int}")] + public async Task RecurringDetail(int id, CancellationToken ct) + => Ok(await _service.RecurringDetailAsync(id, ct)); + + /// Create a new recurring invoice template. + [HttpPost("recurring-invoices")] + public async Task CreateRecurring([FromBody] RecurringInvoicePostModel model, CancellationToken ct) + => Ok(await _service.CreateRecurringAsync(model, ct)); + + /// Update an existing recurring invoice (the model id identifies the template). + [HttpPatch("recurring-invoices")] + public async Task UpdateRecurring([FromBody] RecurringInvoicePatchModel model, CancellationToken ct) + => Ok(await _service.UpdateRecurringAsync(model, ct)); + + /// Create a copy (draft) of an existing recurring invoice. + [HttpPost("recurring-invoices/{id:int}/copy")] + public async Task CopyRecurring(int id, CancellationToken ct) + => Ok(await _service.RecurringCopyAsync(id, ct)); + + /// Delete a recurring invoice by id. + [HttpDelete("recurring-invoices/{id:int}")] + public async Task DeleteRecurring(int id, CancellationToken ct) + => Ok(await _service.DeleteRecurringAsync(id, ct)); + + // ---- Issued tax documents ---- + + /// List issued tax documents (paged). + [HttpGet("issued-tax-documents")] + public async Task ListTaxDocuments(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListTaxDocumentsAsync(page, pageSize, ct)); + + /// Get a pre-filled default tax document model for the given advance (proforma) invoice id. + [HttpGet("issued-tax-documents/default/{advanceInvoiceId:int}")] + public async Task TaxDocumentDefault(int advanceInvoiceId, CancellationToken ct) + => Ok(await _service.TaxDocumentDefaultAsync(advanceInvoiceId, ct)); + + /// Get an issued tax document detail by id. + [HttpGet("issued-tax-documents/{id:int}")] + public async Task TaxDocumentDetail(int id, CancellationToken ct) + => Ok(await _service.TaxDocumentDetailAsync(id, ct)); + + /// Create a new issued tax document. + [HttpPost("issued-tax-documents")] + public async Task CreateTaxDocument([FromBody] IssuedTaxDocumentPostModel model, CancellationToken ct) + => Ok(await _service.CreateTaxDocumentAsync(model, ct)); + + /// Update an existing issued tax document (the model id identifies the document). + [HttpPatch("issued-tax-documents")] + public async Task UpdateTaxDocument([FromBody] IssuedTaxDocumentPatchModel model, CancellationToken ct) + => Ok(await _service.UpdateTaxDocumentAsync(model, ct)); + + /// Delete an issued tax document by id. + [HttpDelete("issued-tax-documents/{id:int}")] + public async Task DeleteTaxDocument(int id, CancellationToken ct) + => Ok(await _service.DeleteTaxDocumentAsync(id, ct)); + + // ---- Issued document templates ---- + + /// List issued document templates (paged). + [HttpGet("issued-document-templates")] + public async Task ListTemplates(int page = 1, int pageSize = 20, CancellationToken ct = default) + => Ok(await _service.ListTemplatesAsync(page, pageSize, ct)); + + /// Get a pre-filled default model for creating a new issued document template. + [HttpGet("issued-document-templates/default")] + public async Task TemplateDefault(CancellationToken ct) + => Ok(await _service.TemplateDefaultAsync(ct)); + + /// Get an issued document template detail by id. + [HttpGet("issued-document-templates/{id:int}")] + public async Task TemplateDetail(int id, CancellationToken ct) + => Ok(await _service.TemplateDetailAsync(id, ct)); + + /// Create a new issued document template. + [HttpPost("issued-document-templates")] + public async Task CreateTemplate([FromBody] IssuedDocumentTemplatePostModel model, CancellationToken ct) + => Ok(await _service.CreateTemplateAsync(model, ct)); + + /// Update an existing issued document template (the model id identifies the template). + [HttpPatch("issued-document-templates")] + public async Task UpdateTemplate([FromBody] IssuedDocumentTemplatePatchModel model, CancellationToken ct) + => Ok(await _service.UpdateTemplateAsync(model, ct)); + + /// Delete an issued document template by id. + [HttpDelete("issued-document-templates/{id:int}")] + public async Task DeleteTemplate(int id, CancellationToken ct) + => Ok(await _service.DeleteTemplateAsync(id, ct)); +} diff --git a/Controllers/StatisticsController.cs b/Controllers/StatisticsController.cs new file mode 100644 index 0000000..2d3fd47 --- /dev/null +++ b/Controllers/StatisticsController.cs @@ -0,0 +1,62 @@ +using IdokladSdk.Enums; +using Microsoft.AspNetCore.Mvc; +using Idoklad.Services; + +namespace Idoklad.Controllers; + +/// Agenda statistics and summaries. Requires iDoklad credentials. +[ApiController] +[Route("statistics")] +[Produces("application/json")] +[Tags("Statistics")] +public sealed class StatisticsController : ControllerBase +{ + private readonly StatisticsService _service; + + public StatisticsController(StatisticsService service) => _service = service; + + /// Invoicing totals for a relative period (e.g. LastSevenDays, ThisMonth). + [HttpGet("invoicing-for-period/{periodType}")] + public async Task InvoicingForPeriod(PeriodType periodType, CancellationToken ct) + => Ok(await _service.InvoicingForPeriodAsync(periodType, ct)); + + /// Invoicing totals for a relative year (e.g. ThisYear, LastYear). + [HttpGet("invoicing-for-year/{yearType}")] + public async Task InvoicingForYear(YearType yearType, CancellationToken ct) + => Ok(await _service.InvoicingForYearAsync(yearType, ct)); + + /// Invoicing summary broken down by quarter. + [HttpGet("quarter-summary")] + public async Task QuarterSummary(CancellationToken ct) + => Ok(await _service.QuarterSummaryAsync(ct)); + + /// Top partners by turnover (optionally limited by count). + [HttpGet("top-partners")] + public async Task TopPartners([FromQuery] int? count, CancellationToken ct) + => Ok(await _service.TopPartnersAsync(count, ct)); + + /// Overall agenda summary (totals and key figures). + [HttpGet("agenda-summary")] + public async Task AgendaSummary(CancellationToken ct) + => Ok(await _service.AgendaSummaryAsync(ct)); + + /// Statistics for a single contact by id. + [HttpGet("contact/{id:int}")] + public async Task StatisticForContact(int id, CancellationToken ct) + => Ok(await _service.StatisticForContactAsync(id, ct)); + + /// Receivables broken down by debt age intervals. + [HttpGet("debt-intervals")] + public async Task DebtIntervals(CancellationToken ct) + => Ok(await _service.DebtIntervalsAsync(ct)); + + /// Top debtors by outstanding amount (optionally limited by count). + [HttpGet("top-debtors")] + public async Task TopDebtors([FromQuery] int? count, CancellationToken ct) + => Ok(await _service.TopDebtorsAsync(count, ct)); + + /// Progress of the agenda towards the VAT-payer registration threshold. + [HttpGet("vat-payer-progress")] + public async Task VatPayerProgress(CancellationToken ct) + => Ok(await _service.VatPayerProgressAsync(ct)); +} diff --git a/Program.cs b/Program.cs index 415a109..e7182f7 100644 --- a/Program.cs +++ b/Program.cs @@ -31,6 +31,13 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); // Use Newtonsoft.Json so request/response binding matches the iDoklad SDK model attributes. builder.Services @@ -81,7 +88,10 @@ app.UseMiddleware(); app.UseSwagger(); app.UseSwaggerUI(options => { - options.SwaggerEndpoint("v1/swagger.json", $"{settings.AppName} v1"); + // Serve the interactive docs at /docs (matching the sibling microsoft-365-service). + options.RoutePrefix = "docs"; + options.SwaggerEndpoint("/swagger/v1/swagger.json", $"{settings.AppName} v1"); + options.DocumentTitle = $"{settings.AppName} – API docs"; }); app.MapControllers(); diff --git a/README.md b/README.md index f412079..d0996c0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ controllery a samostatná dokumentace ve Swaggeru. ## Konfigurace (proměnné prostředí) -Aplikace, ClientId, ClientSecret a ApplicationId získáte ve vývojářském portálu iDoklad. Tyto +Aplikaci, ClientId, ClientSecret a ApplicationId získáte ve vývojářském portálu iDoklad. Tyto hodnoty lze nastavit jako výchozí přes proměnné prostředí, nebo je předávat per-request v hlavičkách (viz níže). @@ -38,7 +38,7 @@ ROOT_PATH= # base path při běhu za reverzní proxy Citlivé proměnné (zejména **secret**) se **nepředávají v query stringu ani v těle** požadavku – **vyžadují se v HTTP hlavičkách**. To je zohledněno i ve Swaggeru: u každého agendového endpointu -jsou hlavičky vtypu `X-...` zdokumentované jako parametry. +jsou tyto hlavičky zdokumentované jako parametry. | Hlavička | Význam | Fallback (env) | | --- | --- | --- | @@ -60,64 +60,56 @@ X-ClientSecret: X-ApplicationId: ``` -## API +## Swagger / OpenAPI -```http -GET /health -GET /version -GET /status +Interaktivní dokumentace běží na **`/docs`**, surový OpenAPI dokument na `/swagger/v1/swagger.json`. +Každý agendový endpoint má ve Swaggeru zdokumentované credential hlavičky i popisky operací. -# Account -GET /account/agenda -GET /account/user +## Pokryté agendy -# Contacts -GET /contacts?page=1&pageSize=20 -GET /contacts/default -GET /contacts/{id} -POST /contacts -PATCH /contacts -DELETE /contacts/{id} +Propojeno je **41 ze 45 clientů** SDK 5.3.0. Standardní agendy podporují stránkovaný `list`, +`detail`, `default` (kde to SDK umožňuje) a `create`/`update`/`delete`; číselníky jsou read-only. -# Issued invoices (vydané faktury) -GET /issued-invoices?page=1&pageSize=20 -GET /issued-invoices/default -GET /issued-invoices/{id} -POST /issued-invoices -PATCH /issued-invoices -POST /issued-invoices/{id}/copy -DELETE /issued-invoices/{id} +Meta (bez credentials): `GET /health`, `GET /version`, `GET /status`. -# Received invoices (přijaté faktury) -GET /received-invoices?page=1&pageSize=20 -GET /received-invoices/default -GET /received-invoices/{id} -POST /received-invoices -PATCH /received-invoices -DELETE /received-invoices/{id} - -# Registry -GET /registers/bank-accounts?page=1&pageSize=20 -GET /registers/bank-accounts/{id} -POST /registers/bank-accounts -PATCH /registers/bank-accounts -DELETE /registers/bank-accounts/{id} -GET /registers/vat-rates?page=1&pageSize=20 -GET /registers/vat-rates/{id} -GET /registers/numeric-sequences?page=1&pageSize=20 -``` +| Tag | Agendy (cesty) | +| --- | --- | +| Account | `/account/agenda`, `/account/user` | +| Contacts | `/contacts` | +| IssuedInvoices | `/issued-invoices` (+ `/default`, `/{id}/copy`) | +| ReceivedInvoices | `/received-invoices` (+ `/default`) | +| SalesDocuments | `/proforma-invoices`, `/credit-notes`, `/sales-receipts`, `/sales-orders`, `/recurring-invoices`, `/issued-tax-documents`, `/issued-document-templates` | +| PurchaseAndCash | `/received-receipts`, `/cash-vouchers`, `/cash-registers` | +| Payments | `/bank-statements`, `/issued-payments`, `/received-payments` (+ `/fully-unpay/{invoiceId}`) | +| Catalog | `/price-list-items`, `/stock-movements`, `/tags` | +| Registers | `/registers/bank-accounts`, `/registers/vat-rates`, `/registers/numeric-sequences` | +| CodeLists | `/code-lists/banks`, `/countries`, `/currencies`, `/constant-symbols`, `/exchange-rates`, `/payment-options`, `/vat-codes`, `/vat-reverse-charge-codes`, `/sales-offices`, `/sales-pos-equipment` | +| Integration | `/webhooks`, `/notifications`, `/logs`, `/registered-sales`, `/unpaired-documents`, `/attachments`, `/system/code-books` | +| Statistics | `/statistics/*` (invoicing-for-period/year, quarter-summary, top-partners, agenda-summary, contact/{id}, debt-intervals, top-debtors, vat-payer-progress) | Request/response těla odpovídají modelům iDoklad SDK (`*PostModel`, `*PatchModel`, `*GetModel`). Serializace používá Newtonsoft.Json, aby se chování shodovalo s atributy modelů v SDK. Chyby z iDoklad API se propagují jako `application/problem+json` s odpovídajícím HTTP statusem. +### Zatím nezapojené (na vyžádání) + +Tyto klienty mají nestandardní/binární/facade charakter a nejsou zatím napojené: + +- **MailClient** – odesílání dokladů e-mailem (facade nad typy dokladů). +- **ReportClient** – generování PDF reportů (binární výstup). +- **DocumentPaymentClient** – facade nad platbami (překrývá se s `issued-payments` / `received-payments`). +- **BatchClient** – dávkové operace. + +Mimo to nejsou napojené pokročilé operace `Recount`, `*Batch` a `RecurringInvoice/NextIssueDates`. +`InboxClient` a `ReceivedDocumentsClient` v SDK 5.3.0 ještě nejsou (přibyly až po vydání). + ## Lokální spuštění ```bash dotnet run ``` -Swagger UI je na `/swagger`, OpenAPI dokument na `/swagger/v1/swagger.json`. +Swagger UI otevřete na `http://localhost:/docs`. ## Architektura diff --git a/Services/CatalogService.cs b/Services/CatalogService.cs new file mode 100644 index 0000000..f7df56e --- /dev/null +++ b/Services/CatalogService.cs @@ -0,0 +1,64 @@ +using IdokladSdk.Models.PriceListItem; +using IdokladSdk.Models.StockMovement; +using IdokladSdk.Models.Tag; +using Idoklad.Client; + +namespace Idoklad.Services; + +/// +/// Catalog and stock agendas: price list items, stock movements and tags. +/// +public sealed class CatalogService +{ + private readonly IdokladApiAccessor _accessor; + + public CatalogService(IdokladApiAccessor accessor) => _accessor = accessor; + + // ---- Price list items ---- + public Task ListPriceListItemsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.PriceListItemClient.List().ToPageAsync(page, pageSize, ct); + + public Task PriceListItemDetailAsync(int id, CancellationToken ct) + => _accessor.Api.PriceListItemClient.Detail(id).ToDetailAsync(ct); + + public async Task PriceListItemDefaultAsync(CancellationToken ct) + => (await _accessor.Api.PriceListItemClient.DefaultAsync(ct)).Unwrap(); + + public async Task CreatePriceListItemAsync(PriceListItemPostModel model, CancellationToken ct) + => (await _accessor.Api.PriceListItemClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdatePriceListItemAsync(PriceListItemPatchModel model, CancellationToken ct) + => (await _accessor.Api.PriceListItemClient.UpdateAsync(model, ct)).Unwrap(); + + // ---- Stock movements ---- + public Task ListStockMovementsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.StockMovementClient.List().ToPageAsync(page, pageSize, ct); + + public Task StockMovementDetailAsync(int id, CancellationToken ct) + => _accessor.Api.StockMovementClient.Detail(id).ToDetailAsync(ct); + + public async Task StockMovementDefaultAsync(int priceListItemId, CancellationToken ct) + => (await _accessor.Api.StockMovementClient.DefaultAsync(priceListItemId, ct)).Unwrap(); + + public async Task CreateStockMovementAsync(StockMovementPostModel model, CancellationToken ct) + => (await _accessor.Api.StockMovementClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateStockMovementAsync(StockMovementPatchModel model, CancellationToken ct) + => (await _accessor.Api.StockMovementClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteStockMovementAsync(int id, CancellationToken ct) + => (await _accessor.Api.StockMovementClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Tags ---- + public Task ListTagsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.TagClient.List().ToPageAsync(page, pageSize, ct); + + public async Task CreateTagAsync(TagPostModel model, CancellationToken ct) + => (await _accessor.Api.TagClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateTagAsync(TagPatchModel model, CancellationToken ct) + => (await _accessor.Api.TagClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteTagAsync(int id, CancellationToken ct) + => (await _accessor.Api.TagClient.DeleteAsync(id, ct)).Unwrap(); +} diff --git a/Services/CodeListsService.cs b/Services/CodeListsService.cs new file mode 100644 index 0000000..6bc97dc --- /dev/null +++ b/Services/CodeListsService.cs @@ -0,0 +1,75 @@ +using Idoklad.Client; + +namespace Idoklad.Services; + +/// +/// Read-only iDoklad code lists (registers): banks, countries, currencies, constant symbols, +/// exchange rates, payment options, VAT codes, VAT reverse-charge codes, sales offices and +/// sales POS equipment. Every agenda supports list + detail. +/// +public sealed class CodeListsService +{ + private readonly IdokladApiAccessor _accessor; + + public CodeListsService(IdokladApiAccessor accessor) => _accessor = accessor; + + public Task ListBanksAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.BankClient.List().ToPageAsync(page, pageSize, ct); + + public Task BankDetailAsync(int id, CancellationToken ct) + => _accessor.Api.BankClient.Detail(id).ToDetailAsync(ct); + + public Task ListCountriesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.CountryClient.List().ToPageAsync(page, pageSize, ct); + + public Task CountryDetailAsync(int id, CancellationToken ct) + => _accessor.Api.CountryClient.Detail(id).ToDetailAsync(ct); + + public Task ListCurrenciesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.CurrencyClient.List().ToPageAsync(page, pageSize, ct); + + public Task CurrencyDetailAsync(int id, CancellationToken ct) + => _accessor.Api.CurrencyClient.Detail(id).ToDetailAsync(ct); + + public Task ListConstantSymbolsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.ConstantSymbolClient.List().ToPageAsync(page, pageSize, ct); + + public Task ConstantSymbolDetailAsync(int id, CancellationToken ct) + => _accessor.Api.ConstantSymbolClient.Detail(id).ToDetailAsync(ct); + + public Task ListExchangeRatesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.ExchangeRateClient.List().ToPageAsync(page, pageSize, ct); + + public Task ExchangeRateDetailAsync(int id, CancellationToken ct) + => _accessor.Api.ExchangeRateClient.Detail(id).ToDetailAsync(ct); + + public Task ListPaymentOptionsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.PaymentOptionClient.List().ToPageAsync(page, pageSize, ct); + + public Task PaymentOptionDetailAsync(int id, CancellationToken ct) + => _accessor.Api.PaymentOptionClient.Detail(id).ToDetailAsync(ct); + + public Task ListVatCodesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.VatCodeClient.List().ToPageAsync(page, pageSize, ct); + + public Task VatCodeDetailAsync(int id, CancellationToken ct) + => _accessor.Api.VatCodeClient.Detail(id).ToDetailAsync(ct); + + public Task ListVatReverseChargeCodesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.VatReverseChargeCodeClient.List().ToPageAsync(page, pageSize, ct); + + public Task VatReverseChargeCodeDetailAsync(int id, CancellationToken ct) + => _accessor.Api.VatReverseChargeCodeClient.Detail(id).ToDetailAsync(ct); + + public Task ListSalesOfficesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.SalesOfficeClient.List().ToPageAsync(page, pageSize, ct); + + public Task SalesOfficeDetailAsync(int id, CancellationToken ct) + => _accessor.Api.SalesOfficeClient.Detail(id).ToDetailAsync(ct); + + public Task ListSalesPosEquipmentAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.SalesPosEquipmentClient.List().ToPageAsync(page, pageSize, ct); + + public Task SalesPosEquipmentDetailAsync(int id, CancellationToken ct) + => _accessor.Api.SalesPosEquipmentClient.Detail(id).ToDetailAsync(ct); +} diff --git a/Services/IntegrationService.cs b/Services/IntegrationService.cs new file mode 100644 index 0000000..78569f9 --- /dev/null +++ b/Services/IntegrationService.cs @@ -0,0 +1,76 @@ +using IdokladSdk.Enums; +using IdokladSdk.Models.Attachment; +using IdokladSdk.Models.Webhook; +using Idoklad.Client; + +namespace Idoklad.Services; + +/// +/// Integration / utility agendas: webhooks, notifications, change log, registered sales (EET), +/// unpaired documents, attachments and the system code books. +/// +public sealed class IntegrationService +{ + private readonly IdokladApiAccessor _accessor; + + public IntegrationService(IdokladApiAccessor accessor) => _accessor = accessor; + + // ---- Webhooks ---- + public Task ListWebhooksAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.WebhookClient.List().ToPageAsync(page, pageSize, ct); + + public Task WebhookDetailAsync(int id, CancellationToken ct) + => _accessor.Api.WebhookClient.Detail(id).ToDetailAsync(ct); + + public async Task CreateWebhookAsync(AgendaWebhookSettingsPostModel model, CancellationToken ct) + => (await _accessor.Api.WebhookClient.PostAsync(model, ct)).Unwrap(); + + public async Task DeleteWebhookAsync(int id, CancellationToken ct) + => (await _accessor.Api.WebhookClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Notifications ---- + public Task ListNotificationsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.NotificationClient.List().ToPageAsync(page, pageSize, ct); + + public async Task DeleteNotificationAsync(int id, CancellationToken ct) + => (await _accessor.Api.NotificationClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Change log ---- + public Task ListLogAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.LogClient.List().ToPageAsync(page, pageSize, ct); + + // ---- Registered sales (EET) ---- + public Task ListRegisteredSalesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.RegisteredSaleClient.List().ToPageAsync(page, pageSize, ct); + + public async Task RegisteredSaleDefaultAsync(CancellationToken ct) + => (await _accessor.Api.RegisteredSaleClient.DefaultAsync(ct)).Unwrap(); + + // ---- Unpaired documents ---- + public Task ListUnpairedDocumentsAsync( + MovementType movementType, PairingDocumentType pairingDocumentType, int page, int pageSize, CancellationToken ct) + => _accessor.Api.UnpairedDocumentClient.List(movementType, pairingDocumentType).ToPageAsync(page, pageSize, ct); + + // ---- Attachments ---- + public async Task GetAttachmentAsync(int attachmentId, bool compressed, CancellationToken ct) + => (await _accessor.Api.AttachmentClient.GetAsync(attachmentId, compressed, ct)).Unwrap(); + + public async Task GetDocumentAttachmentsAsync(int documentId, AttachmentDocumentType documentType, bool compressed, CancellationToken ct) + => (await _accessor.Api.AttachmentClient.GetAsync(documentId, documentType, compressed, ct)).Unwrap(); + + public async Task UploadAttachmentAsync(AttachmentUploadModel model, CancellationToken ct) + => (await _accessor.Api.AttachmentClient.UploadAsync(model, ct)).Unwrap(); + + public async Task DeleteAttachmentAsync(int attachmentId, CancellationToken ct) + => (await _accessor.Api.AttachmentClient.DeleteAsync(attachmentId, ct)).Unwrap(); + + public async Task DeleteDocumentAttachmentsAsync(int documentId, AttachmentDocumentType documentType, CancellationToken ct) + => (await _accessor.Api.AttachmentClient.DeleteAsync(documentId, documentType, ct)).Unwrap(); + + // ---- System code books ---- + public Task CodeBooksAsync(CancellationToken ct) + => _accessor.Api.SystemClient.CodeBooks().ToDetailAsync(ct); + + public Task CodeBooksChangesAsync(DateTime lastCheck, CancellationToken ct) + => _accessor.Api.SystemClient.CodeBooksChanges(lastCheck).ToDetailAsync(ct); +} diff --git a/Services/PaymentsService.cs b/Services/PaymentsService.cs new file mode 100644 index 0000000..af47aa5 --- /dev/null +++ b/Services/PaymentsService.cs @@ -0,0 +1,72 @@ +using IdokladSdk.Models.BankStatement.Patch; +using IdokladSdk.Models.BankStatement.Post; +using IdokladSdk.Models.IssuedDocumentPayment; +using IdokladSdk.Models.ReceivedDocumentPayments; +using Idoklad.Client; + +namespace Idoklad.Services; + +/// +/// Payments and bank statements: bank statements, issued document payments and received document +/// payments. +/// +public sealed class PaymentsService +{ + private readonly IdokladApiAccessor _accessor; + + public PaymentsService(IdokladApiAccessor accessor) => _accessor = accessor; + + // ---- Bank statements ---- + public Task ListBankStatementsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.BankStatementClient.List().ToPageAsync(page, pageSize, ct); + + public Task BankStatementDetailAsync(int id, CancellationToken ct) + => _accessor.Api.BankStatementClient.Detail(id).ToDetailAsync(ct); + + public async Task CreateBankStatementAsync(BankStatementPostModel model, CancellationToken ct) + => (await _accessor.Api.BankStatementClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateBankStatementAsync(BankStatementPatchModel model, CancellationToken ct) + => (await _accessor.Api.BankStatementClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteBankStatementAsync(int id, CancellationToken ct) + => (await _accessor.Api.BankStatementClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Issued document payments ---- + public Task ListIssuedPaymentsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.IssuedDocumentPaymentClient.List().ToPageAsync(page, pageSize, ct); + + public Task IssuedPaymentDetailAsync(int id, CancellationToken ct) + => _accessor.Api.IssuedDocumentPaymentClient.Detail(id).ToDetailAsync(ct); + + public async Task IssuedPaymentDefaultAsync(int invoiceId, CancellationToken ct) + => (await _accessor.Api.IssuedDocumentPaymentClient.DefaultAsync(invoiceId, ct)).Unwrap(); + + public async Task CreateIssuedPaymentAsync(IssuedDocumentPaymentPostModel model, CancellationToken ct) + => (await _accessor.Api.IssuedDocumentPaymentClient.PostAsync(model, ct)).Unwrap(); + + public async Task DeleteIssuedPaymentAsync(int id, CancellationToken ct) + => (await _accessor.Api.IssuedDocumentPaymentClient.DeleteAsync(id, ct)).Unwrap(); + + public async Task FullyUnpayIssuedAsync(int invoiceId, CancellationToken ct) + => (await _accessor.Api.IssuedDocumentPaymentClient.FullyUnpayAsync(invoiceId, ct)).Unwrap(); + + // ---- Received document payments ---- + public Task ListReceivedPaymentsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.ReceivedDocumentPaymentsClient.List().ToPageAsync(page, pageSize, ct); + + public Task ReceivedPaymentDetailAsync(int id, CancellationToken ct) + => _accessor.Api.ReceivedDocumentPaymentsClient.Detail(id).ToDetailAsync(ct); + + public async Task ReceivedPaymentDefaultAsync(int invoiceId, CancellationToken ct) + => (await _accessor.Api.ReceivedDocumentPaymentsClient.DefaultAsync(invoiceId, ct)).Unwrap(); + + public async Task CreateReceivedPaymentAsync(ReceivedDocumentPaymentPostModel model, CancellationToken ct) + => (await _accessor.Api.ReceivedDocumentPaymentsClient.PostAsync(model, ct)).Unwrap(); + + public async Task DeleteReceivedPaymentAsync(int id, CancellationToken ct) + => (await _accessor.Api.ReceivedDocumentPaymentsClient.DeleteAsync(id, ct)).Unwrap(); + + public async Task FullyUnpayReceivedAsync(int invoiceId, CancellationToken ct) + => (await _accessor.Api.ReceivedDocumentPaymentsClient.FullyUnpayAsync(invoiceId, ct)).Unwrap(); +} diff --git a/Services/PurchaseCashService.cs b/Services/PurchaseCashService.cs new file mode 100644 index 0000000..6c6e3e7 --- /dev/null +++ b/Services/PurchaseCashService.cs @@ -0,0 +1,69 @@ +using IdokladSdk.Models.CashRegister; +using IdokladSdk.Models.CashVoucher; +using IdokladSdk.Models.ReceivedReceipt.Patch; +using IdokladSdk.Models.ReceivedReceipt.Post; +using Idoklad.Client; + +namespace Idoklad.Services; + +/// +/// Purchase-side receipts and cash agendas: received receipts, received documents (read-only), +/// cash vouchers and cash registers. +/// +public sealed class PurchaseCashService +{ + private readonly IdokladApiAccessor _accessor; + + public PurchaseCashService(IdokladApiAccessor accessor) => _accessor = accessor; + + // ---- Received receipts ---- + public Task ListReceivedReceiptsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.ReceivedReceiptClient.List().ToPageAsync(page, pageSize, ct); + + public Task ReceivedReceiptDetailAsync(int id, CancellationToken ct) + => _accessor.Api.ReceivedReceiptClient.Detail(id).ToDetailAsync(ct); + + public async Task ReceivedReceiptDefaultAsync(CancellationToken ct) + => (await _accessor.Api.ReceivedReceiptClient.DefaultAsync(ct)).Unwrap(); + + public async Task CreateReceivedReceiptAsync(ReceivedReceiptPostModel model, CancellationToken ct) + => (await _accessor.Api.ReceivedReceiptClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateReceivedReceiptAsync(ReceivedReceiptPatchModel model, CancellationToken ct) + => (await _accessor.Api.ReceivedReceiptClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteReceivedReceiptAsync(int id, CancellationToken ct) + => (await _accessor.Api.ReceivedReceiptClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Cash vouchers ---- + public Task ListCashVouchersAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.CashVoucherClient.List().ToPageAsync(page, pageSize, ct); + + public Task CashVoucherDetailAsync(int id, CancellationToken ct) + => _accessor.Api.CashVoucherClient.Detail(id).ToDetailAsync(ct); + + public async Task CreateCashVoucherAsync(CashVoucherPostModel model, CancellationToken ct) + => (await _accessor.Api.CashVoucherClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateCashVoucherAsync(CashVoucherPatchModel model, CancellationToken ct) + => (await _accessor.Api.CashVoucherClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteCashVoucherAsync(int id, CancellationToken ct) + => (await _accessor.Api.CashVoucherClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Cash registers ---- + public Task ListCashRegistersAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.CashRegisterClient.List().ToPageAsync(page, pageSize, ct); + + public Task CashRegisterDetailAsync(int id, CancellationToken ct) + => _accessor.Api.CashRegisterClient.Detail(id).ToDetailAsync(ct); + + public async Task CreateCashRegisterAsync(CashRegisterPostModel model, CancellationToken ct) + => (await _accessor.Api.CashRegisterClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateCashRegisterAsync(CashRegisterPatchModel model, CancellationToken ct) + => (await _accessor.Api.CashRegisterClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteCashRegisterAsync(int id, CancellationToken ct) + => (await _accessor.Api.CashRegisterClient.DeleteAsync(id, ct)).Unwrap(); +} diff --git a/Services/SalesDocumentsService.cs b/Services/SalesDocumentsService.cs new file mode 100644 index 0000000..b4a67f7 --- /dev/null +++ b/Services/SalesDocumentsService.cs @@ -0,0 +1,168 @@ +using IdokladSdk.Models.CreditNote; +using IdokladSdk.Models.IssuedDocumentTemplate.Patch; +using IdokladSdk.Models.IssuedDocumentTemplate.Post; +using IdokladSdk.Models.IssuedTaxDocument.Patch; +using IdokladSdk.Models.IssuedTaxDocument.Post; +using IdokladSdk.Models.ProformaInvoice; +using IdokladSdk.Models.RecurringInvoice; +using IdokladSdk.Models.SalesOrder; +using IdokladSdk.Models.SalesReceipt; +using Idoklad.Client; + +namespace Idoklad.Services; + +/// +/// Sales-side documents: proforma invoices, credit notes, sales receipts, sales orders, +/// recurring invoices, issued tax documents and issued document templates. +/// +public sealed class SalesDocumentsService +{ + private readonly IdokladApiAccessor _accessor; + + public SalesDocumentsService(IdokladApiAccessor accessor) => _accessor = accessor; + + // ---- Proforma invoices ---- + public Task ListProformaAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.ProformaInvoiceClient.List().ToPageAsync(page, pageSize, ct); + + public Task ProformaDetailAsync(int id, CancellationToken ct) + => _accessor.Api.ProformaInvoiceClient.Detail(id).ToDetailAsync(ct); + + public async Task ProformaDefaultAsync(CancellationToken ct) + => (await _accessor.Api.ProformaInvoiceClient.DefaultAsync(ct)).Unwrap(); + + public async Task ProformaCopyAsync(int id, CancellationToken ct) + => (await _accessor.Api.ProformaInvoiceClient.CopyAsync(id, ct)).Unwrap(); + + public async Task CreateProformaAsync(ProformaInvoicePostModel model, CancellationToken ct) + => (await _accessor.Api.ProformaInvoiceClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateProformaAsync(ProformaInvoicePatchModel model, CancellationToken ct) + => (await _accessor.Api.ProformaInvoiceClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteProformaAsync(int id, CancellationToken ct) + => (await _accessor.Api.ProformaInvoiceClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Credit notes ---- + public Task ListCreditNotesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.CreditNoteClient.List().ToPageAsync(page, pageSize, ct); + + public Task CreditNoteDetailAsync(int id, CancellationToken ct) + => _accessor.Api.CreditNoteClient.Detail(id).ToDetailAsync(ct); + + public async Task CreditNoteDefaultAsync(int invoiceId, CancellationToken ct) + => (await _accessor.Api.CreditNoteClient.DefaultAsync(invoiceId, ct)).Unwrap(); + + public async Task CreateCreditNoteAsync(CreditNotePostModel model, CancellationToken ct) + => (await _accessor.Api.CreditNoteClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateCreditNoteAsync(CreditNotePatchModel model, CancellationToken ct) + => (await _accessor.Api.CreditNoteClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteCreditNoteAsync(int id, CancellationToken ct) + => (await _accessor.Api.CreditNoteClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Sales receipts ---- + public Task ListSalesReceiptsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.SalesReceiptClient.List().ToPageAsync(page, pageSize, ct); + + public Task SalesReceiptDetailAsync(int id, CancellationToken ct) + => _accessor.Api.SalesReceiptClient.Detail(id).ToDetailAsync(ct); + + public async Task SalesReceiptDefaultAsync(CancellationToken ct) + => (await _accessor.Api.SalesReceiptClient.DefaultAsync(ct)).Unwrap(); + + public async Task SalesReceiptCopyAsync(int id, CancellationToken ct) + => (await _accessor.Api.SalesReceiptClient.CopyAsync(id, ct)).Unwrap(); + + public async Task CreateSalesReceiptAsync(SalesReceiptPostModel model, CancellationToken ct) + => (await _accessor.Api.SalesReceiptClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateSalesReceiptAsync(SalesReceiptPatchModel model, CancellationToken ct) + => (await _accessor.Api.SalesReceiptClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteSalesReceiptAsync(int id, CancellationToken ct) + => (await _accessor.Api.SalesReceiptClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Sales orders ---- + public Task ListSalesOrdersAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.SalesOrderClient.List().ToPageAsync(page, pageSize, ct); + + public Task SalesOrderDetailAsync(int id, CancellationToken ct) + => _accessor.Api.SalesOrderClient.Detail(id).ToDetailAsync(ct); + + public async Task SalesOrderDefaultAsync(CancellationToken ct) + => (await _accessor.Api.SalesOrderClient.DefaultAsync(ct)).Unwrap(); + + public async Task SalesOrderCopyAsync(int id, CancellationToken ct) + => (await _accessor.Api.SalesOrderClient.CopyAsync(id, ct)).Unwrap(); + + public async Task CreateSalesOrderAsync(SalesOrderPostModel model, CancellationToken ct) + => (await _accessor.Api.SalesOrderClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateSalesOrderAsync(SalesOrderPatchModel model, CancellationToken ct) + => (await _accessor.Api.SalesOrderClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteSalesOrderAsync(int id, CancellationToken ct) + => (await _accessor.Api.SalesOrderClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Recurring invoices ---- + public Task ListRecurringAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.RecurringInvoiceClient.List().ToPageAsync(page, pageSize, ct); + + public Task RecurringDetailAsync(int id, CancellationToken ct) + => _accessor.Api.RecurringInvoiceClient.Detail(id).ToDetailAsync(ct); + + public async Task RecurringDefaultAsync(CancellationToken ct) + => (await _accessor.Api.RecurringInvoiceClient.DefaultAsync(ct)).Unwrap(); + + public async Task RecurringCopyAsync(int id, CancellationToken ct) + => (await _accessor.Api.RecurringInvoiceClient.CopyAsync(id, ct)).Unwrap(); + + public async Task CreateRecurringAsync(RecurringInvoicePostModel model, CancellationToken ct) + => (await _accessor.Api.RecurringInvoiceClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateRecurringAsync(RecurringInvoicePatchModel model, CancellationToken ct) + => (await _accessor.Api.RecurringInvoiceClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteRecurringAsync(int id, CancellationToken ct) + => (await _accessor.Api.RecurringInvoiceClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Issued tax documents ---- + public Task ListTaxDocumentsAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.IssuedTaxDocumentClient.List().ToPageAsync(page, pageSize, ct); + + public Task TaxDocumentDetailAsync(int id, CancellationToken ct) + => _accessor.Api.IssuedTaxDocumentClient.Detail(id).ToDetailAsync(ct); + + public async Task TaxDocumentDefaultAsync(int advanceInvoiceId, CancellationToken ct) + => (await _accessor.Api.IssuedTaxDocumentClient.DefaultAsync(advanceInvoiceId, ct)).Unwrap(); + + public async Task CreateTaxDocumentAsync(IssuedTaxDocumentPostModel model, CancellationToken ct) + => (await _accessor.Api.IssuedTaxDocumentClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateTaxDocumentAsync(IssuedTaxDocumentPatchModel model, CancellationToken ct) + => (await _accessor.Api.IssuedTaxDocumentClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteTaxDocumentAsync(int id, CancellationToken ct) + => (await _accessor.Api.IssuedTaxDocumentClient.DeleteAsync(id, ct)).Unwrap(); + + // ---- Issued document templates ---- + public Task ListTemplatesAsync(int page, int pageSize, CancellationToken ct) + => _accessor.Api.IssuedDocumentTemplateClient.List().ToPageAsync(page, pageSize, ct); + + public Task TemplateDetailAsync(int id, CancellationToken ct) + => _accessor.Api.IssuedDocumentTemplateClient.Detail(id).ToDetailAsync(ct); + + public async Task TemplateDefaultAsync(CancellationToken ct) + => (await _accessor.Api.IssuedDocumentTemplateClient.DefaultAsync(ct)).Unwrap(); + + public async Task CreateTemplateAsync(IssuedDocumentTemplatePostModel model, CancellationToken ct) + => (await _accessor.Api.IssuedDocumentTemplateClient.PostAsync(model, ct)).Unwrap(); + + public async Task UpdateTemplateAsync(IssuedDocumentTemplatePatchModel model, CancellationToken ct) + => (await _accessor.Api.IssuedDocumentTemplateClient.UpdateAsync(model, ct)).Unwrap(); + + public async Task DeleteTemplateAsync(int id, CancellationToken ct) + => (await _accessor.Api.IssuedDocumentTemplateClient.DeleteAsync(id, ct)).Unwrap(); +} diff --git a/Services/StatisticsService.cs b/Services/StatisticsService.cs new file mode 100644 index 0000000..1e92e9a --- /dev/null +++ b/Services/StatisticsService.cs @@ -0,0 +1,39 @@ +using IdokladSdk.Enums; +using Idoklad.Client; + +namespace Idoklad.Services; + +/// Agenda statistics and summaries. +public sealed class StatisticsService +{ + private readonly IdokladApiAccessor _accessor; + + public StatisticsService(IdokladApiAccessor accessor) => _accessor = accessor; + + public async Task InvoicingForPeriodAsync(PeriodType periodType, CancellationToken ct) + => (await _accessor.Api.StatisticsClient.InvoicingForPeriodAsync(periodType, null, ct)).Unwrap(); + + public async Task InvoicingForYearAsync(YearType yearType, CancellationToken ct) + => (await _accessor.Api.StatisticsClient.InvoicingForYearAsync(yearType, ct)).Unwrap(); + + public async Task QuarterSummaryAsync(CancellationToken ct) + => (await _accessor.Api.StatisticsClient.QuarterSummaryAsync(ct)).Unwrap(); + + public async Task TopPartnersAsync(int? count, CancellationToken ct) + => (await _accessor.Api.StatisticsClient.TopPartnersAsync(count, ct)).Unwrap(); + + public async Task AgendaSummaryAsync(CancellationToken ct) + => (await _accessor.Api.StatisticsClient.AgendaSummaryAsync(ct)).Unwrap(); + + public async Task StatisticForContactAsync(int id, CancellationToken ct) + => (await _accessor.Api.StatisticsClient.StatisticForContactAsync(id, ct)).Unwrap(); + + public async Task DebtIntervalsAsync(CancellationToken ct) + => (await _accessor.Api.StatisticsClient.DebtIntervals(ct)).Unwrap(); + + public async Task TopDebtorsAsync(int? count, CancellationToken ct) + => (await _accessor.Api.StatisticsClient.TopDebtors(count, ct)).Unwrap(); + + public async Task VatPayerProgressAsync(CancellationToken ct) + => (await _accessor.Api.StatisticsClient.VatPayerProgress(ct)).Unwrap(); +} diff --git a/bin/Release/net8.0/Idoklad.dll b/bin/Release/net8.0/Idoklad.dll index 2ad363a..04ac041 100644 Binary files a/bin/Release/net8.0/Idoklad.dll and b/bin/Release/net8.0/Idoklad.dll differ diff --git a/bin/Release/net8.0/Idoklad.exe b/bin/Release/net8.0/Idoklad.exe index f631b37..027d87d 100644 Binary files a/bin/Release/net8.0/Idoklad.exe and b/bin/Release/net8.0/Idoklad.exe differ diff --git a/bin/Release/net8.0/Idoklad.pdb b/bin/Release/net8.0/Idoklad.pdb index 0ec1107..a64b754 100644 Binary files a/bin/Release/net8.0/Idoklad.pdb and b/bin/Release/net8.0/Idoklad.pdb differ diff --git a/bin/Release/net8.0/Idoklad.xml b/bin/Release/net8.0/Idoklad.xml index c0590ea..fa1936b 100644 --- a/bin/Release/net8.0/Idoklad.xml +++ b/bin/Release/net8.0/Idoklad.xml @@ -29,6 +29,13 @@ code, message and iDoklad error code so the failure can be surfaced to the caller. + + + Generic helpers over the SDK's list/detail request builders so the read endpoints of the many + agendas can be expressed in a single line. The concrete payload type is inferred by the SDK and + returned boxed (full data is preserved at runtime; only the Swagger schema is generic). + + Service configuration resolved from environment variables. @@ -75,6 +82,122 @@ Get information about the current user. + + + Price list items, stock movements and tags. Requires iDoklad credentials (headers or environment defaults). + + + + List price list items (paged). + + + Get a pre-filled default model for creating a new price list item. + + + Get a price list item detail by id. + + + Create a new price list item. + + + Update an existing price list item (the model id identifies the item). + + + List stock movements (paged). + + + Get a pre-filled default stock movement model for the given price list item id. + + + Get a stock movement detail by id. + + + Create a new stock movement. + + + Update an existing stock movement (the model id identifies the movement). + + + Delete a stock movement by id. + + + List tags (paged). + + + Create a new tag. + + + Update an existing tag (the model id identifies the tag). + + + Delete a tag by id. + + + + Read-only iDoklad code lists / registers. Requires iDoklad credentials (headers or environment + defaults). + + + + List banks (paged). + + + Get a bank detail by id. + + + List countries (paged). + + + Get a country detail by id. + + + List currencies (paged). + + + Get a currency detail by id. + + + List constant symbols (paged). + + + Get a constant symbol detail by id. + + + List exchange rates (paged). + + + Get an exchange rate detail by id. + + + List payment options (paged). + + + Get a payment option detail by id. + + + List VAT codes (paged). + + + Get a VAT code detail by id. + + + List VAT reverse-charge codes (paged). + + + Get a VAT reverse-charge code detail by id. + + + List sales offices (paged). + + + Get a sales office detail by id. + + + List sales POS equipment (paged). + + + Get a sales POS equipment detail by id. + Contacts (customers/suppliers) agenda. @@ -102,6 +225,63 @@ Delete a contact by id. + + + Integration / utility agendas: webhooks, notifications, change log, registered sales, + unpaired documents, attachments and system code books. Requires iDoklad credentials. + + + + List configured webhooks (paged). + + + Get a webhook detail by id. + + + Create (register) a new webhook subscription. + + + Delete a webhook by id. + + + List notifications (paged). + + + Delete (dismiss) a notification by id. + + + List the agenda change log (paged). + + + List registered sales / EET records (paged). + + + Get a pre-filled default model for creating a new registered sale. + + + List unpaired documents for a movement type and pairing document type. + + + Get a single attachment by its id. + + + Get all attachments of a document. + + + Upload an attachment for a document. + + + Delete a single attachment by its id. + + + Delete all attachments of a document. + + + Get the iDoklad system code books. + + + Get changes to the code books since the given timestamp. + Issued (outgoing) invoices agenda. Requires iDoklad credentials (headers or environment @@ -144,6 +324,115 @@ environment variables. Per-request callers can always override them with headers. + + + Bank statements and document payments. Requires iDoklad credentials (headers or environment defaults). + + + + List bank statements (paged). + + + Get a bank statement detail by id. + + + Create a new bank statement. + + + Update an existing bank statement (the model id identifies the statement). + + + Delete a bank statement by id. + + + List issued document payments (paged). + + + Get a pre-filled default issued payment model for the given issued invoice id. + + + Get an issued document payment detail by id. + + + Create (register) a new payment for an issued document. + + + Delete an issued document payment by id. + + + Fully unpay an issued invoice (removes all its payments). + + + List received document payments (paged). + + + Get a pre-filled default received payment model for the given received invoice id. + + + Get a received document payment detail by id. + + + Create (register) a new payment for a received document. + + + Delete a received document payment by id. + + + Fully unpay a received invoice (removes all its payments). + + + + Received receipts and cash agendas. Requires iDoklad credentials (headers or environment defaults). + + + + List received receipts (paged). + + + Get a pre-filled default model for creating a new received receipt. + + + Get a received receipt detail by id. + + + Create a new received receipt. + + + Update an existing received receipt (the model id identifies the document). + + + Delete a received receipt by id. + + + List cash vouchers (paged). + + + Get a cash voucher detail by id. + + + Create a new cash voucher. + + + Update an existing cash voucher (the model id identifies the document). + + + Delete a cash voucher by id. + + + List cash registers (paged). + + + Get a cash register detail by id. + + + Create a new cash register. + + + Update an existing cash register (the model id identifies the register). + + + Delete a cash register by id. + Received (incoming) invoices agenda. Requires iDoklad credentials (headers or environment @@ -198,6 +487,179 @@ List numeric (document) sequences (paged). + + + Sales-side documents. Requires iDoklad credentials (headers or environment defaults). + + + + List proforma invoices (paged). + + + Get a pre-filled default model for creating a new proforma invoice. + + + Get a proforma invoice detail by id. + + + Create a new proforma invoice. + + + Update an existing proforma invoice (the model id identifies the document). + + + Create a copy (draft) of an existing proforma invoice. + + + Delete a proforma invoice by id. + + + List credit notes (paged). + + + Get a pre-filled default credit note model for the given (issued) invoice id. + + + Get a credit note detail by id. + + + Create a new credit note. + + + Update an existing credit note (the model id identifies the document). + + + Delete a credit note by id. + + + List sales receipts (paged). + + + Get a pre-filled default model for creating a new sales receipt. + + + Get a sales receipt detail by id. + + + Create a new sales receipt. + + + Update an existing sales receipt (the model id identifies the document). + + + Create a copy (draft) of an existing sales receipt. + + + Delete a sales receipt by id. + + + List sales orders (paged). + + + Get a pre-filled default model for creating a new sales order. + + + Get a sales order detail by id. + + + Create a new sales order. + + + Update an existing sales order (the model id identifies the document). + + + Create a copy (draft) of an existing sales order. + + + Delete a sales order by id. + + + List recurring invoices (paged). + + + Get a pre-filled default model for creating a new recurring invoice. + + + Get a recurring invoice detail by id. + + + Create a new recurring invoice template. + + + Update an existing recurring invoice (the model id identifies the template). + + + Create a copy (draft) of an existing recurring invoice. + + + Delete a recurring invoice by id. + + + List issued tax documents (paged). + + + Get a pre-filled default tax document model for the given advance (proforma) invoice id. + + + Get an issued tax document detail by id. + + + Create a new issued tax document. + + + Update an existing issued tax document (the model id identifies the document). + + + Delete an issued tax document by id. + + + List issued document templates (paged). + + + Get a pre-filled default model for creating a new issued document template. + + + Get an issued document template detail by id. + + + Create a new issued document template. + + + Update an existing issued document template (the model id identifies the template). + + + Delete an issued document template by id. + + + Agenda statistics and summaries. Requires iDoklad credentials. + + + Invoicing totals for a relative period (e.g. LastSevenDays, ThisMonth). + + + Invoicing totals for a relative year (e.g. ThisYear, LastYear). + + + Invoicing summary broken down by quarter. + + + Top partners by turnover (optionally limited by count). + + + Overall agenda summary (totals and key figures). + + + Statistics for a single contact by id. + + + Receivables broken down by debt age intervals. + + + Top debtors by outstanding amount (optionally limited by count). + + + Progress of the agenda towards the VAT-payer registration threshold. + Names of the HTTP headers that carry per-request iDoklad credentials. @@ -248,12 +710,42 @@ Account agenda: information about the current agenda and the current user. + + + Catalog and stock agendas: price list items, stock movements and tags. + + + + + Read-only iDoklad code lists (registers): banks, countries, currencies, constant symbols, + exchange rates, payment options, VAT codes, VAT reverse-charge codes, sales offices and + sales POS equipment. Every agenda supports list + detail. + + Contacts (customers/suppliers) agenda. + + + Integration / utility agendas: webhooks, notifications, change log, registered sales (EET), + unpaired documents, attachments and the system code books. + + Issued (outgoing) invoices agenda. + + + Payments and bank statements: bank statements, issued document payments and received document + payments. + + + + + Purchase-side receipts and cash agendas: received receipts, received documents (read-only), + cash vouchers and cash registers. + + Received (incoming) invoices agenda. @@ -262,5 +754,14 @@ Supporting registers: bank accounts, VAT rates and numeric (document) sequences. + + + Sales-side documents: proforma invoices, credit notes, sales receipts, sales orders, + recurring invoices, issued tax documents and issued document templates. + + + + Agenda statistics and summaries. + diff --git a/obj/Debug/net8.0/Idoklad.AssemblyInfo.cs b/obj/Debug/net8.0/Idoklad.AssemblyInfo.cs index 204e691..cb4943f 100644 --- a/obj/Debug/net8.0/Idoklad.AssemblyInfo.cs +++ b/obj/Debug/net8.0/Idoklad.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Idoklad")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+bbec747bb707894338c78c901bf8175f40773f12")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1801d02e5c0482cd820491bce08b14a912d95ab4")] [assembly: System.Reflection.AssemblyProductAttribute("Idoklad")] [assembly: System.Reflection.AssemblyTitleAttribute("Idoklad")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/obj/Debug/net8.0/Idoklad.AssemblyInfoInputs.cache b/obj/Debug/net8.0/Idoklad.AssemblyInfoInputs.cache index 701d671..5dd6e8e 100644 --- a/obj/Debug/net8.0/Idoklad.AssemblyInfoInputs.cache +++ b/obj/Debug/net8.0/Idoklad.AssemblyInfoInputs.cache @@ -1 +1 @@ -4d956995fcb6242da55b1ce331ee5915e0498e0501326f960a23ce2e1059f1e7 +735a164fa1828accabc432712c336fdc612a6180fde6a25df7cafa729751ebdc diff --git a/obj/Release/net8.0/Idoklad.AssemblyInfo.cs b/obj/Release/net8.0/Idoklad.AssemblyInfo.cs index 6428b7c..f94d2ee 100644 --- a/obj/Release/net8.0/Idoklad.AssemblyInfo.cs +++ b/obj/Release/net8.0/Idoklad.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Idoklad")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+bbec747bb707894338c78c901bf8175f40773f12")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+1801d02e5c0482cd820491bce08b14a912d95ab4")] [assembly: System.Reflection.AssemblyProductAttribute("Idoklad")] [assembly: System.Reflection.AssemblyTitleAttribute("Idoklad")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/obj/Release/net8.0/Idoklad.AssemblyInfoInputs.cache b/obj/Release/net8.0/Idoklad.AssemblyInfoInputs.cache index 2004b3e..4259f29 100644 --- a/obj/Release/net8.0/Idoklad.AssemblyInfoInputs.cache +++ b/obj/Release/net8.0/Idoklad.AssemblyInfoInputs.cache @@ -1 +1 @@ -9330b1b41c96efd56de1c2646a1495cef68227a0fe6aa8897950cb2005c1774e +c138318d9dc766fbc6536be68fa013af49fccd3cbba8bbf3d796211a236d31a7 diff --git a/obj/Release/net8.0/Idoklad.csproj.CoreCompileInputs.cache b/obj/Release/net8.0/Idoklad.csproj.CoreCompileInputs.cache index 0edae6c..2741494 100644 --- a/obj/Release/net8.0/Idoklad.csproj.CoreCompileInputs.cache +++ b/obj/Release/net8.0/Idoklad.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -8c52ccc98fa666734950d1f217e2bf36db84e91ef7b161912359e9d71820c6b4 +bdee49910858b6be5d5fdf360ef80f7269429c372c496a47829c966fe92a5ead diff --git a/obj/Release/net8.0/Idoklad.dll b/obj/Release/net8.0/Idoklad.dll index 2ad363a..04ac041 100644 Binary files a/obj/Release/net8.0/Idoklad.dll and b/obj/Release/net8.0/Idoklad.dll differ diff --git a/obj/Release/net8.0/Idoklad.pdb b/obj/Release/net8.0/Idoklad.pdb index 0ec1107..a64b754 100644 Binary files a/obj/Release/net8.0/Idoklad.pdb and b/obj/Release/net8.0/Idoklad.pdb differ diff --git a/obj/Release/net8.0/Idoklad.xml b/obj/Release/net8.0/Idoklad.xml index c0590ea..fa1936b 100644 --- a/obj/Release/net8.0/Idoklad.xml +++ b/obj/Release/net8.0/Idoklad.xml @@ -29,6 +29,13 @@ code, message and iDoklad error code so the failure can be surfaced to the caller. + + + Generic helpers over the SDK's list/detail request builders so the read endpoints of the many + agendas can be expressed in a single line. The concrete payload type is inferred by the SDK and + returned boxed (full data is preserved at runtime; only the Swagger schema is generic). + + Service configuration resolved from environment variables. @@ -75,6 +82,122 @@ Get information about the current user. + + + Price list items, stock movements and tags. Requires iDoklad credentials (headers or environment defaults). + + + + List price list items (paged). + + + Get a pre-filled default model for creating a new price list item. + + + Get a price list item detail by id. + + + Create a new price list item. + + + Update an existing price list item (the model id identifies the item). + + + List stock movements (paged). + + + Get a pre-filled default stock movement model for the given price list item id. + + + Get a stock movement detail by id. + + + Create a new stock movement. + + + Update an existing stock movement (the model id identifies the movement). + + + Delete a stock movement by id. + + + List tags (paged). + + + Create a new tag. + + + Update an existing tag (the model id identifies the tag). + + + Delete a tag by id. + + + + Read-only iDoklad code lists / registers. Requires iDoklad credentials (headers or environment + defaults). + + + + List banks (paged). + + + Get a bank detail by id. + + + List countries (paged). + + + Get a country detail by id. + + + List currencies (paged). + + + Get a currency detail by id. + + + List constant symbols (paged). + + + Get a constant symbol detail by id. + + + List exchange rates (paged). + + + Get an exchange rate detail by id. + + + List payment options (paged). + + + Get a payment option detail by id. + + + List VAT codes (paged). + + + Get a VAT code detail by id. + + + List VAT reverse-charge codes (paged). + + + Get a VAT reverse-charge code detail by id. + + + List sales offices (paged). + + + Get a sales office detail by id. + + + List sales POS equipment (paged). + + + Get a sales POS equipment detail by id. + Contacts (customers/suppliers) agenda. @@ -102,6 +225,63 @@ Delete a contact by id. + + + Integration / utility agendas: webhooks, notifications, change log, registered sales, + unpaired documents, attachments and system code books. Requires iDoklad credentials. + + + + List configured webhooks (paged). + + + Get a webhook detail by id. + + + Create (register) a new webhook subscription. + + + Delete a webhook by id. + + + List notifications (paged). + + + Delete (dismiss) a notification by id. + + + List the agenda change log (paged). + + + List registered sales / EET records (paged). + + + Get a pre-filled default model for creating a new registered sale. + + + List unpaired documents for a movement type and pairing document type. + + + Get a single attachment by its id. + + + Get all attachments of a document. + + + Upload an attachment for a document. + + + Delete a single attachment by its id. + + + Delete all attachments of a document. + + + Get the iDoklad system code books. + + + Get changes to the code books since the given timestamp. + Issued (outgoing) invoices agenda. Requires iDoklad credentials (headers or environment @@ -144,6 +324,115 @@ environment variables. Per-request callers can always override them with headers. + + + Bank statements and document payments. Requires iDoklad credentials (headers or environment defaults). + + + + List bank statements (paged). + + + Get a bank statement detail by id. + + + Create a new bank statement. + + + Update an existing bank statement (the model id identifies the statement). + + + Delete a bank statement by id. + + + List issued document payments (paged). + + + Get a pre-filled default issued payment model for the given issued invoice id. + + + Get an issued document payment detail by id. + + + Create (register) a new payment for an issued document. + + + Delete an issued document payment by id. + + + Fully unpay an issued invoice (removes all its payments). + + + List received document payments (paged). + + + Get a pre-filled default received payment model for the given received invoice id. + + + Get a received document payment detail by id. + + + Create (register) a new payment for a received document. + + + Delete a received document payment by id. + + + Fully unpay a received invoice (removes all its payments). + + + + Received receipts and cash agendas. Requires iDoklad credentials (headers or environment defaults). + + + + List received receipts (paged). + + + Get a pre-filled default model for creating a new received receipt. + + + Get a received receipt detail by id. + + + Create a new received receipt. + + + Update an existing received receipt (the model id identifies the document). + + + Delete a received receipt by id. + + + List cash vouchers (paged). + + + Get a cash voucher detail by id. + + + Create a new cash voucher. + + + Update an existing cash voucher (the model id identifies the document). + + + Delete a cash voucher by id. + + + List cash registers (paged). + + + Get a cash register detail by id. + + + Create a new cash register. + + + Update an existing cash register (the model id identifies the register). + + + Delete a cash register by id. + Received (incoming) invoices agenda. Requires iDoklad credentials (headers or environment @@ -198,6 +487,179 @@ List numeric (document) sequences (paged). + + + Sales-side documents. Requires iDoklad credentials (headers or environment defaults). + + + + List proforma invoices (paged). + + + Get a pre-filled default model for creating a new proforma invoice. + + + Get a proforma invoice detail by id. + + + Create a new proforma invoice. + + + Update an existing proforma invoice (the model id identifies the document). + + + Create a copy (draft) of an existing proforma invoice. + + + Delete a proforma invoice by id. + + + List credit notes (paged). + + + Get a pre-filled default credit note model for the given (issued) invoice id. + + + Get a credit note detail by id. + + + Create a new credit note. + + + Update an existing credit note (the model id identifies the document). + + + Delete a credit note by id. + + + List sales receipts (paged). + + + Get a pre-filled default model for creating a new sales receipt. + + + Get a sales receipt detail by id. + + + Create a new sales receipt. + + + Update an existing sales receipt (the model id identifies the document). + + + Create a copy (draft) of an existing sales receipt. + + + Delete a sales receipt by id. + + + List sales orders (paged). + + + Get a pre-filled default model for creating a new sales order. + + + Get a sales order detail by id. + + + Create a new sales order. + + + Update an existing sales order (the model id identifies the document). + + + Create a copy (draft) of an existing sales order. + + + Delete a sales order by id. + + + List recurring invoices (paged). + + + Get a pre-filled default model for creating a new recurring invoice. + + + Get a recurring invoice detail by id. + + + Create a new recurring invoice template. + + + Update an existing recurring invoice (the model id identifies the template). + + + Create a copy (draft) of an existing recurring invoice. + + + Delete a recurring invoice by id. + + + List issued tax documents (paged). + + + Get a pre-filled default tax document model for the given advance (proforma) invoice id. + + + Get an issued tax document detail by id. + + + Create a new issued tax document. + + + Update an existing issued tax document (the model id identifies the document). + + + Delete an issued tax document by id. + + + List issued document templates (paged). + + + Get a pre-filled default model for creating a new issued document template. + + + Get an issued document template detail by id. + + + Create a new issued document template. + + + Update an existing issued document template (the model id identifies the template). + + + Delete an issued document template by id. + + + Agenda statistics and summaries. Requires iDoklad credentials. + + + Invoicing totals for a relative period (e.g. LastSevenDays, ThisMonth). + + + Invoicing totals for a relative year (e.g. ThisYear, LastYear). + + + Invoicing summary broken down by quarter. + + + Top partners by turnover (optionally limited by count). + + + Overall agenda summary (totals and key figures). + + + Statistics for a single contact by id. + + + Receivables broken down by debt age intervals. + + + Top debtors by outstanding amount (optionally limited by count). + + + Progress of the agenda towards the VAT-payer registration threshold. + Names of the HTTP headers that carry per-request iDoklad credentials. @@ -248,12 +710,42 @@ Account agenda: information about the current agenda and the current user. + + + Catalog and stock agendas: price list items, stock movements and tags. + + + + + Read-only iDoklad code lists (registers): banks, countries, currencies, constant symbols, + exchange rates, payment options, VAT codes, VAT reverse-charge codes, sales offices and + sales POS equipment. Every agenda supports list + detail. + + Contacts (customers/suppliers) agenda. + + + Integration / utility agendas: webhooks, notifications, change log, registered sales (EET), + unpaired documents, attachments and the system code books. + + Issued (outgoing) invoices agenda. + + + Payments and bank statements: bank statements, issued document payments and received document + payments. + + + + + Purchase-side receipts and cash agendas: received receipts, received documents (read-only), + cash vouchers and cash registers. + + Received (incoming) invoices agenda. @@ -262,5 +754,14 @@ Supporting registers: bank accounts, VAT rates and numeric (document) sequences. + + + Sales-side documents: proforma invoices, credit notes, sales receipts, sales orders, + recurring invoices, issued tax documents and issued document templates. + + + + Agenda statistics and summaries. + diff --git a/obj/Release/net8.0/apphost.exe b/obj/Release/net8.0/apphost.exe index f631b37..027d87d 100644 Binary files a/obj/Release/net8.0/apphost.exe and b/obj/Release/net8.0/apphost.exe differ diff --git a/obj/Release/net8.0/ref/Idoklad.dll b/obj/Release/net8.0/ref/Idoklad.dll index 6a407af..6b127be 100644 Binary files a/obj/Release/net8.0/ref/Idoklad.dll and b/obj/Release/net8.0/ref/Idoklad.dll differ diff --git a/obj/Release/net8.0/refint/Idoklad.dll b/obj/Release/net8.0/refint/Idoklad.dll index 6a407af..6b127be 100644 Binary files a/obj/Release/net8.0/refint/Idoklad.dll and b/obj/Release/net8.0/refint/Idoklad.dll differ diff --git a/obj/Release/net8.0/rjsmcshtml.dswa.cache.json b/obj/Release/net8.0/rjsmcshtml.dswa.cache.json index 9d2ebea..3cba7e1 100644 --- a/obj/Release/net8.0/rjsmcshtml.dswa.cache.json +++ b/obj/Release/net8.0/rjsmcshtml.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"B9kWZPkqs9jh4om+wEa7vtkXe98jy4Uk00TDvCFOMAg=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Xoy6Z/ZliL2FaV/OPQYzcdsWaKV5xBoURVM2EiwsMe0=","UnIQ74hcK4OEyD1OXQb8l/Ai5e5GkT1C8ZVGznd7EHM=","vIllxnm6hE/qU5OIVUPxE4SuD2ijmi27OPg5VyWR6ZM=","QqmhCK1Ak7uSaGTG1n4MvYJ\u002BFpdANJykxNBeraLdr/c=","et\u002BonTqXghtchEvOcFNSnpptMGH2zS\u002Bmy59QN1yTclY="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"B9kWZPkqs9jh4om+wEa7vtkXe98jy4Uk00TDvCFOMAg=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Xoy6Z/ZliL2FaV/OPQYzcdsWaKV5xBoURVM2EiwsMe0=","UnIQ74hcK4OEyD1OXQb8l/Ai5e5GkT1C8ZVGznd7EHM=","vIllxnm6hE/qU5OIVUPxE4SuD2ijmi27OPg5VyWR6ZM=","MUXRGW4/fHK8GHUoDLoFwoTh7HBZS3BhPZdp1GGzNvQ=","4\u002BR\u002B6nWtyavKnNL/kes5GvB2nISojVdnYf3VWVk2bWA="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/obj/Release/net8.0/rjsmrazor.dswa.cache.json b/obj/Release/net8.0/rjsmrazor.dswa.cache.json index 9d447f2..dfda1fb 100644 --- a/obj/Release/net8.0/rjsmrazor.dswa.cache.json +++ b/obj/Release/net8.0/rjsmrazor.dswa.cache.json @@ -1 +1 @@ -{"GlobalPropertiesHash":"ITni+o74UkYnIxQUhjgt1e5B31FSFcyvxyxM3WTkkB8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Xoy6Z/ZliL2FaV/OPQYzcdsWaKV5xBoURVM2EiwsMe0=","UnIQ74hcK4OEyD1OXQb8l/Ai5e5GkT1C8ZVGznd7EHM=","vIllxnm6hE/qU5OIVUPxE4SuD2ijmi27OPg5VyWR6ZM=","QqmhCK1Ak7uSaGTG1n4MvYJ\u002BFpdANJykxNBeraLdr/c=","et\u002BonTqXghtchEvOcFNSnpptMGH2zS\u002Bmy59QN1yTclY="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"GlobalPropertiesHash":"ITni+o74UkYnIxQUhjgt1e5B31FSFcyvxyxM3WTkkB8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Xoy6Z/ZliL2FaV/OPQYzcdsWaKV5xBoURVM2EiwsMe0=","UnIQ74hcK4OEyD1OXQb8l/Ai5e5GkT1C8ZVGznd7EHM=","vIllxnm6hE/qU5OIVUPxE4SuD2ijmi27OPg5VyWR6ZM=","MUXRGW4/fHK8GHUoDLoFwoTh7HBZS3BhPZdp1GGzNvQ=","4\u002BR\u002B6nWtyavKnNL/kes5GvB2nISojVdnYf3VWVk2bWA="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file