Skip to content

Commit 889ca21

Browse files
committed
cli: support for new layout bare-wide
1 parent 38cc454 commit 889ca21

3 files changed

Lines changed: 115 additions & 38 deletions

File tree

hledger-lib/Hledger/Reports/ReportOptions.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ instance Default AccountListMode where def = ALFlat
124124
data Layout = LayoutWide (Maybe Int)
125125
| LayoutTall
126126
| LayoutBare
127+
| LayoutBareWide
127128
| LayoutTidy
128129
deriving (Eq, Show)
129130

@@ -432,6 +433,7 @@ layoutopt rawopts = fromMaybe (LayoutWide Nothing) $ layout <|> column
432433
, ("tall", LayoutTall)
433434
, ("bare", LayoutBare)
434435
, ("tidy", LayoutTidy)
436+
, ("bare-wide", LayoutBareWide)
435437
]
436438
-- For `--layout=elided,n`, elide to the given width
437439
(s,n) = break (==',') $ map toLower opt
@@ -440,7 +442,7 @@ layoutopt rawopts = fromMaybe (LayoutWide Nothing) $ layout <|> column
440442
c | Just w' <- readMay c -> Just w'
441443
_ -> usageError "width in --layout=wide,WIDTH must be an integer"
442444

443-
err = usageError "--layout's argument should be \"wide[,WIDTH]\", \"tall\", \"bare\", or \"tidy\""
445+
err = usageError "--layout's argument should be \"wide[,WIDTH]\", \"tall\", \"bare\", \"bare-wide\", or \"tidy\""
444446

445447
-- Get the period specified by any -b/--begin, -e/--end and/or -p/--period
446448
-- options appearing in the command line.

hledger/Hledger/Cli/Commands/Balance.hs

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ import Hledger.Write.Ods (printFods)
308308
import Hledger.Write.Html (Html, styledTableHtml, htmlAsLazyText, toHtml)
309309
import Hledger.Write.Spreadsheet (rawTableContent, headerCell,
310310
addHeaderBorders, addRowSpanHeader,
311-
cellFromMixedAmount, cellsFromMixedAmount)
311+
cellFromMixedAmount, cellsFromMixedAmount, cellFromAmount)
312312
import Hledger.Write.Spreadsheet qualified as Ods
313313

314314

@@ -635,6 +635,9 @@ renderComponent topaligned oneline opts (acctname, dep, total) (FormatField ljus
635635
}
636636

637637

638+
headerWithoutBorders :: [Ods.Cell () text] -> [Ods.Cell Ods.NumLines text]
639+
headerWithoutBorders = map (\c -> c {Ods.cellBorder = Ods.noBorder})
640+
638641
simpleDateSpanCell :: PeriodTitles -> DateSpan -> Ods.Cell Ods.NumLines Text
639642
simpleDateSpanCell ph = Ods.defaultCell . renderPeriodHeading ph
640643

@@ -663,8 +666,11 @@ balanceReportAsSpreadsheet fmt opts (items, total) =
663666
headers =
664667
addHeaderBorders $ map headerCell $
665668
"account" : case layout_ opts of
669+
LayoutBareWide -> allCommodities
666670
LayoutBare -> ["commodity", "balance"]
667671
_ -> ["balance"]
672+
allCommodities =
673+
S.toAscList $ foldMap (\(_,_,_,ma) -> maCommodities ma) items
668674
rows ::
669675
RowClass -> BalanceReportItem ->
670676
[[Ods.Cell Ods.NumLines Text]]
@@ -678,6 +684,15 @@ balanceReportAsSpreadsheet fmt opts (items, total) =
678684
Value -> renderBalanceAcct opts nbsp (name, dispName, dep) in
679685
addRowSpanHeader accountCell $
680686
case layout_ opts of
687+
LayoutBareWide ->
688+
let bopts =
689+
machineFmt {
690+
displayCommodity = False,
691+
displayCommodityOrder = Just allCommodities
692+
} in
693+
[map (\bldAmt ->
694+
fmap wbToText $ cellFromAmount bopts (amountClass rc, bldAmt)) $
695+
showMixedAmountLinesPartsB bopts ma]
681696
LayoutBare ->
682697
map (\a -> [cell $ acommodity a, renderAmount rc $ mixedAmount a])
683698
. amounts $ mixedAmountStripCosts ma
@@ -701,29 +716,41 @@ balanceReportAsSpreadsheet fmt opts (items, total) =
701716
multiBalanceReportAsCsv :: ReportOpts -> MultiBalanceReport -> CSV
702717
multiBalanceReportAsCsv opts@ReportOpts{..} report =
703718
(if transpose_ then transpose else id) $
704-
rawTableContent $ header : body ++ totals
719+
rawTableContent $ header ++ body ++ totals
705720
where
706721
(header, body, totals) =
707-
multiBalanceReportAsSpreadsheetParts machineFmt opts report
722+
multiBalanceReportAsSpreadsheetParts machineFmt opts
723+
(allCommoditiesFromPeriodicReport $ prRows report) report
708724

709725
-- | Render the Spreadsheet table rows (CSV, ODS, HTML) for a MultiBalanceReport.
710726
-- Returns the heading row, 0 or more body rows, and the totals row if enabled.
711727
multiBalanceReportAsSpreadsheetParts ::
712-
AmountFormat -> ReportOpts -> MultiBalanceReport ->
713-
([Ods.Cell Ods.NumLines Text],
728+
AmountFormat -> ReportOpts ->
729+
[CommoditySymbol] -> MultiBalanceReport ->
730+
([[Ods.Cell Ods.NumLines Text]],
714731
[[Ods.Cell Ods.NumLines Text]],
715732
[[Ods.Cell Ods.NumLines Text]])
716-
multiBalanceReportAsSpreadsheetParts fmt opts@ReportOpts{..} (PeriodicReport colspans items tr) =
717-
(headers, concatMap fullRowAsTexts items, addTotalBorders totalrows)
733+
multiBalanceReportAsSpreadsheetParts fmt opts@ReportOpts{..}
734+
allCommodities (PeriodicReport colspans items tr) =
735+
(allHeaders, concatMap fullRowAsTexts items, addTotalBorders totalrows)
718736
where
719737
accountCell label =
720738
(Ods.defaultCell label) {Ods.cellClass = Ods.Class "account"}
721739
hCell cls label = (headerCell label) {Ods.cellClass = Ods.Class cls}
740+
allHeaders =
741+
case layout_ of
742+
LayoutBareWide ->
743+
[headerWithoutBorders $
744+
Ods.emptyCell :
745+
concatMap (Ods.horizontalSpan allCommodities) dateHeaders,
746+
headers]
747+
_ -> [headers]
722748
headers =
723749
addHeaderBorders $
724750
hCell "account" "account" :
725751
case layout_ of
726752
LayoutTidy -> map headerCell tidyColumnLabels
753+
LayoutBareWide -> dateHeaders >> map headerCell allCommodities
727754
LayoutBare -> headerCell "commodity" : dateHeaders
728755
_ -> dateHeaders
729756
dateHeaders =
@@ -744,7 +771,7 @@ multiBalanceReportAsSpreadsheetParts fmt opts@ReportOpts{..} (PeriodicReport col
744771
rowAsText Total (simpleDateSpanCell period_titles_) tr
745772
rowAsText rc dsCell =
746773
map (map (fmap wbToText)) .
747-
multiBalanceRowAsCellBuilders fmt opts colspans rc dsCell
774+
multiBalanceRowAsCellBuilders fmt opts colspans allCommodities rc dsCell
748775

749776
tidyColumnLabels :: [Text]
750777
tidyColumnLabels =
@@ -764,10 +791,12 @@ multiBalanceReportAsSpreadsheet ::
764791
((Int, Int), [[Ods.Cell Ods.NumLines Text]])
765792
multiBalanceReportAsSpreadsheet ropts mbr =
766793
let (header,body,total) =
767-
multiBalanceReportAsSpreadsheetParts oneLineNoCostFmt ropts mbr
794+
multiBalanceReportAsSpreadsheetParts oneLineNoCostFmt ropts
795+
(allCommoditiesFromPeriodicReport $ prRows mbr) mbr
768796
in (if transpose_ ropts then swap *** Ods.transpose else id) $
769-
((1, case layout_ ropts of LayoutWide _ -> 1; _ -> 0),
770-
header : body ++ total)
797+
((case layout_ ropts of LayoutBareWide -> 2; _ -> 1,
798+
case layout_ ropts of LayoutWide _ -> 1; _ -> 0),
799+
header ++ body ++ total)
771800

772801

773802
-- | Render a multi-column balance report as plain text suitable for console output.
@@ -839,10 +868,20 @@ multiBalanceReportAsTable opts@ReportOpts{summary_only_, average_, balanceaccum_
839868
(Group multiColumnTableInterColumnBorder $ map Header colheadings)
840869
(concat rows)
841870
where
842-
colheadings = ["Commodity" | layout_ opts == LayoutBare]
843-
++ (if not summary_only_ then map (reportPeriodName (period_titles_ opts) balanceaccum_ spans) spans else [])
844-
++ [" Total" | multiBalanceHasTotalsColumn opts]
845-
++ ["Average" | average_]
871+
colheadings =
872+
["Commodity" | layout_ opts == LayoutBare]
873+
++ (if not summary_only_
874+
then
875+
let spanNames =
876+
map (reportPeriodName (period_titles_ opts) balanceaccum_ spans) spans in
877+
case layout_ opts of
878+
LayoutBareWide ->
879+
liftA2 (\s c -> T.concat [s, " (", c, ")"]) spanNames allCommodities
880+
_ -> spanNames
881+
else [])
882+
++ [" Total" | multiBalanceHasTotalsColumn opts]
883+
++ ["Average" | average_]
884+
allCommodities = allCommoditiesFromPeriodicReport items
846885
(accts, rows) = unzip $ fmap fullRowAsTexts items'
847886
where
848887
isLeaf rs row = not $ any (\r -> T.isPrefixOf (displayFull (prrName row) <> ":") (displayFull (prrName r))) rs
@@ -851,12 +890,12 @@ multiBalanceReportAsTable opts@ReportOpts{summary_only_, average_, balanceaccum_
851890
else items
852891
fullRowAsTexts row = (replicate (length rs) (renderacct row), rs)
853892
where
854-
rs = multiBalanceRowAsText opts row
893+
rs = multiBalanceRowAsText opts allCommodities row
855894
renderacct row' = renderPeriodicAcct opts " " row'
856895
addtotalrow
857896
| no_total_ opts = id
858897
| otherwise =
859-
let totalrows = multiBalanceRowAsText opts tr
898+
let totalrows = multiBalanceRowAsText opts allCommodities tr
860899
rowhdrs = Group NoLine $ map Header $ totalRowHeadingText : replicate (length totalrows - 1) ""
861900
colhdrs = Header [] -- unused, concatTables will discard
862901
in (flip (concatTables SingleLine) $ Table rowhdrs colhdrs totalrows)
@@ -865,12 +904,17 @@ multiBalanceReportAsTable opts@ReportOpts{summary_only_, average_, balanceaccum_
865904
multiColumnTableInterRowBorder = NoLine
866905
multiColumnTableInterColumnBorder = if pretty_ opts then SingleLine else NoLine
867906

907+
allCommoditiesFromPeriodicReport ::
908+
[PeriodicReportRow a MixedAmount] -> [CommoditySymbol]
909+
allCommoditiesFromPeriodicReport =
910+
S.toAscList . foldMap (foldMap maCommodities . prrAmounts)
911+
868912
multiBalanceRowAsCellBuilders ::
869-
AmountFormat -> ReportOpts -> [DateSpan] ->
913+
AmountFormat -> ReportOpts -> [DateSpan] -> [CommoditySymbol] ->
870914
RowClass -> (DateSpan -> Ods.Cell Ods.NumLines Text) ->
871915
PeriodicReportRow a MixedAmount ->
872916
[[Ods.Cell Ods.NumLines WideBuilder]]
873-
multiBalanceRowAsCellBuilders bopts ropts@ReportOpts{..} colspans
917+
multiBalanceRowAsCellBuilders bopts ropts@ReportOpts{..} colspans allCommodities
874918
rc renderDateSpanCell (PeriodicReportRow _acct as rowtot rowavg) =
875919
case layout_ of
876920
LayoutWide width -> [fmap (cellFromMixedAmount bopts{displayMaxWidth=width}) clsamts]
@@ -881,6 +925,8 @@ multiBalanceRowAsCellBuilders bopts ropts@ReportOpts{..} colspans
881925
. transpose -- each row becomes a list of Text quantities
882926
. map (cellsFromMixedAmount bopts{displayCommodity=False, displayCommodityOrder=Just cs, displayMinWidth=Nothing})
883927
$ clsamts
928+
LayoutBareWide -> [concatMap (cellsFromMixedAmount bopts{displayCommodity=False, displayCommodityOrder=Just allCommodities, displayMinWidth=Nothing})
929+
$ clsamts]
884930
LayoutTidy -> concat
885931
. zipWith (map . addDateColumns) colspans
886932
. map ( zipWith (\c a -> [wbCell c, a]) cs
@@ -923,16 +969,20 @@ multiBalanceHasTotalsColumn :: ReportOpts -> Bool
923969
multiBalanceHasTotalsColumn ropts =
924970
row_total_ ropts && balanceaccum_ ropts `notElem` [Cumulative, Historical]
925971

926-
multiBalanceRowAsText :: ReportOpts -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
927-
multiBalanceRowAsText opts =
972+
multiBalanceRowAsText ::
973+
ReportOpts -> [CommoditySymbol] -> PeriodicReportRow a MixedAmount -> [[WideBuilder]]
974+
multiBalanceRowAsText opts allCommodities =
928975
rawTableContent .
929-
multiBalanceRowAsCellBuilders oneLineNoCostFmt{displayColour=color_ opts} opts []
976+
multiBalanceRowAsCellBuilders oneLineNoCostFmt{displayColour=color_ opts}
977+
opts [] allCommodities
930978
Value (simpleDateSpanCell $ period_titles_ opts)
931979

932-
multiBalanceRowAsCsvText :: ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[T.Text]]
933-
multiBalanceRowAsCsvText opts colspans =
980+
multiBalanceRowAsCsvText ::
981+
ReportOpts -> [DateSpan] -> [CommoditySymbol] ->
982+
PeriodicReportRow a MixedAmount -> [[T.Text]]
983+
multiBalanceRowAsCsvText opts colspans allCommodities =
934984
map (map (wbToText . Ods.cellContent)) .
935-
multiBalanceRowAsCellBuilders machineFmt opts colspans
985+
multiBalanceRowAsCellBuilders machineFmt opts colspans allCommodities
936986
Value (simpleDateSpanCell $ period_titles_ opts)
937987

938988

hledger/Hledger/Cli/CompoundBalanceCommand.hs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Data.List.NonEmpty (NonEmpty((:|)))
2222
import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
2323
import Safe (atMay)
2424
import Data.Map qualified as Map
25+
import Data.Set qualified as Set
2526
import Data.List qualified as List
2627
import Data.List.NonEmpty qualified as NonEmpty
2728
import Data.Text qualified as T
@@ -310,12 +311,14 @@ compoundBalanceReportAsText ropts (CompoundPeriodicReport title _colspans subrep
310311
-- [COL1LINE1, COL2LINE1]
311312
-- [COL1LINE2, COL2LINE2]
312313
-- ]
313-
coltotalslines = multiBalanceRowAsText ropts totalsrow
314+
coltotalslines = multiBalanceRowAsText ropts allCommodities totalsrow
314315
totalstable = Table
315316
(Group NoLine $ map Header $ "Net:" : replicate (length coltotalslines - 1) "") -- row headers
316317
(Header []) -- column headers, concatTables will discard these
317318
coltotalslines -- cell values
318319

320+
allCommodities = allCommoditiesFromSubreports subreports
321+
319322
-- | Convert a named multi balance report to a table suitable for
320323
-- concatenating with others to make a compound balance report table.
321324
-- An empty subreport title is omitted entirely (no title row above the data).
@@ -387,14 +390,24 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
387390
_ -> []
388391
dataHeaders =
389392
(guard (layout_ ropts /= LayoutTidy) >>) $
390-
map (Spr.headerCell . reportPeriodName (period_titles_ ropts) (balanceaccum_ ropts) colspans)
391-
(if not (summary_only_ ropts) then colspans else []) ++
392-
(guard (multiBalanceHasTotalsColumn ropts) >> [Spr.headerCell "Total"]) ++
393-
(guard (average_ ropts) >> [Spr.headerCell "Average"])
393+
map
394+
(dataHeaderCell .
395+
reportPeriodName
396+
(period_titles_ ropts) (balanceaccum_ ropts) colspans)
397+
(if not (summary_only_ ropts) then colspans else []) ++
398+
(guard (multiBalanceHasTotalsColumn ropts) >> [dataHeaderCell "Total"]) ++
399+
(guard (average_ ropts) >> [dataHeaderCell "Average"])
400+
dataHeaderCell label =
401+
(Spr.headerCell label) {Spr.cellSpan = Spr.SpanHorizontal numSubColumns}
394402
headerrow = leadingHeaders ++ dataHeaders
395403

396404
blankrow =
397405
fmap (Spr.horizontalSpan headerrow . Spr.defaultCell) maybeBlank
406+
numSubColumns =
407+
case layout_ ropts of
408+
LayoutBareWide -> length allCommodities
409+
_ -> 1
410+
allCommodities = allCommoditiesFromSubreports subreports
398411

399412
-- Make rows for a subreport: its title row, not the headings row,
400413
-- the data rows, any totals row, and a blank row for whitespace.
@@ -403,15 +416,20 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
403416
subreportrows (subreporttitle, mbr, _increasestotal) =
404417
let
405418
(_, bodyrows, mtotalsrows) =
406-
multiBalanceReportAsSpreadsheetParts fmt ropts mbr
419+
multiBalanceReportAsSpreadsheetParts fmt ropts allCommodities mbr
420+
accountCell =
421+
(Spr.defaultCell subreporttitle) {
422+
Spr.cellStyle = Spr.Body Spr.Total,
423+
Spr.cellClass = Spr.Class "account"
424+
}
407425
titleRows
408426
| T.null subreporttitle = []
409427
| otherwise =
410-
[Spr.horizontalSpan headerrow
411-
((Spr.defaultCell subreporttitle){
412-
Spr.cellStyle = Spr.Body Spr.Total,
413-
Spr.cellClass = Spr.Class "account"
414-
})]
428+
[case layout_ ropts of
429+
LayoutBareWide ->
430+
accountCell :
431+
map Spr.headerCell (dataHeaders >> allCommodities)
432+
_ -> Spr.horizontalSpan headerrow accountCell]
415433
in
416434
titleRows ++
417435
bodyrows ++
@@ -421,7 +439,7 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
421439
totalrows =
422440
if no_total_ ropts || length subreports == 1 then []
423441
else
424-
multiBalanceRowAsCellBuilders fmt ropts colspans
442+
multiBalanceRowAsCellBuilders fmt ropts colspans allCommodities
425443
Total (simpleDateSpanCell $ period_titles_ ropts) totalrow
426444
-- make a table of rendered lines of the report totals row
427445
& map (map (fmap wbToText))
@@ -433,3 +451,10 @@ compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
433451
in (title,
434452
((1,1),
435453
headerrow :| concatMap subreportrows subreports ++ totalrows))
454+
455+
allCommoditiesFromSubreports ::
456+
[(text, PeriodicReport a MixedAmount, bool)] -> [CommoditySymbol]
457+
allCommoditiesFromSubreports =
458+
Set.toAscList .
459+
foldMap (\(_,mbr,_) ->
460+
foldMap (foldMap maCommodities . prrAmounts) $ prRows mbr)

0 commit comments

Comments
 (0)