Download the PHP package arturas88/finvalda-sdk without Composer

On this page you can find all versions of the php package arturas88/finvalda-sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package finvalda-sdk

Finvalda PHP SDK

PHP SDK for the Finvalda (FVS) accounting software web service API.

Built from the official Finvalda API documentation.

Table of Contents

Requirements

Installation

Quick Start

Configuration

Basic Configuration

Laravel Integration

The package auto-registers via Laravel package discovery. Add your credentials to .env:

Publish the config file (optional):

Then inject or use the facade:

Company-Scoped Clients

companyId sets the CompanyID header on every request. Some things are registered per company — report templates in particular — so an individual call sometimes needs a different company than the one the client was configured with:

Both return a client that shares this one's transport and observability state — logger, debug capture and recorder — so company-scoped calls show up in getLastDebugInfo() and recordings() whether logging, debug or recording was switched on before or after the company client was created, and turning any of them off reaches both. A custom HttpClient you injected keeps being used. Repeated calls for the same company return the same client, so calling this in a loop over one company is fine — but each distinct company you pass is retained for the parent's lifetime, which matters when the parent is a long-lived singleton (e.g. the Laravel binding).

FinvaldaConfig::withCompanyId() does the same at the config level.

Logging

Enable PSR-3 logging for request/response debugging:

File payloads are elided by default

Report endpoints answer with the whole document base64'd into the response (~58 KB for a typical invoice PDF), and documents()->uploadFile() sends one the other way as hex, at twice the file's size. Both fit inside the byte budget, so both used to be logged in full, and neither is readable. They are now replaced with "data":"[elided 58000 bytes]" in log records only:

In Laravel: FINVALDA_LOG_FILE_CONTENTS=true and FINVALDA_LOG_BODY_BYTES.

Only string values are elided, so a structured data array is untouched, and only values above 512 bytes qualify. Recording is unaffected — $finvalda->record() still captures bodies verbatim, because you reach for it precisely when you need the bytes, and it is bounded and opt-in.

Both records are logged at debug level. Finvalda API request includes method, endpoint, parameters, and the full request body (body, string or null for GET). Finvalda API response includes method, endpoint, status code, response time, and the full response body (body). Bodies larger than 100 KB are truncated with a ... [truncated N bytes] marker — route the SDK's debug-level records to a suitable handler if log volume is a concern.

Logging to a file without a logging framework

JsonLinesLogger is a PSR-3 sink that appends one JSON object per line — enough to grep with jq, and no dependency beyond the psr/log the SDK already requires:

Each entry carries ts (ISO 8601, milliseconds, with offset), pid, level and message, plus the context keys merged in flat; a context key colliding with one of those four is written prefixed, e.g. context_message. The pid matters when several processes append to one file: LOCK_EX keeps lines intact but interleaves them, so group by pid rather than by adjacency. Missing directories are created. There is no rotation (use logrotate), no buffering and no level filter. A failing sink cannot break an API call: the first failure on each logger instance is reported through the PHP error log and the rest are silent. Credentials are already redacted before a record reaches any logger, so the sink does not redact again. The file still holds full request and response bodies — client names, debts, invoice contents — so it is created 0640, and the permissions of a file that already exists are left alone. Place it where only operators who should see that data can reach it; the directory is yours to lock down.

In Laravel, set FINVALDA_LOG_PATH=/var/log/finvalda/finvalda.log instead of constructing the logger by hand — FINVALDA_LOG_CHANNEL takes precedence when both are set.

Debug Mode

Capture full request/response details for troubleshooting:

Recording Requests

Debug mode holds only the last exchange, as arrays, and captures nothing when a request fails. Recording keeps a short history of exchanges as objects that render themselves — including failed attempts and each retry.

Credential modes:

Mode Output Use it when
CredentialMode::Masked (default) Password: *** Reading recordings, pasting them into an issue
CredentialMode::Env Password: $FVS_PASSWORD You want a runnable curl without printing the secret — export the variables first
CredentialMode::Real Password: s3cret Local debugging only, never in production

In Laravel, enable it per environment without touching code:

The formatted rendering pretty-prints JSON and expands the payload the API carries in xmlstring, so a write operation is readable at a glance:

toCurl() keeps the body exactly as sent, so the command reproduces the call:

