mirror of
https://github.com/hay-kot/homebox.git
synced 2024-11-23 17:15:42 +00:00
feat: user defined currencies (#700)
* basic currency service for loading at runtime * api endpoint for currencies * sort slice before return * remove currency validation * validate using currency service * implement selecting dynamic currency options * bump go version * fix type definition * specify explicit type * change go versions * proper types for assetId * log/return currency error * make case insensative * use ToUpper instead * feat: adding new currencies (#715) * fix: task swag (#710) Co-authored-by: Quoing <pavel.cadersky@mavenir.com> * [feat] Adding new currencies --------- Co-authored-by: quoing <quoing@users.noreply.github.com> Co-authored-by: Quoing <pavel.cadersky@mavenir.com> Co-authored-by: Bradley <41597815+userbradley@users.noreply.github.com> * remove ts file and consoldate new values into json * move flag to options namespace * add env config for currencies * basic documentaion * remove in sync test --------- Co-authored-by: quoing <quoing@users.noreply.github.com> Co-authored-by: Quoing <pavel.cadersky@mavenir.com> Co-authored-by: Bradley <41597815+userbradley@users.noreply.github.com>
This commit is contained in:
parent
d2bde3f241
commit
c4b923847a
39 changed files with 1226 additions and 328 deletions
|
@ -35,6 +35,6 @@
|
|||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "node",
|
||||
"features": {
|
||||
"golang": "1.20"
|
||||
"golang": "1.21"
|
||||
}
|
||||
}
|
||||
|
|
2
.github/workflows/partial-backend.yaml
vendored
2
.github/workflows/partial-backend.yaml
vendored
|
@ -12,7 +12,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
|
|
2
.github/workflows/partial-frontend.yaml
vendored
2
.github/workflows/partial-frontend.yaml
vendored
|
@ -46,7 +46,7 @@ jobs:
|
|||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.20"
|
||||
go-version: "1.21"
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
|
|
|
@ -127,6 +127,22 @@ func (ctrl *V1Controller) HandleBase(ready ReadyFunc, build Build) errchain.Hand
|
|||
}
|
||||
}
|
||||
|
||||
// HandleCurrency godoc
|
||||
//
|
||||
// @Summary Currency
|
||||
// @Tags Base
|
||||
// @Produce json
|
||||
// @Success 200 {object} currencies.Currency
|
||||
// @Router /v1/currency [GET]
|
||||
func (ctrl *V1Controller) HandleCurrency() errchain.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
// Set Cache for 10 Minutes
|
||||
w.Header().Set("Cache-Control", "max-age=600")
|
||||
|
||||
return server.JSON(w, http.StatusOK, ctrl.svc.Currencies.Slice())
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl *V1Controller) HandleCacheWS() errchain.HandlerFunc {
|
||||
m := melody.New()
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
"github.com/hay-kot/homebox/backend/internal/sys/validate"
|
||||
"github.com/hay-kot/homebox/backend/internal/web/adapters"
|
||||
"github.com/hay-kot/httpkit/errchain"
|
||||
)
|
||||
|
@ -52,6 +53,14 @@ func (ctrl *V1Controller) HandleGroupGet() errchain.HandlerFunc {
|
|||
func (ctrl *V1Controller) HandleGroupUpdate() errchain.HandlerFunc {
|
||||
fn := func(r *http.Request, body repo.GroupUpdate) (repo.Group, error) {
|
||||
auth := services.NewContext(r.Context())
|
||||
|
||||
ok := ctrl.svc.Currencies.IsSupported(body.Currency)
|
||||
if !ok {
|
||||
return repo.Group{}, validate.NewFieldErrors(
|
||||
validate.NewFieldError("currency", "currency '" + body.Currency + "' is not supported"),
|
||||
)
|
||||
}
|
||||
|
||||
return ctrl.svc.Group.UpdateGroup(auth, body)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/currencies"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services/reporting/eventbus"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
|
@ -126,12 +128,40 @@ func run(cfg *config.Config) error {
|
|||
return err
|
||||
}
|
||||
|
||||
collectFuncs := []currencies.CollectorFunc{
|
||||
currencies.CollectDefaults(),
|
||||
}
|
||||
|
||||
if cfg.Options.CurrencyConfig != "" {
|
||||
log.Info().
|
||||
Str("path", cfg.Options.CurrencyConfig).
|
||||
Msg("loading currency config file")
|
||||
|
||||
content, err := os.ReadFile(cfg.Options.CurrencyConfig)
|
||||
if err != nil {
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Str("path", cfg.Options.CurrencyConfig).
|
||||
Msg("failed to read currency config file")
|
||||
}
|
||||
|
||||
collectFuncs = append(collectFuncs, currencies.CollectJSON(bytes.NewReader(content)))
|
||||
}
|
||||
|
||||
currencies, err := currencies.CollectionCurrencies(collectFuncs...)
|
||||
if err != nil {
|
||||
log.Fatal().
|
||||
Err(err).
|
||||
Msg("failed to collect currencies")
|
||||
}
|
||||
|
||||
app.bus = eventbus.New()
|
||||
app.db = c
|
||||
app.repos = repo.New(c, app.bus, cfg.Storage.Data)
|
||||
app.services = services.New(
|
||||
app.repos,
|
||||
services.WithAutoIncrementAssetID(cfg.Options.AutoIncrementAssetID),
|
||||
services.WithCurrencies(currencies),
|
||||
)
|
||||
|
||||
// =========================================================================
|
||||
|
|
|
@ -64,6 +64,8 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
|
|||
BuildTime: buildTime,
|
||||
})))
|
||||
|
||||
r.Get(v1Base("/currencies"), chain.ToHandlerFunc(v1Ctrl.HandleCurrency()))
|
||||
|
||||
providers := []v1.AuthProvider{
|
||||
providers.NewLocalProvider(a.services.User),
|
||||
}
|
||||
|
|
|
@ -150,6 +150,25 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/v1/currency": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Base"
|
||||
],
|
||||
"summary": "Currency",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/currencies.Currency"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/groups": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -410,6 +429,16 @@ const docTemplate = `{
|
|||
"description": "location Ids",
|
||||
"name": "locations",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "parent Ids",
|
||||
"name": "parentIds",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
@ -1574,7 +1603,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.ApiSummary"
|
||||
"$ref": "#/definitions/v1.APISummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1645,6 +1674,12 @@ const docTemplate = `{
|
|||
"schema": {
|
||||
"$ref": "#/definitions/v1.LoginForm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "auth provider",
|
||||
"name": "provider",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
@ -1823,6 +1858,23 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"currencies.Currency": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"local": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.DocumentOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1998,12 +2050,6 @@ const docTemplate = `{
|
|||
"$ref": "#/definitions/repo.ItemAttachment"
|
||||
}
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -2181,8 +2227,7 @@ const docTemplate = `{
|
|||
"type": "boolean"
|
||||
},
|
||||
"assetId": {
|
||||
"type": "string",
|
||||
"example": "0"
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
|
@ -2736,15 +2781,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ApiSummary": {
|
||||
"v1.APISummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowRegistration": {
|
||||
|
@ -2773,6 +2810,14 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Build": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -143,6 +143,25 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/v1/currency": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Base"
|
||||
],
|
||||
"summary": "Currency",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/currencies.Currency"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/groups": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -403,6 +422,16 @@
|
|||
"description": "location Ids",
|
||||
"name": "locations",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "parent Ids",
|
||||
"name": "parentIds",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
@ -1567,7 +1596,7 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.ApiSummary"
|
||||
"$ref": "#/definitions/v1.APISummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1638,6 +1667,12 @@
|
|||
"schema": {
|
||||
"$ref": "#/definitions/v1.LoginForm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "auth provider",
|
||||
"name": "provider",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
@ -1816,6 +1851,23 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"currencies.Currency": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"local": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.DocumentOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1991,12 +2043,6 @@
|
|||
"$ref": "#/definitions/repo.ItemAttachment"
|
||||
}
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -2174,8 +2220,7 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"assetId": {
|
||||
"type": "string",
|
||||
"example": "0"
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
|
@ -2729,15 +2774,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ApiSummary": {
|
||||
"v1.APISummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowRegistration": {
|
||||
|
@ -2766,6 +2803,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Build": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
basePath: /api
|
||||
definitions:
|
||||
currencies.Currency:
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
local:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
symbol:
|
||||
type: string
|
||||
type: object
|
||||
repo.DocumentOut:
|
||||
properties:
|
||||
id:
|
||||
|
@ -116,10 +127,6 @@ definitions:
|
|||
items:
|
||||
$ref: '#/definitions/repo.ItemAttachment'
|
||||
type: array
|
||||
children:
|
||||
items:
|
||||
$ref: '#/definitions/repo.ItemSummary'
|
||||
type: array
|
||||
createdAt:
|
||||
type: string
|
||||
description:
|
||||
|
@ -238,7 +245,6 @@ definitions:
|
|||
archived:
|
||||
type: boolean
|
||||
assetId:
|
||||
example: "0"
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
|
@ -608,12 +614,7 @@ definitions:
|
|||
token:
|
||||
type: string
|
||||
type: object
|
||||
v1.ActionAmountResult:
|
||||
properties:
|
||||
completed:
|
||||
type: integer
|
||||
type: object
|
||||
v1.ApiSummary:
|
||||
v1.APISummary:
|
||||
properties:
|
||||
allowRegistration:
|
||||
type: boolean
|
||||
|
@ -632,6 +633,11 @@ definitions:
|
|||
type: string
|
||||
type: array
|
||||
type: object
|
||||
v1.ActionAmountResult:
|
||||
properties:
|
||||
completed:
|
||||
type: integer
|
||||
type: object
|
||||
v1.Build:
|
||||
properties:
|
||||
buildTime:
|
||||
|
@ -789,6 +795,18 @@ paths:
|
|||
summary: Get Item by Asset ID
|
||||
tags:
|
||||
- Items
|
||||
/v1/currency:
|
||||
get:
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/currencies.Currency'
|
||||
summary: Currency
|
||||
tags:
|
||||
- Base
|
||||
/v1/groups:
|
||||
get:
|
||||
produces:
|
||||
|
@ -942,6 +960,13 @@ paths:
|
|||
type: string
|
||||
name: locations
|
||||
type: array
|
||||
- collectionFormat: multi
|
||||
description: parent Ids
|
||||
in: query
|
||||
items:
|
||||
type: string
|
||||
name: parentIds
|
||||
type: array
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
@ -1656,7 +1681,7 @@ paths:
|
|||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/v1.ApiSummary'
|
||||
$ref: '#/definitions/v1.APISummary'
|
||||
summary: Application Info
|
||||
tags:
|
||||
- Base
|
||||
|
@ -1699,6 +1724,10 @@ paths:
|
|||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/v1.LoginForm'
|
||||
- description: auth provider
|
||||
in: query
|
||||
name: provider
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
|
|
@ -115,6 +115,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
|
||||
github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
|
@ -122,6 +124,8 @@ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTS
|
|||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olahol/melody v1.1.4 h1:RQHfKZkQmDxI0+SLZRNBCn4LiXdqxLKRGSkT8Dyoe/E=
|
||||
github.com/olahol/melody v1.1.4/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
|
@ -139,6 +143,10 @@ github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
|||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
|
99
backend/internal/core/currencies/currencies.go
Normal file
99
backend/internal/core/currencies/currencies.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Package currencies provides a shared definition of currencies. This uses a global
|
||||
// variable to hold the currencies.
|
||||
package currencies
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:embed currencies.json
|
||||
var defaults []byte
|
||||
|
||||
type CollectorFunc func() ([]Currency, error)
|
||||
|
||||
func CollectJSON(reader io.Reader) CollectorFunc {
|
||||
return func() ([]Currency, error) {
|
||||
var currencies []Currency
|
||||
err := json.NewDecoder(reader).Decode(¤cies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return currencies, nil
|
||||
}
|
||||
}
|
||||
|
||||
func CollectDefaults() CollectorFunc {
|
||||
return CollectJSON(bytes.NewReader(defaults))
|
||||
}
|
||||
|
||||
func CollectionCurrencies(collectors ...CollectorFunc) ([]Currency, error) {
|
||||
out := make([]Currency, 0, len(collectors))
|
||||
for i := range collectors {
|
||||
c, err := collectors[i]()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out = append(out, c...)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type Currency struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Local string `json:"local"`
|
||||
Symbol string `json:"symbol"`
|
||||
}
|
||||
|
||||
type CurrencyRegistry struct {
|
||||
mu sync.RWMutex
|
||||
registry map[string]Currency
|
||||
}
|
||||
|
||||
func NewCurrencyService(currencies []Currency) *CurrencyRegistry {
|
||||
registry := make(map[string]Currency, len(currencies))
|
||||
for i := range currencies {
|
||||
registry[currencies[i].Code] = currencies[i]
|
||||
}
|
||||
|
||||
return &CurrencyRegistry{
|
||||
registry: registry,
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *CurrencyRegistry) Slice() []Currency {
|
||||
cs.mu.RLock()
|
||||
defer cs.mu.RUnlock()
|
||||
|
||||
keys := make([]string, 0, len(cs.registry))
|
||||
for key := range cs.registry {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
slices.Sort(keys)
|
||||
|
||||
out := make([]Currency, 0, len(cs.registry))
|
||||
for i := range keys {
|
||||
out = append(out, cs.registry[keys[i]])
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (cs *CurrencyRegistry) IsSupported(code string) bool {
|
||||
upper := strings.ToUpper(code)
|
||||
|
||||
cs.mu.RLock()
|
||||
defer cs.mu.RUnlock()
|
||||
_, ok := cs.registry[upper]
|
||||
return ok
|
||||
}
|
626
backend/internal/core/currencies/currencies.json
Normal file
626
backend/internal/core/currencies/currencies.json
Normal file
|
@ -0,0 +1,626 @@
|
|||
[
|
||||
{
|
||||
"code": "AED",
|
||||
"local": "United Arab Emirates",
|
||||
"symbol": "د.إ",
|
||||
"name": "United Arab Emirates Dirham"
|
||||
},
|
||||
{
|
||||
"code": "AFN",
|
||||
"local": "Afghanistan",
|
||||
"symbol": "؋",
|
||||
"name": "Afghan Afghani"
|
||||
},
|
||||
{
|
||||
"code": "ALL",
|
||||
"local": "Albania",
|
||||
"symbol": "L",
|
||||
"name": "Albanian Lek"
|
||||
},
|
||||
{
|
||||
"code": "AMD",
|
||||
"local": "Armenia",
|
||||
"symbol": "֏",
|
||||
"name": "Armenian Dram"
|
||||
},
|
||||
{
|
||||
"code": "ANG",
|
||||
"local": "Netherlands Antilles",
|
||||
"symbol": "ƒ",
|
||||
"name": "Netherlands Antillean Guilder"
|
||||
},
|
||||
{
|
||||
"code": "AOA",
|
||||
"local": "Angola",
|
||||
"symbol": "Kz",
|
||||
"name": "Angolan Kwanza"
|
||||
},
|
||||
{
|
||||
"code": "ARS",
|
||||
"local": "Argentina",
|
||||
"symbol": "$",
|
||||
"name": "Argentine Peso"
|
||||
},
|
||||
{
|
||||
"code": "AUD",
|
||||
"local": "Australia",
|
||||
"symbol": "A$",
|
||||
"name": "Australian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "AWG",
|
||||
"local": "Aruba",
|
||||
"symbol": "ƒ",
|
||||
"name": "Aruban Florin"
|
||||
},
|
||||
{
|
||||
"code": "AZN",
|
||||
"local": "Azerbaijan",
|
||||
"symbol": "₼",
|
||||
"name": "Azerbaijani Manat"
|
||||
},
|
||||
{
|
||||
"code": "BAM",
|
||||
"local": "Bosnia and Herzegovina",
|
||||
"symbol": "KM",
|
||||
"name": "Bosnia and Herzegovina Convertible Mark"
|
||||
},
|
||||
{
|
||||
"code": "BBD",
|
||||
"local": "Barbados",
|
||||
"symbol": "Bds$",
|
||||
"name": "Barbadian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BDT",
|
||||
"local": "Bangladesh",
|
||||
"symbol": "৳",
|
||||
"name": "Bangladeshi Taka"
|
||||
},
|
||||
{
|
||||
"code": "BGN",
|
||||
"local": "Bulgaria",
|
||||
"symbol": "лв",
|
||||
"name": "Bulgarian lev"
|
||||
},
|
||||
{
|
||||
"code": "BHD",
|
||||
"local": "Bahrain",
|
||||
"symbol": "ب.د",
|
||||
"name": "Bahraini Dinar"
|
||||
},
|
||||
{
|
||||
"code": "BIF",
|
||||
"local": "Burundi",
|
||||
"symbol": "FBu",
|
||||
"name": "Burundian Franc"
|
||||
},
|
||||
{
|
||||
"code": "BMD",
|
||||
"local": "Bermuda",
|
||||
"symbol": "BD$",
|
||||
"name": "Bermudian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BND",
|
||||
"local": "Brunei",
|
||||
"symbol": "B$",
|
||||
"name": "Brunei Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BOB",
|
||||
"local": "Bolivia",
|
||||
"symbol": "Bs.",
|
||||
"name": "Bolivian Boliviano"
|
||||
},
|
||||
{
|
||||
"code": "BRL",
|
||||
"local": "Brazil",
|
||||
"symbol": "R$",
|
||||
"name": "Brazilian Real"
|
||||
},
|
||||
{
|
||||
"code": "BSD",
|
||||
"local": "Bahamas",
|
||||
"symbol": "B$",
|
||||
"name": "Bahamian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "BTN",
|
||||
"local": "Bhutan",
|
||||
"symbol": "Nu.",
|
||||
"name": "Bhutanese Ngultrum"
|
||||
},
|
||||
{
|
||||
"code": "BWP",
|
||||
"local": "Botswana",
|
||||
"symbol": "P",
|
||||
"name": "Botswana Pula"
|
||||
},
|
||||
{
|
||||
"code": "BYN",
|
||||
"local": "Belarus",
|
||||
"symbol": "Br",
|
||||
"name": "Belarusian Ruble"
|
||||
},
|
||||
{
|
||||
"code": "BZD",
|
||||
"local": "Belize",
|
||||
"symbol": "BZ$",
|
||||
"name": "Belize Dollar"
|
||||
},
|
||||
{
|
||||
"code": "CAD",
|
||||
"local": "Canada",
|
||||
"symbol": "C$",
|
||||
"name": "Canadian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "CDF",
|
||||
"local": "Democratic Republic of the Congo",
|
||||
"symbol": "FC",
|
||||
"name": "Congolese Franc"
|
||||
},
|
||||
{
|
||||
"code": "CHF",
|
||||
"local": "Switzerland",
|
||||
"symbol": "CHF",
|
||||
"name": "Swiss Franc"
|
||||
},
|
||||
{
|
||||
"code": "CLP",
|
||||
"local": "Chile",
|
||||
"symbol": "CL$",
|
||||
"name": "Chilean Peso"
|
||||
},
|
||||
{
|
||||
"code": "CNY",
|
||||
"local": "China",
|
||||
"symbol": "¥",
|
||||
"name": "Chinese Yuan"
|
||||
},
|
||||
{
|
||||
"code": "COP",
|
||||
"local": "Colombia",
|
||||
"symbol": "COL$",
|
||||
"name": "Colombian Peso"
|
||||
},
|
||||
{
|
||||
"code": "CRC",
|
||||
"local": "Costa Rica",
|
||||
"symbol": "₡",
|
||||
"name": "Costa Rican Colón"
|
||||
},
|
||||
{
|
||||
"code": "CUP",
|
||||
"local": "Cuba",
|
||||
"symbol": "₱",
|
||||
"name": "Cuban Peso"
|
||||
},
|
||||
{
|
||||
"code": "CVE",
|
||||
"local": "Cape Verde",
|
||||
"symbol": "$",
|
||||
"name": "Cape Verdean Escudo"
|
||||
},
|
||||
{
|
||||
"code": "CZK",
|
||||
"local": "Czech Republic",
|
||||
"symbol": "Kč",
|
||||
"name": "Czech Koruna"
|
||||
},
|
||||
{
|
||||
"code": "DJF",
|
||||
"local": "Djibouti",
|
||||
"symbol": "Fdj",
|
||||
"name": "Djiboutian Franc"
|
||||
},
|
||||
{
|
||||
"code": "DKK",
|
||||
"local": "Denmark",
|
||||
"symbol": "kr",
|
||||
"name": "Danish Krone"
|
||||
},
|
||||
{
|
||||
"code": "DOP",
|
||||
"local": "Dominican Republic",
|
||||
"symbol": "RD$",
|
||||
"name": "Dominican Peso"
|
||||
},
|
||||
{
|
||||
"code": "DZD",
|
||||
"local": "Algeria",
|
||||
"symbol": "د.ج",
|
||||
"name": "Algerian Dinar"
|
||||
},
|
||||
{
|
||||
"code": "EGP",
|
||||
"local": "Egypt",
|
||||
"symbol": "£",
|
||||
"name": "Egyptian Pound"
|
||||
},
|
||||
{
|
||||
"code": "ERN",
|
||||
"local": "Eritrea",
|
||||
"symbol": "Nfk",
|
||||
"name": "Eritrean Nakfa"
|
||||
},
|
||||
{
|
||||
"code": "ETB",
|
||||
"local": "Ethiopia",
|
||||
"symbol": "Br",
|
||||
"name": "Ethiopian Birr"
|
||||
},
|
||||
{
|
||||
"code": "EUR",
|
||||
"local": "Eurozone",
|
||||
"symbol": "€",
|
||||
"name": "Euro"
|
||||
},
|
||||
{
|
||||
"code": "FJD",
|
||||
"local": "Fiji",
|
||||
"symbol": "FJ$",
|
||||
"name": "Fijian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "FKP",
|
||||
"local": "Falkland Islands",
|
||||
"symbol": "£",
|
||||
"name": "Falkland Islands Pound"
|
||||
},
|
||||
{
|
||||
"code": "FOK",
|
||||
"local": "Faroe Islands",
|
||||
"symbol": "kr",
|
||||
"name": "Faroese Króna"
|
||||
},
|
||||
{
|
||||
"code": "GBP",
|
||||
"local": "United Kingdom",
|
||||
"symbol": "£",
|
||||
"name": "British Pound Sterling"
|
||||
},
|
||||
{
|
||||
"code": "GEL",
|
||||
"local": "Georgia",
|
||||
"symbol": "₾",
|
||||
"name": "Georgian Lari"
|
||||
},
|
||||
{
|
||||
"code": "GGP",
|
||||
"local": "Guernsey",
|
||||
"symbol": "£",
|
||||
"name": "Guernsey Pound"
|
||||
},
|
||||
{
|
||||
"code": "GHS",
|
||||
"local": "Ghana",
|
||||
"symbol": "GH₵",
|
||||
"name": "Ghanaian Cedi"
|
||||
},
|
||||
{
|
||||
"code": "GIP",
|
||||
"local": "Gibraltar",
|
||||
"symbol": "£",
|
||||
"name": "Gibraltar Pound"
|
||||
},
|
||||
{
|
||||
"code": "GMD",
|
||||
"local": "Gambia",
|
||||
"symbol": "D",
|
||||
"name": "Gambian Dalasi"
|
||||
},
|
||||
{
|
||||
"code": "GNF",
|
||||
"local": "Guinea",
|
||||
"symbol": "FG",
|
||||
"name": "Guinean Franc"
|
||||
},
|
||||
{
|
||||
"code": "GTQ",
|
||||
"local": "Guatemala",
|
||||
"symbol": "Q",
|
||||
"name": "Guatemalan Quetzal"
|
||||
},
|
||||
{
|
||||
"code": "GYD",
|
||||
"local": "Guyana",
|
||||
"symbol": "GY$",
|
||||
"name": "Guyanese Dollar"
|
||||
},
|
||||
{
|
||||
"code": "HKD",
|
||||
"local": "Hong Kong",
|
||||
"symbol": "HK$",
|
||||
"name": "Hong Kong Dollar"
|
||||
},
|
||||
{
|
||||
"code": "HNL",
|
||||
"local": "Honduras",
|
||||
"symbol": "L",
|
||||
"name": "Honduran Lempira"
|
||||
},
|
||||
{
|
||||
"code": "HRK",
|
||||
"local": "Croatia",
|
||||
"symbol": "kn",
|
||||
"name": "Croatian Kuna"
|
||||
},
|
||||
{
|
||||
"code": "HTG",
|
||||
"local": "Haiti",
|
||||
"symbol": "G",
|
||||
"name": "Haitian Gourde"
|
||||
},
|
||||
{
|
||||
"code": "HUF",
|
||||
"local": "Hungary",
|
||||
"symbol": "Ft",
|
||||
"name": "Hungarian Forint"
|
||||
},
|
||||
{
|
||||
"code": "IDR",
|
||||
"local": "Indonesia",
|
||||
"symbol": "Rp",
|
||||
"name": "Indonesian Rupiah"
|
||||
},
|
||||
{
|
||||
"code": "ILS",
|
||||
"local": "Israel",
|
||||
"symbol": "₪",
|
||||
"name": "Israeli New Shekel"
|
||||
},
|
||||
{
|
||||
"code": "IMP",
|
||||
"local": "Isle of Man",
|
||||
"symbol": "£",
|
||||
"name": "Manx Pound"
|
||||
},
|
||||
{
|
||||
"code": "INR",
|
||||
"local": "India",
|
||||
"symbol": "₹",
|
||||
"name": "Indian Rupee"
|
||||
},
|
||||
{
|
||||
"code": "IQD",
|
||||
"local": "Iraq",
|
||||
"symbol": "ع.د",
|
||||
"name": "Iraqi Dinar"
|
||||
},
|
||||
{
|
||||
"code": "IRR",
|
||||
"local": "Iran",
|
||||
"symbol": "﷼",
|
||||
"name": "Iranian Rial"
|
||||
},
|
||||
{
|
||||
"code": "ISK",
|
||||
"local": "Iceland",
|
||||
"symbol": "kr",
|
||||
"name": "Icelandic Króna"
|
||||
},
|
||||
{
|
||||
"code": "JEP",
|
||||
"local": "Jersey",
|
||||
"symbol": "£",
|
||||
"name": "Jersey Pound"
|
||||
},
|
||||
{
|
||||
"code": "JMD",
|
||||
"local": "Jamaica",
|
||||
"symbol": "J$",
|
||||
"name": "Jamaican Dollar"
|
||||
},
|
||||
{
|
||||
"code": "JOD",
|
||||
"local": "Jordan",
|
||||
"symbol": "د.ا",
|
||||
"name": "Jordanian Dinar"
|
||||
},
|
||||
{
|
||||
"code": "JPY",
|
||||
"local": "Japan",
|
||||
"symbol": "¥",
|
||||
"name": "Japanese Yen"
|
||||
},
|
||||
{
|
||||
"code": "KES",
|
||||
"local": "Kenya",
|
||||
"symbol": "KSh",
|
||||
"name": "Kenyan Shilling"
|
||||
},
|
||||
{
|
||||
"code": "KGS",
|
||||
"local": "Kyrgyzstan",
|
||||
"symbol": "с",
|
||||
"name": "Kyrgyzstani Som"
|
||||
},
|
||||
{
|
||||
"code": "KHR",
|
||||
"local": "Cambodia",
|
||||
"symbol": "៛",
|
||||
"name": "Cambodian Riel"
|
||||
},
|
||||
{
|
||||
"code": "KID",
|
||||
"local": "Kiribati",
|
||||
"symbol": "$",
|
||||
"name": "Kiribati Dollar"
|
||||
},
|
||||
{
|
||||
"code": "KMF",
|
||||
"local": "Comoros",
|
||||
"symbol": "CF",
|
||||
"name": "Comorian Franc"
|
||||
},
|
||||
{
|
||||
"code": "KRW",
|
||||
"local": "South Korea",
|
||||
"symbol": "₩",
|
||||
"name": "South Korean Won"
|
||||
},
|
||||
{
|
||||
"code": "KWD",
|
||||
"local": "Kuwait",
|
||||
"symbol": "د.ك",
|
||||
"name": "Kuwaiti Dinar"
|
||||
},
|
||||
{
|
||||
"code": "KYD",
|
||||
"local": "Cayman Islands",
|
||||
"symbol": "CI$",
|
||||
"name": "Cayman Islands Dollar"
|
||||
},
|
||||
{
|
||||
"code": "KZT",
|
||||
"local": "Kazakhstan",
|
||||
"symbol": "₸",
|
||||
"name": "Kazakhstani Tenge"
|
||||
},
|
||||
{
|
||||
"code": "LAK",
|
||||
"local": "Laos",
|
||||
"symbol": "₭",
|
||||
"name": "Lao Kip"
|
||||
},
|
||||
{
|
||||
"code": "LBP",
|
||||
"local": "Lebanon",
|
||||
"symbol": "ل.ل",
|
||||
"name": "Lebanese Pound"
|
||||
},
|
||||
{
|
||||
"code": "LKR",
|
||||
"local": "Sri Lanka",
|
||||
"symbol": "₨",
|
||||
"name": "Sri Lankan Rupee"
|
||||
},
|
||||
{
|
||||
"code": "LRD",
|
||||
"local": "Liberia",
|
||||
"symbol": "L$",
|
||||
"name": "Liberian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "LSL",
|
||||
"local": "Lesotho",
|
||||
"symbol": "M",
|
||||
"name": "Lesotho Loti"
|
||||
},
|
||||
{
|
||||
"code": "LYD",
|
||||
"local": "Libya",
|
||||
"symbol": "ل.د",
|
||||
"name": "Libyan Dinar"
|
||||
},
|
||||
{
|
||||
"code": "MAD",
|
||||
"local": "Morocco",
|
||||
"symbol": "د.م.",
|
||||
"name": "Moroccan Dirham"
|
||||
},
|
||||
{
|
||||
"code": "MDL",
|
||||
"local": "Moldova",
|
||||
"symbol": "lei",
|
||||
"name": "Moldovan Leu"
|
||||
},
|
||||
{
|
||||
"code": "MGA",
|
||||
"local": "Madagascar",
|
||||
"symbol": "Ar",
|
||||
"name": "Malagasy Ariary"
|
||||
},
|
||||
{
|
||||
"code": "MKD",
|
||||
"local": "North Macedonia",
|
||||
"symbol": "ден",
|
||||
"name": "Macedonian Denar"
|
||||
},
|
||||
{
|
||||
"code": "MMK",
|
||||
"local": "Myanmar",
|
||||
"symbol": "K",
|
||||
"name": "Myanmar Kyat"
|
||||
},
|
||||
{
|
||||
"code": "MNT",
|
||||
"local": "Mongolia",
|
||||
"symbol": "₮",
|
||||
"name": "Mongolian Tugrik"
|
||||
},
|
||||
{
|
||||
"code": "MOP",
|
||||
"local": "Macau",
|
||||
"symbol": "MOP$",
|
||||
"name": "Macanese Pataca"
|
||||
},
|
||||
{
|
||||
"code": "MRU",
|
||||
"local": "Mauritania",
|
||||
"symbol": "UM",
|
||||
"name": "Mauritanian Ouguiya"
|
||||
},
|
||||
{
|
||||
"code": "MUR",
|
||||
"local": "Mauritius",
|
||||
"symbol": "₨",
|
||||
"name": "Mauritian Rupee"
|
||||
},
|
||||
{
|
||||
"code": "MVR",
|
||||
"local": "Maldives",
|
||||
"symbol": "Rf",
|
||||
"name": "Maldivian Rufiyaa"
|
||||
},
|
||||
{
|
||||
"code": "MWK",
|
||||
"local": "Malawi",
|
||||
"symbol": "MK",
|
||||
"name": "Malawian Kwacha"
|
||||
},
|
||||
{
|
||||
"code": "MXN",
|
||||
"local": "Mexico",
|
||||
"symbol": "Mex$",
|
||||
"name": "Mexican Peso"
|
||||
},
|
||||
{
|
||||
"code": "MYR",
|
||||
"local": "Malaysia",
|
||||
"symbol": "RM",
|
||||
"name": "Malaysian Ringgit"
|
||||
},
|
||||
{
|
||||
"code": "MZN",
|
||||
"local": "Mozambique",
|
||||
"symbol": "MT",
|
||||
"name": "Mozambican Metical"
|
||||
},
|
||||
{
|
||||
"code": "NAD",
|
||||
"local": "Namibia",
|
||||
"symbol": "N$",
|
||||
"name": "Namibian Dollar"
|
||||
},
|
||||
{
|
||||
"code": "NGN",
|
||||
"local": "Nigeria",
|
||||
"symbol": "₦",
|
||||
"name": "Nigerian Naira"
|
||||
},
|
||||
{
|
||||
"code": "NIO",
|
||||
"local": "Nicaragua",
|
||||
"symbol": "C$",
|
||||
"name": "Nicaraguan Córdoba"
|
||||
},
|
||||
{
|
||||
"code": "UAH",
|
||||
"local": "Ukraine",
|
||||
"symbol": "₴",
|
||||
"name": "Ukrainian Hryvnia"
|
||||
}
|
||||
]
|
|
@ -2,6 +2,7 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"github.com/hay-kot/homebox/backend/internal/core/currencies"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
)
|
||||
|
||||
|
@ -10,12 +11,14 @@ type AllServices struct {
|
|||
Group *GroupService
|
||||
Items *ItemService
|
||||
BackgroundService *BackgroundService
|
||||
Currencies *currencies.CurrencyRegistry
|
||||
}
|
||||
|
||||
type OptionsFunc func(*options)
|
||||
|
||||
type options struct {
|
||||
autoIncrementAssetID bool
|
||||
currencies []currencies.Currency
|
||||
}
|
||||
|
||||
func WithAutoIncrementAssetID(v bool) func(*options) {
|
||||
|
@ -24,13 +27,27 @@ func WithAutoIncrementAssetID(v bool) func(*options) {
|
|||
}
|
||||
}
|
||||
|
||||
func WithCurrencies(v []currencies.Currency) func(*options) {
|
||||
return func(o *options) {
|
||||
o.currencies = v
|
||||
}
|
||||
}
|
||||
|
||||
func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
|
||||
if repos == nil {
|
||||
panic("repos cannot be nil")
|
||||
}
|
||||
|
||||
defaultCurrencies, err := currencies.CollectionCurrencies(
|
||||
currencies.CollectDefaults(),
|
||||
)
|
||||
if err != nil {
|
||||
panic("failed to collect default currencies")
|
||||
}
|
||||
|
||||
options := &options{
|
||||
autoIncrementAssetID: true,
|
||||
currencies: defaultCurrencies,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
|
@ -45,5 +62,6 @@ func New(repos *repo.AllRepos, opts ...OptionsFunc) *AllServices {
|
|||
autoIncrementAssetID: options.autoIncrementAssetID,
|
||||
},
|
||||
BackgroundService: &BackgroundService{repos},
|
||||
Currencies: currencies.NewCurrencyService(options.currencies),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hay-kot/homebox/backend/internal/core/currencies"
|
||||
"github.com/hay-kot/homebox/backend/internal/core/services/reporting/eventbus"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/repo"
|
||||
|
@ -61,7 +62,12 @@ func TestMain(m *testing.M) {
|
|||
|
||||
tClient = client
|
||||
tRepos = repo.New(tClient, tbus, os.TempDir()+"/homebox")
|
||||
tSvc = New(tRepos)
|
||||
|
||||
defaults, _ := currencies.CollectionCurrencies(
|
||||
currencies.CollectDefaults(),
|
||||
)
|
||||
|
||||
tSvc = New(tRepos, WithCurrencies(defaults))
|
||||
defer func() { _ = client.Close() }()
|
||||
|
||||
bootstrap()
|
||||
|
|
|
@ -25,7 +25,7 @@ type Group struct {
|
|||
// Name holds the value of the "name" field.
|
||||
Name string `json:"name,omitempty"`
|
||||
// Currency holds the value of the "currency" field.
|
||||
Currency group.Currency `json:"currency,omitempty"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the GroupQuery when eager-loading is set.
|
||||
Edges GroupEdges `json:"edges"`
|
||||
|
@ -170,7 +170,7 @@ func (gr *Group) assignValues(columns []string, values []any) error {
|
|||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field currency", values[i])
|
||||
} else if value.Valid {
|
||||
gr.Currency = group.Currency(value.String)
|
||||
gr.Currency = value.String
|
||||
}
|
||||
default:
|
||||
gr.selectValues.Set(columns[i], values[i])
|
||||
|
@ -253,7 +253,7 @@ func (gr *Group) String() string {
|
|||
builder.WriteString(gr.Name)
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("currency=")
|
||||
builder.WriteString(fmt.Sprintf("%v", gr.Currency))
|
||||
builder.WriteString(gr.Currency)
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package group
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
|
@ -119,65 +118,12 @@ var (
|
|||
UpdateDefaultUpdatedAt func() time.Time
|
||||
// NameValidator is a validator for the "name" field. It is called by the builders before save.
|
||||
NameValidator func(string) error
|
||||
// DefaultCurrency holds the default value on creation for the "currency" field.
|
||||
DefaultCurrency string
|
||||
// DefaultID holds the default value on creation for the "id" field.
|
||||
DefaultID func() uuid.UUID
|
||||
)
|
||||
|
||||
// Currency defines the type for the "currency" enum field.
|
||||
type Currency string
|
||||
|
||||
// CurrencyUsd is the default value of the Currency enum.
|
||||
const DefaultCurrency = CurrencyUsd
|
||||
|
||||
// Currency values.
|
||||
const (
|
||||
CurrencyAed Currency = "aed"
|
||||
CurrencyAud Currency = "aud"
|
||||
CurrencyBgn Currency = "bgn"
|
||||
CurrencyBrl Currency = "brl"
|
||||
CurrencyCad Currency = "cad"
|
||||
CurrencyChf Currency = "chf"
|
||||
CurrencyCzk Currency = "czk"
|
||||
CurrencyDkk Currency = "dkk"
|
||||
CurrencyEur Currency = "eur"
|
||||
CurrencyGbp Currency = "gbp"
|
||||
CurrencyHkd Currency = "hkd"
|
||||
CurrencyIdr Currency = "idr"
|
||||
CurrencyInr Currency = "inr"
|
||||
CurrencyJpy Currency = "jpy"
|
||||
CurrencyKrw Currency = "krw"
|
||||
CurrencyMxn Currency = "mxn"
|
||||
CurrencyNok Currency = "nok"
|
||||
CurrencyNzd Currency = "nzd"
|
||||
CurrencyPln Currency = "pln"
|
||||
CurrencyRmb Currency = "rmb"
|
||||
CurrencyRon Currency = "ron"
|
||||
CurrencyRub Currency = "rub"
|
||||
CurrencySar Currency = "sar"
|
||||
CurrencySek Currency = "sek"
|
||||
CurrencySgd Currency = "sgd"
|
||||
CurrencyThb Currency = "thb"
|
||||
CurrencyTry Currency = "try"
|
||||
CurrencyUsd Currency = "usd"
|
||||
CurrencyXag Currency = "xag"
|
||||
CurrencyXau Currency = "xau"
|
||||
CurrencyZar Currency = "zar"
|
||||
)
|
||||
|
||||
func (c Currency) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// CurrencyValidator is a validator for the "currency" field enum values. It is called by the builders before save.
|
||||
func CurrencyValidator(c Currency) error {
|
||||
switch c {
|
||||
case CurrencyAed, CurrencyAud, CurrencyBgn, CurrencyBrl, CurrencyCad, CurrencyChf, CurrencyCzk, CurrencyDkk, CurrencyEur, CurrencyGbp, CurrencyHkd, CurrencyIdr, CurrencyInr, CurrencyJpy, CurrencyKrw, CurrencyMxn, CurrencyNok, CurrencyNzd, CurrencyPln, CurrencyRmb, CurrencyRon, CurrencyRub, CurrencySar, CurrencySek, CurrencySgd, CurrencyThb, CurrencyTry, CurrencyUsd, CurrencyXag, CurrencyXau, CurrencyZar:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("group: invalid enum value for currency field: %q", c)
|
||||
}
|
||||
}
|
||||
|
||||
// OrderOption defines the ordering options for the Group queries.
|
||||
type OrderOption func(*sql.Selector)
|
||||
|
||||
|
|
|
@ -71,6 +71,11 @@ func Name(v string) predicate.Group {
|
|||
return predicate.Group(sql.FieldEQ(FieldName, v))
|
||||
}
|
||||
|
||||
// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ.
|
||||
func Currency(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldEQ(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||
func CreatedAtEQ(v time.Time) predicate.Group {
|
||||
return predicate.Group(sql.FieldEQ(FieldCreatedAt, v))
|
||||
|
@ -217,25 +222,70 @@ func NameContainsFold(v string) predicate.Group {
|
|||
}
|
||||
|
||||
// CurrencyEQ applies the EQ predicate on the "currency" field.
|
||||
func CurrencyEQ(v Currency) predicate.Group {
|
||||
func CurrencyEQ(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldEQ(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyNEQ applies the NEQ predicate on the "currency" field.
|
||||
func CurrencyNEQ(v Currency) predicate.Group {
|
||||
func CurrencyNEQ(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldNEQ(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyIn applies the In predicate on the "currency" field.
|
||||
func CurrencyIn(vs ...Currency) predicate.Group {
|
||||
func CurrencyIn(vs ...string) predicate.Group {
|
||||
return predicate.Group(sql.FieldIn(FieldCurrency, vs...))
|
||||
}
|
||||
|
||||
// CurrencyNotIn applies the NotIn predicate on the "currency" field.
|
||||
func CurrencyNotIn(vs ...Currency) predicate.Group {
|
||||
func CurrencyNotIn(vs ...string) predicate.Group {
|
||||
return predicate.Group(sql.FieldNotIn(FieldCurrency, vs...))
|
||||
}
|
||||
|
||||
// CurrencyGT applies the GT predicate on the "currency" field.
|
||||
func CurrencyGT(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldGT(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyGTE applies the GTE predicate on the "currency" field.
|
||||
func CurrencyGTE(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldGTE(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyLT applies the LT predicate on the "currency" field.
|
||||
func CurrencyLT(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldLT(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyLTE applies the LTE predicate on the "currency" field.
|
||||
func CurrencyLTE(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldLTE(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyContains applies the Contains predicate on the "currency" field.
|
||||
func CurrencyContains(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldContains(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field.
|
||||
func CurrencyHasPrefix(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldHasPrefix(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field.
|
||||
func CurrencyHasSuffix(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldHasSuffix(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyEqualFold applies the EqualFold predicate on the "currency" field.
|
||||
func CurrencyEqualFold(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldEqualFold(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field.
|
||||
func CurrencyContainsFold(v string) predicate.Group {
|
||||
return predicate.Group(sql.FieldContainsFold(FieldCurrency, v))
|
||||
}
|
||||
|
||||
// HasUsers applies the HasEdge predicate on the "users" edge.
|
||||
func HasUsers() predicate.Group {
|
||||
return predicate.Group(func(s *sql.Selector) {
|
||||
|
|
|
@ -63,15 +63,15 @@ func (gc *GroupCreate) SetName(s string) *GroupCreate {
|
|||
}
|
||||
|
||||
// SetCurrency sets the "currency" field.
|
||||
func (gc *GroupCreate) SetCurrency(gr group.Currency) *GroupCreate {
|
||||
gc.mutation.SetCurrency(gr)
|
||||
func (gc *GroupCreate) SetCurrency(s string) *GroupCreate {
|
||||
gc.mutation.SetCurrency(s)
|
||||
return gc
|
||||
}
|
||||
|
||||
// SetNillableCurrency sets the "currency" field if the given value is not nil.
|
||||
func (gc *GroupCreate) SetNillableCurrency(gr *group.Currency) *GroupCreate {
|
||||
if gr != nil {
|
||||
gc.SetCurrency(*gr)
|
||||
func (gc *GroupCreate) SetNillableCurrency(s *string) *GroupCreate {
|
||||
if s != nil {
|
||||
gc.SetCurrency(*s)
|
||||
}
|
||||
return gc
|
||||
}
|
||||
|
@ -267,11 +267,6 @@ func (gc *GroupCreate) check() error {
|
|||
if _, ok := gc.mutation.Currency(); !ok {
|
||||
return &ValidationError{Name: "currency", err: errors.New(`ent: missing required field "Group.currency"`)}
|
||||
}
|
||||
if v, ok := gc.mutation.Currency(); ok {
|
||||
if err := group.CurrencyValidator(v); err != nil {
|
||||
return &ValidationError{Name: "currency", err: fmt.Errorf(`ent: validator failed for field "Group.currency": %w`, err)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -320,7 +315,7 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
|
|||
_node.Name = value
|
||||
}
|
||||
if value, ok := gc.mutation.Currency(); ok {
|
||||
_spec.SetField(group.FieldCurrency, field.TypeEnum, value)
|
||||
_spec.SetField(group.FieldCurrency, field.TypeString, value)
|
||||
_node.Currency = value
|
||||
}
|
||||
if nodes := gc.mutation.UsersIDs(); len(nodes) > 0 {
|
||||
|
|
|
@ -57,15 +57,15 @@ func (gu *GroupUpdate) SetNillableName(s *string) *GroupUpdate {
|
|||
}
|
||||
|
||||
// SetCurrency sets the "currency" field.
|
||||
func (gu *GroupUpdate) SetCurrency(gr group.Currency) *GroupUpdate {
|
||||
gu.mutation.SetCurrency(gr)
|
||||
func (gu *GroupUpdate) SetCurrency(s string) *GroupUpdate {
|
||||
gu.mutation.SetCurrency(s)
|
||||
return gu
|
||||
}
|
||||
|
||||
// SetNillableCurrency sets the "currency" field if the given value is not nil.
|
||||
func (gu *GroupUpdate) SetNillableCurrency(gr *group.Currency) *GroupUpdate {
|
||||
if gr != nil {
|
||||
gu.SetCurrency(*gr)
|
||||
func (gu *GroupUpdate) SetNillableCurrency(s *string) *GroupUpdate {
|
||||
if s != nil {
|
||||
gu.SetCurrency(*s)
|
||||
}
|
||||
return gu
|
||||
}
|
||||
|
@ -370,11 +370,6 @@ func (gu *GroupUpdate) check() error {
|
|||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := gu.mutation.Currency(); ok {
|
||||
if err := group.CurrencyValidator(v); err != nil {
|
||||
return &ValidationError{Name: "currency", err: fmt.Errorf(`ent: validator failed for field "Group.currency": %w`, err)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -397,7 +392,7 @@ func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
|||
_spec.SetField(group.FieldName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := gu.mutation.Currency(); ok {
|
||||
_spec.SetField(group.FieldCurrency, field.TypeEnum, value)
|
||||
_spec.SetField(group.FieldCurrency, field.TypeString, value)
|
||||
}
|
||||
if gu.mutation.UsersCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
|
@ -755,15 +750,15 @@ func (guo *GroupUpdateOne) SetNillableName(s *string) *GroupUpdateOne {
|
|||
}
|
||||
|
||||
// SetCurrency sets the "currency" field.
|
||||
func (guo *GroupUpdateOne) SetCurrency(gr group.Currency) *GroupUpdateOne {
|
||||
guo.mutation.SetCurrency(gr)
|
||||
func (guo *GroupUpdateOne) SetCurrency(s string) *GroupUpdateOne {
|
||||
guo.mutation.SetCurrency(s)
|
||||
return guo
|
||||
}
|
||||
|
||||
// SetNillableCurrency sets the "currency" field if the given value is not nil.
|
||||
func (guo *GroupUpdateOne) SetNillableCurrency(gr *group.Currency) *GroupUpdateOne {
|
||||
if gr != nil {
|
||||
guo.SetCurrency(*gr)
|
||||
func (guo *GroupUpdateOne) SetNillableCurrency(s *string) *GroupUpdateOne {
|
||||
if s != nil {
|
||||
guo.SetCurrency(*s)
|
||||
}
|
||||
return guo
|
||||
}
|
||||
|
@ -1081,11 +1076,6 @@ func (guo *GroupUpdateOne) check() error {
|
|||
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Group.name": %w`, err)}
|
||||
}
|
||||
}
|
||||
if v, ok := guo.mutation.Currency(); ok {
|
||||
if err := group.CurrencyValidator(v); err != nil {
|
||||
return &ValidationError{Name: "currency", err: fmt.Errorf(`ent: validator failed for field "Group.currency": %w`, err)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1125,7 +1115,7 @@ func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error
|
|||
_spec.SetField(group.FieldName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := guo.mutation.Currency(); ok {
|
||||
_spec.SetField(group.FieldCurrency, field.TypeEnum, value)
|
||||
_spec.SetField(group.FieldCurrency, field.TypeString, value)
|
||||
}
|
||||
if guo.mutation.UsersCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
|
|
|
@ -117,7 +117,7 @@ var (
|
|||
{Name: "created_at", Type: field.TypeTime},
|
||||
{Name: "updated_at", Type: field.TypeTime},
|
||||
{Name: "name", Type: field.TypeString, Size: 255},
|
||||
{Name: "currency", Type: field.TypeEnum, Enums: []string{"aed", "aud", "bgn", "brl", "cad", "chf", "czk", "dkk", "eur", "gbp", "hkd", "idr", "inr", "jpy", "krw", "mxn", "nok", "nzd", "pln", "rmb", "ron", "rub", "sar", "sek", "sgd", "thb", "try", "usd", "xag", "xau", "zar"}, Default: "usd"},
|
||||
{Name: "currency", Type: field.TypeString, Default: "usd"},
|
||||
}
|
||||
// GroupsTable holds the schema information for the "groups" table.
|
||||
GroupsTable = &schema.Table{
|
||||
|
|
|
@ -2340,7 +2340,7 @@ type GroupMutation struct {
|
|||
created_at *time.Time
|
||||
updated_at *time.Time
|
||||
name *string
|
||||
currency *group.Currency
|
||||
currency *string
|
||||
clearedFields map[string]struct{}
|
||||
users map[uuid.UUID]struct{}
|
||||
removedusers map[uuid.UUID]struct{}
|
||||
|
@ -2581,12 +2581,12 @@ func (m *GroupMutation) ResetName() {
|
|||
}
|
||||
|
||||
// SetCurrency sets the "currency" field.
|
||||
func (m *GroupMutation) SetCurrency(gr group.Currency) {
|
||||
m.currency = &gr
|
||||
func (m *GroupMutation) SetCurrency(s string) {
|
||||
m.currency = &s
|
||||
}
|
||||
|
||||
// Currency returns the value of the "currency" field in the mutation.
|
||||
func (m *GroupMutation) Currency() (r group.Currency, exists bool) {
|
||||
func (m *GroupMutation) Currency() (r string, exists bool) {
|
||||
v := m.currency
|
||||
if v == nil {
|
||||
return
|
||||
|
@ -2597,7 +2597,7 @@ func (m *GroupMutation) Currency() (r group.Currency, exists bool) {
|
|||
// OldCurrency returns the old "currency" field's value of the Group entity.
|
||||
// If the Group object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *GroupMutation) OldCurrency(ctx context.Context) (v group.Currency, err error) {
|
||||
func (m *GroupMutation) OldCurrency(ctx context.Context) (v string, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldCurrency is only allowed on UpdateOne operations")
|
||||
}
|
||||
|
@ -3105,7 +3105,7 @@ func (m *GroupMutation) SetField(name string, value ent.Value) error {
|
|||
m.SetName(v)
|
||||
return nil
|
||||
case group.FieldCurrency:
|
||||
v, ok := value.(group.Currency)
|
||||
v, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
|
|
|
@ -161,6 +161,10 @@ func init() {
|
|||
return nil
|
||||
}
|
||||
}()
|
||||
// groupDescCurrency is the schema descriptor for currency field.
|
||||
groupDescCurrency := groupFields[1].Descriptor()
|
||||
// group.DefaultCurrency holds the default value on creation for the currency field.
|
||||
group.DefaultCurrency = groupDescCurrency.Default.(string)
|
||||
// groupDescID is the schema descriptor for id field.
|
||||
groupDescID := groupMixinFields0[0].Descriptor()
|
||||
// group.DefaultID holds the default value on creation for the id field.
|
||||
|
|
|
@ -27,41 +27,8 @@ func (Group) Fields() []ent.Field {
|
|||
field.String("name").
|
||||
MaxLen(255).
|
||||
NotEmpty(),
|
||||
field.Enum("currency").
|
||||
Default("usd").
|
||||
Values(
|
||||
"aed",
|
||||
"aud",
|
||||
"bgn",
|
||||
"brl",
|
||||
"cad",
|
||||
"chf",
|
||||
"czk",
|
||||
"dkk",
|
||||
"eur",
|
||||
"gbp",
|
||||
"hkd",
|
||||
"idr",
|
||||
"inr",
|
||||
"jpy",
|
||||
"krw",
|
||||
"mxn",
|
||||
"nok",
|
||||
"nzd",
|
||||
"pln",
|
||||
"rmb",
|
||||
"ron",
|
||||
"rub",
|
||||
"sar",
|
||||
"sek",
|
||||
"sgd",
|
||||
"thb",
|
||||
"try",
|
||||
"usd",
|
||||
"xag",
|
||||
"xau",
|
||||
"zar",
|
||||
),
|
||||
field.String("currency").
|
||||
Default("usd"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@ func (g UserMixin) Fields() []ent.Field {
|
|||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (g UserMixin) Edges() []ent.Edge {
|
||||
|
|
|
@ -28,7 +28,7 @@ func NewGroupRepository(db *ent.Client) *GroupRepository {
|
|||
Name: g.Name,
|
||||
CreatedAt: g.CreatedAt,
|
||||
UpdatedAt: g.UpdatedAt,
|
||||
Currency: strings.ToUpper(g.Currency.String()),
|
||||
Currency: strings.ToUpper(g.Currency),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,11 +265,9 @@ func (r *GroupRepository) GroupCreate(ctx context.Context, name string) (Group,
|
|||
}
|
||||
|
||||
func (r *GroupRepository) GroupUpdate(ctx context.Context, ID uuid.UUID, data GroupUpdate) (Group, error) {
|
||||
currency := group.Currency(strings.ToLower(data.Currency))
|
||||
|
||||
entity, err := r.db.Group.UpdateOneID(ID).
|
||||
SetName(data.Name).
|
||||
SetCurrency(currency).
|
||||
SetCurrency(strings.ToLower(data.Currency)).
|
||||
Save(ctx)
|
||||
|
||||
return r.groupMapper.MapErr(entity, err)
|
||||
|
|
|
@ -68,7 +68,7 @@ type (
|
|||
ItemUpdate struct {
|
||||
ParentID uuid.UUID `json:"parentId" extensions:"x-nullable,x-omitempty"`
|
||||
ID uuid.UUID `json:"id"`
|
||||
AssetID AssetID `json:"assetId"`
|
||||
AssetID AssetID `json:"assetId" swaggertype:"string"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Quantity int `json:"quantity"`
|
||||
|
|
|
@ -17,19 +17,20 @@ const (
|
|||
|
||||
type Config struct {
|
||||
conf.Version
|
||||
Mode string `yaml:"mode" conf:"default:development"` // development or production
|
||||
Web WebConfig `yaml:"web"`
|
||||
Storage Storage `yaml:"storage"`
|
||||
Log LoggerConf `yaml:"logger"`
|
||||
Mailer MailerConf `yaml:"mailer"`
|
||||
Demo bool `yaml:"demo"`
|
||||
Debug DebugConf `yaml:"debug"`
|
||||
Options Options `yaml:"options"`
|
||||
Mode string `yaml:"mode" conf:"default:development"` // development or production
|
||||
Web WebConfig `yaml:"web"`
|
||||
Storage Storage `yaml:"storage"`
|
||||
Log LoggerConf `yaml:"logger"`
|
||||
Mailer MailerConf `yaml:"mailer"`
|
||||
Demo bool `yaml:"demo"`
|
||||
Debug DebugConf `yaml:"debug"`
|
||||
Options Options `yaml:"options"`
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
AllowRegistration bool `yaml:"disable_registration" conf:"default:true"`
|
||||
AutoIncrementAssetID bool `yaml:"auto_increment_asset_id" conf:"default:true"`
|
||||
CurrencyConfig string `yaml:"currencies"`
|
||||
}
|
||||
|
||||
type DebugConf struct {
|
||||
|
|
|
@ -88,7 +88,7 @@ func (fe FieldErrors) Nil() bool {
|
|||
return len(fe) == 0
|
||||
}
|
||||
|
||||
// Error implments the error interface.
|
||||
// Error implements the error interface.
|
||||
func (fe FieldErrors) Error() string {
|
||||
d, err := json.Marshal(fe)
|
||||
if err != nil {
|
||||
|
@ -101,6 +101,10 @@ func NewFieldErrors(errs ...FieldError) FieldErrors {
|
|||
return errs
|
||||
}
|
||||
|
||||
func NewFieldError(field, reason string) FieldError {
|
||||
return FieldError{Field: field, Error: reason}
|
||||
}
|
||||
|
||||
func IsFieldError(err error) bool {
|
||||
v := FieldErrors{}
|
||||
return errors.As(err, &v)
|
||||
|
|
|
@ -143,6 +143,25 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/v1/currency": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Base"
|
||||
],
|
||||
"summary": "Currency",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/currencies.Currency"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/groups": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -403,6 +422,16 @@
|
|||
"description": "location Ids",
|
||||
"name": "locations",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"collectionFormat": "multi",
|
||||
"description": "parent Ids",
|
||||
"name": "parentIds",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
@ -1567,7 +1596,7 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1.ApiSummary"
|
||||
"$ref": "#/definitions/v1.APISummary"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1638,6 +1667,12 @@
|
|||
"schema": {
|
||||
"$ref": "#/definitions/v1.LoginForm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "auth provider",
|
||||
"name": "provider",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
@ -1816,6 +1851,23 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"currencies.Currency": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string"
|
||||
},
|
||||
"local": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"symbol": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo.DocumentOut": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -1991,12 +2043,6 @@
|
|||
"$ref": "#/definitions/repo.ItemAttachment"
|
||||
}
|
||||
},
|
||||
"children": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/repo.ItemSummary"
|
||||
}
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -2174,8 +2220,7 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"assetId": {
|
||||
"type": "string",
|
||||
"example": "0"
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
|
@ -2729,15 +2774,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.ApiSummary": {
|
||||
"v1.APISummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allowRegistration": {
|
||||
|
@ -2766,6 +2803,14 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1.ActionAmountResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"completed": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1.Build": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -61,6 +61,7 @@ volumes:
|
|||
| HBOX_WEB_HOST | | host to run the web server on, if you're using docker do not change this |
|
||||
| HBOX_OPTIONS_ALLOW_REGISTRATION | true | allow users to register themselves |
|
||||
| HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID | true | auto increments the asset_id field for new items |
|
||||
| HBOX_OPTIONS_CURRENCY_CONFIG | | json configuration file containing additional currencie |
|
||||
| HBOX_WEB_MAX_UPLOAD_SIZE | 10 | maximum file upload size supported in MB |
|
||||
| HBOX_WEB_READ_TIMEOUT | 10 | Read timeout of HTTP sever |
|
||||
| HBOX_WEB_WRITE_TIMEOUT | 10 | Write timeout of HTTP server |
|
||||
|
@ -104,6 +105,7 @@ volumes:
|
|||
--debug-port/$HBOX_DEBUG_PORT <string> (default: 4000)
|
||||
--options-allow-registration/$HBOX_OPTIONS_ALLOW_REGISTRATION <bool> (default: true)
|
||||
--options-auto-increment-asset-id/$HBOX_OPTIONS_AUTO_INCREMENT_ASSET_ID <bool> (default: true)
|
||||
--options-currency-config/$HBOX_OPTIONS_CURRENCY_CONFIG <string>
|
||||
--help/-h
|
||||
display this help message
|
||||
```
|
||||
|
|
|
@ -55,4 +55,26 @@ Homebox uses [shoutrrr](https://containrrr.dev/shoutrrr/0.7/) to send notificati
|
|||
|
||||
**Notifications are sent on the day the maintenance is scheduled at or around 8am.**
|
||||
|
||||
As of `v0.9.0` we have limited support for complex scheduling of maintenance events. If you have requests for extended functionality, please open an issue on GitHub or reach out on Discord. We're still gauging the demand for this feature.
|
||||
As of `v0.9.0` we have limited support for complex scheduling of maintenance events. If you have requests for extended functionality, please open an issue on GitHub or reach out on Discord. We're still gauging the demand for this feature.
|
||||
|
||||
|
||||
## Custom Currencies
|
||||
|
||||
:octicons-tag-24: v0.11.0
|
||||
|
||||
Homebox allows you to add additional currencies to your instance by specify a JSON file containing the currencies you want to add.
|
||||
|
||||
**Environment Variable:** `HBOX_OPTIONS_CURRENCY_CONFIG`
|
||||
|
||||
### Example
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"code": "AED",
|
||||
"local": "United Arab Emirates",
|
||||
"symbol": "د.إ",
|
||||
"name": "United Arab Emirates Dirham"
|
||||
},
|
||||
]
|
||||
```
|
||||
|
|
|
@ -2,7 +2,6 @@ import { faker } from "@faker-js/faker";
|
|||
import { describe, test, expect } from "vitest";
|
||||
import { factories } from "../factories";
|
||||
import { sharedUserClient } from "../test-utils";
|
||||
import { currencies } from "~~/lib/data/currency";
|
||||
|
||||
describe("first time user workflow (register, login, join group)", () => {
|
||||
test("user should be able to update group", async () => {
|
||||
|
@ -29,20 +28,6 @@ describe("first time user workflow (register, login, join group)", () => {
|
|||
expect(group.currency).toBe("USD");
|
||||
});
|
||||
|
||||
test("currencies should be in sync with backend", async () => {
|
||||
const { client } = await factories.client.singleUse();
|
||||
|
||||
for (const currency of currencies) {
|
||||
const { response, data: group } = await client.group.update({
|
||||
name: faker.person.firstName(),
|
||||
currency: currency.code,
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(group.currency).toBe(currency.code);
|
||||
}
|
||||
});
|
||||
|
||||
test("user should be able to join create join token and have user signup", async () => {
|
||||
const api = factories.client.public();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { sharedUserClient } from "../test-utils";
|
|||
describe("user should be able to create an item and add an attachment", () => {
|
||||
let increment = 0;
|
||||
/**
|
||||
* useLocatio sets up a location resource for testing, and returns a function
|
||||
* useLocation sets up a location resource for testing, and returns a function
|
||||
* that can be used to delete the location from the backend server.
|
||||
*/
|
||||
async function useLocation(api: UserClient): Promise<[LocationOut, () => Promise<void>]> {
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import { BaseAPI, route } from "../base";
|
||||
import { Group, GroupInvitation, GroupInvitationCreate, GroupUpdate } from "../types/data-contracts";
|
||||
import {
|
||||
CurrenciesCurrency,
|
||||
Group,
|
||||
GroupInvitation,
|
||||
GroupInvitationCreate,
|
||||
GroupUpdate,
|
||||
} from "../types/data-contracts";
|
||||
|
||||
export class GroupApi extends BaseAPI {
|
||||
createInvitation(data: GroupInvitationCreate) {
|
||||
|
@ -21,4 +27,10 @@ export class GroupApi extends BaseAPI {
|
|||
url: route("/groups"),
|
||||
});
|
||||
}
|
||||
|
||||
currencies() {
|
||||
return this.http.get<CurrenciesCurrency[]>({
|
||||
url: route("/currencies"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { BaseAPI, route } from "./base";
|
||||
import { ApiSummary, LoginForm, TokenResponse, UserRegistration } from "./types/data-contracts";
|
||||
import { APISummary, LoginForm, TokenResponse, UserRegistration } from "./types/data-contracts";
|
||||
|
||||
export type StatusResult = {
|
||||
health: boolean;
|
||||
|
@ -10,7 +10,7 @@ export type StatusResult = {
|
|||
|
||||
export class PublicApi extends BaseAPI {
|
||||
public status() {
|
||||
return this.http.get<ApiSummary>({ url: route("/status") });
|
||||
return this.http.get<APISummary>({ url: route("/status") });
|
||||
}
|
||||
|
||||
public login(username: string, password: string, stayLoggedIn = false) {
|
||||
|
|
|
@ -10,6 +10,13 @@
|
|||
* ---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
export interface CurrenciesCurrency {
|
||||
code: string;
|
||||
local: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
export interface DocumentOut {
|
||||
id: string;
|
||||
path: string;
|
||||
|
@ -81,7 +88,6 @@ export interface ItemOut {
|
|||
/** @example "0" */
|
||||
assetId: string;
|
||||
attachments: ItemAttachment[];
|
||||
children: ItemSummary[];
|
||||
createdAt: Date | string;
|
||||
description: string;
|
||||
fields: ItemField[];
|
||||
|
@ -141,7 +147,6 @@ export interface ItemSummary {
|
|||
|
||||
export interface ItemUpdate {
|
||||
archived: boolean;
|
||||
/** @example "0" */
|
||||
assetId: string;
|
||||
description: string;
|
||||
fields: ItemField[];
|
||||
|
@ -364,11 +369,7 @@ export interface UserRegistration {
|
|||
token: string;
|
||||
}
|
||||
|
||||
export interface ActionAmountResult {
|
||||
completed: number;
|
||||
}
|
||||
|
||||
export interface ApiSummary {
|
||||
export interface APISummary {
|
||||
allowRegistration: boolean;
|
||||
build: Build;
|
||||
demo: boolean;
|
||||
|
@ -378,6 +379,10 @@ export interface ApiSummary {
|
|||
versions: string[];
|
||||
}
|
||||
|
||||
export interface ActionAmountResult {
|
||||
completed: number;
|
||||
}
|
||||
|
||||
export interface Build {
|
||||
buildTime: string;
|
||||
commit: string;
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
export type Codes =
|
||||
| "AED"
|
||||
| "AUD"
|
||||
| "BGN"
|
||||
| "BRL"
|
||||
| "CAD"
|
||||
| "CHF"
|
||||
| "CZK"
|
||||
| "DKK"
|
||||
| "EUR"
|
||||
| "GBP"
|
||||
| "HKD"
|
||||
| "IDR"
|
||||
| "INR"
|
||||
| "JPY"
|
||||
| "KRW"
|
||||
| "MXN"
|
||||
| "NOK"
|
||||
| "NZD"
|
||||
| "PLN"
|
||||
| "RMB"
|
||||
| "RUB"
|
||||
| "RON"
|
||||
| "SAR"
|
||||
| "SEK"
|
||||
| "SGD"
|
||||
| "THB"
|
||||
| "TRY"
|
||||
| "USD"
|
||||
| "XAG"
|
||||
| "XAU"
|
||||
| "ZAR";
|
||||
|
||||
export type Currency = {
|
||||
code: Codes;
|
||||
local: string;
|
||||
symbol: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const currencies: Currency[] = [
|
||||
{ code: "AED", local: "United Arab Emirates", symbol: "د.إ", name: "United Arab Emirates Dirham" },
|
||||
{ code: "AUD", local: "Australia", symbol: "A$", name: "Australian Dollar" },
|
||||
{ code: "BGN", local: "bg-BG", symbol: "lv", name: "Bulgarian lev" },
|
||||
{ code: "BRL", local: "Brazil", symbol: "R$", name: "Brazilian Real" },
|
||||
{ code: "CAD", local: "Canada", symbol: "C$", name: "Canadian Dollar" },
|
||||
{ code: "CHF", local: "Switzerland", symbol: "CHF", name: "Swiss Franc" },
|
||||
{ code: "CZK", local: "cs-CZ", symbol: "Kč", name: "Czech Koruna" },
|
||||
{ code: "DKK", local: "da-DK", symbol: "kr", name: "Danish Krone" },
|
||||
{ code: "EUR", local: "Eurozone", symbol: "€", name: "Euro" },
|
||||
{ code: "GBP", local: "United Kingdom", symbol: "£", name: "British Pound Sterling" },
|
||||
{ code: "HKD", local: "Hong Kong", symbol: "HK$", name: "Hong Kong Dollar" },
|
||||
{ code: "IDR", local: "Indonesia", symbol: "Rp", name: "Indonesian Rupiah" },
|
||||
{ code: "INR", local: "India", symbol: "₹", name: "Indian Rupee" },
|
||||
{ code: "JPY", local: "Japan", symbol: "¥", name: "Japanese Yen" },
|
||||
{ code: "KRW", local: "South Korea", symbol: "₩", name: "South Korean Won" },
|
||||
{ code: "MXN", local: "Mexico", symbol: "Mex$", name: "Mexican Peso" },
|
||||
{ code: "NOK", local: "Norway", symbol: "kr", name: "Norwegian Krone" },
|
||||
{ code: "NZD", local: "New Zealand", symbol: "NZ$", name: "New Zealand Dollar" },
|
||||
{ code: "PLN", local: "Poland", symbol: "zł", name: "Polish Zloty" },
|
||||
{ code: "RMB", local: "zh-CN", symbol: "¥", name: "Chinese Yuan" },
|
||||
{ code: "RON", local: "ro-RO", symbol: "lei", name: "Romanian Leu" },
|
||||
{ code: "RUB", local: "Russia", symbol: "₽", name: "Russian Ruble" },
|
||||
{ code: "SAR", local: "Saudi Arabia", symbol: "﷼", name: "Saudi Riyal" },
|
||||
{ code: "SEK", local: "Sweden", symbol: "kr", name: "Swedish Krona" },
|
||||
{ code: "SGD", local: "Singapore", symbol: "S$", name: "Singapore Dollar" },
|
||||
{ code: "THB", local: "Thailand", symbol: "฿", name: "Thai Baht" },
|
||||
{ code: "TRY", local: "Turkey", symbol: "₺", name: "Turkish Lira" },
|
||||
{ code: "USD", local: "United States", symbol: "$", name: "United States Dollar" },
|
||||
{ code: "XAG", local: "Global", symbol: "XAG", name: "Silver Troy Ounce" },
|
||||
{ code: "XAU", local: "Global", symbol: "XAU", name: "Gold Troy Ounce" },
|
||||
{ code: "ZAR", local: "South Africa", symbol: "R", name: "South African Rand" },
|
||||
];
|
|
@ -1,8 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { Detail } from "~~/components/global/DetailsSection/types";
|
||||
import { themes } from "~~/lib/data/themes";
|
||||
import { currencies, Currency } from "~~/lib/data/currency";
|
||||
import { NotifierCreate, NotifierOut } from "~~/lib/api/types/data-contracts";
|
||||
import { CurrenciesCurrency, NotifierCreate, NotifierOut } from "~~/lib/api/types/data-contracts";
|
||||
|
||||
definePageMeta({
|
||||
middleware: ["auth"],
|
||||
|
@ -15,9 +14,23 @@
|
|||
const confirm = useConfirm();
|
||||
const notify = useNotifier();
|
||||
|
||||
// Currency Selection
|
||||
const currency = ref<Currency>(currencies[0]);
|
||||
const currencies = computedAsync(async () => {
|
||||
const resp = await api.group.currencies();
|
||||
if (resp.error) {
|
||||
notify.error("Failed to get currencies");
|
||||
return [];
|
||||
}
|
||||
|
||||
return resp.data;
|
||||
});
|
||||
|
||||
// Currency Selection
|
||||
const currency = ref<CurrenciesCurrency>({
|
||||
code: "USD",
|
||||
name: "United States Dollar",
|
||||
local: "en-US",
|
||||
symbol: "$",
|
||||
});
|
||||
watch(currency, () => {
|
||||
if (group.value) {
|
||||
group.value.currency = currency.value.code;
|
||||
|
@ -45,7 +58,7 @@
|
|||
}
|
||||
|
||||
// @ts-expect-error - typescript is stupid, it should know group.value is not null
|
||||
const found = currencies.find(c => c.code === group.value.currency);
|
||||
const found = currencies.value.find(c => c.code === group.value.currency);
|
||||
if (found) {
|
||||
currency.value = found;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue