arturas88/finvalda-sdk

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

Maintainers

Package info

github.com/arturas88/finvalda-sdk

pkg:composer/arturas88/finvalda-sdk

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-03-22 17:30 UTC

This package is auto-updated.

Last update: 2026-03-22 17:32:45 UTC


README

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

Built from the official Finvalda API documentation.

Requirements

  • PHP >= 8.3
  • Guzzle HTTP client

Installation

composer require arturas88/finvalda-sdk

Configuration

use Finvalda\Finvalda;
use Finvalda\FinvaldaConfig;
use Finvalda\Enums\Language;

$config = new FinvaldaConfig(
    baseUrl: 'https://your-server.com/FvsServicePure.svc',
    username: 'your-username',
    password: 'your-password',
    // Optional:
    connString: null,           // Database connection string
    companyId: null,            // Company ID for multi-database setups
    language: Language::Lithuanian, // or Language::English
    removeEmptyStringTags: false,
    removeZeroNumberTags: false,
    removeNewLines: false,
    timeout: 30,
);

$finvalda = new Finvalda($config);

Laravel Integration

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

FINVALDA_BASE_URL=https://your-server.com/FvsServicePure.svc
FINVALDA_USERNAME=your-username
FINVALDA_PASSWORD=your-password
FINVALDA_COMPANY_ID=your-company-id

Publish the config file (optional):

php artisan vendor:publish --tag=finvalda-config

Then inject or use the facade:

// Dependency injection
public function index(Finvalda\Finvalda $finvalda)
{
    $clients = $finvalda->clients()->list();
}

// Facade
use Finvalda\Laravel\Facades\Finvalda;

$clients = Finvalda::clients()->list();

Custom HTTP Client (Testing)

You can inject a custom Guzzle client for testing or custom configuration:

use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;

$mock = new MockHandler([
    new Response(200, [], json_encode(['AccessResult' => 'Success', 'items' => []])),
]);

$httpClient = new \Finvalda\HttpClient($config, new Client(['handler' => HandlerStack::create($mock)]));

Usage

All methods return a Response object with:

  • $response->successful() - whether the request succeeded
  • $response->data - the response data array
  • $response->error - error message if failed
  • $response->raw - full raw response array

Stock / Inventory

// Current stock balances
$response = $finvalda->stock()->balances();
$response = $finvalda->stock()->balances(productCode: 'PROD001', warehouseCode: 'WH01');

// Extended balances (includes product type, tags)
$response = $finvalda->stock()->balancesExtended();

// Balances with selling prices
$response = $finvalda->stock()->balancesWithPrices(includeZeroQuantity: true);

// Balances by warehouse group
$response = $finvalda->stock()->balancesByGroup(warehouseGroupCode: 'GROUP1');

// Ordered products
$response = $finvalda->stock()->orderedProducts();

Clients

// List clients
$response = $finvalda->clients()->list();
$response = $finvalda->clients()->list(modifiedSince: '2024-01-01');

// Get single client
$response = $finvalda->clients()->get('CLIENT001');

// All clients (with optional date filters)
$response = $finvalda->clients()->all();
$response = $finvalda->clients()->all(modifiedSince: '2024-01-01');

// Client email
$response = $finvalda->clients()->email('CLIENT001');

// Client types and tags
$response = $finvalda->clients()->typesAndTags();

// Clients by type
$response = $finvalda->clients()->byType('VIP');

// Accounts (with full filter support)
$response = $finvalda->clients()->accounts(clientCode: 'CLIENT001');
$response = $finvalda->clients()->accounts(
    clientCode: 'CLIENT001',
    journalGroup: 'PARD',
    debtType: 1,
    documentDateFrom: '2024-01-01',
    documentDateTo: '2024-12-31',
);

// Unpaid documents (sales and purchases)
$response = $finvalda->clients()->unpaidDocuments('CLIENT001');
$response = $finvalda->clients()->unpaidPurchaseDocuments('CLIENT001');

// Client debt condition
$response = $finvalda->clients()->debtCondition('CLIENT001', journalGroup: 'PARD');

