feat: vollständige Firmendaten (Adresse, Abrechnung) bei Firmenanlage
Bei Firmenanlage (Registrierung + Betreiber-Firmenanlage) müssen jetzt Adresse (Straße, PLZ, Ort, Land) und Abrechnungsdaten (Rechnungsemail, optional USt-IdNr.) erfasst werden, nicht nur der Firmenname (Migration 0023). USt-IdNr. bewusst optional - Kleinunternehmer nach §19 UStG haben keine. Neue Seite /verwaltung/firma (admin-only) zum Einsehen/ Nachtragen für bestehende Firmen. store.CreateAccount nimmt jetzt ein AccountInput statt nur einen Namen entgegen (Signaturänderung betrifft ~20 Testaufrufe, mechanisch umgestellt). register.html/ betreiber_account_neu.html auf form-card/form-grid umgestellt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,11 +149,15 @@ func (f *fakeStore) newID() string {
|
||||
return fmt.Sprintf("id-%d", f.nextID)
|
||||
}
|
||||
|
||||
func (f *fakeStore) CreateAccount(ctx context.Context, name string) (store.Account, error) {
|
||||
func (f *fakeStore) CreateAccount(ctx context.Context, in store.AccountInput) (store.Account, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
id := f.newID()
|
||||
acc := store.Account{ID: id, Name: name, EinladungToken: "einladung-token-" + id, CreatedAt: time.Now()}
|
||||
acc := store.Account{
|
||||
ID: id, Name: in.Name, EinladungToken: "einladung-token-" + id,
|
||||
Strasse: in.Strasse, PLZ: in.PLZ, Ort: in.Ort, Land: in.Land,
|
||||
UStID: in.UStID, Rechnungsemail: in.Rechnungsemail, CreatedAt: time.Now(),
|
||||
}
|
||||
f.accounts[acc.ID] = acc
|
||||
return acc, nil
|
||||
}
|
||||
@@ -170,6 +174,19 @@ func (f *fakeStore) UpdateAccount(ctx context.Context, id, name string) (store.A
|
||||
return acc, nil
|
||||
}
|
||||
|
||||
func (f *fakeStore) UpdateAccountDetails(ctx context.Context, id string, in store.AccountInput) (store.Account, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
acc, ok := f.accounts[id]
|
||||
if !ok {
|
||||
return store.Account{}, store.ErrNotFound
|
||||
}
|
||||
acc.Name, acc.Strasse, acc.PLZ, acc.Ort, acc.Land, acc.UStID, acc.Rechnungsemail =
|
||||
in.Name, in.Strasse, in.PLZ, in.Ort, in.Land, in.UStID, in.Rechnungsemail
|
||||
f.accounts[id] = acc
|
||||
return acc, nil
|
||||
}
|
||||
|
||||
func (f *fakeStore) GetAccountByEinladungToken(ctx context.Context, token string) (store.Account, error) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
@@ -950,7 +967,7 @@ func newServerWithMailer(t *testing.T, fs *fakeStore) (*web.Server, *mail.FakeMa
|
||||
func seedAccountWithRole(t *testing.T, fs *fakeStore, accountName, email, role string) *http.Cookie {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
acc, err := fs.CreateAccount(ctx, accountName)
|
||||
acc, err := fs.CreateAccount(ctx, store.AccountInput{Name: accountName})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateAccount: %v", err)
|
||||
}
|
||||
@@ -1010,6 +1027,28 @@ func newAuthedTestServer(t *testing.T) (*web.Server, *fakeStore, *http.Cookie) {
|
||||
return s, fs, cookie
|
||||
}
|
||||
|
||||
// firmenPflichtfelder liefert die seit Migration 0023 bei Firmenanlage
|
||||
// (Registrierung und Betreiber-Firmenanlage) verlangten Adress-/
|
||||
// Abrechnungsfelder — gemeinsame Basis für Tests, die nicht speziell
|
||||
// deren Validierung prüfen, sonst müsste jeder Testfall sie einzeln
|
||||
// nachpflegen.
|
||||
func firmenPflichtfelder() url.Values {
|
||||
return url.Values{
|
||||
"strasse": {"Teststraße 1"}, "plz": {"12345"}, "ort": {"Teststadt"},
|
||||
"land": {"Deutschland"}, "rechnungsemail": {"rechnung@example.com"},
|
||||
}
|
||||
}
|
||||
|
||||
// mergeValues kopiert alle Werte aus extra in base (base wird mutiert
|
||||
// und zurückgegeben) — für Tests, die ein Basisformular um wenige
|
||||
// eigene Felder ergänzen wollen.
|
||||
func mergeValues(base url.Values, extra url.Values) url.Values {
|
||||
for k, v := range extra {
|
||||
base[k] = v
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
func postForm(t *testing.T, s *web.Server, cookie *http.Cookie, path string, form url.Values) *httptest.ResponseRecorder {
|
||||
t.Helper()
|
||||
req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(form.Encode()))
|
||||
@@ -1071,10 +1110,10 @@ func TestRegisterThenLoginThenAccessProtectedPage(t *testing.T) {
|
||||
fs := newFakeStore()
|
||||
s := newServer(t, fs)
|
||||
|
||||
regResp := postForm(t, s, nil, "/register", url.Values{
|
||||
regResp := postForm(t, s, nil, "/register", mergeValues(url.Values{
|
||||
"account_name": {"Meine Firma"}, "email": {"neu@example.com"},
|
||||
"password": {"ein-sicheres-passwort"},
|
||||
})
|
||||
}, firmenPflichtfelder()))
|
||||
if regResp.Code != http.StatusSeeOther {
|
||||
t.Fatalf("register status = %d, want 303, body: %s", regResp.Code, regResp.Body.String())
|
||||
}
|
||||
@@ -1101,10 +1140,10 @@ func TestRegisterSeedsStandardGenehmigerRollen(t *testing.T) {
|
||||
fs := newFakeStore()
|
||||
s := newServer(t, fs)
|
||||
|
||||
regResp := postForm(t, s, nil, "/register", url.Values{
|
||||
regResp := postForm(t, s, nil, "/register", mergeValues(url.Values{
|
||||
"account_name": {"Meine Firma"}, "email": {"neu2@example.com"},
|
||||
"password": {"ein-sicheres-passwort"},
|
||||
})
|
||||
}, firmenPflichtfelder()))
|
||||
if regResp.Code != http.StatusSeeOther {
|
||||
t.Fatalf("register status = %d, want 303, body: %s", regResp.Code, regResp.Body.String())
|
||||
}
|
||||
@@ -1147,10 +1186,10 @@ func TestRegisterSeedsStandardGenehmigerRollen(t *testing.T) {
|
||||
func TestRegisterRejectsDuplicateEmail(t *testing.T) {
|
||||
fs := newFakeStore()
|
||||
s := newServer(t, fs)
|
||||
form := url.Values{
|
||||
form := mergeValues(url.Values{
|
||||
"account_name": {"A"}, "email": {"doppelt@example.com"},
|
||||
"password": {"ein-sicheres-passwort"},
|
||||
}
|
||||
}, firmenPflichtfelder())
|
||||
if resp := postForm(t, s, nil, "/register", form); resp.Code != http.StatusSeeOther {
|
||||
t.Fatalf("first register status = %d, want 303", resp.Code)
|
||||
}
|
||||
@@ -1170,7 +1209,7 @@ func TestLoginWithCorrectPassword(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword: %v", err)
|
||||
}
|
||||
acc, err := fs.CreateAccount(context.Background(), "Bestehender Mandant")
|
||||
acc, err := fs.CreateAccount(context.Background(), store.AccountInput{Name: "Bestehender Mandant"})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateAccount: %v", err)
|
||||
}
|
||||
@@ -1194,7 +1233,7 @@ func TestLoginRedirectsBetreiberToPlatformDashboard(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword: %v", err)
|
||||
}
|
||||
acc, err := fs.CreateAccount(context.Background(), "Deklarix Betreiber")
|
||||
acc, err := fs.CreateAccount(context.Background(), store.AccountInput{Name: "Deklarix Betreiber"})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateAccount: %v", err)
|
||||
}
|
||||
@@ -1218,7 +1257,7 @@ func TestLoginRejectsWrongPassword(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("HashPassword: %v", err)
|
||||
}
|
||||
acc, _ := fs.CreateAccount(context.Background(), "X")
|
||||
acc, _ := fs.CreateAccount(context.Background(), store.AccountInput{Name: "X"})
|
||||
if _, err := fs.CreateUser(context.Background(), acc.ID, "x@example.com", hash, "mitarbeiter"); err != nil {
|
||||
t.Fatalf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user