Under CredentialMode::Env the quoting is placeholder-aware, so the command runs as-is once the variables are exported and the secret never appears in the output:

The placeholders are $FVS_PASSWORD (the Password header), $FVS_CONN_STRING (the ConnString header), and $FVS_SPASSWORD (the sPassword query parameter that $finvalda->references()->user() sends to GetFvsUser — a different secret from the connection password, which is why it gets its own name). The SDK only emits them — it never reads them from the environment.

Each Exchange exposes method, url, headers, body, statusCode, reasonPhrase, responseHeaders, responseBody, durationMs, error, and attempt, plus toString(), toCurl(), toArray(), and withCredentials().

Worth knowing:

Retry Policy

Configure automatic retries for transient failures:

Custom HTTP Client (Testing)

Inject a custom Guzzle client for testing or custom configuration:

Typed DTOs & Collections

The SDK provides typed Data Transfer Objects for better IDE support and type safety.

Finding Entities

Use find() to get a single entity as a typed DTO with full IDE autocomplete:

Working with Collections

Use collect() to get typed collections with powerful filtering and transformation methods:

Fluent Operation Builders

Create operations using an intuitive fluent interface instead of complex nested arrays.

Creating a Sale

Using Line DTOs (Recommended for Accounting)

For operations that need VAT, amounts in EUR, objects per line, or other detail fields, use ProductLine and ServiceLine DTOs for full IDE discoverability:

The product() / service() methods accept line DTOs. The existing addProduct() / addService() / addProductLine() / addServiceLine() methods still work — you can mix both styles in the same builder.

Available ProductLine methods: price(), amount(), vat(), discount(), warehouse(), object(), objects(), vatCode(), intrastat(), weight(), firstMeasurement(), secondMeasurement(), info(), marked(), set()

Available ServiceLine methods: price(), amount(), vat(), discount(), object(), objects(), vatCode(), description(), firstMeasurement(), info(), marked(), set()

Quantity convention (important)

Every product/service code (sKodas) is configured in Finvalda with a measurement unit that has two dimensions — a first (primary) unit, a second unit, and a first/second ratio (pirm_antr_sant, from references()->measurementUnits()). Examples: M → first = m, second = cm, ratio 100; KG → first = kg, second = g, ratio 1000; VNT → ratio 1. The nPirmasMat flag selects which dimension nKiekis is read in:

nPirmasMat How Finvalda reads nKiekis
1 (sent) In the first (primary) unit, verbatim. 250 on an "M" product = 250 m.
absent In the second unit, then rescaled by the unit's ratio. 250 on an "M" product = 250 cm = 2.5 m.

The second-measurement default is not a no-op — Finvalda divides by the ratio. VNT products (ratio 1) are unaffected, which is why piece quantities never exposed this. All official Finvalda API examples send product lines with "nPirmasMat":"1".

Services additionally use a ×100 fixed-point encoding for the second measurement (1 → 100, 0.5 → 50); products do not. From the spec:

Service nKiekis — paslaugos kiekis antru matavimu (integer) arba pirmu jei nurodyta nPirmasMat=1. Kiekis padaugintas iš 100. Jeigu reikalingas kiekis 0.5 tada nKiekis = 50, jeigu reikalingas kiekis 1 tada nKiekis = 100. Jeigu naudojamas pirmas matavimas dauginti nereikia.

Product nKiekis — prekės kiekis antru matavimu (integer) arba pirmu jei nurodyta nPirmasMat=1. (no ×100)

What the SDK does for you
Builder helper Default Opt-out
ProductLine::make($code, $qty) First measurement: emits nKiekis = $qty verbatim and nPirmasMat = 1 ->secondMeasurement() (or ->firstMeasurement(false)) drops nPirmasMat so Finvalda rescales by the unit ratio
ServiceLine::make($code, $qty) Second measurement: emits nKiekis = round($qty × 100) ->firstMeasurement() emits nKiekis = $qty as-is and nPirmasMat = 1
addProduct() Emits nKiekis verbatim and nPirmasMat = 1 Pass additionalData: ['nPirmasMat' => 0], or use addProductLine() for raw control
addService() Emits nKiekis verbatim (no scaling, no nPirmasMat) Pass nPirmasMat via additionalData
addProductLine() / addServiceLine() Emit the line array verbatim (no defaults) —