// Settlements (with journal/number support)
$response = $finvalda->clients()->settlements(series: 'SER', document: 'DOC001');
$response = $finvalda->clients()->settlements(journal: 'PARD', number: 123);

// Detailed settlements
$response = $finvalda->clients()->settlementsDetailed(
    series: 'SER',
    document: 'DOC001',
    operationId: 1,
    operationClass: 2,
);

// Settlements from date
$response = $finvalda->clients()->settlementsFromDate(
    series: 'SER',
    document: 'DOC001',
    modifiedSince: '2024-01-01',
);

// Settlements with XML parameter
$response = $finvalda->clients()->settlementsFromDateParam('<params>...</params>');

// Create / update client
$result = $finvalda->clients()->create([
    'sKodas' => 'NEW001',
    'sPavadinimas' => 'New Client Ltd',
    'sDebtSask' => '2410',
    'sKredSask' => '5001',
    'sPvmMokKod' => 'LT',
]);

$result = $finvalda->clients()->update([
    'sKodas' => 'NEW001',
    'sPavadinimas' => 'Updated Client Name',
]);

// Delete a client
$result = $finvalda->clients()->delete('CLIENT001');

// Invoices related to a customer
$response = $finvalda->clients()->invoicesRelatedToCustomer('CLIENT001', debtType: 0);

Products

// List products
$response = $finvalda->products()->list();

// Extended list with filters
$response = $finvalda->products()->listExtended(
    type: 'ELECTRONICS',
    supplier1: 'SUPP01',
    modifiedSince: '2024-01-01',
);

// Single product
$response = $finvalda->products()->get('PROD001');

// All products (with optional date filters)
$response = $finvalda->products()->all(modifiedSince: '2024-01-01');

// Product image
$response = $finvalda->products()->image('PROD001');

// Products in warehouse (with date filters)
$response = $finvalda->products()->inWarehouse('WH01', modifiedSince: '2024-01-01');
$response = $finvalda->products()->inWarehouseOrdered('WH01', order: 1);

// Types, tags, and type groups
$response = $finvalda->products()->typesAndTags();
$response = $finvalda->products()->typeGroups();
$response = $finvalda->products()->typeGroupComposition('GRP01');

// Product history
$response = $finvalda->products()->history('PROD001', dateFrom: '2024-01-01');

// Sold products per period
$response = $finvalda->products()->soldPerPeriod(
    productCode: 'PROD001',
    warehouseCode: 'WH01',
    dateFrom: '2024-01-01',
    dateTo: '2024-12-31',
    salesJournalCode: 'PARD',
);

// Create / update
$result = $finvalda->products()->create([
    'sKodas' => 'NEWPROD',
    'sPavadinimas' => 'New Product',
    'sRysysSuSask' => '2414',
    'sMatavimoVnt' => 'vnt',
    'sMokestis' => 'PVM1',
]);

// Update a product
$result = $finvalda->products()->update([
    'sKodas' => 'PROD001',
    'sPavadinimas' => 'Updated Product Name',
]);

// Batch edit properties
$result = $finvalda->products()->editProperties('PROD001', [
    'dKaina1' => 19.99,
    'sRusis' => 'ELECTRONICS',
]);

// Delete a product
$result = $finvalda->products()->delete('PROD001');

Services

$response = $finvalda->services()->list();
$response = $finvalda->services()->get('SVC001');
$response = $finvalda->services()->all(modifiedSince: '2024-01-01');
$response = $finvalda->services()->typesAndTags();
$response = $finvalda->services()->byType('CONSULTING');

$result = $finvalda->services()->create([
    'sKodas' => 'NEWSVC',
    'sPavadinimas' => 'New Service',
    'sRysysSuSask' => '5001',
    'sMokestis' => 'PVM1',
]);

$result = $finvalda->services()->update(['sKodas' => 'SVC001', 'sPavadinimas' => 'Updated']);
$result = $finvalda->services()->delete('SVC001');

Objects (6 Levels)

// List objects at level 1-6
$response = $finvalda->objects()->list(level: 1);
$response = $finvalda->objects()->list(level: 2, objectCode: 'OBJ001');

