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
This commit is contained in:
xuri 2024-08-18 00:18:02 +08:00
parent d21b598235
commit 9a38657515
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
5 changed files with 39 additions and 16 deletions

View File

@ -14459,7 +14459,7 @@ func (fn *formulaFuncs) VALUE(argsList *list.List) formulaArg {
value, _ := decimal.Float64() value, _ := decimal.Float64()
return newNumberFormulaArg(value * percent) 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) { if !isDateOnlyFmt(text) {
h, m, s, _, _, err := strToTime(text) h, m, s, _, _, err := strToTime(text)
errTime = err.Type == ArgError errTime = err.Type == ArgError
@ -14468,7 +14468,7 @@ func (fn *formulaFuncs) VALUE(argsList *list.List) formulaArg {
} }
} }
y, m, d, _, err := strToDate(text) y, m, d, _, err := strToDate(text)
errDate = err.Type == ArgError errDate := err.Type == ArgError
if !errDate { if !errDate {
dateValue = daysBetween(excelMinTime1900.Unix(), makeDate(y, time.Month(m), d)) + 1 dateValue = daysBetween(excelMinTime1900.Unix(), makeDate(y, time.Month(m), d)) + 1
} }

View File

@ -62,6 +62,10 @@ type PivotTableOptions struct {
} }
// PivotTableField directly maps the field settings of the pivot table. // 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 // Subtotal specifies the aggregation function that applies to this data
// field. The default value is sum. The possible values for this attribute // field. The default value is sum. The possible values for this attribute
// are: // are:
@ -78,8 +82,9 @@ type PivotTableOptions struct {
// Var // Var
// Varp // Varp
// //
// Name specifies the name of the data field. Maximum 255 characters // NumFmt specifies the number format ID of the data field, this filed only
// are allowed in data field name, excess characters will be truncated. // accepts built-in number format ID and does not support custom number format
// expression currently.
type PivotTableField struct { type PivotTableField struct {
Compact bool Compact bool
Data string Data string
@ -87,6 +92,7 @@ type PivotTableField struct {
Outline bool Outline bool
Subtotal string Subtotal string
DefaultSubtotal bool DefaultSubtotal bool
NumFmt int
} }
// AddPivotTable provides the method to add pivot table by given pivot table // 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) dataFieldsSubtotals := f.getPivotTableFieldsSubtotal(opts.Data)
dataFieldsName := f.getPivotTableFieldsName(opts.Data) dataFieldsName := f.getPivotTableFieldsName(opts.Data)
dataFieldsNumFmtID := f.getPivotTableFieldsNumFmtID(opts.Data)
for idx, dataField := range dataFieldsIndex { for idx, dataField := range dataFieldsIndex {
if pt.DataFields == nil { if pt.DataFields == nil {
pt.DataFields = &xlsxDataFields{} pt.DataFields = &xlsxDataFields{}
@ -460,6 +467,7 @@ func (f *File) addPivotDataFields(pt *xlsxPivotTableDefinition, opts *PivotTable
Name: dataFieldsName[idx], Name: dataFieldsName[idx],
Fld: dataField, Fld: dataField,
Subtotal: dataFieldsSubtotals[idx], Subtotal: dataFieldsSubtotals[idx],
NumFmtID: dataFieldsNumFmtID[idx],
}) })
} }
@ -687,6 +695,22 @@ func (f *File) getPivotTableFieldName(name string, fields []PivotTableField) str
return "" 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. // getPivotTableFieldOptions return options for specific field by given field name.
func (f *File) getPivotTableFieldOptions(name string, fields []PivotTableField) (options PivotTableField, ok bool) { func (f *File) getPivotTableFieldOptions(name string, fields []PivotTableField) (options PivotTableField, ok bool) {
for _, field := range fields { for _, field := range fields {
@ -891,6 +915,7 @@ func (f *File) extractPivotTableFields(order []string, pt *xlsxPivotTableDefinit
Data: order[field.Fld], Data: order[field.Fld],
Name: field.Name, Name: field.Name,
Subtotal: cases.Title(language.English).String(field.Subtotal), Subtotal: cases.Title(language.English).String(field.Subtotal),
NumFmt: field.NumFmtID,
}) })
} }
} }

View File

