lib: better inference for unknown account names in csv parser
This commit is contained in:
		
							parent
							
								
									b5d4918c16
								
							
						
					
					
						commit
						32cbe4c7b3
					
				@ -738,19 +738,12 @@ transactionFromCsvRecord sourcepos rules record = t
 | 
				
			|||||||
      ,"the parse error is:      "++customErrorBundlePretty err
 | 
					      ,"the parse error is:      "++customErrorBundlePretty err
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    unknownAccountForAmount amt =
 | 
					    parsePosting' number accountFld amountFld amountInFld amountOutFld balanceFld commentFld =
 | 
				
			||||||
      case isNegativeMixedAmount amt of
 | 
					 | 
				
			||||||
        Just True -> "income:unknown"
 | 
					 | 
				
			||||||
        Just False -> "expense:unknown"
 | 
					 | 
				
			||||||
        _ -> "unknown"
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
    parsePosting' number accountFld amtForUnknownAccount amountFld amountInFld amountOutFld balanceFld commentFld =
 | 
					 | 
				
			||||||
      let currency = maybe (fromMaybe "" mdefaultcurrency) render $
 | 
					      let currency = maybe (fromMaybe "" mdefaultcurrency) render $
 | 
				
			||||||
                      (mfieldtemplate ("currency"++number) `or `mfieldtemplate "currency")
 | 
					                      (mfieldtemplate ("currency"++number) `or `mfieldtemplate "currency")
 | 
				
			||||||
          amount = chooseAmountStr rules record currency amountFld amountInFld amountOutFld                      
 | 
					          amount = chooseAmount rules record currency amountFld amountInFld amountOutFld                      
 | 
				
			||||||
          account' = ((T.pack . render) <$> (mfieldtemplate accountFld
 | 
					          account' = ((T.pack . render) <$> (mfieldtemplate accountFld
 | 
				
			||||||
                                           `or` mdirective ("default-account" ++ number)))
 | 
					                                           `or` mdirective ("default-account" ++ number)))
 | 
				
			||||||
                    `or` (unknownAccountForAmount <$> amtForUnknownAccount)
 | 
					 | 
				
			||||||
          balance = (parsebalance currency number.render) =<< mfieldtemplate balanceFld
 | 
					          balance = (parsebalance currency number.render) =<< mfieldtemplate balanceFld
 | 
				
			||||||
          comment = T.pack $ maybe "" render $ mfieldtemplate commentFld
 | 
					          comment = T.pack $ maybe "" render $ mfieldtemplate commentFld
 | 
				
			||||||
          account =
 | 
					          account =
 | 
				
			||||||