Why product lines default to nPirmasMat=1: real quantities are expressed in the primary unit (you book "250 metres", not "25000 cm"). Omitting the flag silently divided M/KG quantities by their ratio. Both ProductLine::make() and the addProduct() helper default to the primary unit; addProductLine() stays a raw passthrough for full control.

For rare/niche API fields not covered by named methods, use the set() escape hatch:

Creating a Purchase

Document type, series & the operation parameter

series(), documentType(), and the save() parameter are three independent inputs to a create call — none of them is derived from the others:

Builder method API field Controls
documentType() sDokRusis The document type (see codes below)
series() sSerija The document series
save('STANDARD') sParametras The server-configured import/journal profile

documentType() accepts a DocumentType enum case or a raw 2-character code:

Code DocumentType case Meaning
S Invoice Sąskaita faktūra
SF VatInvoice PVM sąskaita faktūra
D DebitInvoice Debetinė sąskaita
DS DebitVatInvoice Debetinė PVM sąskaita
K CreditInvoice Kreditinė sąskaita
KS CreditVatInvoice Kreditinė PVM sąskaita faktūra
KT Other Kita
VS LawyerVatInvoice Advokatų PVM sąskaita faktūra
VD LawyerVatInvoiceDebit Advokatų PVM sąskaita faktūra debetinė
VK LawyerVatInvoiceCredit Advokatų PVM sąskaita faktūra kreditinė

These methods are available on sale(), salesReservation(), salesReturn(), purchase(), purchaseOrder(), and purchaseReturn().

sParametras is required and cannot be bypassed. The operation type (purchase vs. sale, i.e. ItemClassName) is what you pick by choosing the builder, and you set sDokRusis/sSerija yourself — but the journal an operation lands in is resolved server-side from the sParametras profile you pass to save(). There is no header field to specify the journal directly on create; the resulting journal/number come back on the OperationResult.

Additional Purchase Costs

A purchase header can declare up to four additional-cost buckets (papildomos išlaidos) — named categories such as freight, registration or insurance — whose amounts are then allocated per product line. The two halves are one feature: bucket codes without per-line amounts book nothing, and per-line amounts without bucket codes have nowhere to land.

Method API fields Notes
additionalCostCodes([...]) sPapIslaiduKodas1..4 Ordered list fills slots 1-4 in order; a slot-keyed map ([2 => 'TRANSP']) sets individual slots. Max 4 codes, 10 chars each.
ProductLine::additionalCost($slot, $currency, $local) dPapIsldSumaV{slot}, dPapIsldSumaL{slot} Always writes both the currency and EUR halves; $local defaults to $currency.
ProductLine::additionalCosts([$slot => $amount]) same Plural convenience, keyed by slot.

The slot number is the binding. Slot 2 in the header is what slot 2 on the line allocates to. Slots are positional and server-configured — do not reorder them between bookings of the same journal, or previously booked allocations stop lining up with their buckets.

Availability follows the spec exactly:

The SDK does not round the amounts (the fields are Numeric(14,2)) and cannot check that the header actually declares a code in the slot you allocate to — the line object cannot see the header.

Correcting a Purchase

purchaseUpdate() builds the KoregPirkDok envelope for UpdateOperation.

⚠️ A correction is destructive, not an edit

KoregPirkDok corrects an operation by deleting the named detail lines and re-adding the ones you supply. Re-adding a product line rebuilds that product's FIFO stock layer, and the internal delete fails outright once the goods have been consumed by another operation — a sale, write-off, transfer or production run. The error is 4027 — Operacijos detalios eilutės yra panaudotos kitose operacijose! Note it is documented under operation deletion errors, not the 5000–5005 correction family, so an update call can return a deletion-class code.

Three things worth knowing before you ship a caller:

  1. It is not idempotent. A re-added line is a new acquisition with a new cost layer. Re-sending the same correction is not a no-op.
  2. It fails late. Unsold stock corrects fine; the same code path starts failing the day someone sells the goods.
  3. Do not rely on partial success. If a multi-line correction rejects on one consumed line, re-read the operation before retrying.

