# The $5.95 Valuation Residue — Problem Statement and Failed Attempt

**Status:** UNRESOLVED. No fix is in the codebase.
**Date:** 2026-07-12/13
**Author:** Claude (the attempted fix described in §5 was wrong; it was fully reverted)
**Audience:** whoever actually solves this

---

## 1. The problem in one sentence

Pascucci's inventory is carried **$5.95 above** its true landed cost. COGS is understated by
$5.95, inventory and net profit are overstated by $5.95. The gap **grows on every sale** and
never self-corrects.

## 2. Where the $5.95 comes from — the mechanism

When goods are issued (a sale), `connector-erpnext` writes the Material Issue row with an
**explicit pinned `basic_rate`**:

- `finanly/services/connectors/erpnext/src/finanly_connector_erpnext/app.py:2247` (bundle component row)
- `finanly/services/connectors/erpnext/src/finanly_connector_erpnext/app.py:2288` (line row)

```python
line_row = {
    "item_code": item_code,
    "qty": qty,
    "s_warehouse": line_src_wh,
    "basic_rate": round(rate, 6),      # <-- PINNED, deliberately
    "basic_amount": round(amount, 6),
}
```

**This pinning is DELIBERATE and must not be removed.** It exists to fix a rounding discrepancy
between ERPNext's 4-decimal and 2-decimal representations. It is what keeps
`SUM(Bin.stock_value)` exactly equal to the GL `Stock - *` accounts. Today both are
**$79,366.39** — to the cent. Remove the pin and that invariant breaks.

**The side effect:** the pinned rate is a *rounded snapshot* of the moving average at the moment
of issue. Every issue therefore relieves a hair more or less value than the true landed cost.
The difference strands itself in `Stock - *`. It is **cumulative** — as of 2026-07-12 it totals
**+$5.95**, spread across *every item*, not just one.

## 3. How the $5.95 was measured (two independent methods, both agree)

Do not trust a number computed from the Purchase Receipt rate — that misses later Landed Cost
Vouchers (e.g. `MAT-LCV-2026-00009`, $1,626 on 10 Apr). Earlier attempts that ignored this
produced wrong figures ($7.08, then $5.19). Use ERPNext's **own** `stock_value_difference`:

```
landed_rate(item) = SUM(sle.stock_value_difference) / SUM(sle.actual_qty)
                    over Stock Ledger Entries with actual_qty >= 0, is_cancelled = 0
                    (this includes every LCV revaluation, which carry qty = 0)

residue(item)     = Bin.stock_value − (Bin.actual_qty × landed_rate)

total residue     = SUM over all items = +$5.95
```

Cross-check from the COGS side gives the same $5.95.

Reference figure: `PUI-CAP-SAMPLE-120` — 700 units received, `value_in` $23,262.12,
true landed rate **$33.2316/unit**.

## 4. The constraint that makes this hard

Any correction must satisfy **both** of these at once:

1. `SUM(Bin.stock_value)` must still equal the GL `Stock - *` accounts, to the cent.
2. The **landed cost must not change**. Cost of goods is set by the invoice + Landed Cost
   Voucher. An issue consumes it; it must never rewrite it.

A GL-only correction (e.g. a Journal Entry Dr `Stock Adjustment` / Cr `Stock`) satisfies (2) but
**breaks (1)** — the ledger moves and `Bin` does not.

## 5. What was tried and why it FAILED — do not repeat this

**Attempt:** a corrective **Landed Cost Voucher** with negative `applicable_charges` on the
Purchase Receipt rows, booked to `Stock Adjustment - PUI`, run monthly.
(This is what `plans/LANDED_COST_TOOL_PHASE_A8_FIX_PLAN_V1.md` gap #2 prescribes, and the
endpoint `POST /v1/ledger/landed-cost-voucher/correction` already existed for it.)

**It was posted live on 2026-07-12** as `MAT-LCV-2026-00020` (−$7.94 on `MAT-PRE-2026-00009`)
and `MAT-LCV-2026-00021` (+$1.98 on `MAT-PRE-2026-00010`). **Both were cancelled.**

### Why it is the wrong instrument

An LCV works by **changing the receipt's landed cost** and then **retroactively reposting every
downstream stock movement**. That collides head-on with both constraints:

