Add the tax relation with very rough form and handler
This commit is contained in:
parent
0b8107748c
commit
666935b54c
|
@ -15,4 +15,9 @@ values (1, 1)
|
|||
, (1, 2)
|
||||
;
|
||||
|
||||
insert into tax (company_id, name, rate)
|
||||
values (1, 'Retenció 15 %', -0.15)
|
||||
, (1, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
-- Deploy numerus:tax to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: company
|
||||
-- requires: tax_rate
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table tax (
|
||||
tax_id serial primary key,
|
||||
company_id integer not null references company,
|
||||
name text not null,
|
||||
rate tax_rate not null
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table tax to invoicer;
|
||||
grant select, insert, update, delete on table tax to admin;
|
||||
|
||||
grant usage on sequence tax_tax_id_seq to invoicer;
|
||||
grant usage on sequence tax_tax_id_seq to admin;
|
||||
|
||||
alter table tax enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on tax
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = tax.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,14 @@
|
|||
-- Deploy numerus:tax_rate to pg
|
||||
-- requires: schema_numerus
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create domain tax_rate as numeric
|
||||
check (value > -1 and value < 1);
|
||||
|
||||
comment on domain country_code is
|
||||
'A tax rate in the range (-1, 1)';
|
||||
|
||||
commit;
|
|
@ -5,6 +5,7 @@ import (
|
|||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -75,6 +76,12 @@ type CountryOption struct {
|
|||
Name string
|
||||
}
|
||||
|
||||
type Tax struct {
|
||||
Id int
|
||||
Name string
|
||||
Rate int
|
||||
}
|
||||
|
||||
type TaxDetailsPage struct {
|
||||
Title string
|
||||
BusinessName string
|
||||
|
@ -91,6 +98,7 @@ type TaxDetailsPage struct {
|
|||
Countries []CountryOption
|
||||
CurrencyCode string
|
||||
Currencies []CurrencyOption
|
||||
Taxes []Tax
|
||||
}
|
||||
|
||||
func CompanyTaxDetailsHandler() http.Handler {
|
||||
|
@ -125,6 +133,7 @@ func CompanyTaxDetailsHandler() http.Handler {
|
|||
}
|
||||
page.Countries = mustGetCountryOptions(r.Context(), conn, locale)
|
||||
page.Currencies = mustGetCurrencyOptions(r.Context(), conn)
|
||||
page.Taxes = mustGetTaxes(r.Context(), conn, company)
|
||||
mustRenderAppTemplate(w, r, "tax-details.html", page)
|
||||
})
|
||||
}
|
||||
|
@ -182,3 +191,46 @@ func mustGetCurrencyOptions(ctx context.Context, conn *Conn) []CurrencyOption {
|
|||
|
||||
return currencies
|
||||
}
|
||||
|
||||
func mustGetTaxes(ctx context.Context, conn *Conn, company *Company) []Tax {
|
||||
rows, err := conn.Query(ctx, "select tax_id, name, (rate * 100)::integer from tax where company_id = $1 order by rate, name", company.Id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var taxes []Tax
|
||||
for rows.Next() {
|
||||
var tax Tax
|
||||
err = rows.Scan(&tax.Id, &tax.Name, &tax.Rate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
taxes = append(taxes, tax)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
panic(rows.Err())
|
||||
}
|
||||
|
||||
return taxes
|
||||
}
|
||||
|
||||
func CompanyTaxHandler() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
param := r.URL.Path
|
||||
if idx := strings.LastIndexByte(param, '/'); idx >= 0 {
|
||||
param = param[idx+1:]
|
||||
}
|
||||
conn := getConn(r)
|
||||
company := mustGetCompany(r)
|
||||
if taxId, err := strconv.Atoi(param); err == nil {
|
||||
conn.MustExec(r.Context(), "delete from tax where tax_id = $1", taxId)
|
||||
} else {
|
||||
r.ParseForm()
|
||||
name := r.FormValue("name")
|
||||
rate, _ := strconv.Atoi(r.FormValue("rate"))
|
||||
conn.MustExec(r.Context(), "insert into tax (company_id, name, rate) values ($1, $2, $3 / 100::decimal)", company.Id, name, rate)
|
||||
}
|
||||
http.Redirect(w, r, "/company/"+company.Slug+"/tax-details", http.StatusSeeOther)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
func NewRouter(db *Db) http.Handler {
|
||||
companyRouter := http.NewServeMux()
|
||||
companyRouter.Handle("/tax-details", CompanyTaxDetailsHandler())
|
||||
companyRouter.Handle("/tax/", CompanyTaxHandler())
|
||||
companyRouter.Handle("/tax", CompanyTaxHandler())
|
||||
companyRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
mustRenderAppTemplate(w, r, "dashboard.html", nil)
|
||||
})
|
||||
|
|
40
po/ca.po
40
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-28 12:22+0100\n"
|
||||
"POT-Creation-Date: 2023-01-28 14:14+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"
|
||||
|
@ -77,12 +77,12 @@ msgctxt "language option"
|
|||
msgid "Automatic"
|
||||
msgstr "Automàtic"
|
||||
|
||||
#: web/template/profile.html:42 web/template/tax-details.html:66
|
||||
#: web/template/profile.html:42 web/template/tax-details.html:127
|
||||
msgctxt "action"
|
||||
msgid "Save changes"
|
||||
msgstr "Desa canvis"
|
||||
|
||||
#: web/template/tax-details.html:3 pkg/company.go:100
|
||||
#: web/template/tax-details.html:3 pkg/company.go:108
|
||||
msgctxt "title"
|
||||
msgid "Tax Details"
|
||||
msgstr "Configuració fiscal"
|
||||
|
@ -142,6 +142,40 @@ msgctxt "input"
|
|||
msgid "Currency"
|
||||
msgstr "Moneda"
|
||||
|
||||
#: web/template/tax-details.html:74
|
||||
msgctxt "title"
|
||||
msgid "Tax Name"
|
||||
msgstr "Nom import"
|
||||
|
||||
#: web/template/tax-details.html:75
|
||||
msgctxt "title"
|
||||
msgid "Rate (%)"
|
||||
msgstr "Percentatge"
|
||||
|
||||
#: web/template/tax-details.html:96
|
||||
msgid "No taxes added yet."
|
||||
msgstr "No hi ha cap impost."
|
||||
|
||||
#: web/template/tax-details.html:102
|
||||
msgctxt "title"
|
||||
msgid "New Line"
|
||||
msgstr "Nova línia"
|
||||
|
||||
#: web/template/tax-details.html:106
|
||||
msgctxt "input"
|
||||
msgid "Tax name"
|
||||
msgstr "Nom impost"
|
||||
|
||||
#: web/template/tax-details.html:112
|
||||
msgctxt "input"
|
||||
msgid "Rate (%)"
|
||||
msgstr "Percentatge"
|
||||
|
||||
#: web/template/tax-details.html:119
|
||||
msgctxt "action"
|
||||
msgid "Add new tax"
|
||||
msgstr "Afegeix nou impost"
|
||||
|
||||
#: web/template/app.html:20
|
||||
msgctxt "menu"
|
||||
msgid "Account"
|
||||
|
|
40
po/es.po
40
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-28 12:22+0100\n"
|
||||
"POT-Creation-Date: 2023-01-28 14:14+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"
|
||||
|
@ -77,12 +77,12 @@ msgctxt "language option"
|
|||
msgid "Automatic"
|
||||
msgstr "Automático"
|
||||
|
||||
#: web/template/profile.html:42 web/template/tax-details.html:66
|
||||
#: web/template/profile.html:42 web/template/tax-details.html:127
|
||||
msgctxt "action"
|
||||
msgid "Save changes"
|
||||
msgstr "Guardar cambios"
|
||||
|
||||
#: web/template/tax-details.html:3 pkg/company.go:100
|
||||
#: web/template/tax-details.html:3 pkg/company.go:108
|
||||
msgctxt "title"
|
||||
msgid "Tax Details"
|
||||
msgstr "Configuración fiscal"
|
||||
|
@ -142,6 +142,40 @@ msgctxt "input"
|
|||
msgid "Currency"
|
||||
msgstr "Moneda"
|
||||
|
||||
#: web/template/tax-details.html:74
|
||||
msgctxt "title"
|
||||
msgid "Tax Name"
|
||||
msgstr "Nombre impuesto"
|
||||
|
||||
#: web/template/tax-details.html:75
|
||||
msgctxt "title"
|
||||
msgid "Rate (%)"
|
||||
msgstr "Porcentage"
|
||||
|
||||
#: web/template/tax-details.html:96
|
||||
msgid "No taxes added yet."
|
||||
msgstr "No hay impuestos."
|
||||
|
||||
#: web/template/tax-details.html:102
|
||||
msgctxt "title"
|
||||
msgid "New Line"
|
||||
msgstr "Nueva línea"
|
||||
|
||||
#: web/template/tax-details.html:106
|
||||
msgctxt "input"
|
||||
msgid "Tax name"
|
||||
msgstr "Nombre impuesto"
|
||||
|
||||
#: web/template/tax-details.html:112
|
||||
msgctxt "input"
|
||||
msgid "Rate (%)"
|
||||
msgstr "Porcentage"
|
||||
|
||||
#: web/template/tax-details.html:119
|
||||
msgctxt "action"
|
||||
msgid "Add new tax"
|
||||
msgstr "Añadir nuevo impuesto"
|
||||
|
||||
#: web/template/app.html:20
|
||||
msgctxt "menu"
|
||||
msgid "Account"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
-- Revert numerus:tax from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop policy company_policy on numerus.tax;
|
||||
drop table if exists numerus.tax;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:tax_rate from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop domain if exists numerus.tax_rate;
|
||||
|
||||
commit;
|
|
@ -36,3 +36,5 @@ country_i18n [schema_numerus country_code language country] 2023-01-27T19:20:43Z
|
|||
available_countries [schema_numerus country] 2023-01-27T18:49:28Z jordi fita mas <jordi@tandem.blog> # Add the list of available countries
|
||||
company [schema_numerus extension_vat email extension_pg_libphonenumber extension_uri currency_code currency country_code country] 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
|
||||
tax_rate [schema_numerus] 2023-01-28T11:33:39Z jordi fita mas <jordi@tandem.blog> # Add domain for tax rates
|
||||
tax [schema_numerus company tax_rate] 2023-01-28T11:45:47Z jordi fita mas <jordi@tandem.blog> # Add relation for taxes
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
-- Test tax
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(35);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('tax');
|
||||
select has_pk('tax' );
|
||||
select table_privs_are('tax', 'guest', array []::text[]);
|
||||
select table_privs_are('tax', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('tax', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('tax', 'authenticator', array []::text[]);
|
||||
|
||||
SELECT has_sequence('tax_tax_id_seq');
|
||||
SELECT sequence_privs_are('tax_tax_id_seq', 'guest', array[]::text[]);
|
||||
SELECT sequence_privs_are('tax_tax_id_seq', 'invoicer', array['USAGE']);
|
||||
SELECT sequence_privs_are('tax_tax_id_seq', 'admin', array['USAGE']);
|
||||
SELECT sequence_privs_are('tax_tax_id_seq', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('tax', 'tax_id');
|
||||
select col_is_pk('tax', 'tax_id');
|
||||
select col_type_is('tax', 'tax_id', 'integer');
|
||||
select col_not_null('tax', 'tax_id');
|
||||
select col_has_default('tax', 'tax_id');
|
||||
select col_default_is('tax', 'tax_id', 'nextval(''tax_tax_id_seq''::regclass)');
|
||||
|
||||
select has_column('tax', 'company_id');
|
||||
select col_is_fk('tax', 'company_id');
|
||||
select fk_ok('tax', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('tax', 'company_id', 'integer');
|
||||
select col_not_null('tax', 'company_id');
|
||||
select col_hasnt_default('tax', 'company_id');
|
||||
|
||||
select has_column('tax', 'name');
|
||||
select col_type_is('tax', 'name', 'text');
|
||||
select col_not_null('tax', 'name');
|
||||
select col_hasnt_default('tax', 'name');
|
||||
|
||||
select has_column('tax', 'rate');
|
||||
select col_type_is('tax', 'rate', 'tax_rate');
|
||||
select col_not_null('tax', 'rate');
|
||||
select col_hasnt_default('tax', 'rate');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate tax cascade;
|
||||
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_code, currency_code)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into tax (company_id, name, rate)
|
||||
values (2, 'VAT 21 %', 0.21)
|
||||
, (2, 'IRPF -15 %', -0.15)
|
||||
, (4, 'VAT 21 %', 0.21)
|
||||
, (4, 'VAT 10 %', 0.10)
|
||||
, (4, 'VAT 5 %', 0.05)
|
||||
, (4, 'VAT 4 %', 0.04)
|
||||
, (4, 'VAT 0 %', 0.00)
|
||||
;
|
||||
|
||||
prepare tax_data as
|
||||
select company_id, name, rate
|
||||
from tax
|
||||
order by company_id, rate;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('tax_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'tax_data',
|
||||
$$ values ( 2, 'IRPF -15 %', -0.15::tax_rate )
|
||||
, ( 2, 'VAT 21 %', 0.21::tax_rate )
|
||||
$$,
|
||||
'Should only list taxes of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'tax_data',
|
||||
$$ values (4, 'VAT 0 %', 0.00::tax_rate)
|
||||
, (4, 'VAT 4 %', 0.04::tax_rate)
|
||||
, (4, 'VAT 5 %', 0.05::tax_rate)
|
||||
, (4, 'VAT 10 %', 0.10::tax_rate)
|
||||
, (4, 'VAT 21 %', 0.21::tax_rate)
|
||||
$$,
|
||||
'Should only list taxes of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'tax_data',
|
||||
'42501', 'permission denied for table tax',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
-- Test tax_rate
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(7);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_domain('tax_rate');
|
||||
select domain_type_is('tax_rate', 'numeric');
|
||||
|
||||
select lives_ok($$ select 0.21::tax_rate $$, 'Should be able to cast valid positive decimals to tax rate');
|
||||
select lives_ok($$ select -0.15::tax_rate $$, 'Should be able to cast valid negative decimals to tax rate');
|
||||
select lives_ok($$ select 0::tax_rate $$, 'Should be able to cast valid zero to tax rate');
|
||||
|
||||
select throws_ok(
|
||||
$$ SELECT 1::tax_rate $$,
|
||||
23514, null,
|
||||
'Should reject 100 % tax rate'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ SELECT -1::tax_rate $$,
|
||||
23514, null,
|
||||
'Should reject -100 % tax rate'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,15 @@
|
|||
-- Verify numerus:tax on pg
|
||||
|
||||
begin;
|
||||
|
||||
select tax_id
|
||||
, company_id
|
||||
, name
|
||||
, rate
|
||||
from numerus.tax
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.tax'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.tax'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:tax_rate on pg
|
||||
|
||||
begin;
|
||||
|
||||
select pg_catalog.has_type_privilege('numerus.tax_rate', 'usage');
|
||||
|
||||
rollback;
|
|
@ -205,6 +205,15 @@ input[type="submit"]:active, button:active {
|
|||
text-color: var(--numerus--color--white);
|
||||
}
|
||||
|
||||
button.icon {
|
||||
min-width: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.web {
|
||||
margin: 8.5rem 4rem;
|
||||
background-color: var(--numerus--header--background-color);
|
||||
|
@ -248,7 +257,13 @@ main {
|
|||
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"]
|
||||
, input[type="tel"]
|
||||
, input[type="url"]
|
||||
, input[type="number"]
|
||||
, select {
|
||||
background-color: var(--numerus--background-color);
|
||||
border: 1px solid var(--numerus--color--black);
|
||||
border-radius: 0;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{ define "content" }}
|
||||
<section class="dialog-content">
|
||||
<h2>{{(pgettext "Tax Details" "title")}}</h2>
|
||||
<form method="POST">
|
||||
<form id="details" 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>
|
||||
|
@ -49,7 +49,7 @@
|
|||
<option value="{{ .Code }}" {{ if eq .Code $.CountryCode }}selected="selected"{{ end }}>{{ .Name }}</option>
|
||||
{{- end }}
|
||||
</select>
|
||||
<label for="country">{{( pgettext "Country" "input" )}}<label>
|
||||
<label for="country">{{( pgettext "Country" "input" )}}</label>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
|
@ -61,10 +61,70 @@
|
|||
{{- end }}
|
||||
</select>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<form id="newtax" method="POST" action="{{ companyURI "/tax" }}">
|
||||
</form>
|
||||
|
||||
<fieldset>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="50%"></th>
|
||||
<th>{{( pgettext "Tax Name" "title" )}}</th>
|
||||
<th>{{( pgettext "Rate (%)" "title" )}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ with .Taxes }}
|
||||
{{- range $tax := . }}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>{{ .Name }}</td>
|
||||
<td>{{ .Rate }}</td>
|
||||
<td>
|
||||
<form method="POST" action="{{ companyURI "/tax"}}/{{ .Id }}">
|
||||
<input type="hidden" name="_method" name="DELETE"/>
|
||||
<button class="icon" aria-label="{{( gettext "Delete tax" )}}" type="submit"><i class="ri-delete-back-2-line"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
{{ else }}
|
||||
<tr>
|
||||
<td colspan="4">{{( gettext "No taxes added yet." )}}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th scope="row">{{( pgettext "New Line" "title")}}</th>
|
||||
<td>
|
||||
<div class="input">
|
||||
<input form="newtax" type="text" name="name" id="tax_name" required="required" placeholder="{{( pgettext "Tax name" "input" )}}">
|
||||
<label for="tax_name">{{( pgettext "Tax name" "input" )}}</label>
|
||||
</div>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<div class="input">
|
||||
<input form="newtax" type="number" name="rate" id="tax_rate" min="-99" max="99" required="required" placeholder="{{( pgettext "Rate (%)" "input" )}}">
|
||||
<label form="newtax" for="tax_rate">{{( pgettext "Rate (%)" "input" )}}</label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"></td>
|
||||
<td colspan="2">
|
||||
<button form="newtax" type="submit">{{( pgettext "Add new tax" "action" )}}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
|
||||
<button form="details" type="submit">{{( pgettext "Save changes" "action" )}}</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</section>
|
||||
{{- end }}
|
||||
|
|
Loading…
Reference in New Issue