From 9a386575152d038c9a0509000238bbf1f41afccd Mon Sep 17 00:00:00 2001 From: xuri Date: Sun, 18 Aug 2024 00:18:02 +0800 Subject: [PATCH] This related for #720 and closes #1965, add new NumFmt field in the PivotTableField data type - Support set and get built-in number format of the pivot table data filed cells - Update unit tests - Fixed ineffectual assignment issue --- calc.go | 4 ++-- pivotTable.go | 29 +++++++++++++++++++++++++++-- pivotTable_test.go | 8 ++++---- styles.go | 12 +++++------- xmlPivotTable.go | 2 +- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/calc.go b/calc.go index 8777bb9..193e01f 100644 --- a/calc.go +++ b/calc.go @@ -14459,7 +14459,7 @@ func (fn *formulaFuncs) VALUE(argsList *list.List) formulaArg { value, _ := decimal.Float64() return newNumberFormulaArg(value * percent) } - dateValue, timeValue, errTime, errDate := 0.0, 0.0, false, false + dateValue, timeValue, errTime := 0.0, 0.0, false if !isDateOnlyFmt(text) { h, m, s, _, _, err := strToTime(text) errTime = err.Type == ArgError @@ -14468,7 +14468,7 @@ func (fn *formulaFuncs) VALUE(argsList *list.List) formulaArg { } } y, m, d, _, err := strToDate(text) - errDate = err.Type == ArgError + errDate := err.Type == ArgError if !errDate { dateValue = daysBetween(excelMinTime1900.Unix(), makeDate(y, time.Month(m), d)) + 1 } diff --git a/pivotTable.go b/pivotTable.go index 7ed79ad..490b487 100644 --- a/pivotTable.go +++ b/pivotTable.go @@ -62,6 +62,10 @@ type PivotTableOptions struct { } // PivotTableField directly maps the field settings of the pivot table. +// +// Name specifies the name of the data field. Maximum 255 characters +// are allowed in data field name, excess characters will be truncated. +// // Subtotal specifies the aggregation function that applies to this data // field. The default value is sum. The possible values for this attribute // are: @@ -78,8 +82,9 @@ type PivotTableOptions struct { // Var // Varp // -// Name specifies the name of the data field. Maximum 255 characters -// are allowed in data field name, excess characters will be truncated. +// NumFmt specifies the number format ID of the data field, this filed only +// accepts built-in number format ID and does not support custom number format +// expression currently. type PivotTableField struct { Compact bool Data string @@ -87,6 +92,7 @@ type PivotTableField struct { Outline bool Subtotal string DefaultSubtotal bool + NumFmt int } // AddPivotTable provides the method to add pivot table by given pivot table @@ -452,6 +458,7 @@ func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opts *PivotTable } dataFieldsSubtotals := f.getPivotTableFieldsSubtotal(opts.Data) dataFieldsName := f.getPivotTableFieldsName(opts.Data) + dataFieldsNumFmtID := f.getPivotTableFieldsNumFmtID(opts.Data) for idx, dataField := range dataFieldsIndex { if pt.DataFields == nil { pt.DataFields = &xlsxDataFields{} @@ -460,6 +467,7 @@ func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opts *PivotTable Name: dataFieldsName[idx], Fld: dataField, Subtotal: dataFieldsSubtotals[idx], + NumFmtID: dataFieldsNumFmtID[idx], }) } @@ -687,6 +695,22 @@ func (f *File) getPivotTableFieldName(name string, fields []PivotTableField) str return "" } +// getPivotTableFieldsNumFmtID prepare fields number format ID by given pivot +// table fields. +func (f *File) getPivotTableFieldsNumFmtID(fields []PivotTableField) []int { + field := make([]int, len(fields)) + for idx, fld := range fields { + if _, ok := builtInNumFmt[fld.NumFmt]; ok { + field[idx] = fld.NumFmt + continue + } + if (27 <= fld.NumFmt && fld.NumFmt <= 36) || (50 <= fld.NumFmt && fld.NumFmt <= 81) { + field[idx] = fld.NumFmt + } + } + return field +} + // getPivotTableFieldOptions return options for specific field by given field name. func (f *File) getPivotTableFieldOptions(name string, fields []PivotTableField) (options PivotTableField, ok bool) { for _, field := range fields { @@ -891,6 +915,7 @@ func (f *File) extractPivotTableFields(order []string, pt *xlsxPivotTableDefinit Data: order[field.Fld], Name: field.Name, Subtotal: cases.Title(language.English).String(field.Subtotal), + NumFmt: field.NumFmtID, }) } } diff --git a/pivotTable_test.go b/pivotTable_test.go index 133301d..58b1dbe 100644 --- a/pivotTable_test.go +++ b/pivotTable_test.go @@ -34,7 +34,7 @@ func TestPivotTable(t *testing.T) { Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Filter: []PivotTableField{{Data: "Region"}}, Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, - Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Summarize by Sum"}}, + Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Summarize by Sum", NumFmt: 38}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, @@ -131,7 +131,7 @@ func TestPivotTable(t *testing.T) { PivotTableRange: "Sheet2!A1:AN17", Rows: []PivotTableField{{Data: "Month"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type", DefaultSubtotal: true}, {Data: "Year"}}, - Data: []PivotTableField{{Data: "Sales", Subtotal: "Min", Name: "Summarize by Min"}}, + Data: []PivotTableField{{Data: "Sales", Subtotal: "Min", Name: "Summarize by Min", NumFmt: 32}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, @@ -151,7 +151,7 @@ func TestPivotTable(t *testing.T) { PivotTableRange: "Sheet2!A20:AR60", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Type"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Year"}}, - Data: []PivotTableField{{Data: "Sales", Subtotal: "Product", Name: "Summarize by Product"}}, + Data: []PivotTableField{{Data: "Sales", Subtotal: "Product", Name: "Summarize by Product", NumFmt: 32}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, @@ -171,7 +171,7 @@ func TestPivotTable(t *testing.T) { PivotTableRange: "Sheet2!A65:AJ100", Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type"}}, - Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Sum of Sales"}, {Data: "Sales", Subtotal: "Average", Name: "Average of Sales"}}, + Data: []PivotTableField{{Data: "Sales", Subtotal: "Sum", Name: "Sum of Sales", NumFmt: -1}, {Data: "Sales", Subtotal: "Average", Name: "Average of Sales", NumFmt: 38}}, RowGrandTotals: true, ColGrandTotals: true, ShowDrill: true, diff --git a/styles.go b/styles.go index 64c0bc0..3401651 100644 --- a/styles.go +++ b/styles.go @@ -1902,27 +1902,25 @@ func (f *File) newFont(style *Style) (*xlsxFont, error) { // getNumFmtID provides a function to get number format code ID. // If given number format code does not exist, will return -1. -func getNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (numFmtID int) { - numFmtID = -1 +func getNumFmtID(styleSheet *xlsxStyleSheet, style *Style) int { + numFmtID := -1 if _, ok := builtInNumFmt[style.NumFmt]; ok { return style.NumFmt } if (27 <= style.NumFmt && style.NumFmt <= 36) || (50 <= style.NumFmt && style.NumFmt <= 81) { - numFmtID = style.NumFmt - return + return style.NumFmt } if fmtCode, ok := currencyNumFmt[style.NumFmt]; ok { numFmtID = style.NumFmt if styleSheet.NumFmts != nil { for _, numFmt := range styleSheet.NumFmts.NumFmt { if numFmt.FormatCode == fmtCode { - numFmtID = numFmt.NumFmtID - return + return numFmt.NumFmtID } } } } - return + return numFmtID } // newNumFmt provides a function to check if number format code in the range diff --git a/xmlPivotTable.go b/xmlPivotTable.go index fd45ca8..8937503 100644 --- a/xmlPivotTable.go +++ b/xmlPivotTable.go @@ -273,7 +273,7 @@ type xlsxDataField struct { ShowDataAs string `xml:"showDataAs,attr,omitempty"` BaseField int `xml:"baseField,attr,omitempty"` BaseItem int64 `xml:"baseItem,attr,omitempty"` - NumFmtID string `xml:"numFmtId,attr,omitempty"` + NumFmtID int `xml:"numFmtId,attr,omitempty"` ExtLst *xlsxExtLst `xml:"extLst"` }