Check Stock::purchaseOpFor($code)['sold'] first, or call assertNotSold(). UpdPrekeDetEil is not an escape hatch: the spec gives it only sKodas, nKodasN, nPozymis and sPapInfo, so it can flip a line's marked flag and free text but cannot restate amounts. There is no way to re-allocate costs without the delete/re-add cycle.

This emits the documented envelope — note that it is not the insert envelope:

Method Node Notes
journal() / number() sZurnalas, nNumeris Both required — they identify the operation to correct.
header([...]) PirkDokHeadEil Raw field names, merged across calls, validated against the documented column set. Omit to leave the header alone.
additionalCostCodes([...]) PirkDokHeadEil Same method as on insert (Tik KoregPirkDok ir KoregPirkUzsDok).
removeProduct($code, $warehouse) DelPrekeDetEil Warehouse optional.
removeService($code) DelPaslaugaDetEil
product(ProductLine) PirkDokPrekeDetEil Requires sKodas, sSandelis, dSumaV, dSumaL, nKiekis.
service(ServiceLine) PirkDokPaslaugaDetEil Requires sKodas, dSumaV, dSumaL, nKiekis.
assertNotSold() — One purchaseOpFor() round trip per distinct product code; throws ConflictException when a touched product is sold.

What header() deliberately refuses, because PirkDokHeadEil does not accept it for a purchase:

assertNotSold() fails closed: a product whose purchase history cannot be resolved is refused too, since a guard on a destructive call must not pass just because the lookup failed. It is deliberately not run by save() — it costs a round trip per code and hides a decision the caller should be making.

Only purchases are covered. The other six UpdateOperationClass cases share the envelope shape but each has its own Tik ... annotations; use operations()->update() with a hand-built payload for those.

Creating an Internal Transfer

An internal transfer carries a single source/destination warehouse pair at the header level — the detail rows have no per-line warehouse fields. To move stock between different warehouse pairs, create separate transfer operations.

Creating Returns

Note: In live testing the full PardGrazDok variant was rejected with error 2012 ("Xml string is incomplete") even with a spec-correct payload, while the same fields via ->short() (TrumpasPardGrazDok) succeeded. If a full-variant return fails with 2012, use ->short() — it is the shape proven to work.

The originalDocument() / reason() fields (sGrazDokumentas, sGrazZurnalas, nGrazNumeris, sGrazPriezastis) do not appear in the official FVS spec. Finvalda silently ignores unknown fields, so verify against your server that the linkage actually lands before relying on it.

Creating Payments

Builder Advanced Usage

Write-Offs & Capitalization

Creating a Production Operation

Non-Analytical Operations

Inventory Count

Clearing / Set-Off

UVM (Order Management)

Short / Simplified Operations

All sales, purchase, and return builders support a short() mode that uses simplified operation variants. Short operations send minimal headers and let the server fill in defaults.

Builders supporting short(): sale(), salesReservation(), salesReturn(), purchase(), purchaseOrder(), purchaseReturn().

Query Builders

Build queries fluently for better readability and IDE support.

Transaction Query

Operation Query

Validation

Validate data before sending to the API to catch errors early:

The lengths above are illustrative. For the real per-field maximum lengths, see the Field Reference — e.g. a client sKodas is max 15 chars, sPavadinimas max 100.

Field Reference

The API enforces per-field maximum text lengths and numeric precision, and marks each field as mandatory or auto-filled from the server parameter profile. This is documented exhaustively in the official spec, which is included in this repo:

→ docs/FVS_Webservice.md (the single source of truth)

Look up the write payload you're building:

Each field is a table row with these columns: type · field name · description · max length · required · auto-filled-from-parameter · notes. A + in the required column means mandatory; a + in the auto-filled column means the webservice supplies it from the parameter profile when omitted. For example, client sKodas is String max 15, required; sPavadinimas max 100; sEMail max 30, optional. Money amounts are Numeric (14,2).

Resources

All read methods return a Response object:

All write methods return an OperationResult object:

Stock / Inventory

Which purchase operation holds this stock?

For serialised, quantity-1 stock (a VIN, a serial number), purchaseOpFor() answers two questions that otherwise get re-derived by every caller: which purchase operation currently holds it, and has it been sold since. Both come from GetPrekesIstorija.

Clients

Products

Services

Types and tags (rūšys ir požymiai)

