@@ -125,7 +125,7 @@ import Hledger.Data.AccountName (accountNameType)
125125import Hledger.Data.Dates (showDate )
126126import Hledger.Data.AccountType (isAssetType , isEquityType )
127127import Hledger.Data.Amount (amountSetQuantity , amountsRaw , isNegativeAmount , maNegate , mapMixedAmount , mixedAmount , mixedAmountLooksZero , nullmixedamt , noCostFmt , showAmountWith , showMixedAmountOneLine )
128- import Hledger.Data.Errors (makePostingErrorExcerpt , makeTransactionErrorExcerpt )
128+ import Hledger.Data.Errors (makePostingErrorExcerpt , makePostingErrorExcerptByIndex , makeTransactionErrorExcerpt )
129129import Hledger.Data.Journal (journalAccountType , journalCommodityLotsMethod , journalCommodityUsesLots , journalFilePaths , journalInheritedAccountTags , journalMapTransactions , journalTieTransactions , parseReductionMethod )
130130import Hledger.Data.Posting (generatedPostingTagName , hasAmount , isReal , nullposting , originalPosting , postingAddHiddenAndMaybeVisibleTag , postingStripCosts )
131131import Hledger.Data.Transaction (txnTieKnot )
@@ -590,7 +590,7 @@ transactionClassifyLotPostings verbosetags lookupAccountType commodityIsLotful t
590590journalCalculateLots :: Bool -> Journal -> Either String Journal
591591journalCalculateLots verbosetags j
592592 | not $ any (any isLotPosting . tpostings) txns = do
593- mapM_ checkUnclassified [p | t <- txns, p <- tpostings t]
593+ mapM_ checkUnclassified [(t, i, p) | t <- txns, (i, p) <- zip [ 0 .. ] ( tpostings t) ]
594594 Right j
595595 | otherwise = do
596596 validateUserLabels txns
@@ -599,8 +599,8 @@ journalCalculateLots verbosetags j
599599 Right (journalTieTransactions $ j{jtxns = reverse txns'})
600600 where
601601 txns = jtxns j
602- checkUnclassified p
603- | isUnclassifiedLotfulPosting j p = Left (unclassifiedLotWarning j p)
602+ checkUnclassified (t, i, p)
603+ | isUnclassifiedLotfulPosting j p = Left (unclassifiedLotWarning j t i p)
604604 | otherwise = Right ()
605605
606606-- Disposal balancing
@@ -770,17 +770,19 @@ isUnclassifiedLotfulPosting j p =
770770 | hasAccountLotsTag = any ((/= 0 ) . aquantity) amts
771771 | otherwise = False
772772
773- -- | Build a warning message for an unclassified lotful posting.
774- unclassifiedLotWarning :: Journal -> Posting -> String
775- unclassifiedLotWarning j p =
773+ -- | Build an error message for an unclassified lotful posting.
774+ -- Takes the transaction and the 0-based posting index for precise source location.
775+ unclassifiedLotWarning :: Journal -> Transaction -> Int -> Posting -> String
776+ unclassifiedLotWarning j t idx p =
776777 let amts = amountsRaw (pamount p)
777778 lotfulCommodities = [acommodity a | a <- amts, journalCommodityUsesLots j (acommodity a)]
778779 hasAccountTag = any ((== " lots" ) . T. toLower . fst ) (ptags p)
779780 source = case (lotfulCommodities, hasAccountTag) of
780781 (c: _, _) -> T. unpack c ++ " is declared lotful (commodity lots: tag)"
781782 ([] , True ) -> T. unpack (paccount p) ++ " is declared lotful (account lots: tag)"
782783 _ -> " posting involves a lotful commodity or account"
783- in postingErrPrefix p
784+ (f, line, _, ex) = makePostingErrorExcerptByIndex t idx
785+ in printf " %s:%d:\n %s\n " f line ex
784786 ++ source ++ " but this posting was not classified as\n "
785787 ++ " acquire, dispose, or transfer. Lot state will not be updated.\n "
786788 ++ " Possible fixes: add a cost basis ({$X}), a price (@ $X),\n "
@@ -937,7 +939,7 @@ processTransaction verbosetags j needsLabels (ls, acc) t = do
937939 (st', newPs) <- processDisposePosting verbosetags j t st p
938940 foldMPostings st' (reverse newPs ++ acc') ps tmap
939941 | isUnclassifiedLotfulPosting j p =
940- Left (unclassifiedLotWarning j p)
942+ Left (unclassifiedLotWarning j t i p)
941943 | otherwise =
942944 foldMPostings st (p: acc') ps tmap
943945
0 commit comments