- It violates constraint (2) by definition — changing landed cost is what an LCV *is*.
- The repost recomputes outgoing SLE values from the running moving average, which would
  **overwrite the pinned `basic_rate` on every Material Issue since April**, rewriting COGS
  across the whole period. That destroys the very design the pinning exists to protect.

### What actually happened when it posted

ERPNext posts the LCV's **GL entries immediately** but queues the **Bin/valuation repost** as a
background `Repost Item Valuation` job. That queue was **stalled** (39 rows queued, oldest from
2026-05-02, none executing). Result:

| | Before | After the LCVs | After cancelling |
|---|---|---|---|
| `Bin.stock_value` | 79,366.39 | 79,366.39 | 79,366.39 |
| GL `Stock` (Coffee+Machines) | 79,366.39 | **79,360.44** | 79,366.39 |
| Bin − GL | 0.00 | **5.95 ← BROKEN** | 0.00 |
| `Stock Adjustment - PUI` | 0.00 | 5.96 | 0.00 |
| Trial balance | balanced | balanced | balanced |

The ledger moved, the item valuation did not. The invariant broke. Both vouchers were cancelled
and the books returned to baseline. **No repost was ever executed, so the pinned rates were never
touched.**

## 6. Suggested direction (NOT implemented, NOT validated)

A **Stock Reconciliation** (revaluation) writes the new valuation into `Bin` **and** posts the
offset to `Stock Adjustment` in the **same voucher** — both sides move together, so constraint
(1) holds, no history is reposted, and the pinned rates are never read.

Open questions that must be answered before building it:

- A Stock Reconciliation emits an SLE with `actual_qty = 0` and a non-zero
  `stock_value_difference`. That row would be picked up by the `landed_rate` formula in §3 and
  distort it. The measurement must **exclude** Stock Reconciliation revaluations from the
  landed-cost basis — they are corrections, not landed cost.
- Does a Stock Reconciliation itself trigger a `Repost Item Valuation`? If so, the same stalled-
  queue / pinned-rate hazard applies and this approach is **also** unsafe. **Verify this first.**
- The residue must be swept **periodically** (it is a leak, not a one-off). Cadence decided by
  the operator: monthly, always post regardless of size, and alert loudly above $1.00 (a residue
  that large is a bug, not rounding).

## 7. Current state (verified 2026-07-13)

```
Bin.stock_value            :  79,366.39
GL Stock (Coffee+Machines) :  79,366.39
Bin - GL                   :       0.00   OK
Stock Adjustment - PUI     :       0.00
Inventory Disposal Loss    :       0.00
Trial balance Dr / Cr      : 765,382.18 / 765,382.18   OK
tabSeries MAT-LCV-2026-    : 18
Repost Item Valuation Queued: 0   (all 39 deleted so they can never run)
Draft Stock Entries        : 0
```

- The **$5.95 residue is still present and uncorrected.**
- All code written for the failed attempt has been **removed** (`grep -r valuation_residue` returns
  nothing). The affected files are byte-identical to git HEAD.
- Cancelled as part of the failed attempt: `MAT-LCV-2026-00020`, `MAT-LCV-2026-00021`.

## 8. Related, still-open

**The 4 disposed units.** Flexport's Inventory Transaction History has 2 rows with
`Category = "Disposed"` totalling **4 units of `PUI-CAP-SAMPLE-120`**, both in **Quarantine**.
At $33.2316 landed that is **$132.93**, currently **not booked** — inventory is overstated by
that amount as well.

Operator rules that any future disposal automation MUST respect:

- **Only `Category = "Disposed"` is a real disposal.** `Adjustment` is Flexport's own
  cycle-count/counting churn (it nets −1,679 units over 365 days and means nothing). A previous
  job booked `Adjustment` as disposal and had to be cancelled; it is now disabled in
  `celery_app.py` and must not be re-enabled.
- **Only Quarantine needs to be looked at.** Items in Quarantine are held; Flexport **waits for
  the operator's import decision before disposing**. They stay in Quarantine until the operator
  acts. So Quarantine stock is *not* automatically a loss.
- **No disposal fee has ever been billed.** Every line item across all 9 booked Flexport invoices
  (bills 639186 → 659863) was read: the charge types are Fulfillment, Storage (DTC + Reserve),
  Returns-Shipping, Prep/BCL Labels, Non-Compliance (B2B + DTC), Over Receive, Unexpected SKU,
  Missing Container Label, and credits. **There is no disposal/removal/destruction charge on any
  invoice.**