// Get single object
$response = $finvalda->objects()->get(level: 1, objectCode: 'OBJ001');

// Create / update
$result = $finvalda->objects()->create(level: 1, data: [
    'sKodas' => 'DEPT01',
    'sPavadinimas' => 'Sales Department',
]);

$result = $finvalda->objects()->update(level: 1, data: [
    'sKodas' => 'DEPT01',
    'sPavadinimas' => 'Updated Department',
]);

Transactions (Financial Detail Data)

Read transaction details using typed filter DTOs:

use Finvalda\Filters\TransactionFilter;
use Finvalda\Filters\PaymentFilter;

// Create a reusable filter
$filter = new TransactionFilter(
    dateFrom: '2024-01-01',
    dateTo: '2024-12-31',
    journalGroup: 'PARD_GRP',
);

// Sales (headers, detail lines, detail with prime cost)
$response = $finvalda->transactions()->sales($filter);
$response = $finvalda->transactions()->salesDetail(new TransactionFilter(journal: 'PARD', opNumber: 123));
$response = $finvalda->transactions()->salesDetailWithPrimeCost($filter);

// Sale Reservations
$response = $finvalda->transactions()->saleReservations($filter);
$response = $finvalda->transactions()->saleReservationsDetail($filter);

// Sales Returns
$response = $finvalda->transactions()->salesReturns($filter);
$response = $finvalda->transactions()->salesReturnsDetail($filter);

// DateTimeInterface is also accepted
$filter = new TransactionFilter(
    dateFrom: new \DateTime('2024-01-01'),
    dateTo: new \DateTimeImmutable('2024-12-31'),
);

// Purchases (headers, detail, extended detail)
$response = $finvalda->transactions()->purchases($filter);
$response = $finvalda->transactions()->purchasesDetail($filter);
$response = $finvalda->transactions()->purchasesExtendedDetail($filter);

// Purchase Orders & Returns (headers + detail)
$response = $finvalda->transactions()->purchaseOrders($filter);
$response = $finvalda->transactions()->purchaseOrdersDetail($filter);
$response = $finvalda->transactions()->purchaseReturns($filter);
$response = $finvalda->transactions()->purchaseReturnsDetail($filter);

// Inflows with payment reference filter
$response = $finvalda->transactions()->inflowsDetail(
    filter: $filter,
    paymentFilter: new PaymentFilter(
        payedForDocSeries: 'AA',
        payedForDocOrderNumber: 'SF-001',
    ),
);

// Advance Payments (standard + extended)
$response = $finvalda->transactions()->advancedPaymentsDetail(
    filter: $filter,
    client: 'CLIENT001',
    offsetStatus: 0,
);
$response = $finvalda->transactions()->advancedPaymentsDetailExtended(filter: $filter);

// Disbursements, Clearing Offsets
$response = $finvalda->transactions()->disbursementsDetail($filter);
$response = $finvalda->transactions()->clearingOffsDetail($filter);

// OMM (Order Management Module) — sales + purchases, headers + detail
$response = $finvalda->transactions()->ommSales($filter);
$response = $finvalda->transactions()->ommSalesDetail($filter);
$response = $finvalda->transactions()->ommSalesXmlCondition('<condition>...</condition>');
$response = $finvalda->transactions()->ommSalesXmlConditionWithTitle('<condition>...</condition>');
$response = $finvalda->transactions()->ommPurchases($filter);
$response = $finvalda->transactions()->ommPurchasesDetail($filter);

// Fixed Assets & Currency
$response = $finvalda->transactions()->depreciationOfFixedAssets(year: 2024, month: 6);
$response = $finvalda->transactions()->depreciationOfFixedAssetsObjects(assetCode: 'ASSET01');
$response = $finvalda->transactions()->currencyDebtRecount($filter);

// Low Value Inventory
$response = $finvalda->transactions()->lowValueInventory();

Operations (Create, Update, Delete)

use Finvalda\Enums\OperationClass;
use Finvalda\Enums\DeleteOperationClass;
use Finvalda\Enums\UpdateOperationClass;
use Finvalda\Enums\OpClass;

// Create a sale
$result = $finvalda->operations()->create(OperationClass::Sale, [
    'sKlientas' => 'CLIENT001',
    'tData' => '2024-06-15',
    'sDokumentas' => 'SF-001',
    'sValiuta' => 'EUR',
    'sSerija' => 'AA',
    'PardDokPrekeDetEil' => [
        [
            'sKodas' => 'PROD001',
            'sSandelis' => 'WH01',
            'nKiekis' => 10,
            'dSumaV' => 199.90,
            'dSumaL' => 199.90,
        ],
    ],
]);

if ($result->success) {
    echo "Created: {$result->journal} #{$result->number}";
}

// Create a purchase
$result = $finvalda->operations()->create(OperationClass::Purchase, [
    'sKlientas' => 'SUPPLIER001',
    'tData' => '2024-06-15',
    'sDokumentas' => 'PO-001',
    'sValiuta' => 'EUR',
    'PirkDokPrekeDetEil' => [
        [
            'sKodas' => 'PROD001',
            'sSandelis' => 'WH01',
            'nKiekis' => 100,
            'dSumaV' => 500.00,
            'dSumaL' => 500.00,
        ],
    ],
]);

// Create an internal transfer
$result = $finvalda->operations()->create(OperationClass::InternalTransfer, [
    'tData' => '2024-06-15',
    'VidPerkDokDetEil' => [
        [
            'sKodas' => 'PROD001',
            'sSandelisIs' => 'WH01',
            'sSandelisI' => 'WH02',
            'nKiekis' => 50,
        ],
    ],
]);

// Delete an operation
$result = $finvalda->operations()->delete(
    DeleteOperationClass::Sale,
    journal: 'PARD',
    number: 123,
);

// Update an operation
$result = $finvalda->operations()->update(UpdateOperationClass::Sale, [
    'sZurnalas' => 'PARD',
    'nNumeris' => 123,
    'PardDokHeadEil' => [
        'sPastaba' => 'Updated comment',
    ],
]);

// Read operations with filters (via GetOperations)
$response = $finvalda->operations()->get(OpClass::Sales, [
    'OpDateFrom' => '2024-01-01',
    'OpDateTill' => '2024-12-31',
    'Client' => 'CLIENT001',
]);

// Read with JSON body (supports complex filters, column selection, fullOp)
$response = $finvalda->operations()->query(OpClass::Sales, [
    'OpDateFrom' => '2024-01-01',
    'OpDateTill' => '2024-12-31',
    'fullOp' => true,
    'columns' => ['sKlientas', 'tData', 'sDokumentas'],
]);

// Lock / unlock operations
$finvalda->operations()->lock('PARD', 123);
$finvalda->operations()->unlock('PARD', 123);
$response = $finvalda->operations()->isLocked('PARD', 123);

// Change journal of an operation
$result = $finvalda->operations()->changeJournal([
    'sZurnalas' => 'PARD',
    'nNumeris' => 123,
    'sNaujasZurnalas' => 'PARD2',
]);

// Copy an operation
$result = $finvalda->operations()->copy([
    'sZurnalas' => 'PARD',
    'nNumeris' => 123,
]);

// Activity report by objects
$response = $finvalda->operations()->activityByObjects([
    'DateFrom' => '2024-01-01',
    'DateTo' => '2024-12-31',
]);

Order Management (UVM)

// Sales reservation status by journal + number
$response = $finvalda->orderManagement()->salesReservationStatus('PARD', 123);

// List reservations with date range filters
$response = $finvalda->orderManagement()->completedReservations(
    journalGroup: 'PARD_GRP',
    dateFrom: '2024-01-01',
    dateTo: '2024-12-31',
);
$response = $finvalda->orderManagement()->pendingReservations();
$response = $finvalda->orderManagement()->cancelledReservations();

// Ordered products from reservations
$response = $finvalda->orderManagement()->orderedProducts(dateFrom: '2024-01-01');

Pricing & Discounts

All pricing methods support modifiedSince and createdSince date filters.

