cli: CompoundBalanceCommand.compoundBalanceReportAsSpreadsheet: common function for CSV, HTML, FODS export
This commit is contained in:
parent
71a7879213
commit
9d1ba5c588
@ -18,6 +18,7 @@ module Hledger.Write.Spreadsheet (
|
|||||||
emptyCell,
|
emptyCell,
|
||||||
transposeCell,
|
transposeCell,
|
||||||
transpose,
|
transpose,
|
||||||
|
horizontalSpan,
|
||||||
addRowSpanHeader,
|
addRowSpanHeader,
|
||||||
rawTableContent,
|
rawTableContent,
|
||||||
) where
|
) where
|
||||||
@ -171,6 +172,15 @@ transpose :: [[Cell border text]] -> [[Cell border text]]
|
|||||||
transpose = List.transpose . map (map transposeCell)
|
transpose = List.transpose . map (map transposeCell)
|
||||||
|
|
||||||
|
|
||||||
|
horizontalSpan ::
|
||||||
|
(Lines border, Monoid text) =>
|
||||||
|
[a] -> Cell border text -> [Cell border text]
|
||||||
|
horizontalSpan subCells cell =
|
||||||
|
zipWith const
|
||||||
|
(cell{cellSpan = SpanHorizontal $ length subCells}
|
||||||
|
: repeat (emptyCell {cellSpan = Covered}))
|
||||||
|
subCells
|
||||||
|
|
||||||
addRowSpanHeader ::
|
addRowSpanHeader ::
|
||||||
Cell border text ->
|
Cell border text ->
|
||||||
[[Cell border text]] -> [[Cell border text]]
|
[[Cell border text]] -> [[Cell border text]]
|
||||||
|
|||||||
@ -266,6 +266,7 @@ module Hledger.Cli.Commands.Balance (
|
|||||||
,multiBalanceHasTotalsColumn
|
,multiBalanceHasTotalsColumn
|
||||||
,addTotalBorders
|
,addTotalBorders
|
||||||
,simpleDateSpanCell
|
,simpleDateSpanCell
|
||||||
|
,nbsp
|
||||||
,RowClass(..)
|
,RowClass(..)
|
||||||
-- ** Tests
|
-- ** Tests
|
||||||
,tests_Balance
|
,tests_Balance
|
||||||
|
|||||||
@ -14,19 +14,24 @@ module Hledger.Cli.CompoundBalanceCommand (
|
|||||||
,compoundBalanceCommand
|
,compoundBalanceCommand
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
|
||||||
|
import Data.List.NonEmpty (NonEmpty((:|)))
|
||||||
|
import Data.Bifunctor (second)
|
||||||
|
import qualified Data.Map as Map
|
||||||
import qualified Data.List as List
|
import qualified Data.List as List
|
||||||
import Data.Maybe (fromMaybe, mapMaybe)
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Lazy as TL
|
import qualified Data.Text.Lazy as TL
|
||||||
import qualified Data.Text.Lazy.Builder as TB
|
import qualified Data.Text.Lazy.Builder as TB
|
||||||
import Data.Time.Calendar (Day, addDays)
|
import Data.Time.Calendar (Day, addDays)
|
||||||
import System.Console.CmdArgs.Explicit as C (Mode, flagNone, flagReq)
|
import System.Console.CmdArgs.Explicit as C (Mode, flagNone, flagReq)
|
||||||
|
import qualified System.IO as IO
|
||||||
|
import Hledger.Write.Ods (printFods)
|
||||||
import Hledger.Write.Csv (CSV, printCSV, printTSV)
|
import Hledger.Write.Csv (CSV, printCSV, printTSV)
|
||||||
import Hledger.Write.Html.Attribute (stylesheet, tableStyle, alignleft, alignright)
|
import Hledger.Write.Html.Lucid (printHtml)
|
||||||
import qualified Hledger.Write.Html.Lucid as Html
|
import Hledger.Write.Html.Attribute (stylesheet, tableStyle, alignleft)
|
||||||
import qualified Hledger.Write.Spreadsheet as Spr
|
import qualified Hledger.Write.Spreadsheet as Spr
|
||||||
import Lucid as L hiding (value_)
|
import Lucid as L hiding (value_)
|
||||||
import Safe (tailDef)
|
|
||||||
import Text.Tabular.AsciiWide as Tabular hiding (render)
|
import Text.Tabular.AsciiWide as Tabular hiding (render)
|
||||||
|
|
||||||
import Hledger
|
import Hledger
|
||||||
@ -197,6 +202,10 @@ compoundBalanceCommand CompoundBalanceCommandSpec{..} opts@CliOpts{reportspec_=r
|
|||||||
"csv" -> printCSV . compoundBalanceReportAsCsv ropts'
|
"csv" -> printCSV . compoundBalanceReportAsCsv ropts'
|
||||||
"tsv" -> printTSV . compoundBalanceReportAsCsv ropts'
|
"tsv" -> printTSV . compoundBalanceReportAsCsv ropts'
|
||||||
"html" -> L.renderText . compoundBalanceReportAsHtml ropts'
|
"html" -> L.renderText . compoundBalanceReportAsHtml ropts'
|
||||||
|
"fods" -> printFods IO.localeEncoding .
|
||||||
|
fmap (second NonEmpty.toList) . uncurry Map.singleton .
|
||||||
|
compoundBalanceReportAsSpreadsheet
|
||||||
|
oneLineNoCostFmt "Account" (Just "") ropts'
|
||||||
"json" -> toJsonText
|
"json" -> toJsonText
|
||||||
x -> error' $ unsupportedOutputFormatError x
|
x -> error' $ unsupportedOutputFormatError x
|
||||||
|
|
||||||
@ -302,99 +311,85 @@ compoundBalanceReportAsText ropts (CompoundPeriodicReport title _colspans subrep
|
|||||||
-- subreport title row, and an overall title row, one headings row, and an
|
-- subreport title row, and an overall title row, one headings row, and an
|
||||||
-- optional overall totals row is added.
|
-- optional overall totals row is added.
|
||||||
compoundBalanceReportAsCsv :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> CSV
|
compoundBalanceReportAsCsv :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> CSV
|
||||||
compoundBalanceReportAsCsv ropts (CompoundPeriodicReport title colspans subreports totalrow) =
|
compoundBalanceReportAsCsv ropts cbr =
|
||||||
addtotals $
|
let spreadsheet =
|
||||||
padRow title
|
snd $ snd $
|
||||||
: ( "Account"
|
compoundBalanceReportAsSpreadsheet
|
||||||
: ["Commodity" | layout_ ropts == LayoutBare]
|
machineFmt "Account" Nothing ropts cbr
|
||||||
++ map (reportPeriodName (balanceaccum_ ropts) colspans) colspans
|
in Spr.rawTableContent $
|
||||||
++ (if multiBalanceHasTotalsColumn ropts then ["Total"] else [])
|
Spr.horizontalSpan (NonEmpty.head spreadsheet)
|
||||||
++ (if average_ ropts then ["Average"] else [])
|
(Spr.headerCell (cbrTitle cbr)) :
|
||||||
)
|
NonEmpty.toList spreadsheet
|
||||||
: concatMap (subreportAsCsv ropts) subreports
|
|
||||||
where
|
|
||||||
-- | Add a subreport title row and drop the heading row.
|
|
||||||
subreportAsCsv ropts1 (subreporttitle, multibalreport, _) =
|
|
||||||
padRow subreporttitle :
|
|
||||||
tailDef [] (multiBalanceReportAsCsv ropts1 multibalreport)
|
|
||||||
padRow s = take numcols $ s : repeat ""
|
|
||||||
where
|
|
||||||
numcols
|
|
||||||
| null subreports = 1
|
|
||||||
| otherwise =
|
|
||||||
(1 +) $ -- account name column
|
|
||||||
(if layout_ ropts == LayoutBare then (1+) else id) $
|
|
||||||
(if multiBalanceHasTotalsColumn ropts then (1+) else id) $
|
|
||||||
(if average_ ropts then (1+) else id) $
|
|
||||||
maximum $ -- depends on non-null subreports
|
|
||||||
map (length . prDates . second3) subreports
|
|
||||||
addtotals
|
|
||||||
| no_total_ ropts || length subreports == 1 = id
|
|
||||||
| otherwise = (++ map ("Net:" : ) (multiBalanceRowAsCsvText ropts colspans totalrow))
|
|
||||||
|
|
||||||
-- | Render a compound balance report as HTML.
|
-- | Render a compound balance report as HTML.
|
||||||
compoundBalanceReportAsHtml :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> Html ()
|
compoundBalanceReportAsHtml :: ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount -> Html ()
|
||||||
compoundBalanceReportAsHtml ropts cbr =
|
compoundBalanceReportAsHtml ropts cbr =
|
||||||
|
let (title, (_fixed, cells)) =
|
||||||
|
compoundBalanceReportAsSpreadsheet
|
||||||
|
oneLineNoCostFmt "" (Just nbsp) ropts cbr
|
||||||
|
colspanattr = colspan_ $ T.pack $ show $ length $ NonEmpty.head cells
|
||||||
|
in do
|
||||||
|
link_ [rel_ "stylesheet", href_ "hledger.css"]
|
||||||
|
style_ $ stylesheet $
|
||||||
|
tableStyle ++ [
|
||||||
|
("td:nth-child(1)", "white-space:nowrap"),
|
||||||
|
("tr:nth-child(odd) td", "background-color:#eee")
|
||||||
|
]
|
||||||
|
table_ $ do
|
||||||
|
tr_ $ th_ [colspanattr, style_ alignleft] $ h2_ $ toHtml title
|
||||||
|
printHtml $ NonEmpty.toList $ fmap (map (fmap L.toHtml)) cells
|
||||||
|
|
||||||
|
-- | Render a compound balance report as Spreadsheet.
|
||||||
|
compoundBalanceReportAsSpreadsheet ::
|
||||||
|
AmountFormat -> T.Text -> Maybe T.Text ->
|
||||||
|
ReportOpts -> CompoundPeriodicReport DisplayName MixedAmount ->
|
||||||
|
(T.Text, ((Maybe Int, Maybe Int), NonEmpty [Spr.Cell Spr.NumLines T.Text]))
|
||||||
|
compoundBalanceReportAsSpreadsheet fmt accountLabel maybeBlank ropts cbr =
|
||||||
let
|
let
|
||||||
CompoundPeriodicReport title colspans subreports totalrow = cbr
|
CompoundPeriodicReport title colspans subreports totalrow = cbr
|
||||||
headerrow =
|
headerrow =
|
||||||
th_ "" :
|
Spr.headerCell accountLabel :
|
||||||
(guard (layout_ ropts == LayoutBare) >> [th_ "Commodity"]) ++
|
(guard (layout_ ropts == LayoutBare) >> [Spr.headerCell "Commodity"]) ++
|
||||||
map (th_ [style_ alignright] . toHtml .
|
map (Spr.headerCell . reportPeriodName (balanceaccum_ ropts) colspans)
|
||||||
reportPeriodName (balanceaccum_ ropts) colspans)
|
|
||||||
colspans ++
|
colspans ++
|
||||||
(guard (multiBalanceHasTotalsColumn ropts) >> [th_ "Total"]) ++
|
(guard (multiBalanceHasTotalsColumn ropts) >> [Spr.headerCell "Total"]) ++
|
||||||
(guard (average_ ropts) >> [th_ "Average"])
|
(guard (average_ ropts) >> [Spr.headerCell "Average"])
|
||||||
|
|
||||||
colspanattr = colspan_ $ T.pack $ show $ length headerrow
|
blankrow =
|
||||||
leftattr = style_ alignleft
|
fmap (Spr.horizontalSpan headerrow . Spr.defaultCell) maybeBlank
|
||||||
blankrow = tr_ $ td_ [colspanattr] $ toHtmlRaw (" "::String)
|
|
||||||
|
|
||||||
titlerows =
|
|
||||||
[tr_ $ th_ [colspanattr, leftattr] $ h2_ $ toHtml title
|
|
||||||
,tr_ $ mconcat headerrow
|
|
||||||
]
|
|
||||||
|
|
||||||
-- Make rows for a subreport: its title row, not the headings row,
|
-- Make rows for a subreport: its title row, not the headings row,
|
||||||
-- the data rows, any totals row, and a blank row for whitespace.
|
-- the data rows, any totals row, and a blank row for whitespace.
|
||||||
subreportrows :: (T.Text, MultiBalanceReport, Bool) -> [Html ()]
|
subreportrows ::
|
||||||
|
(T.Text, MultiBalanceReport, Bool) -> [[Spr.Cell Spr.NumLines T.Text]]
|
||||||
subreportrows (subreporttitle, mbr, _increasestotal) =
|
subreportrows (subreporttitle, mbr, _increasestotal) =
|
||||||
let
|
let
|
||||||
-- TODO: should the commodity_column be displayed as a subaccount in this case as well?
|
|
||||||
(_, bodyrows, mtotalsrows) =
|
(_, bodyrows, mtotalsrows) =
|
||||||
multiBalanceReportAsSpreadsheetParts oneLineNoCostFmt ropts mbr
|
multiBalanceReportAsSpreadsheetParts fmt ropts mbr
|
||||||
formatRow = Html.formatRow . map (fmap L.toHtml)
|
|
||||||
|
|
||||||
in
|
in
|
||||||
[tr_ $ th_ [colspanattr, leftattr, class_ "account"] $ toHtml subreporttitle]
|
Spr.horizontalSpan headerrow
|
||||||
++ map formatRow bodyrows
|
((Spr.defaultCell subreporttitle){
|
||||||
++ map formatRow mtotalsrows
|
Spr.cellStyle = Spr.Body Spr.Total,
|
||||||
++ [blankrow]
|
Spr.cellClass = Spr.Class "account"
|
||||||
|
}) :
|
||||||
|
bodyrows ++
|
||||||
|
mtotalsrows ++
|
||||||
|
maybeToList blankrow ++
|
||||||
|
[]
|
||||||
|
|
||||||
totalrows =
|
totalrows =
|
||||||
if no_total_ ropts || length subreports == 1 then []
|
if no_total_ ropts || length subreports == 1 then []
|
||||||
else
|
else
|
||||||
multiBalanceRowAsCellBuilders oneLineNoCostFmt ropts colspans
|
multiBalanceRowAsCellBuilders fmt ropts colspans
|
||||||
Total simpleDateSpanCell totalrow
|
Total simpleDateSpanCell totalrow
|
||||||
-- make a table of rendered lines of the report totals row
|
-- make a table of rendered lines of the report totals row
|
||||||
& map (map (fmap wbToText))
|
& map (map (fmap wbToText))
|
||||||
& Spr.addRowSpanHeader
|
& Spr.addRowSpanHeader
|
||||||
((Spr.defaultCell "Net:") {Spr.cellClass = Spr.Class "account"})
|
((Spr.defaultCell "Net:") {Spr.cellClass = Spr.Class "account"})
|
||||||
-- insert a headings column, with Net: on the first line only
|
-- insert a headings column, with Net: on the first line only
|
||||||
& addTotalBorders -- marking the first for special styling
|
& addTotalBorders -- marking the first row for special styling
|
||||||
& map (Html.formatRow . map (fmap L.toHtml))
|
|
||||||
-- convert to a list of HTML totals rows
|
|
||||||
|
|
||||||
in do
|
|
||||||
link_ [rel_ "stylesheet", href_ "hledger.css"]
|
|
||||||
style_ $ stylesheet $
|
|
||||||
tableStyle ++ [
|
|
||||||
("td:nth-child(1)", "white-space:nowrap"),
|
|
||||||
("tr:nth-child(even) td", "background-color:#eee")
|
|
||||||
]
|
|
||||||
table_ $ mconcat $
|
|
||||||
titlerows
|
|
||||||
++ [blankrow]
|
|
||||||
++ concatMap subreportrows subreports
|
|
||||||
++ totalrows
|
|
||||||
|
|
||||||
|
in (title,
|
||||||
|
((Just 1, Just 1),
|
||||||
|
headerrow :| concatMap subreportrows subreports ++ totalrows))
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user