Syrve POS API SDK

External Fiscal Registers

f you can’t find the right model on the list of Syrve supported fiscal registers (FCR), you can write your own plugin. This plugin will be connected to Syrve POS and considered as an external FCR. Check the Introduction article. External FCR plugins must be licensed.

Setting Up

You can set up an external FCR in 2 steps.

Step 1: Register a new FCR model. For this, you need to implement the ICashRegisterFactory interface and register it by calling API methods:

var cashRegisterFactory = new SampleCashRegisterFactory();
PluginContext.Operations.RegisterCashRegisterFactory(cashRegisterFactory)

You need this to make your new FCR model visible in Syrve Office so you can then add it and set up as cash register equipment on your POS.

NewCashRegisterModel

Step 2: Add a new FCR model. For this, you need to implement the ICashRegister interface and add its instance to ICashRegisterFactory.Create():

class SampleCashRegisterFactory : MarshalByRefObject, ICashRegisterFactory
{
    ...
    public ICashRegister Create(Guid deviceId, [NotNull] CashRegisterSettings settings)
    {
        if (settings == null)
            throw new ArgumentNullException(nameof(settings));

        return new SampleCashRegister(deviceId, settings);
    }
}

By clicking «Complete» in the new device dialog (Syrve Office > Equipment Settings), you invoke ICashRegisterFactory.Create()which would add a new FCR to the list of equipment. After that, Syrve Office can communicate with the external FCR—send commands and receive responses.

CreateCashRegister

Fiscal Register Settings

Each unit of equipment has its own set of settings ‒ IDeviceFactory.DefaultDeviceSettings:

interface IDeviceFactory
{
    ...
    [NotNull]
    DeviceSettings DefaultDeviceSettings { get; }
}
Custom Settings

Settings may vary depending on the FCR model. The DeviceSettings.Settings container is provided for this purpose:

class DeviceSettings
{
    ...
    List<DeviceSetting> Settings { get; set; }
}
Possible options:

When the FCR is connected, all the DeviceSettings.Settings collection settings will be given on «Additional Settings» tab:

CustomCashRegisterSettings

Required Settings:

Certain settings are shared by all FCR models. Those are:

FiscalRegisterTaxItems

FiscalRegisterTaxItems

class CashRegisterSettings : DeviceSettings
{
    [CanBeNull]
    DeviceNumberSetting Font0Width;
    [CanBeNull]
    DeviceCustomEnumSetting OfdProtocolVersion;
    List<FiscalRegisterTaxItem> FiscalRegisterTaxItems;
    List<FiscalRegisterPaymentType> FiscalRegisterPaymentTypes;
}

Syrve POS to External Fiscal Registers Interaction

Syrve POS communicates with External Fiscal Registers using the ICashRegister interface. This interface is used to control external FCR operations. For example, if an order is being paid in Syrve POS, the control will come in the ICashRegister.DoCheque() command with the ChequeTask details required to perform the operation on the FCR. Syrve POS would standby until the command is executed and would analyze the FCR response ‒ CashRegisterResult.

FCR Operations

1. Setup() — FCR setup. This is the first command executed by the plugin. It is invoked when you add a new FCR or edit existing FCR settings. The primary plugin’s task is to save and apply new CashRegisterSettings settings that arrive as an argument. More often than not, this command stops the FCR driver, applies new settings, and starts the FCR if it has been running:

public void Setup([NotNull] DeviceSettings newSettings)
{
    if (newSettings == null)
        throw new ArgumentNullException(nameof(newSettings));

    Stop();
    Settings = (CashRegisterSettings)newSettings;
    if (newSettings.Autorun && wasConnected)
        Start();
}

2. Start() — FCR startup. The command is invoked by clicking «Start» in Syrve Office. The FCR can also start automatically if the «Start automatically» option is enabled in the FCR settings at the time the device is added.

StartExternalCashRegister

Usually, this command initializes the driver, opens the port, connects to the device, and tests the connection:

public void Start()
{
    SetState(State.Starting);
    try
    {
        driver = new Driver(); // device driver initialization
        driver.Start()
    }
    catch (Exception e)
    {
        PluginContext.Log.Error($"Failed to load driver with error: {e.Message}");
        SetState(State.Stopped);
        throw new DeviceException(e.Message);
    }
    SetState(State.Running);
}

3. Stop() — FCR stopping. The command is invoked by clicking Stop in Syrve Office. The command is used to stop the device, free up the resources, and close ports, for example:

public void  Stop()
{
    SetState(State.Stopping);
    try
    {
        driver?.close();
    }
    catch (Exception e)
    {
        throw new DeviceException(e.Message);
    }
    driver = null;
    SetState(State.Stopped);
}