// Combined client + item prices (discounts and additional prices)
$response = $finvalda->pricing()->clientItemPrices(clientCode: 'CLI001', itemCode: 'PROD001');
$response = $finvalda->pricing()->clientTypeItemPrices(clientTypeCode: 'VIP', itemCode: 'PROD001');
$response = $finvalda->pricing()->clientItemTypePrices(clientCode: 'CLI001', itemTypeCode: 'ELECTRONICS');
$response = $finvalda->pricing()->clientTypeItemTypePrices(
    clientTypeCode: 'VIP',
    itemTypeCode: 'ELECTRONICS',
);

// Product-specific (client + product, client + product type)
$response = $finvalda->pricing()->clientProductDiscounts('CLI001', modifiedSince: '2024-01-01');
$response = $finvalda->pricing()->clientProductAdditionalPrices('CLI001');
$response = $finvalda->pricing()->clientProductTypeDiscounts('CLI001');
$response = $finvalda->pricing()->clientProductTypeAdditionalPrices('CLI001');

// Service-specific (client + service, client + service type)
$response = $finvalda->pricing()->clientServiceDiscounts('CLI001');
$response = $finvalda->pricing()->clientServiceAdditionalPrices('CLI001');
$response = $finvalda->pricing()->clientServiceTypeDiscounts('CLI001');
$response = $finvalda->pricing()->clientServiceTypeAdditionalPrices('CLI001');

// Client TYPE + product/service
$response = $finvalda->pricing()->clientTypeProductDiscounts('VIP');
$response = $finvalda->pricing()->clientTypeProductAdditionalPrices('VIP');
$response = $finvalda->pricing()->clientTypeProductTypeDiscounts('VIP');
$response = $finvalda->pricing()->clientTypeProductTypeAdditionalPrices('VIP');

// Client TYPE + service
$response = $finvalda->pricing()->clientTypeServiceDiscounts('VIP');
$response = $finvalda->pricing()->clientTypeServiceAdditionalPrices('VIP');
$response = $finvalda->pricing()->clientTypeServiceTypeDiscounts('VIP');
$response = $finvalda->pricing()->clientTypeServiceTypeAdditionalPrices('VIP');

// Recommended price calculation
$response = $finvalda->pricing()->recommendedPrice([
    'invoiceType' => 0, // 0=sales, 1=purchases
    'invoiceDate' => ['year' => 2024, 'month' => 6, 'day' => 15],
    'itemType' => 1,    // 1=product, 2=service
    'itemCode' => 'PROD001',
    'itemAmount' => 10,
    'warehouseCode' => 'WH01',
    'clientCode' => 'CLI001',
]);

Documents

use Finvalda\Enums\DocumentEntityType;

// Upload from file path
$result = $finvalda->documents()->uploadFile('invoice.pdf', '/path/to/invoice.pdf');

// Upload hex-encoded content directly
$result = $finvalda->documents()->upload('doc.pdf', $hexContent);

// Attach to entity
$result = $finvalda->documents()->attach(
    DocumentEntityType::Sale,
    entityCode: 'CLI001',
    filename: 'invoice.pdf',
    journal: 'PARD',
    number: 123,
);

// Get attached documents
$response = $finvalda->documents()->attached(DocumentEntityType::Client, 'CLI001');

// Delete
$result = $finvalda->documents()->delete('invoice.pdf');

Reports & Invoices

// Generate invoice PDF (sParam contains serialized operation selection)
$response = $finvalda->reports()->makeInvoice('param_string');

// Generate report PDF
$response = $finvalda->reports()->makeReport('param_string');

// List auto-reports
$response = $finvalda->reports()->autoReports();
$response = $finvalda->reports()->autoReport('report_filename.pdf');

Descriptions (Universal Query)

use Finvalda\Enums\DescriptionType;

// Generic query with pagination and column selection
$response = $finvalda->descriptions()->get(DescriptionType::Products, [
    'Codes' => ['PROD001', 'PROD002'],
], page: 1, limit: 50);

