feat(docs): put-xlsx with excelize cutover workbook
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 5s
Tests / Test (Go 1.25) (pull_request) Successful in 1m59s
Tests / Test (Go stable) (pull_request) Successful in 2m5s
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 5s
Tests / Test (Go 1.25) (pull_request) Successful in 1m59s
Tests / Test (Go stable) (pull_request) Successful in 2m5s
Add internal/xlspipe (github.com/xuri/excelize/v2) and oo docs put-xlsx to generate multi-sheet budgets with named inputs, SUM/AVG/MIN formulas, cross-sheet links, Russian labels, and cell comments for OnlyOffice. Template cutover-portugal: sheets Ввод, Вс 6.09, Чт 3.09, Ср гип, Сводка. Upload via UploadProjectFileReplacing (--replace default). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package xlspipe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
const commentAuthor = "oo"
|
||||
|
||||
// Save writes the workbook to path (creates parent dirs).
|
||||
func Save(f *excelize.File, path string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.SaveAs(path)
|
||||
}
|
||||
|
||||
// quoteSheet returns an Excel sheet reference safe for formulas ('Name'!A1).
|
||||
func quoteSheet(name string) string {
|
||||
return "'" + strings.ReplaceAll(name, "'", "''") + "'"
|
||||
}
|
||||
|
||||
// defineInput registers a named range on sheet!B{row}, sets value, note, optional comment.
|
||||
func defineInput(f *excelize.File, sheet, name string, row int, value float64, note, comment string) error {
|
||||
cell := fmt.Sprintf("B%d", row)
|
||||
if err := f.SetCellFloat(sheet, cell, value, 2, 64); err != nil {
|
||||
return err
|
||||
}
|
||||
if note != "" {
|
||||
if err := f.SetCellStr(sheet, fmt.Sprintf("C%d", row), note); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
ref := fmt.Sprintf("%s!$B$%d", quoteSheet(sheet), row)
|
||||
if err := f.SetDefinedName(&excelize.DefinedName{Name: name, RefersTo: ref}); err != nil {
|
||||
return err
|
||||
}
|
||||
if comment != "" {
|
||||
return addCellComment(f, sheet, cell, comment)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addCellComment(f *excelize.File, sheet, cell, text string) error {
|
||||
return f.AddComment(sheet, excelize.Comment{
|
||||
Cell: cell,
|
||||
Author: commentAuthor,
|
||||
Text: text,
|
||||
Width: 280,
|
||||
Height: 120,
|
||||
})
|
||||
}
|
||||
|
||||
func setHeaders(f *excelize.File, sheet string, headers ...string) error {
|
||||
for i, h := range headers {
|
||||
cell, err := excelize.CoordinatesToCellName(i+1, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.SetCellStr(sheet, cell, h); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setFormulaRow(f *excelize.File, sheet string, row int, label string, low, mid, high, note string) error {
|
||||
if err := f.SetCellStr(sheet, fmt.Sprintf("A%d", row), label); err != nil {
|
||||
return err
|
||||
}
|
||||
for col, formula := range []string{low, mid, high} {
|
||||
cell, err := excelize.CoordinatesToCellName(col+2, row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.SetCellFormula(sheet, cell, formula); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if note != "" {
|
||||
return f.SetCellStr(sheet, fmt.Sprintf("E%d", row), note)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func styleSheet(f *excelize.File, sheet, inputsSheet string) error {
|
||||
widths := map[string]float64{"A": 34, "B": 12, "C": 12, "D": 12, "E": 40}
|
||||
for col, w := range widths {
|
||||
if err := f.SetColWidth(sheet, col, col, w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
headerStyle, err := f.NewStyle(&excelize.Style{
|
||||
Font: &excelize.Font{Bold: true},
|
||||
Fill: excelize.Fill{Type: "pattern", Color: []string{"#E8F0FE"}, Pattern: 1},
|
||||
Alignment: &excelize.Alignment{Horizontal: "center", WrapText: true},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
euroStyle, err := f.NewStyle(&excelize.Style{NumFmt: 4})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
inputStyle, err := f.NewStyle(&excelize.Style{
|
||||
NumFmt: 4,
|
||||
Fill: excelize.Fill{Type: "pattern", Color: []string{"#FFF9E6"}, Pattern: 1},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.SetCellStyle(sheet, "A1", "E1", headerStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
if sheet == inputsSheet {
|
||||
return f.SetCellStyle(sheet, "B2", "B30", inputStyle)
|
||||
}
|
||||
if err := f.SetCellStyle(sheet, "B2", "D20", euroStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func finalizeWorkbook(f *excelize.File) error {
|
||||
if err := f.SetCalcProps(&excelize.CalcPropsOptions{FullCalcOnLoad: boolPtr(true)}); err != nil {
|
||||
return err
|
||||
}
|
||||
return f.UpdateLinkedValue()
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool { return &v }
|
||||
Reference in New Issue
Block a user