Documentation
¶
Overview ¶
Example (PurchaseInventory) ¶
package main
import (
"fmt"
"time"
"github.com/HashemJaafar7/accounting"
)
// Set up in-memory storage
var inventoryStore = make(map[accounting.AccountAddress]accounting.Inventory)
var lastEntry accounting.AccountingEntry
var journal []accounting.AccountingEntry
// Set up required helper functions
func getInventory(addr accounting.AccountAddress) (accounting.Inventory, error) {
return inventoryStore[addr], nil
}
func setInventory(addr accounting.AccountAddress, inv accounting.Inventory) error {
inventoryStore[addr] = inv
return nil
}
func getLastEntry() (accounting.AccountingEntry, error) {
return lastEntry, nil
}
func setEntry(entry accounting.AccountingEntry) error {
lastEntry = entry
journal = append(journal, entry)
return nil
}
func main() {
const (
capital accounting.AccountAddress = -1001
USD accounting.AccountAddress = 2001
inventory accounting.AccountAddress = 1001
COGS accounting.AccountAddress = 3001
revenue accounting.AccountAddress = -4001
)
{
// Create an entry to start capital
entry := accounting.AccountingEntry{
EntryNumber: 1,
TimeUnix: time.Now().Unix(),
DoubleEntry: []accounting.SingleEntry{
{
CostFlowType: accounting.INFLOW, //
AccountAddress: capital, // capital
Quantity: 0, //
Amount: 1000, // $1000 total
},
{
CostFlowType: accounting.INFLOW, // Cash going in
AccountAddress: USD, // Cash account
Quantity: 1000, //
Amount: 1000, // $1000 total
},
},
}
// Add the entry to the journal
err := accounting.AddToJournal(entry, getInventory, setInventory, getLastEntry, setEntry)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Print the resulting inventory balance
printInventory()
}
{
purchaseEntry := accounting.AccountingEntry{
EntryNumber: 2,
TimeUnix: time.Now().Unix(),
DoubleEntry: []accounting.SingleEntry{
{
CostFlowType: accounting.INFLOW,
AccountAddress: inventory, // Inventory account
Quantity: 50, // 50 units
Amount: 500, // $500 total
},
{
CostFlowType: accounting.WAC,
AccountAddress: USD, // Cash account
Quantity: 500,
Amount: 500,
},
},
}
err := accounting.AddToJournal(purchaseEntry, getInventory, setInventory, getLastEntry, setEntry)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Print the resulting inventory balance
printInventory()
}
{
// Now sell 60 units at $15 each using FIFO costing
saleEntry := accounting.AccountingEntry{
EntryNumber: 3,
TimeUnix: time.Now().Unix(),
DoubleEntry: []accounting.SingleEntry{
{
CostFlowType: accounting.FIFO, // Use FIFO costing
AccountAddress: inventory, // Inventory account
Quantity: 5, // Sell 5 units
Amount: 50, // Cost of goods sold ($10/unit)
},
{
CostFlowType: accounting.INFLOW, //
AccountAddress: COGS, // COGS account
Quantity: 5, //
Amount: 50, //
},
{
CostFlowType: accounting.INFLOW, //
AccountAddress: USD, // cash account
Quantity: 80, // Sell 60 units
Amount: 80, // Cost of goods sold ($10/unit)
},
{
CostFlowType: accounting.INFLOW, //
AccountAddress: revenue, // revenue account
Quantity: 5, // Sell 5 units price 16
Amount: 80, //
},
},
}
err := accounting.AddToJournal(saleEntry, getInventory, setInventory, getLastEntry, setEntry)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// Print the resulting inventory balance
printInventory()
}
i := accounting.EntryNumber(1)
iterOnJournalFunction := func() (accounting.AccountingEntry, bool, error) {
a := journal[i]
i++
return a, len(journal) == int(i)+1, nil
}
err := accounting.CheckAllTheJournal(setInventory, iterOnJournalFunction)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
}
func printInventory() {
for k, v := range inventoryStore {
fmt.Printf("address: %v\tinventory:%v\n", k, v)
}
fmt.Println("____________________________________________")
}
Output: address: -1001 inventory:[{1 0 1000}] address: 2001 inventory:[{1 1000 1000}] ____________________________________________ address: -1001 inventory:[{1 0 1000}] address: 2001 inventory:[{2 500 500}] address: 1001 inventory:[{2 50 500}] ____________________________________________ address: 2001 inventory:[{2 500 500} {3 80 80}] address: 1001 inventory:[{2 45 450}] address: 3001 inventory:[{3 5 50}] address: -4001 inventory:[{3 5 80}] address: -1001 inventory:[{1 0 1000}] ____________________________________________
Index ¶
- Constants
- func AddToJournal(entry AccountingEntry, getInventoryFunction GetInventory, ...) error
- func CheckAllTheJournal(setInventoryFunction SetInventory, iterOnJournalFunction IterOnJournal) error
- type AccountAddress
- type AccountAddressAndInventory
- type AccountingEntry
- type Amount
- type CostFlowType
- type DoubleEntry
- type EntryNumber
- type GetInventory
- type GetLastEntry
- type Inventory
- type InventoryRecord
- type IsDebit
- type IterOnJournal
- type Quantity
- type SetEntry
- type SetInventory
- type SingleEntry
- type TimeUnix
Examples ¶
Constants ¶
const ( ErrEntryNumberShouldBeBiggerByOne = "ErrEntryNumberShouldBeBiggerByOne" ErrTimeShouldBeBigger = "ErrTimeShouldBeBigger" ErrEntryMustHaveAtLeast_2Entries = "ErrEntryMustHaveAtLeast_2Entries" ErrDuplicateAccountInEntry = "ErrDuplicateAccountInEntry" ErrSumOfAmountsIsNotZeroAndDebitMoreThanCredit = "ErrSumOfAmountsIsNotZeroAndDebitMoreThanCredit" ErrInventoryNotFoundForAccountAddress = "ErrInventoryNotFoundForAccountAddress" ErrQuantityAndAmountShouldBothBeDebitOrCredit = "ErrQuantityAndAmountShouldBothBeDebitOrCredit" ErrTheCostFlowTypeIsWrong = "ErrTheCostFlowTypeIsWrong" ErrInventoryIsEmpty = "ErrInventoryIsEmpty" ErrInsufficientQuantityInInventory = "ErrInsufficientQuantityInInventory" ErrAmountMismatch = "ErrAmountMismatch" ErrInsufficientAmountInInventory = "ErrInsufficientAmountInInventory" ErrTheQuantityAndAmountShouldBeBothPositive = "ErrTheQuantityAndAmountShouldBeBothPositive" ErrYouShouldUseCostFlowTypeNONEIfYouHaveQuantityOrAmountZero = "ErrYouShouldUseCostFlowTypeNONEIfYouHaveQuantityOrAmountZero" )
errors
Variables ¶
This section is empty.
Functions ¶
func AddToJournal ¶
func AddToJournal(entry AccountingEntry, getInventoryFunction GetInventory, setInventoryFunction SetInventory, getLastEntryFunction GetLastEntry, setEntryFunction SetEntry, ) error
AddToJournal adds a new accounting entry to the journal while maintaining double-entry accounting principles. It takes the following parameters:
- entry: The AccountingEntry to be added to the journal
- getInventoryFunction: A function to retrieve the current inventory for an account address
- setInventoryFunction: A function to update the inventory for an account address
- getLastEntryFunction: A function to get the last entry from the journal
- setEntryFunction: A function to save a new entry to the journal
The function performs the following steps: 1. Retrieves current inventory for all accounts involved in the entry 2. Gets the last journal entry for reference 3. Validates and processes the double-entry accounting rules 4. Saves the new entry to the journal 5. Updates the inventory for all affected accounts
Returns an error if any operation fails during the process.
func CheckAllTheJournal ¶
func CheckAllTheJournal(setInventoryFunction SetInventory, iterOnJournalFunction IterOnJournal) error
CheckAllTheJournal iterates through journal entries and processes double-entry accounting by updating account inventories. It takes two function parameters:
setInventoryFunction: A function that updates the inventory for a given address iterOnJournalFunction: A function that iterates through journal entries
The function processes entries sequentially, checking and validating double-entry accounting rules. For each processed entry, it updates an in-memory map of address inventories. Finally, it persists all updated inventories using the setInventoryFunction.
Returns an error if any operation fails during journal processing or inventory updates.
Types ¶
type AccountAddress ¶
type AccountAddress int64
type AccountAddressAndInventory ¶
type AccountAddressAndInventory map[AccountAddress]Inventory
func CheckAndProcessDoubleEntry ¶
func CheckAndProcessDoubleEntry(lastEntryNumber EntryNumber, lastTimeUnix TimeUnix, entry AccountingEntry, accountAddressAndInventoryVariable AccountAddressAndInventory) (AccountAddressAndInventory, error)
CheckAndProcessDoubleEntry validates and processes a double-entry accounting transaction. It ensures the integrity of the accounting entry and updates the inventory records accordingly.
Parameters:
- lastEntryNumber: The previous entry number for sequence validation
- lastTimeUnix: The timestamp of the last entry for chronological validation
- entry: The accounting entry to be processed
- accountAddressAndInventoryVariable: Current state of inventory records for all accounts
Returns:
- AccountAddressAndInventory: Updated inventory records after processing the entry
- error: Error if any validation fails or processing encounters issues
The function performs the following validations:
- Ensures entry number is sequential
- Verifies timestamp is after the last entry
- Checks minimum of 2 entries in double-entry
- Validates debit and credit balance
- Prevents duplicate accounts in single entry
- Verifies positive amounts and quantities
- Ensures valid cost flow types
After validation, it processes inventory records according to various transaction scenarios:
- Handles inflow and outflow of quantities and amounts
- Manages inventory adjustments (zero quantity or amount cases)
- Applies cost flow accounting methods
- Removes zero-value inventory records
The function handles different combinations of positive, negative, and zero values for both amounts and quantities, applying appropriate business rules for each case.
type AccountingEntry ¶
type AccountingEntry struct {
EntryNumber
TimeUnix // the time in unix in seconds
DoubleEntry
}
type CostFlowType ¶
type CostFlowType uint8
const ( INFLOW CostFlowType = iota WAC FIFO LIFO HIFO LOFO NONE )
type DoubleEntry ¶
type DoubleEntry []SingleEntry
type EntryNumber ¶
type EntryNumber uint64
type GetInventory ¶
type GetInventory func(key AccountAddress) (Inventory, error)
type GetLastEntry ¶
type GetLastEntry func() (AccountingEntry, error)
type Inventory ¶
type Inventory []InventoryRecord
type InventoryRecord ¶
type InventoryRecord struct {
EntryNumber
Quantity
Amount
}
type IsDebit ¶
type IsDebit bool
func GetStatus ¶
func GetStatus(costFlowType CostFlowType, accountAddress AccountAddress) IsDebit
GetStatus determines if a account status a debit based on cost flow type and account address. It compares the cost flow direction (inflow/outflow) with the natural debit/credit state of the account. Returns true if the account status a debit, false if it a credit. Parameters:
- costFlowType: Indicates whether money/value is flowing in or out (INFLOW/WAC/FIFO/LIFO/HIFO/LOFO/NONE)
- accountAddress: The address/identifier of the account being affected
func IsNatureDebit ¶
func IsNatureDebit(accountAddress AccountAddress) IsDebit
IsNatureDebit determines if an account has a debit nature based on its address. A positive or zero account address indicates a debit nature account (assets, expenses), while a negative address indicates a credit nature account (liabilities, revenues, equity).
Parameters:
- accountAddress: The address of the account to check
Returns:
- isDebit: true if the account has a debit nature, false if credit nature
type IterOnJournal ¶
type IterOnJournal func() (AccountingEntry, bool, error)
type SetEntry ¶
type SetEntry func(value AccountingEntry) error
type SetInventory ¶
type SetInventory func(key AccountAddress, value Inventory) error
type SingleEntry ¶
type SingleEntry struct {
CostFlowType
AccountAddress
Quantity
Amount
}