miniGL is a general-purpose double-entry, multi-layer accounting system. As long as your transactions align with the manual processes your accounting team uses to record business activities, you can implement them in miniGL. At its core, miniGL operates on simple principles: credits and debits to the appropriate accounts.
Regarding your items:
From the system's perspective, when a user deposits funds, you are receiving money (a 'debit' into an asset account), while the wallet balance represents a liability. Thus, you credit the wallet account in the liabilities section. This is a straightforward example; in practice, you may have escrow accounts that are already categorized as liabilities, but your accounting team will define these specifics.
When a user pays a merchant, the liability shifts. Instead of owing the {card, wallet}holder, you now owe the merchant or its acquirer. The corresponding general ledger transaction will:
miniGL provides full flexibility to create accounts for agents and sub-agents. This enables detailed tracking and management of transactions involving value-added services. This is where miniGL’s innovation truly stands out: its layering capability. For more details, see miniGL Layers. While this concept may initially seem complex for the accounting team to grasp, it offers powerful solutions for scenarios like managing cash-in and cash-out fees.
For example, we have a wallet system based on miniGL with an ambitious fee plan, you could implement a structure where money incurs a fee when it enters the system but becomes free to use when it leaves. However, inter-wallet transfers reset this status, ensuring that money transferred internally between customers pays a fee at the cash-out stage. Layers (as well as 'tags') make implementing such policies seamless and efficient.
Scaling with miniGL
You may also have heard of jCard, an interface bridging the ISO-8583 world with a double-entry record system based on miniGL. At least two jCard customers are currently processing over one billion cards on file (many more at lower scale). Each card has at least one miniGL account, demonstrating the system's scalability. These implementations often run in properly sharded environments or within per-program containers, managing hundreds of millions of cards each.
Looking ahead, we’re developing a new version of miniGL—massiveGL—designed to scale even further, just in case every human on the planet wants to have an account :)
--
--
jPOS is licensed under AGPL - free for community usage for your open-source project. Licenses are also available for commercial usage. Please support jPOS, contact: sa...@jpos.org
---
You received this message because you are subscribed to the Google Groups "jPOS Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jpos-users+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/jpos-users/268b101c-b56c-46d3-bab4-21a7ce3c74ean%40googlegroups.com.
1000 Cash and Cash Equivalents
1010 Cash on Hand
1020 Cash in Bank
2000 Customer Deposits
2010 Checking Accounts--created 2 checking accounts under this.
<?xml version="1.0" ?>
<!DOCTYPE minigl SYSTEM 'http://jpos.org/dtd/minigl.dtd'>
<minigl>
<create-schema />
<user>
<nick>cap10mycaap10</nick>
<name>Venon Mapfunde</name>
<grant>read</grant>
<grant>write</grant>
<grant>grant</grant>
</user>
<user>
<nick>travis</nick>
<name>Travis build</name>
<grant>read</grant>
<grant>write</grant>
<grant>grant</grant>
</user>
<!-- ADD YOUR username here -->
<user>
<nick>bob</nick>
<name>Bob Book Keeper</name>
<grant>read</grant>
<grant>write</grant>
<grant>grant</grant>
</user>
<user>
<nick>eve</nick>
<name>Eve Noperm</name>
</user>
<currency id="840">
<symbol>USD</symbol>
<name>US Dollars</name>
</currency>
<chart-of-accounts code="TestChart" created="20050101">
<description>Test Chart</description>
<composite-account code="1" type="debit" >
<description>Assets</description>
<composite-account code="10" type="debit" >
<description>Cash</description>
<account code="1010" type="debit" currency="840">
<description>Cash on Hand</description>
</account>
<account code="1020" type="debit" currency="840">
<description>Cash in Bank</description>
</account>
</composite-account>
</composite-account>
<composite-account code="6" type="credit" >
<description>Liabilities</description>
<composite-account code="60" type="credit">
<description>Customer Deposits</description>
<composite-account code="6064" type="credit" >
<description>Checking Accounts</description>
<account code="6064230000000201" type="credit" currency="840">
<description>Checking Account - 6064230000000201</description>
</account>
<account code="6064230000000409" type="credit" currency="840">
<description>Checking Account - 6064230000000409</description>
</account>
</composite-account>
</composite-account>
</composite-account>
</chart-of-accounts>
<!--
Layers plan:
Reporting currency (840) goes to layer 0 (reporting currency)
Uruguayan pesos (858) goes to layer 858
Virtual layer 1 is a trip fund
Virtual layer 2 is a budget layer
-->
<journal>
<name>TestJournal</name>
<start>20010101</start>
<!-- <end>20301231</end> -->
<status>open</status>
<chart>TestChart</chart>
<grant user="bob">post</grant>
<grant user="bob">checkpoint</grant>
<grant user="bob">summarize</grant>
<grant user="travis">post</grant>
<grant user="travis">checkpoint</grant>
<grant user="travis">summarize</grant>
<rule clazz="org.jpos.gl.rule.CanPost">
Verify permissions, start/end dates, status, and chart
</rule>
<rule clazz="org.jpos.gl.rule.DoubleEntry">
Verify that credits equals debits
<layers>0</layers>
</rule>
<rule clazz="org.jpos.gl.rule.FinalMinBalance" account="6064230000000201">
Check that the account has always a balance greater than 0.00
<param>0.00</param>
<layers>0,1</layers>
</rule>
<rule clazz="org.jpos.gl.rule.FinalMinBalance" account="6064230000000409">
Check that the account has always a balance greater than 0.00
<param>0.00</param>
<layers>0,1</layers>
</rule>
<rule clazz="org.jpos.gl.rule.FinalMaxBalance" account="6064230000000201">
Put a limit to Bob's wallet balance
<param>50000.00</param>
<layers>0,1</layers>
</rule>
<rule clazz="org.jpos.gl.rule.FinalMaxBalance" account="6064230000000409">
Put a limit to Alice's wallet balance
<param>10000.00</param>
<layers>0,1</layers>
</rule>
</journal>
<transaction post-date="20050101" date="20050101130000" journal="TestJournal">
<detail>Bob's initial deposit</detail>
<tags>tag1 tag2</tags>
<entry account="1010" type="debit">
<detail>Bob Check #001 (USD layer 0)</detail>
<tags>the-debit</tags>
<amount>1000.00</amount>
</entry>
<entry account="6064230000000201" type="credit">
<detail>Bob Initial cash (USD layer 0)</detail>
<tags>the-credit</tags>
<amount>1000.00</amount>
</entry>
</transaction>
<transaction post-date="20050101" date="20050101130000" journal="TestJournal">
<detail>Alice's initial deposit</detail>
<tags>tag1 tag2</tags>
<entry account="1010" type="debit">
<detail>Alice Check #001 (USD layer 0)</detail>
<tags>the-debit</tags>
<amount>1000.00</amount>
</entry>
<entry account="6064230000000409" type="credit">
<detail>Alice Initial cash (USD layer 0)</detail>
<tags>the-credit</tags>
<amount>1000.00</amount>
</entry>
</transaction>
</minigl>
I also added some tests:
@Test
public void testCreateTransactions() throws Exception {
Journal j = gls.getJournal("TestJournal");
Transaction tx = gls.beginTransaction();
start("createTransaction");
GLTransaction txn = createTransaction();
gls.post(j, txn);
checkPoint("post ");
tx.commit();
checkPoint("commit");
gls.session().clear();
}
@Test
private GLTransaction createTransaction() throws Exception {
GLTransaction txn = new GLTransaction("Transfer of 400");
txn.setPostDate(Util.parseDateTime("20241228120000"));
var debitAccount = getAccount("6064230000000201");
var creditAccount = getAccount("6064230000000409");
BigDecimal amount = new BigDecimal(400).setScale(2);
txn.createDebit(debitAccount, amount, "Test Transfer " + amount);
txn.createCredit(creditAccount, amount, "Test Transfer " + amount);
return txn;
}
@Test
private FinalAccount getAccount(String accountNumber) throws Exception {
FinalAccount acct = gls.getFinalAccount("TestChart", accountNumber);
return acct;
}
@Test
public void findBalance() throws Exception {
testCreateTransactions();
Journal j = gls.getJournal("TestJournal");
var x = gls.getBalance(j, getAccount("6064230000000201"), new short[]{0});
assert (x.compareTo(new BigDecimal(600)) == 0);
}
My next task is to do this in a real code not test and I am still to figure out how to set up the database connection. My goal is to be able to create some rest controllers that recieve instructions to create an account and do some financial transactions.
Venon Mapfunde(PMP,Msc Software Engineering,Bsc Computer Science & Mathematics) Tel:+263 775 091 262 Email:taka...@gmail.com Skype: venon.mapfunde
To view this discussion visit https://groups.google.com/d/msgid/jpos-users/CAAgSK%3DmmqpnV5fPFSNAn_%2BGtoZnsELQPYQa4FYD7f3-HKNngug%40mail.gmail.com.
It’s great to hear that the jCard API has been helpful for you. I’ll address your questions below:
Is there a way I can set a Rule on an account programmatically when I create it, e.g., for double-entry?
Rules in jPOS are applied at the Journal/Layer level. For example, you can apply rules like DoubleEntry
to specific layers. In jCard, this is configured in a minigl-setup.xml
file, which might look like this:
<journal>
<name>jcard</name>
<start>20010101</start>
<!--<end>20201231</end>-->
<status>open</status>
<chart>jcard</chart>
<layer id="858">Pesos</layer>
<layer id="1858">Pending Pesos</layer>
<layer id="840">Dollars</layer>
<layer id="1840">Pending Dollars</layer>
<layer id="2840">Credit line, USD</layer>
<grant user="admin">read</grant>
<grant user="admin">post</grant>
<grant user="admin">checkpoint</grant>
<grant user="admin">summarize</grant>
<rule clazz="org.jpos.gl.rule.CanPost">
Verifies permissions, start/end dates, status, and chart
</rule>
<rule clazz="org.jpos.gl.rule.DoubleEntry">
Verifies that credits equals debits
<layers>858,840,1858,1840</layers>
</rule>
These rules are imported during the initial system setup using the glimport
CLI command. While you can programmatically create or adjust these rules by referencing the glimport
source code, they are typically designed to remain static once applied. For instance, enabling double-entry on specific journals or layers is usually a long-term configuration.
How can I calculate the balance quickly after 10,000 transactions? How do Balance Cache and Checkpoints work?
Both Balance Cache and Checkpoints improve balance computation efficiency but serve different purposes:
Balance Cache tracks the most recent balance, regardless of the transaction’s postDate
. This is especially useful in issuer systems where you need a fast computation of the available balance to decide whether a transaction should be approved or declined.
Checkpoints, on the other hand, account for the postDate
and are more resource-intensive to compute. They’re ideal for generating statements for specific periods. For example, you can create a checkpoint at the end of a month so that subsequent statements don’t need to process the entire transaction history again.
Tags and Layers: What are their roles? Don’t worry about your confusion, I’m sure you haven’t learn this in high school because it’s some kind of jPOS invention. We have a hard time explaining the concept even to professional CPAs, but once they get it, they love it.
Tags: These are essentially metadata for transactions, stored to support application-specific use cases. For example, if you tag certain entries with #fee
, you can later identify and include or exclude those entries (e.g., fees that shouldn’t be reversed during a transaction reversal).
Layers: These act as sub-journals within a journal. The layer number is defined as part of your application’s logic, and its use depends on your “layer plan.” For instance, in jCard, we use ISO-4217 currency codes (e.g., 840
for USD) and add offsets for specific purposes:
+1000
: Pending transactions (e.g., 1840
for pending USD).
+2000
: Credit allowances.
+3000
: On-hold credits (e.g., for refunds or potentially fraudulent transactions).
For example, if you receive a refund that shouldn’t be immediately available, you could post it to the 3840
layer. A batch process or authorized operator could later “ground” the funds by reversing the entry in 3840
and posting it to 840
in a single GLTransaction.
How do I make sure i dont post the same transaction–I did not see something like a reference I can post with my transaction to ensure that no transaction is repeated..
That’s where the surrounding application comes into play. In jCard we have a Tranlog record with all the transaction metadata, and using that metadata, that includes the RRN, PAN, Amount, currency, MTI, etc. etc. we decide if the transaction needs to be posted to miniGL or has to be handled as a retransmission with no financial impact.
The DoubleEntry
rule can be configured to apply only to certain layers—such as the accounting layer (offset=0
) and the pending layer (offset=1000
)—while being relaxed in others, like the layer with offset=3000
. This allows you to have a GL transaction with a matching credit/debit in the 840 layer and a single-leg entry, for example, in the 3840 layer.
That’s one option. For greater control, you can define a bridge account in the chart of accounts and use it as the counterparty for entries you want to register in the 3840 layer.
It might seem confusing at first, but this is standard accounting practice—your accounting team will fully understand.
--
--
jPOS is licensed under AGPL - free for community usage for your open-source project. Licenses are also available for commercial usage. Please support jPOS, contact: sa...@jpos.org
---
You received this message because you are subscribed to the Google Groups "jPOS Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jpos-users+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/jpos-users/2e9f2340-607a-4e0f-9838-e67811e10f6dn%40googlegroups.com.