Point of Sales use case

Connect your POS to Pennylane

📘

Please use the Oauth 2.0 process to connect your customers through the integration.

Please note that our users' API tokens are intended for personal use, not for use by partners. Your app will not be published in our official Marketplace if it doesn't use OAuth.

A note about IDs

Please note that each company has its own IDs. The ID of ledger accounts (comptes comptables) are different for each company. You need to store and use the company's own IDs.

Identify the right journal

1. List journals and allow your users to choose the Journal they use for their POS entries.

Retrieve journals

curl --request GET \
     --url 'https://app.pennylane.com/api/external/v2/journals?page=1&per_page=100' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx'

Response :

{
  "total_pages": 1,
  "current_page": 1,
  "per_page": 20,
  "total_items": 14,
  "items": [
    {
      "code": "VT",
      "id": 1389447,
      "label": "Journal de vente"
    },
    {
      "code": "HA",
      "id": 1389448,
      "label": "Journal d'achat"
    },
    ........

Save the ID and use it for every entry you post.

It may happen that no “Caisse” type journal exists. To create one, follow the next step ⬇️

2. If there is no journal to use for POS, allow your user to Create a journal

Post the code and the label. Values must be understandable by accountants _(exemple : Journal de Caisse, Journal de la caisse XXX, Caisse XXX, ...)_

curl --request POST \
     --url https://app.pennylane.com/api/external/v2/journals \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx' \
     --header 'content-type: application/json' \
     --data '
{
  "code": "POSXX",
  "label": "Journal de caisse XXX"
}

Response :

{
  "code": "POSXX",
  "id": 6350564,
  "label": "Journal de caisse XXX"
}

Save the ID and use it for every entry you post

For this example, the journal_id to use to create entries is 6350564

Create your accounting entry layout


1. Ledger accounts for Net income

📘

Revenues are posted into "706xxx" ledger accounts, which should be differentiated by VAT rate to simplify accounting.

The most common ledger accounts are the following :

  • Revenues with 20% VAT : 7062
  • Revenues with 10% VAT : 7061

Payload example :

curl --request GET \
     --url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%22706%22%7D%5D' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx'

Response :

{
  "total_pages": 1,
  "current_page": 1,
  "per_page": 1000,
  "total_items": 53,
  "items": [
    {
      "id": 263389185,
      "number": "7061",
      "label": "Prestations de services",
      "vat_rate": "any",
      "country_alpha2": "any",
      "enabled": true
    },
    {
  ]
}

Give to your customers the possibility to match ledger accounts with the type of revenue

For this example, theledger_account_id to use for 10% VAT revenue is 263389185

If a ledger account doesn't exist you can create it.

Payload example : the ledger account 7062 is missing

curl --request POST \
     --url https://app.pennylane.com/api/external/v2/ledger_accounts \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx'
     --header 'content-type: application/json' \
     --data '
{
  "number": "7062",
  "label": "Ventes à 20%"
}
'

Response :

{
  "id": 1118517253,
  "number": "7062",
  "label": "Ventes à 20%",
  "vat_rate": "any",
  "country_alpha2": "any",
  "enabled": true
}

You can now use the new ledger account

For this example, the ledger_account_id to use for 20% VAT is1118517253

2. Ledger accounts for VAT

📘

VAT amounts are posted into "4457xxx" ledger accounts, which should be differentiated by VAT rate to simplify accounting.

The most common VAT ledger accounts in metropolitan France are :

  • 20% VAT : 44571009
  • 10% VAT : 44571008
  • 5,5% VAT : 44571006

The most common VAT ledger accounts in french DOM-TOM are :

  • 8,5% VAT : 44571007
  • 5,5% VAT : 44571006
  • 2,1% VAT : 44571005

Payload example :

curl --request GET \
     --url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%2244571%22%7D%5D' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx'

Response :

{
  "total_pages": 1,
  "current_page": 1,
  "per_page": 1000,
  "total_items": 7,
  "items": [
    {
      "id": 263381975,
      "number": "44571008",
      "label": "TVA collectée à 10%",
      "vat_rate": "any",
      "country_alpha2": "any",
      "enabled": true
    },
    {
      "id": 263381977,
      "number": "44571009",
      "label": "TVA collectée à 20%",
      "vat_rate": "any",
      "country_alpha2": "any",
      "enabled": true
    },

You can directly associate your VAT and related ledger account IDs.

For this example, the ledger_account_id to use for 10% VAT is 263381975

2.2 Create VAT ledger accounts

There is almost no case were you will have to create a VAT ledger account, as long as they exists by default on every company on Pennylane.

Otherwise, please contact your partnership manager to talk about it.

3. Ledger accounts for Type of payments

📘

Payment types are posted into "511xxx" ledger accounts, and don't need to be differentiated by VAT.

The most common ledger accounts are the following :

  • Checks : 5112
  • Uber eats : 511008
  • Cash : 530

Payload example for Cash :

curl --request GET \
     --url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%2253%22%7D%5D' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx'

Payload example for other :

curl --request GET \
     --url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%22511%22%7D%5D' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx'

Response :

{
  "total_pages": 1,
  "current_page": 1,
  "per_page": 1000,
  "total_items": 6,
  "items": [
    {
      "id": 263382102,
      "number": "511008",
      "label": "Encaissements Uber Eats",
      "vat_rate": "any",
      "country_alpha2": "any",
      "enabled": true
    },
    {
      "id": 263382104,
      "number": "5112",
      "label": "Chèques à encaisser",
      "vat_rate": "any",
      "country_alpha2": "any",
      "enabled": true
    },

Give to you customers the possibility to match ledger accounts with the type of payments

For this example, the ledger_account_id to use for Uber Eats payments is 263382102

If a ledger account doesn't exist you can create it.

Payload example : the ledger account for "Deliveroo" is missing

curl --request POST \
     --url https://app.pennylane.com/api/external/v2/ledger_accounts \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx' \
     --header 'content-type: application/json' \
     --data '
{
  "number": "5110012",
  "label": "Deliveroo"
}
'

Response :

{
  "id": 1118517352,
  "number": "5110012",
  "label": "Deliveroo",
  "vat_rate": "any",
  "country_alpha2": "any",
  "enabled": true
}

You can now use the new ledger account

For this example, the ledger_account_id to use for Deliveroo payment type is1118517352

Anticipate rounding differences

📘

Every rounding issue or unexpected differences have to be handled.

The sum of Revenue and VAT are in Creditside
The sum of Payment type are in Debitside
The sum of Creditand Debitmust be equal.

If not, you have to add an entry line to balance the entry.

If the Credit is less than the Debit, you must add a line in the Credit with the ledger account 758.
For the opposite, use the 658 on Debit

Payload example to retrieve the ID of ledger_account 658

curl --request GET \
     --url 'https://app.pennylane.com/api/external/v2/ledger_accounts?page=1&per_page=1000&filter=%5B%7B%22field%22%3A%20%22number%22%2C%20%22operator%22%3A%20%22start_with%22%2C%20%22value%22%3A%20%22658%22%7D%5D' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx'

Response :

{
  "total_pages": 1,
  "current_page": 1,
  "per_page": 1000,
  "total_items": 7,
  "items": [
    {
      "id": 263382333,
      "number": "658",
      "label": "Pénalités et autres charges (anciennement Charges diverses de gestion courante)",
      "vat_rate": "any",
      "country_alpha2": "any",
      "enabled": true
    },

You can directly associate your VAT and related ledger accounts.

For this example, the ledger_account_id to use to balance the entry on debit is 263382333

Let's analyze a simple 'Ticket Z' ⬇️

Payload for this example :

curl --request POST \
     --url https://app.pennylane.com/api/external/v2/ledger_entries \
     --header 'accept: application/json' \
     --header 'authorization: Bearer xxx' \
     --header 'content-type: application/json' \
     --data '
{
  "date": "2025-01-01",
  "label": "Ticket Z - Restaurant Test - 01-01-2025",
  "journal_id": 6350564,
  "ledger_attachment_id": 32325335
  "ledger_entry_lines": [
    {
      "debit": "0",
      "credit": "90.91",
      "ledger_account_id": 263389185
    },
    {
      "debit": "0",
      "credit": "9.09",
      "ledger_account_id": 263381975
    },
    {
      "debit": "99.99",
      "credit": "0",
      "ledger_account_id": 263382102
    },
    {
      "debit": "0.01",
      "credit": "0",
      "ledger_account_id": 263382333
    }
  ]
}
'

Response :

{
  "label": "Ticket Z - Restaurant Test - 01-01-2025",
  "date": "2025-01-01",
  "journal_id": 6350564,
  "ledger_attachment_id": 32325335
  "id": 1753102653001,
  "ledger_attachment_id": null,
  "ledger_attachment_filename": null,
  "ledger_entry_lines": [
    {
      "id": 7222323527,
      "debit": "0.0",
      "credit": "90.91",
      "label": "",
      "ledger_account_id": 263389185
    },
    {
      "id": 7222323528,
      "debit": "0.0",
      "credit": "9.09",
      "label": "",
      "ledger_account_id": 263381975
    },
    {
      "id": 7222323529,
      "debit": "99.99",
      "credit": "0.0",
      "label": "",
      "ledger_account_id": 263382102
    },
    {
      "id": 7222323530,
      "debit": "0.01",
      "credit": "0.0",
      "label": "",
      "ledger_account_id": 263382333
    }
  ]
}

If your entry is not balanced, yo will receive an error :
Please check the Anticipate rounding differences part

{  
  "status": 422,  
  "error": "Entry lines are not balanced"  
}

Congratulations, your entry has been successfully registered!

Please keep the information on your side to know if a Ticket Z has been correctly registered or not.
If you retry the same payload, nothing will prevent a double entry on Pennylane - You are responsible for the data quality that you send.
If your entry doesn't get a 201 response, it hasn't been registered and you need to try again.