4. RemoveDevice() — Deletion. The command is invoked if the Delete shortcut menu item is clicked on the external FCR device in Syrve Office. The FCR object will be marked as removed in the Office if the command does not throw an exception.

5. GetDeviceInfo() — FCR status request. The command is invoked each time the «Administration« > «Equipment Settings« menu item is selected or by clicking «Update« in the Equipment Settings window.

GetDeviceInfo

Depending on the DeviceInfo response, the Syrve Office reads the FCR communication protocol as follows: is the FCR operational and what commands can be sent to it. The FCR status is described by the DeviceInfo type:

Example of the started and running device status request:

public DeviceInfo GetDeviceInfo()
{
    return new DeviceInfo
    {
        State = State.Running,
        Comment = Running",
        Settings = currentCashRegisterSettings
    };
}

6. DoOpenSession() — open till shift (TS). Applicable to the FCRs, drivers of which have the «Open Shift» command. If the FCR has no separate command to open a shift, a successful response CashRegisterResult.Success = true should be given without executing the operation in the FCR.

7. DoXReport() — print an X Report or similar (interim daily report without closing a shift). An Syrve POS till shift is considered open, if the DoOpenSession() command is executed without any exceptions, and DoXReport() returns a successful result: CashRegisterResult.Success = true.

8. DoBillCheque() — guest bill or canceling guest bill. Executed if the FCR supports guest bill printing CashRegisterDriverParameters.IsBillTaskSupported = true (see «Invoice» type receipt).

Order details arrive in the BillTask argument:

DiscountPercent = (Order total / DiscountSum) * 100
IncreasePercent = (Order total / IncreaseSum) * 100
ResultSum = Order total - all discounts - rounding amount(RoundSum) + all surcharges

Order items are described through the ChequeSale type:

Discount = (Item price / DiscountSum) * 100
Increase = (Item price / IncreaseSum) * 100
Sum = item price  all applicable discounts + all surcharges
PluginContext.Operations.GetProductById(chequeTask.Sales[0].ProductId)

Once the Guest Bill button is tapped on the order screen, the terminal sends a command to the FCR and waits for the response CashRegisterResult which includes:

TotalIncomeSum = sales amount  refund amount

Once the till shift is closed or opened, TotalIncomeSum must be zero.

This is how a response to the majority of FCR commands looks like, whether it is the payment, prepayment, guest bill, Z report printing, X report printing, deposit, or withdrawal. Depending on the response contents, Syrve POS tells whether an FCR command is executed or not and what is the result. In the general case, if an operation that receives such a response is unsuccessful (Success=false), Syrve POS would display an error message on the screen (Message) and consider the FCR command as failed. This does not apply to the doubling check carried out each time a monetary operation (payment, refund, deposit, withdrawal, prepayment) takes place. This procedure is aimed to prevent duplicate monetary transactions in case the FCR returns an error. Prior to each such operation, Syrve POS would match FCR amounts against internal calculations to identify any discrepancies.

Negative results are processed equally by Syrve POS, whereas positive results are analyzed case by case. Thus, in the case of a guest bill, an order/bill number will be saved in the Syrve POS database provided BillNumber != null.

9. DoCheque() — order closing, prepayment, refund. The command is invoked when an order is being closed (Pay tapped on the cash register screen) in Syrve POS. All the required order information, like payments and cashier details, arrives within the ChequeTaskargument which includes the BillTask description and adds the following parameters:

All the above-mentioned payment types put together cover the order price.

If so configured, Syrve POS matches till shift financial calculations with the FCR calculations when processing payments, prepayments, refunds, deposits, and withdrawals; by default, this verification is enabled. This helps avoid repeated receipt printing that may actually take place even if the result is unsuccessful ‒ Success=false. For this, the CashSum and TotalIncomeSum fields are analyzed. Also, once the payment is made, Syrve POS saves the FCR DocumentNumber in the database; you can then see this number and the SaleNumber in the closed order detail

DoCheque() implementation example:

