lib: Write.Spreadsheet: support for borders like in existing HTML export
cli: Commands.Balance: use for FODS export and balance and budget export to HTML
This commit is contained in:
parent
9ff1ee9127
commit
2ed13afed4
@ -8,24 +8,43 @@ module Hledger.Write.Html (
|
||||
printHtml,
|
||||
) where
|
||||
|
||||
import qualified Hledger.Write.Spreadsheet as Spr
|
||||
import Hledger.Write.Spreadsheet (Type(..), Style(..), Emphasis(..), Cell(..))
|
||||
|
||||
import qualified Data.Text as Text
|
||||
import qualified Lucid.Base as LucidBase
|
||||
import qualified Lucid
|
||||
import Data.Text (Text)
|
||||
import Data.Foldable (for_)
|
||||
|
||||
|
||||
printHtml :: [[Cell (Lucid.Html ())]] -> Lucid.Html ()
|
||||
printHtml table =
|
||||
printHtml :: (Lines border) => [[Cell border (Lucid.Html ())]] -> Lucid.Html ()
|
||||
printHtml table = do
|
||||
Lucid.style_ $ Text.unlines $
|
||||
"" :
|
||||
"table {border-collapse:collapse}" :
|
||||
"th, td {padding-left:1em}" :
|
||||
"th.account, td.account {padding-left:0;}" :
|
||||
[]
|
||||
Lucid.table_ $ for_ table $ \row ->
|
||||
Lucid.tr_ $ for_ row $ \cell ->
|
||||
formatCell cell
|
||||
Lucid.tr_ $ for_ row $ \cell ->
|
||||
formatCell cell
|
||||
|
||||
formatCell :: Cell (Lucid.Html ()) -> Lucid.Html ()
|
||||
formatCell :: (Lines border) => Cell border (Lucid.Html ()) -> Lucid.Html ()
|
||||
formatCell cell =
|
||||
let str = cellContent cell in
|
||||
let border field access =
|
||||
map (field<>) $ borderLines $ access $ cellBorder cell in
|
||||
let leftBorder = border "border-left:" Spr.borderLeft in
|
||||
let rightBorder = border "border-right:" Spr.borderRight in
|
||||
let topBorder = border "border-top:" Spr.borderTop in
|
||||
let bottomBorder = border "border-bottom:" Spr.borderBottom in
|
||||
let style =
|
||||
case leftBorder++rightBorder++topBorder++bottomBorder of
|
||||
[] -> []
|
||||
ss -> [Lucid.style_ $ Text.intercalate "; " ss] in
|
||||
case cellStyle cell of
|
||||
Head -> Lucid.th_ str
|
||||
Head -> Lucid.th_ style str
|
||||
Body emph ->
|
||||
let align =
|
||||
case cellType cell of
|
||||
@ -36,4 +55,18 @@ formatCell cell =
|
||||
case emph of
|
||||
Item -> id
|
||||
Total -> Lucid.b_
|
||||
in Lucid.td_ align $ withEmph str
|
||||
in Lucid.td_ (style++align) $ withEmph str
|
||||
|
||||
|
||||
class (Spr.Lines border) => Lines border where
|
||||
borderLines :: border -> [Text]
|
||||
|
||||
instance Lines () where
|
||||
borderLines () = []
|
||||
|
||||
instance Lines Spr.NumLines where
|
||||
borderLines prop =
|
||||
case prop of
|
||||
Spr.NoLine -> []
|
||||
Spr.SingleLine -> ["black"]
|
||||
Spr.DoubleLine -> ["double black"]
|
||||
|
||||
@ -12,6 +12,7 @@ module Hledger.Write.Ods (
|
||||
printFods,
|
||||
) where
|
||||
|
||||
import qualified Hledger.Write.Spreadsheet as Spr
|
||||
import Hledger.Write.Spreadsheet (Type(..), Style(..), Emphasis(..), Cell(..))
|
||||
import Hledger.Data.Types (CommoditySymbol, AmountPrecision(..))
|
||||
import Hledger.Data.Types (acommodity, aquantity, astyle, asprecision)
|
||||
@ -20,12 +21,14 @@ import qualified Data.Text.Lazy as TL
|
||||
import qualified Data.Text as T
|
||||
import Data.Text (Text)
|
||||
|
||||
import qualified Data.Foldable as Fold
|
||||
import qualified Data.List as List
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Set as Set
|
||||
import Data.Foldable (fold)
|
||||
import Data.Map (Map)
|
||||
import Data.Set (Set)
|
||||
import Data.Maybe (mapMaybe)
|
||||
import Data.Maybe (catMaybes)
|
||||
|
||||
import qualified System.IO as IO
|
||||
import Text.Printf (printf)
|
||||
@ -33,7 +36,7 @@ import Text.Printf (printf)
|
||||
|
||||
printFods ::
|
||||
IO.TextEncoding ->
|
||||
Map Text ((Maybe Int, Maybe Int), [[Cell Text]]) -> TL.Text
|
||||
Map Text ((Maybe Int, Maybe Int), [[Cell Spr.NumLines Text]]) -> TL.Text
|
||||
printFods encoding tables =
|
||||
let fileOpen customStyles =
|
||||
map (map (\c -> case c of '\'' -> '"'; _ -> c)) $
|
||||
@ -57,20 +60,6 @@ printFods encoding tables =
|
||||
" xmlns:field='urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0'" :
|
||||
" xmlns:form='urn:oasis:names:tc:opendocument:xmlns:form:1.0'>" :
|
||||
"<office:styles>" :
|
||||
" <style:style style:name='head' style:family='table-cell'>" :
|
||||
" <style:paragraph-properties fo:text-align='center'/>" :
|
||||
" <style:text-properties fo:font-weight='bold'/>" :
|
||||
" </style:style>" :
|
||||
" <style:style style:name='foot' style:family='table-cell'>" :
|
||||
" <style:text-properties fo:font-weight='bold'/>" :
|
||||
" </style:style>" :
|
||||
" <style:style style:name='amount' style:family='table-cell'>" :
|
||||
" <style:paragraph-properties fo:text-align='end'/>" :
|
||||
" </style:style>" :
|
||||
" <style:style style:name='total-amount' style:family='table-cell'>" :
|
||||
" <style:paragraph-properties fo:text-align='end'/>" :
|
||||
" <style:text-properties fo:font-weight='bold'/>" :
|
||||
" </style:style>" :
|
||||
" <number:date-style style:name='iso-date'>" :
|
||||
" <number:year number:style='long'/>" :
|
||||
" <number:text>-</number:text>" :
|
||||
@ -78,12 +67,6 @@ printFods encoding tables =
|
||||
" <number:text>-</number:text>" :
|
||||
" <number:day number:style='long'/>" :
|
||||
" </number:date-style>" :
|
||||
" <style:style style:name='date' style:family='table-cell'" :
|
||||
" style:data-style-name='iso-date'/>" :
|
||||
" <style:style style:name='foot-date' style:family='table-cell'" :
|
||||
" style:data-style-name='iso-date'>" :
|
||||
" <style:text-properties fo:font-weight='bold'/>" :
|
||||
" </style:style>" :
|
||||
customStyles ++
|
||||
"</office:styles>" :
|
||||
[]
|
||||
@ -135,7 +118,7 @@ printFods encoding tables =
|
||||
in TL.unlines $ map (TL.fromStrict . T.pack) $
|
||||
fileOpen
|
||||
(let styles = cellStyles (foldMap (concat.snd) tables) in
|
||||
(numberConfig =<< Set.toList (Set.map snd styles))
|
||||
(numberConfig =<< Set.toList (foldMap (numberParams.snd) styles))
|
||||
++
|
||||
(cellConfig =<< Set.toList styles)) ++
|
||||
tableConfig (fmap fst tables) ++
|
||||
@ -150,18 +133,23 @@ printFods encoding tables =
|
||||
fileClose
|
||||
|
||||
|
||||
cellStyles :: [Cell Text] -> Set (Emphasis, (CommoditySymbol, AmountPrecision))
|
||||
dataStyleFromType :: Type -> DataStyle
|
||||
dataStyleFromType typ =
|
||||
case typ of
|
||||
TypeString -> DataString
|
||||
TypeDate -> DataDate
|
||||
TypeAmount amt -> DataAmount (acommodity amt) (asprecision $ astyle amt)
|
||||
TypeMixedAmount -> DataMixedAmount
|
||||
|
||||
cellStyles ::
|
||||
(Ord border) =>
|
||||
[Cell border Text] ->
|
||||
Set ((Spr.Border border, Style), DataStyle)
|
||||
cellStyles =
|
||||
Set.fromList .
|
||||
mapMaybe (\cell ->
|
||||
case cellType cell of
|
||||
TypeAmount amt ->
|
||||
Just
|
||||
(case cellStyle cell of
|
||||
Body emph -> emph
|
||||
Head -> Total,
|
||||
(acommodity amt, asprecision $ astyle amt))
|
||||
_ -> Nothing)
|
||||
map (\cell ->
|
||||
((cellBorder cell, cellStyle cell),
|
||||
dataStyleFromType $ cellType cell))
|
||||
|
||||
numberStyleName :: (CommoditySymbol, AmountPrecision) -> String
|
||||
numberStyleName (comm, prec) =
|
||||
@ -170,6 +158,10 @@ numberStyleName (comm, prec) =
|
||||
NaturalPrecision -> "natural"
|
||||
Precision k -> show k
|
||||
|
||||
numberParams :: DataStyle -> Set (CommoditySymbol, AmountPrecision)
|
||||
numberParams (DataAmount comm prec) = Set.singleton (comm, prec)
|
||||
numberParams _ = Set.empty
|
||||
|
||||
numberConfig :: (CommoditySymbol, AmountPrecision) -> [String]
|
||||
numberConfig (comm, prec) =
|
||||
let precStr =
|
||||
@ -191,41 +183,123 @@ emphasisName emph =
|
||||
Item -> "item"
|
||||
Total -> "total"
|
||||
|
||||
cellConfig :: (Emphasis, (CommoditySymbol, AmountPrecision)) -> [String]
|
||||
cellConfig (emph, numParam) =
|
||||
let name = numberStyleName numParam in
|
||||
let style :: String
|
||||
cellStyleName :: Style -> String
|
||||
cellStyleName style =
|
||||
case style of
|
||||
Head -> "head"
|
||||
Body emph -> emphasisName emph
|
||||
|
||||
linesName :: Spr.NumLines -> Maybe String
|
||||
linesName prop =
|
||||
case prop of
|
||||
Spr.NoLine -> Nothing
|
||||
Spr.SingleLine -> Just "single"
|
||||
Spr.DoubleLine -> Just "double"
|
||||
|
||||
linesStyle :: Spr.NumLines -> String
|
||||
linesStyle prop =
|
||||
case prop of
|
||||
Spr.NoLine -> "none"
|
||||
Spr.SingleLine -> "1.5pt solid #000000"
|
||||
Spr.DoubleLine -> "1.5pt double-thin #000000"
|
||||
|
||||
borderLabels :: Spr.Border String
|
||||
borderLabels = Spr.Border "left" "right" "top" "bottom"
|
||||
|
||||
borderName :: Spr.Border Spr.NumLines -> String
|
||||
borderName border =
|
||||
(\bs ->
|
||||
case bs of
|
||||
[] -> "noborder"
|
||||
_ ->
|
||||
("border="++) $ List.intercalate "," $
|
||||
map (\(name,num) -> name ++ ':' : num) bs) $
|
||||
catMaybes $ Fold.toList $
|
||||
liftA2
|
||||
(\name numLines -> (,) name <$> linesName numLines)
|
||||
borderLabels
|
||||
border
|
||||
|
||||
borderStyle :: Spr.Border Spr.NumLines -> [String]
|
||||
borderStyle border =
|
||||
if border == Spr.noBorder
|
||||
then []
|
||||
else (:[]) $
|
||||
printf " <style:table-cell-properties%s/>" $
|
||||
(id :: String -> String) $ fold $
|
||||
liftA2 (printf " fo:border-%s='%s'") borderLabels $
|
||||
fmap linesStyle border
|
||||
|
||||
data DataStyle =
|
||||
DataString
|
||||
| DataDate
|
||||
| DataAmount CommoditySymbol AmountPrecision
|
||||
| DataMixedAmount
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
cellConfig :: ((Spr.Border Spr.NumLines, Style), DataStyle) -> [String]
|
||||
cellConfig ((border, cstyle), dataStyle) =
|
||||
let moreStyles =
|
||||
borderStyle border
|
||||
++
|
||||
(
|
||||
case cstyle of
|
||||
Body Item -> []
|
||||
Body Total ->
|
||||
[" <style:text-properties fo:font-weight='bold'/>"]
|
||||
Head ->
|
||||
" <style:paragraph-properties fo:text-align='center'/>" :
|
||||
" <style:text-properties fo:font-weight='bold'/>" :
|
||||
[]
|
||||
)
|
||||
++
|
||||
(
|
||||
case dataStyle of
|
||||
DataMixedAmount ->
|
||||
[" <style:paragraph-properties fo:text-align='end'/>"]
|
||||
_ -> []
|
||||
)
|
||||
cstyleName = cellStyleName cstyle
|
||||
bordName = borderName border
|
||||
style :: String
|
||||
style =
|
||||
printf "style:name='%s-%s' style:data-style-name='number-%s'"
|
||||
(emphasisName emph) name name in
|
||||
case emph of
|
||||
Item ->
|
||||
case dataStyle of
|
||||
DataDate ->
|
||||
printf
|
||||
"style:name='%s-%s-date' style:data-style-name='iso-date'"
|
||||
cstyleName bordName
|
||||
DataAmount comm prec ->
|
||||
let name = numberStyleName (comm, prec) in
|
||||
printf
|
||||
"style:name='%s-%s-%s' style:data-style-name='number-%s'"
|
||||
cstyleName bordName name name
|
||||
_ -> printf "style:name='%s-%s'" cstyleName bordName
|
||||
in
|
||||
case moreStyles of
|
||||
[] ->
|
||||
printf " <style:style style:family='table-cell' %s/>" style :
|
||||
[]
|
||||
Total ->
|
||||
_ ->
|
||||
printf " <style:style style:family='table-cell' %s>" style :
|
||||
" <style:text-properties fo:font-weight='bold'/>" :
|
||||
moreStyles ++
|
||||
" </style:style>" :
|
||||
[]
|
||||
|
||||
|
||||
formatCell :: Cell Text -> [String]
|
||||
formatCell :: Cell Spr.NumLines Text -> [String]
|
||||
formatCell cell =
|
||||
let style, valueType :: String
|
||||
style =
|
||||
case (cellStyle cell, cellType cell) of
|
||||
(Body emph, TypeAmount amt) -> tableStyle $ numberStyle emph amt
|
||||
(Body Item, TypeString) -> ""
|
||||
(Body Item, TypeMixedAmount) -> tableStyle "amount"
|
||||
(Body Item, TypeDate) -> tableStyle "date"
|
||||
(Body Total, TypeString) -> tableStyle "foot"
|
||||
(Body Total, TypeMixedAmount) -> tableStyle "total-amount"
|
||||
(Body Total, TypeDate) -> tableStyle "foot-date"
|
||||
(Head, _) -> tableStyle "head"
|
||||
numberStyle emph amt =
|
||||
printf "%s-%s"
|
||||
(emphasisName emph)
|
||||
(numberStyleName (acommodity amt, asprecision $ astyle amt))
|
||||
style = tableStyle styleName
|
||||
cstyleName = cellStyleName $ cellStyle cell
|
||||
bordName = borderName $ cellBorder cell
|
||||
styleName :: String
|
||||
styleName =
|
||||
case dataStyleFromType $ cellType cell of
|
||||
DataDate -> printf "%s-%s-date" cstyleName bordName
|
||||
DataAmount comm prec ->
|
||||
let name = numberStyleName (comm, prec) in
|
||||
printf "%s-%s-%s" cstyleName bordName name
|
||||
_ -> printf "%s-%s" cstyleName bordName
|
||||
tableStyle = printf " table:style-name='%s'"
|
||||
|
||||
valueType =
|
||||
|
||||
@ -7,6 +7,10 @@ module Hledger.Write.Spreadsheet (
|
||||
Style(..),
|
||||
Emphasis(..),
|
||||
Cell(..),
|
||||
Border(..),
|
||||
Lines(..),
|
||||
NumLines(..),
|
||||
noBorder,
|
||||
defaultCell,
|
||||
emptyCell,
|
||||
) where
|
||||
@ -27,23 +31,62 @@ data Style = Body Emphasis | Head
|
||||
data Emphasis = Item | Total
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
data Cell text =
|
||||
|
||||
class Lines border where noLine :: border
|
||||
instance Lines () where noLine = ()
|
||||
instance Lines NumLines where noLine = NoLine
|
||||
|
||||
{- |
|
||||
The same as Tab.Properties, but has 'Eq' and 'Ord' instances.
|
||||
We need those for storing 'NumLines' in 'Set's.
|
||||
-}
|
||||
data NumLines = NoLine | SingleLine | DoubleLine
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
data Border lines =
|
||||
Border {
|
||||
borderLeft, borderRight,
|
||||
borderTop, borderBottom :: lines
|
||||
}
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
instance Functor Border where
|
||||
fmap f (Border left right top bottom) =
|
||||
Border (f left) (f right) (f top) (f bottom)
|
||||
|
||||
instance Applicative Border where
|
||||
pure a = Border a a a a
|
||||
Border fLeft fRight fTop fBottom <*> Border left right top bottom =
|
||||
Border (fLeft left) (fRight right) (fTop top) (fBottom bottom)
|
||||
|
||||
instance Foldable Border where
|
||||
foldMap f (Border left right top bottom) =
|
||||
f left <> f right <> f top <> f bottom
|
||||
|
||||
noBorder :: (Lines border) => Border border
|
||||
noBorder = pure noLine
|
||||
|
||||
|
||||
data Cell border text =
|
||||
Cell {
|
||||
cellType :: Type,
|
||||
cellBorder :: Border border,
|
||||
cellStyle :: Style,
|
||||
cellContent :: text
|
||||
}
|
||||
|
||||
instance Functor Cell where
|
||||
fmap f (Cell typ style content) = Cell typ style $ f content
|
||||
instance Functor (Cell border) where
|
||||
fmap f (Cell typ border style content) =
|
||||
Cell typ border style $ f content
|
||||
|
||||
defaultCell :: text -> Cell text
|
||||
defaultCell :: (Lines border) => text -> Cell border text
|
||||
defaultCell text =
|
||||
Cell {
|
||||
cellType = TypeString,
|
||||
cellBorder = noBorder,
|
||||
cellStyle = Body Item,
|
||||
cellContent = text
|
||||
}
|
||||
|
||||
emptyCell :: (Monoid text) => Cell text
|
||||
emptyCell :: (Lines border, Monoid text) => Cell border text
|
||||
emptyCell = defaultCell mempty
|
||||
|
||||
@ -550,22 +550,42 @@ renderComponent topaligned oneline opts (acctname, dep, total) (FormatField ljus
|
||||
,displayColour = color_ opts
|
||||
}
|
||||
|
||||
|
||||
headerCell :: Text -> Ods.Cell Ods.NumLines Text
|
||||
headerCell text =
|
||||
let deflt = Ods.defaultCell text
|
||||
in
|
||||
deflt {
|
||||
Ods.cellStyle = Ods.Head,
|
||||
Ods.cellBorder =
|
||||
(Ods.cellBorder deflt) {Ods.borderBottom = Ods.DoubleLine}
|
||||
}
|
||||
|
||||
addTotalBorders :: [[Ods.Cell border text]] -> [[Ods.Cell Ods.NumLines text]]
|
||||
addTotalBorders =
|
||||
zipWith
|
||||
(\border ->
|
||||
map (\c -> c {
|
||||
Ods.cellStyle = Ods.Body Ods.Total,
|
||||
Ods.cellBorder = Ods.noBorder {Ods.borderTop = border}}))
|
||||
(Ods.DoubleLine : repeat Ods.NoLine)
|
||||
|
||||
-- | Render a single-column balance report as FODS.
|
||||
balanceReportAsSpreadsheet :: ReportOpts -> BalanceReport -> [[Ods.Cell Text]]
|
||||
balanceReportAsSpreadsheet ::
|
||||
ReportOpts -> BalanceReport -> [[Ods.Cell Ods.NumLines Text]]
|
||||
balanceReportAsSpreadsheet opts (items, total) =
|
||||
headers :
|
||||
concatMap (\(a, _, _, b) -> rows a b) items ++
|
||||
if no_total_ opts then []
|
||||
else map (map (\c -> c {Ods.cellStyle = Ods.Body Ods.Total})) $
|
||||
rows totalRowHeadingCsv total
|
||||
else addTotalBorders $ rows totalRowHeadingCsv total
|
||||
where
|
||||
cell = Ods.defaultCell
|
||||
headers =
|
||||
map (\content -> (cell content) {Ods.cellStyle = Ods.Head}) $
|
||||
map headerCell $
|
||||
"account" : case layout_ opts of
|
||||
LayoutBare -> ["commodity", "balance"]
|
||||
_ -> ["balance"]
|
||||
rows :: AccountName -> MixedAmount -> [[Ods.Cell Text]]
|
||||
rows :: AccountName -> MixedAmount -> [[Ods.Cell Ods.NumLines Text]]
|
||||
rows name ma = case layout_ opts of
|
||||
LayoutBare ->
|
||||
map (\a ->
|
||||
@ -583,7 +603,9 @@ balanceReportAsSpreadsheet opts (items, total) =
|
||||
| layout_ opts == LayoutBare = (False, Just $ S.toList $ maCommodities mixedAmt)
|
||||
| otherwise = (True, Nothing)
|
||||
|
||||
cellFromMixedAmount :: AmountFormat -> MixedAmount -> Ods.Cell WideBuilder
|
||||
cellFromMixedAmount ::
|
||||
(Ods.Lines border) =>
|
||||
AmountFormat -> MixedAmount -> Ods.Cell border WideBuilder
|
||||
cellFromMixedAmount bopts mixedAmt =
|
||||
(Ods.defaultCell $ showMixedAmountB bopts mixedAmt) {
|
||||
Ods.cellType =
|
||||
@ -592,7 +614,9 @@ cellFromMixedAmount bopts mixedAmt =
|
||||
Nothing -> Ods.TypeMixedAmount
|
||||
}
|
||||
|
||||
cellsFromMixedAmount :: AmountFormat -> MixedAmount -> [Ods.Cell WideBuilder]
|
||||
cellsFromMixedAmount ::
|
||||
(Ods.Lines border) =>
|
||||
AmountFormat -> MixedAmount -> [Ods.Cell border WideBuilder]
|
||||
cellsFromMixedAmount bopts mixedAmt =
|
||||
map
|
||||
(\(str,amt) ->
|
||||
@ -630,14 +654,14 @@ multiBalanceReportAsCsvHelper ishtml opts =
|
||||
|
||||
-- Helper for CSV and ODS and HTML rendering.
|
||||
multiBalanceReportAsSpreadsheetHelper ::
|
||||
Bool -> ReportOpts -> MultiBalanceReport -> ([[Ods.Cell Text]], [[Ods.Cell Text]])
|
||||
Bool -> ReportOpts -> MultiBalanceReport ->
|
||||
([[Ods.Cell Ods.NumLines Text]], [[Ods.Cell Ods.NumLines Text]])
|
||||
multiBalanceReportAsSpreadsheetHelper ishtml opts@ReportOpts{..} (PeriodicReport colspans items tr) =
|
||||
(headers : concatMap fullRowAsTexts items,
|
||||
map (map (\c -> c{Ods.cellStyle = Ods.Body Ods.Total})) totalrows)
|
||||
(headers : concatMap fullRowAsTexts items, addTotalBorders totalrows)
|
||||
where
|
||||
cell = Ods.defaultCell
|
||||
headers =
|
||||
map (\content -> (cell content) {Ods.cellStyle = Ods.Head}) $
|
||||
map headerCell $
|
||||
"account" :
|
||||
case layout_ of
|
||||
LayoutTidy -> ["period", "start_date", "end_date", "commodity", "value"]
|
||||
@ -782,7 +806,8 @@ multiBalanceReportHtmlFootRow ropts isfirstline (hdr:cells) =
|
||||
-- | Render the ODS table rows for a MultiBalanceReport.
|
||||
-- Returns the heading row, 0 or more body rows, and the totals row if enabled.
|
||||
multiBalanceReportAsSpreadsheet ::
|
||||
ReportOpts -> MultiBalanceReport -> ((Maybe Int, Maybe Int), [[Ods.Cell Text]])
|
||||
ReportOpts -> MultiBalanceReport ->
|
||||
((Maybe Int, Maybe Int), [[Ods.Cell Ods.NumLines Text]])
|
||||
multiBalanceReportAsSpreadsheet ropts mbr =
|
||||
let (upper,lower) = multiBalanceReportAsSpreadsheetHelper True ropts mbr
|
||||
in ((Just 1, case layout_ ropts of LayoutWide _ -> Just 1; _ -> Nothing),
|
||||
@ -885,7 +910,8 @@ multiBalanceRowAsTextBuilders bopts ropts colspans row =
|
||||
multiBalanceRowAsCellBuilders bopts ropts colspans row
|
||||
|
||||
multiBalanceRowAsCellBuilders ::
|
||||
AmountFormat -> ReportOpts -> [DateSpan] -> PeriodicReportRow a MixedAmount -> [[Ods.Cell WideBuilder]]
|
||||
AmountFormat -> ReportOpts -> [DateSpan] ->
|
||||
PeriodicReportRow a MixedAmount -> [[Ods.Cell Ods.NumLines WideBuilder]]
|
||||
multiBalanceRowAsCellBuilders bopts ReportOpts{..} colspans (PeriodicReportRow _ as rowtot rowavg) =
|
||||
case layout_ of
|
||||
LayoutWide width -> [fmap (cellFromMixedAmount bopts{displayMaxWidth=width}) allamts]
|
||||
@ -1192,14 +1218,15 @@ budgetReportAsCsv ropts report
|
||||
= map (map Ods.cellContent) $
|
||||
budgetReportAsSpreadsheet ropts report
|
||||
|
||||
budgetReportAsSpreadsheet :: ReportOpts -> BudgetReport -> [[Ods.Cell Text]]
|
||||
budgetReportAsSpreadsheet ::
|
||||
ReportOpts -> BudgetReport -> [[Ods.Cell Ods.NumLines Text]]
|
||||
budgetReportAsSpreadsheet
|
||||
ReportOpts{..}
|
||||
(PeriodicReport colspans items totrow)
|
||||
= (if transpose_ then transpose else id) $
|
||||
|
||||
-- heading row
|
||||
(map (\content -> (cell content) {Ods.cellStyle = Ods.Head}) $
|
||||
(map headerCell $
|
||||
"Account" :
|
||||
["Commodity" | layout_ == LayoutBare ]
|
||||
++ concatMap (\spn -> [showDateSpan spn, "budget"]) colspans
|
||||
@ -1211,7 +1238,7 @@ budgetReportAsSpreadsheet
|
||||
concatMap (rowAsTexts prrFullName) items
|
||||
|
||||
-- totals row
|
||||
++ map (map (\c -> c {Ods.cellStyle = Ods.Body Ods.Total}))
|
||||
++ addTotalBorders
|
||||
(concat [ rowAsTexts (const totalRowHeadingBudgetCsv) totrow | not no_total_ ])
|
||||
|
||||
where
|
||||
@ -1221,7 +1248,7 @@ budgetReportAsSpreadsheet
|
||||
|
||||
rowAsTexts :: (PeriodicReportRow a BudgetCell -> Text)
|
||||
-> PeriodicReportRow a BudgetCell
|
||||
-> [[Ods.Cell Text]]
|
||||
-> [[Ods.Cell Ods.NumLines Text]]
|
||||
rowAsTexts render row@(PeriodicReportRow _ as (rowtot,budgettot) (rowavg, budgetavg))
|
||||
| layout_ /= LayoutBare = [cell (render row) : map showNorm vals]
|
||||
| otherwise =
|
||||
|
||||
Loading…
Reference in New Issue
Block a user