Compare commits
No commits in common. "627841d4ddca191eb777ff6be2851665d9a814a9" and "d9c93b8797adb31e454652d5a2de87ff6bf06ec5" have entirely different histories.
627841d4dd
...
d9c93b8797
|
@ -2,17 +2,9 @@ begin;
|
|||
|
||||
set search_path to auth, numerus, public;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, role)
|
||||
values (1, 'demo@numerus', 'Demo User', 'demo', 'invoicer')
|
||||
, (2, 'admin@numerus', 'Demo Admin', 'admin', 'admin')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country, currency_code)
|
||||
values (1, 'Juli Verd', 'ES40404040D', 'Pesebre', parse_packed_phone_number('972 50 60 70', 'ES'), 'info@numerus.cat', 'https://numerus.cat/', 'C/ de l’Hort', 'Castelló d’Empúries', 'Alt Empordà', '17486', 'Espanya', 'EUR');
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (1, 1)
|
||||
, (1, 2)
|
||||
insert into auth."user" (email, name, password, role)
|
||||
values ('demo@numerus', 'Demo User', 'demo', 'invoicer')
|
||||
, ('admin@numerus', 'Demo Admin', 'admin', 'admin')
|
||||
;
|
||||
|
||||
commit;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
-- Deploy numerus:available_currencies to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: currency
|
||||
|
||||
begin;
|
||||
|
||||
insert into numerus.currency(currency_code, currency_symbol)
|
||||
values ('EUR', '€')
|
||||
, ('USD', '$')
|
||||
;
|
||||
|
||||
commit;
|
|
@ -1,36 +0,0 @@
|
|||
-- Deploy numerus:company to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: extension_vat
|
||||
-- requires: email
|
||||
-- requires: extension_pg_libphonenumber
|
||||
-- requires: extension_uri
|
||||
-- requires: currency_code
|
||||
-- requires: currency
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus,public;
|
||||
|
||||
create table company (
|
||||
company_id serial primary key,
|
||||
slug uuid not null unique default gen_random_uuid(),
|
||||
business_name text not null,
|
||||
vatin vatin not null,
|
||||
trade_name text not null,
|
||||
phone packed_phone_number not null,
|
||||
email email not null,
|
||||
web uri not null,
|
||||
address text not null,
|
||||
city text not null,
|
||||
province text not null,
|
||||
postal_code text not null,
|
||||
country text not null,
|
||||
currency_code currency_code not null references currency,
|
||||
created_at timestamptz not null default current_timestamp
|
||||
);
|
||||
|
||||
grant select on table company to invoicer;
|
||||
grant select on table company to admin;
|
||||
|
||||
|
||||
commit;
|
|
@ -1,38 +0,0 @@
|
|||
-- Deploy numerus:company_user to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: user
|
||||
-- requires: company
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
create table company_user (
|
||||
company_id integer not null references company,
|
||||
user_id integer not null references "user",
|
||||
primary key (company_id, user_id)
|
||||
);
|
||||
|
||||
grant select on table company_user to invoicer;
|
||||
grant select on table company_user to admin;
|
||||
|
||||
|
||||
alter table company enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on company
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = company.company_id
|
||||
)
|
||||
);
|
||||
|
||||
-- TODO:
|
||||
-- I think we can not do the same for company_user because it would be
|
||||
-- an infinite loop, but in this case i think it is fine because we can
|
||||
-- only see ids, nothing more.
|
||||
|
||||
commit;
|
|
@ -1,18 +0,0 @@
|
|||
-- Deploy numerus:currency to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: currency_code
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table currency (
|
||||
currency_code currency_code not null primary key,
|
||||
currency_symbol text not null,
|
||||
decimal_digits integer not null default 2
|
||||
);
|
||||
|
||||
grant select on table currency to invoicer;
|
||||
grant select on table currency to admin;
|
||||
|
||||
commit;
|
|
@ -1,14 +0,0 @@
|
|||
-- Deploy numerus:currency_code to pg
|
||||
-- requires: schema_numerus
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create domain currency_code as text
|
||||
check (value ~ '^[A-Z]{3}$');
|
||||
|
||||
comment on domain currency_code is
|
||||
'A correctly formated, but not necessarily valid, ISO 4217 currency code';
|
||||
|
||||
commit;
|
|
@ -1,8 +0,0 @@
|
|||
-- Deploy numerus:extension_pg_libphonenumber to pg
|
||||
-- requires: schema_public
|
||||
|
||||
begin;
|
||||
|
||||
create extension if not exists pg_libphonenumber;
|
||||
|
||||
commit;
|
|
@ -1,8 +0,0 @@
|
|||
-- Deploy numerus:extension_uri to pg
|
||||
-- requires: schema_public
|
||||
|
||||
begin;
|
||||
|
||||
create extension if not exists uri;
|
||||
|
||||
commit;
|
|
@ -1,8 +0,0 @@
|
|||
-- Deploy numerus:extension_vat to pg
|
||||
-- requires: schema_public
|
||||
|
||||
begin;
|
||||
|
||||
create extension if not exists vat;
|
||||
|
||||
commit;
|
105
pkg/company.go
105
pkg/company.go
|
@ -1,105 +0,0 @@
|
|||
package pkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ContextCompanyKey = "numerus-company"
|
||||
)
|
||||
|
||||
type Company struct {
|
||||
Id int
|
||||
Slug string
|
||||
}
|
||||
|
||||
func CompanyHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
slug := r.URL.Path
|
||||
if idx := strings.IndexByte(slug, '/'); idx >= 0 {
|
||||
slug = slug[:idx]
|
||||
}
|
||||
|
||||
conn := getConn(r)
|
||||
company := &Company{
|
||||
Slug: slug,
|
||||
}
|
||||
err := conn.QueryRow(r.Context(), "select company_id from company where slug = $1", slug).Scan(&company.Id)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), ContextCompanyKey, company)
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
// Same as StripPrefix
|
||||
p := strings.TrimPrefix(r.URL.Path, slug)
|
||||
rp := strings.TrimPrefix(r.URL.RawPath, slug)
|
||||
if len(p) < len(r.URL.Path) && (r.URL.RawPath == "" || len(rp) < len(r.URL.RawPath)) {
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
r2.URL = new(url.URL)
|
||||
*r2.URL = *r.URL
|
||||
if p == "" {
|
||||
r2.URL.Path = "/"
|
||||
} else {
|
||||
r2.URL.Path = p
|
||||
}
|
||||
r2.URL.RawPath = rp
|
||||
next.ServeHTTP(w, r2)
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func getCompany(r *http.Request) *Company {
|
||||
company := r.Context().Value(ContextCompanyKey)
|
||||
if company == nil {
|
||||
return nil
|
||||
}
|
||||
return company.(*Company)
|
||||
}
|
||||
|
||||
type TaxDetailsPage struct {
|
||||
Title string
|
||||
BusinessName string
|
||||
VATIN string
|
||||
TradeName string
|
||||
Phone string
|
||||
Email string
|
||||
Web string
|
||||
Address string
|
||||
City string
|
||||
Province string
|
||||
PostalCode string
|
||||
Country string
|
||||
}
|
||||
|
||||
func CompanyTaxDetailsHandler() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
locale := getLocale(r)
|
||||
page := &TaxDetailsPage{
|
||||
Title: pgettext("title", "Tax Details", locale),
|
||||
}
|
||||
company := mustGetCompany(r)
|
||||
conn := getConn(r)
|
||||
err := conn.QueryRow(r.Context(), "select business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country from company where company_id = $1", company.Id).Scan(&page.BusinessName, &page.VATIN, &page.TradeName, &page.Phone, &page.Email, &page.Web, &page.Address, &page.City, &page.Province, &page.PostalCode, &page.Country);
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mustRenderAppTemplate(w, r, "tax-details.html", page)
|
||||
});
|
||||
}
|
||||
|
||||
func mustGetCompany(r *http.Request) *Company {
|
||||
company := getCompany(r)
|
||||
if company == nil {
|
||||
panic(errors.New("company: required but not found"))
|
||||
}
|
||||
return company;
|
||||
}
|
21
pkg/login.go
21
pkg/login.go
|
@ -61,9 +61,13 @@ func LoginHandler() http.Handler {
|
|||
|
||||
func LogoutHandler() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn := getConn(r)
|
||||
conn.MustExec(r.Context(), "select logout()")
|
||||
http.SetCookie(w, createSessionCookie("", -24*time.Hour))
|
||||
user := getUser(r)
|
||||
if user.LoggedIn {
|
||||
conn := getConn(r)
|
||||
conn.MustExec(r.Context(), "select logout()")
|
||||
http.SetCookie(w, createSessionCookie("", -24*time.Hour))
|
||||
}
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -126,14 +130,3 @@ func getUser(r *http.Request) *AppUser {
|
|||
func getConn(r *http.Request) *Conn {
|
||||
return r.Context().Value(ContextConnKey).(*Conn)
|
||||
}
|
||||
|
||||
func Authenticated(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user := getUser(r)
|
||||
if user.LoggedIn {
|
||||
next.ServeHTTP(w, r);
|
||||
} else {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ type ProfilePage struct {
|
|||
func ProfileHandler() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user := getUser(r)
|
||||
if !user.LoggedIn {
|
||||
http.Redirect(w, r, "/login", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
conn := getConn(r)
|
||||
locale := getLocale(r)
|
||||
page := ProfilePage{
|
||||
|
|
|
@ -5,28 +5,15 @@ import (
|
|||
)
|
||||
|
||||
func NewRouter(db *Db) http.Handler {
|
||||
companyRouter := http.NewServeMux()
|
||||
companyRouter.Handle("/tax-details", CompanyTaxDetailsHandler())
|
||||
companyRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
mustRenderAppTemplate(w, r, "dashboard.html", nil)
|
||||
})
|
||||
|
||||
router := http.NewServeMux()
|
||||
router.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
|
||||
router.Handle("/login", LoginHandler())
|
||||
router.Handle("/logout", Authenticated(LogoutHandler()))
|
||||
router.Handle("/profile", Authenticated(ProfileHandler()))
|
||||
router.Handle("/company/", Authenticated(http.StripPrefix("/company/", CompanyHandler(companyRouter))))
|
||||
router.Handle("/logout", LogoutHandler())
|
||||
router.Handle("/profile", ProfileHandler())
|
||||
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
user := getUser(r)
|
||||
if user.LoggedIn {
|
||||
conn := getConn(r)
|
||||
var slug string
|
||||
err := conn.QueryRow(r.Context(), "select slug::text from company order by company_id limit 1").Scan(&slug)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
http.Redirect(w, r, "/company/"+slug, http.StatusFound)
|
||||
mustRenderAppTemplate(w, r, "dashboard.html", nil)
|
||||
} else {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
}
|
||||
|
|
|
@ -12,17 +12,10 @@ func templateFile (name string) string {
|
|||
|
||||
func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename string, data interface{}) {
|
||||
locale := getLocale(r)
|
||||
company := getCompany(r)
|
||||
t := template.New(filename)
|
||||
t.Funcs(template.FuncMap{
|
||||
"gettext": locale.Get,
|
||||
"pgettext": locale.GetC,
|
||||
"companyURI": func(uri string) string {
|
||||
if company == nil {
|
||||
return uri;
|
||||
}
|
||||
return "/company/" + company.Slug + uri;
|
||||
},
|
||||
})
|
||||
if _, err := t.ParseFiles(templateFile(filename), templateFile(layout)); err != nil {
|
||||
panic(err)
|
||||
|
|
86
po/ca.po
86
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-01-24 21:37+0100\n"
|
||||
"POT-Creation-Date: 2023-01-23 18:50+0100\n"
|
||||
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||
|
@ -26,13 +26,12 @@ msgstr "Entrada"
|
|||
msgid "Invalid user or password"
|
||||
msgstr "Nom d’usuari o contrasenya incorrectes"
|
||||
|
||||
#: web/template/login.html:13 web/template/profile.html:15
|
||||
#: web/template/tax-details.html:23
|
||||
#: web/template/login.html:13 web/template/profile.html:14
|
||||
msgctxt "input"
|
||||
msgid "Email"
|
||||
msgstr "Correu-e"
|
||||
|
||||
#: web/template/login.html:18 web/template/profile.html:23
|
||||
#: web/template/login.html:18 web/template/profile.html:22
|
||||
msgctxt "input"
|
||||
msgid "Password"
|
||||
msgstr "Contrasenya"
|
||||
|
@ -42,112 +41,51 @@ msgctxt "action"
|
|||
msgid "Login"
|
||||
msgstr "Entra"
|
||||
|
||||
#: web/template/profile.html:3 pkg/profile.go:29
|
||||
#: web/template/profile.html:2 pkg/profile.go:33
|
||||
msgctxt "title"
|
||||
msgid "User Settings"
|
||||
msgstr "Configuració usuari"
|
||||
|
||||
#: web/template/profile.html:6
|
||||
#: web/template/profile.html:5
|
||||
msgctxt "title"
|
||||
msgid "User Access Data"
|
||||
msgstr "Dades accés usuari"
|
||||
|
||||
#: web/template/profile.html:10
|
||||
#: web/template/profile.html:9
|
||||
msgctxt "input"
|
||||
msgid "User name"
|
||||
msgstr "Nom d’usuari"
|
||||
|
||||
#: web/template/profile.html:19
|
||||
#: web/template/profile.html:18
|
||||
msgctxt "title"
|
||||
msgid "Password Change"
|
||||
msgstr "Canvi contrasenya"
|
||||
|
||||
#: web/template/profile.html:28
|
||||
#: web/template/profile.html:27
|
||||
msgctxt "input"
|
||||
msgid "Password Confirmation"
|
||||
msgstr "Confirmació contrasenya"
|
||||
|
||||
#: web/template/profile.html:33
|
||||
#: web/template/profile.html:31
|
||||
msgctxt "input"
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
|
||||
#: web/template/profile.html:36
|
||||
#: web/template/profile.html:33
|
||||
msgctxt "language option"
|
||||
msgid "Automatic"
|
||||
msgstr "Automàtic"
|
||||
|
||||
#: web/template/profile.html:42
|
||||
#: web/template/profile.html:38
|
||||
msgctxt "action"
|
||||
msgid "Save changes"
|
||||
msgstr "Desa canvis"
|
||||
|
||||
#: web/template/tax-details.html:3 pkg/company.go:87
|
||||
msgctxt "title"
|
||||
msgid "Tax Details"
|
||||
msgstr "Configuració fiscal"
|
||||
|
||||
#: web/template/tax-details.html:7
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nom i cognom"
|
||||
|
||||
#: web/template/tax-details.html:11
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: web/template/tax-details.html:15
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nom comercial"
|
||||
|
||||
#: web/template/tax-details.html:19
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Telèfon"
|
||||
|
||||
#: web/template/tax-details.html:27
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: web/template/tax-details.html:31
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Adreça"
|
||||
|
||||
#: web/template/tax-details.html:35
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Població"
|
||||
|
||||
#: web/template/tax-details.html:39
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Província"
|
||||
|
||||
#: web/template/tax-details.html:43
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Codi postal"
|
||||
|
||||
#: web/template/tax-details.html:47
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: web/template/app.html:20
|
||||
msgctxt "menu"
|
||||
msgid "Account"
|
||||
msgstr "Compte"
|
||||
|
||||
#: web/template/app.html:26
|
||||
msgctxt "menu"
|
||||
msgid "Tax Details"
|
||||
msgstr "Configuració fiscal"
|
||||
|
||||
#: web/template/app.html:33
|
||||
#: web/template/app.html:27
|
||||
msgctxt "action"
|
||||
msgid "Logout"
|
||||
msgstr "Surt"
|
||||
|
|
86
po/es.po
86
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-01-24 21:37+0100\n"
|
||||
"POT-Creation-Date: 2023-01-23 18:50+0100\n"
|
||||
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
|
@ -26,13 +26,12 @@ msgstr "Entrada"
|
|||
msgid "Invalid user or password"
|
||||
msgstr "Nombre de usuario o contraseña inválido"
|
||||
|
||||
#: web/template/login.html:13 web/template/profile.html:15
|
||||
#: web/template/tax-details.html:23
|
||||
#: web/template/login.html:13 web/template/profile.html:14
|
||||
msgctxt "input"
|
||||
msgid "Email"
|
||||
msgstr "Correo-e"
|
||||
|
||||
#: web/template/login.html:18 web/template/profile.html:23
|
||||
#: web/template/login.html:18 web/template/profile.html:22
|
||||
msgctxt "input"
|
||||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
@ -42,112 +41,51 @@ msgctxt "action"
|
|||
msgid "Login"
|
||||
msgstr "Entrar"
|
||||
|
||||
#: web/template/profile.html:3 pkg/profile.go:29
|
||||
#: web/template/profile.html:2 pkg/profile.go:33
|
||||
msgctxt "title"
|
||||
msgid "User Settings"
|
||||
msgstr "Configuración usuario"
|
||||
|
||||
#: web/template/profile.html:6
|
||||
#: web/template/profile.html:5
|
||||
msgctxt "title"
|
||||
msgid "User Access Data"
|
||||
msgstr "Datos acceso usuario"
|
||||
|
||||
#: web/template/profile.html:10
|
||||
#: web/template/profile.html:9
|
||||
msgctxt "input"
|
||||
msgid "User name"
|
||||
msgstr "Nombre de usuario"
|
||||
|
||||
#: web/template/profile.html:19
|
||||
#: web/template/profile.html:18
|
||||
msgctxt "title"
|
||||
msgid "Password Change"
|
||||
msgstr "Cambio de contraseña"
|
||||
|
||||
#: web/template/profile.html:28
|
||||
#: web/template/profile.html:27
|
||||
msgctxt "input"
|
||||
msgid "Password Confirmation"
|
||||
msgstr "Confirmación contrasenya"
|
||||
|
||||
#: web/template/profile.html:33
|
||||
#: web/template/profile.html:31
|
||||
msgctxt "input"
|
||||
msgid "Language"
|
||||
msgstr "Idioma"
|
||||
|
||||
#: web/template/profile.html:36
|
||||
#: web/template/profile.html:33
|
||||
msgctxt "language option"
|
||||
msgid "Automatic"
|
||||
msgstr "Automático"
|
||||
|
||||
#: web/template/profile.html:42
|
||||
#: web/template/profile.html:38
|
||||
msgctxt "action"
|
||||
msgid "Save changes"
|
||||
msgstr "Guardar cambios"
|
||||
|
||||
#: web/template/tax-details.html:3 pkg/company.go:87
|
||||
msgctxt "title"
|
||||
msgid "Tax Details"
|
||||
msgstr "Configuración fiscal"
|
||||
|
||||
#: web/template/tax-details.html:7
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nombre y apellidos"
|
||||
|
||||
#: web/template/tax-details.html:11
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: web/template/tax-details.html:15
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nombre comercial"
|
||||
|
||||
#: web/template/tax-details.html:19
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Teléfono"
|
||||
|
||||
#: web/template/tax-details.html:27
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: web/template/tax-details.html:31
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Dirección"
|
||||
|
||||
#: web/template/tax-details.html:35
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Población"
|
||||
|
||||
#: web/template/tax-details.html:39
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Provincia"
|
||||
|
||||
#: web/template/tax-details.html:43
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Código postal"
|
||||
|
||||
#: web/template/tax-details.html:47
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: web/template/app.html:20
|
||||
msgctxt "menu"
|
||||
msgid "Account"
|
||||
msgstr "Cuenta"
|
||||
|
||||
#: web/template/app.html:26
|
||||
msgctxt "menu"
|
||||
msgid "Tax Details"
|
||||
msgstr "Configuración fiscal"
|
||||
|
||||
#: web/template/app.html:33
|
||||
#: web/template/app.html:27
|
||||
msgctxt "action"
|
||||
msgid "Logout"
|
||||
msgstr "Salir"
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
-- Revert numerus:available_currencies from pg
|
||||
|
||||
begin;
|
||||
|
||||
delete from numerus.currency;
|
||||
|
||||
commit;
|
|
@ -1,7 +0,0 @@
|
|||
-- Revert numerus:company from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table numerus.company;
|
||||
|
||||
commit;
|
|
@ -1,9 +0,0 @@
|
|||
-- Revert numerus:company_user from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop policy if exists company_policy on numerus.company;
|
||||
drop policy if exists company_policy on numerus.company_user;
|
||||
drop table if exists numerus.company_user;
|
||||
|
||||
commit;
|
|
@ -1,7 +0,0 @@
|
|||
-- Revert numerus:currency from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table numerus.currency;
|
||||
|
||||
commit;
|
|
@ -1,7 +0,0 @@
|
|||
-- Revert numerus:currency_code from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop domain if exists numerus.currency_code;
|
||||
|
||||
commit;
|
|
@ -1,7 +0,0 @@
|
|||
-- Revert numerus:extension_pg_libphonenumber from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop extension if exists pg_libphonenumber;
|
||||
|
||||
commit;
|
|
@ -1,7 +0,0 @@
|
|||
-- Revert numerus:extension_uri from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop extension if exists uri;
|
||||
|
||||
commit;
|
|
@ -1,7 +0,0 @@
|
|||
-- Revert numerus:extension_vat from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop extension if exists vat;
|
||||
|
||||
commit;
|
12
sqitch.plan
12
sqitch.plan
|
@ -9,11 +9,11 @@ schema_numerus [roles] 2023-01-12T22:57:22Z jordi fita mas <jordi@tandem.blog> #
|
|||
extension_citext [schema_public] 2023-01-12T23:03:33Z jordi fita mas <jordi@tandem.blog> # Add citext extension
|
||||
email [schema_numerus extension_citext] 2023-01-12T23:09:59Z jordi fita mas <jordi@tandem.blog> # Add email domain
|
||||
language [schema_numerus] 2023-01-21T20:55:49Z jordi fita mas <jordi@tandem.blog> # Add relation of available languages
|
||||
user [roles schema_auth email language] 2023-01-12T23:44:03Z jordi fita mas <jordi@tandem.blog> # Create user relation
|
||||
user [roles schema_auth email language] 2023-01-12T23:44:03Z jordi fita mas <jordi@tandem.blog> # Create user table
|
||||
ensure_role_exists [schema_auth user] 2023-01-12T23:57:59Z jordi fita mas <jordi@tandem.blog> # Add trigger to ensure the user’s role exists
|
||||
extension_pgcrypto [schema_auth] 2023-01-13T00:11:50Z jordi fita mas <jordi@tandem.blog> # Add pgcrypto extension
|
||||
encrypt_password [schema_auth user extension_pgcrypto] 2023-01-13T00:14:30Z jordi fita mas <jordi@tandem.blog> # Add trigger to encrypt user’s password
|
||||
login_attempt [schema_auth] 2023-01-17T14:05:49Z jordi fita mas <jordi@tandem.blog> # Add relation to log login attempts
|
||||
login_attempt [schema_auth] 2023-01-17T14:05:49Z jordi fita mas <jordi@tandem.blog> # Add table to log login attempts
|
||||
login [roles schema_numerus schema_auth extension_pgcrypto email user login_attempt] 2023-01-13T00:32:32Z jordi fita mas <jordi@tandem.blog> # Add function to login
|
||||
current_user_cookie [schema_numerus] 2023-01-21T20:16:28Z jordi fita mas <jordi@tandem.blog> # Add function to get the cookie of the current Numerus’ user
|
||||
current_user_email [schema_numerus] 2023-01-23T19:11:53Z jordi fita mas <jordi@tandem.blog> # Add function to get the email of the current Numerus’ user
|
||||
|
@ -24,11 +24,3 @@ set_cookie [schema_public check_cookie] 2023-01-19T11:00:22Z jordi fita mas <jor
|
|||
available_languages [schema_numerus language] 2023-01-21T21:11:08Z jordi fita mas <jordi@tandem.blog> # Add the initial available languages
|
||||
user_profile [schema_numerus user current_user_email current_user_cookie] 2023-01-21T23:18:20Z jordi fita mas <jordi@tandem.blog> # Add view for user profile
|
||||
change_password [schema_numerus user] 2023-01-23T20:22:45Z jordi fita mas <jordi@tandem.blog> # Add function to change the current user’s password
|
||||
extension_vat [schema_public] 2023-01-24T10:28:17Z jordi fita mas <jordi@tandem.blog> # Add vat extension
|
||||
extension_pg_libphonenumber [schema_public] 2023-01-24T13:50:14Z jordi fita mas <jordi@tandem.blog> # Add extension for phone numbers
|
||||
extension_uri [schema_public] 2023-01-24T14:29:29Z jordi fita mas <jordi@tandem.blog> # Add extension for URIs
|
||||
currency_code [schema_numerus] 2023-01-24T14:36:04Z jordi fita mas <jordi@tandem.blog> # Add the domain for currency code in ISO 4217
|
||||
currency [schema_numerus currency_code] 2023-01-24T14:45:26Z jordi fita mas <jordi@tandem.blog> # Add the relation for currencies
|
||||
available_currencies [schema_numerus currency] 2023-01-24T14:54:18Z jordi fita mas <jordi@tandem.blog> # Add the initial list of available currencies
|
||||
company [schema_numerus extension_vat email extension_pg_libphonenumber extension_uri currency_code currency] 2023-01-24T15:03:15Z jordi fita mas <jordi@tandem.blog> # Add the relation for companies
|
||||
company_user [schema_numerus user company] 2023-01-24T17:50:06Z jordi fita mas <jordi@tandem.blog> # Add the relation of companies and their users
|
||||
|
|
164
test/company.sql
164
test/company.sql
|
@ -1,164 +0,0 @@
|
|||
-- Test company
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(77);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('company');
|
||||
select has_pk('company');
|
||||
select table_privs_are('company', 'guest', array []::text[]);
|
||||
select table_privs_are('company', 'invoicer', array ['SELECT']);
|
||||
select table_privs_are('company', 'admin', array ['SELECT']);
|
||||
select table_privs_are('company', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('company', 'company_id');
|
||||
select col_is_pk('company', 'company_id');
|
||||
select col_type_is('company', 'company_id', 'integer');
|
||||
select col_not_null('company', 'company_id');
|
||||
select col_has_default('company', 'company_id');
|
||||
select col_default_is('company', 'company_id', 'nextval(''company_company_id_seq''::regclass)');
|
||||
|
||||
select has_column('company', 'slug');
|
||||
select col_is_unique('company', 'slug');
|
||||
select col_type_is('company', 'slug', 'uuid');
|
||||
select col_not_null('company', 'slug');
|
||||
select col_has_default('company', 'slug');
|
||||
select col_default_is('company', 'slug', 'gen_random_uuid()');
|
||||
|
||||
select has_column('company', 'business_name');
|
||||
select col_type_is('company', 'business_name', 'text');
|
||||
select col_not_null('company', 'business_name');
|
||||
select col_hasnt_default('company', 'business_name');
|
||||
|
||||
select has_column('company', 'vatin');
|
||||
select col_type_is('company', 'vatin', 'vatin');
|
||||
select col_not_null('company', 'vatin');
|
||||
select col_hasnt_default('company', 'vatin');
|
||||
|
||||
select has_column('company', 'trade_name');
|
||||
select col_type_is('company', 'trade_name', 'text');
|
||||
select col_not_null('company', 'trade_name');
|
||||
select col_hasnt_default('company', 'trade_name');
|
||||
|
||||
select has_column('company', 'phone');
|
||||
select col_type_is('company', 'phone', 'packed_phone_number');
|
||||
select col_not_null('company', 'phone');
|
||||
select col_hasnt_default('company', 'phone');
|
||||
|
||||
select has_column('company', 'email');
|
||||
select col_type_is('company', 'email', 'email');
|
||||
select col_not_null('company', 'email');
|
||||
select col_hasnt_default('company', 'email');
|
||||
|
||||
select has_column('company', 'web');
|
||||
select col_type_is('company', 'web', 'uri');
|
||||
select col_not_null('company', 'web');
|
||||
select col_hasnt_default('company', 'web');
|
||||
|
||||
select has_column('company', 'address');
|
||||
select col_type_is('company', 'address', 'text');
|
||||
select col_not_null('company', 'address');
|
||||
select col_hasnt_default('company', 'address');
|
||||
|
||||
select has_column('company', 'city');
|
||||
select col_type_is('company', 'city', 'text');
|
||||
select col_not_null('company', 'city');
|
||||
select col_hasnt_default('company', 'city');
|
||||
|
||||
select has_column('company', 'province');
|
||||
select col_type_is('company', 'province', 'text');
|
||||
select col_not_null('company', 'province');
|
||||
select col_hasnt_default('company', 'province');
|
||||
|
||||
select has_column('company', 'postal_code');
|
||||
select col_type_is('company', 'postal_code', 'text');
|
||||
select col_not_null('company', 'postal_code');
|
||||
select col_hasnt_default('company', 'postal_code');
|
||||
|
||||
select has_column('company', 'country');
|
||||
select col_type_is('company', 'country', 'text');
|
||||
select col_not_null('company', 'country');
|
||||
select col_hasnt_default('company', 'country');
|
||||
|
||||
select has_column('company', 'currency_code');
|
||||
select col_is_fk('company', 'currency_code');
|
||||
select fk_ok('company', 'currency_code', 'currency', 'currency_code');
|
||||
select col_type_is('company', 'currency_code', 'currency_code');
|
||||
select col_not_null('company', 'currency_code');
|
||||
select col_hasnt_default('company', 'currency_code');
|
||||
|
||||
select has_column('company', 'created_at');
|
||||
select col_type_is('company', 'created_at', 'timestamp with time zone');
|
||||
select col_not_null('company', 'created_at');
|
||||
select col_has_default('company', 'created_at');
|
||||
select col_default_is('company', 'created_at', current_timestamp);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country, currency_code)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'EUR')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'USD')
|
||||
, (6, 'Company 6', 'XX345', '', '777-777-777', 'c@c', '', '', '', '', '', '', 'USD')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (2, 5)
|
||||
, (4, 1)
|
||||
, (6, 5)
|
||||
;
|
||||
|
||||
prepare company_data as
|
||||
select company_id, business_name
|
||||
from company
|
||||
order by company_id;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('company_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select results_eq(
|
||||
'company_data',
|
||||
$$ values ( 2, 'Company 2' )
|
||||
, ( 4, 'Company 4' )
|
||||
$$,
|
||||
'Should only list companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select results_eq(
|
||||
'company_data',
|
||||
$$ values ( 2, 'Company 2' )
|
||||
, ( 6, 'Company 6' )
|
||||
$$,
|
||||
'Should only list companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'company_data',
|
||||
'42501', 'permission denied for table company',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select finish();
|
||||
rollback;
|
|
@ -1,39 +0,0 @@
|
|||
-- Test company_user
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(19);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('company_user');
|
||||
select has_pk('company_user' );
|
||||
select col_is_pk('company_user', array['company_id', 'user_id']);
|
||||
select table_privs_are('company_user', 'guest', array []::text[]);
|
||||
select table_privs_are('company_user', 'invoicer', array ['SELECT']);
|
||||
select table_privs_are('company_user', 'admin', array ['SELECT']);
|
||||
select table_privs_are('company_user', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('company_user', 'company_id');
|
||||
select col_is_fk('company_user', 'company_id');
|
||||
select fk_ok('company_user', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('company_user', 'company_id', 'integer');
|
||||
select col_not_null('company_user', 'company_id');
|
||||
select col_hasnt_default('company_user', 'company_id');
|
||||
|
||||
select has_column('company_user', 'user_id');
|
||||
select col_is_fk('company_user', 'user_id');
|
||||
select fk_ok('company_user', 'user_id', 'user', 'user_id');
|
||||
select col_type_is('company_user', 'user_id', 'integer');
|
||||
select col_not_null('company_user', 'user_id');
|
||||
select col_hasnt_default('company_user', 'user_id');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
-- Test currency
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(20);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_table('currency');
|
||||
select has_pk('currency');
|
||||
select table_privs_are('currency', 'guest', array []::text[]);
|
||||
select table_privs_are('currency', 'invoicer', array ['SELECT']);
|
||||
select table_privs_are('currency', 'admin', array ['SELECT']);
|
||||
select table_privs_are('currency', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('currency', 'currency_code');
|
||||
select col_is_pk('currency', 'currency_code');
|
||||
select col_type_is('currency', 'currency_code', 'currency_code');
|
||||
select col_not_null('currency', 'currency_code');
|
||||
select col_hasnt_default('currency', 'currency_code');
|
||||
|
||||
select has_column('currency', 'currency_symbol');
|
||||
select col_type_is('currency', 'currency_symbol', 'text');
|
||||
select col_not_null('currency', 'currency_symbol');
|
||||
select col_hasnt_default('currency', 'currency_symbol');
|
||||
|
||||
select has_column('currency', 'decimal_digits');
|
||||
select col_type_is('currency', 'decimal_digits', 'integer');
|
||||
select col_not_null('currency', 'decimal_digits');
|
||||
select col_has_default('currency', 'decimal_digits');
|
||||
select col_default_is('currency', 'decimal_digits', 2);
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate currency cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
select finish();
|
||||
rollback;
|
|
@ -1,38 +0,0 @@
|
|||
-- Test currency_code
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(6);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_domain('currency_code');
|
||||
select domain_type_is('currency_code', 'text');
|
||||
|
||||
select lives_ok($$ select 'EUR'::currency_code $$, 'Should be able to cast valid text to currency code');
|
||||
|
||||
select throws_ok(
|
||||
$$ SELECT '123'::currency_code $$,
|
||||
23514, null,
|
||||
'Should reject numeric text'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ SELECT 'eur'::currency_code $$,
|
||||
23514, null,
|
||||
'Should reject lowecase text'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ SELECT 'EURO'::currency_code $$,
|
||||
23514, null,
|
||||
'Should reject text longer than three letters'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -9,12 +9,9 @@ select plan(1);
|
|||
|
||||
select extensions_are(array[
|
||||
'citext'
|
||||
, 'pg_libphonenumber'
|
||||
, 'pgtap'
|
||||
, 'pgcrypto'
|
||||
, 'plpgsql'
|
||||
, 'uri'
|
||||
, 'vat'
|
||||
]);
|
||||
|
||||
select *
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
-- Verify numerus:available_currencies on pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
select 1 / count(*)
|
||||
from currency
|
||||
where currency_code = 'EUR'
|
||||
and currency_symbol = '€'
|
||||
and decimal_digits = 2
|
||||
;
|
||||
|
||||
select 1 / count(*)
|
||||
from currency
|
||||
where currency_code = 'USD'
|
||||
and currency_symbol = '$'
|
||||
and decimal_digits = 2
|
||||
;
|
||||
|
||||
rollback;
|
|
@ -1,23 +0,0 @@
|
|||
-- Verify numerus:company on pg
|
||||
|
||||
begin;
|
||||
|
||||
select company_id
|
||||
, slug
|
||||
, business_name
|
||||
, vatin
|
||||
, trade_name
|
||||
, phone
|
||||
, email
|
||||
, web
|
||||
, address
|
||||
, city
|
||||
, province
|
||||
, postal_code
|
||||
, country
|
||||
, currency_code
|
||||
, created_at
|
||||
from numerus.company
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -1,10 +0,0 @@
|
|||
-- Verify numerus:company_user on pg
|
||||
|
||||
begin;
|
||||
|
||||
select company_id
|
||||
, user_id
|
||||
from numerus.company_user
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -1,11 +0,0 @@
|
|||
-- Verify numerus:currency on pg
|
||||
|
||||
begin;
|
||||
|
||||
select currency_code
|
||||
, currency_symbol
|
||||
, decimal_digits
|
||||
from numerus.currency
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -1,7 +0,0 @@
|
|||
-- Verify numerus:currency_code on pg
|
||||
|
||||
begin;
|
||||
|
||||
select pg_catalog.has_type_privilege('numerus.currency_code', 'usage');
|
||||
|
||||
rollback;
|
|
@ -1,7 +0,0 @@
|
|||
-- Verify numerus:extension_pg_libphonenumber on pg
|
||||
|
||||
begin;
|
||||
|
||||
select 1/count(*) from pg_extension where extname = 'pg_libphonenumber';
|
||||
|
||||
rollback;
|
|
@ -1,7 +0,0 @@
|
|||
-- Verify numerus:extension_uri on pg
|
||||
|
||||
begin;
|
||||
|
||||
select 1/count(*) from pg_extension where extname = 'uri';
|
||||
|
||||
rollback;
|
|
@ -1,7 +0,0 @@
|
|||
-- Verify numerus:extension_vat on pg
|
||||
|
||||
begin;
|
||||
|
||||
select 1/count(*) from pg_extension where extname = 'vat';
|
||||
|
||||
rollback;
|
|
@ -245,10 +245,9 @@ main {
|
|||
.input {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
input[type="text"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"], select {
|
||||
input[type="text"], input[type="password"], input[type="email"], select {
|
||||
background-color: var(--numerus--background-color);
|
||||
border: 1px solid var(--numerus--color--black);
|
||||
border-radius: 0;
|
||||
|
@ -256,10 +255,6 @@ input[type="text"], input[type="password"], input[type="email"], input[type="tel
|
|||
min-width: 30rem;
|
||||
}
|
||||
|
||||
input.width-2x {
|
||||
min-width: 60.95rem;
|
||||
}
|
||||
|
||||
.input input::placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
@ -314,7 +309,7 @@ fieldset {
|
|||
}
|
||||
|
||||
.full-width legend {
|
||||
margin-bottom: -1rem;
|
||||
margin-bottom: initial;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,13 +17,7 @@
|
|||
<li role="presentation">
|
||||
<a role="menuitem" href="/profile">
|
||||
<i class="ri-account-circle-line"></i>
|
||||
{{( pgettext "Account" "menu" )}}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a role="menuitem" href="{{ companyURI "/tax-details" }}">
|
||||
<i class="ri-vip-diamond-line"></i>
|
||||
{{( pgettext "Tax Details" "menu" )}}
|
||||
{{( gettext "Account" )}}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
{{ define "content" }}
|
||||
<section class="dialog-content">
|
||||
<h2>{{(pgettext "Tax Details" "title")}}</h2>
|
||||
<form method="POST">
|
||||
<div class="input">
|
||||
<input type="text" name="business_name" id="business_name" required="required" value="{{ .BusinessName }}" placeholder="{{( pgettext "Business name" "input" )}}">
|
||||
<label for="business_name">{{( pgettext "Business name" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="vatin" id="vatin" required="required" value="{{ .VATIN }}" placeholder="{{( pgettext "VAT number" "input" )}}">
|
||||
<label for="vatin">{{( pgettext "VAT number" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="trade_name" id="trade_name" value="{{ .TradeName }}" placeholder="{{( pgettext "Trade name" "input" )}}">
|
||||
<label for="trade_name">{{( pgettext "Trade name" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="tel" name="phone" id="phone" required="required" value="{{ .Phone }}" placeholder="{{( pgettext "Phone" "input" )}}">
|
||||
<label for="phone">{{( pgettext "Phone" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="email" name="email" id="email" required="required" value="{{ .Email }}" placeholder="{{( pgettext "Email" "input" )}}">
|
||||
<label for="email">{{( pgettext "Email" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="url" name="web" id="web" value="{{ .Web }}" placeholder="{{( pgettext "Web" "input" )}}">
|
||||
<label for="web">{{( pgettext "Web" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="address" id="address" class="width-2x" required="required" value="{{ .Address }}" placeholder="{{( pgettext "Address" "input" )}}">
|
||||
<label for="address">{{( pgettext "Address" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="city" id="city" required="required" value="{{ .City }}" placeholder="{{( pgettext "City" "input" )}}">
|
||||
<label for="city">{{( pgettext "City" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="province" id="province" required="required" value="{{ .City }}" placeholder="{{( pgettext "Province" "input" )}}">
|
||||
<label for="province">{{( pgettext "Province" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="postal_code" id="postal_code" required="required" value="{{ .PostalCode }}" placeholder="{{( pgettext "Postal code" "input" )}}">
|
||||
<label for="postal_code">{{( pgettext "Postal code" "input" )}}</label>
|
||||
</div>
|
||||
<div class="input">
|
||||
<input type="text" name="country" id="country" required="required" value="{{ .Country }}" placeholder="{{( pgettext "Country" "input" )}}">
|
||||
<label for="country">{{( pgettext "Country" "input" )}}</label>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{{- end }}
|
Loading…
Reference in New Issue