Add customer and invoices sections
Copied as much as i could from Numerus, and made as few modifications as i could to adapt to this code base; it is, quite frankly, a piece of shit. We need to be able to create invoices from scratch “just in case”, apparently, but it is not yet possible to create an invoice from a booking.
This commit is contained in:
parent
3559ff311b
commit
17f7520876
|
@ -10,6 +10,7 @@ Build-Depends:
|
|||
golang-any,
|
||||
golang-github-jackc-pgx-v4-dev,
|
||||
golang-github-leonelquinteros-gotext-dev,
|
||||
golang-github-rainycape-unidecode-dev,
|
||||
golang-golang-x-text-dev,
|
||||
postgresql-all (>= 217~),
|
||||
sqitch,
|
||||
|
|
|
@ -24,6 +24,21 @@ values (52, 42, 'employee')
|
|||
, (52, 43, 'admin')
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (1, 52, 'Pagament', '')
|
||||
;
|
||||
|
||||
insert into tax_class (tax_class_id, company_id, name)
|
||||
values (1, 52, 'VAT')
|
||||
;
|
||||
|
||||
insert into tax (tax_id, company_id, tax_class_id, name, rate)
|
||||
values (1, 52, 1, 'General VAT (21 %)', 0.21)
|
||||
, (2, 52, 1, 'Reduced VAT (10 %)', 0.10)
|
||||
, (3, 52, 1, 'Super-reduced VAT (4 %)', 0.04)
|
||||
, (4, 52, 1, 'VAT free (0 %)', 0.00)
|
||||
;
|
||||
|
||||
select setup_redsys(52, '361716962', '1', 'test', 'redirect', 'sq7HjrUOBfKmC576ILgskD5srU870gJ7');
|
||||
|
||||
select setup_location(52, '<div><h3>On som</h3><p>Ctra. de Sadernes, km 2, 17855 MONTAGUT i OIX</p></div>', '<iframe src="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d44661.89614700166!2d2.57381383167473!3d42.24000148364468!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x12bab79ff3b0f007%3A0x65b7563a5d1548e6!2sCamping%20Montagut!5e0!3m2!1sca!2sus!4v1703225042845!5m2!1sca!2sus" width="100%" height="600" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>', '<div><p><strong>Càmping i Safari tents:</strong><br />de 08/04 a 09/10</p><p><strong>Cabanes i Bungalows:</strong><br />de 08/04 a 11/12</p><p><strong>ACSI</strong>:<br />de 08/04 a 11/12</p></div>');
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
-- Deploy camper:add_contact to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: email
|
||||
-- requires: extension_pg_libphonenumber
|
||||
-- requires: country_code
|
||||
-- requires: contact
|
||||
-- requires: contact_phone
|
||||
-- requires: contact_email
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function add_contact(company_id integer, name text, id_document_type_id text, id_document_number text, phone text, email text, address text, city text, province text, postal_code text, country_code country_code) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
cslug uuid;
|
||||
begin
|
||||
insert into contact (company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
returning contact_id, slug
|
||||
into cid, cslug
|
||||
;
|
||||
|
||||
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(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;
|
||||
|
||||
return cslug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function add_contact(integer, text, text, text, text, text, text, text, text, text, country_code) from public;
|
||||
grant execute on function add_contact(integer, text, text, text, text, text, text, text, text, text, country_code) to employee;
|
||||
grant execute on function add_contact(integer, text, text, text, text, text, text, text, text, text, country_code) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,75 @@
|
|||
-- Deploy camper:add_invoice to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice
|
||||
-- requires: company
|
||||
-- requires: currency
|
||||
-- requires: parse_price
|
||||
-- requires: new_invoice_product
|
||||
-- requires: tax
|
||||
-- requires: invoice_product
|
||||
-- requires: invoice_product_product
|
||||
-- requires: invoice_product_tax
|
||||
-- requires: next_invoice_number
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function add_invoice(company integer, invoice_date date, contact_id integer, notes text, payment_method_id integer, products new_invoice_product[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
iid integer;
|
||||
pslug uuid;
|
||||
product new_invoice_product;
|
||||
ccode text;
|
||||
ipid integer;
|
||||
begin
|
||||
insert into invoice (company_id, invoice_number, invoice_date, contact_id, notes, currency_code, payment_method_id)
|
||||
select company_id
|
||||
, next_invoice_number(add_invoice.company, invoice_date)
|
||||
, invoice_date
|
||||
, contact_id
|
||||
, notes
|
||||
, currency_code
|
||||
, add_invoice.payment_method_id
|
||||
from company
|
||||
where company.company_id = add_invoice.company
|
||||
returning invoice_id, slug, currency_code
|
||||
into iid, pslug, ccode;
|
||||
|
||||
foreach product in array products
|
||||
loop
|
||||
insert into invoice_product (invoice_id, name, description, price, quantity, discount_rate)
|
||||
select iid
|
||||
, product.name
|
||||
, coalesce(product.description, '')
|
||||
, parse_price(product.price, currency.decimal_digits)
|
||||
, product.quantity
|
||||
, product.discount_rate
|
||||
from currency
|
||||
where currency_code = ccode
|
||||
returning invoice_product_id
|
||||
into ipid;
|
||||
|
||||
if product.product_id is not null then
|
||||
insert into invoice_product_product (invoice_product_id, product_id)
|
||||
values (ipid, product.product_id);
|
||||
end if;
|
||||
|
||||
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
|
||||
select ipid, tax_id, tax.rate
|
||||
from tax
|
||||
join unnest(product.tax) as ptax(tax_id) using (tax_id);
|
||||
end loop;
|
||||
|
||||
return pslug;
|
||||
end;
|
||||
$$
|
||||
language plpgsql;
|
||||
|
||||
revoke execute on function add_invoice(integer, date, integer, text, integer, new_invoice_product[]) from public;
|
||||
grant execute on function add_invoice(integer, date, integer, text, integer, new_invoice_product[]) to employee;
|
||||
grant execute on function add_invoice(integer, date, integer, text, integer, new_invoice_product[]) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,28 @@
|
|||
-- Deploy camper:available_invoice_status to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_status
|
||||
-- requires: invoice_status_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper;
|
||||
|
||||
insert into invoice_status (invoice_status, name)
|
||||
values ('created', 'Created')
|
||||
, ('sent', 'Sent')
|
||||
, ('paid', 'Paid')
|
||||
, ('unpaid', 'Unpaid')
|
||||
;
|
||||
|
||||
insert into invoice_status_i18n (invoice_status, lang_tag, name)
|
||||
values ('created', 'ca', 'Creada')
|
||||
, ('sent', 'ca', 'Enviada')
|
||||
, ('paid', 'ca', 'Cobrada')
|
||||
, ('unpaid', 'ca', 'No cobrada')
|
||||
, ('created', 'es', 'Creada')
|
||||
, ('sent', 'es', 'Enviada')
|
||||
, ('paid', 'es', 'Cobrada')
|
||||
, ('unpaid', 'es', 'No cobrada')
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,64 @@
|
|||
-- Deploy camper:compute_new_invoice_amount to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: company
|
||||
-- requires: currency
|
||||
-- requires: tax
|
||||
-- requires: new_invoice_product
|
||||
-- requires: new_invoice_amount
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function compute_new_invoice_amount(company_id integer, products new_invoice_product[]) returns new_invoice_amount as
|
||||
$$
|
||||
declare
|
||||
result new_invoice_amount;
|
||||
begin
|
||||
if array_length(products, 1) is null then
|
||||
select to_price(0, decimal_digits), array[]::text[][], to_price(0, decimal_digits)
|
||||
from company
|
||||
join currency using (currency_code)
|
||||
where company.company_id = compute_new_invoice_amount.company_id
|
||||
into result.subtotal, result.taxes, result.total;
|
||||
else
|
||||
with product as (
|
||||
select round(parse_price(price, currency.decimal_digits) * quantity * (1 - discount_rate))::integer as subtotal
|
||||
, tax
|
||||
, decimal_digits
|
||||
from unnest(products)
|
||||
join company on company.company_id = compute_new_invoice_amount.company_id
|
||||
join currency using (currency_code)
|
||||
)
|
||||
, tax_amount as (
|
||||
select tax_id
|
||||
, sum(round(subtotal * tax.rate)::integer)::integer as amount
|
||||
, decimal_digits
|
||||
from product, unnest(product.tax) as product_tax(tax_id)
|
||||
join tax using (tax_id)
|
||||
group by tax_id, decimal_digits
|
||||
)
|
||||
, tax_total as (
|
||||
select sum(amount)::integer as amount, array_agg(array[name, to_price(amount, decimal_digits)]) as taxes
|
||||
from tax_amount
|
||||
join tax using (tax_id)
|
||||
)
|
||||
select to_price(sum(subtotal)::integer, decimal_digits)
|
||||
, coalesce(taxes, array[]::text[][])
|
||||
, to_price(sum(subtotal)::integer + coalesce(tax_total.amount, 0), decimal_digits) as total
|
||||
from product, tax_total
|
||||
group by tax_total.amount, taxes, decimal_digits
|
||||
into result.subtotal, result.taxes, result.total;
|
||||
end if;
|
||||
|
||||
return result;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
stable;
|
||||
|
||||
revoke execute on function compute_new_invoice_amount(integer, new_invoice_product[]) from public;
|
||||
grant execute on function compute_new_invoice_amount(integer, new_invoice_product[]) to employee;
|
||||
grant execute on function compute_new_invoice_amount(integer, new_invoice_product[]) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,45 @@
|
|||
-- Deploy camper:contact to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: user_profile
|
||||
-- requires: company
|
||||
-- requires: id_document_type
|
||||
-- requires: country_code
|
||||
-- requires: country
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table contact (
|
||||
contact_id integer generated by default as identity primary key,
|
||||
company_id integer not null references company,
|
||||
slug uuid not null unique default gen_random_uuid(),
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 1),
|
||||
id_document_type_id varchar(1) not null references id_document_type,
|
||||
id_document_number text 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,
|
||||
created_at timestamptz not null default current_timestamp
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table contact to employee;
|
||||
grant select, insert, update, delete on table contact to admin;
|
||||
|
||||
alter table contact enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on contact
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = contact.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,31 @@
|
|||
-- Deploy camper:contact_email to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: email
|
||||
-- requires: contact
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, 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 employee;
|
||||
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
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,30 @@
|
|||
-- Deploy camper:contact_phone to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: extension_pg_libphonenumber
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, 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 employee;
|
||||
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
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,14 @@
|
|||
-- Deploy camper:discount_rate to pg
|
||||
-- requires: schema_camper
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create domain discount_rate as numeric
|
||||
check (VALUE >= 0 and VALUE <= 1);
|
||||
|
||||
comment on domain discount_rate is
|
||||
'A rate for discount in the range [0, 1]';
|
||||
|
||||
commit;
|
|
@ -0,0 +1,72 @@
|
|||
-- Deploy camper:edit_contact to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: email
|
||||
-- requires: country_code
|
||||
-- requires: contact
|
||||
-- requires: extension_pg_libphonenumber
|
||||
-- requires: contact_phone
|
||||
-- requires: contact_email
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function edit_contact(contact_slug uuid, name text, id_document_type_id text, id_document_number text, phone text, email text, address text, city text, province text, postal_code text, country_code country_code) returns uuid as
|
||||
$$
|
||||
declare
|
||||
cid integer;
|
||||
begin
|
||||
update contact
|
||||
set name = edit_contact.name
|
||||
, id_document_type_id = edit_contact.id_document_type_id
|
||||
, id_document_number = edit_contact.id_document_number
|
||||
, address = edit_contact.address
|
||||
, city = edit_contact.city
|
||||
, province = edit_contact.province
|
||||
, postal_code = edit_contact.postal_code
|
||||
, country_code = edit_contact.country_code
|
||||
where slug = contact_slug
|
||||
returning contact_id
|
||||
into cid
|
||||
;
|
||||
|
||||
if cid is null then
|
||||
return null;
|
||||
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(phone, coalesce(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, email)
|
||||
on conflict (contact_id) do update
|
||||
set email = excluded.email
|
||||
;
|
||||
end if;
|
||||
|
||||
return contact_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function edit_contact(uuid, text, text, text, text, text, text, text, text, text, country_code) from public;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, text, text, text, text, text, country_code) to employee;
|
||||
grant execute on function edit_contact(uuid, text, text, text, text, text, text, text, text, text, country_code) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,110 @@
|
|||
-- Deploy camper:edit_invoice to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice
|
||||
-- requires: currency
|
||||
-- requires: parse_price
|
||||
-- requires: edited_invoice_product
|
||||
-- requires: tax
|
||||
-- requires: invoice_product
|
||||
-- requires: invoice_product_product
|
||||
-- requires: invoice_product_tax
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function edit_invoice(invoice_slug uuid, invoice_status text, contact_id integer, notes text, payment_method_id integer, products edited_invoice_product[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
iid integer;
|
||||
products_to_keep integer[];
|
||||
products_to_delete integer[];
|
||||
company integer;
|
||||
ccode text;
|
||||
product edited_invoice_product;
|
||||
ipid integer;
|
||||
begin
|
||||
update invoice
|
||||
set contact_id = edit_invoice.contact_id
|
||||
, invoice_status = edit_invoice.invoice_status
|
||||
, notes = edit_invoice.notes
|
||||
, payment_method_id = edit_invoice.payment_method_id
|
||||
where slug = invoice_slug
|
||||
returning invoice_id, company_id, currency_code
|
||||
into iid, company, ccode
|
||||
;
|
||||
|
||||
if iid is null then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
foreach product in array products
|
||||
loop
|
||||
if product.invoice_product_id is null then
|
||||
insert into invoice_product (invoice_id, name, description, price, quantity, discount_rate)
|
||||
select iid
|
||||
, product.name
|
||||
, coalesce(product.description, '')
|
||||
, parse_price(product.price, currency.decimal_digits)
|
||||
, product.quantity
|
||||
, product.discount_rate
|
||||
from currency
|
||||
where currency_code = ccode
|
||||
returning invoice_product_id
|
||||
into ipid;
|
||||
else
|
||||
ipid := product.invoice_product_id;
|
||||
|
||||
update invoice_product
|
||||
set name = product.name
|
||||
, description = coalesce(product.description, '')
|
||||
, price = parse_price(product.price, currency.decimal_digits)
|
||||
, quantity = product.quantity
|
||||
, discount_rate = product.discount_rate
|
||||
from currency
|
||||
where invoice_product_id = ipid
|
||||
and currency_code = ccode;
|
||||
end if;
|
||||
products_to_keep := array_append(products_to_keep, ipid);
|
||||
|
||||
if product.product_id is null then
|
||||
delete from invoice_product_product where invoice_product_id = ipid;
|
||||
else
|
||||
insert into invoice_product_product (invoice_product_id, product_id)
|
||||
values (ipid, product.product_id)
|
||||
on conflict (invoice_product_id) do update
|
||||
set product_id = product.product_id;
|
||||
end if;
|
||||
|
||||
delete from invoice_product_tax where invoice_product_id = ipid;
|
||||
|
||||
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
|
||||
select ipid, tax_id, tax.rate
|
||||
from tax
|
||||
join unnest(product.tax) as ptax(tax_id) using (tax_id);
|
||||
end loop;
|
||||
|
||||
select array_agg(invoice_product_id)
|
||||
into products_to_delete
|
||||
from invoice_product
|
||||
where invoice_id = iid
|
||||
and not (invoice_product_id = any(products_to_keep));
|
||||
|
||||
if array_length(products_to_delete, 1) > 0 then
|
||||
delete from invoice_product_tax where invoice_product_id = any(products_to_delete);
|
||||
delete from invoice_product_product where invoice_product_id = any(products_to_delete);
|
||||
delete from invoice_product where invoice_product_id = any(products_to_delete);
|
||||
end if;
|
||||
|
||||
return invoice_slug;
|
||||
end;
|
||||
$$
|
||||
language plpgsql;
|
||||
|
||||
revoke execute on function edit_invoice(uuid, text, integer, text, integer, edited_invoice_product[]) from public;
|
||||
grant execute on function edit_invoice(uuid, text, integer, text, integer, edited_invoice_product[]) to employee;
|
||||
grant execute on function edit_invoice(uuid, text, integer, text, integer, edited_invoice_product[]) to admin;
|
||||
|
||||
|
||||
commit;
|
|
@ -0,0 +1,20 @@
|
|||
-- Deploy camper:edited_invoice_product to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: discount_rate
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create type edited_invoice_product as
|
||||
( invoice_product_id integer
|
||||
, product_id integer
|
||||
, name text
|
||||
, description text
|
||||
, price text
|
||||
, quantity integer
|
||||
, discount_rate discount_rate
|
||||
, tax integer[]
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,44 @@
|
|||
-- Deploy camper:invoice to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: user_profile
|
||||
-- requires: company
|
||||
-- requires: contact
|
||||
-- requires: invoice_status
|
||||
-- requires: currency
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table invoice (
|
||||
invoice_id integer generated by default as identity primary key,
|
||||
company_id integer not null references company,
|
||||
slug uuid not null unique default gen_random_uuid(),
|
||||
invoice_number text not null constraint invoice_number_not_empty check(length(trim(invoice_number)) > 1),
|
||||
invoice_date date not null default current_date,
|
||||
contact_id integer not null references contact,
|
||||
invoice_status text not null default 'created' references invoice_status,
|
||||
notes text not null default '',
|
||||
payment_method_id integer not null references payment_method,
|
||||
currency_code text not null references currency,
|
||||
created_at timestamptz not null default current_timestamp
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table invoice to employee;
|
||||
grant select, insert, update, delete on table invoice to admin;
|
||||
|
||||
alter table invoice enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on invoice
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = invoice.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,22 @@
|
|||
-- Deploy camper:invoice_amount to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_product
|
||||
-- requires: invoice_product_amount
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace view invoice_amount as
|
||||
select invoice_id
|
||||
, sum(subtotal)::integer as subtotal
|
||||
, sum(total)::integer as total
|
||||
from invoice_product
|
||||
join invoice_product_amount using (invoice_product_id)
|
||||
group by invoice_id
|
||||
;
|
||||
|
||||
grant select on table invoice_amount to employee;
|
||||
grant select on table invoice_amount to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,32 @@
|
|||
-- Deploy camper:invoice_number_counter to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: company
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table invoice_number_counter (
|
||||
company_id integer not null references company,
|
||||
year integer not null constraint year_always_positive check(year > 0),
|
||||
currval integer not null constraint counter_zero_or_positive check(currval >= 0),
|
||||
primary key (company_id, year)
|
||||
);
|
||||
|
||||
grant select, insert, update on table invoice_number_counter to employee;
|
||||
grant select, insert, update on table invoice_number_counter to admin;
|
||||
|
||||
alter table invoice_number_counter enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on invoice_number_counter
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = invoice_number_counter.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,38 @@
|
|||
-- Deploy camper:invoice_product to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice
|
||||
-- requires: discount_rate
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table invoice_product (
|
||||
invoice_product_id integer generated by default as identity primary key,
|
||||
invoice_id integer not null references invoice,
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 0),
|
||||
description text not null default '',
|
||||
price integer not null,
|
||||
quantity integer not null default 1,
|
||||
discount_rate discount_rate not null default 0.0
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table invoice_product to employee;
|
||||
grant select, insert, update, delete on table invoice_product to admin;
|
||||
|
||||
grant usage on sequence invoice_product_invoice_product_id_seq to employee;
|
||||
grant usage on sequence invoice_product_invoice_product_id_seq to admin;
|
||||
|
||||
alter table invoice_product enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on invoice_product
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from invoice
|
||||
where invoice.invoice_id = invoice_product.invoice_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,22 @@
|
|||
-- Deploy camper:invoice_product_amount to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_product
|
||||
-- requires: invoice_product_tax
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace view invoice_product_amount as
|
||||
select invoice_product_id
|
||||
, round(price * quantity * (1 - discount_rate))::integer as subtotal
|
||||
, max(round(price * quantity * (1 - discount_rate))::integer) + coalesce(sum(round(round(price * quantity * (1 - discount_rate))::integer * tax_rate)::integer)::integer, 0) as total
|
||||
from invoice_product
|
||||
left join invoice_product_tax using (invoice_product_id)
|
||||
group by invoice_product_id, price, quantity, discount_rate
|
||||
;
|
||||
|
||||
grant select on table invoice_product_amount to employee;
|
||||
grant select on table invoice_product_amount to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,18 @@
|
|||
-- Deploy camper:invoice_product_product to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_product
|
||||
-- requires: product
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper;
|
||||
|
||||
create table invoice_product_product (
|
||||
invoice_product_id integer primary key references invoice_product,
|
||||
product_id integer not null references product
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table invoice_product_product to employee;
|
||||
grant select, insert, update, delete on table invoice_product_product to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,33 @@
|
|||
-- Deploy camper:invoice_product_tax to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_product
|
||||
-- requires: tax
|
||||
-- requires: tax_rate
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table invoice_product_tax (
|
||||
invoice_product_id integer not null references invoice_product,
|
||||
tax_id integer not null references tax,
|
||||
tax_rate tax_rate not null,
|
||||
primary key (invoice_product_id, tax_id)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table invoice_product_tax to employee;
|
||||
grant select, insert, update, delete on table invoice_product_tax to admin;
|
||||
|
||||
alter table invoice_product_tax enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on invoice_product_tax
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from invoice_product
|
||||
where invoice_product.invoice_product_id = invoice_product_tax.invoice_product_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,16 @@
|
|||
-- Deploy camper:invoice_status to pg
|
||||
-- requires: schema_camper
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table invoice_status (
|
||||
invoice_status text primary key,
|
||||
name text not null
|
||||
);
|
||||
|
||||
grant select on table invoice_status to employee;
|
||||
grant select on table invoice_status to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,20 @@
|
|||
-- Deploy camper:invoice_status_i18n to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_status
|
||||
-- requires: language
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table invoice_status_i18n (
|
||||
invoice_status text not null references invoice_status,
|
||||
lang_tag text not null references language,
|
||||
name text not null,
|
||||
primary key (invoice_status, lang_tag)
|
||||
);
|
||||
|
||||
grant select on table invoice_status_i18n to employee;
|
||||
grant select on table invoice_status_i18n to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Deploy camper:invoice_tax_amount to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_product
|
||||
-- requires: invoice_product_tax
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace view invoice_tax_amount as
|
||||
select invoice_id
|
||||
, tax_id
|
||||
, sum(round(round(price * quantity * (1 - discount_rate))::integer * tax_rate)::integer)::integer as amount
|
||||
from invoice_product
|
||||
join invoice_product_tax using (invoice_product_id)
|
||||
group by invoice_id
|
||||
, tax_id
|
||||
;
|
||||
|
||||
grant select on table invoice_tax_amount to employee;
|
||||
grant select on table invoice_tax_amount to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,14 @@
|
|||
-- Deploy camper:new_invoice_amount to pg
|
||||
-- requires: schema_camper
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create type new_invoice_amount as (
|
||||
subtotal text,
|
||||
taxes text[][],
|
||||
total text
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,19 @@
|
|||
-- Deploy camper:new_invoice_product to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: discount_rate
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create type new_invoice_product as (
|
||||
product_id integer,
|
||||
name text,
|
||||
description text,
|
||||
price text,
|
||||
quantity integer,
|
||||
discount_rate discount_rate,
|
||||
tax integer[]
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,38 @@
|
|||
-- Deploy camper:next_invoice_number to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: invoice_number_counter
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function next_invoice_number(company integer, invoice_date date) returns text
|
||||
as
|
||||
$$
|
||||
declare
|
||||
num integer;
|
||||
invoice_number text;
|
||||
begin
|
||||
insert into invoice_number_counter (company_id, year, currval)
|
||||
values (next_invoice_number.company, date_part('year', invoice_date), 1)
|
||||
on conflict (company_id, year) do
|
||||
update
|
||||
set currval = invoice_number_counter.currval + 1
|
||||
returning currval
|
||||
into num;
|
||||
|
||||
select to_char(invoice_date, to_char(num, 'FM' || replace(invoice_number_format, '"', '\""')))
|
||||
into invoice_number
|
||||
from company
|
||||
where company_id = next_invoice_number.company;
|
||||
|
||||
return invoice_number;
|
||||
end;
|
||||
$$
|
||||
language plpgsql;
|
||||
|
||||
revoke execute on function next_invoice_number(integer, date) from public;
|
||||
grant execute on function next_invoice_number(integer, date) to employee;
|
||||
grant execute on function next_invoice_number(integer, date) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,34 @@
|
|||
-- Deploy camper:payment_method to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: user_profile
|
||||
-- requires: company
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table payment_method (
|
||||
payment_method_id integer generated by default as identity primary key,
|
||||
company_id integer not null references company,
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 0),
|
||||
instructions text not null
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table payment_method to employee;
|
||||
grant select, insert, update, delete on table payment_method to admin;
|
||||
|
||||
alter table payment_method enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on payment_method
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = payment_method.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,40 @@
|
|||
-- Deploy camper:product to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: user_profile
|
||||
-- requires: company
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table product (
|
||||
product_id integer generated by default as identity primary key,
|
||||
company_id integer not null references company,
|
||||
slug uuid not null default gen_random_uuid(),
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 0),
|
||||
description text not null default '',
|
||||
price integer not null,
|
||||
created_at timestamptz not null default current_timestamp
|
||||
);
|
||||
|
||||
comment on column product.price is
|
||||
'Price is stored in cents.';
|
||||
|
||||
grant select, insert, update, delete on table product to employee;
|
||||
grant select, insert, update, delete on table product to admin;
|
||||
|
||||
alter table product enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on product
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = product.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,31 @@
|
|||
-- Deploy camper:product_tax to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: product
|
||||
-- requires: tax
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table product_tax (
|
||||
product_id integer not null references product,
|
||||
tax_id integer not null references tax,
|
||||
primary key (product_id, tax_id)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table product_tax to employee;
|
||||
grant select, insert, update, delete on table product_tax to admin;
|
||||
|
||||
alter table product_tax enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on product_tax
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from product
|
||||
where product.product_id = product_tax.product_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,37 @@
|
|||
-- Deploy camper:tax to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: user_profile
|
||||
-- requires: company
|
||||
-- requires: tax_rate
|
||||
-- requires: tax_class
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table tax (
|
||||
tax_id integer generated by default as identity primary key,
|
||||
company_id integer not null references company,
|
||||
tax_class_id integer not null references tax_class,
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 0),
|
||||
rate tax_rate not null
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table tax to employee;
|
||||
grant select, insert, update, delete on table tax to admin;
|
||||
|
||||
alter table tax enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on tax
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = tax.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,33 @@
|
|||
-- Deploy camper:tax_class to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: user_profile
|
||||
-- requires: company
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table tax_class (
|
||||
tax_class_id integer generated by default as identity not null primary key,
|
||||
company_id integer not null references company,
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 0)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table tax_class to employee;
|
||||
grant select, insert, update, delete on table tax_class to admin;
|
||||
|
||||
alter table tax_class enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on tax_class
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = tax_class.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,14 @@
|
|||
-- Deploy camper:tax_rate to pg
|
||||
-- requires: schema_camper
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create domain tax_rate as numeric
|
||||
check (value > -1 and value < 1);
|
||||
|
||||
comment on domain tax_rate is
|
||||
'A rate for taxes in the range (-1, 1)';
|
||||
|
||||
commit;
|
3
go.mod
3
go.mod
|
@ -4,15 +4,16 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/jackc/pgconn v1.11.0
|
||||
github.com/jackc/pgio v1.0.0
|
||||
github.com/jackc/pgtype v1.10.0
|
||||
github.com/jackc/pgx/v4 v4.15.0
|
||||
github.com/leonelquinteros/gotext v1.5.0
|
||||
github.com/rainycape/unidecode v0.0.0-20150906181237-c9cf8cdbbfe8
|
||||
golang.org/x/text v0.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.2 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -88,6 +88,8 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rainycape/unidecode v0.0.0-20150906181237-c9cf8cdbbfe8 h1:iZTHFqK/oFrjyFDkiw5U/RjQxkMlkpq6tHQIO407i+s=
|
||||
github.com/rainycape/unidecode v0.0.0-20150906181237-c9cf8cdbbfe8/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
||||
|
|
|
@ -13,9 +13,11 @@ import (
|
|||
"dev.tandem.ws/tandem/camper/pkg/booking"
|
||||
"dev.tandem.ws/tandem/camper/pkg/campsite"
|
||||
"dev.tandem.ws/tandem/camper/pkg/company"
|
||||
"dev.tandem.ws/tandem/camper/pkg/customer"
|
||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||
"dev.tandem.ws/tandem/camper/pkg/home"
|
||||
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
||||
"dev.tandem.ws/tandem/camper/pkg/invoice"
|
||||
"dev.tandem.ws/tandem/camper/pkg/legal"
|
||||
"dev.tandem.ws/tandem/camper/pkg/location"
|
||||
"dev.tandem.ws/tandem/camper/pkg/media"
|
||||
|
@ -32,7 +34,9 @@ type adminHandler struct {
|
|||
booking *booking.AdminHandler
|
||||
campsite *campsite.AdminHandler
|
||||
company *company.AdminHandler
|
||||
customer *customer.AdminHandler
|
||||
home *home.AdminHandler
|
||||
invoice *invoice.AdminHandler
|
||||
legal *legal.AdminHandler
|
||||
location *location.AdminHandler
|
||||
media *media.AdminHandler
|
||||
|
@ -49,7 +53,9 @@ func newAdminHandler(mediaDir string) *adminHandler {
|
|||
booking: booking.NewAdminHandler(),
|
||||
campsite: campsite.NewAdminHandler(),
|
||||
company: company.NewAdminHandler(),
|
||||
customer: customer.NewAdminHandler(),
|
||||
home: home.NewAdminHandler(),
|
||||
invoice: invoice.NewAdminHandler(),
|
||||
legal: legal.NewAdminHandler(),
|
||||
location: location.NewAdminHandler(),
|
||||
media: media.NewAdminHandler(mediaDir),
|
||||
|
@ -85,10 +91,14 @@ func (h *adminHandler) Handle(user *auth.User, company *auth.Company, conn *data
|
|||
h.campsite.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "company":
|
||||
h.company.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "customers":
|
||||
h.customer.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "home":
|
||||
h.home.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "legal":
|
||||
h.legal.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "invoices":
|
||||
h.invoice.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "location":
|
||||
h.location.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "media":
|
||||
|
|
|
@ -7,6 +7,7 @@ package booking
|
|||
|
||||
import (
|
||||
"context"
|
||||
"dev.tandem.ws/tandem/camper/pkg/ods"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -197,16 +198,16 @@ func (page bookingIndex) MustRender(w http.ResponseWriter, r *http.Request, user
|
|||
"Holder Name",
|
||||
"Status",
|
||||
}
|
||||
ods, err := writeTableOds(page, columns, user.Locale, func(sb *strings.Builder, entry *bookingEntry) error {
|
||||
if err := writeCellString(sb, entry.Reference); err != nil {
|
||||
table, err := ods.WriteTable(page, columns, user.Locale, func(sb *strings.Builder, entry *bookingEntry) error {
|
||||
if err := ods.WriteCellString(sb, entry.Reference); err != nil {
|
||||
return err
|
||||
}
|
||||
writeCellDate(sb, entry.ArrivalDate)
|
||||
writeCellDate(sb, entry.DepartureDate)
|
||||
if err := writeCellString(sb, entry.HolderName); err != nil {
|
||||
ods.WriteCellDate(sb, entry.ArrivalDate)
|
||||
ods.WriteCellDate(sb, entry.DepartureDate)
|
||||
if err := ods.WriteCellString(sb, entry.HolderName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeCellString(sb, entry.StatusLabel); err != nil {
|
||||
if err := ods.WriteCellString(sb, entry.StatusLabel); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -214,7 +215,7 @@ func (page bookingIndex) MustRender(w http.ResponseWriter, r *http.Request, user
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mustWriteOdsResponse(w, ods, user.Locale.Pgettext("bookings.ods", "filename"))
|
||||
ods.MustWriteResponse(w, table, user.Locale.Pgettext("bookings.ods", "filename"))
|
||||
default:
|
||||
template.MustRenderAdmin(w, r, user, company, "booking/index.gohtml", page)
|
||||
}
|
||||
|
|
|
@ -71,9 +71,9 @@ func newCheckinForm(slug string) *checkInForm {
|
|||
}
|
||||
|
||||
func (f *checkInForm) FillFromDatabase(ctx context.Context, conn *database.Conn, l *locale.Locale) error {
|
||||
documentTypes := mustGetDocumentTypeOptions(ctx, conn, l)
|
||||
documentTypes := form.MustGetDocumentTypeOptions(ctx, conn, l)
|
||||
sexes := mustGetSexOptions(ctx, conn, l)
|
||||
countries := mustGetCountryOptions(ctx, conn, l)
|
||||
countries := form.MustGetCountryOptions(ctx, conn, l)
|
||||
|
||||
rows, err := conn.Query(ctx, `
|
||||
select array[id_document_type_id]
|
||||
|
@ -118,10 +118,6 @@ func (f *checkInForm) FillFromDatabase(ctx context.Context, conn *database.Conn,
|
|||
return nil
|
||||
}
|
||||
|
||||
func mustGetDocumentTypeOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*form.Option {
|
||||
return form.MustGetOptions(ctx, conn, "select idt.id_document_type_id::text, coalesce(i18n.name, idt.name) as l10n_name from id_document_type as idt left join id_document_type_i18n as i18n on idt.id_document_type_id = i18n.id_document_type_id and i18n.lang_tag = $1 order by l10n_name", l.Language)
|
||||
}
|
||||
|
||||
func mustGetSexOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*form.Option {
|
||||
return form.MustGetOptions(ctx, conn, "select sex.sex_id::text, coalesce(i18n.name, sex.name) as l10n_name from sex left join sex_i18n as i18n on sex.sex_id = i18n.sex_id and i18n.lang_tag = $1 order by l10n_name", l.Language)
|
||||
}
|
||||
|
@ -131,9 +127,9 @@ func (f *checkInForm) Parse(r *http.Request, user *auth.User, conn *database.Con
|
|||
return err
|
||||
}
|
||||
|
||||
documentTypes := mustGetDocumentTypeOptions(r.Context(), conn, user.Locale)
|
||||
documentTypes := form.MustGetDocumentTypeOptions(r.Context(), conn, user.Locale)
|
||||
sexes := mustGetSexOptions(r.Context(), conn, user.Locale)
|
||||
countries := mustGetCountryOptions(r.Context(), conn, user.Locale)
|
||||
countries := form.MustGetCountryOptions(r.Context(), conn, user.Locale)
|
||||
|
||||
guest := newGuestFormWithOptions(documentTypes, sexes, countries, nil)
|
||||
count := guest.count(r)
|
||||
|
@ -180,9 +176,9 @@ type guestForm struct {
|
|||
}
|
||||
|
||||
func newGuestForm(ctx context.Context, conn *database.Conn, l *locale.Locale, slug string) (*guestForm, error) {
|
||||
documentTypes := mustGetDocumentTypeOptions(ctx, conn, l)
|
||||
documentTypes := form.MustGetDocumentTypeOptions(ctx, conn, l)
|
||||
sexes := mustGetSexOptions(ctx, conn, l)
|
||||
countries := mustGetCountryOptions(ctx, conn, l)
|
||||
countries := form.MustGetCountryOptions(ctx, conn, l)
|
||||
|
||||
var country []string
|
||||
row := conn.QueryRow(ctx, "select array[coalesce(country_code, '')] from booking where slug = $1", slug)
|
||||
|
|
|
@ -547,7 +547,7 @@ func newBookingCustomerFields(ctx context.Context, conn *database.Conn, l *local
|
|||
},
|
||||
Country: &form.Select{
|
||||
Name: "country",
|
||||
Options: mustGetCountryOptions(ctx, conn, l),
|
||||
Options: form.MustGetCountryOptions(ctx, conn, l),
|
||||
},
|
||||
Email: &form.Input{
|
||||
Name: "email",
|
||||
|
@ -561,10 +561,6 @@ func newBookingCustomerFields(ctx context.Context, conn *database.Conn, l *local
|
|||
}
|
||||
}
|
||||
|
||||
func mustGetCountryOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*form.Option {
|
||||
return form.MustGetOptions(ctx, conn, "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name", l.Language)
|
||||
}
|
||||
|
||||
func (f *bookingCustomerFields) FillValues(r *http.Request) {
|
||||
f.FullName.FillValue(r)
|
||||
f.Address.FillValue(r)
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
package customer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||
"dev.tandem.ws/tandem/camper/pkg/form"
|
||||
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
)
|
||||
|
||||
type AdminHandler struct {
|
||||
}
|
||||
|
||||
func NewAdminHandler() *AdminHandler {
|
||||
return &AdminHandler{}
|
||||
}
|
||||
|
||||
func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *database.Conn) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var head string
|
||||
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
|
||||
|
||||
switch head {
|
||||
case "":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
serveCustomerIndex(w, r, user, company, conn)
|
||||
case http.MethodPost:
|
||||
addCustomer(w, r, user, company, conn)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPost)
|
||||
}
|
||||
case "new":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
f := newCustomerForm(r.Context(), conn, user.Locale)
|
||||
f.MustRender(w, r, user, company)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet)
|
||||
}
|
||||
default:
|
||||
f := newCustomerForm(r.Context(), conn, user.Locale)
|
||||
if err := f.FillFromDatabase(r.Context(), conn, head); err != nil {
|
||||
if database.ErrorIsNotFound(err) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
h.customerHandler(user, company, conn, f).ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) customerHandler(user *auth.User, company *auth.Company, conn *database.Conn, f *customerForm) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var head string
|
||||
head, r.URL.Path = httplib.ShiftPath(r.URL.Path)
|
||||
|
||||
switch head {
|
||||
case "":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
f.MustRender(w, r, user, company)
|
||||
case http.MethodPut:
|
||||
editCustomer(w, r, user, company, conn, f)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
|
||||
}
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func serveCustomerIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
|
||||
customers, err := collectCustomerEntries(r.Context(), conn, company)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
page := &customerIndex{
|
||||
Customers: customers,
|
||||
}
|
||||
page.MustRender(w, r, user, company)
|
||||
}
|
||||
|
||||
func collectCustomerEntries(ctx context.Context, conn *database.Conn, company *auth.Company) ([]*customerEntry, error) {
|
||||
rows, err := conn.Query(ctx, `
|
||||
select '/admin/customers/' || slug
|
||||
, name
|
||||
, coalesce(email::text, '')
|
||||
, coalesce(phone::text, '')
|
||||
from contact
|
||||
left join contact_email using (contact_id)
|
||||
left join contact_phone using (contact_id)
|
||||
where company_id = $1
|
||||
order by name
|
||||
`, company.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var customers []*customerEntry
|
||||
for rows.Next() {
|
||||
customer := &customerEntry{}
|
||||
if err = rows.Scan(&customer.URL, &customer.Name, &customer.Email, &customer.Phone); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
customers = append(customers, customer)
|
||||
}
|
||||
|
||||
return customers, nil
|
||||
}
|
||||
|
||||
type customerEntry struct {
|
||||
URL string
|
||||
Name string
|
||||
Email string
|
||||
Phone string
|
||||
}
|
||||
|
||||
type customerIndex struct {
|
||||
Customers []*customerEntry
|
||||
}
|
||||
|
||||
func (page *customerIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "customer/index.gohtml", page)
|
||||
}
|
||||
|
||||
func addCustomer(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
|
||||
f := newCustomerForm(r.Context(), conn, user.Locale)
|
||||
processCustomerForm(w, r, user, company, conn, f, func(ctx context.Context, tx *database.Tx) error {
|
||||
var err error
|
||||
f.Slug, err = tx.AddContact(ctx, company.ID, f.FullName.Val, f.IDDocumentType.String(), f.IDDocumentNumber.Val, f.Phone.Val, f.Email.Val, f.Address.Val, f.City.Val, f.Province.Val, f.PostalCode.Val, f.Country.String())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func editCustomer(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *customerForm) {
|
||||
processCustomerForm(w, r, user, company, conn, f, func(ctx context.Context, tx *database.Tx) error {
|
||||
_, err := tx.EditContact(ctx, f.Slug, f.FullName.Val, f.IDDocumentType.String(), f.IDDocumentNumber.Val, f.Phone.Val, f.Email.Val, f.Address.Val, f.City.Val, f.Province.Val, f.PostalCode.Val, f.Country.String())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func processCustomerForm(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *customerForm, act func(ctx context.Context, tx *database.Tx) error) {
|
||||
if err := f.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := user.VerifyCSRFToken(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if ok, err := f.Valid(r.Context(), conn, user.Locale); err != nil {
|
||||
panic(err)
|
||||
} else if !ok {
|
||||
if !httplib.IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
f.MustRender(w, r, user, company)
|
||||
return
|
||||
}
|
||||
|
||||
tx := conn.MustBegin(r.Context())
|
||||
if err := act(r.Context(), tx); err == nil {
|
||||
if err := tx.Commit(r.Context()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
if err := tx.Rollback(r.Context()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
httplib.Redirect(w, r, "/admin/customers", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
type customerForm struct {
|
||||
URL string
|
||||
Slug string
|
||||
FullName *form.Input
|
||||
IDDocumentType *form.Select
|
||||
IDDocumentNumber *form.Input
|
||||
Address *form.Input
|
||||
City *form.Input
|
||||
Province *form.Input
|
||||
PostalCode *form.Input
|
||||
Country *form.Select
|
||||
Email *form.Input
|
||||
Phone *form.Input
|
||||
}
|
||||
|
||||
func newCustomerForm(ctx context.Context, conn *database.Conn, l *locale.Locale) *customerForm {
|
||||
return &customerForm{
|
||||
FullName: &form.Input{
|
||||
Name: "full_name",
|
||||
},
|
||||
IDDocumentType: &form.Select{
|
||||
Name: "id_document_type",
|
||||
Options: form.MustGetDocumentTypeOptions(ctx, conn, l),
|
||||
},
|
||||
IDDocumentNumber: &form.Input{
|
||||
Name: "id_document_number",
|
||||
},
|
||||
Address: &form.Input{
|
||||
Name: "address",
|
||||
},
|
||||
City: &form.Input{
|
||||
Name: "city",
|
||||
},
|
||||
Province: &form.Input{
|
||||
Name: "province",
|
||||
},
|
||||
PostalCode: &form.Input{
|
||||
Name: "postal_code",
|
||||
},
|
||||
Country: &form.Select{
|
||||
Name: "country",
|
||||
Options: form.MustGetCountryOptions(ctx, conn, l),
|
||||
},
|
||||
Email: &form.Input{
|
||||
Name: "email",
|
||||
},
|
||||
Phone: &form.Input{
|
||||
Name: "phone",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *customerForm) FillFromDatabase(ctx context.Context, conn *database.Conn, slug string) error {
|
||||
row := conn.QueryRow(ctx, `
|
||||
select '/admin/customers/' || slug
|
||||
, slug
|
||||
, name
|
||||
, array[id_document_type_id::text]
|
||||
, id_document_number
|
||||
, address
|
||||
, city
|
||||
, province
|
||||
, postal_code
|
||||
, array[country_code::text]
|
||||
, coalesce(email::text, '')
|
||||
, coalesce(phone::text, '')
|
||||
from contact as text
|
||||
left join contact_email using (contact_id)
|
||||
left join contact_phone using (contact_id)
|
||||
where slug = $1
|
||||
`, slug)
|
||||
return row.Scan(
|
||||
&f.URL,
|
||||
&f.Slug,
|
||||
&f.FullName.Val,
|
||||
&f.IDDocumentType.Selected,
|
||||
&f.IDDocumentNumber.Val,
|
||||
&f.Address.Val,
|
||||
&f.City.Val,
|
||||
&f.Province.Val,
|
||||
&f.PostalCode.Val,
|
||||
&f.Country.Selected,
|
||||
&f.Email.Val,
|
||||
&f.Phone.Val,
|
||||
)
|
||||
}
|
||||
|
||||
func (f *customerForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.FullName.FillValue(r)
|
||||
f.IDDocumentType.FillValue(r)
|
||||
f.IDDocumentNumber.FillValue(r)
|
||||
f.Address.FillValue(r)
|
||||
f.City.FillValue(r)
|
||||
f.Province.FillValue(r)
|
||||
f.PostalCode.FillValue(r)
|
||||
f.Country.FillValue(r)
|
||||
f.Email.FillValue(r)
|
||||
f.Phone.FillValue(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *customerForm) Valid(ctx context.Context, conn *database.Conn, l *locale.Locale) (bool, error) {
|
||||
v := form.NewValidator(l)
|
||||
|
||||
var country string
|
||||
if v.CheckSelectedOptions(f.Country, l.GettextNoop("Selected country is not valid.")) {
|
||||
country = f.Country.Selected[0]
|
||||
}
|
||||
|
||||
v.CheckSelectedOptions(f.IDDocumentType, l.GettextNoop("Selected ID document type is not valid."))
|
||||
v.CheckRequired(f.IDDocumentNumber, l.GettextNoop("ID document number can not be empty."))
|
||||
|
||||
if v.CheckRequired(f.FullName, l.GettextNoop("Full name can not be empty.")) {
|
||||
v.CheckMinLength(f.FullName, 1, l.GettextNoop("Full name must have at least one letter."))
|
||||
}
|
||||
|
||||
v.CheckRequired(f.Address, l.GettextNoop("Address can not be empty."))
|
||||
v.CheckRequired(f.City, l.GettextNoop("Town or village can not be empty."))
|
||||
if v.CheckRequired(f.PostalCode, l.GettextNoop("Postcode can not be empty.")) && country != "" {
|
||||
if _, err := v.CheckValidPostalCode(ctx, conn, f.PostalCode, country, l.GettextNoop("This postcode is not valid.")); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
if f.Email.Val != "" {
|
||||
v.CheckValidEmail(f.Email, l.GettextNoop("This email is not valid. It should be like name@domain.com."))
|
||||
}
|
||||
if f.Phone.Val != "" && country != "" {
|
||||
if _, err := v.CheckValidPhone(ctx, conn, f.Phone, country, l.GettextNoop("This phone number is not valid.")); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return v.AllOK, nil
|
||||
}
|
||||
|
||||
func (f *customerForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "customer/form.gohtml", f)
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
const EditedInvoiceProductTypeName = "edited_invoice_product"
|
||||
|
||||
type EditedInvoiceProduct struct {
|
||||
*NewInvoiceProduct
|
||||
InvoiceProductId int
|
||||
}
|
||||
|
||||
func (src EditedInvoiceProduct) EncodeBinary(ci *pgtype.ConnInfo, dst []byte) ([]byte, error) {
|
||||
typeName := EditedInvoiceProductTypeName
|
||||
dt, ok := ci.DataTypeForName(typeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
|
||||
}
|
||||
var invoiceProductId interface{}
|
||||
if src.InvoiceProductId > 0 {
|
||||
invoiceProductId = src.InvoiceProductId
|
||||
}
|
||||
var productId interface{}
|
||||
if src.ProductId > 0 {
|
||||
productId = src.ProductId
|
||||
}
|
||||
values := []interface{}{
|
||||
invoiceProductId,
|
||||
productId,
|
||||
src.Name,
|
||||
src.Description,
|
||||
src.Price,
|
||||
src.Quantity,
|
||||
src.Discount,
|
||||
src.Taxes,
|
||||
}
|
||||
ct := pgtype.NewValue(dt.Value).(*pgtype.CompositeType)
|
||||
if err := ct.Set(values); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ct.EncodeBinary(ci, dst)
|
||||
}
|
||||
|
||||
type EditedInvoiceProductArray []*EditedInvoiceProduct
|
||||
|
||||
func (src EditedInvoiceProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
typeName := EditedInvoiceProductTypeName
|
||||
dt, ok := ci.DataTypeForName(typeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
|
||||
}
|
||||
|
||||
arrayHeader := pgtype.ArrayHeader{
|
||||
ElementOID: int32(dt.OID),
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(src)), LowerBound: 1}},
|
||||
}
|
||||
buf = arrayHeader.EncodeBinary(ci, buf)
|
||||
for _, product := range src {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := product.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
return buf, nil
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
const NewInvoiceProductTypeName = "new_invoice_product"
|
||||
|
||||
type NewInvoiceProduct struct {
|
||||
ProductId int
|
||||
Name string
|
||||
Description string
|
||||
Price string
|
||||
Quantity int
|
||||
Discount float64
|
||||
Taxes []int
|
||||
}
|
||||
|
||||
func (src NewInvoiceProduct) EncodeBinary(ci *pgtype.ConnInfo, dst []byte) ([]byte, error) {
|
||||
typeName := NewInvoiceProductTypeName
|
||||
dt, ok := ci.DataTypeForName(typeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
|
||||
}
|
||||
var productId interface{}
|
||||
if src.ProductId > 0 {
|
||||
productId = src.ProductId
|
||||
}
|
||||
values := []interface{}{
|
||||
productId,
|
||||
src.Name,
|
||||
src.Description,
|
||||
src.Price,
|
||||
src.Quantity,
|
||||
src.Discount,
|
||||
src.Taxes,
|
||||
}
|
||||
ct := pgtype.NewValue(dt.Value).(*pgtype.CompositeType)
|
||||
if err := ct.Set(values); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ct.EncodeBinary(ci, dst)
|
||||
}
|
||||
|
||||
type NewInvoiceProductArray []*NewInvoiceProduct
|
||||
|
||||
func (src NewInvoiceProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
typeName := NewInvoiceProductTypeName
|
||||
dt, ok := ci.DataTypeForName(typeName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
|
||||
}
|
||||
|
||||
arrayHeader := pgtype.ArrayHeader{
|
||||
ElementOID: int32(dt.OID),
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(src)), LowerBound: 1}},
|
||||
}
|
||||
buf = arrayHeader.EncodeBinary(ci, buf)
|
||||
for _, product := range src {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := product.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
return buf, nil
|
||||
}
|
|
@ -370,3 +370,19 @@ func (c *Conn) CheckInGuests(ctx context.Context, bookingSlug string, guests []*
|
|||
_, err := c.Exec(ctx, "select check_in_guests(booking_id, $2) from booking where slug = $1", bookingSlug, CheckedInGuestArray(guests))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) AddInvoice(ctx context.Context, companyID int, date string, customerID int, notes string, paymentMethodID int, products NewInvoiceProductArray) (string, error) {
|
||||
return c.GetText(ctx, "select add_invoice($1, $2, $3, $4, $5, $6)", companyID, date, customerID, notes, paymentMethodID, products)
|
||||
}
|
||||
|
||||
func (c *Conn) EditInvoice(ctx context.Context, invoiceSlug string, invoiceStatus string, contactID int, notes string, paymentMethodID int, products EditedInvoiceProductArray) (string, error) {
|
||||
return c.GetText(ctx, "select edit_invoice($1, $2, $3, $4, $5, $6)", invoiceSlug, invoiceStatus, contactID, notes, paymentMethodID, products)
|
||||
}
|
||||
|
||||
func (tx *Tx) AddContact(ctx context.Context, companyID int, name string, idDocumentType string, idDocumentNumber string, phone string, email string, address string, city string, province string, postalCode string, countryCode string) (string, error) {
|
||||
return tx.GetText(ctx, "select add_contact($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", companyID, name, idDocumentType, idDocumentNumber, phone, email, address, city, province, postalCode, countryCode)
|
||||
}
|
||||
|
||||
func (tx *Tx) EditContact(ctx context.Context, contactSlug, name string, idDocumentType string, idDocumentNumber string, phone string, email string, address string, city string, province string, postalCode string, countryCode string) (string, error) {
|
||||
return tx.GetText(ctx, "select edit_contact($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)", contactSlug, name, idDocumentType, idDocumentNumber, phone, email, address, city, province, postalCode, countryCode)
|
||||
}
|
||||
|
|
|
@ -49,6 +49,11 @@ func registerConnectionTypes(ctx context.Context, conn *pgx.Conn) error {
|
|||
return err
|
||||
}
|
||||
|
||||
discountRateOID, err := registerType(ctx, conn, &pgtype.Numeric{}, "discount_rate")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
redsysRequestType, err := pgtype.NewCompositeType(
|
||||
RedsysRequestTypeName,
|
||||
[]pgtype.CompositeTypeField{
|
||||
|
@ -150,6 +155,47 @@ func registerConnectionTypes(ctx context.Context, conn *pgx.Conn) error {
|
|||
return err
|
||||
}
|
||||
|
||||
newInvoiceProductType, err := pgtype.NewCompositeType(
|
||||
NewInvoiceProductTypeName,
|
||||
[]pgtype.CompositeTypeField{
|
||||
{"product_id", pgtype.Int4OID},
|
||||
{"name", pgtype.TextOID},
|
||||
{"description", pgtype.TextOID},
|
||||
{"price", pgtype.TextOID},
|
||||
{"quantity", pgtype.Int4OID},
|
||||
{"discount_rate", discountRateOID},
|
||||
{"tax", pgtype.Int4ArrayOID},
|
||||
},
|
||||
conn.ConnInfo(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = registerType(ctx, conn, newInvoiceProductType, newInvoiceProductType.TypeName()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
editedInvoiceProductType, err := pgtype.NewCompositeType(
|
||||
EditedInvoiceProductTypeName,
|
||||
[]pgtype.CompositeTypeField{
|
||||
{"invoice_product_id", pgtype.Int4OID},
|
||||
{"product_id", pgtype.Int4OID},
|
||||
{"name", pgtype.TextOID},
|
||||
{"description", pgtype.TextOID},
|
||||
{"price", pgtype.TextOID},
|
||||
{"quantity", pgtype.Int4OID},
|
||||
{"discount_rate", discountRateOID},
|
||||
{"tax", pgtype.Int4ArrayOID},
|
||||
},
|
||||
conn.ConnInfo(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = registerType(ctx, conn, editedInvoiceProductType, editedInvoiceProductType.TypeName()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ package form
|
|||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
|
@ -113,3 +114,11 @@ func MustGetOptions(ctx context.Context, conn *database.Conn, sql string, args .
|
|||
|
||||
return options
|
||||
}
|
||||
|
||||
func MustGetCountryOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*Option {
|
||||
return MustGetOptions(ctx, conn, "select country.country_code, coalesce(i18n.name, country.name) as l10n_name from country left join country_i18n as i18n on country.country_code = i18n.country_code and i18n.lang_tag = $1 order by l10n_name", l.Language)
|
||||
}
|
||||
|
||||
func MustGetDocumentTypeOptions(ctx context.Context, conn *database.Conn, l *locale.Locale) []*Option {
|
||||
return MustGetOptions(ctx, conn, "select idt.id_document_type_id::text, coalesce(i18n.name, idt.name) as l10n_name from id_document_type as idt left join id_document_type_i18n as i18n on idt.id_document_type_id = i18n.id_document_type_id and i18n.lang_tag = $1 order by l10n_name", l.Language)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ const (
|
|||
HxLocation = "HX-Location"
|
||||
HxRedirect = "HX-Redirect"
|
||||
HxRequest = "HX-Request"
|
||||
HxTriggerAfterSettle = "HX-Trigger-After-Settle"
|
||||
)
|
||||
|
||||
func Relocate(w http.ResponseWriter, r *http.Request, url string, code int) {
|
||||
|
@ -37,6 +38,10 @@ func Redirect(w http.ResponseWriter, r *http.Request, url string, code int) {
|
|||
}
|
||||
}
|
||||
|
||||
func TriggerAfterSettle(w http.ResponseWriter, trigger string) {
|
||||
w.Header().Set(HxTriggerAfterSettle, trigger)
|
||||
}
|
||||
|
||||
func IsHTMxRequest(r *http.Request) bool {
|
||||
return r.Header.Get(HxRequest) == "true"
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,65 @@
|
|||
package invoice
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||
"dev.tandem.ws/tandem/camper/pkg/ods"
|
||||
)
|
||||
|
||||
func mustWriteInvoicesOds(invoices []*IndexEntry, taxes map[int]taxMap, taxColumns map[int]string, company *auth.Company, locale *locale.Locale) []byte {
|
||||
taxIDs := extractTaxIDs(taxColumns)
|
||||
columns := make([]string, 6+len(taxIDs))
|
||||
columns[0] = "Date"
|
||||
columns[1] = "Invoice Num."
|
||||
columns[2] = "Customer"
|
||||
columns[3] = "Status"
|
||||
i := 4
|
||||
for _, taxID := range taxIDs {
|
||||
columns[i] = taxColumns[taxID]
|
||||
i++
|
||||
}
|
||||
columns[i] = "Amount"
|
||||
table, err := ods.WriteTable(invoices, columns, locale, func(sb *strings.Builder, invoice *IndexEntry) error {
|
||||
ods.WriteCellDate(sb, invoice.Date)
|
||||
if err := ods.WriteCellString(sb, invoice.Number); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ods.WriteCellString(sb, invoice.CustomerName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ods.WriteCellString(sb, invoice.StatusLabel); err != nil {
|
||||
return err
|
||||
}
|
||||
writeTaxes(sb, taxes[invoice.ID], taxIDs, company, locale)
|
||||
ods.WriteCellFloat(sb, invoice.Total, company, locale)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func extractTaxIDs(taxColumns map[int]string) []int {
|
||||
taxIDs := make([]int, len(taxColumns))
|
||||
i := 0
|
||||
for k := range taxColumns {
|
||||
taxIDs[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Ints(taxIDs[:])
|
||||
return taxIDs
|
||||
}
|
||||
|
||||
func writeTaxes(sb *strings.Builder, taxes taxMap, taxIDs []int, company *auth.Company, locale *locale.Locale) {
|
||||
for _, taxID := range taxIDs {
|
||||
var amount string
|
||||
if taxes != nil {
|
||||
amount = taxes[taxID]
|
||||
}
|
||||
ods.WriteCellFloat(sb, amount, company, locale)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package invoice
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
)
|
||||
|
||||
func mustWriteInvoicesPdf(r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, slugs []string) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
w := zip.NewWriter(buf)
|
||||
for _, slug := range slugs {
|
||||
inv := mustGetInvoice(r.Context(), conn, company, slug)
|
||||
if inv == nil {
|
||||
continue
|
||||
}
|
||||
f, err := w.Create(fmt.Sprintf("%s-%s.pdf", inv.Number, template.Slugify(inv.Invoicee.Name)))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mustWriteInvoicePdf(f, r, user, company, inv)
|
||||
}
|
||||
mustClose(w)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func mustWriteInvoicePdf(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, inv *invoice) {
|
||||
cmd := exec.Command("weasyprint", "--stylesheet", "web/static/invoice.css", "-", "-")
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
err := stdout.Close()
|
||||
if !errors.Is(err, os.ErrClosed) {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
if err = cmd.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
go func() {
|
||||
defer mustClose(stdin)
|
||||
template.MustRenderAdmin(stdin, r, user, company, "invoice/view.gohtml", inv)
|
||||
}()
|
||||
if _, err = io.Copy(w, stdout); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Printf("ERR - %v\n", stderr.String())
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func mustClose(closer io.Closer) {
|
||||
if err := closer.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
package booking
|
||||
package ods
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -44,7 +46,7 @@ const (
|
|||
`
|
||||
)
|
||||
|
||||
func writeTableOds[K interface{}](rows []*K, columns []string, locale *locale.Locale, writeRow func(*strings.Builder, *K) error) ([]byte, error) {
|
||||
func WriteTable[K interface{}](rows []*K, columns []string, locale *locale.Locale, writeRow func(*strings.Builder, *K) error) ([]byte, error) {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
@ -90,7 +92,7 @@ func writeTableOds[K interface{}](rows []*K, columns []string, locale *locale.Lo
|
|||
sb.WriteString(` <table:table-row table:style-name="ro1">
|
||||
`)
|
||||
for _, t := range columns {
|
||||
if err := writeCellString(&sb, locale.GetC(t, "header")); err != nil {
|
||||
if err := WriteCellString(&sb, locale.GetC(t, "header")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +150,7 @@ func writeOdsFile(ods *zip.Writer, name string, content string, method uint16) e
|
|||
return err
|
||||
}
|
||||
|
||||
func writeCellString(sb *strings.Builder, s string) error {
|
||||
func WriteCellString(sb *strings.Builder, s string) error {
|
||||
sb.WriteString(` <table:table-cell office:value-type="string" calcext:value-type="string"><text:p>`)
|
||||
if err := xml.EscapeText(sb, []byte(s)); err != nil {
|
||||
return err
|
||||
|
@ -157,11 +159,15 @@ func writeCellString(sb *strings.Builder, s string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func writeCellDate(sb *strings.Builder, t time.Time) {
|
||||
func WriteCellDate(sb *strings.Builder, t time.Time) {
|
||||
sb.WriteString(fmt.Sprintf(" <table:table-cell table:style-name=\"ce1\" office:value-type=\"date\" office:date-value=\"%s\" calcext:value-type=\"date\"><text:p>%s</text:p></table:table-cell>\n", t.Format(database.ISODateFormat), t.Format("02/01/06")))
|
||||
}
|
||||
|
||||
func mustWriteOdsResponse(w http.ResponseWriter, ods []byte, filename string) {
|
||||
func WriteCellFloat(sb *strings.Builder, s string, company *auth.Company, locale *locale.Locale) {
|
||||
sb.WriteString(fmt.Sprintf(" <table:table-cell office:value-type=\"float\" office:value=\"%s\" calcext:value-type=\"float\"><text:p>%s</text:p></table:table-cell>\n", s, template.FormatPrice(s, locale.Language, locale.CurrencyPattern, company.DecimalDigits, company.CurrencySymbol)))
|
||||
}
|
||||
|
||||
func MustWriteResponse(w http.ResponseWriter, ods []byte, filename string) {
|
||||
w.Header().Set("Content-Type", "application/vnd.oasis.opendocument.spreadsheet")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
|
||||
w.WriteHeader(http.StatusOK)
|
|
@ -117,6 +117,9 @@ func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templ
|
|||
"formatDateAttr": func(time time.Time) string {
|
||||
return time.Format(database.ISODateFormat)
|
||||
},
|
||||
"formatPercent": func(value int) string {
|
||||
return fmt.Sprintf("%d %%", value)
|
||||
},
|
||||
"today": func() string {
|
||||
return time.Now().Format(database.ISODateFormat)
|
||||
},
|
||||
|
@ -129,22 +132,35 @@ func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templ
|
|||
"dec": func(i int) int {
|
||||
return i - 1
|
||||
},
|
||||
"add": func(y, x int) int {
|
||||
return x + y
|
||||
},
|
||||
"sub": func(y, x int) int {
|
||||
return x - y
|
||||
},
|
||||
"int": func(v interface{}) int {
|
||||
switch v := v.(type) {
|
||||
case int:
|
||||
return v
|
||||
case bool:
|
||||
if v {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
case time.Weekday:
|
||||
return int(v)
|
||||
case time.Month:
|
||||
return int(v)
|
||||
default:
|
||||
panic(fmt.Errorf("Could not convert to integer"))
|
||||
panic(fmt.Errorf("could not convert to integer"))
|
||||
}
|
||||
},
|
||||
"hexToDec": func(s string) int {
|
||||
num, _ := strconv.ParseInt(s, 16, 0)
|
||||
return int(num)
|
||||
},
|
||||
"slugify": Slugify,
|
||||
})
|
||||
templates = append(templates, "form.gohtml")
|
||||
files := make([]string, len(templates))
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/rainycape/unidecode"
|
||||
)
|
||||
|
||||
var (
|
||||
nonValidChars = regexp.MustCompile("[^a-z0-9-_]")
|
||||
multipleDashes = regexp.MustCompile("-+")
|
||||
)
|
||||
|
||||
func Slugify(s string) (slug string) {
|
||||
slug = strings.TrimSpace(s)
|
||||
slug = unidecode.Unidecode(slug)
|
||||
slug = strings.ToLower(slug)
|
||||
slug = nonValidChars.ReplaceAllString(slug, "-")
|
||||
slug = multipleDashes.ReplaceAllString(slug, "-")
|
||||
slug = strings.Trim(slug, "-_")
|
||||
return slug
|
||||
}
|
614
po/ca.po
614
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: camper\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2024-04-26 16:53+0200\n"
|
||||
"POT-Creation-Date: 2024-04-28 20:05+0200\n"
|
||||
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||
|
@ -239,6 +239,7 @@ msgstr "Opcions del tipus d’allotjament"
|
|||
#: web/templates/mail/payment/details.gotxt:39
|
||||
#: web/templates/public/booking/fields.gohtml:146
|
||||
#: web/templates/admin/payment/details.gohtml:140
|
||||
#: web/templates/admin/customer/form.gohtml:28
|
||||
#: web/templates/admin/booking/fields.gohtml:188
|
||||
msgctxt "title"
|
||||
msgid "Customer Details"
|
||||
|
@ -247,6 +248,7 @@ msgstr "Detalls del client"
|
|||
#: web/templates/mail/payment/details.gotxt:41
|
||||
#: web/templates/public/booking/fields.gohtml:149
|
||||
#: web/templates/admin/payment/details.gohtml:143
|
||||
#: web/templates/admin/customer/form.gohtml:31
|
||||
#: web/templates/admin/booking/fields.gohtml:191
|
||||
msgctxt "input"
|
||||
msgid "Full name"
|
||||
|
@ -255,6 +257,7 @@ msgstr "Nom i cognoms"
|
|||
#: web/templates/mail/payment/details.gotxt:42
|
||||
#: web/templates/public/booking/fields.gohtml:158
|
||||
#: web/templates/admin/payment/details.gohtml:147
|
||||
#: web/templates/admin/customer/form.gohtml:69
|
||||
#: web/templates/admin/taxDetails.gohtml:69
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
|
@ -263,6 +266,7 @@ msgstr "Adreça"
|
|||
#: web/templates/mail/payment/details.gotxt:43
|
||||
#: web/templates/public/booking/fields.gohtml:167
|
||||
#: web/templates/admin/payment/details.gohtml:151
|
||||
#: web/templates/admin/customer/form.gohtml:105
|
||||
#: web/templates/admin/taxDetails.gohtml:93
|
||||
msgctxt "input"
|
||||
msgid "Postcode"
|
||||
|
@ -278,6 +282,7 @@ msgstr "Població"
|
|||
#: web/templates/mail/payment/details.gotxt:45
|
||||
#: web/templates/public/booking/fields.gohtml:187
|
||||
#: web/templates/admin/payment/details.gohtml:159
|
||||
#: web/templates/admin/customer/form.gohtml:117
|
||||
#: web/templates/admin/taxDetails.gohtml:101
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
|
@ -411,11 +416,16 @@ msgid "Order Number"
|
|||
msgstr "Número de comanda"
|
||||
|
||||
#: web/templates/public/payment/details.gohtml:8
|
||||
#: web/templates/admin/invoice/index.gohtml:103
|
||||
#: web/templates/admin/invoice/view.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
#: web/templates/public/payment/details.gohtml:12
|
||||
#: web/templates/admin/invoice/form.gohtml:119
|
||||
#: web/templates/admin/invoice/view.gohtml:63
|
||||
#: web/templates/admin/invoice/view.gohtml:103
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
@ -579,6 +589,11 @@ msgctxt "input"
|
|||
msgid "Year"
|
||||
msgstr "Any"
|
||||
|
||||
#: web/templates/public/form.gohtml:83 web/templates/admin/form.gohtml:83
|
||||
msgctxt "action"
|
||||
msgid "Filters"
|
||||
msgstr "Filtres"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:49
|
||||
#: web/templates/public/booking/fields.gohtml:278
|
||||
msgctxt "action"
|
||||
|
@ -929,7 +944,7 @@ msgstr "Menú"
|
|||
#: web/templates/admin/campsite/type/option/form.gohtml:16
|
||||
#: web/templates/admin/campsite/type/option/index.gohtml:10
|
||||
#: web/templates/admin/campsite/type/index.gohtml:10
|
||||
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:95
|
||||
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:101
|
||||
#: web/templates/admin/booking/fields.gohtml:266
|
||||
msgctxt "title"
|
||||
msgid "Campsites"
|
||||
|
@ -1004,13 +1019,15 @@ msgid "Campground map"
|
|||
msgstr "Mapa del càmping"
|
||||
|
||||
#: web/templates/public/booking/fields.gohtml:176
|
||||
#: web/templates/admin/customer/form.gohtml:81
|
||||
msgctxt "input"
|
||||
msgid "Town or village"
|
||||
msgstr "Població"
|
||||
|
||||
#: web/templates/public/booking/fields.gohtml:193
|
||||
#: web/templates/admin/customer/form.gohtml:121
|
||||
#: web/templates/admin/booking/fields.gohtml:204
|
||||
#: web/templates/admin/booking/guest.gohtml:109
|
||||
#: web/templates/admin/booking/guest.gohtml:111
|
||||
msgid "Choose a country"
|
||||
msgstr "Esculli un país"
|
||||
|
||||
|
@ -1164,6 +1181,7 @@ msgstr "Àlies"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:51
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:41
|
||||
#: web/templates/admin/season/form.gohtml:50
|
||||
#: web/templates/admin/invoice/product-form.gohtml:16
|
||||
#: web/templates/admin/services/form.gohtml:53
|
||||
#: web/templates/admin/profile.gohtml:29
|
||||
#: web/templates/admin/surroundings/form.gohtml:41
|
||||
|
@ -1188,6 +1206,8 @@ msgstr "Contingut"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:287
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:98
|
||||
#: web/templates/admin/season/form.gohtml:73
|
||||
#: web/templates/admin/customer/form.gohtml:153
|
||||
#: web/templates/admin/invoice/form.gohtml:137
|
||||
#: web/templates/admin/services/form.gohtml:81
|
||||
#: web/templates/admin/surroundings/form.gohtml:69
|
||||
#: web/templates/admin/surroundings/index.gohtml:58
|
||||
|
@ -1211,6 +1231,7 @@ msgstr "Actualitza"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:289
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:100
|
||||
#: web/templates/admin/season/form.gohtml:75
|
||||
#: web/templates/admin/customer/form.gohtml:155
|
||||
#: web/templates/admin/services/form.gohtml:83
|
||||
#: web/templates/admin/surroundings/form.gohtml:71
|
||||
#: web/templates/admin/amenity/feature/form.gohtml:67
|
||||
|
@ -1232,6 +1253,7 @@ msgstr "Afegeix text legal"
|
|||
#: web/templates/admin/campsite/type/option/index.gohtml:30
|
||||
#: web/templates/admin/campsite/type/index.gohtml:29
|
||||
#: web/templates/admin/season/index.gohtml:29
|
||||
#: web/templates/admin/customer/index.gohtml:19
|
||||
#: web/templates/admin/user/index.gohtml:20
|
||||
#: web/templates/admin/surroundings/index.gohtml:83
|
||||
#: web/templates/admin/amenity/feature/index.gohtml:30
|
||||
|
@ -1725,6 +1747,7 @@ msgid "Per night"
|
|||
msgstr "Per nit"
|
||||
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:84
|
||||
#: web/templates/admin/invoice/product-form.gohtml:29
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
@ -1824,12 +1847,316 @@ msgctxt "action"
|
|||
msgid "Cancel"
|
||||
msgstr "Canceŀla"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:8
|
||||
msgctxt "title"
|
||||
msgid "Edit Customer"
|
||||
msgstr "Edició del client"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "New Customer"
|
||||
msgstr "Nou client"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:15
|
||||
#: web/templates/admin/invoice/index.gohtml:105
|
||||
msgctxt "title"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:44
|
||||
#: web/templates/admin/booking/guest.gohtml:8
|
||||
msgctxt "input"
|
||||
msgid "ID document number"
|
||||
msgstr "Número de document d’identitat"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:56
|
||||
#: web/templates/admin/booking/guest.gohtml:20
|
||||
msgctxt "input"
|
||||
msgid "ID document type"
|
||||
msgstr "Tipus de document"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:61
|
||||
#: web/templates/admin/booking/guest.gohtml:25
|
||||
msgid "Choose an ID document type"
|
||||
msgstr "Esculli un tipus de document"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:93
|
||||
#: web/templates/admin/taxDetails.gohtml:85
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Província"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:129
|
||||
#: web/templates/admin/booking/fields.gohtml:239
|
||||
msgctxt "input"
|
||||
msgid "Email (optional)"
|
||||
msgstr "Correu-e (opcional)"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:140
|
||||
#: web/templates/admin/booking/fields.gohtml:248
|
||||
#: web/templates/admin/booking/guest.gohtml:119
|
||||
msgctxt "input"
|
||||
msgid "Phone (optional)"
|
||||
msgstr "Telèfon (opcional)"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:95
|
||||
msgctxt "title"
|
||||
msgid "Customers"
|
||||
msgstr "Clients"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:14
|
||||
msgctxt "action"
|
||||
msgid "Add Customer"
|
||||
msgstr "Afegeix client"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:20
|
||||
#: web/templates/admin/user/login-attempts.gohtml:20
|
||||
#: web/templates/admin/user/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Email"
|
||||
msgstr "Correu-e"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Phone"
|
||||
msgstr "Telèfon"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:33
|
||||
msgid "No customer found."
|
||||
msgstr "No s’ha trobat cap client."
|
||||
|
||||
#: web/templates/admin/dashboard.gohtml:6
|
||||
#: web/templates/admin/dashboard.gohtml:13 web/templates/admin/layout.gohtml:89
|
||||
msgctxt "title"
|
||||
msgid "Dashboard"
|
||||
msgstr "Tauler"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:11
|
||||
#: web/templates/admin/booking/guest.gohtml:5
|
||||
msgctxt "action"
|
||||
msgid "Remove"
|
||||
msgstr "Esborra"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:44
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantitat"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:58
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descompte (%)"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:73
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Imposts"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:79
|
||||
msgid "Select a TAX"
|
||||
msgstr "Escolliu un impost"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:4
|
||||
msgctxt "title"
|
||||
msgid "Edit Invoice “%s”"
|
||||
msgstr "Edició de la factura «%s»"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:6
|
||||
msgctxt "title"
|
||||
msgid "New Invoice"
|
||||
msgstr "Nova factura"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:15
|
||||
#: web/templates/admin/invoice/index.gohtml:2
|
||||
#: web/templates/admin/invoice/view.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:98
|
||||
msgctxt "title"
|
||||
msgid "Invoices"
|
||||
msgstr "Factures"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:32
|
||||
msgid "Product “%s” removed"
|
||||
msgstr "S’ha esborrat el producte «%s»"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:36
|
||||
msgctxt "action"
|
||||
msgid "Undo"
|
||||
msgstr "Desfes"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:51
|
||||
#: web/templates/admin/invoice/index.gohtml:39
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:56
|
||||
msgid "Select a customer"
|
||||
msgstr "Esculliu un client"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:64
|
||||
msgctxt "input"
|
||||
msgid "Invoice date"
|
||||
msgstr "Data de la factura"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:77
|
||||
#: web/templates/admin/invoice/index.gohtml:51
|
||||
msgctxt "input"
|
||||
msgid "Invoice status"
|
||||
msgstr "Estat de la factura"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:92
|
||||
msgctxt "input"
|
||||
msgid "Notes (optional)"
|
||||
msgstr "Notes (opcional)"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:109
|
||||
#: web/templates/admin/invoice/view.gohtml:59
|
||||
msgctxt "title"
|
||||
msgid "Subtotal"
|
||||
msgstr "Subtotal"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:133
|
||||
msgctxt "action"
|
||||
msgid "Add products"
|
||||
msgstr "Afegeix productes"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:140
|
||||
msgctxt "action"
|
||||
msgid "Save"
|
||||
msgstr "Desa"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:25
|
||||
msgctxt "action"
|
||||
msgid "Download invoices"
|
||||
msgstr "Descarrega factures"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:28
|
||||
msgctxt "action"
|
||||
msgid "Export list"
|
||||
msgstr "Exporta llista"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:43
|
||||
msgid "All customers"
|
||||
msgstr "Tots els clients"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:55
|
||||
msgid "All statuses"
|
||||
msgstr "Tots els estats"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:63
|
||||
msgctxt "input"
|
||||
msgid "From date"
|
||||
msgstr "De la data"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:72
|
||||
msgctxt "input"
|
||||
msgid "To date"
|
||||
msgstr "A la data"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:81
|
||||
msgctxt "input"
|
||||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:91
|
||||
msgctxt "action"
|
||||
msgid "Filter"
|
||||
msgstr "Filtra"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:94
|
||||
msgctxt "action"
|
||||
msgid "Reset"
|
||||
msgstr "Restableix"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:97
|
||||
msgctxt "action"
|
||||
msgid "Add invoice"
|
||||
msgstr "Afegeix factura"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:102
|
||||
msgctxt "invoice"
|
||||
msgid "All"
|
||||
msgstr "Totes"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:104
|
||||
msgctxt "title"
|
||||
msgid "Invoice Num."
|
||||
msgstr "Núm. de factura"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:106
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estat"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:107
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descàrrega"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:108
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:115
|
||||
msgctxt "action"
|
||||
msgid "Select invoice %v"
|
||||
msgstr "Selecciona la factura %v"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:144
|
||||
msgctxt "action"
|
||||
msgid "Download invoice %s"
|
||||
msgstr "Descarrega la factura %s"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:154
|
||||
msgid "No invoices added yet."
|
||||
msgstr "No s’ha afegit cap factura encara."
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:161
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:2
|
||||
msgctxt "title"
|
||||
msgid "Invoice %s"
|
||||
msgstr "Factura %s"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:15
|
||||
msgctxt "action"
|
||||
msgid "Edit"
|
||||
msgstr "Edita"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:18
|
||||
msgctxt "action"
|
||||
msgid "Download invoice"
|
||||
msgstr "Descarrega factura"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:53
|
||||
msgctxt "title"
|
||||
msgid "Concept"
|
||||
msgstr "Concepte"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:54
|
||||
msgctxt "title"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:56
|
||||
msgctxt "title"
|
||||
msgid "Discount"
|
||||
msgstr "Descompte"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:58
|
||||
msgctxt "title"
|
||||
msgid "Units"
|
||||
msgstr "Unitats"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:93
|
||||
msgctxt "title"
|
||||
msgid "Tax Base"
|
||||
msgstr "Base imposable"
|
||||
|
||||
#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:18
|
||||
msgctxt "title"
|
||||
msgid "Login"
|
||||
|
@ -1928,12 +2255,6 @@ msgctxt "title"
|
|||
msgid "Users"
|
||||
msgstr "Usuaris"
|
||||
|
||||
#: web/templates/admin/user/login-attempts.gohtml:20
|
||||
#: web/templates/admin/user/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Email"
|
||||
msgstr "Correu-e"
|
||||
|
||||
#: web/templates/admin/user/login-attempts.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "IP Address"
|
||||
|
@ -1985,11 +2306,6 @@ msgctxt "input"
|
|||
msgid "Trade Name"
|
||||
msgstr "Nom comercial"
|
||||
|
||||
#: web/templates/admin/taxDetails.gohtml:85
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Província"
|
||||
|
||||
#: web/templates/admin/taxDetails.gohtml:111
|
||||
msgctxt "input"
|
||||
msgid "Currency"
|
||||
|
@ -2214,11 +2530,11 @@ msgctxt "title"
|
|||
msgid "Bookings"
|
||||
msgstr "Reserves"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:101
|
||||
#: web/templates/admin/layout.gohtml:107
|
||||
msgid "Breadcrumb"
|
||||
msgstr "Fil d’Ariadna"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:113
|
||||
#: web/templates/admin/layout.gohtml:119
|
||||
msgid "Camper Version: %s"
|
||||
msgstr "Camper versió: %s"
|
||||
|
||||
|
@ -2340,7 +2656,7 @@ msgid "Country (optional)"
|
|||
msgstr "País (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:212
|
||||
#: web/templates/admin/booking/guest.gohtml:128
|
||||
#: web/templates/admin/booking/guest.gohtml:130
|
||||
msgctxt "input"
|
||||
msgid "Address (optional)"
|
||||
msgstr "Adreça (opcional)"
|
||||
|
@ -2355,17 +2671,6 @@ msgctxt "input"
|
|||
msgid "Town or village (optional)"
|
||||
msgstr "Població (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:239
|
||||
msgctxt "input"
|
||||
msgid "Email (optional)"
|
||||
msgstr "Correu-e (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:248
|
||||
#: web/templates/admin/booking/guest.gohtml:117
|
||||
msgctxt "input"
|
||||
msgid "Phone (optional)"
|
||||
msgstr "Telèfon (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/form.gohtml:8
|
||||
msgctxt "title"
|
||||
msgid "Edit Booking"
|
||||
|
@ -2430,60 +2735,41 @@ msgstr "Nom del titular"
|
|||
msgid "No booking found."
|
||||
msgstr "No s’ha trobat cap reserva."
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:5
|
||||
msgctxt "action"
|
||||
msgid "Remove"
|
||||
msgstr "Esborra"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:8
|
||||
msgctxt "input"
|
||||
msgid "ID document number"
|
||||
msgstr "Número de document d’identitat"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:20
|
||||
msgctxt "input"
|
||||
msgid "ID document type"
|
||||
msgstr "Tipus de document"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:25
|
||||
msgid "Choose an ID document type"
|
||||
msgstr "Esculli un tipus de document"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:33
|
||||
msgctxt "input"
|
||||
msgid "ID document issue date (if any)"
|
||||
msgstr "Data d’expedició (si hi consta)"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:44
|
||||
#: web/templates/admin/booking/guest.gohtml:45
|
||||
msgctxt "input"
|
||||
msgid "First surname"
|
||||
msgstr "Primer cognom"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:56
|
||||
#: web/templates/admin/booking/guest.gohtml:57
|
||||
msgctxt "input"
|
||||
msgid "Second surname (if has one)"
|
||||
msgstr "Segon cognom (si en té)"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:67
|
||||
#: web/templates/admin/booking/guest.gohtml:68
|
||||
msgctxt "input"
|
||||
msgid "Given name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:79
|
||||
#: web/templates/admin/booking/guest.gohtml:80
|
||||
msgctxt "input"
|
||||
msgid "Sex"
|
||||
msgstr "Sexe"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:84
|
||||
#: web/templates/admin/booking/guest.gohtml:85
|
||||
msgid "Choose a sex"
|
||||
msgstr "Esculli un sexe"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:92
|
||||
#: web/templates/admin/booking/guest.gohtml:93
|
||||
msgctxt "input"
|
||||
msgid "Birthdate"
|
||||
msgstr "Data de naixement"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:104
|
||||
#: web/templates/admin/booking/guest.gohtml:106
|
||||
msgctxt "input"
|
||||
msgid "Nationality"
|
||||
msgstr "Nacionalitat"
|
||||
|
@ -2553,8 +2839,9 @@ msgstr "Rebut amb èxit el pagament de la reserva"
|
|||
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
|
||||
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
|
||||
#: pkg/campsite/feature.go:269 pkg/season/admin.go:411
|
||||
#: pkg/services/admin.go:316 pkg/surroundings/admin.go:340
|
||||
#: pkg/amenity/feature.go:269 pkg/amenity/admin.go:283
|
||||
#: pkg/invoice/admin.go:1092 pkg/services/admin.go:316
|
||||
#: pkg/surroundings/admin.go:340 pkg/amenity/feature.go:269
|
||||
#: pkg/amenity/admin.go:283
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podeu deixar el nom en blanc."
|
||||
|
||||
|
@ -2589,12 +2876,12 @@ msgid "Slide image must be an image media type."
|
|||
msgstr "La imatge de la diapositiva ha de ser un mèdia de tipus imatge."
|
||||
|
||||
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:224
|
||||
#: pkg/booking/public.go:596
|
||||
#: pkg/booking/public.go:592
|
||||
msgid "Email can not be empty."
|
||||
msgstr "No podeu deixar el correu-e en blanc."
|
||||
|
||||
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:225
|
||||
#: pkg/booking/admin.go:437 pkg/booking/public.go:597
|
||||
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:312
|
||||
#: pkg/company/admin.go:225 pkg/booking/admin.go:438 pkg/booking/public.go:593
|
||||
msgid "This email is not valid. It should be like name@domain.com."
|
||||
msgstr "Aquest correu-e no és vàlid. Hauria de ser similar a nom@domini.com."
|
||||
|
||||
|
@ -2623,7 +2910,7 @@ msgstr "L’idioma escollit no és vàlid."
|
|||
msgid "File must be a valid PNG or JPEG image."
|
||||
msgstr "El fitxer has de ser una imatge PNG o JPEG vàlida."
|
||||
|
||||
#: pkg/app/admin.go:73
|
||||
#: pkg/app/admin.go:79
|
||||
msgid "Access forbidden"
|
||||
msgstr "Accés prohibit"
|
||||
|
||||
|
@ -2651,15 +2938,15 @@ msgstr "El valor del màxim ha de ser un número enter."
|
|||
msgid "Maximum must be equal or greater than minimum."
|
||||
msgstr "El valor del màxim ha de ser igual o superir al del mínim."
|
||||
|
||||
#: pkg/campsite/types/option.go:382
|
||||
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1093
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podeu deixar el preu en blanc."
|
||||
|
||||
#: pkg/campsite/types/option.go:383
|
||||
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1094
|
||||
msgid "Price must be a decimal number."
|
||||
msgstr "El preu ha de ser un número decimal."
|
||||
|
||||
#: pkg/campsite/types/option.go:384
|
||||
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1095
|
||||
msgid "Price must be zero or greater."
|
||||
msgstr "El preu ha de ser com a mínim zero."
|
||||
|
||||
|
@ -2805,7 +3092,7 @@ msgctxt "header"
|
|||
msgid "Children (aged 2 to 10)"
|
||||
msgstr "Mainada (entre 2 i 10 anys)"
|
||||
|
||||
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:413 pkg/booking/public.go:177
|
||||
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:414 pkg/booking/public.go:177
|
||||
#: pkg/booking/public.go:232
|
||||
msgid "Selected campsite type is not valid."
|
||||
msgstr "El tipus d’allotjament escollit no és vàlid."
|
||||
|
@ -2843,6 +3130,136 @@ msgstr "No podeu deixar la data de fi en blanc."
|
|||
msgid "End date must be a valid date."
|
||||
msgstr "La data de fi ha de ser una data vàlida."
|
||||
|
||||
#: pkg/customer/admin.go:293 pkg/company/admin.go:207
|
||||
#: pkg/booking/checkin.go:297 pkg/booking/public.go:577
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "El país escollit no és vàlid."
|
||||
|
||||
#: pkg/customer/admin.go:297 pkg/booking/checkin.go:281
|
||||
msgid "Selected ID document type is not valid."
|
||||
msgstr "El tipus de document d’identitat escollit no és vàlid."
|
||||
|
||||
#: pkg/customer/admin.go:298 pkg/booking/checkin.go:282
|
||||
msgid "ID document number can not be empty."
|
||||
msgstr "No podeu deixar el número document d’identitat en blanc."
|
||||
|
||||
#: pkg/customer/admin.go:300 pkg/booking/checkin.go:288
|
||||
#: pkg/booking/checkin.go:289 pkg/booking/admin.go:426
|
||||
#: pkg/booking/public.go:581
|
||||
msgid "Full name can not be empty."
|
||||
msgstr "No podeu deixar el nom i els cognoms en blanc."
|
||||
|
||||
#: pkg/customer/admin.go:301 pkg/booking/admin.go:427 pkg/booking/public.go:582
|
||||
msgid "Full name must have at least one letter."
|
||||
msgstr "El nom i els cognoms han de tenir com a mínim una lletra."
|
||||
|
||||
#: pkg/customer/admin.go:304 pkg/company/admin.go:230 pkg/booking/public.go:585
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podeu deixar l’adreça en blanc."
|
||||
|
||||
#: pkg/customer/admin.go:305 pkg/booking/public.go:586
|
||||
msgid "Town or village can not be empty."
|
||||
msgstr "No podeu deixar la població en blanc."
|
||||
|
||||
#: pkg/customer/admin.go:306 pkg/company/admin.go:233 pkg/booking/public.go:587
|
||||
msgid "Postcode can not be empty."
|
||||
msgstr "No podeu deixar el codi postal en blanc."
|
||||
|
||||
#: pkg/customer/admin.go:307 pkg/company/admin.go:234 pkg/booking/admin.go:433
|
||||
#: pkg/booking/public.go:588
|
||||
msgid "This postcode is not valid."
|
||||
msgstr "Aquest codi postal no és vàlid."
|
||||
|
||||
#: pkg/customer/admin.go:315 pkg/company/admin.go:220
|
||||
#: pkg/booking/checkin.go:301 pkg/booking/admin.go:443
|
||||
#: pkg/booking/public.go:596
|
||||
msgid "This phone number is not valid."
|
||||
msgstr "Aquest número de telèfon no és vàlid."
|
||||
|
||||
#: pkg/invoice/admin.go:649
|
||||
msgctxt "filename"
|
||||
msgid "invoices.zip"
|
||||
msgstr "factures.zip"
|
||||
|
||||
#: pkg/invoice/admin.go:664
|
||||
msgctxt "filename"
|
||||
msgid "invoices.ods"
|
||||
msgstr "factures.ods"
|
||||
|
||||
#: pkg/invoice/admin.go:666 pkg/invoice/admin.go:1285 pkg/invoice/admin.go:1292
|
||||
msgid "Invalid action"
|
||||
msgstr "Acció invàlida"
|
||||
|
||||
#: pkg/invoice/admin.go:830
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "L’estat de factura escollit no és vàlid."
|
||||
|
||||
#: pkg/invoice/admin.go:831
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "El client escollit no és vàlid."
|
||||
|
||||
#: pkg/invoice/admin.go:832
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podeu deixar la data de factura en blanc."
|
||||
|
||||
#: pkg/invoice/admin.go:833
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La data de factura ha de ser una data vàlida."
|
||||
|
||||
#: pkg/invoice/admin.go:980
|
||||
#, c-format
|
||||
msgid "Re: quotation #%s of %s"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/invoice/admin.go:981
|
||||
msgctxt "to_char"
|
||||
msgid "MM/DD/YYYY"
|
||||
msgstr "DD/MM/YYYY"
|
||||
|
||||
#: pkg/invoice/admin.go:1083
|
||||
msgid "Invoice product ID must be an integer."
|
||||
msgstr "L’ID de producte de factura ha de ser enter."
|
||||
|
||||
#: pkg/invoice/admin.go:1084
|
||||
msgid "Invoice product ID one or greater."
|
||||
msgstr "L’ID de producte de factura ha de ser com a mínim u."
|
||||
|
||||
#: pkg/invoice/admin.go:1088
|
||||
msgid "Product ID must be an integer."
|
||||
msgstr "L’ID de producte ha de ser un número enter."
|
||||
|
||||
#: pkg/invoice/admin.go:1089
|
||||
msgid "Product ID must zero or greater."
|
||||
msgstr "L’ID de producte ha de ser com a mínim zero."
|
||||
|
||||
#: pkg/invoice/admin.go:1098
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podeu deixar la quantitat en blanc."
|
||||
|
||||
#: pkg/invoice/admin.go:1099
|
||||
msgid "Quantity must be an integer."
|
||||
msgstr "La quantitat ha de ser un número enter."
|
||||
|
||||
#: pkg/invoice/admin.go:1100
|
||||
msgid "Quantity must one or greater."
|
||||
msgstr "La quantitat ha de ser com a mínim u."
|
||||
|
||||
#: pkg/invoice/admin.go:1103
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podeu deixar el descompte en blanc."
|
||||
|
||||
#: pkg/invoice/admin.go:1104
|
||||
msgid "Discount must be an integer."
|
||||
msgstr "El descompte ha de ser un número enter."
|
||||
|
||||
#: pkg/invoice/admin.go:1105 pkg/invoice/admin.go:1106
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descompte ha de ser un percentatge entre 0 i 100"
|
||||
|
||||
#: pkg/invoice/admin.go:1110
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "L’impost escollit no és vàlid."
|
||||
|
||||
#: pkg/user/admin.go:18
|
||||
msgctxt "role"
|
||||
msgid "guest"
|
||||
|
@ -2902,11 +3319,6 @@ msgstr "No podeu deixar l’adreça de l’enllaç en blanc."
|
|||
msgid "This web address is not valid. It should be like https://domain.com/."
|
||||
msgstr "Aquesta adreça web no és vàlida. Hauria de ser similar a https://domini.com/."
|
||||
|
||||
#: pkg/company/admin.go:207 pkg/booking/checkin.go:301
|
||||
#: pkg/booking/public.go:581
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "El país escollit no és vàlid."
|
||||
|
||||
#: pkg/company/admin.go:211
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podeu deixar el nom d’empresa en blanc."
|
||||
|
@ -2923,19 +3335,10 @@ msgstr "No podeu deixar el NIF en blanc."
|
|||
msgid "This VAT number is not valid."
|
||||
msgstr "Aquest NIF no és vàlid."
|
||||
|
||||
#: pkg/company/admin.go:219 pkg/booking/public.go:599
|
||||
#: pkg/company/admin.go:219 pkg/booking/public.go:595
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podeu deixar el telèfon en blanc."
|
||||
|
||||
#: pkg/company/admin.go:220 pkg/booking/checkin.go:305 pkg/booking/admin.go:442
|
||||
#: pkg/booking/public.go:600
|
||||
msgid "This phone number is not valid."
|
||||
msgstr "Aquest número de telèfon no és vàlid."
|
||||
|
||||
#: pkg/company/admin.go:230 pkg/booking/public.go:589
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podeu deixar l’adreça en blanc."
|
||||
|
||||
#: pkg/company/admin.go:231
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podeu deixar la població en blanc."
|
||||
|
@ -2944,14 +3347,6 @@ msgstr "No podeu deixar la població en blanc."
|
|||
msgid "Province can not be empty."
|
||||
msgstr "No podeu deixar la província en blanc."
|
||||
|
||||
#: pkg/company/admin.go:233 pkg/booking/public.go:591
|
||||
msgid "Postcode can not be empty."
|
||||
msgstr "No podeu deixar el codi postal en blanc."
|
||||
|
||||
#: pkg/company/admin.go:234 pkg/booking/admin.go:432 pkg/booking/public.go:592
|
||||
msgid "This postcode is not valid."
|
||||
msgstr "Aquest codi postal no és vàlid."
|
||||
|
||||
#: pkg/company/admin.go:238
|
||||
msgid "RTC number can not be empty."
|
||||
msgstr "No podeu deixar el número d’RTC en blanc."
|
||||
|
@ -3000,40 +3395,27 @@ msgstr "No podeu deixar el fitxer del mèdia en blanc."
|
|||
msgid "Filename can not be empty."
|
||||
msgstr "No podeu deixar el nom del fitxer en blanc."
|
||||
|
||||
#: pkg/booking/checkin.go:285
|
||||
msgid "Selected ID document type is not valid."
|
||||
msgstr "El tipus de document d’identitat escollit no és vàlid."
|
||||
|
||||
#: pkg/booking/checkin.go:286
|
||||
msgid "ID document number can not be empty."
|
||||
msgstr "No podeu deixar el número document d’identitat en blanc."
|
||||
|
||||
#: pkg/booking/checkin.go:288
|
||||
#: pkg/booking/checkin.go:284
|
||||
msgid "ID document issue date must be a valid date."
|
||||
msgstr "La data d’expedició del document d’identitat ha de ser una data vàlida."
|
||||
|
||||
#: pkg/booking/checkin.go:289
|
||||
#: pkg/booking/checkin.go:285
|
||||
msgid "ID document issue date must be in the past."
|
||||
msgstr "La data d’expedició del document d’identitat ha de ser al passat."
|
||||
|
||||
#: pkg/booking/checkin.go:292 pkg/booking/checkin.go:293
|
||||
#: pkg/booking/admin.go:425 pkg/booking/public.go:585
|
||||
msgid "Full name can not be empty."
|
||||
msgstr "No podeu deixar el nom i els cognoms en blanc."
|
||||
|
||||
#: pkg/booking/checkin.go:294
|
||||
#: pkg/booking/checkin.go:290
|
||||
msgid "Selected sex is not valid."
|
||||
msgstr "El sexe escollit no és vàlid."
|
||||
|
||||
#: pkg/booking/checkin.go:295
|
||||
#: pkg/booking/checkin.go:291
|
||||
msgid "Birthdate can not be empty"
|
||||
msgstr "No podeu deixar la data de naixement en blanc."
|
||||
|
||||
#: pkg/booking/checkin.go:296
|
||||
#: pkg/booking/checkin.go:292
|
||||
msgid "Birthdate must be a valid date."
|
||||
msgstr "La data de naixement ha de ser una data vàlida."
|
||||
|
||||
#: pkg/booking/checkin.go:297
|
||||
#: pkg/booking/checkin.go:293
|
||||
msgid "Birthdate must be in the past."
|
||||
msgstr "La data de naixement ha de ser al passat."
|
||||
|
||||
|
@ -3057,28 +3439,24 @@ msgctxt "cart"
|
|||
msgid "Dog"
|
||||
msgstr "Gos"
|
||||
|
||||
#: pkg/booking/admin.go:217
|
||||
#: pkg/booking/admin.go:218
|
||||
msgctxt "filename"
|
||||
msgid "bookings.ods"
|
||||
msgstr "reserves.ods"
|
||||
|
||||
#: pkg/booking/admin.go:426 pkg/booking/public.go:586
|
||||
msgid "Full name must have at least one letter."
|
||||
msgstr "El nom i els cognoms han de tenir com a mínim una lletra."
|
||||
|
||||
#: pkg/booking/admin.go:431
|
||||
#: pkg/booking/admin.go:432
|
||||
msgid "Country can not be empty to validate the postcode."
|
||||
msgstr "No podeu deixar el país en blanc per validar el codi postal."
|
||||
|
||||
#: pkg/booking/admin.go:441
|
||||
#: pkg/booking/admin.go:442
|
||||
msgid "Country can not be empty to validate the phone."
|
||||
msgstr "No podeu deixar el país en blanc per validar el telèfon."
|
||||
|
||||
#: pkg/booking/admin.go:448
|
||||
#: pkg/booking/admin.go:449
|
||||
msgid "You must select at least one accommodation."
|
||||
msgstr "Heu d’escollir com a mínim un allotjament."
|
||||
|
||||
#: pkg/booking/admin.go:454
|
||||
#: pkg/booking/admin.go:455
|
||||
msgid "The selected accommodations have no available openings in the requested dates."
|
||||
msgstr "Els allotjaments escollits no estan disponibles a les dates demanades."
|
||||
|
||||
|
@ -3191,11 +3569,7 @@ msgstr "El valor de %s ha de ser com a mínim %d."
|
|||
msgid "%s must be at most %d."
|
||||
msgstr "El valor de %s ha de ser com a màxim %d."
|
||||
|
||||
#: pkg/booking/public.go:590
|
||||
msgid "Town or village can not be empty."
|
||||
msgstr "No podeu deixar la població en blanc."
|
||||
|
||||
#: pkg/booking/public.go:605
|
||||
#: pkg/booking/public.go:601
|
||||
msgid "It is mandatory to agree to the reservation conditions."
|
||||
msgstr "És obligatori acceptar les condicions de reserves."
|
||||
|
||||
|
|
614
po/es.po
614
po/es.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: camper\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2024-04-26 16:53+0200\n"
|
||||
"POT-Creation-Date: 2024-04-28 20:05+0200\n"
|
||||
"PO-Revision-Date: 2024-02-06 10:04+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
|
@ -239,6 +239,7 @@ msgstr "Opciones del tipo de alojamiento"
|
|||
#: web/templates/mail/payment/details.gotxt:39
|
||||
#: web/templates/public/booking/fields.gohtml:146
|
||||
#: web/templates/admin/payment/details.gohtml:140
|
||||
#: web/templates/admin/customer/form.gohtml:28
|
||||
#: web/templates/admin/booking/fields.gohtml:188
|
||||
msgctxt "title"
|
||||
msgid "Customer Details"
|
||||
|
@ -247,6 +248,7 @@ msgstr "Detalles del cliente"
|
|||
#: web/templates/mail/payment/details.gotxt:41
|
||||
#: web/templates/public/booking/fields.gohtml:149
|
||||
#: web/templates/admin/payment/details.gohtml:143
|
||||
#: web/templates/admin/customer/form.gohtml:31
|
||||
#: web/templates/admin/booking/fields.gohtml:191
|
||||
msgctxt "input"
|
||||
msgid "Full name"
|
||||
|
@ -255,6 +257,7 @@ msgstr "Nombre y apellidos"
|
|||
#: web/templates/mail/payment/details.gotxt:42
|
||||
#: web/templates/public/booking/fields.gohtml:158
|
||||
#: web/templates/admin/payment/details.gohtml:147
|
||||
#: web/templates/admin/customer/form.gohtml:69
|
||||
#: web/templates/admin/taxDetails.gohtml:69
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
|
@ -263,6 +266,7 @@ msgstr "Dirección"
|
|||
#: web/templates/mail/payment/details.gotxt:43
|
||||
#: web/templates/public/booking/fields.gohtml:167
|
||||
#: web/templates/admin/payment/details.gohtml:151
|
||||
#: web/templates/admin/customer/form.gohtml:105
|
||||
#: web/templates/admin/taxDetails.gohtml:93
|
||||
msgctxt "input"
|
||||
msgid "Postcode"
|
||||
|
@ -278,6 +282,7 @@ msgstr "Población"
|
|||
#: web/templates/mail/payment/details.gotxt:45
|
||||
#: web/templates/public/booking/fields.gohtml:187
|
||||
#: web/templates/admin/payment/details.gohtml:159
|
||||
#: web/templates/admin/customer/form.gohtml:117
|
||||
#: web/templates/admin/taxDetails.gohtml:101
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
|
@ -411,11 +416,16 @@ msgid "Order Number"
|
|||
msgstr "Número de pedido"
|
||||
|
||||
#: web/templates/public/payment/details.gohtml:8
|
||||
#: web/templates/admin/invoice/index.gohtml:103
|
||||
#: web/templates/admin/invoice/view.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Date"
|
||||
msgstr "Fecha"
|
||||
|
||||
#: web/templates/public/payment/details.gohtml:12
|
||||
#: web/templates/admin/invoice/form.gohtml:119
|
||||
#: web/templates/admin/invoice/view.gohtml:63
|
||||
#: web/templates/admin/invoice/view.gohtml:103
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
@ -579,6 +589,11 @@ msgctxt "input"
|
|||
msgid "Year"
|
||||
msgstr "Año"
|
||||
|
||||
#: web/templates/public/form.gohtml:83 web/templates/admin/form.gohtml:83
|
||||
msgctxt "action"
|
||||
msgid "Filters"
|
||||
msgstr "Filtros"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:49
|
||||
#: web/templates/public/booking/fields.gohtml:278
|
||||
msgctxt "action"
|
||||
|
@ -929,7 +944,7 @@ msgstr "Menú"
|
|||
#: web/templates/admin/campsite/type/option/form.gohtml:16
|
||||
#: web/templates/admin/campsite/type/option/index.gohtml:10
|
||||
#: web/templates/admin/campsite/type/index.gohtml:10
|
||||
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:95
|
||||
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:101
|
||||
#: web/templates/admin/booking/fields.gohtml:266
|
||||
msgctxt "title"
|
||||
msgid "Campsites"
|
||||
|
@ -1004,13 +1019,15 @@ msgid "Campground map"
|
|||
msgstr "Mapa del camping"
|
||||
|
||||
#: web/templates/public/booking/fields.gohtml:176
|
||||
#: web/templates/admin/customer/form.gohtml:81
|
||||
msgctxt "input"
|
||||
msgid "Town or village"
|
||||
msgstr "Población"
|
||||
|
||||
#: web/templates/public/booking/fields.gohtml:193
|
||||
#: web/templates/admin/customer/form.gohtml:121
|
||||
#: web/templates/admin/booking/fields.gohtml:204
|
||||
#: web/templates/admin/booking/guest.gohtml:109
|
||||
#: web/templates/admin/booking/guest.gohtml:111
|
||||
msgid "Choose a country"
|
||||
msgstr "Escoja un país"
|
||||
|
||||
|
@ -1164,6 +1181,7 @@ msgstr "Álias"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:51
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:41
|
||||
#: web/templates/admin/season/form.gohtml:50
|
||||
#: web/templates/admin/invoice/product-form.gohtml:16
|
||||
#: web/templates/admin/services/form.gohtml:53
|
||||
#: web/templates/admin/profile.gohtml:29
|
||||
#: web/templates/admin/surroundings/form.gohtml:41
|
||||
|
@ -1188,6 +1206,8 @@ msgstr "Contenido"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:287
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:98
|
||||
#: web/templates/admin/season/form.gohtml:73
|
||||
#: web/templates/admin/customer/form.gohtml:153
|
||||
#: web/templates/admin/invoice/form.gohtml:137
|
||||
#: web/templates/admin/services/form.gohtml:81
|
||||
#: web/templates/admin/surroundings/form.gohtml:69
|
||||
#: web/templates/admin/surroundings/index.gohtml:58
|
||||
|
@ -1211,6 +1231,7 @@ msgstr "Actualizar"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:289
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:100
|
||||
#: web/templates/admin/season/form.gohtml:75
|
||||
#: web/templates/admin/customer/form.gohtml:155
|
||||
#: web/templates/admin/services/form.gohtml:83
|
||||
#: web/templates/admin/surroundings/form.gohtml:71
|
||||
#: web/templates/admin/amenity/feature/form.gohtml:67
|
||||
|
@ -1232,6 +1253,7 @@ msgstr "Añadir texto legal"
|
|||
#: web/templates/admin/campsite/type/option/index.gohtml:30
|
||||
#: web/templates/admin/campsite/type/index.gohtml:29
|
||||
#: web/templates/admin/season/index.gohtml:29
|
||||
#: web/templates/admin/customer/index.gohtml:19
|
||||
#: web/templates/admin/user/index.gohtml:20
|
||||
#: web/templates/admin/surroundings/index.gohtml:83
|
||||
#: web/templates/admin/amenity/feature/index.gohtml:30
|
||||
|
@ -1725,6 +1747,7 @@ msgid "Per night"
|
|||
msgstr "Por noche"
|
||||
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:84
|
||||
#: web/templates/admin/invoice/product-form.gohtml:29
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Precio"
|
||||
|
@ -1824,12 +1847,316 @@ msgctxt "action"
|
|||
msgid "Cancel"
|
||||
msgstr "Cancelar"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:8
|
||||
msgctxt "title"
|
||||
msgid "Edit Customer"
|
||||
msgstr "Edición del cliente"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "New Customer"
|
||||
msgstr "Nuevo cliente"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:15
|
||||
#: web/templates/admin/invoice/index.gohtml:105
|
||||
msgctxt "title"
|
||||
msgid "Customer"
|
||||
msgstr "Cliente"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:44
|
||||
#: web/templates/admin/booking/guest.gohtml:8
|
||||
msgctxt "input"
|
||||
msgid "ID document number"
|
||||
msgstr "Número de documento de identidad"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:56
|
||||
#: web/templates/admin/booking/guest.gohtml:20
|
||||
msgctxt "input"
|
||||
msgid "ID document type"
|
||||
msgstr "Tipo de documento"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:61
|
||||
#: web/templates/admin/booking/guest.gohtml:25
|
||||
msgid "Choose an ID document type"
|
||||
msgstr "Escoja un tipo de documento"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:93
|
||||
#: web/templates/admin/taxDetails.gohtml:85
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Provincia"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:129
|
||||
#: web/templates/admin/booking/fields.gohtml:239
|
||||
msgctxt "input"
|
||||
msgid "Email (optional)"
|
||||
msgstr "Correo-e (opcional)"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:140
|
||||
#: web/templates/admin/booking/fields.gohtml:248
|
||||
#: web/templates/admin/booking/guest.gohtml:119
|
||||
msgctxt "input"
|
||||
msgid "Phone (optional)"
|
||||
msgstr "Teléfono (opcional)"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:95
|
||||
msgctxt "title"
|
||||
msgid "Customers"
|
||||
msgstr "Clientes"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:14
|
||||
msgctxt "action"
|
||||
msgid "Add Customer"
|
||||
msgstr "Añadir cliente"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:20
|
||||
#: web/templates/admin/user/login-attempts.gohtml:20
|
||||
#: web/templates/admin/user/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Email"
|
||||
msgstr "Correo-e"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Phone"
|
||||
msgstr "Teléfono"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:33
|
||||
msgid "No customer found."
|
||||
msgstr "No se ha encontrado ningún cliente."
|
||||
|
||||
#: web/templates/admin/dashboard.gohtml:6
|
||||
#: web/templates/admin/dashboard.gohtml:13 web/templates/admin/layout.gohtml:89
|
||||
msgctxt "title"
|
||||
msgid "Dashboard"
|
||||
msgstr "Panel"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:11
|
||||
#: web/templates/admin/booking/guest.gohtml:5
|
||||
msgctxt "action"
|
||||
msgid "Remove"
|
||||
msgstr "Borrar"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:44
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Cantidad"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:58
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descuento (%)"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:73
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Impuestos"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:79
|
||||
msgid "Select a TAX"
|
||||
msgstr "Escoja un impuesto"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:4
|
||||
msgctxt "title"
|
||||
msgid "Edit Invoice “%s”"
|
||||
msgstr "Edición de la factura «%s»"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:6
|
||||
msgctxt "title"
|
||||
msgid "New Invoice"
|
||||
msgstr "Nueva factura"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:15
|
||||
#: web/templates/admin/invoice/index.gohtml:2
|
||||
#: web/templates/admin/invoice/view.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:98
|
||||
msgctxt "title"
|
||||
msgid "Invoices"
|
||||
msgstr "Facturas"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:32
|
||||
msgid "Product “%s” removed"
|
||||
msgstr "Se ha borrado el producto «%s»"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:36
|
||||
msgctxt "action"
|
||||
msgid "Undo"
|
||||
msgstr "Deshacer"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:51
|
||||
#: web/templates/admin/invoice/index.gohtml:39
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Cliente"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:56
|
||||
msgid "Select a customer"
|
||||
msgstr "Escoja un cliente"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:64
|
||||
msgctxt "input"
|
||||
msgid "Invoice date"
|
||||
msgstr "Fecha de la factura"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:77
|
||||
#: web/templates/admin/invoice/index.gohtml:51
|
||||
msgctxt "input"
|
||||
msgid "Invoice status"
|
||||
msgstr "Estado de factura"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:92
|
||||
msgctxt "input"
|
||||
msgid "Notes (optional)"
|
||||
msgstr "Notas (opcional)"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:109
|
||||
#: web/templates/admin/invoice/view.gohtml:59
|
||||
msgctxt "title"
|
||||
msgid "Subtotal"
|
||||
msgstr "Subtotal"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:133
|
||||
msgctxt "action"
|
||||
msgid "Add products"
|
||||
msgstr "Añadir productos"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:140
|
||||
msgctxt "action"
|
||||
msgid "Save"
|
||||
msgstr "Guardar"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:25
|
||||
msgctxt "action"
|
||||
msgid "Download invoices"
|
||||
msgstr "Descargar facturas"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:28
|
||||
msgctxt "action"
|
||||
msgid "Export list"
|
||||
msgstr "Exportar lista"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:43
|
||||
msgid "All customers"
|
||||
msgstr "Todos los clientes"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:55
|
||||
msgid "All statuses"
|
||||
msgstr "Todos los estados"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:63
|
||||
msgctxt "input"
|
||||
msgid "From date"
|
||||
msgstr "De la fecha"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:72
|
||||
msgctxt "input"
|
||||
msgid "To date"
|
||||
msgstr "A la fecha"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:81
|
||||
msgctxt "input"
|
||||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:91
|
||||
msgctxt "action"
|
||||
msgid "Filter"
|
||||
msgstr "Filtrar"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:94
|
||||
msgctxt "action"
|
||||
msgid "Reset"
|
||||
msgstr "Restablecer"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:97
|
||||
msgctxt "action"
|
||||
msgid "Add invoice"
|
||||
msgstr "Añadir factura"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:102
|
||||
msgctxt "invoice"
|
||||
msgid "All"
|
||||
msgstr "Todas"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:104
|
||||
msgctxt "title"
|
||||
msgid "Invoice Num."
|
||||
msgstr "Núm. de factura"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:106
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estado"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:107
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descarga"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:108
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Importe"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:115
|
||||
msgctxt "action"
|
||||
msgid "Select invoice %v"
|
||||
msgstr "Seleccionar factura %v"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:144
|
||||
msgctxt "action"
|
||||
msgid "Download invoice %s"
|
||||
msgstr "Descargar factura %s"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:154
|
||||
msgid "No invoices added yet."
|
||||
msgstr "No se ha añadido ninguna factura todavía."
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:161
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:2
|
||||
msgctxt "title"
|
||||
msgid "Invoice %s"
|
||||
msgstr "Factura %s"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:15
|
||||
msgctxt "action"
|
||||
msgid "Edit"
|
||||
msgstr "Editar"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:18
|
||||
msgctxt "action"
|
||||
msgid "Download invoice"
|
||||
msgstr "Descargar factura"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:53
|
||||
msgctxt "title"
|
||||
msgid "Concept"
|
||||
msgstr "Concepto"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:54
|
||||
msgctxt "title"
|
||||
msgid "Price"
|
||||
msgstr "Precio"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:56
|
||||
msgctxt "title"
|
||||
msgid "Discount"
|
||||
msgstr "Descuento"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:58
|
||||
msgctxt "title"
|
||||
msgid "Units"
|
||||
msgstr "Unidades"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:93
|
||||
msgctxt "title"
|
||||
msgid "Tax Base"
|
||||
msgstr "Base imponible"
|
||||
|
||||
#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:18
|
||||
msgctxt "title"
|
||||
msgid "Login"
|
||||
|
@ -1928,12 +2255,6 @@ msgctxt "title"
|
|||
msgid "Users"
|
||||
msgstr "Usuarios"
|
||||
|
||||
#: web/templates/admin/user/login-attempts.gohtml:20
|
||||
#: web/templates/admin/user/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Email"
|
||||
msgstr "Correo-e"
|
||||
|
||||
#: web/templates/admin/user/login-attempts.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "IP Address"
|
||||
|
@ -1985,11 +2306,6 @@ msgctxt "input"
|
|||
msgid "Trade Name"
|
||||
msgstr "Nombre comercial"
|
||||
|
||||
#: web/templates/admin/taxDetails.gohtml:85
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Provincia"
|
||||
|
||||
#: web/templates/admin/taxDetails.gohtml:111
|
||||
msgctxt "input"
|
||||
msgid "Currency"
|
||||
|
@ -2214,11 +2530,11 @@ msgctxt "title"
|
|||
msgid "Bookings"
|
||||
msgstr "Reservas"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:101
|
||||
#: web/templates/admin/layout.gohtml:107
|
||||
msgid "Breadcrumb"
|
||||
msgstr "Migas de pan"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:113
|
||||
#: web/templates/admin/layout.gohtml:119
|
||||
msgid "Camper Version: %s"
|
||||
msgstr "Camper versión: %s"
|
||||
|
||||
|
@ -2340,7 +2656,7 @@ msgid "Country (optional)"
|
|||
msgstr "País (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:212
|
||||
#: web/templates/admin/booking/guest.gohtml:128
|
||||
#: web/templates/admin/booking/guest.gohtml:130
|
||||
msgctxt "input"
|
||||
msgid "Address (optional)"
|
||||
msgstr "Dirección (opcional)"
|
||||
|
@ -2355,17 +2671,6 @@ msgctxt "input"
|
|||
msgid "Town or village (optional)"
|
||||
msgstr "Población (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:239
|
||||
msgctxt "input"
|
||||
msgid "Email (optional)"
|
||||
msgstr "Correo-e (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:248
|
||||
#: web/templates/admin/booking/guest.gohtml:117
|
||||
msgctxt "input"
|
||||
msgid "Phone (optional)"
|
||||
msgstr "Teléfono (opcional)"
|
||||
|
||||
#: web/templates/admin/booking/form.gohtml:8
|
||||
msgctxt "title"
|
||||
msgid "Edit Booking"
|
||||
|
@ -2430,60 +2735,41 @@ msgstr "Nombre del titular"
|
|||
msgid "No booking found."
|
||||
msgstr "No se ha encontrado ninguna reserva."
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:5
|
||||
msgctxt "action"
|
||||
msgid "Remove"
|
||||
msgstr "Borrar"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:8
|
||||
msgctxt "input"
|
||||
msgid "ID document number"
|
||||
msgstr "Número de documento de identidad"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:20
|
||||
msgctxt "input"
|
||||
msgid "ID document type"
|
||||
msgstr "Tipo de documento"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:25
|
||||
msgid "Choose an ID document type"
|
||||
msgstr "Escoja un tipo de documento"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:33
|
||||
msgctxt "input"
|
||||
msgid "ID document issue date (if any)"
|
||||
msgstr "Fecha expedición del documento (si hay)"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:44
|
||||
#: web/templates/admin/booking/guest.gohtml:45
|
||||
msgctxt "input"
|
||||
msgid "First surname"
|
||||
msgstr "Primer apellido"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:56
|
||||
#: web/templates/admin/booking/guest.gohtml:57
|
||||
msgctxt "input"
|
||||
msgid "Second surname (if has one)"
|
||||
msgstr "Segundo apellido (si tiene)"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:67
|
||||
#: web/templates/admin/booking/guest.gohtml:68
|
||||
msgctxt "input"
|
||||
msgid "Given name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:79
|
||||
#: web/templates/admin/booking/guest.gohtml:80
|
||||
msgctxt "input"
|
||||
msgid "Sex"
|
||||
msgstr "Sexo"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:84
|
||||
#: web/templates/admin/booking/guest.gohtml:85
|
||||
msgid "Choose a sex"
|
||||
msgstr "Escoja un sexo"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:92
|
||||
#: web/templates/admin/booking/guest.gohtml:93
|
||||
msgctxt "input"
|
||||
msgid "Birthdate"
|
||||
msgstr "Fecha de nacimiento"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:104
|
||||
#: web/templates/admin/booking/guest.gohtml:106
|
||||
msgctxt "input"
|
||||
msgid "Nationality"
|
||||
msgstr "Nacionalidad"
|
||||
|
@ -2553,8 +2839,9 @@ msgstr "Se ha recibido correctamente el pago de la reserva"
|
|||
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
|
||||
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
|
||||
#: pkg/campsite/feature.go:269 pkg/season/admin.go:411
|
||||
#: pkg/services/admin.go:316 pkg/surroundings/admin.go:340
|
||||
#: pkg/amenity/feature.go:269 pkg/amenity/admin.go:283
|
||||
#: pkg/invoice/admin.go:1092 pkg/services/admin.go:316
|
||||
#: pkg/surroundings/admin.go:340 pkg/amenity/feature.go:269
|
||||
#: pkg/amenity/admin.go:283
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podéis dejar el nombre en blanco."
|
||||
|
||||
|
@ -2589,12 +2876,12 @@ msgid "Slide image must be an image media type."
|
|||
msgstr "La imagen de la diapositiva tiene que ser un medio de tipo imagen."
|
||||
|
||||
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:224
|
||||
#: pkg/booking/public.go:596
|
||||
#: pkg/booking/public.go:592
|
||||
msgid "Email can not be empty."
|
||||
msgstr "No podéis dejar el correo-e en blanco."
|
||||
|
||||
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:225
|
||||
#: pkg/booking/admin.go:437 pkg/booking/public.go:597
|
||||
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:312
|
||||
#: pkg/company/admin.go:225 pkg/booking/admin.go:438 pkg/booking/public.go:593
|
||||
msgid "This email is not valid. It should be like name@domain.com."
|
||||
msgstr "Este correo-e no es válido. Tiene que ser parecido a nombre@dominio.com."
|
||||
|
||||
|
@ -2623,7 +2910,7 @@ msgstr "El idioma escogido no es válido."
|
|||
msgid "File must be a valid PNG or JPEG image."
|
||||
msgstr "El archivo tiene que ser una imagen PNG o JPEG válida."
|
||||
|
||||
#: pkg/app/admin.go:73
|
||||
#: pkg/app/admin.go:79
|
||||
msgid "Access forbidden"
|
||||
msgstr "Acceso prohibido"
|
||||
|
||||
|
@ -2651,15 +2938,15 @@ msgstr "El valor del máximo tiene que ser un número entero."
|
|||
msgid "Maximum must be equal or greater than minimum."
|
||||
msgstr "El valor del máximo tiene que ser igual o mayor al del mínimo."
|
||||
|
||||
#: pkg/campsite/types/option.go:382
|
||||
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1093
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podéis dejar el precio en blanco."
|
||||
|
||||
#: pkg/campsite/types/option.go:383
|
||||
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1094
|
||||
msgid "Price must be a decimal number."
|
||||
msgstr "El precio tiene que ser un número decimal."
|
||||
|
||||
#: pkg/campsite/types/option.go:384
|
||||
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1095
|
||||
msgid "Price must be zero or greater."
|
||||
msgstr "El precio tiene que ser como mínimo cero."
|
||||
|
||||
|
@ -2805,7 +3092,7 @@ msgctxt "header"
|
|||
msgid "Children (aged 2 to 10)"
|
||||
msgstr "Niños (de 2 a 10 años)"
|
||||
|
||||
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:413 pkg/booking/public.go:177
|
||||
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:414 pkg/booking/public.go:177
|
||||
#: pkg/booking/public.go:232
|
||||
msgid "Selected campsite type is not valid."
|
||||
msgstr "El tipo de alojamiento escogido no es válido."
|
||||
|
@ -2843,6 +3130,136 @@ msgstr "No podéis dejar la fecha final en blanco."
|
|||
msgid "End date must be a valid date."
|
||||
msgstr "La fecha final tiene que ser una fecha válida."
|
||||
|
||||
#: pkg/customer/admin.go:293 pkg/company/admin.go:207
|
||||
#: pkg/booking/checkin.go:297 pkg/booking/public.go:577
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "El país escogido no es válido."
|
||||
|
||||
#: pkg/customer/admin.go:297 pkg/booking/checkin.go:281
|
||||
msgid "Selected ID document type is not valid."
|
||||
msgstr "El tipo de documento de identidad escogido no es válido."
|
||||
|
||||
#: pkg/customer/admin.go:298 pkg/booking/checkin.go:282
|
||||
msgid "ID document number can not be empty."
|
||||
msgstr "No podéis dejar el número del documento de identidad en blanco."
|
||||
|
||||
#: pkg/customer/admin.go:300 pkg/booking/checkin.go:288
|
||||
#: pkg/booking/checkin.go:289 pkg/booking/admin.go:426
|
||||
#: pkg/booking/public.go:581
|
||||
msgid "Full name can not be empty."
|
||||
msgstr "No podéis dejar el nombre y los apellidos en blanco."
|
||||
|
||||
#: pkg/customer/admin.go:301 pkg/booking/admin.go:427 pkg/booking/public.go:582
|
||||
msgid "Full name must have at least one letter."
|
||||
msgstr "El nombre y los apellidos tienen que tener como mínimo una letra."
|
||||
|
||||
#: pkg/customer/admin.go:304 pkg/company/admin.go:230 pkg/booking/public.go:585
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podéis dejar la dirección en blanco."
|
||||
|
||||
#: pkg/customer/admin.go:305 pkg/booking/public.go:586
|
||||
msgid "Town or village can not be empty."
|
||||
msgstr "No podéis dejar la población en blanco."
|
||||
|
||||
#: pkg/customer/admin.go:306 pkg/company/admin.go:233 pkg/booking/public.go:587
|
||||
msgid "Postcode can not be empty."
|
||||
msgstr "No podéis dejar el código postal en blanco."
|
||||
|
||||
#: pkg/customer/admin.go:307 pkg/company/admin.go:234 pkg/booking/admin.go:433
|
||||
#: pkg/booking/public.go:588
|
||||
msgid "This postcode is not valid."
|
||||
msgstr "Este código postal no es válido."
|
||||
|
||||
#: pkg/customer/admin.go:315 pkg/company/admin.go:220
|
||||
#: pkg/booking/checkin.go:301 pkg/booking/admin.go:443
|
||||
#: pkg/booking/public.go:596
|
||||
msgid "This phone number is not valid."
|
||||
msgstr "Este teléfono no es válido."
|
||||
|
||||
#: pkg/invoice/admin.go:649
|
||||
msgctxt "filename"
|
||||
msgid "invoices.zip"
|
||||
msgstr "facturas.zip"
|
||||
|
||||
#: pkg/invoice/admin.go:664
|
||||
msgctxt "filename"
|
||||
msgid "invoices.ods"
|
||||
msgstr "facturas.ods"
|
||||
|
||||
#: pkg/invoice/admin.go:666 pkg/invoice/admin.go:1285 pkg/invoice/admin.go:1292
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción inválida"
|
||||
|
||||
#: pkg/invoice/admin.go:830
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "El estado de factura escogida no es válido."
|
||||
|
||||
#: pkg/invoice/admin.go:831
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "El cliente escogido no es válido."
|
||||
|
||||
#: pkg/invoice/admin.go:832
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podéis dejar la fecha de factura en blanco."
|
||||
|
||||
#: pkg/invoice/admin.go:833
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La fecha de factura tiene que ser una fecha válida."
|
||||
|
||||
#: pkg/invoice/admin.go:980
|
||||
#, c-format
|
||||
msgid "Re: quotation #%s of %s"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/invoice/admin.go:981
|
||||
msgctxt "to_char"
|
||||
msgid "MM/DD/YYYY"
|
||||
msgstr "DD/MM/YYYY"
|
||||
|
||||
#: pkg/invoice/admin.go:1083
|
||||
msgid "Invoice product ID must be an integer."
|
||||
msgstr "El ID de producto de factura tiene que ser entero."
|
||||
|
||||
#: pkg/invoice/admin.go:1084
|
||||
msgid "Invoice product ID one or greater."
|
||||
msgstr "El ID de producto de factura tiene que ser como mínimo uno."
|
||||
|
||||
#: pkg/invoice/admin.go:1088
|
||||
msgid "Product ID must be an integer."
|
||||
msgstr "El ID de producto tiene que ser un número entero."
|
||||
|
||||
#: pkg/invoice/admin.go:1089
|
||||
msgid "Product ID must zero or greater."
|
||||
msgstr "El ID de producto tiene que ser como mínimo cero."
|
||||
|
||||
#: pkg/invoice/admin.go:1098
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podéis dejar la cantidad en blanco."
|
||||
|
||||
#: pkg/invoice/admin.go:1099
|
||||
msgid "Quantity must be an integer."
|
||||
msgstr "La cantidad tiene que ser un número entero."
|
||||
|
||||
#: pkg/invoice/admin.go:1100
|
||||
msgid "Quantity must one or greater."
|
||||
msgstr "La cantidad tiene que ser como mínimo uno."
|
||||
|
||||
#: pkg/invoice/admin.go:1103
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podéis dejar el descuento en blanco."
|
||||
|
||||
#: pkg/invoice/admin.go:1104
|
||||
msgid "Discount must be an integer."
|
||||
msgstr "El descuento tiene que ser un número entero."
|
||||
|
||||
#: pkg/invoice/admin.go:1105 pkg/invoice/admin.go:1106
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descuento tiene que ser un porcentaje entre 1 y 100."
|
||||
|
||||
#: pkg/invoice/admin.go:1110
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "El impuesto escogido no es válido."
|
||||
|
||||
#: pkg/user/admin.go:18
|
||||
msgctxt "role"
|
||||
msgid "guest"
|
||||
|
@ -2902,11 +3319,6 @@ msgstr "No podéis dejar la dirección del enlace en blanco."
|
|||
msgid "This web address is not valid. It should be like https://domain.com/."
|
||||
msgstr "Esta dirección web no es válida. Tiene que ser parecido a https://dominio.com/."
|
||||
|
||||
#: pkg/company/admin.go:207 pkg/booking/checkin.go:301
|
||||
#: pkg/booking/public.go:581
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "El país escogido no es válido."
|
||||
|
||||
#: pkg/company/admin.go:211
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "No podéis dejar el nombre de empresa en blanco."
|
||||
|
@ -2923,19 +3335,10 @@ msgstr "No podéis dejar el NIF en blanco."
|
|||
msgid "This VAT number is not valid."
|
||||
msgstr "Este NIF no es válido."
|
||||
|
||||
#: pkg/company/admin.go:219 pkg/booking/public.go:599
|
||||
#: pkg/company/admin.go:219 pkg/booking/public.go:595
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "No podéis dejar el teléfono en blanco."
|
||||
|
||||
#: pkg/company/admin.go:220 pkg/booking/checkin.go:305 pkg/booking/admin.go:442
|
||||
#: pkg/booking/public.go:600
|
||||
msgid "This phone number is not valid."
|
||||
msgstr "Este teléfono no es válido."
|
||||
|
||||
#: pkg/company/admin.go:230 pkg/booking/public.go:589
|
||||
msgid "Address can not be empty."
|
||||
msgstr "No podéis dejar la dirección en blanco."
|
||||
|
||||
#: pkg/company/admin.go:231
|
||||
msgid "City can not be empty."
|
||||
msgstr "No podéis dejar la población en blanco."
|
||||
|
@ -2944,14 +3347,6 @@ msgstr "No podéis dejar la población en blanco."
|
|||
msgid "Province can not be empty."
|
||||
msgstr "No podéis dejar la provincia en blanco."
|
||||
|
||||
#: pkg/company/admin.go:233 pkg/booking/public.go:591
|
||||
msgid "Postcode can not be empty."
|
||||
msgstr "No podéis dejar el código postal en blanco."
|
||||
|
||||
#: pkg/company/admin.go:234 pkg/booking/admin.go:432 pkg/booking/public.go:592
|
||||
msgid "This postcode is not valid."
|
||||
msgstr "Este código postal no es válido."
|
||||
|
||||
#: pkg/company/admin.go:238
|
||||
msgid "RTC number can not be empty."
|
||||
msgstr "No podéis dejar el número RTC en blanco."
|
||||
|
@ -3000,40 +3395,27 @@ msgstr "No podéis dejar el archivo del medio en blanco."
|
|||
msgid "Filename can not be empty."
|
||||
msgstr "No podéis dejar el nombre del archivo en blanco."
|
||||
|
||||
#: pkg/booking/checkin.go:285
|
||||
msgid "Selected ID document type is not valid."
|
||||
msgstr "El tipo de documento de identidad escogido no es válido."
|
||||
|
||||
#: pkg/booking/checkin.go:286
|
||||
msgid "ID document number can not be empty."
|
||||
msgstr "No podéis dejar el número del documento de identidad en blanco."
|
||||
|
||||
#: pkg/booking/checkin.go:288
|
||||
#: pkg/booking/checkin.go:284
|
||||
msgid "ID document issue date must be a valid date."
|
||||
msgstr "La fecha de expedición del documento de identidad tiene que ser una fecha válida."
|
||||
|
||||
#: pkg/booking/checkin.go:289
|
||||
#: pkg/booking/checkin.go:285
|
||||
msgid "ID document issue date must be in the past."
|
||||
msgstr "La fecha de expedición del documento de identidad tiene que ser del pasado."
|
||||
|
||||
#: pkg/booking/checkin.go:292 pkg/booking/checkin.go:293
|
||||
#: pkg/booking/admin.go:425 pkg/booking/public.go:585
|
||||
msgid "Full name can not be empty."
|
||||
msgstr "No podéis dejar el nombre y los apellidos en blanco."
|
||||
|
||||
#: pkg/booking/checkin.go:294
|
||||
#: pkg/booking/checkin.go:290
|
||||
msgid "Selected sex is not valid."
|
||||
msgstr "El sexo escogido no es válido."
|
||||
|
||||
#: pkg/booking/checkin.go:295
|
||||
#: pkg/booking/checkin.go:291
|
||||
msgid "Birthdate can not be empty"
|
||||
msgstr "No podéis dejar la fecha de nacimiento en blanco."
|
||||
|
||||
#: pkg/booking/checkin.go:296
|
||||
#: pkg/booking/checkin.go:292
|
||||
msgid "Birthdate must be a valid date."
|
||||
msgstr "La fecha de nacimiento tiene que ser una fecha válida."
|
||||
|
||||
#: pkg/booking/checkin.go:297
|
||||
#: pkg/booking/checkin.go:293
|
||||
msgid "Birthdate must be in the past."
|
||||
msgstr "La fecha de nacimiento tiene que ser del pasado."
|
||||
|
||||
|
@ -3057,28 +3439,24 @@ msgctxt "cart"
|
|||
msgid "Dog"
|
||||
msgstr "Perro"
|
||||
|
||||
#: pkg/booking/admin.go:217
|
||||
#: pkg/booking/admin.go:218
|
||||
msgctxt "filename"
|
||||
msgid "bookings.ods"
|
||||
msgstr "reservas.ods"
|
||||
|
||||
#: pkg/booking/admin.go:426 pkg/booking/public.go:586
|
||||
msgid "Full name must have at least one letter."
|
||||
msgstr "El nombre y los apellidos tienen que tener como mínimo una letra."
|
||||
|
||||
#: pkg/booking/admin.go:431
|
||||
#: pkg/booking/admin.go:432
|
||||
msgid "Country can not be empty to validate the postcode."
|
||||
msgstr "No podéis dejar el país en blanco para validar el código postal."
|
||||
|
||||
#: pkg/booking/admin.go:441
|
||||
#: pkg/booking/admin.go:442
|
||||
msgid "Country can not be empty to validate the phone."
|
||||
msgstr "No podéis dejar el país en blanco para validar el teléfono."
|
||||
|
||||
#: pkg/booking/admin.go:448
|
||||
#: pkg/booking/admin.go:449
|
||||
msgid "You must select at least one accommodation."
|
||||
msgstr "Tenéis que seleccionar como mínimo un alojamiento."
|
||||
|
||||
#: pkg/booking/admin.go:454
|
||||
#: pkg/booking/admin.go:455
|
||||
msgid "The selected accommodations have no available openings in the requested dates."
|
||||
msgstr "Los alojamientos seleccionados no tienen disponibilidad en las fechas pedidas."
|
||||
|
||||
|
@ -3191,11 +3569,7 @@ msgstr "%s tiene que ser como mínimo %d."
|
|||
msgid "%s must be at most %d."
|
||||
msgstr "%s tiene que ser como máximo %d"
|
||||
|
||||
#: pkg/booking/public.go:590
|
||||
msgid "Town or village can not be empty."
|
||||
msgstr "No podéis dejar la población en blanco."
|
||||
|
||||
#: pkg/booking/public.go:605
|
||||
#: pkg/booking/public.go:601
|
||||
msgid "It is mandatory to agree to the reservation conditions."
|
||||
msgstr "Es obligatorio aceptar las condiciones de reserva."
|
||||
|
||||
|
|
614
po/fr.po
614
po/fr.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: camper\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2024-04-26 16:53+0200\n"
|
||||
"POT-Creation-Date: 2024-04-28 20:05+0200\n"
|
||||
"PO-Revision-Date: 2024-02-06 10:05+0100\n"
|
||||
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
|
||||
"Language-Team: French <traduc@traduc.org>\n"
|
||||
|
@ -239,6 +239,7 @@ msgstr "Options de type d’emplacement de camping"
|
|||
#: web/templates/mail/payment/details.gotxt:39
|
||||
#: web/templates/public/booking/fields.gohtml:146
|
||||
#: web/templates/admin/payment/details.gohtml:140
|
||||
#: web/templates/admin/customer/form.gohtml:28
|
||||
#: web/templates/admin/booking/fields.gohtml:188
|
||||
msgctxt "title"
|
||||
msgid "Customer Details"
|
||||
|
@ -247,6 +248,7 @@ msgstr "Détails du client"
|
|||
#: web/templates/mail/payment/details.gotxt:41
|
||||
#: web/templates/public/booking/fields.gohtml:149
|
||||
#: web/templates/admin/payment/details.gohtml:143
|
||||
#: web/templates/admin/customer/form.gohtml:31
|
||||
#: web/templates/admin/booking/fields.gohtml:191
|
||||
msgctxt "input"
|
||||
msgid "Full name"
|
||||
|
@ -255,6 +257,7 @@ msgstr "Nom et prénom"
|
|||
#: web/templates/mail/payment/details.gotxt:42
|
||||
#: web/templates/public/booking/fields.gohtml:158
|
||||
#: web/templates/admin/payment/details.gohtml:147
|
||||
#: web/templates/admin/customer/form.gohtml:69
|
||||
#: web/templates/admin/taxDetails.gohtml:69
|
||||
msgctxt "input"
|
||||
msgid "Address"
|
||||
|
@ -263,6 +266,7 @@ msgstr "Adresse"
|
|||
#: web/templates/mail/payment/details.gotxt:43
|
||||
#: web/templates/public/booking/fields.gohtml:167
|
||||
#: web/templates/admin/payment/details.gohtml:151
|
||||
#: web/templates/admin/customer/form.gohtml:105
|
||||
#: web/templates/admin/taxDetails.gohtml:93
|
||||
msgctxt "input"
|
||||
msgid "Postcode"
|
||||
|
@ -278,6 +282,7 @@ msgstr "Ville"
|
|||
#: web/templates/mail/payment/details.gotxt:45
|
||||
#: web/templates/public/booking/fields.gohtml:187
|
||||
#: web/templates/admin/payment/details.gohtml:159
|
||||
#: web/templates/admin/customer/form.gohtml:117
|
||||
#: web/templates/admin/taxDetails.gohtml:101
|
||||
msgctxt "input"
|
||||
msgid "Country"
|
||||
|
@ -411,11 +416,16 @@ msgid "Order Number"
|
|||
msgstr "Numéro de commande"
|
||||
|
||||
#: web/templates/public/payment/details.gohtml:8
|
||||
#: web/templates/admin/invoice/index.gohtml:103
|
||||
#: web/templates/admin/invoice/view.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
#: web/templates/public/payment/details.gohtml:12
|
||||
#: web/templates/admin/invoice/form.gohtml:119
|
||||
#: web/templates/admin/invoice/view.gohtml:63
|
||||
#: web/templates/admin/invoice/view.gohtml:103
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Totale"
|
||||
|
@ -579,6 +589,11 @@ msgctxt "input"
|
|||
msgid "Year"
|
||||
msgstr "Année"
|
||||
|
||||
#: web/templates/public/form.gohtml:83 web/templates/admin/form.gohtml:83
|
||||
msgctxt "action"
|
||||
msgid "Filters"
|
||||
msgstr "Filtres"
|
||||
|
||||
#: web/templates/public/campsite/type.gohtml:49
|
||||
#: web/templates/public/booking/fields.gohtml:278
|
||||
msgctxt "action"
|
||||
|
@ -929,7 +944,7 @@ msgstr "Menu"
|
|||
#: web/templates/admin/campsite/type/option/form.gohtml:16
|
||||
#: web/templates/admin/campsite/type/option/index.gohtml:10
|
||||
#: web/templates/admin/campsite/type/index.gohtml:10
|
||||
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:95
|
||||
#: web/templates/admin/layout.gohtml:46 web/templates/admin/layout.gohtml:101
|
||||
#: web/templates/admin/booking/fields.gohtml:266
|
||||
msgctxt "title"
|
||||
msgid "Campsites"
|
||||
|
@ -1004,13 +1019,15 @@ msgid "Campground map"
|
|||
msgstr "Plan du camping"
|
||||
|
||||
#: web/templates/public/booking/fields.gohtml:176
|
||||
#: web/templates/admin/customer/form.gohtml:81
|
||||
msgctxt "input"
|
||||
msgid "Town or village"
|
||||
msgstr "Ville"
|
||||
|
||||
#: web/templates/public/booking/fields.gohtml:193
|
||||
#: web/templates/admin/customer/form.gohtml:121
|
||||
#: web/templates/admin/booking/fields.gohtml:204
|
||||
#: web/templates/admin/booking/guest.gohtml:109
|
||||
#: web/templates/admin/booking/guest.gohtml:111
|
||||
msgid "Choose a country"
|
||||
msgstr "Choisissez un pays"
|
||||
|
||||
|
@ -1164,6 +1181,7 @@ msgstr "Slug"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:51
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:41
|
||||
#: web/templates/admin/season/form.gohtml:50
|
||||
#: web/templates/admin/invoice/product-form.gohtml:16
|
||||
#: web/templates/admin/services/form.gohtml:53
|
||||
#: web/templates/admin/profile.gohtml:29
|
||||
#: web/templates/admin/surroundings/form.gohtml:41
|
||||
|
@ -1188,6 +1206,8 @@ msgstr "Contenu"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:287
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:98
|
||||
#: web/templates/admin/season/form.gohtml:73
|
||||
#: web/templates/admin/customer/form.gohtml:153
|
||||
#: web/templates/admin/invoice/form.gohtml:137
|
||||
#: web/templates/admin/services/form.gohtml:81
|
||||
#: web/templates/admin/surroundings/form.gohtml:69
|
||||
#: web/templates/admin/surroundings/index.gohtml:58
|
||||
|
@ -1211,6 +1231,7 @@ msgstr "Mettre à jour"
|
|||
#: web/templates/admin/campsite/type/form.gohtml:289
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:100
|
||||
#: web/templates/admin/season/form.gohtml:75
|
||||
#: web/templates/admin/customer/form.gohtml:155
|
||||
#: web/templates/admin/services/form.gohtml:83
|
||||
#: web/templates/admin/surroundings/form.gohtml:71
|
||||
#: web/templates/admin/amenity/feature/form.gohtml:67
|
||||
|
@ -1232,6 +1253,7 @@ msgstr "Ajouter un texte juridique"
|
|||
#: web/templates/admin/campsite/type/option/index.gohtml:30
|
||||
#: web/templates/admin/campsite/type/index.gohtml:29
|
||||
#: web/templates/admin/season/index.gohtml:29
|
||||
#: web/templates/admin/customer/index.gohtml:19
|
||||
#: web/templates/admin/user/index.gohtml:20
|
||||
#: web/templates/admin/surroundings/index.gohtml:83
|
||||
#: web/templates/admin/amenity/feature/index.gohtml:30
|
||||
|
@ -1725,6 +1747,7 @@ msgid "Per night"
|
|||
msgstr "Par nuit"
|
||||
|
||||
#: web/templates/admin/campsite/type/option/form.gohtml:84
|
||||
#: web/templates/admin/invoice/product-form.gohtml:29
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Prix"
|
||||
|
@ -1824,12 +1847,316 @@ msgctxt "action"
|
|||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:8
|
||||
msgctxt "title"
|
||||
msgid "Edit Customer"
|
||||
msgstr "Modifier le client"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "New Customer"
|
||||
msgstr "Nouveau client"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:15
|
||||
#: web/templates/admin/invoice/index.gohtml:105
|
||||
msgctxt "title"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:44
|
||||
#: web/templates/admin/booking/guest.gohtml:8
|
||||
msgctxt "input"
|
||||
msgid "ID document number"
|
||||
msgstr "Numéro de document d’identité"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:56
|
||||
#: web/templates/admin/booking/guest.gohtml:20
|
||||
msgctxt "input"
|
||||
msgid "ID document type"
|
||||
msgstr "Type de document d’identité"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:61
|
||||
#: web/templates/admin/booking/guest.gohtml:25
|
||||
msgid "Choose an ID document type"
|
||||
msgstr "Choisissez un type de document d’identité"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:93
|
||||
#: web/templates/admin/taxDetails.gohtml:85
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Province"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:129
|
||||
#: web/templates/admin/booking/fields.gohtml:239
|
||||
msgctxt "input"
|
||||
msgid "Email (optional)"
|
||||
msgstr "E-mail (facultatif)"
|
||||
|
||||
#: web/templates/admin/customer/form.gohtml:140
|
||||
#: web/templates/admin/booking/fields.gohtml:248
|
||||
#: web/templates/admin/booking/guest.gohtml:119
|
||||
msgctxt "input"
|
||||
msgid "Phone (optional)"
|
||||
msgstr "Téléphone (facultatif)"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:95
|
||||
msgctxt "title"
|
||||
msgid "Customers"
|
||||
msgstr "Clients"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:14
|
||||
msgctxt "action"
|
||||
msgid "Add Customer"
|
||||
msgstr "Ajouter un client"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:20
|
||||
#: web/templates/admin/user/login-attempts.gohtml:20
|
||||
#: web/templates/admin/user/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Email"
|
||||
msgstr "E-mail"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Phone"
|
||||
msgstr "Téléphone"
|
||||
|
||||
#: web/templates/admin/customer/index.gohtml:33
|
||||
msgid "No customer found."
|
||||
msgstr "Aucun client trouvée."
|
||||
|
||||
#: web/templates/admin/dashboard.gohtml:6
|
||||
#: web/templates/admin/dashboard.gohtml:13 web/templates/admin/layout.gohtml:89
|
||||
msgctxt "title"
|
||||
msgid "Dashboard"
|
||||
msgstr "Tableau de bord"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:11
|
||||
#: web/templates/admin/booking/guest.gohtml:5
|
||||
msgctxt "action"
|
||||
msgid "Remove"
|
||||
msgstr "Retirer"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:44
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantité"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:58
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Rabais (%)"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:73
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Taxes"
|
||||
|
||||
#: web/templates/admin/invoice/product-form.gohtml:79
|
||||
msgid "Select a TAX"
|
||||
msgstr "Choisissez une taxe"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:4
|
||||
msgctxt "title"
|
||||
msgid "Edit Invoice “%s”"
|
||||
msgstr "Modifier la facture «%s»"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:6
|
||||
msgctxt "title"
|
||||
msgid "New Invoice"
|
||||
msgstr "Nouvelle facture"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:15
|
||||
#: web/templates/admin/invoice/index.gohtml:2
|
||||
#: web/templates/admin/invoice/view.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:98
|
||||
msgctxt "title"
|
||||
msgid "Invoices"
|
||||
msgstr "Factures"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:32
|
||||
msgid "Product “%s” removed"
|
||||
msgstr "Produit «%s» supprimé"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:36
|
||||
msgctxt "action"
|
||||
msgid "Undo"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:51
|
||||
#: web/templates/admin/invoice/index.gohtml:39
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:56
|
||||
msgid "Select a customer"
|
||||
msgstr "Choisissez un client"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:64
|
||||
msgctxt "input"
|
||||
msgid "Invoice date"
|
||||
msgstr "Date de facture"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:77
|
||||
#: web/templates/admin/invoice/index.gohtml:51
|
||||
msgctxt "input"
|
||||
msgid "Invoice status"
|
||||
msgstr "Statut de la facture"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:92
|
||||
msgctxt "input"
|
||||
msgid "Notes (optional)"
|
||||
msgstr "Remarques (facultatif)"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:109
|
||||
#: web/templates/admin/invoice/view.gohtml:59
|
||||
msgctxt "title"
|
||||
msgid "Subtotal"
|
||||
msgstr "Sous-totale"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:133
|
||||
msgctxt "action"
|
||||
msgid "Add products"
|
||||
msgstr "Ajouter des produits"
|
||||
|
||||
#: web/templates/admin/invoice/form.gohtml:140
|
||||
msgctxt "action"
|
||||
msgid "Save"
|
||||
msgstr "Enregistrer"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:25
|
||||
msgctxt "action"
|
||||
msgid "Download invoices"
|
||||
msgstr "Télécharger les factures"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:28
|
||||
msgctxt "action"
|
||||
msgid "Export list"
|
||||
msgstr "Exporter la liste"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:43
|
||||
msgid "All customers"
|
||||
msgstr "Tous les clients"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:55
|
||||
msgid "All statuses"
|
||||
msgstr "Tous les statuts"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:63
|
||||
msgctxt "input"
|
||||
msgid "From date"
|
||||
msgstr "Partir de la date"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:72
|
||||
msgctxt "input"
|
||||
msgid "To date"
|
||||
msgstr "À ce jour"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:81
|
||||
msgctxt "input"
|
||||
msgid "Invoice number"
|
||||
msgstr "Numéro de facture"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:91
|
||||
msgctxt "action"
|
||||
msgid "Filter"
|
||||
msgstr "Filtrer"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:94
|
||||
msgctxt "action"
|
||||
msgid "Reset"
|
||||
msgstr "Réinitialiser"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:97
|
||||
msgctxt "action"
|
||||
msgid "Add invoice"
|
||||
msgstr "Nouvelle facture"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:102
|
||||
msgctxt "invoice"
|
||||
msgid "All"
|
||||
msgstr "Toutes"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:104
|
||||
msgctxt "title"
|
||||
msgid "Invoice Num."
|
||||
msgstr "Num. de facture"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:106
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Statut"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:107
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Téléchargement"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:108
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:115
|
||||
msgctxt "action"
|
||||
msgid "Select invoice %v"
|
||||
msgstr "Sélectionner la facture %v"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:144
|
||||
msgctxt "action"
|
||||
msgid "Download invoice %s"
|
||||
msgstr "Télécharger la facture %s"
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:154
|
||||
msgid "No invoices added yet."
|
||||
msgstr "Aucune facture n’a encore été ajouté."
|
||||
|
||||
#: web/templates/admin/invoice/index.gohtml:161
|
||||
msgid "Total"
|
||||
msgstr "Totale"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:2
|
||||
msgctxt "title"
|
||||
msgid "Invoice %s"
|
||||
msgstr "Facture %s"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:15
|
||||
msgctxt "action"
|
||||
msgid "Edit"
|
||||
msgstr "Éditer"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:18
|
||||
msgctxt "action"
|
||||
msgid "Download invoice"
|
||||
msgstr "Télécharger facture"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:53
|
||||
msgctxt "title"
|
||||
msgid "Concept"
|
||||
msgstr "Concept"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:54
|
||||
msgctxt "title"
|
||||
msgid "Price"
|
||||
msgstr "Prix"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:56
|
||||
msgctxt "title"
|
||||
msgid "Discount"
|
||||
msgstr "Rabais"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:58
|
||||
msgctxt "title"
|
||||
msgid "Units"
|
||||
msgstr "Unités"
|
||||
|
||||
#: web/templates/admin/invoice/view.gohtml:93
|
||||
msgctxt "title"
|
||||
msgid "Tax Base"
|
||||
msgstr "Import imposable"
|
||||
|
||||
#: web/templates/admin/login.gohtml:6 web/templates/admin/login.gohtml:18
|
||||
msgctxt "title"
|
||||
msgid "Login"
|
||||
|
@ -1928,12 +2255,6 @@ msgctxt "title"
|
|||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: web/templates/admin/user/login-attempts.gohtml:20
|
||||
#: web/templates/admin/user/index.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "Email"
|
||||
msgstr "E-mail"
|
||||
|
||||
#: web/templates/admin/user/login-attempts.gohtml:21
|
||||
msgctxt "header"
|
||||
msgid "IP Address"
|
||||
|
@ -1985,11 +2306,6 @@ msgctxt "input"
|
|||
msgid "Trade Name"
|
||||
msgstr "Nom commercial"
|
||||
|
||||
#: web/templates/admin/taxDetails.gohtml:85
|
||||
msgctxt "input"
|
||||
msgid "Province"
|
||||
msgstr "Province"
|
||||
|
||||
#: web/templates/admin/taxDetails.gohtml:111
|
||||
msgctxt "input"
|
||||
msgid "Currency"
|
||||
|
@ -2214,11 +2530,11 @@ msgctxt "title"
|
|||
msgid "Bookings"
|
||||
msgstr "Réservations"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:101
|
||||
#: web/templates/admin/layout.gohtml:107
|
||||
msgid "Breadcrumb"
|
||||
msgstr "Fil d’Ariane"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:113
|
||||
#: web/templates/admin/layout.gohtml:119
|
||||
msgid "Camper Version: %s"
|
||||
msgstr "Camper version: %s"
|
||||
|
||||
|
@ -2340,7 +2656,7 @@ msgid "Country (optional)"
|
|||
msgstr "Pays (facultatif)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:212
|
||||
#: web/templates/admin/booking/guest.gohtml:128
|
||||
#: web/templates/admin/booking/guest.gohtml:130
|
||||
msgctxt "input"
|
||||
msgid "Address (optional)"
|
||||
msgstr "Adresse (facultatif)"
|
||||
|
@ -2355,17 +2671,6 @@ msgctxt "input"
|
|||
msgid "Town or village (optional)"
|
||||
msgstr "Ville (facultatif)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:239
|
||||
msgctxt "input"
|
||||
msgid "Email (optional)"
|
||||
msgstr "E-mail (facultatif)"
|
||||
|
||||
#: web/templates/admin/booking/fields.gohtml:248
|
||||
#: web/templates/admin/booking/guest.gohtml:117
|
||||
msgctxt "input"
|
||||
msgid "Phone (optional)"
|
||||
msgstr "Téléphone (facultatif)"
|
||||
|
||||
#: web/templates/admin/booking/form.gohtml:8
|
||||
msgctxt "title"
|
||||
msgid "Edit Booking"
|
||||
|
@ -2430,60 +2735,41 @@ msgstr "Nom du titulaire"
|
|||
msgid "No booking found."
|
||||
msgstr "Aucune réservation trouvée."
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:5
|
||||
msgctxt "action"
|
||||
msgid "Remove"
|
||||
msgstr "Retirer"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:8
|
||||
msgctxt "input"
|
||||
msgid "ID document number"
|
||||
msgstr "Numéro de document d’identité"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:20
|
||||
msgctxt "input"
|
||||
msgid "ID document type"
|
||||
msgstr "Type de document d’identité"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:25
|
||||
msgid "Choose an ID document type"
|
||||
msgstr "Choisissez un type de document d’identité"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:33
|
||||
msgctxt "input"
|
||||
msgid "ID document issue date (if any)"
|
||||
msgstr "Date de délivrance du document d’identité (si j'en ai)"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:44
|
||||
#: web/templates/admin/booking/guest.gohtml:45
|
||||
msgctxt "input"
|
||||
msgid "First surname"
|
||||
msgstr "Premier nom"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:56
|
||||
#: web/templates/admin/booking/guest.gohtml:57
|
||||
msgctxt "input"
|
||||
msgid "Second surname (if has one)"
|
||||
msgstr "Deuxième nom"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:67
|
||||
#: web/templates/admin/booking/guest.gohtml:68
|
||||
msgctxt "input"
|
||||
msgid "Given name"
|
||||
msgstr "Prénom"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:79
|
||||
#: web/templates/admin/booking/guest.gohtml:80
|
||||
msgctxt "input"
|
||||
msgid "Sex"
|
||||
msgstr "Sexe"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:84
|
||||
#: web/templates/admin/booking/guest.gohtml:85
|
||||
msgid "Choose a sex"
|
||||
msgstr "Choisissez un sexe"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:92
|
||||
#: web/templates/admin/booking/guest.gohtml:93
|
||||
msgctxt "input"
|
||||
msgid "Birthdate"
|
||||
msgstr "Date de naissance"
|
||||
|
||||
#: web/templates/admin/booking/guest.gohtml:104
|
||||
#: web/templates/admin/booking/guest.gohtml:106
|
||||
msgctxt "input"
|
||||
msgid "Nationality"
|
||||
msgstr "Nationalité"
|
||||
|
@ -2553,8 +2839,9 @@ msgstr "Paiement de réservation reçu avec succès"
|
|||
#: pkg/legal/admin.go:258 pkg/app/user.go:249 pkg/campsite/types/option.go:365
|
||||
#: pkg/campsite/types/feature.go:272 pkg/campsite/types/admin.go:577
|
||||
#: pkg/campsite/feature.go:269 pkg/season/admin.go:411
|
||||
#: pkg/services/admin.go:316 pkg/surroundings/admin.go:340
|
||||
#: pkg/amenity/feature.go:269 pkg/amenity/admin.go:283
|
||||
#: pkg/invoice/admin.go:1092 pkg/services/admin.go:316
|
||||
#: pkg/surroundings/admin.go:340 pkg/amenity/feature.go:269
|
||||
#: pkg/amenity/admin.go:283
|
||||
msgid "Name can not be empty."
|
||||
msgstr "Le nom ne peut pas être laissé vide."
|
||||
|
||||
|
@ -2589,12 +2876,12 @@ msgid "Slide image must be an image media type."
|
|||
msgstr "L’image de la diapositive doit être de type média d’image."
|
||||
|
||||
#: pkg/app/login.go:56 pkg/app/user.go:246 pkg/company/admin.go:224
|
||||
#: pkg/booking/public.go:596
|
||||
#: pkg/booking/public.go:592
|
||||
msgid "Email can not be empty."
|
||||
msgstr "L’e-mail ne peut pas être vide."
|
||||
|
||||
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/company/admin.go:225
|
||||
#: pkg/booking/admin.go:437 pkg/booking/public.go:597
|
||||
#: pkg/app/login.go:57 pkg/app/user.go:247 pkg/customer/admin.go:312
|
||||
#: pkg/company/admin.go:225 pkg/booking/admin.go:438 pkg/booking/public.go:593
|
||||
msgid "This email is not valid. It should be like name@domain.com."
|
||||
msgstr "Cette adresse e-mail n’est pas valide. Il devrait en être name@domain.com."
|
||||
|
||||
|
@ -2623,7 +2910,7 @@ msgstr "La langue sélectionnée n’est pas valide."
|
|||
msgid "File must be a valid PNG or JPEG image."
|
||||
msgstr "Le fichier doit être une image PNG ou JPEG valide."
|
||||
|
||||
#: pkg/app/admin.go:73
|
||||
#: pkg/app/admin.go:79
|
||||
msgid "Access forbidden"
|
||||
msgstr "Accès interdit"
|
||||
|
||||
|
@ -2651,15 +2938,15 @@ msgstr "Le maximum doit être un nombre entier."
|
|||
msgid "Maximum must be equal or greater than minimum."
|
||||
msgstr "Le maximum doit être égal ou supérieur au minimum."
|
||||
|
||||
#: pkg/campsite/types/option.go:382
|
||||
#: pkg/campsite/types/option.go:382 pkg/invoice/admin.go:1093
|
||||
msgid "Price can not be empty."
|
||||
msgstr "Le prix ne peut pas être vide."
|
||||
|
||||
#: pkg/campsite/types/option.go:383
|
||||
#: pkg/campsite/types/option.go:383 pkg/invoice/admin.go:1094
|
||||
msgid "Price must be a decimal number."
|
||||
msgstr "Le prix doit être un nombre décimal."
|
||||
|
||||
#: pkg/campsite/types/option.go:384
|
||||
#: pkg/campsite/types/option.go:384 pkg/invoice/admin.go:1095
|
||||
msgid "Price must be zero or greater."
|
||||
msgstr "Le prix doit être égal ou supérieur à zéro."
|
||||
|
||||
|
@ -2805,7 +3092,7 @@ msgctxt "header"
|
|||
msgid "Children (aged 2 to 10)"
|
||||
msgstr "Enfants (de 2 à 10 anys)"
|
||||
|
||||
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:413 pkg/booking/public.go:177
|
||||
#: pkg/campsite/admin.go:280 pkg/booking/admin.go:414 pkg/booking/public.go:177
|
||||
#: pkg/booking/public.go:232
|
||||
msgid "Selected campsite type is not valid."
|
||||
msgstr "Le type d’emplacement sélectionné n’est pas valide."
|
||||
|
@ -2843,6 +3130,136 @@ msgstr "La date de fin ne peut pas être vide."
|
|||
msgid "End date must be a valid date."
|
||||
msgstr "La date de fin doit être une date valide."
|
||||
|
||||
#: pkg/customer/admin.go:293 pkg/company/admin.go:207
|
||||
#: pkg/booking/checkin.go:297 pkg/booking/public.go:577
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Le pays sélectionné n’est pas valide."
|
||||
|
||||
#: pkg/customer/admin.go:297 pkg/booking/checkin.go:281
|
||||
msgid "Selected ID document type is not valid."
|
||||
msgstr "Le type de document d’identité sélectionné n’est pas valide."
|
||||
|
||||
#: pkg/customer/admin.go:298 pkg/booking/checkin.go:282
|
||||
msgid "ID document number can not be empty."
|
||||
msgstr "Le numéro de documento d’identité ne peut pas être vide."
|
||||
|
||||
#: pkg/customer/admin.go:300 pkg/booking/checkin.go:288
|
||||
#: pkg/booking/checkin.go:289 pkg/booking/admin.go:426
|
||||
#: pkg/booking/public.go:581
|
||||
msgid "Full name can not be empty."
|
||||
msgstr "Le nom complet ne peut pas être vide."
|
||||
|
||||
#: pkg/customer/admin.go:301 pkg/booking/admin.go:427 pkg/booking/public.go:582
|
||||
msgid "Full name must have at least one letter."
|
||||
msgstr "Le nom complet doit comporter au moins une lettre."
|
||||
|
||||
#: pkg/customer/admin.go:304 pkg/company/admin.go:230 pkg/booking/public.go:585
|
||||
msgid "Address can not be empty."
|
||||
msgstr "L’adresse ne peut pas être vide."
|
||||
|
||||
#: pkg/customer/admin.go:305 pkg/booking/public.go:586
|
||||
msgid "Town or village can not be empty."
|
||||
msgstr "La ville ne peut pas être vide."
|
||||
|
||||
#: pkg/customer/admin.go:306 pkg/company/admin.go:233 pkg/booking/public.go:587
|
||||
msgid "Postcode can not be empty."
|
||||
msgstr "Le code postal ne peut pas être vide."
|
||||
|
||||
#: pkg/customer/admin.go:307 pkg/company/admin.go:234 pkg/booking/admin.go:433
|
||||
#: pkg/booking/public.go:588
|
||||
msgid "This postcode is not valid."
|
||||
msgstr "Ce code postal n’est pas valide."
|
||||
|
||||
#: pkg/customer/admin.go:315 pkg/company/admin.go:220
|
||||
#: pkg/booking/checkin.go:301 pkg/booking/admin.go:443
|
||||
#: pkg/booking/public.go:596
|
||||
msgid "This phone number is not valid."
|
||||
msgstr "Ce numéro de téléphone n’est pas valide."
|
||||
|
||||
#: pkg/invoice/admin.go:649
|
||||
msgctxt "filename"
|
||||
msgid "invoices.zip"
|
||||
msgstr "factures.zip"
|
||||
|
||||
#: pkg/invoice/admin.go:664
|
||||
msgctxt "filename"
|
||||
msgid "invoices.ods"
|
||||
msgstr "factures.ods"
|
||||
|
||||
#: pkg/invoice/admin.go:666 pkg/invoice/admin.go:1285 pkg/invoice/admin.go:1292
|
||||
msgid "Invalid action"
|
||||
msgstr "Actin invalide"
|
||||
|
||||
#: pkg/invoice/admin.go:830
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "L’statut sélectionné n’est pas valide."
|
||||
|
||||
#: pkg/invoice/admin.go:831
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "Le client sélectionné n’est pas valide."
|
||||
|
||||
#: pkg/invoice/admin.go:832
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "La date de facture ne peut pas être vide."
|
||||
|
||||
#: pkg/invoice/admin.go:833
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La date de facture doit être une date valide."
|
||||
|
||||
#: pkg/invoice/admin.go:980
|
||||
#, c-format
|
||||
msgid "Re: quotation #%s of %s"
|
||||
msgstr ""
|
||||
|
||||
#: pkg/invoice/admin.go:981
|
||||
msgctxt "to_char"
|
||||
msgid "MM/DD/YYYY"
|
||||
msgstr "DD/MM/YYYY"
|
||||
|
||||
#: pkg/invoice/admin.go:1083
|
||||
msgid "Invoice product ID must be an integer."
|
||||
msgstr "Le ID de produit de facture doit être un entier."
|
||||
|
||||
#: pkg/invoice/admin.go:1084
|
||||
msgid "Invoice product ID one or greater."
|
||||
msgstr "Le ID de produit de facture doit être égal ou supérieur à un."
|
||||
|
||||
#: pkg/invoice/admin.go:1088
|
||||
msgid "Product ID must be an integer."
|
||||
msgstr "Le ID de produit doit être un entier."
|
||||
|
||||
#: pkg/invoice/admin.go:1089
|
||||
msgid "Product ID must zero or greater."
|
||||
msgstr "Le ID de produit doit être égal ou supérieur à zéro."
|
||||
|
||||
#: pkg/invoice/admin.go:1098
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "La quantité ne peut pas être vide."
|
||||
|
||||
#: pkg/invoice/admin.go:1099
|
||||
msgid "Quantity must be an integer."
|
||||
msgstr "La quantité doit être un entier."
|
||||
|
||||
#: pkg/invoice/admin.go:1100
|
||||
msgid "Quantity must one or greater."
|
||||
msgstr "La quantité doit être égnal ou supérieur à zéro."
|
||||
|
||||
#: pkg/invoice/admin.go:1103
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "Le rabais ne peut pas être vide."
|
||||
|
||||
#: pkg/invoice/admin.go:1104
|
||||
msgid "Discount must be an integer."
|
||||
msgstr "Le rabais doit être un entier."
|
||||
|
||||
#: pkg/invoice/admin.go:1105 pkg/invoice/admin.go:1106
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "Le rabais doit être un pourcentage compris entre 0 et 100."
|
||||
|
||||
#: pkg/invoice/admin.go:1110
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "La taxe sélectionnée n’est pas valide."
|
||||
|
||||
#: pkg/user/admin.go:18
|
||||
msgctxt "role"
|
||||
msgid "guest"
|
||||
|
@ -2902,11 +3319,6 @@ msgstr "L’addresse du lien ne peut pas être vide."
|
|||
msgid "This web address is not valid. It should be like https://domain.com/."
|
||||
msgstr "Cette adresse web n’est pas valide. Il devrait en être https://domain.com/."
|
||||
|
||||
#: pkg/company/admin.go:207 pkg/booking/checkin.go:301
|
||||
#: pkg/booking/public.go:581
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "Le pays sélectionné n’est pas valide."
|
||||
|
||||
#: pkg/company/admin.go:211
|
||||
msgid "Business name can not be empty."
|
||||
msgstr "Le nom de l’entreprise ne peut pas être vide."
|
||||
|
@ -2923,19 +3335,10 @@ msgstr "Le numéro de TVA ne peut pas être vide."
|
|||
msgid "This VAT number is not valid."
|
||||
msgstr "Ce numéro de TVA n’est pas valide."
|
||||
|
||||
#: pkg/company/admin.go:219 pkg/booking/public.go:599
|
||||
#: pkg/company/admin.go:219 pkg/booking/public.go:595
|
||||
msgid "Phone can not be empty."
|
||||
msgstr "Le téléphone ne peut pas être vide."
|
||||
|
||||
#: pkg/company/admin.go:220 pkg/booking/checkin.go:305 pkg/booking/admin.go:442
|
||||
#: pkg/booking/public.go:600
|
||||
msgid "This phone number is not valid."
|
||||
msgstr "Ce numéro de téléphone n’est pas valide."
|
||||
|
||||
#: pkg/company/admin.go:230 pkg/booking/public.go:589
|
||||
msgid "Address can not be empty."
|
||||
msgstr "L’adresse ne peut pas être vide."
|
||||
|
||||
#: pkg/company/admin.go:231
|
||||
msgid "City can not be empty."
|
||||
msgstr "La ville ne peut pas être vide."
|
||||
|
@ -2944,14 +3347,6 @@ msgstr "La ville ne peut pas être vide."
|
|||
msgid "Province can not be empty."
|
||||
msgstr "La province ne peut pas être vide."
|
||||
|
||||
#: pkg/company/admin.go:233 pkg/booking/public.go:591
|
||||
msgid "Postcode can not be empty."
|
||||
msgstr "Le code postal ne peut pas être vide."
|
||||
|
||||
#: pkg/company/admin.go:234 pkg/booking/admin.go:432 pkg/booking/public.go:592
|
||||
msgid "This postcode is not valid."
|
||||
msgstr "Ce code postal n’est pas valide."
|
||||
|
||||
#: pkg/company/admin.go:238
|
||||
msgid "RTC number can not be empty."
|
||||
msgstr "Le numéro RTC ne peut pas être vide."
|
||||
|
@ -3000,40 +3395,27 @@ msgstr "Le fichier téléchargé ne peut pas être vide."
|
|||
msgid "Filename can not be empty."
|
||||
msgstr "Le nom de fichier ne peut pas être vide."
|
||||
|
||||
#: pkg/booking/checkin.go:285
|
||||
msgid "Selected ID document type is not valid."
|
||||
msgstr "Le type de document d’identité sélectionné n’est pas valide."
|
||||
|
||||
#: pkg/booking/checkin.go:286
|
||||
msgid "ID document number can not be empty."
|
||||
msgstr "Le numéro de documento d’identité ne peut pas être vide."
|
||||
|
||||
#: pkg/booking/checkin.go:288
|
||||
#: pkg/booking/checkin.go:284
|
||||
msgid "ID document issue date must be a valid date."
|
||||
msgstr "La date de délivrance du document d’identité doit être une date valide."
|
||||
|
||||
#: pkg/booking/checkin.go:289
|
||||
#: pkg/booking/checkin.go:285
|
||||
msgid "ID document issue date must be in the past."
|
||||
msgstr "La ate de délivrance du document d’identité doit être du passé."
|
||||
|
||||
#: pkg/booking/checkin.go:292 pkg/booking/checkin.go:293
|
||||
#: pkg/booking/admin.go:425 pkg/booking/public.go:585
|
||||
msgid "Full name can not be empty."
|
||||
msgstr "Le nom complet ne peut pas être vide."
|
||||
|
||||
#: pkg/booking/checkin.go:294
|
||||
#: pkg/booking/checkin.go:290
|
||||
msgid "Selected sex is not valid."
|
||||
msgstr "Le sexe sélectionné n’est pas valide."
|
||||
|
||||
#: pkg/booking/checkin.go:295
|
||||
#: pkg/booking/checkin.go:291
|
||||
msgid "Birthdate can not be empty"
|
||||
msgstr "La date de naissance ne peut pas être vide."
|
||||
|
||||
#: pkg/booking/checkin.go:296
|
||||
#: pkg/booking/checkin.go:292
|
||||
msgid "Birthdate must be a valid date."
|
||||
msgstr "La date de naissance doit être une date valide."
|
||||
|
||||
#: pkg/booking/checkin.go:297
|
||||
#: pkg/booking/checkin.go:293
|
||||
msgid "Birthdate must be in the past."
|
||||
msgstr "La date de naissance doit être du passé."
|
||||
|
||||
|
@ -3057,28 +3439,24 @@ msgctxt "cart"
|
|||
msgid "Dog"
|
||||
msgstr "Chien"
|
||||
|
||||
#: pkg/booking/admin.go:217
|
||||
#: pkg/booking/admin.go:218
|
||||
msgctxt "filename"
|
||||
msgid "bookings.ods"
|
||||
msgstr "reservations.ods"
|
||||
|
||||
#: pkg/booking/admin.go:426 pkg/booking/public.go:586
|
||||
msgid "Full name must have at least one letter."
|
||||
msgstr "Le nom complet doit comporter au moins une lettre."
|
||||
|
||||
#: pkg/booking/admin.go:431
|
||||
#: pkg/booking/admin.go:432
|
||||
msgid "Country can not be empty to validate the postcode."
|
||||
msgstr "Le pays ne peut pas être vide pour valider le code postal."
|
||||
|
||||
#: pkg/booking/admin.go:441
|
||||
#: pkg/booking/admin.go:442
|
||||
msgid "Country can not be empty to validate the phone."
|
||||
msgstr "Le pays ne peut pas être vide pour valider le téléphone."
|
||||
|
||||
#: pkg/booking/admin.go:448
|
||||
#: pkg/booking/admin.go:449
|
||||
msgid "You must select at least one accommodation."
|
||||
msgstr "Vous devez sélectionner au moins un hébergement."
|
||||
|
||||
#: pkg/booking/admin.go:454
|
||||
#: pkg/booking/admin.go:455
|
||||
msgid "The selected accommodations have no available openings in the requested dates."
|
||||
msgstr "Les hébergements sélectionnés n’ont pas de disponibilités aux dates demandées."
|
||||
|
||||
|
@ -3191,11 +3569,7 @@ msgstr "%s doit être %d ou plus."
|
|||
msgid "%s must be at most %d."
|
||||
msgstr "%s doit être tout au plus %d."
|
||||
|
||||
#: pkg/booking/public.go:590
|
||||
msgid "Town or village can not be empty."
|
||||
msgstr "La ville ne peut pas être vide."
|
||||
|
||||
#: pkg/booking/public.go:605
|
||||
#: pkg/booking/public.go:601
|
||||
msgid "It is mandatory to agree to the reservation conditions."
|
||||
msgstr "Il est obligatoire d’accepter les conditions de réservation."
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
-- Deploy camper:add_contact to pg
|
||||
-- requires: schema_camper
|
||||
-- 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 camper, public;
|
||||
|
||||
drop function if exists add_contact(integer, text, text, text, text, text, text, text, text, text, country_code);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:add_invoice from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.add_invoice(integer, date, integer, text, integer, camper.new_invoice_product[]);
|
||||
|
||||
commit;
|
|
@ -1,4 +1,4 @@
|
|||
-- Revert numerus:available_currencies from pg
|
||||
-- Revert camper:available_currencies from pg
|
||||
|
||||
begin;
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
-- Revert camper:available_invoice_status from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper;
|
||||
|
||||
delete from invoice_status_i18n;
|
||||
delete from invoice_status;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:compute_new_invoice_amount from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.compute_new_invoice_amount(integer, camper.new_invoice_product[]);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,8 @@
|
|||
-- Revert camper:contact from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop policy if exists company_policy on camper.contact;
|
||||
drop table if exists camper.contact;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Revert camper:contact_email from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, 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,24 @@
|
|||
-- Revert camper:contact_phone from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
alter table contact
|
||||
add column phone packed_phone_number default '+34000000000'
|
||||
;
|
||||
|
||||
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
|
||||
, alter column phone drop default
|
||||
;
|
||||
|
||||
drop table if exists contact_phone;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:discount_rate from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop domain if exists camper.discount_rate;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,17 @@
|
|||
-- Deploy camper:edit_contact to pg
|
||||
-- requires: schema_camper
|
||||
-- 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 camper, public;
|
||||
|
||||
drop function if exists edit_contact(uuid, text, text, text, text, text, text, text, text, text, country_code);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:edit_invoice from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.edit_invoice(uuid, text, integer, text, integer, camper.edited_invoice_product[]);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:edited_invoice_product from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop type if exists camper.edited_invoice_product;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.invoice;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_amount from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop view if exists camper.invoice_amount;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_number_counter from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.invoice_number_counter;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_product from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.invoice_product;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_product_amount from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop view if exists camper.invoice_product_amount;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_product_product from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.invoice_product_product;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_product_tax from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.invoice_product_tax;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_status from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.invoice_status;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_status_i18n from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.invoice_status_i18n;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:invoice_tax_amount from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop view if exists camper.invoice_tax_amount;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:new_invoice_amount from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop type if exists camper.new_invoice_amount;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:new_invoice_product from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop type if exists camper.new_invoice_product;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:next_invoice_number from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.next_invoice_number(integer, date);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:payment_method from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.payment_method;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:product from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.product;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:product_tax from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.product_tax;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,8 @@
|
|||
-- Revert camper:tax from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop policy if exists company_policy on camper.tax;
|
||||
drop table if exists camper.tax;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:tax_class from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.tax_class;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:tax_rate from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop domain if exists camper.tax_rate;
|
||||
|
||||
commit;
|
30
sqitch.plan
30
sqitch.plan
|
@ -299,3 +299,33 @@ available_id_document_types [id_document_type id_document_type_i18n] 2024-04-25T
|
|||
booking_guest [roles schema_camper booking sex id_document_type extension_pg_libphonenumber] 2024-04-26T09:40:17Z jordi fita mas <jordi@tandem.blog> # Add relation of booking guests
|
||||
checked_in_guest [schema_camper] 2024-04-26T09:58:54Z jordi fita mas <jordi@tandem.blog> # Add type for checked-in guest
|
||||
check_in_guests [roles schema_camper booking booking_guest checked_in_guest extension_pg_libphonenumber] 2024-04-26T10:31:53Z jordi fita mas <jordi@tandem.blog> # Add function to check-in guests
|
||||
contact [roles schema_camper user_profile company id_document_type country_code country] 2024-04-27T22:48:18Z jordi fita mas <jordi@tandem.blog> # Add the relation for contacts
|
||||
contact_phone [roles schema_camper extension_pg_libphonenumber] 2024-04-27T23:18:19Z jordi fita mas <jordi@tandem.blog> # Add relation to keep contacts’ phone numbers
|
||||
contact_email [roles schema_camper email contact] 2024-04-27T23:18:19Z jordi fita mas <jordi@tandem.blog> # Add relation to keep contacts’ emails
|
||||
invoice_status [schema_camper] 2024-04-27T22:48:26Z jordi fita mas <jordi@tandem.blog> # A relation of invoice status
|
||||
invoice_status_i18n [schema_camper invoice_status language] 2024-04-27T22:48:18Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice status’ translatable texts
|
||||
available_invoice_status [schema_camper invoice_status invoice_status_i18n] 2024-04-27T22:48:06Z jordi fita mas <jordi@tandem.blog> # Add the list of available invoice status
|
||||
payment_method [roles schema_camper user_profile company] 2024-04-27T23:49:41Z jordi fita mas <jordi@tandem.blog> # Add relation of payment method
|
||||
invoice [roles schema_camper user_profile company contact invoice_status payment_method currency] 2024-04-27T22:46:21Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice
|
||||
discount_rate [roles schema_camper] 2024-04-27T23:54:40Z jordi fita mas <jordi@tandem.blog> # Add domain for discount rates
|
||||
invoice_product [roles schema_camper invoice discount_rate] 2024-04-27T23:54:08Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice product
|
||||
tax_class [roles schema_camper user_profile company] 2024-04-27T23:57:14Z jordi fita mas <jordi@tandem.blog> # Add the relation for tax classes
|
||||
tax_rate [roles schema_camper] 2024-04-27T23:57:39Z jordi fita mas <jordi@tandem.blog> # Add domain for tax rates
|
||||
tax [roles schema_camper user_profile company tax_rate tax_class] 2024-04-27T23:57:47Z jordi fita mas <jordi@tandem.blog> # Add relation for taxes
|
||||
product [roles schema_camper user_profile company] 2024-04-28T00:44:24Z jordi fita mas <jordi@tandem.blog> # Add relation for products
|
||||
product_tax [roles schema_camper product tax] 2024-04-28T00:44:49Z jordi fita mas <jordi@tandem.blog> # Add relation of product taxes
|
||||
invoice_product_product [roles schema_camper invoice_product product] 2024-04-28T00:43:30Z jordi fita mas <jordi@tandem.blog> # Add relation of invoice products and registered products
|
||||
invoice_product_tax [roles schema_camper invoice_product tax tax_rate] 2024-04-27T23:54:30Z jordi fita mas <jordi@tandem.blog> # Add relation for taxes in invoice products
|
||||
new_invoice_product [roles schema_camper discount_rate] 2024-04-27T23:54:01Z jordi fita mas <jordi@tandem.blog> # Add type for passing products to new invoices
|
||||
invoice_number_counter [roles schema_camper company] 2024-04-27T23:54:48Z jordi fita mas <jordi@tandem.blog> # Add relation to count invoice numbers
|
||||
next_invoice_number [roles schema_camper invoice_number_counter] 2024-04-27T23:54:48Z jordi fita mas <jordi@tandem.blog> # Add function to retrieve the next invoice number
|
||||
add_invoice [roles schema_camper invoice company currency parse_price new_invoice_product tax invoice_product invoice_product_product invoice_product_tax next_invoice_number] 2024-04-27T23:54:46Z jordi fita mas <jordi@tandem.blog> # Add function to create new invoices
|
||||
invoice_tax_amount [roles schema_camper invoice_product invoice_product_tax] 2024-04-27T23:54:35Z jordi fita mas <jordi@tandem.blog> # Add view for invoice tax amount
|
||||
invoice_product_amount [roles schema_camper invoice_product invoice_product_tax] 2024-04-27T23:54:05Z jordi fita mas <jordi@tandem.blog> # Add view for invoice product subtotal and total
|
||||
invoice_amount [roles schema_camper invoice_product invoice_product_amount] 2024-04-27T23:54:46Z jordi fita mas <jordi@tandem.blog> # Add view to compute subtotal and total for invoices
|
||||
new_invoice_amount [roles schema_camper] 2024-04-27T23:54:25Z jordi fita mas <jordi@tandem.blog> # Add type to return when computing new invoice amounts
|
||||
compute_new_invoice_amount [roles schema_camper company currency tax new_invoice_product new_invoice_amount] 2024-04-27T23:54:13Z jordi fita mas <jordi@tandem.blog> # Add function to compute the subtotal, taxes, and total amounts for a new invoice
|
||||
edited_invoice_product [roles schema_camper discount_rate] 2024-04-27T23:54:24Z jordi fita mas <jordi@tandem.blog> # Add typo for passing products to edited invoices
|
||||
edit_invoice [roles schema_camper invoice currency parse_price edited_invoice_product tax invoice_product invoice_product_product invoice_product_tax] 2024-04-27T23:54:50Z jordi fita mas <jordi@tandem.blog> # Add function to edit invoices
|
||||
add_contact [roles schema_camper email extension_pg_libphonenumber country_code contact contact_email contact_phone] 2024-04-28T14:21:37Z jordi fita mas <jordi@tandem.blog> # Add function to create new contacts
|
||||
edit_contact [roles schema_camper email country_code contact extension_pg_libphonenumber contact_email contact_phone] 2024-04-28T14:21:27Z jordi fita mas <jordi@tandem.blog> # Add function to edit contacts
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
-- Test add_contact
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(14);
|
||||
|
||||
set search_path to auth, camper, public;
|
||||
|
||||
select has_function('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code']);
|
||||
select function_lang_is('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'plpgsql');
|
||||
select function_returns('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'uuid');
|
||||
select isnt_definer('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code']);
|
||||
select volatility_is('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'volatile');
|
||||
select function_privs_are('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'guest', array []::text[]);
|
||||
select function_privs_are('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'employee', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'add_contact', array ['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate contact_email cascade;
|
||||
truncate contact_phone cascade;
|
||||
truncate contact cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_contact(1, 'Contact 2.2', 'D', '41414141A', '977 977 977', '', 'Fake St., 123', 'Fake City', 'Fake Province', '17400', 'ES') $$,
|
||||
'Should be able to insert a second contact for the first company with no email but with phone'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_contact(2, 'Contact 4.1', 'C', '123ABC', '', 'e@e', 'Bullshit Av., 1', 'Another City', 'Another Province', 'ARBNNL22', 'FR') $$,
|
||||
'Should be able to insert a contact for the second company with no phone but with email'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code, created_at from contact $$,
|
||||
$$ values (1, 'Contact 2.2', 'D', '41414141A', 'Fake St., 123', 'Fake City', 'Fake Province', '17400', 'ES', CURRENT_TIMESTAMP)
|
||||
, (2, 'Contact 4.1', 'C', '123ABC', 'Bullshit Av., 1', 'Another City', 'Another Province', 'ARBNNL22', 'FR', CURRENT_TIMESTAMP)
|
||||
$$,
|
||||
'Should have created all contacts'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, phone::text from contact join contact_phone using (contact_id) $$,
|
||||
$$ values ('Contact 2.2', '+34 977 97 79 77')
|
||||
$$,
|
||||
'Should have created all contacts’ phone'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select name, email::text from contact join contact_email using (contact_id) $$,
|
||||
$$ values ('Contact 4.1', 'e@e')
|
||||
$$,
|
||||
'Should have created all contacts’ email'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,138 @@
|
|||
-- Test add_invoice
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(16);
|
||||
|
||||
set search_path to auth, camper, public;
|
||||
|
||||
select has_function('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]']);
|
||||
select function_lang_is('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]'], 'plpgsql');
|
||||
select function_returns('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]'], 'uuid');
|
||||
select isnt_definer('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]']);
|
||||
select volatility_is('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]'], 'volatile');
|
||||
select function_privs_are('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]'], 'employee', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'new_invoice_product[]'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice_number_counter cascade;
|
||||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact cascade;
|
||||
truncate product cascade;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag, invoice_number_format)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca', '"F"YYYY0000')
|
||||
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca', '"INV"000-YY')
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
insert into invoice_number_counter (company_id, year, currval)
|
||||
values (1, 2023, '5')
|
||||
, (2, 2023, '55')
|
||||
;
|
||||
|
||||
insert into tax_class (tax_class_id, company_id, name)
|
||||
values (11, 1, 'tax')
|
||||
, (22, 2, 'tax')
|
||||
;
|
||||
|
||||
insert into tax (tax_id, company_id, tax_class_id, name, rate)
|
||||
values (3, 1, 11, 'IRPF -15 %', -0.15)
|
||||
, (4, 1, 11, 'IVA 21 %', 0.21)
|
||||
, (5, 2, 22, 'IRPF -7 %', -0.07)
|
||||
, (6, 2, 22, 'IVA 10 %', 0.10)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (14, 2, 'Contact 4.1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (15, 2, 'Contact 4.2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
insert into product (product_id, company_id, name, price)
|
||||
values ( 7, 1, 'Product 2.1', 1212)
|
||||
, ( 8, 1, 'Product 2.2', 2424)
|
||||
, ( 9, 2, 'Product 4.1', 4848)
|
||||
, (10, 2, 'Product 4.2', 9696)
|
||||
, (11, 2, 'Product 4.3', 1010)
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_invoice(1, '2023-02-15', 12, 'Notes 1', 111, '{"(7,Product 1,Description 1,12.24,2,0.0,{4})"}') $$,
|
||||
'Should be able to insert an invoice for the first company with a product'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_invoice(1, '2023-02-16', 13, 'Notes 2', 111, '{"(7,Product 1 bis,Description 1 bis,33.33,1,0.50,\"{4,3}\")","(8,Product 2,Description 2,24.00,3,0.75,{})"}') $$,
|
||||
'Should be able to insert a second invoice for the first company with two product'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_invoice(2, '2023-02-14', 15, 'Notes 3', 222, '{"(11,Product 4.3,,11.11,1,0.0,{6})","(,Product 4.4,Description 4.4,22.22,3,0.05,{})"}') $$,
|
||||
'Should be able to insert an invoice for the second company with a product'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id, currency_code, created_at from invoice $$,
|
||||
$$ values (1, 'F20230006', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', current_timestamp)
|
||||
, (1, 'F20230007', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', current_timestamp)
|
||||
, (2, 'INV056-23', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', current_timestamp)
|
||||
$$,
|
||||
'Should have created all invoices'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_number, name, description, price, quantity, discount_rate from invoice_product join invoice using (invoice_id) $$,
|
||||
$$ values ('F20230006', 'Product 1', 'Description 1', 1224, 2, 0.00)
|
||||
, ('F20230007', 'Product 1 bis', 'Description 1 bis', 3333, 1, 0.50)
|
||||
, ('F20230007', 'Product 2', 'Description 2', 2400, 3, 0.75)
|
||||
, ('INV056-23', 'Product 4.3', '', 1111, 1, 0.0)
|
||||
, ('INV056-23', 'Product 4.4', 'Description 4.4', 2222, 3, 0.05)
|
||||
$$,
|
||||
'Should have created all invoice products'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_number, product_id, name from invoice_product left join invoice_product_product using (invoice_product_id) join invoice using (invoice_id) $$,
|
||||
$$ values ('F20230006', 7, 'Product 1')
|
||||
, ('F20230007', 7, 'Product 1 bis')
|
||||
, ('F20230007', 8, 'Product 2')
|
||||
, ('INV056-23', 11, 'Product 4.3')
|
||||
, ('INV056-23', NULL, 'Product 4.4')
|
||||
$$,
|
||||
'Should have linked all invoice products'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_number, name, tax_id, tax_rate from invoice_product_tax join invoice_product using (invoice_product_id) join invoice using (invoice_id) $$,
|
||||
$$ values ('F20230006', 'Product 1', 4, 0.21)
|
||||
, ('F20230007', 'Product 1 bis', 4, 0.21)
|
||||
, ('F20230007', 'Product 1 bis', 3, -0.15)
|
||||
, ('INV056-23', 'Product 4.3', 6, 0.10)
|
||||
$$,
|
||||
'Should have created all invoice product taxes'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,71 @@
|
|||
-- Test compute_new_invoice_amount
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(14);
|
||||
|
||||
set search_path to camper, auth, public;
|
||||
|
||||
select has_function('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]']);
|
||||
select function_lang_is('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]'], 'plpgsql');
|
||||
select function_returns('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]'], 'new_invoice_amount');
|
||||
select isnt_definer('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]']);
|
||||
select volatility_is('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]'], 'stable');
|
||||
select function_privs_are('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]'], 'employee', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'compute_new_invoice_amount', array ['integer', 'new_invoice_product[]'], 'authenticator', array []::text[]);
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into tax_class (tax_class_id, company_id, name)
|
||||
values (11, 1, 'tax')
|
||||
;
|
||||
|
||||
insert into tax (tax_id, company_id, tax_class_id, name, rate)
|
||||
values (2, 1, 11, 'IRPF -15 %', -0.15)
|
||||
, (3, 1, 11, 'IVA 4 %', 0.04)
|
||||
, (4, 1, 11, 'IVA 10 %', 0.10)
|
||||
, (5, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
select is(
|
||||
compute_new_invoice_amount(1, '{}'),
|
||||
'(0.00,"{}",0.00)'::new_invoice_amount
|
||||
);
|
||||
|
||||
select is(
|
||||
compute_new_invoice_amount(1, '{"(6,P,D,1.00,1,0.0,\"{2,5}\")","(6,P,D,2.00,2,0.1,{3})"}'),
|
||||
'(4.60,"{{IRPF -15 %,-0.15},{IVA 4 %,0.14},{IVA 21 %,0.21}}",4.80)'::new_invoice_amount
|
||||
);
|
||||
|
||||
select is(
|
||||
compute_new_invoice_amount(1, '{"(6,P,D,2.22,3,0.0,\"{2,4,5}\")","(6,P,D,3.33,4,0.2,{4})"}'),
|
||||
'(17.32,"{{IRPF -15 %,-1.00},{IVA 10 %,1.74},{IVA 21 %,1.40}}",19.46)'::new_invoice_amount
|
||||
);
|
||||
|
||||
select is(
|
||||
compute_new_invoice_amount(1, '{"(6,P,D,4.44,5,0.0,\"{4,5}\")","(6,P,D,5.55,6,0.1,\"{5,3}\")"}'),
|
||||
'(52.17,"{{IVA 4 %,1.20},{IVA 10 %,2.22},{IVA 21 %,10.95}}",66.54)'::new_invoice_amount
|
||||
);
|
||||
|
||||
select is(
|
||||
compute_new_invoice_amount(1, '{"(6,P,D,7.77,8,0.0,\"{}\")"}'),
|
||||
'(62.16,"{}",62.16)'::new_invoice_amount
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,170 @@
|
|||
-- Test contact
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(68);
|
||||
|
||||
set search_path to camper, auth, public;
|
||||
|
||||
select has_table('contact');
|
||||
select has_pk('contact' );
|
||||
select table_privs_are('contact', 'guest', array []::text[]);
|
||||
select table_privs_are('contact', 'employee', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('contact', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('contact', 'contact_id');
|
||||
select col_is_pk('contact', 'contact_id');
|
||||
select col_type_is('contact', 'contact_id', 'integer');
|
||||
select col_not_null('contact', 'contact_id');
|
||||
select col_hasnt_default('contact', 'contact_id');
|
||||
|
||||
select has_column('contact', 'company_id');
|
||||
select col_is_fk('contact', 'company_id');
|
||||
select fk_ok('contact', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('contact', 'company_id', 'integer');
|
||||
select col_not_null('contact', 'company_id');
|
||||
select col_hasnt_default('contact', 'company_id');
|
||||
|
||||
select has_column('contact', 'slug');
|
||||
select col_is_unique('contact', 'slug');
|
||||
select col_type_is('contact', 'slug', 'uuid');
|
||||
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', 'name');
|
||||
select col_type_is('contact', 'name', 'text');
|
||||
select col_not_null('contact', 'name');
|
||||
select col_hasnt_default('contact', 'name');
|
||||
|
||||
select has_column('contact', 'id_document_type_id');
|
||||
select col_is_fk('contact', 'id_document_type_id');
|
||||
select fk_ok('contact', 'id_document_type_id', 'id_document_type', 'id_document_type_id');
|
||||
select col_type_is('contact', 'id_document_type_id', 'character varying(1)');
|
||||
select col_not_null('contact', 'id_document_type_id');
|
||||
select col_hasnt_default('contact', 'id_document_type_id');
|
||||
|
||||
select has_column('contact', 'id_document_number');
|
||||
select col_type_is('contact', 'id_document_number', 'text');
|
||||
select col_not_null('contact', 'id_document_number');
|
||||
select col_hasnt_default('contact', 'id_document_number');
|
||||
|
||||
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_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', 'created_at');
|
||||
select col_type_is('contact', 'created_at', 'timestamp with time zone');
|
||||
select col_not_null('contact', 'created_at');
|
||||
select col_has_default('contact', 'created_at');
|
||||
select col_default_is('contact', 'created_at', 'CURRENT_TIMESTAMP');
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate contact cascade;
|
||||
truncate company_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into contact (company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (2, 'Contact 1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (4, 'Contact 2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
prepare contact_data as
|
||||
select company_id, name
|
||||
from contact
|
||||
order by company_id, name;
|
||||
|
||||
set role employee;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
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', 'co4');
|
||||
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', 'co2');
|
||||
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 (company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (2, ' ', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
$$,
|
||||
'23514', 'new row for relation "contact" violates check constraint "name_not_empty"',
|
||||
'Should not allow contacts with blank trade name'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
-- 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 camper, 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', 'employee', 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_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (6, 2, 'C1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (8, 4, 'C2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (9, 4, 'C3', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
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 employee;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
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', 'co4');
|
||||
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', 'co4');
|
||||
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,114 @@
|
|||
-- 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 camper, 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', 'employee', 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_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (6, 2, 'C1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (8, 4, 'C2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (9, 4, 'C3', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
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 employee;
|
||||
select is_empty('contact_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
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', 'co4');
|
||||
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', 'co4');
|
||||
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,34 @@
|
|||
-- Test discount_rate
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(7);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_domain('discount_rate');
|
||||
select domain_type_is('discount_rate', 'numeric');
|
||||
|
||||
select lives_ok($$ select 1::discount_rate $$, 'Should be able to cast valid 100 % to discount rate');
|
||||
select lives_ok($$ select 0.21::discount_rate $$, 'Should be able to cast valid positive decimals to discount rate');
|
||||
select lives_ok($$ select 0::discount_rate $$, 'Should be able to cast valid zero to discount rate');
|
||||
|
||||
select throws_ok(
|
||||
$$ SELECT (-0.01)::discount_rate $$,
|
||||
23514, null,
|
||||
'Should reject negative discount rate'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ SELECT 1.01::discount_rate $$,
|
||||
23514, null,
|
||||
'Should not allow past the 100 % discount'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,95 @@
|
|||
-- Test edit_contact
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(15);
|
||||
|
||||
set search_path to auth, camper, public;
|
||||
|
||||
select has_function('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code']);
|
||||
select function_lang_is('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'plpgsql');
|
||||
select function_returns('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'uuid');
|
||||
select isnt_definer('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code']);
|
||||
select volatility_is('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'volatile');
|
||||
select function_privs_are('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'guest', array []::text[]);
|
||||
select function_privs_are('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'employee', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'edit_contact', array ['uuid', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'country_code'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate contact_email cascade;
|
||||
truncate contact_phone cascade;
|
||||
truncate contact cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, slug, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', 'C', 'XX555', '', '', '', '', 'FR')
|
||||
, (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', 'C', 'XX666', '', '', '', '', 'DE')
|
||||
, (14, 1, '12fd031b-8f4d-4ac1-9dde-7df336dc6d52', 'Contact 3', 'C', 'XX777', '', '', '', '', 'JP')
|
||||
;
|
||||
|
||||
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')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_contact('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 2.1', 'D', '40404040D', '999-999-999', 'c1@c1', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES') $$,
|
||||
'Should be able to edit the first contact'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_contact('b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2.2', 'C', 'XXX666', '', '', '', '', '', '', 'GB') $$,
|
||||
'Should be able to edit the second contact'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_contact('12fd031b-8f4d-4ac1-9dde-7df336dc6d52', 'Contact 2.3', 'D', '41414141L', '111-111-111', 'd2@d2', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES') $$,
|
||||
'Should be able to edit the third contact'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code from contact $$,
|
||||
$$ values (1, 'Contact 2.1', 'D', '40404040D', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES')
|
||||
, (1, 'Contact 2.2', 'C', 'XXX666', '', '', '', '', 'GB')
|
||||
, (1, 'Contact 2.3', 'D', '41414141L', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES')
|
||||
$$,
|
||||
'Should have updated all contacts'
|
||||
);
|
||||
|
||||
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 *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,149 @@
|
|||
-- Test edit_invoice
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(15);
|
||||
|
||||
set search_path to auth, camper, public;
|
||||
|
||||
select has_function('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]']);
|
||||
select function_lang_is('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]'], 'plpgsql');
|
||||
select function_returns('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]'], 'uuid');
|
||||
select isnt_definer('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]']);
|
||||
select volatility_is('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]'], 'volatile');
|
||||
select function_privs_are('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]'], 'employee', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('camper', 'edit_invoice', array ['uuid', 'text', 'integer', 'text', 'integer', 'edited_invoice_product[]'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact cascade;
|
||||
truncate product cascade;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
, (112, 1, 'bank', 'send money to my bank account')
|
||||
;
|
||||
|
||||
insert into tax_class (tax_class_id, company_id, name)
|
||||
values (11, 1, 'tax')
|
||||
;
|
||||
|
||||
insert into tax (tax_id, company_id, tax_class_id, name, rate)
|
||||
values (3, 1, 11, 'IRPF -15 %', -0.15)
|
||||
, (4, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into product (product_id, company_id, name, price)
|
||||
values ( 7, 1, 'Product 1.1', 1212)
|
||||
, ( 8, 1, 'Product 2.2', 2424)
|
||||
, ( 9, 1, 'Product 3.3', 3636)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (12, 1, 'Contact 2.1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (13, 1, 'Contact 2.2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code)
|
||||
values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12, 111, 'EUR')
|
||||
, (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR')
|
||||
;
|
||||
|
||||
insert into invoice_product (invoice_product_id, invoice_id, name, price)
|
||||
values (19, 15, 'P1.0', 1100)
|
||||
, (20, 15, 'P2.0', 2200)
|
||||
, (21, 16, 'P1.1', 1111)
|
||||
, (22, 16, 'P2.1', 2211)
|
||||
;
|
||||
|
||||
insert into invoice_product_product (invoice_product_id, product_id)
|
||||
values (19, 7)
|
||||
, (20, 8)
|
||||
, (21, 7)
|
||||
, (22, 8)
|
||||
;
|
||||
|
||||
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
|
||||
values (19, 4, 0.21)
|
||||
, (20, 4, 0.21)
|
||||
, (21, 3, -0.07)
|
||||
, (21, 4, 0.21)
|
||||
, (22, 3, -0.15)
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_invoice('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'paid', 13, 'Notes 1', 112, '{"(20,,p1.0,D1,11.01,2,0.50,{4})","(,,p1.3,D3,33.33,3,0.05,{3})"}') $$,
|
||||
'Should be able to edit the first invoice'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_invoice('b57b980b-247b-4be4-a0b7-03a7819c53ae', 'sent', 12, 'Notes 2', 111, '{"(21,7,P1.1,,11.11,1,0.0,{3})","(22,8,p2.1,D2,24.00,3,0.75,\"{3,4}\")","(,9,p3.3,,36.36,2,0.05,{4})"}') $$,
|
||||
'Should be able to edit the second invoice'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id from invoice $$,
|
||||
$$ values ('INV1', '2023-03-10'::date, 13, 'paid', 'Notes 1', 112)
|
||||
, ('INV2', '2023-03-09'::date, 12, 'sent', 'Notes 2', 111)
|
||||
$$,
|
||||
'Should have updated all invoices'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_number, name, description, price, quantity, discount_rate from invoice_product join invoice using (invoice_id) $$,
|
||||
$$ values ('INV1', 'p1.0', 'D1', 1101, 2, 0.50)
|
||||
, ('INV1', 'p1.3', 'D3', 3333, 3, 0.05)
|
||||
, ('INV2', 'P1.1', '', 1111, 1, 0.00)
|
||||
, ('INV2', 'p2.1', 'D2', 2400, 3, 0.75)
|
||||
, ('INV2', 'p3.3', '', 3636, 2, 0.05)
|
||||
$$,
|
||||
'Should have updated all existing invoice products, added new ones, and removed the ones not give to the function'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_number, product_id, name from invoice_product left join invoice_product_product using (invoice_product_id) join invoice using (invoice_id) $$,
|
||||
$$ values ('INV1', NULL, 'p1.0')
|
||||
, ('INV1', NULL, 'p1.3')
|
||||
, ('INV2', 7, 'P1.1')
|
||||
, ('INV2', 8, 'p2.1')
|
||||
, ('INV2', 9, 'p3.3')
|
||||
$$,
|
||||
'Should have updated all existing invoice products id, added new ones, and removed the ones not give to the function'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_number, name, tax_id, tax_rate from invoice_product_tax join invoice_product using (invoice_product_id) join invoice using (invoice_id) $$,
|
||||
$$ values ('INV1', 'p1.0', 4, 0.21)
|
||||
, ('INV1', 'p1.3', 3, -0.15)
|
||||
, ('INV2', 'P1.1', 3, -0.15)
|
||||
, ('INV2', 'p2.1', 3, -0.15)
|
||||
, ('INV2', 'p2.1', 4, 0.21)
|
||||
, ('INV2', 'p3.3', 4, 0.21)
|
||||
$$,
|
||||
'Should have updated all invoice product taxes, added new ones, and removed the ones not given to the function'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,27 @@
|
|||
-- Test edited_invoice_product
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(10);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_composite('camper', 'edited_invoice_product', 'Composite type camper.edited_invoice_product should exist');
|
||||
select columns_are('camper', 'edited_invoice_product', array['invoice_product_id', 'product_id', 'name', 'description', 'price', 'quantity', 'discount_rate', 'tax']);
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'invoice_product_id'::name, 'integer');
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'product_id'::name, 'integer');
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'name'::name, 'text');
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'description'::name, 'text');
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'price'::name, 'text');
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'quantity'::name, 'integer');
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'discount_rate'::name, 'discount_rate');
|
||||
select col_type_is('camper'::name, 'edited_invoice_product'::name, 'tax'::name, 'integer[]');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,188 @@
|
|||
-- Test invoice
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(72);
|
||||
|
||||
set search_path to camper, auth, public;
|
||||
|
||||
select has_table('invoice');
|
||||
select has_pk('invoice' );
|
||||
select table_privs_are('invoice', 'guest', array []::text[]);
|
||||
select table_privs_are('invoice', 'employee', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('invoice', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('invoice', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('invoice', 'invoice_id');
|
||||
select col_is_pk('invoice', 'invoice_id');
|
||||
select col_type_is('invoice', 'invoice_id', 'integer');
|
||||
select col_not_null('invoice', 'invoice_id');
|
||||
select col_hasnt_default('invoice', 'invoice_id');
|
||||
|
||||
select has_column('invoice', 'company_id');
|
||||
select col_is_fk('invoice', 'company_id');
|
||||
select fk_ok('invoice', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('invoice', 'company_id', 'integer');
|
||||
select col_not_null('invoice', 'company_id');
|
||||
select col_hasnt_default('invoice', 'company_id');
|
||||
|
||||
select has_column('invoice', 'slug');
|
||||
select col_is_unique('invoice', 'slug');
|
||||
select col_type_is('invoice', 'slug', 'uuid');
|
||||
select col_not_null('invoice', 'slug');
|
||||
select col_has_default('invoice', 'slug');
|
||||
select col_default_is('invoice', 'slug', 'gen_random_uuid()');
|
||||
|
||||
select has_column('invoice', 'invoice_number');
|
||||
select col_type_is('invoice', 'invoice_number', 'text');
|
||||
select col_not_null('invoice', 'invoice_number');
|
||||
select col_hasnt_default('invoice', 'invoice_number');
|
||||
|
||||
select has_column('invoice', 'invoice_date');
|
||||
select col_type_is('invoice', 'invoice_date', 'date');
|
||||
select col_not_null('invoice', 'invoice_date');
|
||||
select col_has_default('invoice', 'invoice_date');
|
||||
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 col_type_is('invoice', 'contact_id', 'integer');
|
||||
select col_not_null('invoice', 'contact_id');
|
||||
select col_hasnt_default('invoice', 'contact_id');
|
||||
|
||||
select has_column('invoice', 'invoice_status');
|
||||
select col_is_fk('invoice', 'invoice_status');
|
||||
select fk_ok('invoice', 'invoice_status', 'invoice_status', 'invoice_status');
|
||||
select col_type_is('invoice', 'invoice_status', 'text');
|
||||
select col_not_null('invoice', 'invoice_status');
|
||||
select col_has_default('invoice', 'invoice_status');
|
||||
select col_default_is('invoice', 'invoice_status', 'created');
|
||||
|
||||
select has_column('invoice', 'notes');
|
||||
select col_type_is('invoice', 'notes', 'text');
|
||||
select col_not_null('invoice', 'notes');
|
||||
select col_has_default('invoice', 'notes');
|
||||
select col_default_is('invoice', 'notes', '');
|
||||
|
||||
select has_column('invoice', 'payment_method_id');
|
||||
select col_is_fk('invoice', 'payment_method_id');
|
||||
select fk_ok('invoice', 'payment_method_id', 'payment_method', 'payment_method_id');
|
||||
select col_type_is('invoice', 'payment_method_id', 'integer');
|
||||
select col_not_null('invoice', 'payment_method_id');
|
||||
select col_hasnt_default('invoice', 'payment_method_id');
|
||||
|
||||
select has_column('invoice', 'currency_code');
|
||||
select col_is_fk('invoice', 'currency_code');
|
||||
select fk_ok('invoice', 'currency_code', 'currency', 'currency_code');
|
||||
select col_type_is('invoice', 'currency_code', 'text');
|
||||
select col_not_null('invoice', 'currency_code');
|
||||
select col_hasnt_default('invoice', 'currency_code');
|
||||
|
||||
select has_column('invoice', 'created_at');
|
||||
select col_type_is('invoice', 'created_at', 'timestamp with time zone');
|
||||
select col_not_null('invoice', 'created_at');
|
||||
select col_has_default('invoice', 'created_at');
|
||||
select col_default_is('invoice', 'created_at', 'CURRENT_TIMESTAMP');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice cascade;
|
||||
truncate contact cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (6, 2, 'Contact 1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (8, 4, 'Contact 2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (company_id, invoice_number, contact_id, currency_code, payment_method_id)
|
||||
values (2, 'INV020001', 6, 'EUR', 222)
|
||||
, (4, 'INV040001', 8, 'EUR', 444)
|
||||
;
|
||||
|
||||
|
||||
prepare invoice_data as
|
||||
select company_id, invoice_number
|
||||
from invoice
|
||||
order by company_id, invoice_number;
|
||||
|
||||
set role employee;
|
||||
select is_empty('invoice_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
select bag_eq(
|
||||
'invoice_data',
|
||||
$$ values (2, 'INV020001')
|
||||
$$,
|
||||
'Should only list invoices of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4');
|
||||
select bag_eq(
|
||||
'invoice_data',
|
||||
$$ values (4, 'INV040001')
|
||||
$$,
|
||||
'Should only list invoices of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie', 'co4');
|
||||
select throws_ok(
|
||||
'invoice_data',
|
||||
'42501', 'permission denied for table invoice',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select throws_ok( $$
|
||||
insert into invoice (company_id, invoice_number, contact_id, currency_code, payment_method_id)
|
||||
values (2, ' ', 6, 'EUR', 222)
|
||||
$$,
|
||||
'23514', 'new row for relation "invoice" violates check constraint "invoice_number_not_empty"',
|
||||
'Should not allow invoice with blank number'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
-- Test invoice_amount
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to camper, auth, public;
|
||||
|
||||
select has_view('invoice_amount');
|
||||
select table_privs_are('invoice_amount', 'guest', array[]::text[]);
|
||||
select table_privs_are('invoice_amount', 'employee', array['SELECT']);
|
||||
select table_privs_are('invoice_amount', 'admin', array['SELECT']);
|
||||
select table_privs_are('invoice_amount', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('invoice_amount', 'invoice_id');
|
||||
select col_type_is('invoice_amount', 'invoice_id', 'integer');
|
||||
|
||||
select has_column('invoice_amount', 'subtotal');
|
||||
select col_type_is('invoice_amount', 'subtotal', 'integer');
|
||||
|
||||
select has_column('invoice_amount', 'total');
|
||||
select col_type_is('invoice_amount', 'total', 'integer');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice_product_tax cascade;
|
||||
truncate invoice_product cascade;
|
||||
truncate invoice cascade;
|
||||
truncate contact cascade;
|
||||
truncate tax cascade;
|
||||
truncate tax_class cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 1', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
;
|
||||
|
||||
insert into tax_class (tax_class_id, company_id, name)
|
||||
values (11, 1, 'tax')
|
||||
;
|
||||
|
||||
insert into tax (tax_id, company_id, tax_class_id, name, rate)
|
||||
values (2, 1, 11, 'IRPF -15 %', -0.15)
|
||||
, (3, 1, 11, 'IVA 4 %', 0.04)
|
||||
, (4, 1, 11, 'IVA 10 %', 0.10)
|
||||
, (5, 1, 11, 'IVA 21 %', 0.21)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (7, 1, 'Contact', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, invoice_date, contact_id, currency_code, payment_method_id)
|
||||
values ( 8, 1, 'I1', current_date, 7, 'EUR', 111)
|
||||
, ( 9, 1, 'I2', current_date, 7, 'EUR', 111)
|
||||
, (10, 1, 'I3', current_date, 7, 'EUR', 111)
|
||||
, (11, 1, 'I4', current_date, 7, 'EUR', 111)
|
||||
;
|
||||
|
||||
insert into invoice_product (invoice_product_id, invoice_id, name, price, quantity, discount_rate)
|
||||
values (12, 8, 'P', 100, 1, 0.0)
|
||||
, (13, 8, 'P', 200, 2, 0.1)
|
||||
, (14, 9, 'P', 222, 3, 0.0)
|
||||
, (15, 9, 'P', 333, 4, 0.2)
|
||||
, (16, 10, 'P', 444, 5, 0.0)
|
||||
, (17, 10, 'P', 555, 6, 0.1)
|
||||
, (18, 11, 'P', 777, 8, 0.0)
|
||||
;
|
||||
|
||||
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
|
||||
values (12, 2, -0.15)
|
||||
, (12, 5, 0.21)
|
||||
, (13, 3, 0.04)
|
||||
, (14, 4, 0.10)
|
||||
, (14, 5, 0.21)
|
||||
, (14, 2, -0.07)
|
||||
, (15, 4, 0.10)
|
||||
, (16, 4, 0.10)
|
||||
, (16, 5, 0.21)
|
||||
, (17, 5, 0.21)
|
||||
, (17, 3, 0.04)
|
||||
;
|
||||
|
||||
select bag_eq(
|
||||
$$ select invoice_id, subtotal, total from invoice_amount $$,
|
||||
$$ values ( 8, 460, 480)
|
||||
, ( 9, 1732, 1999)
|
||||
, (10, 5217, 6654)
|
||||
, (11, 6216, 6216)
|
||||
$$,
|
||||
'Should compute the amount for all taxes in the invoiced products.'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,141 @@
|
|||
-- Test invoice_number_counter
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(28);
|
||||
|
||||
set search_path to camper, auth, public;
|
||||
|
||||
select has_table('invoice_number_counter');
|
||||
select has_pk('invoice_number_counter' );
|
||||
select col_is_pk('invoice_number_counter', array['company_id', 'year']);
|
||||
|
||||
select table_privs_are('invoice_number_counter', 'guest', array []::text[]);
|
||||
select table_privs_are('invoice_number_counter', 'employee', array ['SELECT', 'INSERT', 'UPDATE']);
|
||||
select table_privs_are('invoice_number_counter', 'admin', array ['SELECT', 'INSERT', 'UPDATE']);
|
||||
select table_privs_are('invoice_number_counter', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('invoice_number_counter', 'company_id');
|
||||
select col_is_fk('invoice_number_counter', 'company_id');
|
||||
select fk_ok('invoice_number_counter', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('invoice_number_counter', 'company_id', 'integer');
|
||||
select col_not_null('invoice_number_counter', 'company_id');
|
||||
select col_hasnt_default('invoice_number_counter', 'company_id');
|
||||
|
||||
select has_column('invoice_number_counter', 'year');
|
||||
select col_type_is('invoice_number_counter', 'year', 'integer');
|
||||
select col_not_null('invoice_number_counter', 'year');
|
||||
select col_hasnt_default('invoice_number_counter', 'year');
|
||||
|
||||
select has_column('invoice_number_counter', 'currval');
|
||||
select col_type_is('invoice_number_counter', 'currval', 'integer');
|
||||
select col_not_null('invoice_number_counter', 'currval');
|
||||
select col_hasnt_default('invoice_number_counter', 'currval');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate invoice_number_counter cascade;
|
||||
truncate company_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into invoice_number_counter (company_id, year, currval)
|
||||
values (2, 2010, 6)
|
||||
, (2, 2011, 8)
|
||||
, (4, 2010, 8)
|
||||
, (4, 2012, 10)
|
||||
;
|
||||
|
||||
|
||||
prepare counter_data as
|
||||
select company_id, year, currval
|
||||
from invoice_number_counter
|
||||
;
|
||||
|
||||
set role employee;
|
||||
select is_empty('counter_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
select bag_eq(
|
||||
'counter_data',
|
||||
$$ values (2, 2010, 6)
|
||||
, (2, 2011, 8)
|
||||
$$,
|
||||
'Should only list invoices of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4');
|
||||
select bag_eq(
|
||||
'counter_data',
|
||||
$$ values (4, 2010, 8)
|
||||
, (4, 2012, 10)
|
||||
$$,
|
||||
'Should only list invoices of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie', 'co4');
|
||||
select throws_ok(
|
||||
'counter_data',
|
||||
'42501', 'permission denied for table invoice_number_counter',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select lives_ok( $$
|
||||
insert into invoice_number_counter (company_id, year, currval)
|
||||
values (2, 2009, 0)
|
||||
$$,
|
||||
'Should allow starting a counter from zero'
|
||||
);
|
||||
|
||||
select throws_ok( $$
|
||||
insert into invoice_number_counter (company_id, year, currval)
|
||||
values (2, 2008, -1)
|
||||
$$,
|
||||
'23514', 'new row for relation "invoice_number_counter" violates check constraint "counter_zero_or_positive"',
|
||||
'Should not allow starting a counter from a negative value'
|
||||
);
|
||||
|
||||
select throws_ok( $$
|
||||
insert into invoice_number_counter (company_id, year, currval)
|
||||
values (2, -2008, 1)
|
||||
$$,
|
||||
'23514', 'new row for relation "invoice_number_counter" violates check constraint "year_always_positive"',
|
||||
'Should not allow counters for invoices issued before Jesus Christ was born'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
-- Test invoice_product
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(45);
|
||||
|
||||
set search_path to camper, auth, public;
|
||||
|
||||
select has_table('invoice_product');
|
||||
select has_pk('invoice_product' );
|
||||
select table_privs_are('invoice_product', 'guest', array []::text[]);
|
||||
select table_privs_are('invoice_product', 'employee', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('invoice_product', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('invoice_product', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('invoice_product', 'invoice_product_id');
|
||||
select col_is_pk('invoice_product', 'invoice_product_id');
|
||||
select col_type_is('invoice_product', 'invoice_product_id', 'integer');
|
||||
select col_not_null('invoice_product', 'invoice_product_id');
|
||||
select col_hasnt_default('invoice_product', 'invoice_product_id');
|
||||
|
||||
select has_column('invoice_product', 'invoice_id');
|
||||
select col_is_fk('invoice_product', 'invoice_id');
|
||||
select fk_ok('invoice_product', 'invoice_id', 'invoice', 'invoice_id');
|
||||
select col_type_is('invoice_product', 'invoice_id', 'integer');
|
||||
select col_not_null('invoice_product', 'invoice_id');
|
||||
select col_hasnt_default('invoice_product', 'invoice_id');
|
||||
|
||||
select has_column('invoice_product', 'name');
|
||||
select col_type_is('invoice_product', 'name', 'text');
|
||||
select col_not_null('invoice_product', 'name');
|
||||
select col_hasnt_default('invoice_product', 'name');
|
||||
|
||||
select has_column('invoice_product', 'description');
|
||||
select col_type_is('invoice_product', 'description', 'text');
|
||||
select col_not_null('invoice_product', 'description');
|
||||
select col_has_default('invoice_product', 'description');
|
||||
select col_default_is('invoice_product', 'description', '');
|
||||
|
||||
select has_column('invoice_product', 'price');
|
||||
select col_type_is('invoice_product', 'price', 'integer');
|
||||
select col_not_null('invoice_product', 'price');
|
||||
select col_hasnt_default('invoice_product', 'price');
|
||||
|
||||
select has_column('invoice_product', 'quantity');
|
||||
select col_type_is('invoice_product', 'quantity', 'integer');
|
||||
select col_not_null('invoice_product', 'quantity');
|
||||
select col_has_default('invoice_product', 'quantity');
|
||||
select col_default_is('invoice_product', 'quantity', 1);
|
||||
|
||||
select has_column('invoice_product', 'discount_rate');
|
||||
select col_type_is('invoice_product', 'discount_rate', 'discount_rate');
|
||||
select col_not_null('invoice_product', 'discount_rate');
|
||||
select col_has_default('invoice_product', 'discount_rate');
|
||||
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 cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company_host cascade;
|
||||
truncate company_user cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, tourist_tax, tourist_tax_max_days, country_code, currency_code, default_lang_tag)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 60, 7, 'ES', 'EUR', 'ca')
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 60, 7, 'FR', 'USD', 'ca')
|
||||
;
|
||||
|
||||
insert into company_user (company_id, user_id, role)
|
||||
values (2, 1, 'admin')
|
||||
, (4, 5, 'admin')
|
||||
;
|
||||
|
||||
insert into company_host (company_id, host)
|
||||
values (2, 'co2')
|
||||
, (4, 'co4')
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name, id_document_type_id, id_document_number, address, city, province, postal_code, country_code)
|
||||
values (6, 2, 'Contact 1', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
, (8, 4, 'Contact 2', 'D', '41440443Q', 'Fake St', 'City', 'Province', '17600', 'ES')
|
||||
;
|
||||
|
||||
insert into invoice (invoice_id, company_id, invoice_number, contact_id, currency_code, payment_method_id)
|
||||
values (10, 2, 'INV020001', 6, 'EUR', 222)
|
||||
, (12, 4, 'INV040001', 8, 'EUR', 444)
|
||||
;
|
||||
|
||||
insert into invoice_product (invoice_id, name, description, price, quantity)
|
||||
values (10, 'product 1', 'description 1', 1212, 1)
|
||||
, (12, 'product 2', 'description 2', 2424, 2)
|
||||
;
|
||||
|
||||
|
||||
prepare invoice_product_data as
|
||||
select invoice_id, name, price, quantity
|
||||
from invoice_product
|
||||
order by invoice_id;
|
||||
|
||||
set role employee;
|
||||
select is_empty('invoice_product_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
select bag_eq(
|
||||
'invoice_product_data',
|
||||
$$ values (10, 'product 1', 1212, 1)
|
||||
$$,
|
||||
'Should only list products of invoices of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog', 'co4');
|
||||
select bag_eq(
|
||||
'invoice_product_data',
|
||||
$$ values (12, 'product 2', 2424, 2)
|
||||
$$,
|
||||
'Should only list products of invoices of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie', 'co4');
|
||||
select throws_ok(
|
||||
'invoice_product_data',
|
||||
'42501', 'permission denied for table invoice_product',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select throws_ok( $$
|
||||
insert into invoice_product (invoice_id, name, description, price, quantity)
|
||||
values (10, ' ', '', 1212, 1)
|
||||
$$,
|
||||
'23514', 'new row for relation "invoice_product" violates check constraint "name_not_empty"',
|
||||
'Should not allow invoice products with blank name'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue