Compare commits
42 Commits
lash/custo
...
alfred/tes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07fd1110ed
|
||
|
|
2f1dbb9147
|
||
|
|
c324e29aea
|
||
|
|
f91056cca2 | ||
| fd84f6ae98 | |||
|
|
1e8d5b1b83
|
||
|
|
628a57ea10
|
||
|
|
a37908323f
|
||
|
|
9c27be3a9d
|
||
|
|
1190c5b6f2 | ||
| b95452ae5f | |||
|
|
14df16098c
|
||
|
|
1cdd5a37ae | ||
|
|
6b3b8ffabe
|
||
|
|
6647416115
|
||
|
|
4155b267ee
|
||
| 0f2d6def23 | |||
|
|
c78081fb84
|
||
|
|
ff3c597158
|
||
|
|
09c5f3a14c
|
||
|
|
5a586eb67a
|
||
|
|
6a945f8f20
|
||
|
|
39f8c86e8b
|
||
|
|
73b501c8aa
|
||
|
|
a6cd4b5fca
|
||
|
|
d47bc6c241 | ||
|
|
dd55906e70
|
||
|
|
6fdf3735d0
|
||
|
|
b492421851
|
||
|
|
128c0162d2
|
||
|
|
ebe94c705f
|
||
|
|
df0c1b3429
|
||
|
|
d66fd894bc
|
||
|
|
28185fc2c5
|
||
|
|
64a87231ca
|
||
|
|
0e480e3d55
|
||
|
|
ffea1a0b96
|
||
|
|
be5bd16616
|
||
|
|
93723616f6
|
||
|
|
c9257ba0d6
|
||
|
|
caabf4f8af
|
||
|
|
f884b19012
|
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.23.4
|
||||
require (
|
||||
git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac
|
||||
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d
|
||||
git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250123142805-2181388f5bf1
|
||||
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69
|
||||
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244
|
||||
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694
|
||||
github.com/alecthomas/assert/v2 v2.2.2
|
||||
|
||||
4
go.sum
4
go.sum
@@ -2,8 +2,8 @@ git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac h1:f/E0ZTclVfMEn
|
||||
git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
|
||||
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d h1:5mzLas+jxTUtusOKx4XvU+n2QvrV/mH17MnJRy46siQ=
|
||||
git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60=
|
||||
git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250123142805-2181388f5bf1 h1:BJHfokTHzrw9QjQ+4s2HmSER0iBPuE7byW5oQC2zLIQ=
|
||||
git.grassecon.net/grassrootseconomics/sarafu-api v0.0.0-20250123142805-2181388f5bf1/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
|
||||
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69 h1:cbBpm9uNJak58MpFpNXJuvgCmz+A8kquXr9har4expg=
|
||||
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8=
|
||||
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244 h1:BXotWSKg04U97sf/xeWJuUTSVgKk2aEK+5BtBrnafXQ=
|
||||
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244/go.mod h1:6B6ByxXOiRY0NR7K02Bf3fEu7z+2c/6q8PFVNjC5G8w=
|
||||
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E=
|
||||
|
||||
@@ -102,14 +102,12 @@ func NewMenuHandlers(appFlags *FlagManager, userdataStore db.Db, accountService
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// WithPersister sets persister instance to the handlers.
|
||||
// func (h *MenuHandlers) WithPersister(pe *persist.Persister) *MenuHandlers {
|
||||
// SetPersister sets persister instance to the handlers.
|
||||
func (h *MenuHandlers) SetPersister(pe *persist.Persister) {
|
||||
if h.pe != nil {
|
||||
panic("persister already set")
|
||||
}
|
||||
h.pe = pe
|
||||
//return h
|
||||
}
|
||||
|
||||
// Init initializes the handler for a new session.
|
||||
@@ -436,6 +434,11 @@ func (h *MenuHandlers) CheckBlockedNumPinMisMatch(ctx context.Context, sym strin
|
||||
logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
|
||||
return res, err
|
||||
}
|
||||
if len(hashedTemporaryPin) == 0 {
|
||||
logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
|
||||
if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) {
|
||||
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
|
||||
} else {
|
||||
@@ -464,6 +467,10 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [
|
||||
logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
|
||||
return res, err
|
||||
}
|
||||
if len(hashedTemporaryPin) == 0 {
|
||||
logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
|
||||
if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) {
|
||||
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
|
||||
@@ -498,13 +505,17 @@ func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []b
|
||||
logg.ErrorCtxf(ctx, "failed to read blockedPhonenumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err)
|
||||
return res, err
|
||||
}
|
||||
hashedTmporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), storedb.DATA_TEMPORARY_VALUE)
|
||||
hashedTemporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), storedb.DATA_TEMPORARY_VALUE)
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to read hashedTmporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
|
||||
return res, err
|
||||
}
|
||||
if len(hashedTemporaryPin) == 0 {
|
||||
logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
|
||||
err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_ACCOUNT_PIN, []byte(hashedTmporaryPin))
|
||||
err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
@@ -637,6 +648,11 @@ func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input []
|
||||
logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err)
|
||||
return res, err
|
||||
}
|
||||
if len(hashedTemporaryPin) == 0 {
|
||||
logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
|
||||
if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) {
|
||||
res.FlagSet = []uint32{flag_valid_pin}
|
||||
res.FlagReset = []uint32{flag_pin_mismatch}
|
||||
@@ -672,6 +688,10 @@ func (h *MenuHandlers) SaveFirstname(ctx context.Context, sym string, input []by
|
||||
firstNameSet := h.st.MatchFlag(flag_firstname_set, true)
|
||||
if allowUpdate {
|
||||
temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(temporaryFirstName) == 0 {
|
||||
logg.ErrorCtxf(ctx, "temporaryFirstName is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
err = store.WriteEntry(ctx, sessionId, storedb.DATA_FIRST_NAME, []byte(temporaryFirstName))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write firstName entry with", "key", storedb.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err)
|
||||
@@ -716,6 +736,10 @@ func (h *MenuHandlers) SaveFamilyname(ctx context.Context, sym string, input []b
|
||||
|
||||
if allowUpdate {
|
||||
temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(temporaryFamilyName) == 0 {
|
||||
logg.ErrorCtxf(ctx, "temporaryFamilyName is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
err = store.WriteEntry(ctx, sessionId, storedb.DATA_FAMILY_NAME, []byte(temporaryFamilyName))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write familyName entry with", "key", storedb.DATA_FAMILY_NAME, "value", temporaryFamilyName, "error", err)
|
||||
@@ -786,6 +810,10 @@ func (h *MenuHandlers) SaveYob(ctx context.Context, sym string, input []byte) (r
|
||||
|
||||
if allowUpdate {
|
||||
temporaryYob, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(temporaryYob) == 0 {
|
||||
logg.ErrorCtxf(ctx, "temporaryYob is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
err = store.WriteEntry(ctx, sessionId, storedb.DATA_YOB, []byte(temporaryYob))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write yob entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", temporaryYob, "error", err)
|
||||
@@ -825,6 +853,10 @@ func (h *MenuHandlers) SaveLocation(ctx context.Context, sym string, input []byt
|
||||
|
||||
if allowUpdate {
|
||||
temporaryLocation, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(temporaryLocation) == 0 {
|
||||
logg.ErrorCtxf(ctx, "temporaryLocation is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
err = store.WriteEntry(ctx, sessionId, storedb.DATA_LOCATION, []byte(temporaryLocation))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write location entry with", "key", storedb.DATA_LOCATION, "value", temporaryLocation, "error", err)
|
||||
@@ -866,6 +898,10 @@ func (h *MenuHandlers) SaveGender(ctx context.Context, sym string, input []byte)
|
||||
|
||||
if allowUpdate {
|
||||
temporaryGender, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(temporaryGender) == 0 {
|
||||
logg.ErrorCtxf(ctx, "temporaryGender is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
err = store.WriteEntry(ctx, sessionId, storedb.DATA_GENDER, []byte(temporaryGender))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write gender entry with", "key", storedb.DATA_GENDER, "value", gender, "error", err)
|
||||
@@ -907,6 +943,10 @@ func (h *MenuHandlers) SaveOfferings(ctx context.Context, sym string, input []by
|
||||
|
||||
if allowUpdate {
|
||||
temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(temporaryOfferings) == 0 {
|
||||
logg.ErrorCtxf(ctx, "temporaryOfferings is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
err = store.WriteEntry(ctx, sessionId, storedb.DATA_OFFERINGS, []byte(temporaryOfferings))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to write offerings entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", offerings, "error", err)
|
||||
@@ -1504,6 +1544,8 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
|
||||
AliasAddress, err = h.accountService.CheckAliasAddress(ctx, recipient)
|
||||
if err == nil {
|
||||
AliasAddressResult = AliasAddress.Address
|
||||
} else {
|
||||
logg.ErrorCtxf(ctx, "failed to resolve alias", "alias", recipient, "error_alias_check", err)
|
||||
}
|
||||
} else {
|
||||
//Perform a search for each search domain,break on first match
|
||||
@@ -1513,6 +1555,8 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input
|
||||
if err == nil {
|
||||
AliasAddressResult = AliasAddress.Address
|
||||
continue
|
||||
} else {
|
||||
logg.ErrorCtxf(ctx, "failed to resolve alias", "alias", recipient, "error_alias_check", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1577,6 +1621,10 @@ func (h *MenuHandlers) InviteValidRecipient(ctx context.Context, sym string, inp
|
||||
l.AddDomain("default")
|
||||
|
||||
recipient, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(recipient) == 0 {
|
||||
logg.ErrorCtxf(ctx, "recipient is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
|
||||
// TODO
|
||||
// send an invitation SMS
|
||||
@@ -1695,6 +1743,10 @@ func (h *MenuHandlers) GetRecipient(ctx context.Context, sym string, input []byt
|
||||
}
|
||||
store := h.userdataStore
|
||||
recipient, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE)
|
||||
if len(recipient) == 0 {
|
||||
logg.ErrorCtxf(ctx, "recipient is empty", "key", storedb.DATA_TEMPORARY_VALUE)
|
||||
return res, fmt.Errorf("Data error encountered")
|
||||
}
|
||||
|
||||
res.Content = string(recipient)
|
||||
|
||||
@@ -2253,6 +2305,7 @@ func (h *MenuHandlers) ViewTransactionStatement(ctx context.Context, sym string,
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// persistInitialLanguageCode receives an initial language code and persists it to the store
|
||||
func (h *MenuHandlers) persistInitialLanguageCode(ctx context.Context, sessionId string, code string) error {
|
||||
store := h.userdataStore
|
||||
_, err := store.ReadEntry(ctx, sessionId, storedb.DATA_INITIAL_LANGUAGE_CODE)
|
||||
@@ -2285,6 +2338,8 @@ func (h *MenuHandlers) persistLanguageCode(ctx context.Context, code string) err
|
||||
return h.persistInitialLanguageCode(ctx, sessionId, code)
|
||||
}
|
||||
|
||||
// constructAccountAlias retrieves and alias based on the first and family name
|
||||
// and writes the result in DATA_ACCOUNT_ALIAS
|
||||
func (h *MenuHandlers) constructAccountAlias(ctx context.Context) error {
|
||||
var alias string
|
||||
store := h.userdataStore
|
||||
@@ -2316,6 +2371,7 @@ func (h *MenuHandlers) constructAccountAlias(ctx context.Context) error {
|
||||
aliasInput := fmt.Sprintf("%s%s", firstName, familyName)
|
||||
aliasResult, err := h.accountService.RequestAlias(ctx, string(pubKey), aliasInput)
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to retrieve alias", "alias", aliasInput, "error_alias_request", err)
|
||||
return fmt.Errorf("Failed to retrieve alias: %s", err.Error())
|
||||
}
|
||||
alias = aliasResult.Alias
|
||||
@@ -2327,3 +2383,22 @@ func (h *MenuHandlers) constructAccountAlias(ctx context.Context) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearTemporaryValue empties the DATA_TEMPORARY_VALUE at the main menu to prevent
|
||||
// previously stored data from being accessed
|
||||
func (h *MenuHandlers) ClearTemporaryValue(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||
var res resource.Result
|
||||
sessionId, ok := ctx.Value("SessionId").(string)
|
||||
if !ok {
|
||||
return res, fmt.Errorf("missing session")
|
||||
}
|
||||
userStore := h.userdataStore
|
||||
|
||||
// clear the temporary value at the start
|
||||
err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(""))
|
||||
if err != nil {
|
||||
logg.ErrorCtxf(ctx, "failed to clear DATA_TEMPORARY_VALUE entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", "empty", "error", err)
|
||||
return res, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,11 @@ import (
|
||||
|
||||
"git.defalsify.org/vise.git/db"
|
||||
"git.defalsify.org/vise.git/engine"
|
||||
"git.defalsify.org/vise.git/logging"
|
||||
"git.defalsify.org/vise.git/persist"
|
||||
"git.defalsify.org/vise.git/resource"
|
||||
"git.defalsify.org/vise.git/logging"
|
||||
|
||||
"git.grassecon.net/grassrootseconomics/sarafu-api/remote"
|
||||
// sarafu_engine "git.grassecon.net/grassrootseconomics/sarafu-vise/engine"
|
||||
"git.grassecon.net/grassrootseconomics/sarafu-vise/handlers/application"
|
||||
)
|
||||
|
||||
@@ -67,7 +66,6 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//appHandlers = appHandlers.WithPersister(ls.Pe)
|
||||
appHandlers.SetPersister(ls.Pe)
|
||||
ls.DbRs.AddLocalFunc("check_blocked_status", appHandlers.CheckBlockedStatus)
|
||||
ls.DbRs.AddLocalFunc("set_language", appHandlers.SetLanguage)
|
||||
@@ -125,6 +123,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService)
|
||||
ls.DbRs.AddLocalFunc("update_all_profile_items", appHandlers.UpdateAllProfileItems)
|
||||
ls.DbRs.AddLocalFunc("set_back", appHandlers.SetBack)
|
||||
ls.DbRs.AddLocalFunc("show_blocked_account", appHandlers.ShowBlockedAccount)
|
||||
ls.DbRs.AddLocalFunc("clear_temporary_value", appHandlers.ClearTemporaryValue)
|
||||
ls.first = appHandlers.Init
|
||||
|
||||
return appHandlers, nil
|
||||
|
||||
42
profile/profile_test.go
Normal file
42
profile/profile_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/assert/v2"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInsertOrShift(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
profile Profile
|
||||
index int
|
||||
value string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "Insert within range",
|
||||
profile: Profile{ProfileItems: []string{"A", "B", "C"}, Max: 5},
|
||||
index: 1,
|
||||
value: "X",
|
||||
expected: []string{"A", "X"},
|
||||
},
|
||||
{
|
||||
name: "Insert beyond range",
|
||||
profile: Profile{ProfileItems: []string{"A"}, Max: 5},
|
||||
index: 3,
|
||||
value: "Y",
|
||||
expected: []string{"A", "0", "0", "Y"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := tt.profile
|
||||
p.InsertOrShift(tt.index, tt.value)
|
||||
require.NotNil(t, p.ProfileItems)
|
||||
assert.Equal(t, tt.expected, p.ProfileItems)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
LOAD clear_temporary_value 2
|
||||
RELOAD clear_temporary_value
|
||||
LOAD set_default_voucher 8
|
||||
RELOAD set_default_voucher
|
||||
LOAD check_vouchers 10
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
LOAD confirm_pin_change 7
|
||||
LOAD set_back 6
|
||||
LOAD authorize_account 5
|
||||
LOAD reset_allow_update 4
|
||||
|
||||
Reference in New Issue
Block a user