mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-27 00:44:45 +00:00
docs: add human readable specifications for stock ledger (#29308)
* docs: add human readable specifications for stock ledger * docs: reposting technical implementation notes
This commit is contained in:
103
erpnext/stock/spec/README.md
Normal file
103
erpnext/stock/spec/README.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Implementation notes for Stock Ledger
|
||||||
|
|
||||||
|
|
||||||
|
## Important files
|
||||||
|
|
||||||
|
- `stock/stock_ledger.py`
|
||||||
|
- `controllers/stock_controller.py`
|
||||||
|
- `stock/valuation.py`
|
||||||
|
|
||||||
|
## What is in an Stock Ledger Entry (SLE)?
|
||||||
|
|
||||||
|
Stock Ledger Entry is a single row in the Stock Ledger. It signifies some
|
||||||
|
modification of stock for a particular Item in the specified warehouse.
|
||||||
|
|
||||||
|
- `item_code`: item for which ledger entry is made
|
||||||
|
- `warehouse`: warehouse where inventory is affected
|
||||||
|
- `actual_qty`: change in qty
|
||||||
|
- `qty_after_transaction`: quantity available after the transaction is processed
|
||||||
|
- `incoming_rate`: rate at which inventory was received.
|
||||||
|
- `is_cancelled`: if 1 then stock ledger entry is cancelled and should not be used
|
||||||
|
for any business logic except for the code that handles cancellation.
|
||||||
|
- `posting_date` & `posting_time`: Specify the temporal ordering of stock ledger
|
||||||
|
entries. Ties are broken by `creation` timestamp.
|
||||||
|
- `voucher_type`: Many transaction can create SLE, e.g. Stock Entry, Purchase
|
||||||
|
Invoice
|
||||||
|
- `voucher_no`: `name` of the transaction that created SLE
|
||||||
|
- `voucher_detail_no`: `name` of the child table row from parent transaction
|
||||||
|
that created the SLE.
|
||||||
|
- `dependant_sle_voucher_detail_no`: cross-warehouse transfers need this
|
||||||
|
reference in order to update dependent warehouse rates in case of change in
|
||||||
|
rate.
|
||||||
|
- `recalculate_rate`: if this is checked in/out rates are recomputed on
|
||||||
|
transactions.
|
||||||
|
- `valuation_rate`: current average valuation rate.
|
||||||
|
- `stock_value`: current total stock value
|
||||||
|
- `stock_value_difference`: stock value difference made between last and current
|
||||||
|
entry. This value is booked in accounting ledger.
|
||||||
|
- `stock_queue`: if FIFO/LIFO is used this represents queue/stack maintained for
|
||||||
|
computing incoming rate for inventory getting consumed.
|
||||||
|
- `batch_no`: batch no for which stock entry is made; each stock entry can only
|
||||||
|
affect one batch number.
|
||||||
|
- `serial_no`: newline separated list of serial numbers that were added (if
|
||||||
|
actual_qty > 0) or else removed. Currently multiple serial nos can have single
|
||||||
|
SLE but this will likely change in future.
|
||||||
|
|
||||||
|
|
||||||
|
## Implementation of Stock Ledger
|
||||||
|
|
||||||
|
Stock Ledger Entry affects stock of combinations of (item_code, warehouse) and
|
||||||
|
optionally batch no if specified. For simplicity, lets avoid batch no. for now.
|
||||||
|
|
||||||
|
|
||||||
|
Stock Ledger Entry table stores stock ledger for all combinations of item_code
|
||||||
|
and warehouse. So whenever any operations are to be performed on said
|
||||||
|
item-warehouse combination stock ledger is filtered and sorted by posting
|
||||||
|
datetime. A typical query that will give you individual ledger looks like this:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
select *
|
||||||
|
from `tabStock Ledger Entry` as sle
|
||||||
|
where
|
||||||
|
is_cancelled = 0 --- cancelled entries don't affect ledger
|
||||||
|
and item_code = 'item_code' and warehouse = 'warehouse_name'
|
||||||
|
order by timestamp(posting_date, posting_time), creation
|
||||||
|
```
|
||||||
|
|
||||||
|
New entry is just an update to the last entry which is found by looking at last
|
||||||
|
row in the filter ledger.
|
||||||
|
|
||||||
|
|
||||||
|
### Serial nos
|
||||||
|
|
||||||
|
Serial numbers do not follow any valuation method configuration and they are
|
||||||
|
consumed at rate they were produced unless they are grouped in which case they
|
||||||
|
are consumed at weighted average rate.
|
||||||
|
|
||||||
|
|
||||||
|
### Batch Nos
|
||||||
|
|
||||||
|
Batches are currently NOT consumed as per batch wise valuation rate, instead
|
||||||
|
global FIFO queue for the item is used for valuation rate.
|
||||||
|
|
||||||
|
|
||||||
|
## Creation process of SLEs
|
||||||
|
|
||||||
|
- SLE creation is usually triggered by Stock Transactions using a method
|
||||||
|
conventionally named `update_stock_ledger()` This might not be defined for
|
||||||
|
stock transaction and could be specified somewhere in inheritance hierarchy of
|
||||||
|
controllers.
|
||||||
|
- This method produces SLE objects which are processed by `make_sl_entries` in
|
||||||
|
`stock_ledger.py` which commits the SLE to database.
|
||||||
|
- `update_entries_after` class is used to process ONLY the inserted SLE's queue
|
||||||
|
and valuation.
|
||||||
|
- The change in qty is propagated to future entries immediately. Valuation and
|
||||||
|
queue for future entries is processed in background using repost item
|
||||||
|
valuation.
|
||||||
|
|
||||||
|
|
||||||
|
## Accounting impact
|
||||||
|
|
||||||
|
- Accounting impact for stock transaction is handled by `get_gl_entries()`
|
||||||
|
method on controllers. Each transaction has different business logic for
|
||||||
|
booking the accounting impact.
|
||||||
38
erpnext/stock/spec/reposting.md
Normal file
38
erpnext/stock/spec/reposting.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Stock Reposting
|
||||||
|
|
||||||
|
Stock "reposting" is process of re-processing Stock Ledger Entry and GL Entries
|
||||||
|
in event of backdated stock transaction.
|
||||||
|
|
||||||
|
*Backdated stock transaction*: Any stock transaction for which some
|
||||||
|
item-warehouse combination has a future transactions.
|
||||||
|
|
||||||
|
## Why is this required?
|
||||||
|
Stock Ledger is stateful, it maintains queue, qty at any
|
||||||
|
point in time. So if you do a backdated transaction all future values change,
|
||||||
|
queues need to be re-evaluated etc. Watch Nabin and Rohit's conference
|
||||||
|
presentation for explanation: https://www.youtube.com/watch?v=mw3WAnekGIM
|
||||||
|
|
||||||
|
## How is this implemented?
|
||||||
|
Whenever backdated transaction is detected, instead of
|
||||||
|
fully processing it while submitting, the processing is queued using "Repost
|
||||||
|
Item Valuation" doctype. Every hour a scheduled job runs and processes this
|
||||||
|
queue (for up to maximum of 25 minutes)
|
||||||
|
|
||||||
|
|
||||||
|
## Queue implementation
|
||||||
|
- "Repost item valuation" (RIV) is automatically submitted from backdated transactions. (check stock_controller.py)
|
||||||
|
- Draft and cancelled RIV are ignored.
|
||||||
|
- Keep filter of "submitted" documents when doing anything with RIVs.
|
||||||
|
- The default status is "Queued".
|
||||||
|
- When background job runs, it picks the oldest pending reposts and changes the status to "In Progress" and when it finishes it
|
||||||
|
changes to "Completed"
|
||||||
|
- There are two more status: "Failed" when reposting failed and "Skipped" when reposting is deemed not necessary so it's skipped.
|
||||||
|
- technical detail: Entry point for whole process is "repost_entries" function in repost_item_valuation.py
|
||||||
|
|
||||||
|
|
||||||
|
## How to identify broken stock data:
|
||||||
|
There are 4 major reports for checking broken stock data:
|
||||||
|
- Incorrect balance qty after the transaction - to check if the running total of qty isn't correct.
|
||||||
|
- Incorrect stock value report - to check incorrect value books in accounts for stock transactions
|
||||||
|
- Incorrect serial no valuation -specific to serial nos
|
||||||
|
- Stock ledger invariant check - combined report for checking qty, running total, queue, balance value etc
|
||||||
Reference in New Issue
Block a user