mirror of
https://github.com/hay-kot/homebox.git
synced 2025-08-04 16:50:27 +00:00
new reporting service
This commit is contained in:
parent
2e96d8c4c2
commit
8fcd0d84d3
6 changed files with 102 additions and 9 deletions
|
@ -1,11 +1,16 @@
|
||||||
package services
|
package services
|
||||||
|
|
||||||
import "github.com/hay-kot/homebox/backend/internal/data/repo"
|
import (
|
||||||
|
"github.com/hay-kot/homebox/backend/internal/core/services/reporting"
|
||||||
|
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
type AllServices struct {
|
type AllServices struct {
|
||||||
User *UserService
|
User *UserService
|
||||||
Group *GroupService
|
Group *GroupService
|
||||||
Items *ItemService
|
Items *ItemService
|
||||||
|
Reporting *reporting.ReportingService
|
||||||
}
|
}
|
||||||
|
|
||||||
type OptionsFunc func(*options)
|
type OptionsFunc func(*options)
|
||||||
|
@ -40,5 +45,7 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
|
||||||
repo: repos,
|
repo: repos,
|
||||||
autoIncrementAssetID: options.autoIncrementAssetID,
|
autoIncrementAssetID: options.autoIncrementAssetID,
|
||||||
},
|
},
|
||||||
|
// TODO: don't use global logger
|
||||||
|
Reporting: reporting.NewReportingService(repos, &log.Logger),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
85
backend/internal/core/services/reporting/reporting.go
Normal file
85
backend/internal/core/services/reporting/reporting.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package reporting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocarina/gocsv"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReportingService struct {
|
||||||
|
repos *repo.AllRepos
|
||||||
|
l *zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReportingService(repos *repo.AllRepos, l *zerolog.Logger) *ReportingService {
|
||||||
|
gocsv.SetCSVWriter(func(out io.Writer) *gocsv.SafeCSVWriter {
|
||||||
|
writer := csv.NewWriter(out)
|
||||||
|
writer.Comma = '\t'
|
||||||
|
return gocsv.NewSafeCSVWriter(writer)
|
||||||
|
})
|
||||||
|
|
||||||
|
return &ReportingService{
|
||||||
|
repos: repos,
|
||||||
|
l: l,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
|
||||||
|
// NullableTime is a custom type that implements the MarshalCSV interface
|
||||||
|
// to allow for nullable time.Time fields in the CSV output to be empty
|
||||||
|
// and not "0001-01-01". It also overrides the default CSV output format
|
||||||
|
type NullableTime time.Time
|
||||||
|
|
||||||
|
func (t NullableTime) MarshalCSV() (string, error) {
|
||||||
|
if time.Time(t).IsZero() {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
// YYYY-MM-DD
|
||||||
|
return time.Time(t).Format("2006-01-02"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type BillOfMaterialsEntry struct {
|
||||||
|
PurchaseDate NullableTime `csv:"Purchase Date"`
|
||||||
|
Name string `csv:"Name"`
|
||||||
|
Description string `csv:"Description"`
|
||||||
|
Manufacturer string `csv:"Manufacturer"`
|
||||||
|
SerialNumber string `csv:"Serial Number"`
|
||||||
|
ModelNumber string `csv:"Model Number"`
|
||||||
|
Quantity int `csv:"Quantity"`
|
||||||
|
Price float64 `csv:"Price"`
|
||||||
|
TotalPrice float64 `csv:"Total Price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BillOfMaterialsTSV returns a byte slice of the Bill of Materials for a given GID in TSV format
|
||||||
|
// See BillOfMaterialsEntry for the format of the output
|
||||||
|
func (rs *ReportingService) BillOfMaterialsTSV(ctx context.Context, GID uuid.UUID) ([]byte, error) {
|
||||||
|
entities, err := rs.repos.Items.GetAll(ctx, GID)
|
||||||
|
if err != nil {
|
||||||
|
rs.l.Debug().Err(err).Msg("failed to get all items for BOM Csv Reporting")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bomEntries := make([]BillOfMaterialsEntry, len(entities))
|
||||||
|
for i, entity := range entities {
|
||||||
|
bomEntries[i] = BillOfMaterialsEntry{
|
||||||
|
PurchaseDate: NullableTime(entity.PurchaseTime),
|
||||||
|
Name: entity.Name,
|
||||||
|
Description: entity.Description,
|
||||||
|
Manufacturer: entity.Manufacturer,
|
||||||
|
SerialNumber: entity.SerialNumber,
|
||||||
|
ModelNumber: entity.ModelNumber,
|
||||||
|
Quantity: entity.Quantity,
|
||||||
|
Price: entity.PurchasePrice,
|
||||||
|
TotalPrice: entity.PurchasePrice * float64(entity.Quantity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gocsv.MarshalBytes(&bomEntries)
|
||||||
|
}
|
|
@ -12,10 +12,10 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed testdata/import.csv
|
//go:embed .testdata/import.csv
|
||||||
var CSVData_Comma []byte
|
var CSVData_Comma []byte
|
||||||
|
|
||||||
//go:embed testdata/import.tsv
|
//go:embed .testdata/import.tsv
|
||||||
var CSVData_Tab []byte
|
var CSVData_Tab []byte
|
||||||
|
|
||||||
func loadcsv() [][]string {
|
func loadcsv() [][]string {
|
||||||
|
|
|
@ -186,6 +186,7 @@ func mapItemSummary(item *ent.Item) ItemSummary {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
mapItemOutErr = mapTErrFunc(mapItemOut)
|
mapItemOutErr = mapTErrFunc(mapItemOut)
|
||||||
|
mapItemsOutErr = mapTEachErrFunc(mapItemOut)
|
||||||
)
|
)
|
||||||
|
|
||||||
func mapFields(fields []*ent.ItemField) []ItemField {
|
func mapFields(fields []*ent.ItemField) []ItemField {
|
||||||
|
@ -434,8 +435,8 @@ func (e *ItemsRepository) QueryByAssetID(ctx context.Context, gid uuid.UUID, ass
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAll returns all the items in the database with the Labels and Locations eager loaded.
|
// GetAll returns all the items in the database with the Labels and Locations eager loaded.
|
||||||
func (e *ItemsRepository) GetAll(ctx context.Context, gid uuid.UUID) ([]ItemSummary, error) {
|
func (e *ItemsRepository) GetAll(ctx context.Context, gid uuid.UUID) ([]ItemOut, error) {
|
||||||
return mapItemsSummaryErr(e.db.Item.Query().
|
return mapItemsOutErr(e.db.Item.Query().
|
||||||
Where(item.HasGroupWith(group.ID(gid))).
|
Where(item.HasGroupWith(group.ID(gid))).
|
||||||
WithLabel().
|
WithLabel().
|
||||||
WithLocation().
|
WithLocation().
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue