Split contact relation into tax_details, phone, web, and email
We need to have contacts with just a name: we need to assign freelancer’s quote as expense linked the government, but of course we do not have a phone or email for that “contact”, much less a VATIN or other tax details. It is also interesting for other expenses-only contacts to not have to input all tax details, as we may not need to invoice then, thus are useless for us, but sometimes it might be interesting to have them, “just in case”. Of course, i did not want to make nullable any of the tax details required to generate an invoice, otherwise we could allow illegal invoices. Therefore, that data had to go in a different relation, and invoice’s foreign key update to point to that relation, not just customer, or we would again be able to create invalid invoices. We replaced the contact’s trade name with just name, because we do not need _three_ names for a contact, but we _do_ need two: the one we use to refer to them and the business name for tax purposes. The new contact_phone, contact_web, and contact_email relations could be simply a nullable field, but i did not see the point, since there are not that many instances where i need any of this data. Now company.taxDetailsForm is no longer “the same as contactForm with some extra fields”, because i have to add a check whether the user needs to invoice the contact, to check that the required values are there. I have an additional problem with the contact form when not using JavaScript: i must set the required field to all tax details fields to avoid the “(optional)” suffix, and because they _are_ required when that checkbox is enabled, but i can not set them optional when the check is unchecked. My solution for now is to ignore the form validation, and later i will add some JavaScript that adds the validation again, so it will work in all cases.
This commit is contained in:
parent
30cd15ee89
commit
1c0f126c58
|
@ -42,12 +42,12 @@ values (123, 123, 'Retenció 15 %', -0.15)
|
|||
;
|
||||
|
||||
alter sequence contact_contact_id_seq restart with 123;
|
||||
select add_contact (123, 'Melcior', '1', 'Rei Blanc', '0732621', 'melcio@reismags.cat', '', 'C/ Principal, 1', 'Shiraz', 'Fars', '1', 'IR', array['pesebre', 'mag']);
|
||||
select add_contact (123, 'Gaspar', '2', 'Rei Ros', '111', 'gaspar@reismags.cat', '', 'C/ Principal, 2', 'Nova Delhi', 'Delhi', '2', 'IN', array['pesebre', 'mag']);
|
||||
select add_contact (123, 'Baltasar', '3', 'Rei Negre', '1-111-111', 'baltasar@reismags.cat', '', 'C/ Principal, 3', 'Sanaa', 'Sanaa', '3', 'YE', array['pesebre', 'mag']);
|
||||
select add_contact (123, 'Caganera', '41414141L', '', '222 222 222', 'caganera@pesebre.cat', '', 'C/ De l’Hort, 4', 'Olot', 'Girona', '17800', 'ES', array['pesebre', 'persona']);
|
||||
select add_contact (123, 'Bou', '41414142C', '', '333 333 333', 'bou@pesebre.cat', '', 'C/ De la Palla, 5', 'Sant Climent Sescebes', 'Girona', '17751', 'ES', array['pesebre', 'bestia']);
|
||||
select add_contact (123, 'Rabadà', '41414143K', '', '444 444 444', 'rabada@pesebre.cat', '', 'C/ De les Ovelles, 6', 'Fornells de la Selva', 'Girona', '17458', 'ES', array['pesebre', 'persona']);
|
||||
select add_contact (123, 'Melcior', '0732621', 'melcio@reismags.cat', '', '(Rei Blanc,1,"C/ Principal, 1",Shiraz,Fars,1,IR)', array['pesebre', 'mag']);
|
||||
select add_contact (123, 'Gaspar', '111', 'gaspar@reismags.cat', '', '(Rei Ros,2,"C/ Principal, 2",Nova Delhi,Delhi,2,IN)', array['pesebre', 'mag']);
|
||||
select add_contact (123, 'Baltasar', '1-111-111', 'baltasar@reismags.cat', '', '(Rei Negre,3,"C/ Principal, 3",Sanaa,Sanaa,3,YE)', array['pesebre', 'mag']);
|
||||
select add_contact (123, 'Caganera', '222 222 222', 'caganera@pesebre.cat', '', '(Caganera,41414141L,"C/ De l’Hort, 4",Olot,Girona,17800,ES)', array['pesebre', 'persona']);
|
||||
select add_contact (123, 'Bou', '333 333 333', 'bou@pesebre.cat', '', '(Bou,41414142C,"C/ De la Palla, 5",Sant Climent Sescebes,Girona,17751,ES)', array['pesebre', 'bestia']);
|
||||
select add_contact (123, 'Rabadà', '444 444 444', 'rabada@pesebre.cat', '', '(Rabadà,41414143K,"C/ De les Ovelles, 6",Fornells de la Selva,Girona,17458,ES)', array['pesebre', 'persona']);
|
||||
|
||||
alter sequence product_product_id_seq restart with 123;
|
||||
select add_product(123, 'Or', 'Metall de transició tou, brillant, groc, pesant, mal·leable, dúctil i que no reacciona amb la majoria de productes químics, però és sensible al clor i a l’aigua règia.', '55.92', array[124], array['metall']);
|
||||
|
|
|
@ -7,21 +7,47 @@
|
|||
-- requires: country_code
|
||||
-- requires: contact
|
||||
-- requires: tag_name
|
||||
-- requires: tax_details
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_contact(company_id integer, business_name text, vatin text, trade_name text, phone text, email email, web uri, address text, city text, province text, postal_code text, country_code country_code, tags tag_name[]) returns uuid as
|
||||
create or replace function add_contact(company_id integer, name text, phone text, email email, web uri, tax_details tax_details, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
cslug uuid;
|
||||
begin
|
||||
insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags)
|
||||
values (add_contact.company_id, add_contact.business_name, (add_contact.country_code || add_contact.vatin)::vatin, add_contact.trade_name, parse_packed_phone_number(add_contact.phone, add_contact.country_code), add_contact.email, add_contact.web, add_contact.address, add_contact.city, add_contact.province, add_contact.postal_code, add_contact.country_code, add_contact.tags)
|
||||
insert into contact (company_id, name, tags)
|
||||
values (add_contact.company_id, add_contact.name, add_contact.tags)
|
||||
returning contact_id, slug
|
||||
into cid, cslug;
|
||||
into cid, cslug
|
||||
;
|
||||
|
||||
if tax_details is not null then
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (cid, tax_details.business_name, (tax_details.country_code || tax_details.vatin)::vatin, tax_details.address, tax_details.city, tax_details.province, tax_details.postal_code, tax_details.country_code)
|
||||
;
|
||||
end if;
|
||||
|
||||
if phone is not null and trim(phone) <> '' then
|
||||
insert into contact_phone (contact_id, phone)
|
||||
values (cid, parse_packed_phone_number(add_contact.phone, coalesce(tax_details.country_code, 'ES')))
|
||||
;
|
||||
end if;
|
||||
|
||||
if email is not null and trim(email) <> '' then
|
||||
insert into contact_email (contact_id, email)
|
||||
values (cid, add_contact.email)
|
||||
;
|
||||
end if;
|
||||
|
||||
if web is not null and web <> '' then
|
||||
insert into contact_web (contact_id, uri)
|
||||
values (cid, add_contact.web)
|
||||
;
|
||||
end if;
|
||||
|
||||
return cslug;
|
||||
end
|
||||
|
@ -29,8 +55,10 @@ $$
|
|||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) from public;
|
||||
grant execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to invoicer;
|
||||
grant execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to admin;
|
||||
revoke execute on function add_contact(integer, text, text, email, uri, tax_details, tag_name[]) from public;
|
||||
grant execute on function add_contact(integer, text, text, email, uri, tax_details, tag_name[]) to invoicer;
|
||||
grant execute on function add_contact(integer, text, text, email, uri, tax_details, tag_name[]) to admin;
|
||||
|
||||
drop function if exists add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]);
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
-- Deploy numerus:add_contact to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: extension_vat
|
||||
-- requires: email
|
||||
-- requires: extension_pg_libphonenumber
|
||||
-- requires: extension_uri
|
||||
-- requires: country_code
|
||||
-- requires: contact
|
||||
-- requires: tag_name
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_contact(company_id integer, business_name text, vatin text, trade_name text, phone text, email email, web uri, address text, city text, province text, postal_code text, country_code country_code, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
cslug uuid;
|
||||
begin
|
||||
insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags)
|
||||
values (add_contact.company_id, add_contact.business_name, (add_contact.country_code || add_contact.vatin)::vatin, add_contact.trade_name, parse_packed_phone_number(add_contact.phone, add_contact.country_code), add_contact.email, add_contact.web, add_contact.address, add_contact.city, add_contact.province, add_contact.postal_code, add_contact.country_code, add_contact.tags)
|
||||
returning contact_id, slug
|
||||
into cid, cslug;
|
||||
|
||||
return cslug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) from public;
|
||||
grant execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to invoicer;
|
||||
grant execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,39 @@
|
|||
-- Deploy numerus:contact_email to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: email
|
||||
-- requires: contact
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table contact_email (
|
||||
contact_id integer primary key references contact,
|
||||
email email not null
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table contact_email to invoicer;
|
||||
grant select, insert, update, delete on table contact_email to admin;
|
||||
|
||||
alter table contact_email enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on contact_email
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from contact
|
||||
where contact.contact_id = contact_email.contact_id
|
||||
)
|
||||
);
|
||||
|
||||
insert into contact_email
|
||||
select contact_id, email
|
||||
from contact;
|
||||
|
||||
alter table contact
|
||||
drop column email
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,38 @@
|
|||
-- Deploy numerus:contact_phone to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: extension_pg_libphonenumber
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table contact_phone (
|
||||
contact_id integer primary key references contact,
|
||||
phone packed_phone_number not null
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table contact_phone to invoicer;
|
||||
grant select, insert, update, delete on table contact_phone to admin;
|
||||
|
||||
alter table contact_phone enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on contact_phone
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from contact
|
||||
where contact.contact_id = contact_phone.contact_id
|
||||
)
|
||||
);
|
||||
|
||||
insert into contact_phone
|
||||
select contact_id, phone
|
||||
from contact;
|
||||
|
||||
alter table contact
|
||||
drop column phone
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,60 @@
|
|||
-- Deploy numerus:contact_tax_details to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: contact
|
||||
-- requires: extension_vat
|
||||
-- requires: country_code
|
||||
-- requires: country
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table contact_tax_details (
|
||||
contact_id integer primary key references contact,
|
||||
business_name text not null constraint business_name_not_empty check(length(trim(business_name)) > 1),
|
||||
vatin vatin not null,
|
||||
address text not null,
|
||||
city text not null,
|
||||
province text not null,
|
||||
postal_code text not null,
|
||||
country_code country_code not null references country
|
||||
);
|
||||
|
||||
alter table contact_tax_details enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on contact_tax_details
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from contact
|
||||
where contact.contact_id = contact_tax_details.contact_id
|
||||
)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table contact_tax_details to invoicer;
|
||||
grant select, insert, update, delete on table contact_tax_details to admin;
|
||||
|
||||
insert into contact_tax_details
|
||||
select contact_id, business_name, vatin, address, city, province, postal_code, country_code
|
||||
from contact;
|
||||
|
||||
update contact set trade_name = business_name where trade_name = '';
|
||||
|
||||
alter table contact
|
||||
rename column trade_name to name
|
||||
;
|
||||
|
||||
alter table contact
|
||||
drop column business_name
|
||||
, drop column vatin
|
||||
, drop column address
|
||||
, drop column city
|
||||
, drop column province
|
||||
, drop column postal_code
|
||||
, drop column country_code
|
||||
, add constraint name_not_empty check(length(trim(name)) > 1)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,39 @@
|
|||
-- Deploy numerus:contact_web to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: extension_uri
|
||||
-- requires: contact
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table contact_web (
|
||||
contact_id integer primary key references contact,
|
||||
uri uri not null
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table contact_web to invoicer;
|
||||
grant select, insert, update, delete on table contact_web to admin;
|
||||
|
||||
alter table contact_web enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on contact_web
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from contact
|
||||
where contact.contact_id = contact_web.contact_id
|
||||
)
|
||||
);
|
||||
|
||||
insert into contact_web
|
||||
select contact_id, web
|
||||
from contact;
|
||||
|
||||
alter table contact
|
||||
drop column web
|
||||
;
|
||||
|
||||
commit;
|
|
@ -7,29 +7,20 @@
|
|||
-- requires: contact
|
||||
-- requires: extension_vat
|
||||
-- requires: extension_pg_libphonenumber
|
||||
-- requires: tax_details
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_contact(contact_slug uuid, business_name text, vatin text, trade_name text, phone text, email email, web uri, address text, city text, province text, postal_code text, country_code country_code, tags tag_name[]) returns uuid as
|
||||
create or replace function edit_contact(contact_slug uuid, name text, phone text, email email, web uri, tax_details tax_details, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
company integer;
|
||||
begin
|
||||
update contact
|
||||
set business_name = edit_contact.business_name
|
||||
, vatin = (edit_contact.country_code || edit_contact.vatin)::vatin
|
||||
, trade_name = edit_contact.trade_name
|
||||
, phone = parse_packed_phone_number( edit_contact.phone, edit_contact.country_code)
|
||||
, email = edit_contact.email
|
||||
, web = edit_contact.web
|
||||
, address = edit_contact.address
|
||||
, city = edit_contact.city
|
||||
, province = edit_contact.province
|
||||
, postal_code = edit_contact.postal_code
|
||||
, country_code = edit_contact.country_code
|
||||
set name = edit_contact.name
|
||||
, tags = edit_contact.tags
|
||||
where slug = contact_slug
|
||||
returning contact_id, company_id
|
||||
|
@ -40,14 +31,72 @@ begin
|
|||
return null;
|
||||
end if;
|
||||
|
||||
if tax_details is null then
|
||||
delete
|
||||
from contact_tax_details
|
||||
where contact_id = cid
|
||||
;
|
||||
else
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (cid, tax_details.business_name, (tax_details.country_code || tax_details.vatin)::vatin, tax_details.address, tax_details.city, tax_details.province, tax_details.postal_code, tax_details.country_code)
|
||||
on conflict (contact_id) do update
|
||||
set business_name = excluded.business_name
|
||||
, vatin = excluded.vatin
|
||||
, address = excluded.address
|
||||
, city = excluded.city
|
||||
, province = excluded.province
|
||||
, postal_code = excluded.postal_code
|
||||
, country_code = excluded.country_code
|
||||
;
|
||||
end if;
|
||||
|
||||
if phone is null or trim(phone) = '' then
|
||||
delete from contact_phone
|
||||
where contact_id = cid
|
||||
;
|
||||
else
|
||||
insert into contact_phone (contact_id, phone)
|
||||
values (cid, parse_packed_phone_number(edit_contact.phone, coalesce(tax_details.country_code, 'ES')))
|
||||
on conflict (contact_id) do update
|
||||
set phone = excluded.phone
|
||||
;
|
||||
end if;
|
||||
|
||||
if email is null or trim(email) = '' then
|
||||
delete from contact_email
|
||||
where contact_id = cid
|
||||
;
|
||||
else
|
||||
insert into contact_email (contact_id, email)
|
||||
values (cid, edit_contact.email)
|
||||
on conflict (contact_id) do update
|
||||
set email = excluded.email
|
||||
;
|
||||
end if;
|
||||
|
||||
if web is null or web = '' then
|
||||
delete from contact_web
|
||||
where contact_id = cid
|
||||
;
|
||||
else
|
||||
insert into contact_web (contact_id, uri)
|
||||
values (cid, edit_contact.web)
|
||||
on conflict (contact_id) do update
|
||||
set uri = excluded.uri
|
||||
;
|
||||
end if;
|
||||
|
||||
return contact_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) from public;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to invoicer;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to admin;
|
||||
revoke execute on function edit_contact(uuid, text, text, email, uri, tax_details, tag_name[]) from public;
|
||||
grant execute on function edit_contact(uuid, text, text, email, uri, tax_details, tag_name[]) to invoicer;
|
||||
grant execute on function edit_contact(uuid, text, text, email, uri, tax_details, tag_name[]) to admin;
|
||||
|
||||
|
||||
drop function if exists edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]);
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
-- Deploy numerus:edit_contact to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: email
|
||||
-- requires: extension_uri
|
||||
-- requires: country_code
|
||||
-- requires: tag_name
|
||||
-- requires: contact
|
||||
-- requires: extension_vat
|
||||
-- requires: extension_pg_libphonenumber
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_contact(contact_slug uuid, business_name text, vatin text, trade_name text, phone text, email email, web uri, address text, city text, province text, postal_code text, country_code country_code, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
company integer;
|
||||
begin
|
||||
update contact
|
||||
set business_name = edit_contact.business_name
|
||||
, vatin = (edit_contact.country_code || edit_contact.vatin)::vatin
|
||||
, trade_name = edit_contact.trade_name
|
||||
, phone = parse_packed_phone_number( edit_contact.phone, edit_contact.country_code)
|
||||
, email = edit_contact.email
|
||||
, web = edit_contact.web
|
||||
, address = edit_contact.address
|
||||
, city = edit_contact.city
|
||||
, province = edit_contact.province
|
||||
, postal_code = edit_contact.postal_code
|
||||
, country_code = edit_contact.country_code
|
||||
, tags = edit_contact.tags
|
||||
where slug = contact_slug
|
||||
returning contact_id, company_id
|
||||
into cid, company
|
||||
;
|
||||
|
||||
if cid is null then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
return contact_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) from public;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to invoicer;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,13 @@
|
|||
-- Deploy numerus:invoice_contact_id_fkey to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: invoice
|
||||
-- requires: contact_tax_details
|
||||
|
||||
begin;
|
||||
|
||||
alter table numerus.invoice
|
||||
drop constraint invoice_contact_id_fkey
|
||||
, add foreign key (contact_id) references numerus.contact_tax_details (contact_id)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,20 @@
|
|||
-- Deploy numerus:tax_details to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: extension_vat
|
||||
-- requires: country_code
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create type tax_details as (
|
||||
business_name text,
|
||||
vatin text,
|
||||
address text,
|
||||
city text,
|
||||
province text,
|
||||
postal_code text,
|
||||
country_code country_code
|
||||
);
|
||||
|
||||
commit;
|
144
pkg/company.go
144
pkg/company.go
|
@ -85,7 +85,18 @@ type PaymentMethod struct {
|
|||
}
|
||||
|
||||
type taxDetailsForm struct {
|
||||
*contactForm
|
||||
locale *Locale
|
||||
TradeName *InputField
|
||||
BusinessName *InputField
|
||||
VATIN *InputField
|
||||
Phone *InputField
|
||||
Email *InputField
|
||||
Web *InputField
|
||||
Address *InputField
|
||||
City *InputField
|
||||
Province *InputField
|
||||
PostalCode *InputField
|
||||
Country *SelectField
|
||||
Currency *SelectField
|
||||
InvoiceNumberFormat *InputField
|
||||
NextInvoiceNumber *InputField
|
||||
|
@ -96,7 +107,94 @@ type taxDetailsForm struct {
|
|||
|
||||
func newTaxDetailsForm(ctx context.Context, conn *Conn, locale *Locale) *taxDetailsForm {
|
||||
return &taxDetailsForm{
|
||||
contactForm: newContactForm(ctx, conn, locale),
|
||||
locale: locale,
|
||||
TradeName: &InputField{
|
||||
Name: "trade_name",
|
||||
Label: pgettext("input", "Trade name", locale),
|
||||
Type: "text",
|
||||
},
|
||||
Phone: &InputField{
|
||||
Name: "phone",
|
||||
Label: pgettext("input", "Phone", locale),
|
||||
Type: "tel",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="tel"`,
|
||||
},
|
||||
},
|
||||
Email: &InputField{
|
||||
Name: "email",
|
||||
Label: pgettext("input", "Email", locale),
|
||||
Type: "email",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="email"`,
|
||||
},
|
||||
},
|
||||
Web: &InputField{
|
||||
Name: "web",
|
||||
Label: pgettext("input", "Web", locale),
|
||||
Type: "url",
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="url"`,
|
||||
},
|
||||
},
|
||||
BusinessName: &InputField{
|
||||
Name: "business_name",
|
||||
Label: pgettext("input", "Business name", locale),
|
||||
Type: "text",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="organization"`,
|
||||
`minlength="2"`,
|
||||
},
|
||||
},
|
||||
VATIN: &InputField{
|
||||
Name: "vatin",
|
||||
Label: pgettext("input", "VAT number", locale),
|
||||
Type: "text",
|
||||
Required: true,
|
||||
},
|
||||
Address: &InputField{
|
||||
Name: "address",
|
||||
Label: pgettext("input", "Address", locale),
|
||||
Type: "text",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="address-line1"`,
|
||||
},
|
||||
},
|
||||
City: &InputField{
|
||||
Name: "city",
|
||||
Label: pgettext("input", "City", locale),
|
||||
Type: "text",
|
||||
Required: true,
|
||||
},
|
||||
Province: &InputField{
|
||||
Name: "province",
|
||||
Label: pgettext("input", "Province", locale),
|
||||
Type: "text",
|
||||
Required: true,
|
||||
},
|
||||
PostalCode: &InputField{
|
||||
Name: "postal_code",
|
||||
Label: pgettext("input", "Postal code", locale),
|
||||
Type: "text",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="postal-code"`,
|
||||
},
|
||||
},
|
||||
Country: &SelectField{
|
||||
Name: "country",
|
||||
Label: pgettext("input", "Country", locale),
|
||||
Options: mustGetCountryOptions(ctx, conn, locale),
|
||||
Required: true,
|
||||
Selected: []string{"ES"},
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="country"`,
|
||||
},
|
||||
},
|
||||
Currency: &SelectField{
|
||||
Name: "currency",
|
||||
Label: pgettext("input", "Currency", locale),
|
||||
|
@ -143,9 +241,20 @@ func newTaxDetailsForm(ctx context.Context, conn *Conn, locale *Locale) *taxDeta
|
|||
}
|
||||
|
||||
func (form *taxDetailsForm) Parse(r *http.Request) error {
|
||||
if err := form.contactForm.Parse(r); err != nil {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
form.TradeName.FillValue(r)
|
||||
form.BusinessName.FillValue(r)
|
||||
form.VATIN.FillValue(r)
|
||||
form.Phone.FillValue(r)
|
||||
form.Email.FillValue(r)
|
||||
form.Web.FillValue(r)
|
||||
form.Address.FillValue(r)
|
||||
form.City.FillValue(r)
|
||||
form.Province.FillValue(r)
|
||||
form.PostalCode.FillValue(r)
|
||||
form.Country.FillValue(r)
|
||||
form.Currency.FillValue(r)
|
||||
form.InvoiceNumberFormat.FillValue(r)
|
||||
form.NextInvoiceNumber.FillValue(r)
|
||||
|
@ -157,12 +266,39 @@ func (form *taxDetailsForm) Parse(r *http.Request) error {
|
|||
|
||||
func (form *taxDetailsForm) Validate(ctx context.Context, conn *Conn) bool {
|
||||
validator := newFormValidator()
|
||||
|
||||
country := ""
|
||||
if validator.CheckValidSelectOption(form.Country, gettext("Selected country is not valid.", form.locale)) {
|
||||
country = form.Country.Selected[0]
|
||||
}
|
||||
|
||||
validator.CheckRequiredInput(form.BusinessName, gettext("Business name can not be empty.", form.locale))
|
||||
validator.CheckInputMinLength(form.BusinessName, 2, gettext("Business name must have at least two letters.", form.locale))
|
||||
if validator.CheckRequiredInput(form.VATIN, gettext("VAT number can not be empty.", form.locale)) {
|
||||
validator.CheckValidVATINInput(form.VATIN, country, gettext("This value is not a valid VAT number.", form.locale))
|
||||
}
|
||||
if validator.CheckRequiredInput(form.Phone, gettext("Phone can not be empty.", form.locale)) {
|
||||
validator.CheckValidPhoneInput(form.Phone, country, gettext("This value is not a valid phone number.", form.locale))
|
||||
}
|
||||
if validator.CheckRequiredInput(form.Email, gettext("Email can not be empty.", form.locale)) {
|
||||
validator.CheckValidEmailInput(form.Email, gettext("This value is not a valid email. It should be like name@domain.com.", form.locale))
|
||||
}
|
||||
if form.Web.Val != "" {
|
||||
validator.CheckValidURL(form.Web, gettext("This value is not a valid web address. It should be like https://domain.com/.", form.locale))
|
||||
}
|
||||
validator.CheckRequiredInput(form.Address, gettext("Address can not be empty.", form.locale))
|
||||
validator.CheckRequiredInput(form.City, gettext("City can not be empty.", form.locale))
|
||||
validator.CheckRequiredInput(form.Province, gettext("Province can not be empty.", form.locale))
|
||||
if validator.CheckRequiredInput(form.PostalCode, gettext("Postal code can not be empty.", form.locale)) {
|
||||
validator.CheckValidPostalCode(ctx, conn, form.PostalCode, country, gettext("This value is not a valid postal code.", form.locale))
|
||||
}
|
||||
validator.CheckValidSelectOption(form.Currency, gettext("Selected currency is not valid.", form.locale))
|
||||
validator.CheckRequiredInput(form.InvoiceNumberFormat, gettext("Invoice number format can not be empty.", form.locale))
|
||||
validator.CheckValidInteger(form.NextInvoiceNumber, 1, math.MaxInt32, gettext("Next invoice number must be a number greater than zero.", form.locale))
|
||||
validator.CheckRequiredInput(form.QuoteNumberFormat, gettext("Quotation number format can not be empty.", form.locale))
|
||||
validator.CheckValidInteger(form.NextQuoteNumber, 1, math.MaxInt32, gettext("Next quotation number must be a number greater than zero.", form.locale))
|
||||
return form.contactForm.Validate(ctx, conn) && validator.AllOK()
|
||||
|
||||
return validator.AllOK()
|
||||
}
|
||||
|
||||
func (form *taxDetailsForm) mustFillFromDatabase(ctx context.Context, conn *Conn, company *Company) *taxDetailsForm {
|
||||
|
|
181
pkg/contacts.go
181
pkg/contacts.go
|
@ -69,7 +69,7 @@ type editContactPage struct {
|
|||
func mustRenderEditContactForm(w http.ResponseWriter, r *http.Request, slug string, form *contactForm) {
|
||||
page := &editContactPage{
|
||||
Slug: slug,
|
||||
ContactName: form.BusinessName.Val,
|
||||
ContactName: form.Name.String(),
|
||||
Form: form,
|
||||
}
|
||||
mustRenderMainTemplate(w, r, "contacts/edit.gohtml", page)
|
||||
|
@ -95,7 +95,7 @@ func HandleAddContact(w http.ResponseWriter, r *http.Request, _ httprouter.Param
|
|||
return
|
||||
}
|
||||
company := mustGetCompany(r)
|
||||
conn.MustExec(r.Context(), "select add_contact($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", company.Id, form.BusinessName, form.VATIN, form.TradeName, form.Phone, form.Email, form.Web, form.Address, form.City, form.Province, form.PostalCode, form.Country, form.Tags)
|
||||
conn.MustExec(r.Context(), "select add_contact($1, $2, $3, $4, $5, $6, $7)", company.Id, form.Name, form.Phone.ValueOrNil(), form.Email.ValueOrNil(), form.Web.ValueOrNil(), form.TaxDetails(), form.Tags)
|
||||
htmxRedirect(w, r, companyURI(company, "/contacts"))
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ func HandleUpdateContact(w http.ResponseWriter, r *http.Request, params httprout
|
|||
mustRenderEditContactForm(w, r, params[0].Value, form)
|
||||
return
|
||||
}
|
||||
slug := conn.MustGetText(r.Context(), "", "select edit_contact($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)", params[0].Value, form.BusinessName, form.VATIN, form.TradeName, form.Phone, form.Email, form.Web, form.Address, form.City, form.Province, form.PostalCode, form.Country, form.Tags)
|
||||
slug := conn.MustGetText(r.Context(), "", "select edit_contact($1, $2, $3, $4, $5, $6, $7)", params[0].Value, form.Name, form.Phone.ValueOrNil(), form.Email.ValueOrNil(), form.Web.ValueOrNil(), form.TaxDetails(), form.Tags)
|
||||
if slug == "" {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ func mustCollectContactEntries(ctx context.Context, conn *Conn, company *Company
|
|||
if filters != nil {
|
||||
name := strings.TrimSpace(filters.Name.String())
|
||||
if name != "" {
|
||||
appendWhere("contact.business_name ilike $%d", "%"+name+"%")
|
||||
appendWhere("contact.name ilike $%d", "%"+name+"%")
|
||||
}
|
||||
if len(filters.Tags.Tags) > 0 {
|
||||
if filters.TagsCondition.Selected == "and" {
|
||||
|
@ -189,13 +189,15 @@ func mustCollectContactEntries(ctx context.Context, conn *Conn, company *Company
|
|||
}
|
||||
rows := conn.MustQuery(ctx, fmt.Sprintf(`
|
||||
select slug
|
||||
, business_name
|
||||
, email
|
||||
, phone
|
||||
, name
|
||||
, coalesce(email::text, '')
|
||||
, coalesce(phone::text, '')
|
||||
, tags
|
||||
from contact
|
||||
left join contact_email using (contact_id)
|
||||
left join contact_phone using (contact_id)
|
||||
where (%s)
|
||||
order by business_name
|
||||
order by name
|
||||
`, strings.Join(where, ") AND (")), args...)
|
||||
defer rows.Close()
|
||||
|
||||
|
@ -215,24 +217,59 @@ func mustCollectContactEntries(ctx context.Context, conn *Conn, company *Company
|
|||
}
|
||||
|
||||
type contactForm struct {
|
||||
locale *Locale
|
||||
BusinessName *InputField
|
||||
VATIN *InputField
|
||||
TradeName *InputField
|
||||
Phone *InputField
|
||||
Email *InputField
|
||||
Web *InputField
|
||||
Address *InputField
|
||||
City *InputField
|
||||
Province *InputField
|
||||
PostalCode *InputField
|
||||
Country *SelectField
|
||||
Tags *TagsField
|
||||
locale *Locale
|
||||
Name *InputField
|
||||
HasTaxDetails *CheckField
|
||||
BusinessName *InputField
|
||||
VATIN *InputField
|
||||
Phone *InputField
|
||||
Email *InputField
|
||||
Web *InputField
|
||||
Address *InputField
|
||||
City *InputField
|
||||
Province *InputField
|
||||
PostalCode *InputField
|
||||
Country *SelectField
|
||||
Tags *TagsField
|
||||
}
|
||||
|
||||
func newContactForm(ctx context.Context, conn *Conn, locale *Locale) *contactForm {
|
||||
return &contactForm{
|
||||
locale: locale,
|
||||
Name: &InputField{
|
||||
Name: "name",
|
||||
Label: pgettext("input", "Name", locale),
|
||||
Type: "text",
|
||||
Required: true,
|
||||
},
|
||||
Phone: &InputField{
|
||||
Name: "phone",
|
||||
Label: pgettext("input", "Phone", locale),
|
||||
Type: "tel",
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="tel"`,
|
||||
},
|
||||
},
|
||||
Email: &InputField{
|
||||
Name: "email",
|
||||
Label: pgettext("input", "Email", locale),
|
||||
Type: "email",
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="email"`,
|
||||
},
|
||||
},
|
||||
Web: &InputField{
|
||||
Name: "web",
|
||||
Label: pgettext("input", "Web", locale),
|
||||
Type: "url",
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="url"`,
|
||||
},
|
||||
},
|
||||
HasTaxDetails: &CheckField{
|
||||
Name: "has_tax_details",
|
||||
Label: pgettext("input", "Need to input tax details", locale),
|
||||
},
|
||||
BusinessName: &InputField{
|
||||
Name: "business_name",
|
||||
Label: pgettext("input", "Business name", locale),
|
||||
|
@ -249,37 +286,6 @@ func newContactForm(ctx context.Context, conn *Conn, locale *Locale) *contactFor
|
|||
Type: "text",
|
||||
Required: true,
|
||||
},
|
||||
TradeName: &InputField{
|
||||
Name: "trade_name",
|
||||
Label: pgettext("input", "Trade name", locale),
|
||||
Type: "text",
|
||||
},
|
||||
Phone: &InputField{
|
||||
Name: "phone",
|
||||
Label: pgettext("input", "Phone", locale),
|
||||
Type: "tel",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="tel"`,
|
||||
},
|
||||
},
|
||||
Email: &InputField{
|
||||
Name: "email",
|
||||
Label: pgettext("input", "Email", locale),
|
||||
Type: "email",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="email"`,
|
||||
},
|
||||
},
|
||||
Web: &InputField{
|
||||
Name: "web",
|
||||
Label: pgettext("input", "Web", locale),
|
||||
Type: "url",
|
||||
Attributes: []template.HTMLAttr{
|
||||
`autocomplete="url"`,
|
||||
},
|
||||
},
|
||||
Address: &InputField{
|
||||
Name: "address",
|
||||
Label: pgettext("input", "Address", locale),
|
||||
|
@ -331,9 +337,10 @@ func (form *contactForm) Parse(r *http.Request) error {
|
|||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
form.Name.FillValue(r)
|
||||
form.HasTaxDetails.FillValue(r)
|
||||
form.BusinessName.FillValue(r)
|
||||
form.VATIN.FillValue(r)
|
||||
form.TradeName.FillValue(r)
|
||||
form.Phone.FillValue(r)
|
||||
form.Email.FillValue(r)
|
||||
form.Web.FillValue(r)
|
||||
|
@ -349,42 +356,50 @@ func (form *contactForm) Parse(r *http.Request) error {
|
|||
func (form *contactForm) Validate(ctx context.Context, conn *Conn) bool {
|
||||
validator := newFormValidator()
|
||||
|
||||
country := ""
|
||||
if validator.CheckValidSelectOption(form.Country, gettext("Selected country is not valid.", form.locale)) {
|
||||
country = form.Country.Selected[0]
|
||||
country := "ES"
|
||||
if form.HasTaxDetails.Checked {
|
||||
if validator.CheckValidSelectOption(form.Country, gettext("Selected country is not valid.", form.locale)) {
|
||||
country = form.Country.Selected[0]
|
||||
}
|
||||
validator.CheckRequiredInput(form.BusinessName, gettext("Business name can not be empty.", form.locale))
|
||||
validator.CheckInputMinLength(form.BusinessName, 2, gettext("Business name must have at least two letters.", form.locale))
|
||||
if validator.CheckRequiredInput(form.VATIN, gettext("VAT number can not be empty.", form.locale)) {
|
||||
validator.CheckValidVATINInput(form.VATIN, country, gettext("This value is not a valid VAT number.", form.locale))
|
||||
}
|
||||
validator.CheckRequiredInput(form.Address, gettext("Address can not be empty.", form.locale))
|
||||
validator.CheckRequiredInput(form.City, gettext("City can not be empty.", form.locale))
|
||||
validator.CheckRequiredInput(form.Province, gettext("Province can not be empty.", form.locale))
|
||||
|
||||
if validator.CheckRequiredInput(form.PostalCode, gettext("Postal code can not be empty.", form.locale)) {
|
||||
validator.CheckValidPostalCode(ctx, conn, form.PostalCode, country, gettext("This value is not a valid postal code.", form.locale))
|
||||
}
|
||||
}
|
||||
|
||||
validator.CheckRequiredInput(form.BusinessName, gettext("Business name can not be empty.", form.locale))
|
||||
validator.CheckInputMinLength(form.BusinessName, 2, gettext("Business name must have at least two letters.", form.locale))
|
||||
if validator.CheckRequiredInput(form.VATIN, gettext("VAT number can not be empty.", form.locale)) {
|
||||
validator.CheckValidVATINInput(form.VATIN, country, gettext("This value is not a valid VAT number.", form.locale))
|
||||
}
|
||||
if validator.CheckRequiredInput(form.Phone, gettext("Phone can not be empty.", form.locale)) {
|
||||
validator.CheckRequiredInput(form.Name, gettext("Name can not be empty.", form.locale))
|
||||
validator.CheckInputMinLength(form.Name, 2, gettext("Name must have at least two letters.", form.locale))
|
||||
|
||||
if form.Phone.Val != "" {
|
||||
validator.CheckValidPhoneInput(form.Phone, country, gettext("This value is not a valid phone number.", form.locale))
|
||||
}
|
||||
if validator.CheckRequiredInput(form.Email, gettext("Email can not be empty.", form.locale)) {
|
||||
if form.Email.Val != "" {
|
||||
validator.CheckValidEmailInput(form.Email, gettext("This value is not a valid email. It should be like name@domain.com.", form.locale))
|
||||
}
|
||||
if form.Web.Val != "" {
|
||||
validator.CheckValidURL(form.Web, gettext("This value is not a valid web address. It should be like https://domain.com/.", form.locale))
|
||||
}
|
||||
validator.CheckRequiredInput(form.Address, gettext("Address can not be empty.", form.locale))
|
||||
validator.CheckRequiredInput(form.City, gettext("City can not be empty.", form.locale))
|
||||
validator.CheckRequiredInput(form.Province, gettext("Province can not be empty.", form.locale))
|
||||
if validator.CheckRequiredInput(form.PostalCode, gettext("Postal code can not be empty.", form.locale)) {
|
||||
validator.CheckValidPostalCode(ctx, conn, form.PostalCode, country, gettext("This value is not a valid postal code.", form.locale))
|
||||
}
|
||||
|
||||
return validator.AllOK()
|
||||
}
|
||||
|
||||
func (form *contactForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slug string) bool {
|
||||
return !notFoundErrorOrPanic(conn.QueryRow(ctx, `
|
||||
select business_name
|
||||
select name
|
||||
, vatin is not null
|
||||
, business_name
|
||||
, substr(vatin::text, 3)
|
||||
, trade_name
|
||||
, phone
|
||||
, email
|
||||
, web
|
||||
, uri
|
||||
, address
|
||||
, city
|
||||
, province
|
||||
|
@ -392,11 +407,16 @@ func (form *contactForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
|
|||
, country_code
|
||||
, tags
|
||||
from contact
|
||||
left join contact_email using (contact_id)
|
||||
left join contact_phone using (contact_id)
|
||||
left join contact_web using (contact_id)
|
||||
left join contact_tax_details using (contact_id)
|
||||
where slug = $1
|
||||
`, slug).Scan(
|
||||
form.Name,
|
||||
form.HasTaxDetails,
|
||||
form.BusinessName,
|
||||
form.VATIN,
|
||||
form.TradeName,
|
||||
form.Phone,
|
||||
form.Email,
|
||||
form.Web,
|
||||
|
@ -408,6 +428,21 @@ func (form *contactForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
|
|||
form.Tags))
|
||||
}
|
||||
|
||||
func (form *contactForm) TaxDetails() *CustomerTaxDetails {
|
||||
if !form.HasTaxDetails.Checked {
|
||||
return nil
|
||||
}
|
||||
return &CustomerTaxDetails{
|
||||
BusinessName: form.BusinessName.String(),
|
||||
VATIN: form.VATIN.String(),
|
||||
Address: form.Address.String(),
|
||||
City: form.City.String(),
|
||||
Province: form.Province.String(),
|
||||
PostalCode: form.PostalCode.String(),
|
||||
CountryCode: form.Country.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func ServeEditContactTags(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
conn := getConn(r)
|
||||
locale := getLocale(r)
|
||||
|
|
|
@ -52,7 +52,7 @@ func mustCollectExpenseEntries(ctx context.Context, conn *Conn, company *Company
|
|||
, invoice_date
|
||||
, invoice_number
|
||||
, to_price(amount, decimal_digits)
|
||||
, contact.business_name
|
||||
, contact.name
|
||||
, coalesce(attachment.original_filename, '')
|
||||
, expense.tags
|
||||
from expense
|
||||
|
|
38
pkg/form.go
38
pkg/form.go
|
@ -59,6 +59,13 @@ func (field *InputField) Value() (driver.Value, error) {
|
|||
return field.Val, nil
|
||||
}
|
||||
|
||||
func (field *InputField) ValueOrNil() driver.Valuer {
|
||||
if field.Val == "" {
|
||||
return nil
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
func (field *InputField) FillValue(r *http.Request) {
|
||||
field.Val = strings.TrimSpace(r.FormValue(field.Name))
|
||||
}
|
||||
|
@ -287,6 +294,37 @@ func (field *RadioField) isValidOption(selected string) bool {
|
|||
return field.FindOption(selected) != nil
|
||||
}
|
||||
|
||||
type CheckField struct {
|
||||
Name string
|
||||
Label string
|
||||
Checked bool
|
||||
Attributes []template.HTMLAttr
|
||||
Required bool
|
||||
Errors []error
|
||||
}
|
||||
|
||||
func (field *CheckField) FillValue(r *http.Request) {
|
||||
field.Checked = len(r.Form[field.Name]) > 0
|
||||
}
|
||||
|
||||
func (field *CheckField) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
field.Checked = false
|
||||
return nil
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
field.Checked = v
|
||||
default:
|
||||
field.Checked, _ = strconv.ParseBool(fmt.Sprintf("%v", v))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (field *CheckField) Value() (driver.Value, error) {
|
||||
return field.Checked, nil
|
||||
}
|
||||
|
||||
type FileField struct {
|
||||
Name string
|
||||
Label string
|
||||
|
|
|
@ -64,7 +64,7 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, locale *Locale,
|
|||
select invoice.slug
|
||||
, invoice_date
|
||||
, invoice_number
|
||||
, contact.business_name
|
||||
, contact.name
|
||||
, invoice.tags
|
||||
, invoice.invoice_status
|
||||
, isi18n.name
|
||||
|
@ -379,7 +379,41 @@ func mustGetInvoice(ctx context.Context, conn *Conn, company *Company, slug stri
|
|||
}
|
||||
var invoiceId int
|
||||
var decimalDigits int
|
||||
if notFoundErrorOrPanic(conn.QueryRow(ctx, "select invoice_id, decimal_digits, invoice_number, invoice_date, notes, instructions, business_name, vatin, phone, email, address, city, province, postal_code, to_price(subtotal, decimal_digits), to_price(total, decimal_digits) from invoice join payment_method using (payment_method_id) join contact using (contact_id) join invoice_amount using (invoice_id) join currency using (currency_code) where invoice.slug = $1", slug).Scan(&invoiceId, &decimalDigits, &inv.Number, &inv.Date, &inv.Notes, &inv.PaymentInstructions, &inv.Invoicee.Name, &inv.Invoicee.VATIN, &inv.Invoicee.Phone, &inv.Invoicee.Email, &inv.Invoicee.Address, &inv.Invoicee.City, &inv.Invoicee.Province, &inv.Invoicee.PostalCode, &inv.Subtotal, &inv.Total)) {
|
||||
if notFoundErrorOrPanic(conn.QueryRow(ctx, `
|
||||
select invoice_id
|
||||
, decimal_digits
|
||||
, invoice_number
|
||||
, invoice_date
|
||||
, notes
|
||||
, instructions
|
||||
, business_name
|
||||
, vatin
|
||||
, address
|
||||
, city
|
||||
, province
|
||||
, postal_code
|
||||
, to_price(subtotal, decimal_digits)
|
||||
, to_price(total, decimal_digits)
|
||||
from invoice
|
||||
join payment_method using (payment_method_id)
|
||||
join contact_tax_details using (contact_id)
|
||||
join invoice_amount using (invoice_id)
|
||||
join currency using (currency_code)
|
||||
where invoice.slug = $1`, slug).Scan(
|
||||
&invoiceId,
|
||||
&decimalDigits,
|
||||
&inv.Number,
|
||||
&inv.Date,
|
||||
&inv.Notes,
|
||||
&inv.PaymentInstructions,
|
||||
&inv.Invoicee.Name,
|
||||
&inv.Invoicee.VATIN,
|
||||
&inv.Invoicee.Address,
|
||||
&inv.Invoicee.City,
|
||||
&inv.Invoicee.Province,
|
||||
&inv.Invoicee.PostalCode,
|
||||
&inv.Subtotal,
|
||||
&inv.Total)) {
|
||||
return nil
|
||||
}
|
||||
if err := conn.QueryRow(ctx, "select business_name, vatin, phone, email, address, city, province, postal_code, legal_disclaimer from company where company_id = $1", company.Id).Scan(&inv.Invoicer.Name, &inv.Invoicer.VATIN, &inv.Invoicer.Phone, &inv.Invoicer.Email, &inv.Invoicer.Address, &inv.Invoicer.City, &inv.Invoicer.Province, &inv.Invoicer.PostalCode, &inv.LegalDisclaimer); err != nil {
|
||||
|
@ -605,7 +639,7 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
|
|||
Name: "customer",
|
||||
Label: pgettext("input", "Customer", locale),
|
||||
Required: true,
|
||||
Options: mustGetContactOptions(ctx, conn, company),
|
||||
Options: mustGetCustomerOptions(ctx, conn, company),
|
||||
},
|
||||
Date: &InputField{
|
||||
Name: "date",
|
||||
|
@ -826,7 +860,11 @@ func mustGetTaxOptions(ctx context.Context, conn *Conn, company *Company) []*Sel
|
|||
}
|
||||
|
||||
func mustGetContactOptions(ctx context.Context, conn *Conn, company *Company) []*SelectOption {
|
||||
return MustGetOptions(ctx, conn, "select contact_id::text, business_name from contact where company_id = $1 order by business_name", company.Id)
|
||||
return MustGetOptions(ctx, conn, "select contact_id::text, name from contact where company_id = $1 order by name", company.Id)
|
||||
}
|
||||
|
||||
func mustGetCustomerOptions(ctx context.Context, conn *Conn, company *Company) []*SelectOption {
|
||||
return MustGetOptions(ctx, conn, "select contact_id::text, name from contact join contact_tax_details using (contact_id) where company_id = $1 order by name", company.Id)
|
||||
}
|
||||
|
||||
func mustGetDefaultPaymentMethod(ctx context.Context, conn *Conn, company *Company) string {
|
||||
|
|
|
@ -7,6 +7,38 @@ import (
|
|||
"github.com/jackc/pgx/v4"
|
||||
)
|
||||
|
||||
type CustomerTaxDetails struct {
|
||||
BusinessName string
|
||||
VATIN string
|
||||
Address string
|
||||
City string
|
||||
Province string
|
||||
PostalCode string
|
||||
CountryCode string
|
||||
}
|
||||
|
||||
func (src CustomerTaxDetails) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
typeName := "tax_details"
|
||||
dt, ok := ci.DataTypeForName(typeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
|
||||
}
|
||||
values := []interface{}{
|
||||
src.BusinessName,
|
||||
src.VATIN,
|
||||
src.Address,
|
||||
src.City,
|
||||
src.Province,
|
||||
src.PostalCode,
|
||||
src.CountryCode,
|
||||
}
|
||||
ct := pgtype.NewValue(dt.Value).(pgtype.ValueTranscoder)
|
||||
if err := ct.Set(values); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ct.EncodeBinary(ci, buf)
|
||||
}
|
||||
|
||||
type NewInvoiceProductArray []*invoiceProductForm
|
||||
|
||||
func (src NewInvoiceProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
|
@ -262,6 +294,32 @@ func registerPgTypes(ctx context.Context, conn *pgx.Conn) error {
|
|||
return err
|
||||
}
|
||||
|
||||
countryCodeOID, err := registerPgType(ctx, conn, &pgtype.Text{}, "country_code")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
taxDetailsType, err := pgtype.NewCompositeType(
|
||||
"tax_details",
|
||||
[]pgtype.CompositeTypeField{
|
||||
{"business_name", pgtype.TextOID},
|
||||
{"vatin", pgtype.TextOID},
|
||||
{"address", pgtype.TextOID},
|
||||
{"city", pgtype.TextOID},
|
||||
{"province", pgtype.TextOID},
|
||||
{"postal_code", pgtype.TextOID},
|
||||
{"discount_rate", countryCodeOID},
|
||||
},
|
||||
conn.ConnInfo(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = registerPgType(ctx, conn, taxDetailsType, taxDetailsType.TypeName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = conn.Exec(ctx, "reset role")
|
||||
return err
|
||||
}
|
||||
|
|
12
pkg/quote.go
12
pkg/quote.go
|
@ -62,7 +62,7 @@ func mustCollectQuoteEntries(ctx context.Context, conn *Conn, locale *Locale, fi
|
|||
select quote.slug
|
||||
, quote_date
|
||||
, quote_number
|
||||
, coalesce(contact.business_name, '')
|
||||
, coalesce(contact.name, '')
|
||||
, quote.tags
|
||||
, quote.quote_status
|
||||
, isi18n.name
|
||||
|
@ -333,6 +333,7 @@ type quote struct {
|
|||
Date time.Time
|
||||
Quoter taxDetails
|
||||
HasQuotee bool
|
||||
HasTaxDetails bool
|
||||
Quotee taxDetails
|
||||
TermsAndConditions string
|
||||
Notes string
|
||||
|
@ -372,10 +373,9 @@ func mustGetQuote(ctx context.Context, conn *Conn, company *Company, slug string
|
|||
, notes
|
||||
, coalesce(instructions, '')
|
||||
, contact_id is not null
|
||||
, coalesce(business_name, '')
|
||||
, coalesce(business_name, contact.name, '')
|
||||
, contact_tax_details.contact_id is not null
|
||||
, coalesce(vatin::text, '')
|
||||
, coalesce(phone::text, '')
|
||||
, coalesce(email, '')
|
||||
, coalesce(address, '')
|
||||
, coalesce(city, '')
|
||||
, coalesce(province, '')
|
||||
|
@ -387,6 +387,7 @@ func mustGetQuote(ctx context.Context, conn *Conn, company *Company, slug string
|
|||
left join payment_method using (payment_method_id)
|
||||
left join quote_contact using (quote_id)
|
||||
left join contact using (contact_id)
|
||||
left join contact_tax_details using (contact_id)
|
||||
join quote_amount using (quote_id)
|
||||
join currency using (currency_code)
|
||||
where quote.slug = $1`, slug).Scan(
|
||||
|
@ -399,9 +400,8 @@ func mustGetQuote(ctx context.Context, conn *Conn, company *Company, slug string
|
|||
&quo.PaymentInstructions,
|
||||
&quo.HasQuotee,
|
||||
&quo.Quotee.Name,
|
||||
&quo.HasTaxDetails,
|
||||
&quo.Quotee.VATIN,
|
||||
&quo.Quotee.Phone,
|
||||
&quo.Quotee.Email,
|
||||
&quo.Quotee.Address,
|
||||
&quo.Quotee.City,
|
||||
&quo.Quotee.Province,
|
||||
|
|
358
po/ca.po
358
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-06-20 11:35+0200\n"
|
||||
"POT-Creation-Date: 2023-06-30 21:08+0200\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"
|
||||
|
@ -65,7 +65,7 @@ msgstr "Nom"
|
|||
|
||||
#: web/template/invoices/products.gohtml:50
|
||||
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
|
||||
#: web/template/quotes/view.gohtml:71 web/template/products/index.gohtml:42
|
||||
#: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42
|
||||
msgctxt "title"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
@ -95,15 +95,15 @@ msgstr "Desfes"
|
|||
|
||||
#: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67
|
||||
#: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61
|
||||
#: web/template/quotes/view.gohtml:76 web/template/quotes/edit.gohtml:62
|
||||
#: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:62
|
||||
msgctxt "title"
|
||||
msgid "Subtotal"
|
||||
msgstr "Subtotal"
|
||||
|
||||
#: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71
|
||||
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71
|
||||
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:80
|
||||
#: web/template/quotes/view.gohtml:120 web/template/quotes/edit.gohtml:72
|
||||
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:82
|
||||
#: web/template/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:72
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
@ -116,7 +116,7 @@ msgstr "Actualitza"
|
|||
|
||||
#: web/template/invoices/new.gohtml:90 web/template/invoices/edit.gohtml:91
|
||||
#: web/template/quotes/new.gohtml:91 web/template/quotes/edit.gohtml:92
|
||||
#: web/template/contacts/new.gohtml:39 web/template/contacts/edit.gohtml:43
|
||||
#: web/template/contacts/new.gohtml:44 web/template/contacts/edit.gohtml:48
|
||||
#: web/template/expenses/new.gohtml:33 web/template/expenses/edit.gohtml:38
|
||||
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
|
||||
msgctxt "action"
|
||||
|
@ -230,22 +230,22 @@ msgctxt "action"
|
|||
msgid "Download invoice"
|
||||
msgstr "Descarrega factura"
|
||||
|
||||
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:70
|
||||
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:72
|
||||
msgctxt "title"
|
||||
msgid "Concept"
|
||||
msgstr "Concepte"
|
||||
|
||||
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:73
|
||||
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:75
|
||||
msgctxt "title"
|
||||
msgid "Discount"
|
||||
msgstr "Descompte"
|
||||
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:75
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:77
|
||||
msgctxt "title"
|
||||
msgid "Units"
|
||||
msgstr "Unitats"
|
||||
|
||||
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:110
|
||||
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:112
|
||||
msgctxt "title"
|
||||
msgid "Tax Base"
|
||||
msgstr "Base imposable"
|
||||
|
@ -280,7 +280,7 @@ msgctxt "input"
|
|||
msgid "(Max. %s)"
|
||||
msgstr "(Màx. %s)"
|
||||
|
||||
#: web/template/form.gohtml:194
|
||||
#: web/template/form.gohtml:200
|
||||
msgctxt "action"
|
||||
msgid "Filters"
|
||||
msgstr "Filtra"
|
||||
|
@ -382,7 +382,7 @@ msgctxt "action"
|
|||
msgid "Download quotation"
|
||||
msgstr "Descarrega pressupost"
|
||||
|
||||
#: web/template/quotes/view.gohtml:63
|
||||
#: web/template/quotes/view.gohtml:65
|
||||
msgid "Terms and Conditions:"
|
||||
msgstr "Condicions d’acceptació:"
|
||||
|
||||
|
@ -648,7 +648,7 @@ msgctxt "title"
|
|||
msgid "Edit Product “%s”"
|
||||
msgstr "Edició del producte «%s»"
|
||||
|
||||
#: pkg/login.go:37 pkg/profile.go:40 pkg/contacts.go:268
|
||||
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:255
|
||||
msgctxt "input"
|
||||
msgid "Email"
|
||||
msgstr "Correu-e"
|
||||
|
@ -658,11 +658,11 @@ msgctxt "input"
|
|||
msgid "Password"
|
||||
msgstr "Contrasenya"
|
||||
|
||||
#: pkg/login.go:70 pkg/profile.go:89 pkg/contacts.go:365
|
||||
#: pkg/login.go:70 pkg/company.go:283 pkg/profile.go:89
|
||||
msgid "Email can not be empty."
|
||||
msgstr "No podeu deixar el correu-e en blanc."
|
||||
|
||||
#: pkg/login.go:71 pkg/profile.go:90 pkg/contacts.go:366
|
||||
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:385
|
||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
|
||||
|
||||
|
@ -674,16 +674,16 @@ msgstr "No podeu deixar la contrasenya en blanc."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:871
|
||||
#: pkg/contacts.go:135
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
||||
#: pkg/contacts.go:135 pkg/contacts.go:241
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
||||
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
||||
#: pkg/invoices.go:623 pkg/invoices.go:1170 pkg/contacts.go:140
|
||||
#: pkg/contacts.go:325
|
||||
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:140
|
||||
#: pkg/contacts.go:331
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
@ -716,147 +716,250 @@ msgstr "Qualsevol"
|
|||
msgid "Invoices must have at least one of the specified labels."
|
||||
msgstr "Les factures han de tenir com a mínim una de les etiquetes."
|
||||
|
||||
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:885
|
||||
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:923
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripció"
|
||||
|
||||
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:889
|
||||
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:927
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
||||
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:918
|
||||
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:956
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Imposts"
|
||||
|
||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:967
|
||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
||||
#: pkg/contacts.go:378
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podeu deixar el nom en blanc."
|
||||
|
||||
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:968
|
||||
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:1006
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podeu deixar el preu en blanc."
|
||||
|
||||
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:969
|
||||
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:1007
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El preu ha de ser un número major a zero."
|
||||
|
||||
#: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
|
||||
#: pkg/invoices.go:977
|
||||
#: pkg/invoices.go:1015
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Heu seleccionat un impost que no és vàlid."
|
||||
|
||||
#: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
|
||||
#: pkg/invoices.go:978
|
||||
#: pkg/invoices.go:1016
|
||||
msgid "You can only select a tax of each class."
|
||||
msgstr "Només podeu seleccionar un impost de cada classe."
|
||||
|
||||
#: pkg/company.go:102
|
||||
#: pkg/company.go:113
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nom comercial"
|
||||
|
||||
#: pkg/company.go:118 pkg/contacts.go:247
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Telèfon"
|
||||
|
||||
#: pkg/company.go:136 pkg/contacts.go:263
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: pkg/company.go:144 pkg/contacts.go:275
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nom i cognoms"
|
||||
|
||||
#: pkg/company.go:154 pkg/contacts.go:285
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: pkg/company.go:160 pkg/contacts.go:291
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Adreça"
|
||||
|
||||
#: pkg/company.go:169 pkg/contacts.go:300
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Població"
|
||||
|
||||
#: pkg/company.go:175 pkg/contacts.go:306
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Província"
|
||||
|
||||
#: pkg/company.go:181 pkg/contacts.go:312
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Codi postal"
|
||||
|
||||
#: pkg/company.go:190 pkg/contacts.go:321
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: pkg/company.go:200
|
||||
msgctxt "input"
|
||||
msgid "Currency"
|
||||
msgstr "Moneda"
|
||||
|
||||
#: pkg/company.go:109
|
||||
#: pkg/company.go:207
|
||||
msgctxt "input"
|
||||
msgid "Invoice number format"
|
||||
msgstr "Format del número de factura"
|
||||
|
||||
#: pkg/company.go:115
|
||||
#: pkg/company.go:213
|
||||
msgctxt "input"
|
||||
msgid "Next invoice number"
|
||||
msgstr "Següent número de factura"
|
||||
|
||||
#: pkg/company.go:124
|
||||
#: pkg/company.go:222
|
||||
msgctxt "input"
|
||||
msgid "Quotation number format"
|
||||
msgstr "Format del número de pressupost"
|
||||
|
||||
#: pkg/company.go:130
|
||||
#: pkg/company.go:228
|
||||
msgctxt "input"
|
||||
msgid "Next quotation number"
|
||||
msgstr "Següent número de pressupost"
|
||||
|
||||
#: pkg/company.go:139
|
||||
#: pkg/company.go:237
|
||||
msgctxt "input"
|
||||
msgid "Legal disclaimer"
|
||||
msgstr "Nota legal"
|
||||
|
||||
#: pkg/company.go:160
|
||||
#: pkg/company.go:271 pkg/contacts.go:361
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Heu seleccionat un país que no és vàlid."
|
||||
|
||||
#: pkg/company.go:275 pkg/contacts.go:364
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podeu deixar el nom i els cognoms en blanc."
|
||||
|
||||
#: pkg/company.go:276 pkg/contacts.go:365
|
||||
msgid "Business name must have at least two letters."
|
||||
msgstr "Nom i cognoms han de tenir com a mínim dues lletres."
|
||||
|
||||
#: pkg/company.go:277 pkg/contacts.go:366
|
||||
msgid "VAT number can not be empty."
|
||||
msgstr "No podeu deixar el DNI o NIF en blanc."
|
||||
|
||||
#: pkg/company.go:278 pkg/contacts.go:367
|
||||
msgid "This value is not a valid VAT number."
|
||||
msgstr "Aquest valor no és un DNI o NIF vàlid."
|
||||
|
||||
#: pkg/company.go:280
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podeu deixar el telèfon en blanc."
|
||||
|
||||
#: pkg/company.go:281 pkg/contacts.go:382
|
||||
msgid "This value is not a valid phone number."
|
||||
msgstr "Aquest valor no és un telèfon vàlid."
|
||||
|
||||
#: pkg/company.go:287 pkg/contacts.go:388
|
||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||
msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/."
|
||||
|
||||
#: pkg/company.go:289 pkg/contacts.go:369
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podeu deixar l’adreça en blanc."
|
||||
|
||||
#: pkg/company.go:290 pkg/contacts.go:370
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podeu deixar la població en blanc."
|
||||
|
||||
#: pkg/company.go:291 pkg/contacts.go:371
|
||||
msgid "Province can not be empty."
|
||||
msgstr "No podeu deixar la província en blanc."
|
||||
|
||||
#: pkg/company.go:292 pkg/contacts.go:373
|
||||
msgid "Postal code can not be empty."
|
||||
msgstr "No podeu deixar el codi postal en blanc."
|
||||
|
||||
#: pkg/company.go:293 pkg/contacts.go:374
|
||||
msgid "This value is not a valid postal code."
|
||||
msgstr "Aquest valor no és un codi postal vàlid."
|
||||
|
||||
#: pkg/company.go:295
|
||||
msgid "Selected currency is not valid."
|
||||
msgstr "Heu seleccionat una moneda que no és vàlida."
|
||||
|
||||
#: pkg/company.go:161
|
||||
#: pkg/company.go:296
|
||||
msgid "Invoice number format can not be empty."
|
||||
msgstr "No podeu deixar el format del número de factura en blanc."
|
||||
|
||||
#: pkg/company.go:162
|
||||
#: pkg/company.go:297
|
||||
msgid "Next invoice number must be a number greater than zero."
|
||||
msgstr "El següent número de factura ha de ser un número major a zero."
|
||||
|
||||
#: pkg/company.go:163
|
||||
#: pkg/company.go:298
|
||||
msgid "Quotation number format can not be empty."
|
||||
msgstr "No podeu deixar el format del número de pressupost en blanc."
|
||||
|
||||
#: pkg/company.go:164
|
||||
#: pkg/company.go:299
|
||||
msgid "Next quotation number must be a number greater than zero."
|
||||
msgstr "El següent número de pressupost ha de ser un número major a zero."
|
||||
|
||||
#: pkg/company.go:427
|
||||
#: pkg/company.go:563
|
||||
msgctxt "input"
|
||||
msgid "Tax name"
|
||||
msgstr "Nom impost"
|
||||
|
||||
#: pkg/company.go:433
|
||||
#: pkg/company.go:569
|
||||
msgctxt "input"
|
||||
msgid "Tax Class"
|
||||
msgstr "Classe d’impost"
|
||||
|
||||
#: pkg/company.go:436
|
||||
#: pkg/company.go:572
|
||||
msgid "Select a tax class"
|
||||
msgstr "Escolliu una classe d’impost"
|
||||
|
||||
#: pkg/company.go:440
|
||||
#: pkg/company.go:576
|
||||
msgctxt "input"
|
||||
msgid "Rate (%)"
|
||||
msgstr "Percentatge"
|
||||
|
||||
#: pkg/company.go:463
|
||||
#: pkg/company.go:599
|
||||
msgid "Tax name can not be empty."
|
||||
msgstr "No podeu deixar el nom de l’impost en blanc."
|
||||
|
||||
#: pkg/company.go:464
|
||||
#: pkg/company.go:600
|
||||
msgid "Selected tax class is not valid."
|
||||
msgstr "Heu seleccionat una classe d’impost que no és vàlida."
|
||||
|
||||
#: pkg/company.go:465
|
||||
#: pkg/company.go:601
|
||||
msgid "Tax rate can not be empty."
|
||||
msgstr "No podeu deixar percentatge en blanc."
|
||||
|
||||
#: pkg/company.go:466
|
||||
#: pkg/company.go:602
|
||||
msgid "Tax rate must be an integer between -99 and 99."
|
||||
msgstr "El percentatge ha de ser entre -99 i 99."
|
||||
|
||||
#: pkg/company.go:529
|
||||
#: pkg/company.go:665
|
||||
msgctxt "input"
|
||||
msgid "Payment method name"
|
||||
msgstr "Nom del mètode de pagament"
|
||||
|
||||
#: pkg/company.go:535
|
||||
#: pkg/company.go:671
|
||||
msgctxt "input"
|
||||
msgid "Instructions"
|
||||
msgstr "Instruccions"
|
||||
|
||||
#: pkg/company.go:553
|
||||
#: pkg/company.go:689
|
||||
msgid "Payment method name can not be empty."
|
||||
msgstr "No podeu deixar el nom del mètode de pagament en blanc."
|
||||
|
||||
#: pkg/company.go:554
|
||||
#: pkg/company.go:690
|
||||
msgid "Payment instructions can not be empty."
|
||||
msgstr "No podeu deixar les instruccions de pagament en blanc."
|
||||
|
||||
#: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:606
|
||||
#: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:640
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
@ -901,8 +1004,8 @@ msgstr "Els pressuposts han de tenir com a mínim una de les etiquetes."
|
|||
msgid "quotations.zip"
|
||||
msgstr "pressuposts.zip"
|
||||
|
||||
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:555
|
||||
#: pkg/invoices.go:1145 pkg/invoices.go:1153
|
||||
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:589
|
||||
#: pkg/invoices.go:1183 pkg/invoices.go:1191
|
||||
msgid "Invalid action"
|
||||
msgstr "Acció invàlida."
|
||||
|
||||
|
@ -920,12 +1023,12 @@ msgctxt "input"
|
|||
msgid "Terms and conditions"
|
||||
msgstr "Condicions d’acceptació"
|
||||
|
||||
#: pkg/quote.go:625 pkg/invoices.go:618
|
||||
#: pkg/quote.go:625 pkg/invoices.go:652
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notes"
|
||||
|
||||
#: pkg/quote.go:634 pkg/invoices.go:628
|
||||
#: pkg/quote.go:634 pkg/invoices.go:662
|
||||
msgctxt "input"
|
||||
msgid "Payment Method"
|
||||
msgstr "Mètode de pagament"
|
||||
|
@ -938,7 +1041,7 @@ msgstr "Escolliu un mètode de pagament."
|
|||
msgid "Selected quotation status is not valid."
|
||||
msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
|
||||
|
||||
#: pkg/quote.go:673 pkg/invoices.go:665
|
||||
#: pkg/quote.go:673 pkg/invoices.go:699
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "Heu seleccionat un client que no és vàlid."
|
||||
|
||||
|
@ -950,21 +1053,21 @@ msgstr "No podeu deixar la data del pressupost en blanc."
|
|||
msgid "Quotation date must be a valid date."
|
||||
msgstr "La data del pressupost ha de ser vàlida."
|
||||
|
||||
#: pkg/quote.go:679 pkg/invoices.go:669
|
||||
#: pkg/quote.go:679 pkg/invoices.go:703
|
||||
msgid "Selected payment method is not valid."
|
||||
msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
|
||||
|
||||
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:861 pkg/invoices.go:866
|
||||
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:899 pkg/invoices.go:904
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/quote.go:851 pkg/invoices.go:899
|
||||
#: pkg/quote.go:851 pkg/invoices.go:937
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantitat"
|
||||
|
||||
#: pkg/quote.go:860 pkg/invoices.go:908
|
||||
#: pkg/quote.go:860 pkg/invoices.go:946
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descompte (%)"
|
||||
|
@ -973,23 +1076,23 @@ msgstr "Descompte (%)"
|
|||
msgid "Quotation product ID must be a number greater than zero."
|
||||
msgstr "L’ID del producte de pressupost ha de ser un número major a zero."
|
||||
|
||||
#: pkg/quote.go:917 pkg/invoices.go:965
|
||||
#: pkg/quote.go:917 pkg/invoices.go:1003
|
||||
msgid "Product ID must be a positive number or zero."
|
||||
msgstr "L’ID del producte ha de ser un número positiu o zero."
|
||||
|
||||
#: pkg/quote.go:923 pkg/invoices.go:971
|
||||
#: pkg/quote.go:923 pkg/invoices.go:1009
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podeu deixar la quantitat en blanc."
|
||||
|
||||
#: pkg/quote.go:924 pkg/invoices.go:972
|
||||
#: pkg/quote.go:924 pkg/invoices.go:1010
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La quantitat ha de ser un número major a zero."
|
||||
|
||||
#: pkg/quote.go:926 pkg/invoices.go:974
|
||||
#: pkg/quote.go:926 pkg/invoices.go:1012
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podeu deixar el descompte en blanc."
|
||||
|
||||
#: pkg/quote.go:927 pkg/invoices.go:975
|
||||
#: pkg/quote.go:927 pkg/invoices.go:1013
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
|
||||
|
||||
|
@ -1070,7 +1173,7 @@ msgctxt "input"
|
|||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:161 pkg/invoices.go:612
|
||||
#: pkg/expenses.go:161 pkg/invoices.go:646
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Data de factura"
|
||||
|
@ -1089,7 +1192,7 @@ msgstr "Fitxer"
|
|||
msgid "Selected contact is not valid."
|
||||
msgstr "Heu seleccionat un contacte que no és vàlid."
|
||||
|
||||
#: pkg/expenses.go:212 pkg/invoices.go:667
|
||||
#: pkg/expenses.go:212 pkg/invoices.go:701
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La data de facturació ha de ser vàlida."
|
||||
|
||||
|
@ -1110,142 +1213,49 @@ msgctxt "input"
|
|||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:600
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:634
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estat de la factura"
|
||||
|
||||
#: pkg/invoices.go:448
|
||||
#: pkg/invoices.go:482
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escolliu un client a facturar."
|
||||
|
||||
#: pkg/invoices.go:549
|
||||
#: pkg/invoices.go:583
|
||||
msgid "invoices.zip"
|
||||
msgstr "factures.zip"
|
||||
|
||||
#: pkg/invoices.go:664
|
||||
#: pkg/invoices.go:698
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "Heu seleccionat un estat de factura que no és vàlid."
|
||||
|
||||
#: pkg/invoices.go:666
|
||||
#: pkg/invoices.go:700
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podeu deixar la data de la factura en blanc."
|
||||
|
||||
#: pkg/invoices.go:802
|
||||
#: pkg/invoices.go:836
|
||||
#, c-format
|
||||
msgid "Re: quotation #%s of %s"
|
||||
msgstr "Ref: pressupost núm. %s del %s"
|
||||
|
||||
#: pkg/invoices.go:803
|
||||
#: pkg/invoices.go:837
|
||||
msgctxt "to_char"
|
||||
msgid "MM/DD/YYYY"
|
||||
msgstr "DD/MM/YYYY"
|
||||
|
||||
#: pkg/invoices.go:962
|
||||
#: pkg/invoices.go:1000
|
||||
msgid "Invoice product ID must be a number greater than zero."
|
||||
msgstr "L’ID del producte de factura ha de ser un número major a zero."
|
||||
|
||||
#: pkg/contacts.go:238
|
||||
#: pkg/contacts.go:271
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nom i cognoms"
|
||||
msgid "Need to input tax details"
|
||||
msgstr "Necessito poder facturar aquest contacte"
|
||||
|
||||
#: pkg/contacts.go:248
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: pkg/contacts.go:254
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nom comercial"
|
||||
|
||||
#: pkg/contacts.go:259
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Telèfon"
|
||||
|
||||
#: pkg/contacts.go:277
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: pkg/contacts.go:285
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Adreça"
|
||||
|
||||
#: pkg/contacts.go:294
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Població"
|
||||
|
||||
#: pkg/contacts.go:300
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Província"
|
||||
|
||||
#: pkg/contacts.go:306
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Codi postal"
|
||||
|
||||
#: pkg/contacts.go:315
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: pkg/contacts.go:353
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Heu seleccionat un país que no és vàlid."
|
||||
|
||||
#: pkg/contacts.go:357
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podeu deixar el nom i els cognoms en blanc."
|
||||
|
||||
#: pkg/contacts.go:358
|
||||
msgid "Business name must have at least two letters."
|
||||
msgstr "Nom i cognoms han de tenir com a mínim dues lletres."
|
||||
|
||||
#: pkg/contacts.go:359
|
||||
msgid "VAT number can not be empty."
|
||||
msgstr "No podeu deixar el DNI o NIF en blanc."
|
||||
|
||||
#: pkg/contacts.go:360
|
||||
msgid "This value is not a valid VAT number."
|
||||
msgstr "Aquest valor no és un DNI o NIF vàlid."
|
||||
|
||||
#: pkg/contacts.go:362
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podeu deixar el telèfon en blanc."
|
||||
|
||||
#: pkg/contacts.go:363
|
||||
msgid "This value is not a valid phone number."
|
||||
msgstr "Aquest valor no és un telèfon vàlid."
|
||||
|
||||
#: pkg/contacts.go:369
|
||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||
msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/."
|
||||
|
||||
#: pkg/contacts.go:371
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podeu deixar l’adreça en blanc."
|
||||
|
||||
#: pkg/contacts.go:372
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podeu deixar la població en blanc."
|
||||
|
||||
#: pkg/contacts.go:373
|
||||
msgid "Province can not be empty."
|
||||
msgstr "No podeu deixar la província en blanc."
|
||||
|
||||
#: pkg/contacts.go:374
|
||||
msgid "Postal code can not be empty."
|
||||
msgstr "No podeu deixar el codi postal en blanc."
|
||||
|
||||
#: pkg/contacts.go:375
|
||||
msgid "This value is not a valid postal code."
|
||||
msgstr "Aquest valor no és un codi postal vàlid."
|
||||
#: pkg/contacts.go:379
|
||||
msgid "Name must have at least two letters."
|
||||
msgstr "El nom ha de tenir com a mínim dues lletres."
|
||||
|
||||
#~ msgctxt "action"
|
||||
#~ msgid "Update contact"
|
||||
|
|
358
po/es.po
358
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-06-20 11:35+0200\n"
|
||||
"POT-Creation-Date: 2023-06-30 21:08+0200\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"
|
||||
|
@ -65,7 +65,7 @@ msgstr "Nombre"
|
|||
|
||||
#: web/template/invoices/products.gohtml:50
|
||||
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
|
||||
#: web/template/quotes/view.gohtml:71 web/template/products/index.gohtml:42
|
||||
#: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42
|
||||
msgctxt "title"
|
||||
msgid "Price"
|
||||
msgstr "Precio"
|
||||
|
@ -95,15 +95,15 @@ msgstr "Deshacer"
|
|||
|
||||
#: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67
|
||||
#: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61
|
||||
#: web/template/quotes/view.gohtml:76 web/template/quotes/edit.gohtml:62
|
||||
#: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:62
|
||||
msgctxt "title"
|
||||
msgid "Subtotal"
|
||||
msgstr "Subtotal"
|
||||
|
||||
#: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71
|
||||
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71
|
||||
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:80
|
||||
#: web/template/quotes/view.gohtml:120 web/template/quotes/edit.gohtml:72
|
||||
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:82
|
||||
#: web/template/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:72
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
@ -116,7 +116,7 @@ msgstr "Actualizar"
|
|||
|
||||
#: web/template/invoices/new.gohtml:90 web/template/invoices/edit.gohtml:91
|
||||
#: web/template/quotes/new.gohtml:91 web/template/quotes/edit.gohtml:92
|
||||
#: web/template/contacts/new.gohtml:39 web/template/contacts/edit.gohtml:43
|
||||
#: web/template/contacts/new.gohtml:44 web/template/contacts/edit.gohtml:48
|
||||
#: web/template/expenses/new.gohtml:33 web/template/expenses/edit.gohtml:38
|
||||
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
|
||||
msgctxt "action"
|
||||
|
@ -230,22 +230,22 @@ msgctxt "action"
|
|||
msgid "Download invoice"
|
||||
msgstr "Descargar factura"
|
||||
|
||||
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:70
|
||||
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:72
|
||||
msgctxt "title"
|
||||
msgid "Concept"
|
||||
msgstr "Concepto"
|
||||
|
||||
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:73
|
||||
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:75
|
||||
msgctxt "title"
|
||||
msgid "Discount"
|
||||
msgstr "Descuento"
|
||||
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:75
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:77
|
||||
msgctxt "title"
|
||||
msgid "Units"
|
||||
msgstr "Unidades"
|
||||
|
||||
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:110
|
||||
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:112
|
||||
msgctxt "title"
|
||||
msgid "Tax Base"
|
||||
msgstr "Base imponible"
|
||||
|
@ -280,7 +280,7 @@ msgctxt "input"
|
|||
msgid "(Max. %s)"
|
||||
msgstr "(Máx. %s)"
|
||||
|
||||
#: web/template/form.gohtml:194
|
||||
#: web/template/form.gohtml:200
|
||||
msgctxt "action"
|
||||
msgid "Filters"
|
||||
msgstr "Filtrar"
|
||||
|
@ -382,7 +382,7 @@ msgctxt "action"
|
|||
msgid "Download quotation"
|
||||
msgstr "Descargar presupuesto"
|
||||
|
||||
#: web/template/quotes/view.gohtml:63
|
||||
#: web/template/quotes/view.gohtml:65
|
||||
msgid "Terms and Conditions:"
|
||||
msgstr "Condiciones de aceptación:"
|
||||
|
||||
|
@ -648,7 +648,7 @@ msgctxt "title"
|
|||
msgid "Edit Product “%s”"
|
||||
msgstr "Edición del producto «%s»"
|
||||
|
||||
#: pkg/login.go:37 pkg/profile.go:40 pkg/contacts.go:268
|
||||
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:255
|
||||
msgctxt "input"
|
||||
msgid "Email"
|
||||
msgstr "Correo-e"
|
||||
|
@ -658,11 +658,11 @@ msgctxt "input"
|
|||
msgid "Password"
|
||||
msgstr "Contraseña"
|
||||
|
||||
#: pkg/login.go:70 pkg/profile.go:89 pkg/contacts.go:365
|
||||
#: pkg/login.go:70 pkg/company.go:283 pkg/profile.go:89
|
||||
msgid "Email can not be empty."
|
||||
msgstr "No podéis dejar el correo-e en blanco."
|
||||
|
||||
#: pkg/login.go:71 pkg/profile.go:90 pkg/contacts.go:366
|
||||
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:385
|
||||
msgid "This value is not a valid email. It should be like name@domain.com."
|
||||
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
|
||||
|
||||
|
@ -674,16 +674,16 @@ msgstr "No podéis dejar la contraseña en blanco."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nombre de usuario o contraseña inválido."
|
||||
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:871
|
||||
#: pkg/contacts.go:135
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
||||
#: pkg/contacts.go:135 pkg/contacts.go:241
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
||||
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
||||
#: pkg/invoices.go:623 pkg/invoices.go:1170 pkg/contacts.go:140
|
||||
#: pkg/contacts.go:325
|
||||
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:140
|
||||
#: pkg/contacts.go:331
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
@ -716,147 +716,250 @@ msgstr "Cualquiera"
|
|||
msgid "Invoices must have at least one of the specified labels."
|
||||
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
|
||||
|
||||
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:885
|
||||
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:923
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:889
|
||||
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:927
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Precio"
|
||||
|
||||
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:918
|
||||
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:956
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Impuestos"
|
||||
|
||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:967
|
||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
||||
#: pkg/contacts.go:378
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podéis dejar el nombre en blanco."
|
||||
|
||||
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:968
|
||||
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:1006
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podéis dejar el precio en blanco."
|
||||
|
||||
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:969
|
||||
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:1007
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El precio tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
|
||||
#: pkg/invoices.go:977
|
||||
#: pkg/invoices.go:1015
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Habéis escogido un impuesto que no es válido."
|
||||
|
||||
#: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
|
||||
#: pkg/invoices.go:978
|
||||
#: pkg/invoices.go:1016
|
||||
msgid "You can only select a tax of each class."
|
||||
msgstr "Solo podéis escoger un impuesto de cada clase."
|
||||
|
||||
#: pkg/company.go:102
|
||||
#: pkg/company.go:113
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nombre comercial"
|
||||
|
||||
#: pkg/company.go:118 pkg/contacts.go:247
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Teléfono"
|
||||
|
||||
#: pkg/company.go:136 pkg/contacts.go:263
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: pkg/company.go:144 pkg/contacts.go:275
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nombre y apellidos"
|
||||
|
||||
#: pkg/company.go:154 pkg/contacts.go:285
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: pkg/company.go:160 pkg/contacts.go:291
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Dirección"
|
||||
|
||||
#: pkg/company.go:169 pkg/contacts.go:300
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Población"
|
||||
|
||||
#: pkg/company.go:175 pkg/contacts.go:306
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Provincia"
|
||||
|
||||
#: pkg/company.go:181 pkg/contacts.go:312
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Código postal"
|
||||
|
||||
#: pkg/company.go:190 pkg/contacts.go:321
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: pkg/company.go:200
|
||||
msgctxt "input"
|
||||
msgid "Currency"
|
||||
msgstr "Moneda"
|
||||
|
||||
#: pkg/company.go:109
|
||||
#: pkg/company.go:207
|
||||
msgctxt "input"
|
||||
msgid "Invoice number format"
|
||||
msgstr "Formato del número de factura"
|
||||
|
||||
#: pkg/company.go:115
|
||||
#: pkg/company.go:213
|
||||
msgctxt "input"
|
||||
msgid "Next invoice number"
|
||||
msgstr "Siguiente número de factura"
|
||||
|
||||
#: pkg/company.go:124
|
||||
#: pkg/company.go:222
|
||||
msgctxt "input"
|
||||
msgid "Quotation number format"
|
||||
msgstr "Formato del número de presupuesto"
|
||||
|
||||
#: pkg/company.go:130
|
||||
#: pkg/company.go:228
|
||||
msgctxt "input"
|
||||
msgid "Next quotation number"
|
||||
msgstr "Siguiente número de presupuesto"
|
||||
|
||||
#: pkg/company.go:139
|
||||
#: pkg/company.go:237
|
||||
msgctxt "input"
|
||||
msgid "Legal disclaimer"
|
||||
msgstr "Nota legal"
|
||||
|
||||
#: pkg/company.go:160
|
||||
#: pkg/company.go:271 pkg/contacts.go:361
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Habéis escogido un país que no es válido."
|
||||
|
||||
#: pkg/company.go:275 pkg/contacts.go:364
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podéis dejar el nombre y los apellidos en blanco."
|
||||
|
||||
#: pkg/company.go:276 pkg/contacts.go:365
|
||||
msgid "Business name must have at least two letters."
|
||||
msgstr "El nombre y los apellidos deben contener como mínimo dos letras."
|
||||
|
||||
#: pkg/company.go:277 pkg/contacts.go:366
|
||||
msgid "VAT number can not be empty."
|
||||
msgstr "No podéis dejar el DNI o NIF en blanco."
|
||||
|
||||
#: pkg/company.go:278 pkg/contacts.go:367
|
||||
msgid "This value is not a valid VAT number."
|
||||
msgstr "Este valor no es un DNI o NIF válido."
|
||||
|
||||
#: pkg/company.go:280
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podéis dejar el teléfono en blanco."
|
||||
|
||||
#: pkg/company.go:281 pkg/contacts.go:382
|
||||
msgid "This value is not a valid phone number."
|
||||
msgstr "Este valor no es un teléfono válido."
|
||||
|
||||
#: pkg/company.go:287 pkg/contacts.go:388
|
||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||
msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/."
|
||||
|
||||
#: pkg/company.go:289 pkg/contacts.go:369
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podéis dejar la dirección en blanco."
|
||||
|
||||
#: pkg/company.go:290 pkg/contacts.go:370
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podéis dejar la población en blanco."
|
||||
|
||||
#: pkg/company.go:291 pkg/contacts.go:371
|
||||
msgid "Province can not be empty."
|
||||
msgstr "No podéis dejar la provincia en blanco."
|
||||
|
||||
#: pkg/company.go:292 pkg/contacts.go:373
|
||||
msgid "Postal code can not be empty."
|
||||
msgstr "No podéis dejar el código postal en blanco."
|
||||
|
||||
#: pkg/company.go:293 pkg/contacts.go:374
|
||||
msgid "This value is not a valid postal code."
|
||||
msgstr "Este valor no es un código postal válido válido."
|
||||
|
||||
#: pkg/company.go:295
|
||||
msgid "Selected currency is not valid."
|
||||
msgstr "Habéis escogido una moneda que no es válida."
|
||||
|
||||
#: pkg/company.go:161
|
||||
#: pkg/company.go:296
|
||||
msgid "Invoice number format can not be empty."
|
||||
msgstr "No podéis dejar el formato del número de factura en blanco."
|
||||
|
||||
#: pkg/company.go:162
|
||||
#: pkg/company.go:297
|
||||
msgid "Next invoice number must be a number greater than zero."
|
||||
msgstr "El siguiente número de factura tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/company.go:163
|
||||
#: pkg/company.go:298
|
||||
msgid "Quotation number format can not be empty."
|
||||
msgstr "No podéis dejar el formato del número de presupuesto en blanco."
|
||||
|
||||
#: pkg/company.go:164
|
||||
#: pkg/company.go:299
|
||||
msgid "Next quotation number must be a number greater than zero."
|
||||
msgstr "El siguiente número de presupuesto tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/company.go:427
|
||||
#: pkg/company.go:563
|
||||
msgctxt "input"
|
||||
msgid "Tax name"
|
||||
msgstr "Nombre impuesto"
|
||||
|
||||
#: pkg/company.go:433
|
||||
#: pkg/company.go:569
|
||||
msgctxt "input"
|
||||
msgid "Tax Class"
|
||||
msgstr "Clase de impuesto"
|
||||
|
||||
#: pkg/company.go:436
|
||||
#: pkg/company.go:572
|
||||
msgid "Select a tax class"
|
||||
msgstr "Escoged una clase de impuesto"
|
||||
|
||||
#: pkg/company.go:440
|
||||
#: pkg/company.go:576
|
||||
msgctxt "input"
|
||||
msgid "Rate (%)"
|
||||
msgstr "Porcentaje"
|
||||
|
||||
#: pkg/company.go:463
|
||||
#: pkg/company.go:599
|
||||
msgid "Tax name can not be empty."
|
||||
msgstr "No podéis dejar el nombre del impuesto en blanco."
|
||||
|
||||
#: pkg/company.go:464
|
||||
#: pkg/company.go:600
|
||||
msgid "Selected tax class is not valid."
|
||||
msgstr "Habéis escogido una clase impuesto que no es válida."
|
||||
|
||||
#: pkg/company.go:465
|
||||
#: pkg/company.go:601
|
||||
msgid "Tax rate can not be empty."
|
||||
msgstr "No podéis dejar el porcentaje en blanco."
|
||||
|
||||
#: pkg/company.go:466
|
||||
#: pkg/company.go:602
|
||||
msgid "Tax rate must be an integer between -99 and 99."
|
||||
msgstr "El porcentaje tiene que estar entre -99 y 99."
|
||||
|
||||
#: pkg/company.go:529
|
||||
#: pkg/company.go:665
|
||||
msgctxt "input"
|
||||
msgid "Payment method name"
|
||||
msgstr "Nombre del método de pago"
|
||||
|
||||
#: pkg/company.go:535
|
||||
#: pkg/company.go:671
|
||||
msgctxt "input"
|
||||
msgid "Instructions"
|
||||
msgstr "Instrucciones"
|
||||
|
||||
#: pkg/company.go:553
|
||||
#: pkg/company.go:689
|
||||
msgid "Payment method name can not be empty."
|
||||
msgstr "No podéis dejar el nombre del método de pago en blanco."
|
||||
|
||||
#: pkg/company.go:554
|
||||
#: pkg/company.go:690
|
||||
msgid "Payment instructions can not be empty."
|
||||
msgstr "No podéis dejar las instrucciones de pago en blanco."
|
||||
|
||||
#: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:606
|
||||
#: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:640
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Cliente"
|
||||
|
@ -901,8 +1004,8 @@ msgstr "Los presupuestos deben tener como mínimo una de las etiquetas."
|
|||
msgid "quotations.zip"
|
||||
msgstr "presupuestos.zip"
|
||||
|
||||
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:555
|
||||
#: pkg/invoices.go:1145 pkg/invoices.go:1153
|
||||
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:589
|
||||
#: pkg/invoices.go:1183 pkg/invoices.go:1191
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción inválida."
|
||||
|
||||
|
@ -920,12 +1023,12 @@ msgctxt "input"
|
|||
msgid "Terms and conditions"
|
||||
msgstr "Condiciones de aceptación"
|
||||
|
||||
#: pkg/quote.go:625 pkg/invoices.go:618
|
||||
#: pkg/quote.go:625 pkg/invoices.go:652
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notas"
|
||||
|
||||
#: pkg/quote.go:634 pkg/invoices.go:628
|
||||
#: pkg/quote.go:634 pkg/invoices.go:662
|
||||
msgctxt "input"
|
||||
msgid "Payment Method"
|
||||
msgstr "Método de pago"
|
||||
|
@ -938,7 +1041,7 @@ msgstr "Escoged un método e pago."
|
|||
msgid "Selected quotation status is not valid."
|
||||
msgstr "Habéis escogido un estado de presupuesto que no es válido."
|
||||
|
||||
#: pkg/quote.go:673 pkg/invoices.go:665
|
||||
#: pkg/quote.go:673 pkg/invoices.go:699
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "Habéis escogido un cliente que no es válido."
|
||||
|
||||
|
@ -950,21 +1053,21 @@ msgstr "No podéis dejar la fecha del presupuesto en blanco."
|
|||
msgid "Quotation date must be a valid date."
|
||||
msgstr "La fecha de presupuesto debe ser válida."
|
||||
|
||||
#: pkg/quote.go:679 pkg/invoices.go:669
|
||||
#: pkg/quote.go:679 pkg/invoices.go:703
|
||||
msgid "Selected payment method is not valid."
|
||||
msgstr "Habéis escogido un método de pago que no es válido."
|
||||
|
||||
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:861 pkg/invoices.go:866
|
||||
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:899 pkg/invoices.go:904
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/quote.go:851 pkg/invoices.go:899
|
||||
#: pkg/quote.go:851 pkg/invoices.go:937
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Cantidad"
|
||||
|
||||
#: pkg/quote.go:860 pkg/invoices.go:908
|
||||
#: pkg/quote.go:860 pkg/invoices.go:946
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descuento (%)"
|
||||
|
@ -973,23 +1076,23 @@ msgstr "Descuento (%)"
|
|||
msgid "Quotation product ID must be a number greater than zero."
|
||||
msgstr "El ID de producto de presupuesto tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/quote.go:917 pkg/invoices.go:965
|
||||
#: pkg/quote.go:917 pkg/invoices.go:1003
|
||||
msgid "Product ID must be a positive number or zero."
|
||||
msgstr "El ID de producto tiene que ser un número positivo o cero."
|
||||
|
||||
#: pkg/quote.go:923 pkg/invoices.go:971
|
||||
#: pkg/quote.go:923 pkg/invoices.go:1009
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podéis dejar la cantidad en blanco."
|
||||
|
||||
#: pkg/quote.go:924 pkg/invoices.go:972
|
||||
#: pkg/quote.go:924 pkg/invoices.go:1010
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La cantidad tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/quote.go:926 pkg/invoices.go:974
|
||||
#: pkg/quote.go:926 pkg/invoices.go:1012
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podéis dejar el descuento en blanco."
|
||||
|
||||
#: pkg/quote.go:927 pkg/invoices.go:975
|
||||
#: pkg/quote.go:927 pkg/invoices.go:1013
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
|
||||
|
||||
|
@ -1070,7 +1173,7 @@ msgctxt "input"
|
|||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:161 pkg/invoices.go:612
|
||||
#: pkg/expenses.go:161 pkg/invoices.go:646
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Fecha de factura"
|
||||
|
@ -1089,7 +1192,7 @@ msgstr "Archivo"
|
|||
msgid "Selected contact is not valid."
|
||||
msgstr "Habéis escogido un contacto que no es válido."
|
||||
|
||||
#: pkg/expenses.go:212 pkg/invoices.go:667
|
||||
#: pkg/expenses.go:212 pkg/invoices.go:701
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La fecha de factura debe ser válida."
|
||||
|
||||
|
@ -1110,142 +1213,49 @@ msgctxt "input"
|
|||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:600
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:634
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estado de la factura"
|
||||
|
||||
#: pkg/invoices.go:448
|
||||
#: pkg/invoices.go:482
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escoged un cliente a facturar."
|
||||
|
||||
#: pkg/invoices.go:549
|
||||
#: pkg/invoices.go:583
|
||||
msgid "invoices.zip"
|
||||
msgstr "facturas.zip"
|
||||
|
||||
#: pkg/invoices.go:664
|
||||
#: pkg/invoices.go:698
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "Habéis escogido un estado de factura que no es válido."
|
||||
|
||||
#: pkg/invoices.go:666
|
||||
#: pkg/invoices.go:700
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podéis dejar la fecha de la factura en blanco."
|
||||
|
||||
#: pkg/invoices.go:802
|
||||
#: pkg/invoices.go:836
|
||||
#, c-format
|
||||
msgid "Re: quotation #%s of %s"
|
||||
msgstr "Ref: presupuesto n.º %s del %s"
|
||||
|
||||
#: pkg/invoices.go:803
|
||||
#: pkg/invoices.go:837
|
||||
msgctxt "to_char"
|
||||
msgid "MM/DD/YYYY"
|
||||
msgstr "DD/MM/YYYY"
|
||||
|
||||
#: pkg/invoices.go:962
|
||||
#: pkg/invoices.go:1000
|
||||
msgid "Invoice product ID must be a number greater than zero."
|
||||
msgstr "El ID de producto de factura tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/contacts.go:238
|
||||
#: pkg/contacts.go:271
|
||||
msgctxt "input"
|
||||
msgid "Business name"
|
||||
msgstr "Nombre y apellidos"
|
||||
msgid "Need to input tax details"
|
||||
msgstr "Necesito facturar este contacto"
|
||||
|
||||
#: pkg/contacts.go:248
|
||||
msgctxt "input"
|
||||
msgid "VAT number"
|
||||
msgstr "DNI / NIF"
|
||||
|
||||
#: pkg/contacts.go:254
|
||||
msgctxt "input"
|
||||
msgid "Trade name"
|
||||
msgstr "Nombre comercial"
|
||||
|
||||
#: pkg/contacts.go:259
|
||||
msgctxt "input"
|
||||
msgid "Phone"
|
||||
msgstr "Teléfono"
|
||||
|
||||
#: pkg/contacts.go:277
|
||||
msgctxt "input"
|
||||
msgid "Web"
|
||||
msgstr "Web"
|
||||
|
||||
#: pkg/contacts.go:285
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
msgstr "Dirección"
|
||||
|
||||
#: pkg/contacts.go:294
|
||||
msgctxt "input"
|
||||
msgid "City"
|
||||
msgstr "Población"
|
||||
|
||||
#: pkg/contacts.go:300
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Provincia"
|
||||
|
||||
#: pkg/contacts.go:306
|
||||
msgctxt "input"
|
||||
msgid "Postal code"
|
||||
msgstr "Código postal"
|
||||
|
||||
#: pkg/contacts.go:315
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
msgstr "País"
|
||||
|
||||
#: pkg/contacts.go:353
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Habéis escogido un país que no es válido."
|
||||
|
||||
#: pkg/contacts.go:357
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podéis dejar el nombre y los apellidos en blanco."
|
||||
|
||||
#: pkg/contacts.go:358
|
||||
msgid "Business name must have at least two letters."
|
||||
msgstr "El nombre y los apellidos deben contener como mínimo dos letras."
|
||||
|
||||
#: pkg/contacts.go:359
|
||||
msgid "VAT number can not be empty."
|
||||
msgstr "No podéis dejar el DNI o NIF en blanco."
|
||||
|
||||
#: pkg/contacts.go:360
|
||||
msgid "This value is not a valid VAT number."
|
||||
msgstr "Este valor no es un DNI o NIF válido."
|
||||
|
||||
#: pkg/contacts.go:362
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podéis dejar el teléfono en blanco."
|
||||
|
||||
#: pkg/contacts.go:363
|
||||
msgid "This value is not a valid phone number."
|
||||
msgstr "Este valor no es un teléfono válido."
|
||||
|
||||
#: pkg/contacts.go:369
|
||||
msgid "This value is not a valid web address. It should be like https://domain.com/."
|
||||
msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/."
|
||||
|
||||
#: pkg/contacts.go:371
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podéis dejar la dirección en blanco."
|
||||
|
||||
#: pkg/contacts.go:372
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podéis dejar la población en blanco."
|
||||
|
||||
#: pkg/contacts.go:373
|
||||
msgid "Province can not be empty."
|
||||
msgstr "No podéis dejar la provincia en blanco."
|
||||
|
||||
#: pkg/contacts.go:374
|
||||
msgid "Postal code can not be empty."
|
||||
msgstr "No podéis dejar el código postal en blanco."
|
||||
|
||||
#: pkg/contacts.go:375
|
||||
msgid "This value is not a valid postal code."
|
||||
msgstr "Este valor no es un código postal válido válido."
|
||||
#: pkg/contacts.go:379
|
||||
msgid "Name must have at least two letters."
|
||||
msgstr "El nombre debe contener como mínimo dos letras."
|
||||
|
||||
#~ msgctxt "action"
|
||||
#~ msgid "Update contact"
|
||||
|
|
|
@ -1,7 +1,39 @@
|
|||
-- Revert numerus:add_contact from pg
|
||||
-- Deploy numerus:add_contact to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: extension_vat
|
||||
-- requires: email
|
||||
-- requires: extension_pg_libphonenumber
|
||||
-- requires: extension_uri
|
||||
-- requires: country_code
|
||||
-- requires: contact
|
||||
-- requires: tag_name
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.add_contact(integer, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[]);
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_contact(company_id integer, business_name text, vatin text, trade_name text, phone text, email email, web uri, address text, city text, province text, postal_code text, country_code country_code, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
cslug uuid;
|
||||
begin
|
||||
insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags)
|
||||
values (add_contact.company_id, add_contact.business_name, (add_contact.country_code || add_contact.vatin)::vatin, add_contact.trade_name, parse_packed_phone_number(add_contact.phone, add_contact.country_code), add_contact.email, add_contact.web, add_contact.address, add_contact.city, add_contact.province, add_contact.postal_code, add_contact.country_code, add_contact.tags)
|
||||
returning contact_id, slug
|
||||
into cid, cslug;
|
||||
|
||||
return cslug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) from public;
|
||||
grant execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to invoicer;
|
||||
grant execute on function add_contact(integer, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to admin;
|
||||
|
||||
|
||||
drop function if exists add_contact(integer, text, text, email, uri, tax_details, tag_name[]);
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:add_contact from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.add_contact(integer, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[]);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Revert numerus:contact_email from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
alter table contact
|
||||
add column email email
|
||||
;
|
||||
|
||||
update contact
|
||||
set email = email.email
|
||||
from contact_email as email
|
||||
where email.contact_id = email.contact_id
|
||||
;
|
||||
|
||||
alter table contact
|
||||
alter column email set not null
|
||||
;
|
||||
|
||||
drop table if exists contact_email;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Revert numerus:contact_phone from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
alter table contact
|
||||
add column phone packed_phone_number
|
||||
;
|
||||
|
||||
update contact
|
||||
set phone = phone.phone
|
||||
from contact_phone as phone
|
||||
where phone.contact_id = contact.contact_id
|
||||
;
|
||||
|
||||
alter table contact
|
||||
alter column phone set not null
|
||||
;
|
||||
|
||||
drop table if exists contact_phone;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,51 @@
|
|||
-- Revert numerus:contact_tax_details from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
alter table contact
|
||||
drop constraint name_not_empty
|
||||
, add column country_code country_code
|
||||
, add column postal_code text
|
||||
, add column province text
|
||||
, add column city text
|
||||
, add column address text
|
||||
, add column vatin vatin
|
||||
, add column business_name text constraint business_name_not_empty check(length(trim(business_name)) > 1)
|
||||
;
|
||||
|
||||
alter table contact
|
||||
rename column name to trade_name
|
||||
;
|
||||
|
||||
update contact
|
||||
set business_name = tax.business_name
|
||||
, vatin = tax.vatin
|
||||
, address = tax.address
|
||||
, city = tax.city
|
||||
, province = tax.province
|
||||
, postal_code = tax.postal_code
|
||||
, country_code = tax.country_code
|
||||
from contact_tax_details as tax
|
||||
where tax.contact_id = contact.contact_id
|
||||
;
|
||||
|
||||
alter table contact
|
||||
alter column business_name set not null
|
||||
, alter column vatin set not null
|
||||
, alter column address set not null
|
||||
, alter column city set not null
|
||||
, alter column province set not null
|
||||
, alter column postal_code set not null
|
||||
, alter column country_code set not null
|
||||
;
|
||||
|
||||
update contact
|
||||
set trade_name = ''
|
||||
where trade_name = business_name
|
||||
;
|
||||
|
||||
drop table if exists contact_tax_details;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Revert numerus:contact_web from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
alter table contact
|
||||
add column web uri
|
||||
;
|
||||
|
||||
update contact
|
||||
set web = web.uri
|
||||
from contact_web as web
|
||||
where web.contact_id = contact.contact_id
|
||||
;
|
||||
|
||||
alter table contact
|
||||
alter column web set not null
|
||||
;
|
||||
|
||||
drop table if exists numerus.contact_web;
|
||||
|
||||
commit;
|
|
@ -1,7 +1,55 @@
|
|||
-- Revert numerus:edit_contact from pg
|
||||
-- Deploy numerus:edit_contact to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: email
|
||||
-- requires: extension_uri
|
||||
-- requires: country_code
|
||||
-- requires: tag_name
|
||||
-- requires: contact
|
||||
-- requires: extension_vat
|
||||
-- requires: extension_pg_libphonenumber
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.edit_contact(uuid, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[]);
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_contact(contact_slug uuid, business_name text, vatin text, trade_name text, phone text, email email, web uri, address text, city text, province text, postal_code text, country_code country_code, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
company integer;
|
||||
begin
|
||||
update contact
|
||||
set business_name = edit_contact.business_name
|
||||
, vatin = (edit_contact.country_code || edit_contact.vatin)::vatin
|
||||
, trade_name = edit_contact.trade_name
|
||||
, phone = parse_packed_phone_number( edit_contact.phone, edit_contact.country_code)
|
||||
, email = edit_contact.email
|
||||
, web = edit_contact.web
|
||||
, address = edit_contact.address
|
||||
, city = edit_contact.city
|
||||
, province = edit_contact.province
|
||||
, postal_code = edit_contact.postal_code
|
||||
, country_code = edit_contact.country_code
|
||||
, tags = edit_contact.tags
|
||||
where slug = contact_slug
|
||||
returning contact_id, company_id
|
||||
into cid, company
|
||||
;
|
||||
|
||||
if cid is null then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
return contact_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) from public;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to invoicer;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, email, uri, text, text, text, text, country_code, tag_name[]) to admin;
|
||||
|
||||
drop function if exists edit_contact(uuid, text, text, email, uri, tax_details, tag_name[]);
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:edit_contact from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.edit_contact(uuid, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[]);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,10 @@
|
|||
-- Revert numerus:invoice_contact_id_fkey from pg
|
||||
|
||||
begin;
|
||||
|
||||
alter table numerus.invoice
|
||||
drop constraint invoice_contact_id_fkey
|
||||
, add foreign key (contact_id) references numerus.contact (contact_id)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:tax_details from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop type if exists numerus.tax_details;
|
||||
|
||||
commit;
|
|
@ -98,3 +98,12 @@ compute_new_quote_amount [roles schema_numerus company tax new_quote_product new
|
|||
edited_quote_product [schema_numerus discount_rate] 2023-06-07T13:03:23Z jordi fita mas <jordi@tandem.blog> # Add type for passing products to edit quotations
|
||||
edit_quote [roles schema_numerus quote currency parse_price edited_quote_product tax quote_contact quote_payment_method quote_product quote_product_tax quote_product_product tag_name] 2023-06-07T13:08:10Z jordi fita mas <jordi@tandem.blog> # Add function to edit quotations
|
||||
@v0 2023-06-12T14:05:34Z jordi fita mas <jordi@tandem.blog> # Tag version 0
|
||||
|
||||
contact_phone [roles schema_numerus extension_pg_libphonenumber] 2023-06-28T11:04:19Z jordi fita mas <jordi@tandem.blog> # Add relation to keep contacts’ phone numbers
|
||||
contact_email [roles schema_numerus email contact] 2023-06-28T11:47:19Z jordi fita mas <jordi@tandem.blog> # Add relation to keep contacts’ emails
|
||||
contact_web [roles schema_numerus extension_uri contact] 2023-06-28T12:01:07Z jordi fita mas <jordi@tandem.blog> # Add relation to keep contacts’ websites
|
||||
contact_tax_details [roles schema_numerus contact extension_vat country_code country] 2023-06-23T09:14:03Z jordi fita mas <jordi@tandem.blog> # Add relation of contact’s tax details
|
||||
tax_details [schema_numerus extension_vat country_code] 2023-06-29T10:57:57Z jordi fita mas <jordi@tandem.blog> # Add composite type for contacts’ tax details
|
||||
add_contact [add_contact@v0 tax_details] 2023-06-29T11:10:15Z jordi fita mas <jordi@tandem.blog> # Change add contact to accept a tax_detail parameter and use the new relations
|
||||
edit_contact [edit_contact@v0 tax_details] 2023-06-29T11:50:41Z jordi fita mas <jordi@tandem.blog> # Change edit_contact to require tax_details parameter and to use new relations for web, email, and phone
|
||||
invoice_contact_id_fkey [schema_numerus invoice contact_tax_details] 2023-06-30T16:50:45Z jordi fita mas <jordi@tandem.blog> # Update invoice’s contact_id foreign key to point to tax sales
|
||||
|
|
|
@ -5,19 +5,19 @@ reset client_min_messages;
|
|||
|
||||
begin;
|
||||
|
||||
select plan(14);
|
||||
select plan(18);
|
||||
|
||||
set search_path to auth, numerus, public;
|
||||
|
||||
select has_function('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]']);
|
||||
select function_lang_is('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'plpgsql');
|
||||
select function_returns('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'uuid');
|
||||
select isnt_definer('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]']);
|
||||
select volatility_is('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'volatile');
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'authenticator', array []::text[]);
|
||||
select has_function('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]']);
|
||||
select function_lang_is('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'plpgsql');
|
||||
select function_returns('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'uuid');
|
||||
select isnt_definer('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]']);
|
||||
select volatility_is('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'volatile');
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
|
@ -41,35 +41,67 @@ values (111, 1, 'cash', 'cash')
|
|||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_contact(1, 'Contact 2.1', '40404040D', 'Trade Contact 2.1', '777-777-777', 'c@c', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '17486', 'ES', '{tag1,tag2}') $$,
|
||||
'Should be able to insert a contact for the first company with two tags'
|
||||
$$ select add_contact(1, 'Contact 2.1', '777-777-777', null, 'https://c', null, '{tag1,tag2}') $$,
|
||||
'Should be able to insert a contact for the first company with two tags, no email, and no tax details'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_contact(1, 'Contact 2.2', '41414141L', 'Trade Contact 2.2', '888-888-888', 'd@d', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17487', 'ES', '{}') $$,
|
||||
'Should be able to insert a second contact for the first company with no tag'
|
||||
$$ select add_contact(1, 'Contact 2.2', null, 'd@d', null, '(Contact 2.2 Ltd,41414141L,"Fake St., 123",City 2.2,Province 2.2,17487,ES)', '{}') $$,
|
||||
'Should be able to insert a second contact for the first company with no tag, no phone, and not website'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_contact(2, 'Contact 4.1', '42424242Y', '', '999-999-999', 'e@e', '', 'Yet Another Fake St., 123', 'City 4.1', 'Province 4.1', '17488', 'ES', '{tag2}') $$,
|
||||
'Should be able to insert a contact for the second company with a tag'
|
||||
$$ select add_contact(2, 'Contact 4.1', '999-999-999', 'e@e', 'http://e', '(Contact 4.1 Ltd,42424242Y,"Another Fake St., 123",City 4.1,Province 4.1,17488,ES)', '{tag2}') $$,
|
||||
'Should be able to insert a contact for the second company with a tag and everything else'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_contact(1, 'Contact 2.3', '43434343Q', '', '000-000-000', 'f@f', '', 'The Last Fake St., 123', '', '', '', 'ES', '{tag2}') $$,
|
||||
$$ select add_contact(1, 'Contact 2.3', null, null, null, null, '{tag2}') $$,
|
||||
'Should be able to insert another contact with a repeated tag'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, tags, created_at from contact $$,
|
||||
$$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 777 77 77 77', 'c@c', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '17486', 'ES', '{tag1,tag2}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 888 88 88 88', 'd@d', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17487', 'ES', '{}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (2, 'Contact 4.1', 'ES42424242Y', '', '+34 999 99 99 99', 'e@e', '', 'Yet Another Fake St., 123', 'City 4.1', 'Province 4.1', '17488', 'ES', '{tag2}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (1, 'Contact 2.3', 'ES43434343Q', '', '+34 000000000', 'f@f', '', 'The Last Fake St., 123', '', '', '', 'ES', '{tag2}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
$$ select company_id, name, tags, created_at from contact $$,
|
||||
$$ values (1, 'Contact 2.1', '{tag1,tag2}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (1, 'Contact 2.2', '{}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (2, 'Contact 4.1', '{tag2}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (1, 'Contact 2.3', '{tag2}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
$$,
|
||||
'Should have created all contacts'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, business_name, vatin::text, address, city, province, postal_code, country_code::text from contact join contact_tax_details using (contact_id) $$,
|
||||
$$ values ('Contact 2.2', 'Contact 2.2 Ltd', 'ES41414141L', 'Fake St., 123', 'City 2.2', 'Province 2.2', '17487', 'ES')
|
||||
, ('Contact 4.1', 'Contact 4.1 Ltd', 'ES42424242Y', 'Another Fake St., 123', 'City 4.1', 'Province 4.1', '17488', 'ES')
|
||||
$$,
|
||||
'Should have created all contacts’ tax details'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, phone::text from contact join contact_phone using (contact_id) $$,
|
||||
$$ values ('Contact 2.1', '+34 777 77 77 77')
|
||||
, ('Contact 4.1', '+34 999 99 99 99')
|
||||
$$,
|
||||
'Should have created all contacts’ phone'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, email::text from contact join contact_email using (contact_id) $$,
|
||||
$$ values ('Contact 2.2', 'd@d')
|
||||
, ('Contact 4.1', 'e@e')
|
||||
$$,
|
||||
'Should have created all contacts’ email'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, uri::text from contact join contact_web using (contact_id) $$,
|
||||
$$ values ('Contact 2.1', 'https://c')
|
||||
, ('Contact 4.1', 'http://e')
|
||||
$$,
|
||||
'Should have created all contacts’ web'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
|
|
@ -55,11 +55,11 @@ values (3, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (6, 2, 22, 'IVA 10 %', 0.10)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
, (14, 2, 'Contact 4.1', 'XX777', '', '999-999-999', 'e@e', '', '', '', '', '', 'ES')
|
||||
, (15, 2, 'Contact 4.2', 'XX888', '', '000-000-000', 'f@f', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (12, 1, 'Contact 2.1')
|
||||
, (13, 1, 'Contact 2.2')
|
||||
, (14, 2, 'Contact 4.1')
|
||||
, (15, 2, 'Contact 4.2')
|
||||
;
|
||||
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ truncate invoice_number_counter cascade;
|
|||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate product cascade;
|
||||
truncate tax cascade;
|
||||
|
@ -72,11 +73,18 @@ values ( 7, 1, 'Product 2.1', 1212)
|
|||
, (11, 2, 'Product 4.3', 1010)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
, (14, 2, 'Contact 4.1', 'XX777', '', '999-999-999', 'e@e', '', '', '', '', '', 'ES')
|
||||
, (15, 2, 'Contact 4.2', 'XX888', '', '000-000-000', 'f@f', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (12, 1, 'Contact 2.1')
|
||||
, (13, 1, 'Contact 2.2')
|
||||
, (14, 2, 'Contact 4.1')
|
||||
, (15, 2, 'Contact 4.2')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (12, 'Contact 2.1', 'XX555', '', '', '', '', 'ES')
|
||||
, (13, 'Contact 2.2', 'XX666', '', '', '', '', 'ES')
|
||||
, (14, 'Contact 4.1', 'XX777', '', '', '', '', 'ES')
|
||||
, (15, 'Contact 4.2', 'XX888', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
|
||||
|
|
|
@ -74,11 +74,11 @@ values ( 7, 1, 'Product 2.1', 1212)
|
|||
, (11, 2, 'Product 4.3', 1010)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
, (14, 2, 'Contact 4.1', 'XX777', '', '999-999-999', 'e@e', '', '', '', '', '', 'ES')
|
||||
, (15, 2, 'Contact 4.2', 'XX888', '', '000-000-000', 'f@f', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (12, 1, 'Contact 2.1')
|
||||
, (13, 1, 'Contact 2.2')
|
||||
, (14, 2, 'Contact 4.1')
|
||||
, (15, 2, 'Contact 4.2')
|
||||
;
|
||||
|
||||
|
||||
|
|
|
@ -53,9 +53,9 @@ values (3, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (4, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (12, 1, 'Contact 2.1')
|
||||
, (13, 1, 'Contact 2.2')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, slug, invoice_number, invoice_date, contact_id, amount, currency_code, tags)
|
||||
|
|
|
@ -5,7 +5,7 @@ reset client_min_messages;
|
|||
|
||||
begin;
|
||||
|
||||
select plan(90);
|
||||
select plan(48);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
|
@ -43,62 +43,10 @@ select col_not_null('contact', 'slug');
|
|||
select col_has_default('contact', 'slug');
|
||||
select col_default_is('contact', 'slug', 'gen_random_uuid()');
|
||||
|
||||
select has_column('contact', 'business_name');
|
||||
select col_type_is('contact', 'business_name', 'text');
|
||||
select col_not_null('contact', 'business_name');
|
||||
select col_hasnt_default('contact', 'business_name');
|
||||
|
||||
select has_column('contact', 'vatin');
|
||||
select col_type_is('contact', 'vatin', 'vatin');
|
||||
select col_not_null('contact', 'vatin');
|
||||
select col_hasnt_default('contact', 'vatin');
|
||||
|
||||
select has_column('contact', 'trade_name');
|
||||
select col_type_is('contact', 'trade_name', 'text');
|
||||
select col_not_null('contact', 'trade_name');
|
||||
select col_hasnt_default('contact', 'trade_name');
|
||||
|
||||
select has_column('contact', 'phone');
|
||||
select col_type_is('contact', 'phone', 'packed_phone_number');
|
||||
select col_not_null('contact', 'phone');
|
||||
select col_hasnt_default('contact', 'phone');
|
||||
|
||||
select has_column('contact', 'email');
|
||||
select col_type_is('contact', 'email', 'email');
|
||||
select col_not_null('contact', 'email');
|
||||
select col_hasnt_default('contact', 'email');
|
||||
|
||||
select has_column('contact', 'web');
|
||||
select col_type_is('contact', 'web', 'uri');
|
||||
select col_not_null('contact', 'web');
|
||||
select col_hasnt_default('contact', 'web');
|
||||
|
||||
select has_column('contact', 'address');
|
||||
select col_type_is('contact', 'address', 'text');
|
||||
select col_not_null('contact', 'address');
|
||||
select col_hasnt_default('contact', 'address');
|
||||
|
||||
select has_column('contact', 'city');
|
||||
select col_type_is('contact', 'city', 'text');
|
||||
select col_not_null('contact', 'city');
|
||||
select col_hasnt_default('contact', 'city');
|
||||
|
||||
select has_column('contact', 'province');
|
||||
select col_type_is('contact', 'province', 'text');
|
||||
select col_not_null('contact', 'province');
|
||||
select col_hasnt_default('contact', 'province');
|
||||
|
||||
select has_column('contact', 'postal_code');
|
||||
select col_type_is('contact', 'postal_code', 'text');
|
||||
select col_not_null('contact', 'postal_code');
|
||||
select col_hasnt_default('contact', 'postal_code');
|
||||
|
||||
select has_column('contact', 'country_code');
|
||||
select col_type_is('contact', 'country_code', 'country_code');
|
||||
select col_is_fk('contact', 'country_code');
|
||||
select col_type_is('contact', 'country_code', 'country_code');
|
||||
select col_not_null('contact', 'country_code');
|
||||
select col_hasnt_default('contact', 'country_code');
|
||||
select has_column('contact', 'name');
|
||||
select col_type_is('contact', 'name', 'text');
|
||||
select col_not_null('contact', 'name');
|
||||
select col_hasnt_default('contact', 'name');
|
||||
|
||||
select has_column('contact', 'tags');
|
||||
select col_type_is('contact', 'tags', 'tag_name[]');
|
||||
|
@ -144,15 +92,15 @@ values (2, 1)
|
|||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (company_id, name)
|
||||
values (2, 'Contact 1')
|
||||
, (4, 'Contact 2')
|
||||
;
|
||||
|
||||
prepare contact_data as
|
||||
select company_id, business_name
|
||||
select company_id, name
|
||||
from contact
|
||||
order by company_id, business_name;
|
||||
order by company_id, name;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
|
@ -185,11 +133,11 @@ select throws_ok(
|
|||
reset role;
|
||||
|
||||
select throws_ok( $$
|
||||
insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (2, ' ', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES')
|
||||
insert into contact (company_id, name)
|
||||
values (2, ' ')
|
||||
$$,
|
||||
'23514', 'new row for relation "contact" violates check constraint "business_name_not_empty"',
|
||||
'Should not allow contacts with blank business name'
|
||||
'23514', 'new row for relation "contact" violates check constraint "name_not_empty"',
|
||||
'Should not allow contacts with blank trade name'
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
-- Test contact_email
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(21);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('contact_email');
|
||||
select has_pk('contact_email' );
|
||||
select table_privs_are('contact_email', 'guest', array []::text[]);
|
||||
select table_privs_are('contact_email', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_email', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_email', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('contact_email', 'contact_id');
|
||||
select col_is_pk('contact_email', 'contact_id');
|
||||
select col_is_fk('contact_email', 'contact_id');
|
||||
select fk_ok('contact_email', 'contact_id', 'contact', 'contact_id');
|
||||
select col_type_is('contact_email', 'contact_id', 'integer');
|
||||
select col_not_null('contact_email', 'contact_id');
|
||||
select col_hasnt_default('contact_email', 'contact_id');
|
||||
|
||||
select has_column('contact_email', 'email');
|
||||
select col_type_is('contact_email', 'email', 'email');
|
||||
select col_not_null('contact_email', 'email');
|
||||
select col_hasnt_default('contact_email', 'email');
|
||||
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate contact_email cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
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, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (6, 2, 'C1')
|
||||
, (8, 4, 'C2')
|
||||
, (9, 4, 'C3')
|
||||
;
|
||||
|
||||
insert into contact_email (contact_id, email)
|
||||
values (6, 'c@c')
|
||||
, (8, 'd@d')
|
||||
;
|
||||
|
||||
prepare contact_data as
|
||||
select company_id, email
|
||||
from contact
|
||||
join contact_email using (contact_id)
|
||||
order by company_id, email;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (2, 'c@c')
|
||||
$$,
|
||||
'Should only list contacts of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (4, 'd@d')
|
||||
$$,
|
||||
'Should only list contacts of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'contact_data',
|
||||
'42501', 'permission denied for table contact',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
-- Test contact_phone
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(21);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('contact_phone');
|
||||
select has_pk('contact_phone' );
|
||||
select table_privs_are('contact_phone', 'guest', array []::text[]);
|
||||
select table_privs_are('contact_phone', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_phone', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_phone', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('contact_phone', 'contact_id');
|
||||
select col_is_pk('contact_phone', 'contact_id');
|
||||
select col_is_fk('contact_phone', 'contact_id');
|
||||
select fk_ok('contact_phone', 'contact_id', 'contact', 'contact_id');
|
||||
select col_type_is('contact_phone', 'contact_id', 'integer');
|
||||
select col_not_null('contact_phone', 'contact_id');
|
||||
select col_hasnt_default('contact_phone', 'contact_id');
|
||||
|
||||
select has_column('contact_phone', 'phone');
|
||||
select col_type_is('contact_phone', 'phone', 'packed_phone_number');
|
||||
select col_not_null('contact_phone', 'phone');
|
||||
select col_hasnt_default('contact_phone', 'phone');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate contact_phone cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
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, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (6, 2, 'C1')
|
||||
, (8, 4, 'C2')
|
||||
, (9, 4, 'C3')
|
||||
;
|
||||
|
||||
insert into contact_phone (contact_id, phone)
|
||||
values (6, '777-777-777')
|
||||
, (8, '888-888-888')
|
||||
;
|
||||
|
||||
prepare contact_data as
|
||||
select company_id, phone
|
||||
from contact
|
||||
join contact_phone using (contact_id)
|
||||
order by company_id, phone;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (2, '777-777-777'::packed_phone_number)
|
||||
$$,
|
||||
'Should only list contacts of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (4, '888-888-888'::packed_phone_number)
|
||||
$$,
|
||||
'Should only list contacts of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'contact_data',
|
||||
'42501', 'permission denied for table contact',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
-- Test contact_tax_details
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(48);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('contact_tax_details');
|
||||
select has_pk('contact_tax_details' );
|
||||
select table_privs_are('contact_tax_details', 'guest', array []::text[]);
|
||||
select table_privs_are('contact_tax_details', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_tax_details', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_tax_details', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('contact_tax_details', 'contact_id');
|
||||
select col_is_pk('contact_tax_details', 'contact_id');
|
||||
select col_is_fk('contact_tax_details', 'contact_id');
|
||||
select fk_ok('contact_tax_details', 'contact_id', 'contact', 'contact_id');
|
||||
select col_type_is('contact_tax_details', 'contact_id', 'integer');
|
||||
select col_not_null('contact_tax_details', 'contact_id');
|
||||
select col_hasnt_default('contact_tax_details', 'contact_id');
|
||||
|
||||
select has_column('contact_tax_details', 'business_name');
|
||||
select col_type_is('contact_tax_details', 'business_name', 'text');
|
||||
select col_not_null('contact_tax_details', 'business_name');
|
||||
select col_hasnt_default('contact_tax_details', 'business_name');
|
||||
|
||||
select has_column('contact_tax_details', 'vatin');
|
||||
select col_type_is('contact_tax_details', 'vatin', 'vatin');
|
||||
select col_not_null('contact_tax_details', 'vatin');
|
||||
select col_hasnt_default('contact_tax_details', 'vatin');
|
||||
|
||||
select has_column('contact_tax_details', 'address');
|
||||
select col_type_is('contact_tax_details', 'address', 'text');
|
||||
select col_not_null('contact_tax_details', 'address');
|
||||
select col_hasnt_default('contact_tax_details', 'address');
|
||||
|
||||
select has_column('contact_tax_details', 'city');
|
||||
select col_type_is('contact_tax_details', 'city', 'text');
|
||||
select col_not_null('contact_tax_details', 'city');
|
||||
select col_hasnt_default('contact_tax_details', 'city');
|
||||
|
||||
select has_column('contact_tax_details', 'province');
|
||||
select col_type_is('contact_tax_details', 'province', 'text');
|
||||
select col_not_null('contact_tax_details', 'province');
|
||||
select col_hasnt_default('contact_tax_details', 'province');
|
||||
|
||||
select has_column('contact_tax_details', 'postal_code');
|
||||
select col_type_is('contact_tax_details', 'postal_code', 'text');
|
||||
select col_not_null('contact_tax_details', 'postal_code');
|
||||
select col_hasnt_default('contact_tax_details', 'postal_code');
|
||||
|
||||
select has_column('contact_tax_details', 'country_code');
|
||||
select col_is_fk('contact_tax_details', 'country_code');
|
||||
select col_type_is('contact_tax_details', 'country_code', 'country_code');
|
||||
select col_type_is('contact_tax_details', 'country_code', 'country_code');
|
||||
select col_not_null('contact_tax_details', 'country_code');
|
||||
select col_hasnt_default('contact_tax_details', 'country_code');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
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, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (6, 2, 'C1')
|
||||
, (8, 4, 'C2')
|
||||
, (9, 4, 'C3')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (6, 'Contact 1', 'XX555', '', '', '', '', 'ES')
|
||||
, (8, 'Contact 2', 'XX666', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
prepare contact_data as
|
||||
select company_id, business_name
|
||||
from contact
|
||||
join contact_tax_details using (contact_id)
|
||||
order by company_id, business_name;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (2, 'Contact 1')
|
||||
$$,
|
||||
'Should only list contacts of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (4, 'Contact 2')
|
||||
$$,
|
||||
'Should only list contacts of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'contact_data',
|
||||
'42501', 'permission denied for table contact',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select throws_ok( $$
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (9, ' ', 'XX123', '', '', '', '', 'ES')
|
||||
$$,
|
||||
'23514', 'new row for relation "contact_tax_details" violates check constraint "business_name_not_empty"',
|
||||
'Should not allow contacts with blank business name'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
-- Test contact_web
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(21);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('contact_web');
|
||||
select has_pk('contact_web' );
|
||||
select table_privs_are('contact_web', 'guest', array []::text[]);
|
||||
select table_privs_are('contact_web', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_web', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact_web', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('contact_web', 'contact_id');
|
||||
select col_is_pk('contact_web', 'contact_id');
|
||||
select col_is_fk('contact_web', 'contact_id');
|
||||
select fk_ok('contact_web', 'contact_id', 'contact', 'contact_id');
|
||||
select col_type_is('contact_web', 'contact_id', 'integer');
|
||||
select col_not_null('contact_web', 'contact_id');
|
||||
select col_hasnt_default('contact_web', 'contact_id');
|
||||
|
||||
select has_column('contact_web', 'uri');
|
||||
select col_type_is('contact_web', 'uri', 'uri');
|
||||
select col_not_null('contact_web', 'uri');
|
||||
select col_hasnt_default('contact_web', 'uri');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate contact_web cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
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, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (6, 2, 'C1')
|
||||
, (8, 4, 'C2')
|
||||
, (9, 4, 'C3')
|
||||
;
|
||||
|
||||
insert into contact_web (contact_id, uri)
|
||||
values (6, 'http://rainforest.com/')
|
||||
, (8, 'https://kiwi.com/')
|
||||
;
|
||||
|
||||
prepare contact_data as
|
||||
select company_id, uri
|
||||
from contact
|
||||
join contact_web using (contact_id)
|
||||
order by company_id, uri;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (2, 'http://rainforest.com/'::uri)
|
||||
$$,
|
||||
'Should only list contacts of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'contact_data',
|
||||
$$ values (4, 'https://kiwi.com/'::uri)
|
||||
$$,
|
||||
'Should only list contacts of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'contact_data',
|
||||
'42501', 'permission denied for table contact',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -5,19 +5,19 @@ reset client_min_messages;
|
|||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
select plan(17);
|
||||
|
||||
set search_path to auth, numerus, public;
|
||||
|
||||
select has_function('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]']);
|
||||
select function_lang_is('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'plpgsql');
|
||||
select function_returns('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'uuid');
|
||||
select isnt_definer('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]']);
|
||||
select volatility_is('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'volatile');
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'email', 'uri', 'text', 'text', 'text', 'text', 'country_code', 'tag_name[]'], 'authenticator', array []::text[]);
|
||||
select has_function('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]']);
|
||||
select function_lang_is('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'plpgsql');
|
||||
select function_returns('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'uuid');
|
||||
select isnt_definer('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]']);
|
||||
select volatility_is('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'volatile');
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'text', 'email', 'uri', 'tax_details', 'tag_name[]'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
|
@ -40,30 +40,89 @@ values (111, 1, 'cash', 'cash')
|
|||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into contact (contact_id, company_id, slug, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags)
|
||||
values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES', '{tag1}')
|
||||
, (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES', '{tag2}')
|
||||
insert into contact (contact_id, company_id, slug, name, tags)
|
||||
values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', '{tag1}')
|
||||
, (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', '{tag2}')
|
||||
, (14, 1, '12fd031b-8f4d-4ac1-9dde-7df336dc6d52', 'Contact 3', '{tag3}')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (12, 'Contact 1 Ltd', 'XX555', '', '', '', '', 'ES')
|
||||
, (13, 'Contact 2 Ltd', 'XX666', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into contact_phone (contact_id, phone)
|
||||
values (12, '777-777-777')
|
||||
, (13, '888-888-888')
|
||||
;
|
||||
|
||||
insert into contact_email (contact_id, email)
|
||||
values (12, 'c@c')
|
||||
, (13, 'd@d')
|
||||
;
|
||||
|
||||
insert into contact_web (contact_id, uri)
|
||||
values (12, 'https://1/')
|
||||
, (13, 'https://2/')
|
||||
;
|
||||
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_contact('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 2.1', '40404040D', 'Trade Contact 2.1', '999-999-999', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', array['tag1']) $$,
|
||||
$$ select edit_contact('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 2.1', '999-999-999', 'c1@c1', 'https://c', '(Contact 2.1 Ltd,40404040D,"Fake St., 123",City 2.1,Province 2.1,19486,ES)', array['tag1']) $$,
|
||||
'Should be able to edit the first contact'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_contact('b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2.2', '41414141L', 'Trade Contact 2.2', '111-111-111', 'd2@d2', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES', array['tag1', 'tag3']) $$,
|
||||
$$ select edit_contact('b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2.2', null, null, null, null, array['tag1', 'tag3']) $$,
|
||||
'Should be able to edit the second contact'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_contact('12fd031b-8f4d-4ac1-9dde-7df336dc6d52', 'Contact 2.3', '111-111-111', 'd2@d2', 'https://d', '(Contact 2.3 Ltd,41414141L,"Another Fake St., 123",City 2.2,Province 2.2,17417,ES)', array['tag2']) $$,
|
||||
'Should be able to edit the third contact'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, tags, created_at from contact $$,
|
||||
$$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 999 99 99 99', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', '{tag1}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 111111111', 'd2@d2', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES', '{tag1,tag3}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
$$ select company_id, name, tags, created_at from contact $$,
|
||||
$$ values (1, 'Contact 2.1', '{tag1}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (1, 'Contact 2.2', '{tag1,tag3}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
, (1, 'Contact 2.3', '{tag2}'::tag_name[], CURRENT_TIMESTAMP)
|
||||
$$,
|
||||
'Should have updated all contacts'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, business_name, vatin::text, address, city, province, postal_code, country_code::text from contact join contact_tax_details using (contact_id) $$,
|
||||
$$ values ('Contact 2.1', 'Contact 2.1 Ltd', 'ES40404040D', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES')
|
||||
, ('Contact 2.3', 'Contact 2.3 Ltd', 'ES41414141L', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES')
|
||||
$$,
|
||||
'Should have updated all contacts’ tax details'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, phone::text from contact join contact_phone using (contact_id) $$,
|
||||
$$ values ('Contact 2.1', '+34 999 99 99 99')
|
||||
, ('Contact 2.3', '+34 111111111')
|
||||
$$,
|
||||
'Should have updated all contacts’ phone'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, email::text from contact join contact_email using (contact_id) $$,
|
||||
$$ values ('Contact 2.1', 'c1@c1')
|
||||
, ('Contact 2.3', 'd2@d2')
|
||||
$$,
|
||||
'Should have updated all contacts’ email'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, uri::text from contact join contact_web using (contact_id) $$,
|
||||
$$ values ('Contact 2.1', 'https://c')
|
||||
, ('Contact 2.3', 'https://d')
|
||||
$$,
|
||||
'Should have updated all contacts’ web'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
|
|
@ -53,9 +53,9 @@ values (3, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (4, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (12, 1, 'Contact 2.1')
|
||||
, (13, 1, 'Contact 2.2')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, slug, invoice_number, invoice_date, contact_id, amount, currency_code, tags)
|
||||
|
|
|
@ -24,6 +24,7 @@ set client_min_messages to warning;
|
|||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate product cascade;
|
||||
truncate tax cascade;
|
||||
|
@ -61,9 +62,14 @@ values ( 7, 1, 'Product 1.1', 1212)
|
|||
, ( 9, 1, 'Product 3.3', 3636)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (12, 1, 'Contact 2.1')
|
||||
, (13, 1, 'Contact 2.2')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (12, 'Contact 2.1', 'XX555', '', '', '', '', 'ES')
|
||||
, (13, 'Contact 2.2', 'XX666', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code, tags)
|
||||
|
|
|
@ -63,9 +63,9 @@ values ( 7, 1, 'Product 1.1', 1212)
|
|||
, ( 9, 1, 'Product 3.3', 3636)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (12, 1, 'Contact 2.1')
|
||||
, (13, 1, 'Contact 2.2')
|
||||
;
|
||||
|
||||
insert into quote (quote_id, company_id, slug, quote_number, quote_date, currency_code, tags)
|
||||
|
|
|
@ -118,9 +118,9 @@ values (2, 1)
|
|||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (6, 2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (8, 4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (6, 2, 'Contact 1')
|
||||
, (8, 4, 'Contact 2')
|
||||
;
|
||||
|
||||
insert into expense (company_id, invoice_number, contact_id, invoice_date, amount, currency_code)
|
||||
|
|
|
@ -87,9 +87,9 @@ values (3, 2, 22, 'IVA 21 %', 0.21)
|
|||
, (6, 4, 44, 'IVA 10 %', 0.10)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values ( 9, 2, 'Customer 1', 'XX555', '', '777-777-777', 'c1@e', '', '', '', '', '', 'ES')
|
||||
, (10, 4, 'Customer 2', 'XX666', '', '888-888-888', 'c2@e', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values ( 9, 2, 'Customer 1')
|
||||
, (10, 4, 'Customer 2')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, invoice_number, contact_id, invoice_date, amount, currency_code)
|
||||
|
|
|
@ -84,9 +84,9 @@ values (3, 2, 22, 'IVA 21 %', 0.21)
|
|||
, (6, 4, 44, 'IVA 10 %', 0.10)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values ( 9, 2, 'Customer 1', 'XX555', '', '777-777-777', 'c1@e', '', '', '', '', '', 'ES')
|
||||
, (10, 4, 'Customer 2', 'XX666', '', '888-888-888', 'c2@e', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values ( 9, 2, 'Customer 1')
|
||||
, (10, 4, 'Customer 2')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, invoice_number, contact_id, invoice_date, amount, currency_code)
|
||||
|
|
|
@ -58,8 +58,8 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (5, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (7, 1, 'Contact')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, invoice_number, invoice_date, contact_id, amount, currency_code)
|
||||
|
|
|
@ -56,7 +56,7 @@ select col_default_is('invoice', 'invoice_date', 'CURRENT_DATE');
|
|||
|
||||
select has_column('invoice', 'contact_id');
|
||||
select col_is_fk('invoice', 'contact_id');
|
||||
select fk_ok('invoice', 'contact_id', 'contact', 'contact_id');
|
||||
select fk_ok('invoice', 'contact_id', 'contact_tax_details', 'contact_id');
|
||||
select col_type_is('invoice', 'contact_id', 'integer');
|
||||
select col_not_null('invoice', 'contact_id');
|
||||
select col_hasnt_default('invoice', 'contact_id');
|
||||
|
@ -104,6 +104,7 @@ select col_default_is('invoice', 'created_at', 'CURRENT_TIMESTAMP');
|
|||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
|
@ -135,9 +136,14 @@ values (2, 1)
|
|||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (6, 2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (8, 4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (6, 2, 'Contact 1')
|
||||
, (8, 4, 'Contact 2')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (6, 'Contact 1', 'XX555', '', '', '', '', 'ES')
|
||||
, (8, 'Contact 2', 'XX666', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (company_id, invoice_number, contact_id, currency_code, payment_method_id)
|
||||
|
|
|
@ -29,6 +29,7 @@ set client_min_messages to warning;
|
|||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
|
@ -59,8 +60,12 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (5, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (7, 1, 'Contact')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (7, 'Contact', 'XX555', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, invoice_date, contact_id, currency_code, payment_method_id)
|
||||
|
|
|
@ -68,6 +68,7 @@ select col_default_is('invoice_product', 'discount_rate', '0.0');
|
|||
set client_min_messages to warning;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
|
@ -99,9 +100,14 @@ values (2, 1)
|
|||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (6, 2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
, (8, 4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (6, 2, 'Contact 1')
|
||||
, (8, 4, 'Contact 2')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (6, 'Contact 1', 'XX555', '', '', '', '', 'ES')
|
||||
, (8, 'Contact 2', 'XX666', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, contact_id, currency_code, payment_method_id)
|
||||
|
|
|
@ -29,6 +29,7 @@ set client_min_messages to warning;
|
|||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
|
@ -59,8 +60,12 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (5, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (7, 1, 'Contact')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (7, 'Contact', 'XX555', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, invoice_date, contact_id, currency_code, payment_method_id)
|
||||
|
|
|
@ -43,6 +43,7 @@ truncate invoice_product cascade;
|
|||
truncate invoice cascade;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
|
@ -84,9 +85,14 @@ values (3, 2, 22, 'IVA 21 %', 0.21)
|
|||
, (6, 4, 44, 'IVA 10 %', 0.10)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values ( 9, 2, 'Customer 1', 'XX555', '', '777-777-777', 'c1@e', '', '', '', '', '', 'ES')
|
||||
, (10, 4, 'Customer 2', 'XX666', '', '888-888-888', 'c2@e', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values ( 9, 2, 'Customer 1')
|
||||
, (10, 4, 'Customer 2')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values ( 9, 'Customer 1', 'XX555', '', '', '', '', 'ES')
|
||||
, (10, 'Customer 2', 'XX666', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, contact_id, currency_code, payment_method_id)
|
||||
|
|
|
@ -29,6 +29,7 @@ set client_min_messages to warning;
|
|||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact_tax_details cascade;
|
||||
truncate contact cascade;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
|
@ -59,8 +60,12 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (5, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (7, 1, 'Contact')
|
||||
;
|
||||
|
||||
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
|
||||
values (7, 'Contact', 'XX555', '', '', '', '', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, invoice_date, contact_id, currency_code, payment_method_id)
|
||||
|
|
|
@ -59,8 +59,8 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
|
|||
, (5, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values (7, 1, 'Contact')
|
||||
;
|
||||
|
||||
insert into quote (quote_id, company_id, quote_number, quote_date, currency_code)
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
-- Test tax_details
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(9);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_composite('numerus', 'tax_details', 'Composite type numerus.tax_details should exist');
|
||||
select columns_are('numerus', 'tax_details', array['business_name', 'vatin', 'address', 'city', 'province', 'postal_code', 'country_code']);
|
||||
select col_type_is('numerus'::name, 'tax_details'::name, 'business_name'::name, 'text');
|
||||
select col_type_is('numerus'::name, 'tax_details'::name, 'vatin'::name, 'text');
|
||||
select col_type_is('numerus'::name, 'tax_details'::name, 'address'::name, 'text');
|
||||
select col_type_is('numerus'::name, 'tax_details'::name, 'city'::name, 'text');
|
||||
select col_type_is('numerus'::name, 'tax_details'::name, 'province'::name, 'text');
|
||||
select col_type_is('numerus'::name, 'tax_details'::name, 'postal_code'::name, 'text');
|
||||
select col_type_is('numerus'::name, 'tax_details'::name, 'country_code'::name, 'country_code');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.add_contact(integer, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[])', 'execute');
|
||||
select has_function_privilege('numerus.add_contact(integer, text, text, numerus.email, uri, numerus.tax_details, numerus.tag_name[])', 'execute');
|
||||
|
||||
rollback;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:add_contact on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.add_contact(integer, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[])', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,13 @@
|
|||
-- Verify numerus:contact_email on pg
|
||||
|
||||
begin;
|
||||
|
||||
select contact_id
|
||||
, email
|
||||
from numerus.contact_email
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.contact_email'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.contact_email'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,13 @@
|
|||
-- Verify numerus:contact_phone on pg
|
||||
|
||||
begin;
|
||||
|
||||
select contact_id
|
||||
, phone
|
||||
from numerus.contact_phone
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.contact_phone'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.contact_phone'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,19 @@
|
|||
-- Verify numerus:contact_tax_details on pg
|
||||
|
||||
begin;
|
||||
|
||||
select contact_id
|
||||
, business_name
|
||||
, vatin
|
||||
, address
|
||||
, city
|
||||
, province
|
||||
, postal_code
|
||||
, country_code
|
||||
from numerus.contact_tax_details
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.contact_tax_details'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.contact_tax_details'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,13 @@
|
|||
-- Verify numerus:contact_web on pg
|
||||
|
||||
begin;
|
||||
|
||||
select contact_id
|
||||
, uri
|
||||
from numerus.contact_web
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.contact_web'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.contact_web'::regclass;
|
||||
|
||||
rollback;
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.edit_contact(uuid, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[])', 'execute');
|
||||
select has_function_privilege('numerus.edit_contact(uuid, text, text, numerus.email, uri, numerus.tax_details, numerus.tag_name[])', 'execute');
|
||||
|
||||
rollback;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:edit_contact on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.edit_contact(uuid, text, text, text, text, numerus.email, uri, text, text, text, text, numerus.country_code, numerus.tag_name[])', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,13 @@
|
|||
-- Verify numerus:invoice_contact_id_fkey.sql on pg
|
||||
|
||||
begin;
|
||||
|
||||
select 1/count(*)
|
||||
from pg_catalog.pg_constraint
|
||||
where conrelid = 'numerus.invoice'::regclass
|
||||
and contype = 'f'
|
||||
and conname = 'invoice_contact_id_fkey'
|
||||
and pg_catalog.pg_get_constraintdef(oid, true) = 'FOREIGN KEY (contact_id) REFERENCES numerus.contact_tax_details(contact_id)'
|
||||
;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:tax_details on pg
|
||||
|
||||
begin;
|
||||
|
||||
select pg_catalog.has_type_privilege('numerus.tax_details', 'usage');
|
||||
|
||||
rollback;
|
|
@ -717,6 +717,14 @@ main > nav {
|
|||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.contact-tax-details {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input:checked ~ .contact-tax-details {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
/* Multiselect, tags */
|
||||
|
||||
.tag {
|
||||
|
|
|
@ -24,23 +24,28 @@
|
|||
|
||||
{{ with .Form }}
|
||||
<div class="contact-data">
|
||||
{{ template "input-field" .BusinessName }}
|
||||
{{ template "input-field" .VATIN }}
|
||||
{{ template "input-field" .TradeName }}
|
||||
{{ template "input-field" .Name }}
|
||||
{{ template "input-field" .Phone }}
|
||||
{{ template "input-field" .Email }}
|
||||
{{ template "input-field" .Web }}
|
||||
{{ template "tags-field" .Tags }}
|
||||
</div>
|
||||
|
||||
{{ template "check-field" .HasTaxDetails }}
|
||||
|
||||
<div class="contact-data contact-tax-details">
|
||||
{{ template "input-field" .BusinessName }}
|
||||
{{ template "input-field" .VATIN }}
|
||||
{{ template "input-field" .Address }}
|
||||
{{ template "input-field" .City }}
|
||||
{{ template "input-field" .Province }}
|
||||
{{ template "input-field" .PostalCode }}
|
||||
{{ template "select-field" .Country }}
|
||||
{{ template "tags-field" .Tags }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<fieldset>
|
||||
<button class="primary" type="submit">{{( pgettext "Save" "action" )}}</button>
|
||||
<button formnovalidate class="primary" type="submit">{{( pgettext "Save" "action" )}}</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
</section>
|
||||
|
|
|
@ -21,22 +21,27 @@
|
|||
{{ csrfToken }}
|
||||
|
||||
<div class="contact-data">
|
||||
{{ template "input-field" .BusinessName | addInputAttr "autofocus" }}
|
||||
{{ template "input-field" .VATIN }}
|
||||
{{ template "input-field" .TradeName }}
|
||||
{{ template "input-field" .Phone }}
|
||||
{{ template "input-field" .Email }}
|
||||
{{ template "input-field" .Web }}
|
||||
{{ template "input-field" .Address }}
|
||||
{{ template "input-field" .City }}
|
||||
{{ template "input-field" .Province }}
|
||||
{{ template "input-field" .PostalCode }}
|
||||
{{ template "select-field" .Country }}
|
||||
{{ template "tags-field" .Tags }}
|
||||
{{ template "input-field" .Name | addInputAttr "autofocus" }}
|
||||
{{ template "input-field" .Phone }}
|
||||
{{ template "input-field" .Email }}
|
||||
{{ template "input-field" .Web }}
|
||||
{{ template "tags-field" .Tags }}
|
||||
</div>
|
||||
|
||||
{{ template "check-field" .HasTaxDetails }}
|
||||
|
||||
<div class="contact-data contact-tax-details">
|
||||
{{ template "input-field" .BusinessName }}
|
||||
{{ template "input-field" .VATIN }}
|
||||
{{ template "input-field" .Address }}
|
||||
{{ template "input-field" .City }}
|
||||
{{ template "input-field" .Province }}
|
||||
{{ template "input-field" .PostalCode }}
|
||||
{{ template "select-field" .Country }}
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<button class="primary" type="submit">{{( pgettext "Save" "action" )}}</button>
|
||||
<button formnovalidate class="primary" type="submit">{{( pgettext "Save" "action" )}}</button>
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -142,6 +142,12 @@
|
|||
</fieldset>
|
||||
{{- end }}
|
||||
|
||||
{{ define "check-field" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.CheckField*/ -}}
|
||||
<input id="{{ .Name }}-field" type="checkbox" name="{{.Name}}"{{ if .Checked}} checked="checked"{{ end }}{{ if .Required }} required="required"{{ end }}>
|
||||
<label for="{{ .Name }}-field">{{.Label }}</label>
|
||||
{{- end }}
|
||||
|
||||
{{ define "invoice-product-form" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.invoiceProductForm*/ -}}
|
||||
<fieldset class="new-invoice-product"
|
||||
|
|
|
@ -51,18 +51,20 @@
|
|||
|
||||
<div>
|
||||
{{ if .HasQuotee -}}
|
||||
<address class="quotee">
|
||||
{{ .Quotee.Name }}<br>
|
||||
{{ .Quotee.VATIN }}<br>
|
||||
{{ .Quotee.Address }}<br>
|
||||
{{ .Quotee.City }} ({{ .Quotee.PostalCode}}), {{ .Quotee.Province }}<br>
|
||||
</address>
|
||||
<address class="quotee">
|
||||
{{ .Quotee.Name }}<br>
|
||||
{{ if .HasTaxDetails -}}
|
||||
{{ .Quotee.VATIN }}<br>
|
||||
{{ .Quotee.Address }}<br>
|
||||
{{ .Quotee.City }} ({{ .Quotee.PostalCode}}), {{ .Quotee.Province }}<br>
|
||||
{{- end }}
|
||||
</address>
|
||||
{{- end }}
|
||||
|
||||
{{ if .TermsAndConditions -}}
|
||||
<p class="terms_and_conditions">{{(gettext "Terms and Conditions:")}} {{ .TermsAndConditions }}</p>
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{- $columns := 5 | add (len .TaxClasses) | add (boolToInt .HasDiscounts) -}}
|
||||
<table>
|
||||
<thead>
|
||||
|
|
Loading…
Reference in New Issue