@ -34,7 +34,7 @@ func TestPivotTable(t *testing.T) {
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
Filter: []PivotTableField{{Data: "Region"}}, Filter: []PivotTableField{{Data: "Region"}},
Columns: []PivotTableField{{Data: "Type", DefaultSubtotal: true}}, 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, RowGrandTotals: true,
ColGrandTotals: true, ColGrandTotals: true,
ShowDrill: true, ShowDrill: true,
@ -131,7 +131,7 @@ func TestPivotTable(t *testing.T) {
PivotTableRange: "Sheet2!A1:AN17", PivotTableRange: "Sheet2!A1:AN17",
Rows: []PivotTableField{{Data: "Month"}}, Rows: []PivotTableField{{Data: "Month"}},
Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type", DefaultSubtotal: true}, {Data: "Year"}}, 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, RowGrandTotals: true,
ColGrandTotals: true, ColGrandTotals: true,
ShowDrill: true, ShowDrill: true,
@ -151,7 +151,7 @@ func TestPivotTable(t *testing.T) {
PivotTableRange: "Sheet2!A20:AR60", PivotTableRange: "Sheet2!A20:AR60",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Type"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Type"}},
Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Year"}}, 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, RowGrandTotals: true,
ColGrandTotals: true, ColGrandTotals: true,
ShowDrill: true, ShowDrill: true,
@ -171,7 +171,7 @@ func TestPivotTable(t *testing.T) {
PivotTableRange: "Sheet2!A65:AJ100", PivotTableRange: "Sheet2!A65:AJ100",
Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}}, Rows: []PivotTableField{{Data: "Month", DefaultSubtotal: true}, {Data: "Year"}},
Columns: []PivotTableField{{Data: "Region", DefaultSubtotal: true}, {Data: "Type"}}, 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, RowGrandTotals: true,
ColGrandTotals: true, ColGrandTotals: true,
ShowDrill: true, ShowDrill: true,

View File

@ -1902,27 +1902,25 @@ func (f *File) newFont(style *Style) (*xlsxFont, error) {
// getNumFmtID provides a function to get number format code ID. // getNumFmtID provides a function to get number format code ID.
// If given number format code does not exist, will return -1. // If given number format code does not exist, will return -1.
func getNumFmtID(styleSheet *xlsxStyleSheet, style *Style) (numFmtID int) { func getNumFmtID(styleSheet *xlsxStyleSheet, style *Style) int {
numFmtID = -1 numFmtID := -1
if _, ok := builtInNumFmt[style.NumFmt]; ok { if _, ok := builtInNumFmt[style.NumFmt]; ok {
return style.NumFmt return style.NumFmt
} }
if (27 <= style.NumFmt && style.NumFmt <= 36) || (50 <= style.NumFmt && style.NumFmt <= 81) { if (27 <= style.NumFmt && style.NumFmt <= 36) || (50 <= style.NumFmt && style.NumFmt <= 81) {
numFmtID = style.NumFmt return style.NumFmt
return
} }
if fmtCode, ok := currencyNumFmt[style.NumFmt]; ok { if fmtCode, ok := currencyNumFmt[style.NumFmt]; ok {
numFmtID = style.NumFmt numFmtID = style.NumFmt
if styleSheet.NumFmts != nil { if styleSheet.NumFmts != nil {
for _, numFmt := range styleSheet.NumFmts.NumFmt { for _, numFmt := range styleSheet.NumFmts.NumFmt {
if numFmt.FormatCode == fmtCode { if numFmt.FormatCode == fmtCode {
numFmtID = numFmt.NumFmtID return numFmt.NumFmtID
return
} }
} }
} }
} }
return return numFmtID
} }
// newNumFmt provides a function to check if number format code in the range // newNumFmt provides a function to check if number format code in the range

View File

@ -273,7 +273,7 @@ type xlsxDataField struct {
ShowDataAs string `xml:"showDataAs,attr,omitempty"` ShowDataAs string `xml:"showDataAs,attr,omitempty"`
BaseField int `xml:"baseField,attr,omitempty"` BaseField int `xml:"baseField,attr,omitempty"`
BaseItem int64 `xml:"baseItem,attr,omitempty"` BaseItem int64 `xml:"baseItem,attr,omitempty"`
NumFmtID string `xml:"numFmtId,attr,omitempty"` NumFmtID int `xml:"numFmtId,attr,omitempty"`
ExtLst *xlsxExtLst `xml:"extLst"` ExtLst *xlsxExtLst `xml:"extLst"`
} }