Products, clients and services each have a "type" (rūšis) plus a number of "tag" groups (požymiai). Finvalda exposes them through one endpoint per entity:

Entity Endpoint Accessor
Products GetPrekiuRusisPozymius $finvalda->products()
Clients GetKlientuRusisPozymius $finvalda->clients()
Services GetPaslauguRusisPozymius $finvalda->services()

One call returns the whole dictionary. Each endpoint returns every type and every tag group in a single response. The rows are discriminated by a tipas column. The legacy nID request parameter is ignored by the server — passing different values returns byte-identical results — so the SDK does not send it and filters by tipas client-side instead.

tipas → field mapping (note the non-sequential numbering for clients/services):

Entity Type Tag1 Tag2 Tag3 Tag4 Tag5 Tag6 Tag9 Tag10 Tag11
Products 0 1 2 3 4 5 6 9 10 11
Clients 22 12 13 14 — — — — — —
Services 18 15 16 17 — — — — — —

These integers are the ProductTypeId / ClientTypeId / ServiceTypeId enum values. Servers may define additional tipas values that have no enum case — for example products often expose tipas = 100 ("Apmokestinamieji gaminiai"). Pass those as a raw int. A tag group the server has not configured simply yields an empty collection; that is normal and not an error.

Returned columns (mapped onto the TypeTag DTO):

Column DTO property Notes
tipas ->tipas int discriminator (see above)
kodas ->code the code you reference
pavadinimas ->name display name
info1 ->info1 products only
info2 ->info2 products only

typesAndTags() and allTypesAndTags() share a single cached request, so calling both (or several filtered reads) on the same resource instance does not fan out into multiple round-trips.

Creating, updating and deleting types and tags. The dictionary read above is read-only; manage entries via References (create/update/delete for product and client types and tags — see the Reference Data section):

The kodas returned by typesAndTags() is exactly what you pass into Products::create() as sRusis (type) and sPozymis1..N (tags):

Objects (6 Levels)

Transactions (Financial Detail Data)

Operations (Create, Update, Delete)

Operations require a $parameter argument which is server-configured. See Server-Configured Parameters.

Order Management (UVM)

Pricing & Discounts

Documents

Reports & Invoices

Descriptions (Universal Query)

Reference Data

User Permissions

Pagination

For large datasets, use lazy pagination with the Cursor class:

Error Handling

Server-Configured Parameters

Finvalda uses server-configured parameters that depend on your installation.

sParametras (Operations)

Required for operation methods (create, update, delete). Tells the server which journal configuration to use.

Deeper reference — docs/parameters/: explains what a sParametras profile actually contains (journal, operation type, series, document type, accounts, VAT, division, employee, Intrastat data, flags), how it is configured in the FvsNETParamKonfig tool, and a YAML format (parameters.example.yaml) for cataloguing your own profiles. With that catalog filled in, an AI assistant can match a transaction to the right profile, explain a profile, or draft FvsNETParamKonfig setup instructions for a new one. Keep deployment-specific values out of version control (the catalog file is git-ignored).

sFvsImportoParametras (Items)

Optional data field for item methods. Include in data array if required:

Troubleshooting Parameter Errors

If you receive an error like:

This means the parameter is not configured on your server. Contact your Finvalda administrator for valid parameter values.

API Versions

This SDK targets V2 (FvsServicePure) - the recommended REST interface.

Version URL Pattern Description
V2 (recommended) .../FvsServicePure.svc Clean REST JSON/XML
V1 .../FvsServiceR.svc/rest REST with string-wrapped responses
V0 .../FvsService.asmx SOAP + REST XML

Keeping Up to Date

The SDK is built from the official Postman collection. To check for new endpoints:

Note on parameter names. The legacy method signatures in docs/FVS_Webservice.txt describe the older V0 (SOAP) interface and do not always match the V2 FvsServicePure endpoint this SDK targets. For example, GetPrekesSandelyje is documented with sSanKod but the V2 endpoint actually honors sSandKod (verified against a live server). When a query filter appears to be silently ignored, confirm the exact parameter name against a live server rather than trusting the .txt signature.

License

MIT


All versions of finvalda-sdk with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
guzzlehttp/guzzle Version ^7.0
psr/log Version ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package arturas88/finvalda-sdk contains the following files

Loading the files please wait ...