public CashRegisterResult DoCheque([NotNull] ChequeTask chequeTask)
{
	if (chequeTask == null)
		throw new ArgumentNullException(nameof(chequeTask));

	if (chequeTask.IsCancellation)
		throw new DeviceException("Cancellation receipts are not supported");

	driver.SetCashier(chequeTask.CashierName, chequeTask.CashierTaxpayerId);

	//Canceling open documents if any
	driver.ResetDevice();
	
	//Opening receipt
	if (chequeTask.isRefund)
		driver.OpenRefundCheck();
	else
		driver.OpenSaleCheck()

	//printing table and order number
	driver.PrintText(string.IsNullOrWhiteSpace(chequeTask.TableNumberLocalized)
		? string.Format("Table: {0}, Order № {1}", chequeTask.TableNumber, chequeTask.OrderNumber)
		: chequeTask.TableNumberLocalized);

	//Printing additional strings at the top of receipt
	if (!string.IsNullOrWhiteSpace(chequeTask.TextBeforeCheque))
		driver.PrintText(chequeTask.TextBeforeCheque);

	foreach (var sale in chequeTask.Sales)
	{
		var taxId = GetTaxId(sale); //retrieving tax rate
		driver.RegisterItem(sale.Name, sale.Price, sale.Amount, taxId);

		//Registering item discount
		if (sale.DiscountSum > 0.0m)
			driver.RegisterDiscount(sale.DiscountSum)
		
		//Registering item surcharge
		if (sale.IncreaseSum > 0.0m)
			driver.RegisterIncrease(sale.IncreaseSum);
	}
	
	//Printing receipt subtotal
	driver.PrintSubtotal();
	
	//Registering receipt subtotal discount
	if (chequeTask.DiscountSum > 0.0m)
		driver.RegisterSubtotalDiscount(chequeTask.DiscountSum)

	//Registering receipt subtotal surcharge
	if (chequeTask.IncreaseSum > 0.0m)
		driver.RegisterSubtotalIncrease(chequeTask.IncreaseSum);

	foreach (var cardPayment in chequeTask.CardPayments)
	{
		var paymentRegisterId = GetPaymentRegisterId(cardPayment); //get payment type number
		driver.AddPayment(GetCardPaymentId(cardPayment), cardPayment.Sum);
	}

	cardPayments = chequeTask.CardPayments.Sum(cardPament=>cardPayment.Sum);
	
	if (chequeTask.CashPayment > 0.0m || cardPayments == 0.0m)
	{
		//cashPaymentId – cash payment type number
		driver.AddPayment(cashPaymentId, cardPayment.Sum);
	}

	//Printing additional strings at the bottom of receipt
	if (!string.IsNullOrWhiteSpace(chequeTask.TextAfterCheque))
		driver.PrintText(chequeTask.TextAfterCheque);

	//Closing receipt
	driver.CloseCheck();
	return GetCashRegisterData();
}

10. GetCashRegisterData() — FCR data request command. It is invoked each time a fiscal operation takes place to retrieve money amounts details, FCR serial number (if it changed within one TS, Syrve POS would ask to close it and open a new one), or the FCR date and time at the shift opening to reconcile the data.

GetCashRegisterData() implementation example:

public CashRegisterResult GetCashRegisterData()
{
	CheckState(State.Running);
	var totalIncomeSum = driver.GetSalesSum() - driver.GetRefundsSum();
	var cashSum = driver.GetCashSum();
	var serialNumber = driver.GetSerialNumber();
	var sessionNumber = driver.GetSessionNumber();
	var dateTime = driver.GetDateTime();
	var receiptNumber = driver.GetLastReceiptNumber();
	var documentNumber = driver.GetLastDocumentNumber();

	return new CashRegisterResult(cashSum, totalIncomeSum, sessionNumber, serialNumber, receiptNumber, 
								  documentNumber, null, dateTime.ToString(CultureInfo.InvariantCulture));
}

11. GetCashRegisterStatus() — FCR status request command. This status is given in the Syrve POS status bar. If, for example, the FCR returns the RestaurantMode service mode and it does not correspond to the Syrve POS service mode, a warning will be given: «Invalid FCR mode». SessionStatus is used to check if the shift is overdue or not. Besides, an FCR may display a message in the status bar if:

    status.Success = false && status.Message != null

Syrve POS interrogates the FCR status each time a fiscal operation takes place.

12. OpenDrawer() — open cash drawer. Depending on the «Open Cash Drawer» payment type settings, an FCR will be sent a command to open the drawer.

13. IsDrawerOpened() — whether or not a cash drawer is open. If an FCR does not support a cash drawer status retrieval command, true needs to be returned

14. PrintText() — printing of non-fiscal documents. For example, reports or item barcodes. The document text will be sent to the PrintText() command to print it out on paper.

15. DoPayIn() — depositing money to the cash register. Cashier details can be retrieved the following way:

IUser cashier = PluginContext.Operations.GetUserById(cashierId)

16. DoPayOut() — withdrawing money from the cash register.

17. DoCorrection() — adjustment bill printing.

Correction

Based on the CorrectionTask object, an FCR can tell which adjustment bill should be posted:

18. DoZReport() — close till shift on the FCR. An Syrve POS till shift is considered closed if the DoZReport() command returns a successful result: CashRegisterResult.Success = true.