// Convenience methods
$response = $finvalda->descriptions()->stockOnDate('2024-06-15', ['Warehouse' => 'WH01']);
$response = $finvalda->descriptions()->products(['Type' => 'ELECTRONICS'], page: 1, limit: 100);
$response = $finvalda->descriptions()->clients(['Email' => 'client@example.com']);
$response = $finvalda->descriptions()->services();
$response = $finvalda->descriptions()->currentStock(['Warehouse' => 'WH01']);
$response = $finvalda->descriptions()->fixedAssets();
$response = $finvalda->descriptions()->barCodes(['Codes' => ['PROD001']]);
$response = $finvalda->descriptions()->prices(['Client' => 'CLI001']);
$response = $finvalda->descriptions()->currencyRates('2024-01-01', '2024-12-31', ['USD', 'GBP']);
$response = $finvalda->descriptions()->clientGroups();
$response = $finvalda->descriptions()->warehouseGroups();
$response = $finvalda->descriptions()->logbookGroups();
$response = $finvalda->descriptions()->opTypeGroups();
$response = $finvalda->descriptions()->vehicles();
$response = $finvalda->descriptions()->calendarEvents('admin');
$response = $finvalda->descriptions()->documentSeries(type: 0);
$response = $finvalda->descriptions()->invoiceList();
$response = $finvalda->descriptions()->reportList('Pardavimai');
$response = $finvalda->descriptions()->tagsAndTypes('product', 0);

Reference Data

$response = $finvalda->references()->measurementUnits();
$response = $finvalda->references()->warehouses();
$response = $finvalda->references()->taxes();
$response = $finvalda->references()->paymentTerms();
$response = $finvalda->references()->user();
$response = $finvalda->references()->materiallyResponsiblePersons(code: 'EMP01');

// Create reference entities
$result = $finvalda->references()->createBank(['sKodas' => 'BNK01', 'sPavadinimas' => 'My Bank']);
$result = $finvalda->references()->createWarehouse(['sKodas' => 'WH03', 'sPavadinimas' => 'Warehouse 3']);
$result = $finvalda->references()->createPaymentTerm(['sKodas' => 'NET30', 'sPavadinimas' => 'Net 30']);
$result = $finvalda->references()->createClientType(['sKodas' => 'VIP', 'sPavadinimas' => 'VIP Clients']);
$result = $finvalda->references()->createProductType(['sKodas' => 'ELEC', 'sPavadinimas' => 'Electronics']);

// Update reference entities
$result = $finvalda->references()->updateWarehouse(['sKodas' => 'WH03', 'sPavadinimas' => 'Updated']);
$result = $finvalda->references()->updatePaymentTerm(['sKodas' => 'NET30', 'sPavadinimas' => 'Updated']);

// Add item to group
$result = $finvalda->references()->addToGroup('Fvs.PrekesRusis', 'GRP01', 'PROD001');

User Permissions

$response = $finvalda->permissions()->warehouses();
$response = $finvalda->permissions()->clients();
$response = $finvalda->permissions()->operationTypes();
$response = $finvalda->permissions()->operationJournals();

// Or by class ID directly
$response = $finvalda->permissions()->get(65); // warehouses

Error Handling

use Finvalda\Exceptions\FinvaldaException;
use Finvalda\Exceptions\AccessDeniedException;

try {
    $response = $finvalda->clients()->list();

    if ($response->failed()) {
        echo "Error: {$response->error}";
    }
} catch (AccessDeniedException $e) {
    echo "Access denied: {$e->getMessage()}";
} catch (FinvaldaException $e) {
    echo "API error: {$e->getMessage()}";
}

For write operations (create, update, delete), the OperationResult object provides:

$result = $finvalda->operations()->create(OperationClass::Sale, $data);

if ($result->success) {
    echo "Series: {$result->series}";
    echo "Document: {$result->document}";
    echo "Journal: {$result->journal}";
    echo "Number: {$result->number}";
} else {
    echo "Error #{$result->errorCode}: {$result->error}";
}

API Versions

This SDK targets V2 (FvsServicePure) - the recommended REST interface that returns clean JSON/XML. Configure baseUrl accordingly:

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:

bin/sync-postman-collection

This compares the latest online collection with the local copy and reports new/removed endpoints.

License

MIT