feat: asset tags/ids (#142)

* add schema

* run db migration

* bulk seed asset IDs

* breaking: update runtime options

* conditionally increment asset IDs

* update API endpoints

* fix import asset id assignment

* refactor display + marshal/unmarshal

* add docs page

* add to form field

* hide 000-000 values

* update ENV vars
This commit is contained in:
Hayden 2022-11-13 14:17:55 -09:00 committed by GitHub
parent 976f68252d
commit 6dc2ae1bea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 905 additions and 72 deletions

View file

@ -0,0 +1,30 @@
package repo
import (
"bytes"
"fmt"
"strconv"
)
type AssetID int
func (aid AssetID) MarshalJSON() ([]byte, error) {
aidStr := fmt.Sprintf("%06d", aid)
aidStr = fmt.Sprintf("%s-%s", aidStr[:3], aidStr[3:])
return []byte(fmt.Sprintf(`"%s"`, aidStr)), nil
}
func (aid *AssetID) UnmarshalJSON(d []byte) error {
d = bytes.Replace(d, []byte(`"`), []byte(``), -1)
d = bytes.Replace(d, []byte(`-`), []byte(``), -1)
aidInt, err := strconv.Atoi(string(d))
if err != nil {
return err
}
*aid = AssetID(aidInt)
return nil
}

View file

@ -0,0 +1,115 @@
package repo
import (
"encoding/json"
"reflect"
"testing"
)
func TestAssetID_MarshalJSON(t *testing.T) {
tests := []struct {
name string
aid AssetID
want []byte
wantErr bool
}{
{
name: "basic test",
aid: 123,
want: []byte(`"000-123"`),
},
{
name: "zero test",
aid: 0,
want: []byte(`"000-000"`),
},
{
name: "large int",
aid: 123456789,
want: []byte(`"123-456789"`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.aid.MarshalJSON()
if (err != nil) != tt.wantErr {
t.Errorf("AssetID.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("AssetID.MarshalJSON() = %v, want %v", got, tt.want)
}
})
}
}
func TestAssetID_UnmarshalJSON(t *testing.T) {
type args struct {
data []byte
}
tests := []struct {
name string
aid *AssetID
args args
want AssetID
wantErr bool
}{
{
name: "basic test",
aid: new(AssetID),
want: 123,
args: args{
data: []byte(`{"AssetID":"000123"}`),
},
},
{
name: "dashed format",
aid: new(AssetID),
want: 123,
args: args{
data: []byte(`{"AssetID":"000-123"}`),
},
},
{
name: "no leading zeros",
aid: new(AssetID),
want: 123,
args: args{
data: []byte(`{"AssetID":"123"}`),
},
},
{
name: "trailing zeros",
aid: new(AssetID),
want: 123000,
args: args{
data: []byte(`{"AssetID":"000123000"}`),
},
},
{
name: "large int",
aid: new(AssetID),
want: 123456789,
args: args{
data: []byte(`{"AssetID":"123456789"}`),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
st := struct {
AssetID AssetID `json:"AssetID"`
}{}
err := json.Unmarshal(tt.args.data, &st)
if (err != nil) != tt.wantErr {
t.Errorf("AssetID.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
return
}
if st.AssetID != tt.want {
t.Errorf("AssetID.UnmarshalJSON() = %v, want %v", st.AssetID, tt.want)
}
})
}
}

View file

@ -44,6 +44,7 @@ type (
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable"`
Name string `json:"name"`
Description string `json:"description"`
AssetID AssetID `json:"-"`
// Edges
LocationID uuid.UUID `json:"locationId"`
@ -52,6 +53,7 @@ type (
ItemUpdate struct {
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable,x-omitempty"`
ID uuid.UUID `json:"id"`
AssetID AssetID `json:"assetId"`
Name string `json:"name"`
Description string `json:"description"`
Quantity int `json:"quantity"`
@ -107,6 +109,7 @@ type (
ItemOut struct {
Parent *ItemSummary `json:"parent,omitempty" extensions:"x-nullable,x-omitempty"`
ItemSummary
AssetID AssetID `json:"assetId,string"`
SerialNumber string `json:"serialNumber"`
ModelNumber string `json:"modelNumber"`
@ -215,6 +218,7 @@ func mapItemOut(item *ent.Item) ItemOut {
return ItemOut{
Parent: parent,
AssetID: AssetID(item.AssetID),
ItemSummary: mapItemSummary(item),
LifetimeWarranty: item.LifetimeWarranty,
WarrantyExpires: item.WarrantyExpires,
@ -359,13 +363,53 @@ func (e *ItemsRepository) GetAll(ctx context.Context, gid uuid.UUID) ([]ItemSumm
All(ctx))
}
func (e *ItemsRepository) GetAllZeroAssetID(ctx context.Context, GID uuid.UUID) ([]ItemSummary, error) {
q := e.db.Item.Query().Where(
item.HasGroupWith(group.ID(GID)),
item.AssetID(0),
).Order(
ent.Asc(item.FieldCreatedAt),
)
return mapItemsSummaryErr(q.All(ctx))
}
func (e *ItemsRepository) GetHighestAssetID(ctx context.Context, GID uuid.UUID) (AssetID, error) {
q := e.db.Item.Query().Where(
item.HasGroupWith(group.ID(GID)),
).Order(
ent.Desc(item.FieldAssetID),
).Limit(1)
result, err := q.First(ctx)
if err != nil {
if ent.IsNotFound(err) {
return 0, nil
}
return 0, err
}
return AssetID(result.AssetID), nil
}
func (e *ItemsRepository) SetAssetID(ctx context.Context, GID uuid.UUID, ID uuid.UUID, assetID AssetID) error {
q := e.db.Item.Update().Where(
item.HasGroupWith(group.ID(GID)),
item.ID(ID),
)
_, err := q.SetAssetID(int(assetID)).Save(ctx)
return err
}
func (e *ItemsRepository) Create(ctx context.Context, gid uuid.UUID, data ItemCreate) (ItemOut, error) {
q := e.db.Item.Create().
SetImportRef(data.ImportRef).
SetName(data.Name).
SetDescription(data.Description).
SetGroupID(gid).
SetLocationID(data.LocationID)
SetLocationID(data.LocationID).
SetAssetID(int(data.AssetID))
if data.LabelIDs != nil && len(data.LabelIDs) > 0 {
q.AddLabelIDs(data.LabelIDs...)
@ -414,7 +458,8 @@ func (e *ItemsRepository) UpdateByGroup(ctx context.Context, gid uuid.UUID, data
SetInsured(data.Insured).
SetWarrantyExpires(data.WarrantyExpires).
SetWarrantyDetails(data.WarrantyDetails).
SetQuantity(data.Quantity)
SetQuantity(data.Quantity).
SetAssetID(int(data.AssetID))
currentLabels, err := e.db.Item.Query().Where(item.ID(data.ID)).QueryLabel().All(ctx)
if err != nil {