19. GetQueryInfo() — request for FCR extended commands. Using the GetQueryInfo() and DirectIo() commands, the FCR can add custom commands to be invoked from the Plugins section of Syrve POS. The GetQueryInfo() method returns the description of custom FR commands, whereas DirectIo() executes them.

Customer greeting example:

public QueryInfoResult GetQueryInfo()
{
    var supportedCommands = new List<SupportedCommand>
    {
        // user greeting command with the "HelloWorld" codename
        new SupportedCommand("HelloWorld", "Greeting")
        {
            // input parameters
            Parameters = new List<RequiredParameter>
            {
                // username input field
                new RequiredParameter("UserName", "Username", "Enter username", "string")
            }
        }
    };

    var result = new QueryInfoResult
    {
        SupportedCommands = supportedCommands
    };

    return result;
}
public DirectIoResult DirectIo(CommandExecute command)
{
    if (command == null)
        throw new ArgumentNullException(nameof(command));

    var result = new DirectIoResult { Document = new Document { Lines = new List<string>() } };

    // retrieve user greeting command by the "HelloWorld" codename 
    if (command.Name == "HelloWorld")
    {
        // reading data from the username input parameter
        const string paramName = "UserName";
        var userName = command.Parameters.FirstOrDefault(item => item.Name == paramName)?.Value;
        
        // writing greeting message
        result.Message = userName != null ? $"Hi, {userName}!" : "Hello everybody!";
    }

    return result;
}

Selecting FCR commands:

SelectCashRegisterCommand

Entering user name:

InputCommandParameters

Customer greeting:

CommandResult

Therefore, the plugin may request certain customer details, show messages, display documents in the UI. QueryInfoResult:

20. DirectIo() — FR custom command execution. If the command returns DirectIoResult.Document filled up, this document will be displayed on the Syrve POS screen.

21. GetCashRegisterDriverParameters() — FCR driver settings.

Example:

public CashRegisterDriverParameters GetCashRegisterDriverParameters()
{
    return new CashRegisterDriverParameters
    {
        CanPrintText = true, \\ Whether or not FCR supports text printing
        SupportsPayInAfterSessionClose = true, \\ Whether or not FCR supports deposits in closed till shift
        CanPrintBarcode = true, \\ Whether or not FCR supports printing of simple barcodes
        CanPrintQRCode = true, \\ Whether or not FCR supports printing of QR codes
        CanUseFontSizes = true, \\ Whether or not FCR supports barcodes-to-text printing
        Font0Width = 44, \\ F0 font row width
        Font1Width = 42,\\ F1 font row width
        Font2Width = 22,\\ F2 font row width
        IsCancellationSupported = true, \\ Whether or not FCR supports Canceling operation        
		ZeroCashOnClose = false, \\ whether or not the FCR zeroes cash register balance out at the till shift closing
        IsBillTaskSupported = false, \\ Whether or not FCR supports printing guest bills via the Bill command
    };
}

Usually, such settings are received from the device driver once it is started as they are unknown before. These settings let Syrve POS know if the FCR can print text or not, deposit money in the closed till shift or not, supports printing of QR codes or not, and so on.

FAQ

1. Why an external FCR is not available on the list of devices?

Option 1: It is likely that the external FCR is not registered properly. You can check this in api.log logs in the Syrve POS data folder. Availability of the following entries means that the external FCR has been successfully registered:

[2019-04-05 14:58:52,546] DEBUG [48]  Plugin CashRegisterPluginFolderName connected.
[2019-04-05 14:58:52,731] DEBUG [48]  Plugin CashRegisterPluginFolderName is calling RegisterCashRegisterFactory operation
[2019-04-05 14:58:52,791]  INFO [48]  Device factory: "CodeName" added.

CashRegisterPluginFolderName — folder name with the .../Plugins/CashRegisterPluginFolderName plugin, CodeNameDeviceSettings.CodeName. Otherwise, logs would register an exception.

Option 2: Update the equipment settings page in Syrve Office: «Administration» > «Equipment Settings» > «Update». If you open this tab before starting Syrve POS, the list of available models is not complete. Update the list or close it and open it again.

2. How can I get a cashier name by Guid cashierId?

IUser user = PluginContext.Operations.GetUserById(cashierId);
var name = user.Name;

3. How to configure deposit and withdrawal types in Syrve Office to call FCR methods DoPayIn() and DoPayOut()?

Most likely you have non-fiscal deposits and withdrawals configured. Those are just accounting movements that do not invoke FCR commands. Fiscal deposits and withdrawals must have an empty Chief Account. See Syrveuser guides on the types of deposits and withdrawals.