@ -759,29 +752,26 @@ transactionFromCsvRecord sourcepos rules record = t
 | 
				
			|||||||
              Nothing ->
 | 
					              Nothing ->
 | 
				
			||||||
                -- If we have amount or balance assertion (which implies potential amount change),
 | 
					                -- If we have amount or balance assertion (which implies potential amount change),
 | 
				
			||||||
                -- but no account name, lets generate "unknown" account name.
 | 
					                -- but no account name, lets generate "unknown" account name.
 | 
				
			||||||
                -- If we can figure out whether this is income or expense based on amount, do that
 | 
					 | 
				
			||||||
                -- otherwise stick to "unknown"
 | 
					 | 
				
			||||||
                case (amount, balance) of
 | 
					                case (amount, balance) of
 | 
				
			||||||
                  (Just amt, _ ) -> Just $ unknownAccountForAmount amt
 | 
					                  (Just _, _ ) -> Just "unknown"
 | 
				
			||||||
                  (_, Just _)  -> Just "unknown"
 | 
					                  (_, Just _)  -> Just "unknown"
 | 
				
			||||||
                  (Nothing, Nothing) -> Nothing
 | 
					                  (Nothing, Nothing) -> Nothing
 | 
				
			||||||
          in
 | 
					          in
 | 
				
			||||||
        case account of
 | 
					        case account of
 | 
				
			||||||
          Nothing -> Nothing
 | 
					          Nothing -> Nothing
 | 
				
			||||||
          Just account -> 
 | 
					          Just account -> 
 | 
				
			||||||
            Just $ posting {paccount=account, pamount=fromMaybe nullmixedamt amount, ptransaction=Just t, pbalanceassertion=toAssertion <$> balance, pcomment = comment}
 | 
					            Just $ posting {paccount=account, pamount=fromMaybe missingmixedamt amount, ptransaction=Just t', pbalanceassertion=toAssertion <$> balance, pcomment = comment}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parsePosting number =              
 | 
					    parsePosting number =              
 | 
				
			||||||
      parsePosting' number
 | 
					      parsePosting' number
 | 
				
			||||||
      ("account"++number)
 | 
					      ("account"++number)
 | 
				
			||||||
      Nothing
 | 
					 | 
				
			||||||
      ("amount"++number)
 | 
					      ("amount"++number)
 | 
				
			||||||
      ("amount"++number++"-in")
 | 
					      ("amount"++number++"-in")
 | 
				
			||||||
      ("amount"++number++"-out")
 | 
					      ("amount"++number++"-out")
 | 
				
			||||||
      ("balance"++number)
 | 
					      ("balance"++number)
 | 
				
			||||||
      ("comment" ++ number)
 | 
					      ("comment" ++ number)
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    postingLegacy = parsePosting' "" "account1" Nothing "amount" "amount-in" "amount-out" "balance" "comment1"
 | 
					    postingLegacy = parsePosting' "" "account1" "amount" "amount-in" "amount-out" "balance" "comment1"
 | 
				
			||||||
    posting1' = parsePosting "1"
 | 
					    posting1' = parsePosting "1"
 | 
				
			||||||
    posting1 =
 | 
					    posting1 =
 | 
				
			||||||
      case (postingLegacy,posting1') of
 | 
					      case (postingLegacy,posting1') of
 | 
				
			||||||
@ -807,28 +797,37 @@ transactionFromCsvRecord sourcepos rules record = t
 | 
				
			|||||||
                                         , "amount/amount-in/amount-out is " ++ showMixedAmount al
 | 
					                                         , "amount/amount-in/amount-out is " ++ showMixedAmount al
 | 
				
			||||||
                                         , "amount1/amount1-in/amount1-out is " ++ showMixedAmount a1
 | 
					                                         , "amount1/amount1-in/amount1-out is " ++ showMixedAmount a1
 | 
				
			||||||
                                         ]
 | 
					                                         ]
 | 
				
			||||||
          in posting {paccount=paccount posting1, pamount=amount, ptransaction=Just t, pbalanceassertion=balanceassertion, pcomment = pcomment posting1}
 | 
					          in posting {paccount=paccount posting1, pamount=amount, ptransaction=Just t', pbalanceassertion=balanceassertion, pcomment = pcomment posting1}
 | 
				
			||||||
        (Nothing, Nothing) -> error' $ unlines [ "sadly, no posting was generated for account1"
 | 
					        (Nothing, Nothing) -> error' $ unlines [ "sadly, no posting was generated for account1, cannot generate transaction"
 | 
				
			||||||
                                               , showRecord record
 | 
					                                               , showRecord record
 | 
				
			||||||
                                               , showRules rules record
 | 
					                                               , showRules rules record
 | 
				
			||||||
                                               ]
 | 
					                                               ]
 | 
				
			||||||
    -- Posting 2 is special -- if there are no postings 3-9, we want to preserve legacy behaviour and
 | 
					    postings2to9 = catMaybes $ [ parsePosting i | x<-[2..9], let i = show x]
 | 
				
			||||||
    -- we want account to be income:unknown or expense:unknown if it is not specified,
 | 
					 | 
				
			||||||
    -- based on the amount from posting 1
 | 
					 | 
				
			||||||
    postings3to9 = catMaybes $ [ parsePosting i | x<-[3..9], let i = show x]
 | 
					 | 
				
			||||||
    postings =
 | 
					    postings =
 | 
				
			||||||
      if postings3to9 == []
 | 
					      if postings2to9 == []
 | 
				
			||||||
      then [fromMaybe justOnePostingError $ parsePosting' "2" "account2" (Just $ negate $ pamount posting1) "amount2" "amount2-in" "amount2-out" "balance2" "comment2"]
 | 
					      then [posting1,posting{paccount="unknown", pamount=missingmixedamt, ptransaction=Just t'}]
 | 
				
			||||||
      else case parsePosting "2" of
 | 
					      else posting1:postings2to9
 | 
				
			||||||
        Just posting2 -> posting2:postings3to9
 | 
					 | 
				
			||||||
        Nothing -> postings3to9
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    justOnePostingError = error' $ unlines [ "Found single posting, cannot generate transaction"
 | 
					    balanced = balanceTransaction Nothing t'
 | 
				
			||||||
                                           , showRecord record
 | 
					    t =
 | 
				
			||||||
                                           , showRules rules record
 | 
					      case balanced of
 | 
				
			||||||
                                           ]                                           
 | 
					        Left _ -> t'
 | 
				
			||||||
 | 
					        Right balanced ->
 | 
				
			||||||
 | 
					          -- If we managed to balance transaction, lets infer better names for all "unknown" accounts
 | 
				
			||||||
 | 
					          t' {tpostings =
 | 
				
			||||||
 | 
					              [ originalPosting {paccount=newAccount}
 | 
				
			||||||
 | 
					              | (originalPosting,p) <- zip postings (tpostings balanced)
 | 
				
			||||||
 | 
					              , let account = paccount p
 | 
				
			||||||
 | 
					              , let newAccount =
 | 
				
			||||||
 | 
					                      if account/="unknown"
 | 
				
			||||||
 | 
					                      then account
 | 
				
			||||||
 | 
					                      else case isNegativeMixedAmount (pamount p) of
 | 
				
			||||||
 | 
					                             Just True -> "income:unknown"
 | 
				
			||||||
 | 
					                             Just False -> "expense:unknown"
 | 
				
			||||||
 | 
					                             _ -> "unknown"
 | 
				
			||||||
 | 
					              ]}
 | 
				
			||||||
    -- build the transaction
 | 
					    -- build the transaction
 | 
				
			||||||
    t = nulltransaction{
 | 
					    t' = nulltransaction{
 | 
				
			||||||
      tsourcepos               = genericSourcePos sourcepos,
 | 
					      tsourcepos               = genericSourcePos sourcepos,
 | 
				
			||||||
      tdate                    = date',
 | 
					      tdate                    = date',
 | 
				
			||||||
      tdate2                   = mdate2',
 | 
					      tdate2                   = mdate2',
 | 
				
			||||||
@ -837,15 +836,15 @@ transactionFromCsvRecord sourcepos rules record = t
 | 
				
			|||||||
      tdescription             = T.pack description,
 | 
					      tdescription             = T.pack description,
 | 
				
			||||||
      tcomment                 = T.pack comment,
 | 
					      tcomment                 = T.pack comment,
 | 
				
			||||||
      tprecedingcomment        = T.pack precomment,
 | 
					      tprecedingcomment        = T.pack precomment,
 | 
				
			||||||
      tpostings                = posting1:postings
 | 
					      tpostings                = postings
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    toAssertion (a, b) = assertion{
 | 
					    toAssertion (a, b) = assertion{
 | 
				
			||||||
      baamount   = a,
 | 
					      baamount   = a,
 | 
				
			||||||
      baposition = b
 | 
					      baposition = b
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
chooseAmountStr :: CsvRules -> CsvRecord -> String -> String -> String -> String -> Maybe MixedAmount
 | 
					chooseAmount :: CsvRules -> CsvRecord -> String -> String -> String -> String -> Maybe MixedAmount
 | 
				
			||||||
chooseAmountStr rules record currency amountFld amountInFld amountOutFld =
 | 
					chooseAmount rules record currency amountFld amountInFld amountOutFld =
 | 
				
			||||||
 let
 | 
					 let
 | 
				
			||||||
   mamount    = getEffectiveAssignment rules record amountFld
 | 
					   mamount    = getEffectiveAssignment rules record amountFld
 | 
				
			||||||
   mamountin  = getEffectiveAssignment rules record amountInFld
 | 
					   mamountin  = getEffectiveAssignment rules record amountInFld
 | 
				
			||||||
 | 
				
			|||||||
@ -218,7 +218,7 @@ account3   expenses:tax
 | 
				
			|||||||
$  ./hledger-csv
 | 
					$  ./hledger-csv
 | 
				
			||||||
2009/09/10 Flubber Co
 | 
					2009/09/10 Flubber Co
 | 
				
			||||||
    assets:myacct              $50 = $321
 | 
					    assets:myacct              $50 = $321
 | 
				
			||||||
    unknown           = $123
 | 
					    income:unknown     = $123
 | 
				
			||||||
    expenses:tax            $0.234  ; VAT
 | 
					    expenses:tax            $0.234  ; VAT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
>=0
 | 
					>=0
 | 
				
			||||||
@ -346,8 +346,8 @@ amount %2
 | 
				
			|||||||
date %1
 | 
					date %1
 | 
				
			||||||
date-format %Y/%m/%d
 | 
					date-format %Y/%m/%d
 | 
				
			||||||
$  ./hledger-csv | hledger balance -f - --no-total 
 | 
					$  ./hledger-csv | hledger balance -f - --no-total 
 | 
				
			||||||
 | 
					           $1,001.00  expense:unknown
 | 
				
			||||||
          $-1,001.00  income:unknown
 | 
					          $-1,001.00  income:unknown
 | 
				
			||||||
           $1,001.00  unknown
 | 
					 | 
				
			||||||
>=0
 | 
					>=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 18. Conditional skips
 | 
					# 18. Conditional skips
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user