feat(receipt-hunt): convert a foreign receipt instead of refusing to compare it (#1497)
* feat(receipt-hunt): compare a foreign receipt by converting it, not by refusing A Swedish bank posts a converted figure for a purchase abroad while the receipt states the original: Anthropic bills 180,00 EUR and the statement reads -2 014,32 kr. Neither number appears in the other document, so the matcher refused the pair rather than guess. On a SaaS-heavy ledger that is not an edge case: of 25 receipts fetched from a real company's mailboxes, 14 were in USD or EUR and none could ever pair. The receipt's total is now resolved into kronor with Riksbanken's rate for its own date, and handed to the same matcher, which still wants the merchant and the date to agree. The seam already existed: the scorer passed null where a SEK value would go, with a comment explaining that cross-currency pairs were deliberately incomparable. Surfaces that do not resolve a rate still pass nothing and behave exactly as before. Rates are fetched once per currency and day. Riksbanken answers 429 to a caller that asks per document, and a run holds a dozen receipts from one vendor in one month. A rate that cannot be resolved leaves the receipt exactly as incomparable as it was. Two calibration faults surfaced once the amounts became comparable: A converted total is judged at 9% rather than 5%. Riksbanken publishes a mid rate and a card issuer charges its own, so the two carry a known spread on top of any disagreement about the sum: measured against real statements, 1.2% to 3%. Holding both to one bar treats a rate spread as if it were a discrepancy. The date tolerance moves from 3 days to 10. A card settles days after the purchase, an international one routinely a week later, and a forwarded receipt carries the purchase date while the statement carries the posting. At three days the signal scored zero for ordinary correct pairs and took a quarter of the weight with it: a receipt agreeing to within 1%, from a merchant the matcher recognised, still capped at 0.62. The 27 human- confirmed pairs and the 7 near-misses that must not match all still hold. Measured on that ledger: purchases with any candidate at all go 9 -> 13, and the Anthropic pair proposes at 0.82 where it was previously unscoreable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(receipt-hunt): an undated receipt is not converted at today's rate Raised in review. A foreign receipt with no invoiceDate fell back to the current date, which put a receipt of unknown age into amount matching on the strength of a guess: a rate two years out is how something incomparable acquires confidence it has not earned. No date, no conversion, and the receipt stays exactly as it was. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
Jakob Wennberg
parent
1d635b0d25
commit
4c479f84f4
@@ -6,8 +6,35 @@
|
||||
*/
|
||||
|
||||
// Matching configuration (re-exported for consumers)
|
||||
export const DATE_TOLERANCE_DAYS = 3
|
||||
/**
|
||||
* How far a receipt's date may sit from the bank's before the date stops
|
||||
* counting as agreement.
|
||||
*
|
||||
* Ten days, not three. A card purchase settles days after it happens, an
|
||||
* international one routinely a week later, and a forwarded receipt carries
|
||||
* the date of the purchase while the statement carries the date of the
|
||||
* posting. At three days the signal scored zero for ordinary, correct pairs
|
||||
* and took a quarter of the weight down with it: a receipt matching to within
|
||||
* 1% from a merchant the matcher recognised still capped at 0.62, under every
|
||||
* threshold that decides anything.
|
||||
*
|
||||
* The date remains real evidence at this width. A receipt from March still
|
||||
* disagrees with a purchase in September.
|
||||
*/
|
||||
export const DATE_TOLERANCE_DAYS = 10
|
||||
export const AMOUNT_TOLERANCE_PERCENT = 0.05
|
||||
|
||||
/**
|
||||
* Tolerance for a total that had to be converted into kronor first.
|
||||
*
|
||||
* A same-currency comparison is two readings of one number, so 5% is generous.
|
||||
* A converted one carries a second, known error: Riksbanken publishes a mid
|
||||
* rate and a card issuer charges its own, typically a point or two away, on a
|
||||
* settlement day that need not be the receipt's. Holding both to the same bar
|
||||
* treats a rate spread as if it were a disagreement about the sum. Measured
|
||||
* against real statements, the spread ran 1.2% to 3%.
|
||||
*/
|
||||
export const CONVERTED_AMOUNT_TOLERANCE_PERCENT = 0.09
|
||||
export const MIN_MATCH_CONFIDENCE = 0.4
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user