Add the payments section
This actually should be the “payments and receivables” section, however this is quite a mouthful; a “receivable” is a payment made **to** you, therefore “payments” is ok. In fact, there is still no receivables in there, as they should be in a separate relation, to constraint them to invoices instead of expenses. It will be done in a separate commit. Since this section will be, in a sense, sort of simplified accounting, i needed to introduce the “payment account” concept. There is no way, yet, for users to add them, because i have to revamp the “tax details” section, but this commit started to grow too big already. The same reasoning for the attachment payment slips as PDF to payment: something i have to add, but not yet in this commit.
This commit is contained in:
parent
f546632a89
commit
ad5bc271b6
|
@ -0,0 +1,67 @@
|
|||
-- Deploy numerus:add_payment to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment
|
||||
-- requires: expense_payment
|
||||
-- requires: company
|
||||
-- requires: currency
|
||||
-- requires: parse_price
|
||||
-- requires: tag_name
|
||||
-- requires: update_expense_payment_status
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_payment(company integer, expense_id integer, payment_date date, payment_account_id integer, description text, amount text, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
pslug uuid;
|
||||
pid integer;
|
||||
amount_cents integer;
|
||||
begin
|
||||
insert into payment
|
||||
( company_id
|
||||
, payment_account_id
|
||||
, description
|
||||
, payment_date
|
||||
, amount
|
||||
, currency_code
|
||||
, payment_status
|
||||
, tags
|
||||
)
|
||||
select company_id
|
||||
, payment_account_id
|
||||
, description
|
||||
, payment_date
|
||||
, parse_price(amount, currency.decimal_digits)
|
||||
, currency_code
|
||||
, 'complete'
|
||||
, tags
|
||||
from company
|
||||
join currency using (currency_code)
|
||||
where company.company_id = add_payment.company
|
||||
returning payment_id, slug, payment.amount
|
||||
into pid, pslug, amount_cents
|
||||
;
|
||||
|
||||
if expense_id is not null then
|
||||
-- must be inserted before updating statuses, so that it can see this
|
||||
-- payment’s amount too.
|
||||
insert into expense_payment (expense_id, payment_id)
|
||||
values (expense_id, pid);
|
||||
|
||||
perform update_expense_payment_status(pid, expense_id, amount_cents);
|
||||
end if;
|
||||
|
||||
return pslug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function add_payment(integer, integer, date, integer, text, text, tag_name[]) from public;
|
||||
grant execute on function add_payment(integer, integer, date, integer, text, text, tag_name[]) to invoicer;
|
||||
grant execute on function add_payment(integer, integer, date, integer, text, text, tag_name[]) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,35 @@
|
|||
-- Deploy numerus:add_payment_account_bank to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
-- requires: payment_account_bank
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_payment_account_bank(company integer, name text, iban iban) returns uuid as
|
||||
$$
|
||||
declare
|
||||
account_id integer;
|
||||
account_slug uuid;
|
||||
begin
|
||||
insert into payment_account (company_id, payment_account_type, name)
|
||||
select company, 'bank', add_payment_account_bank.name
|
||||
returning payment_account_id, slug into account_id, account_slug;
|
||||
|
||||
insert into payment_account_bank (payment_account_id, iban)
|
||||
values (account_id, iban)
|
||||
;
|
||||
|
||||
return account_slug;
|
||||
end;
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function add_payment_account_bank(integer, text, iban) from public;
|
||||
grant execute on function add_payment_account_bank(integer, text, iban) to invoicer;
|
||||
grant execute on function add_payment_account_bank(integer, text, iban) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,34 @@
|
|||
-- Deploy numerus:add_payment_account_card to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
-- requires: payment_account_card
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_payment_account_card(company integer, name text, four_digits text, exp_date date) returns uuid as
|
||||
$$
|
||||
declare
|
||||
account_id integer;
|
||||
account_slug uuid;
|
||||
begin
|
||||
insert into payment_account (company_id, payment_account_type, name)
|
||||
select company, 'card', add_payment_account_card.name
|
||||
returning payment_account_id, slug into account_id, account_slug;
|
||||
|
||||
insert into payment_account_card (payment_account_id, last_four_digits, expiration_date)
|
||||
values (account_id, four_digits, exp_date);
|
||||
|
||||
return account_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function add_payment_account_card(integer, text, text, date) from public;
|
||||
grant execute on function add_payment_account_card(integer, text, text, date) to invoicer;
|
||||
grant execute on function add_payment_account_card(integer, text, text, date) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Deploy numerus:add_payment_account_cash to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_payment_account_cash(company integer, name text) returns uuid as
|
||||
$$
|
||||
insert into payment_account (company_id, payment_account_type, name)
|
||||
values (company, 'cash', name)
|
||||
returning slug;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function add_payment_account_cash(integer, text) from public;
|
||||
grant execute on function add_payment_account_cash(integer, text) to invoicer;
|
||||
grant execute on function add_payment_account_cash(integer, text) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Deploy numerus:add_payment_account_other to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function add_payment_account_other(company integer, name text) returns uuid as
|
||||
$$
|
||||
insert into payment_account (company_id, payment_account_type, name)
|
||||
values (company, 'other', name)
|
||||
returning slug;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function add_payment_account_other(integer, text) from public;
|
||||
grant execute on function add_payment_account_other(integer, text) to invoicer;
|
||||
grant execute on function add_payment_account_other(integer, text) to admin;
|
||||
|
||||
commit;
|
|
@ -9,14 +9,19 @@ set search_path to numerus;
|
|||
|
||||
insert into expense_status (expense_status, name)
|
||||
values ('pending', 'Pending')
|
||||
, ('partial', 'Partial')
|
||||
, ('paid', 'Paid')
|
||||
on conflict (expense_status) do nothing
|
||||
;
|
||||
|
||||
insert into expense_status_i18n (expense_status, lang_tag, name)
|
||||
values ('pending', 'ca', 'Pendent')
|
||||
, ('partial', 'ca', 'Parcial')
|
||||
, ('paid', 'ca', 'Pagada')
|
||||
, ('pending', 'es', 'Pendiente')
|
||||
, ('partial', 'es', 'Parcial')
|
||||
, ('paid', 'es', 'Pagada')
|
||||
on conflict (expense_status, lang_tag) do nothing
|
||||
;
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
-- Deploy numerus:available_expense_status to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: expense_status
|
||||
-- requires: expense_status_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
insert into expense_status (expense_status, name)
|
||||
values ('pending', 'Pending')
|
||||
, ('paid', 'Paid')
|
||||
;
|
||||
|
||||
insert into expense_status_i18n (expense_status, lang_tag, name)
|
||||
values ('pending', 'ca', 'Pendent')
|
||||
, ('paid', 'ca', 'Pagada')
|
||||
, ('pending', 'es', 'Pendiente')
|
||||
, ('paid', 'es', 'Pagada')
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,28 @@
|
|||
-- Deploy numerus:available_payment_account_types to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account_type
|
||||
-- requires: payment_account_type_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
insert into payment_account_type (payment_account_type, name)
|
||||
values ('bank', 'Bank')
|
||||
, ('card', 'Credit Card')
|
||||
, ('cash', 'Cash')
|
||||
, ('other', 'Other')
|
||||
;
|
||||
|
||||
insert into payment_account_type_i18n (payment_account_type, lang_tag, name)
|
||||
values ('bank', 'ca', 'Banc')
|
||||
, ('card', 'ca', 'Targeta de crèdit')
|
||||
, ('cash', 'ca', 'Efectiu')
|
||||
, ('other', 'ca', 'Altres')
|
||||
, ('bank', 'es', 'Banco')
|
||||
, ('card', 'es', 'Tarjeta de crédito')
|
||||
, ('cash', 'es', 'Efectivo')
|
||||
, ('other', 'es', 'Otros')
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,22 @@
|
|||
-- Deploy numerus:available_payment_status to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_status
|
||||
-- requires: payment_status_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
insert into payment_status (payment_status, name)
|
||||
values ('partial', 'Partial')
|
||||
, ('complete', 'Complete')
|
||||
;
|
||||
|
||||
insert into payment_status_i18n (payment_status, lang_tag, name)
|
||||
values ('partial', 'ca', 'Parcial')
|
||||
, ('partial', 'es', 'Parcial')
|
||||
, ('complete', 'ca', 'Complet')
|
||||
, ('complete', 'es', 'Completo')
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,53 @@
|
|||
-- Deploy numerus:edit_payment to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment
|
||||
-- requires: expense_payment
|
||||
-- requires: currency
|
||||
-- requires: parse_price
|
||||
-- requires: tag_name
|
||||
-- requires: update_expense_payment_status
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_payment(payment_slug uuid, payment_date date, payment_account_id integer, description text, amount text, tags tag_name[]) returns uuid as
|
||||
$$
|
||||
declare
|
||||
pid integer;
|
||||
eid integer;
|
||||
amount_cents integer;
|
||||
begin
|
||||
update payment
|
||||
set payment_date = edit_payment.payment_date
|
||||
, payment_account_id = edit_payment.payment_account_id
|
||||
, description = edit_payment.description
|
||||
, amount = parse_price(edit_payment.amount, decimal_digits)
|
||||
, tags = edit_payment.tags
|
||||
from currency
|
||||
where slug = payment_slug
|
||||
and currency.currency_code = payment.currency_code
|
||||
returning payment_id, payment.amount
|
||||
into pid, amount_cents
|
||||
;
|
||||
|
||||
select expense_id into eid
|
||||
from expense_payment
|
||||
where payment_id = pid;
|
||||
|
||||
if eid is not null then
|
||||
perform update_expense_payment_status(pid, eid, amount_cents);
|
||||
end if;
|
||||
|
||||
return payment_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function edit_payment(uuid, date, integer, text, text, tag_name[]) from public;
|
||||
grant execute on function edit_payment(uuid, date, integer, text, text, tag_name[]) to invoicer;
|
||||
grant execute on function edit_payment(uuid, date, integer, text, text, tag_name[]) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,44 @@
|
|||
-- Deploy numerus:edit_payment_account_bank to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
-- requires: payment_account_bank
|
||||
-- requires: extension_pgcrypto
|
||||
-- requires: extension_iban
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_payment_account_bank(account_slug uuid, new_name text, new_iban iban) returns uuid as
|
||||
$$
|
||||
declare
|
||||
account_id int;
|
||||
begin
|
||||
update payment_account
|
||||
set name = new_name
|
||||
where slug = account_slug
|
||||
and payment_account_type = 'bank'
|
||||
returning payment_account_id into account_id
|
||||
;
|
||||
|
||||
if account_id is null then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
update payment_account_bank
|
||||
set iban = new_iban
|
||||
where payment_account_id = account_id
|
||||
;
|
||||
|
||||
return account_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function edit_payment_account_bank(uuid, text, iban) from public;
|
||||
grant execute on function edit_payment_account_bank(uuid, text, iban) to invoicer;
|
||||
grant execute on function edit_payment_account_bank(uuid, text, iban) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,44 @@
|
|||
-- Deploy numerus:edit_payment_account_card to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
-- requires: payment_account_card
|
||||
-- requires: extension_pgcrypto
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_payment_account_card(account_slug uuid, new_name text, new_last_digits text, new_exp_date date) returns uuid as
|
||||
$$
|
||||
declare
|
||||
account_id integer;
|
||||
begin
|
||||
update payment_account
|
||||
set name = new_name
|
||||
where slug = account_slug
|
||||
and payment_account_type = 'card'
|
||||
returning payment_account_id into account_id
|
||||
;
|
||||
|
||||
if account_id is null then
|
||||
return null;
|
||||
end if;
|
||||
|
||||
update payment_account_card
|
||||
set last_four_digits = new_last_digits
|
||||
, expiration_date = new_exp_date
|
||||
where payment_account_id = account_id
|
||||
;
|
||||
|
||||
return account_slug;
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
;
|
||||
|
||||
revoke execute on function edit_payment_account_card(uuid, text, text, date) from public;
|
||||
grant execute on function edit_payment_account_card(uuid, text, text, date) to invoicer;
|
||||
grant execute on function edit_payment_account_card(uuid, text, text, date) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,27 @@
|
|||
-- Deploy numerus:edit_payment_account_cash to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
-- requires: extension_pgcrypto
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_payment_account_cash(account_slug uuid, new_name text) returns uuid as
|
||||
$$
|
||||
update payment_account
|
||||
set name = new_name
|
||||
where slug = account_slug
|
||||
and payment_account_type = 'cash'
|
||||
returning slug
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function edit_payment_account_cash(uuid, text) from public;
|
||||
grant execute on function edit_payment_account_cash(uuid, text) to invoicer;
|
||||
grant execute on function edit_payment_account_cash(uuid, text) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,27 @@
|
|||
-- Deploy numerus:edit_payment_account_other to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
-- requires: extension_pgcrypto
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function edit_payment_account_other(account_slug uuid, new_name text) returns uuid as
|
||||
$$
|
||||
update payment_account
|
||||
set name = new_name
|
||||
where slug = account_slug
|
||||
and payment_account_type = 'other'
|
||||
returning slug
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function edit_payment_account_other(uuid, text) from public;
|
||||
grant execute on function edit_payment_account_other(uuid, text) to invoicer;
|
||||
grant execute on function edit_payment_account_other(uuid, text) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,32 @@
|
|||
-- Deploy numerus:expense_payment to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: expense
|
||||
-- requires: payment
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table expense_payment (
|
||||
expense_id integer not null references expense,
|
||||
payment_id integer not null references payment,
|
||||
primary key (expense_id, payment_id)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table expense_payment to invoicer;
|
||||
grant select, insert, update, delete on table expense_payment to admin;
|
||||
|
||||
alter table expense_payment enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on expense_payment
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from expense
|
||||
where expense.expense_id = expense_payment.expense_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,47 @@
|
|||
-- Deploy numerus:payment to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: company
|
||||
-- requires: payment_account
|
||||
-- requires: currency
|
||||
-- requires: tag_name
|
||||
-- requires: payment_status
|
||||
-- requires: extension_pgcrypto
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment (
|
||||
payment_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(),
|
||||
description text not null,
|
||||
payment_date date not null default current_date,
|
||||
payment_account_id integer not null references payment_account,
|
||||
amount integer not null,
|
||||
currency_code text not null references currency,
|
||||
tags tag_name[] not null default '{}',
|
||||
payment_status text not null default 'complete' references payment_status,
|
||||
created_at timestamptz not null default current_timestamp
|
||||
);
|
||||
|
||||
create index on payment using gin (tags);
|
||||
|
||||
grant select, insert, update, delete on table payment to invoicer;
|
||||
grant select, insert, update, delete on table payment to admin;
|
||||
|
||||
alter table payment enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on payment
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = payment.company_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,38 @@
|
|||
-- Deploy numerus:payment_account to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: company
|
||||
-- requires: payment_account_type
|
||||
-- requires: extension_pgcrypto
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment_account (
|
||||
payment_account_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(),
|
||||
payment_account_type text not null references payment_account_type,
|
||||
name text not null constraint payment_account_name_not_empty check(length(trim(name)) > 0),
|
||||
unique (payment_account_id, payment_account_type)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table payment_account to invoicer;
|
||||
grant select, insert, update, delete on table payment_account to admin;
|
||||
|
||||
alter table payment_account enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on payment_account
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from company_user
|
||||
join user_profile using (user_id)
|
||||
where company_user.company_id = payment_account.company_id
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
commit;
|
|
@ -0,0 +1,33 @@
|
|||
-- Deploy numerus:payment_account_bank to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
-- requires: extension_iban
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment_account_bank (
|
||||
payment_account_id integer primary key,
|
||||
payment_account_type text not null default 'bank' constraint payment_account_type_is_bank check (payment_account_type = 'bank'),
|
||||
iban iban not null,
|
||||
foreign key (payment_account_id, payment_account_type) references payment_account (payment_account_id, payment_account_type)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table payment_account_bank to invoicer;
|
||||
grant select, insert, update, delete on table payment_account_bank to admin;
|
||||
|
||||
alter table payment_account_bank enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on payment_account_bank
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from payment_account
|
||||
where payment_account.payment_account_id = payment_account_bank.payment_account_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,33 @@
|
|||
-- Deploy numerus:payment_account_card to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment_account_card (
|
||||
payment_account_id integer primary key,
|
||||
payment_account_type text not null default 'card' constraint payment_account_type_is_card check (payment_account_type = 'card'),
|
||||
last_four_digits text not null constraint last_four_digits_are_digits check ( last_four_digits ~ '^\d{4}$'),
|
||||
expiration_date date not null,
|
||||
foreign key (payment_account_id, payment_account_type) references payment_account (payment_account_id, payment_account_type)
|
||||
);
|
||||
|
||||
grant select, insert, update, delete on table payment_account_card to invoicer;
|
||||
grant select, insert, update, delete on table payment_account_card to admin;
|
||||
|
||||
alter table payment_account_card enable row level security;
|
||||
|
||||
create policy company_policy
|
||||
on payment_account_card
|
||||
using (
|
||||
exists(
|
||||
select 1
|
||||
from payment_account
|
||||
where payment_account.payment_account_id = payment_account_card.payment_account_id
|
||||
)
|
||||
);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,17 @@
|
|||
-- Deploy numerus:payment_account_type to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment_account_type (
|
||||
payment_account_type text primary key,
|
||||
name text not null
|
||||
);
|
||||
|
||||
grant select on table payment_account_type to invoicer;
|
||||
grant select on table payment_account_type to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,21 @@
|
|||
-- Deploy numerus:payment_account_type_i18n to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_account_type
|
||||
-- requires: language
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment_account_type_i18n (
|
||||
payment_account_type text not null references payment_account_type,
|
||||
lang_tag text not null references language,
|
||||
name text not null,
|
||||
primary key (payment_account_type, lang_tag)
|
||||
);
|
||||
|
||||
grant select on table payment_account_type_i18n to invoicer;
|
||||
grant select on table payment_account_type_i18n to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,17 @@
|
|||
-- Deploy numerus:payment_status to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment_status (
|
||||
payment_status text primary key,
|
||||
name text not null
|
||||
);
|
||||
|
||||
grant select on table payment_status to invoicer;
|
||||
grant select on table payment_status to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,21 @@
|
|||
-- Deploy numerus:payment_status_i18n to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: payment_status
|
||||
-- requires: language
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create table payment_status_i18n (
|
||||
payment_status text not null references payment_status,
|
||||
lang_tag text not null references language,
|
||||
name text not null,
|
||||
primary key (payment_status, lang_tag)
|
||||
);
|
||||
|
||||
grant select on table payment_status_i18n to invoicer;
|
||||
grant select on table payment_status_i18n to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,43 @@
|
|||
-- Deploy numerus:update_expense_payment_status to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_numerus
|
||||
-- requires: expense
|
||||
-- requires: payment
|
||||
-- requires: expense_payment
|
||||
-- requires: available_expense_status
|
||||
-- requires: available_payment_status
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
create or replace function update_expense_payment_status(pid integer, eid integer, amount_cents integer) returns void as
|
||||
$$
|
||||
update payment
|
||||
set payment_status = case when expense.amount > amount_cents or exists (select 1 from expense_payment as ep where ep.expense_id = expense.expense_id and payment_id <> pid) then 'partial' else 'complete' end
|
||||
from expense
|
||||
where expense.expense_id = eid
|
||||
and payment_id = pid
|
||||
;
|
||||
|
||||
update expense
|
||||
set expense_status = case when paid_amount >= expense.amount then 'paid' else 'partial' end
|
||||
from (
|
||||
select expense_payment.expense_id
|
||||
, sum(payment.amount) as paid_amount
|
||||
from expense_payment
|
||||
join payment using (payment_id)
|
||||
group by expense_payment.expense_id
|
||||
) as payment
|
||||
where payment.expense_id = expense.expense_id
|
||||
and expense.expense_id = eid
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function update_expense_payment_status(integer, integer, integer) from public;
|
||||
grant execute on function update_expense_payment_status(integer, integer, integer) to invoicer;
|
||||
grant execute on function update_expense_payment_status(integer, integer, integer) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,340 @@
|
|||
package pkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ExpirationDateFormat = "01/06"
|
||||
AccountTypeBank = "bank"
|
||||
AccountTypeCard = "card"
|
||||
AccountTypeCash = "cash"
|
||||
AccountTypeOther = "other"
|
||||
)
|
||||
|
||||
func servePaymentAccountIndex(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
conn := getConn(r)
|
||||
locale := getLocale(r)
|
||||
|
||||
page := NewPaymentAccountIndexPage(r.Context(), conn, locale)
|
||||
page.MustRender(w, r)
|
||||
}
|
||||
|
||||
type PaymentAccountIndexPage struct {
|
||||
Accounts []*PaymentAccountEntry
|
||||
}
|
||||
|
||||
func NewPaymentAccountIndexPage(ctx context.Context, conn *Conn, locale *Locale) *PaymentAccountIndexPage {
|
||||
return &PaymentAccountIndexPage{
|
||||
Accounts: mustCollectPaymentAccountEntries(ctx, conn, locale),
|
||||
}
|
||||
}
|
||||
|
||||
func (page *PaymentAccountIndexPage) MustRender(w http.ResponseWriter, r *http.Request) {
|
||||
mustRenderMainTemplate(w, r, "payments/accounts/index.gohtml", page)
|
||||
}
|
||||
|
||||
type PaymentAccountEntry struct {
|
||||
ID int
|
||||
Slug string
|
||||
Type string
|
||||
TypeLabel string
|
||||
Name string
|
||||
IBAN string
|
||||
LastFourDigits string
|
||||
ExpirationDate string
|
||||
}
|
||||
|
||||
func mustCollectPaymentAccountEntries(ctx context.Context, conn *Conn, locale *Locale) []*PaymentAccountEntry {
|
||||
rows := conn.MustQuery(ctx, `
|
||||
select payment_account_id
|
||||
, slug
|
||||
, payment_account.payment_account_type
|
||||
, coalesce(i18n.name, payment_account_type.name)
|
||||
, payment_account.name
|
||||
, coalesce(iban::text, '') as iban
|
||||
, coalesce(last_four_digits, '') as last_four_digits
|
||||
, expiration_date
|
||||
from payment_account
|
||||
left join payment_account_bank using (payment_account_id, payment_account_type)
|
||||
left join payment_account_card using (payment_account_id, payment_account_type)
|
||||
join payment_account_type using (payment_account_type)
|
||||
left join payment_account_type_i18n as i18n on payment_account_type.payment_account_type = i18n.payment_account_type and i18n.lang_tag = $1
|
||||
order by payment_account_id
|
||||
`, locale.Language.String())
|
||||
defer rows.Close()
|
||||
|
||||
var entries []*PaymentAccountEntry
|
||||
for rows.Next() {
|
||||
entry := &PaymentAccountEntry{}
|
||||
var expirationDate pgtype.Date
|
||||
if err := rows.Scan(&entry.ID, &entry.Slug, &entry.Type, &entry.TypeLabel, &entry.Name, &entry.IBAN, &entry.LastFourDigits, &expirationDate); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if expirationDate.Status == pgtype.Present {
|
||||
entry.ExpirationDate = expirationDate.Time.Format(ExpirationDateFormat)
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
panic(rows.Err())
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func servePaymentAccountForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
locale := getLocale(r)
|
||||
conn := getConn(r)
|
||||
company := mustGetCompany(r)
|
||||
form := newPaymentAccountForm(r.Context(), conn, locale, company)
|
||||
slug := params[0].Value
|
||||
if slug == "new" {
|
||||
form.MustRender(w, r)
|
||||
return
|
||||
}
|
||||
if !ValidUuid(slug) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if !form.MustFillFromDatabase(r.Context(), conn, slug) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
form.MustRender(w, r)
|
||||
}
|
||||
|
||||
type PaymentAccountForm struct {
|
||||
locale *Locale
|
||||
company *Company
|
||||
Slug string
|
||||
Type *RadioField
|
||||
Name *InputField
|
||||
IBAN *InputField
|
||||
LastFourDigits *InputField
|
||||
ExpirationMonthYear *InputField
|
||||
}
|
||||
|
||||
func newPaymentAccountForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *PaymentAccountForm {
|
||||
return &PaymentAccountForm{
|
||||
locale: locale,
|
||||
company: company,
|
||||
Type: &RadioField{
|
||||
Name: "payment_account_type",
|
||||
Label: pgettext("input", "Type", locale),
|
||||
Required: true,
|
||||
Options: MustGetRadioOptions(ctx, conn, "select payment_account_type, i18n.name from payment_account_type join payment_account_type_i18n as i18n using (payment_account_type) where i18n.lang_tag = $1 order by payment_account_type", locale.Language.String()),
|
||||
Attributes: []template.HTMLAttr{
|
||||
`x-model="type"`,
|
||||
},
|
||||
},
|
||||
Name: &InputField{
|
||||
Name: "name",
|
||||
Label: pgettext("input", "Name", locale),
|
||||
Required: true,
|
||||
Type: "text",
|
||||
},
|
||||
IBAN: &InputField{
|
||||
Name: "iban",
|
||||
Label: pgettext("input", "IBAN", locale),
|
||||
Required: true,
|
||||
Type: "text",
|
||||
},
|
||||
LastFourDigits: &InputField{
|
||||
Name: "last_four_digits",
|
||||
Label: pgettext("input", "Card’s last four digits", locale),
|
||||
Required: true,
|
||||
Type: "text",
|
||||
Attributes: []template.HTMLAttr{
|
||||
`maxlength="4"`,
|
||||
`minlength="4"`,
|
||||
`pattern="[0-9]{4}"`,
|
||||
},
|
||||
},
|
||||
ExpirationMonthYear: &InputField{
|
||||
Name: "expiration_date",
|
||||
Label: pgettext("input", "Expiration date", locale),
|
||||
Required: true,
|
||||
Type: "text",
|
||||
Attributes: []template.HTMLAttr{
|
||||
`maxlength="5"`,
|
||||
`minlength="5"`,
|
||||
`pattern="[0-9]{2}/[0-9]{2}"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *PaymentAccountForm) MustRender(w http.ResponseWriter, r *http.Request) {
|
||||
if f.Slug == "" {
|
||||
mustRenderMainTemplate(w, r, "payments/accounts/new.gohtml", f)
|
||||
} else {
|
||||
mustRenderMainTemplate(w, r, "payments/accounts/edit.gohtml", f)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *PaymentAccountForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slug string) bool {
|
||||
selectedType := f.Type.Selected
|
||||
var expirationDate pgtype.Date
|
||||
if notFoundErrorOrPanic(conn.QueryRow(ctx, `
|
||||
select payment_account_type
|
||||
, name
|
||||
, coalesce(iban::text, '') as iban
|
||||
, coalesce(last_four_digits, '') as last_four_digits
|
||||
, expiration_date
|
||||
from payment_account
|
||||
left join payment_account_bank using (payment_account_id, payment_account_type)
|
||||
left join payment_account_card using (payment_account_id, payment_account_type)
|
||||
where slug = $1
|
||||
`, slug).Scan(
|
||||
f.Type,
|
||||
f.Name,
|
||||
f.IBAN,
|
||||
f.LastFourDigits,
|
||||
&expirationDate)) {
|
||||
f.Type.Selected = selectedType
|
||||
return false
|
||||
}
|
||||
f.Slug = slug
|
||||
if expirationDate.Status == pgtype.Present {
|
||||
f.ExpirationMonthYear.Val = expirationDate.Time.Format(ExpirationDateFormat)
|
||||
} else {
|
||||
f.ExpirationMonthYear.Val = ""
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *PaymentAccountForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Type.FillValue(r)
|
||||
f.Name.FillValue(r)
|
||||
f.IBAN.FillValue(r)
|
||||
f.LastFourDigits.FillValue(r)
|
||||
f.ExpirationMonthYear.FillValue(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *PaymentAccountForm) Validate(ctx context.Context, conn *Conn) bool {
|
||||
validator := newFormValidator()
|
||||
|
||||
if validator.CheckValidRadioOption(f.Type, gettext("Selected payment account type is not valid.", f.locale)) {
|
||||
switch f.Type.Selected {
|
||||
case AccountTypeBank:
|
||||
if validator.CheckRequiredInput(f.IBAN, gettext("IBAN can not be empty.", f.locale)) {
|
||||
validator.CheckValidIBANInput(ctx, conn, f.IBAN, gettext("This value is not a valid IBAN.", f.locale))
|
||||
}
|
||||
case AccountTypeCard:
|
||||
if validator.CheckRequiredInput(f.LastFourDigits, gettext("Last four digits can not be empty.", f.locale)) {
|
||||
if validator.CheckInputLength(f.LastFourDigits, 4, gettext("You must enter the card’s last four digits", f.locale)) {
|
||||
validator.CheckValidInteger(f.LastFourDigits, 0, 9999, gettext("Last four digits must be a number.", f.locale))
|
||||
}
|
||||
}
|
||||
if validator.CheckRequiredInput(f.ExpirationMonthYear, gettext("Expiration date can not be empty.", f.locale)) {
|
||||
_, err := time.Parse(ExpirationDateFormat, f.ExpirationMonthYear.Val)
|
||||
validator.checkInput(f.ExpirationMonthYear, err == nil, gettext("Expiration date should be a valid date in format MM/YY (e.g., 08/24).", f.locale))
|
||||
}
|
||||
}
|
||||
}
|
||||
validator.CheckRequiredInput(f.Name, gettext("Payment account name can not be empty.", f.locale))
|
||||
|
||||
return validator.AllOK()
|
||||
}
|
||||
|
||||
func (f *PaymentAccountForm) ExpirationDate() (time.Time, error) {
|
||||
date, err := time.Parse(ExpirationDateFormat, f.ExpirationMonthYear.Val)
|
||||
if err != nil {
|
||||
return date, err
|
||||
}
|
||||
return date.AddDate(0, 1, -1), nil
|
||||
}
|
||||
|
||||
func handleAddPaymentAccount(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
locale := getLocale(r)
|
||||
conn := getConn(r)
|
||||
company := mustGetCompany(r)
|
||||
form := newPaymentAccountForm(r.Context(), conn, locale, company)
|
||||
if err := form.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := verifyCsrfTokenValid(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if !form.Validate(r.Context(), conn) {
|
||||
if !IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
form.MustRender(w, r)
|
||||
return
|
||||
}
|
||||
switch form.Type.Selected {
|
||||
case AccountTypeBank:
|
||||
conn.MustExec(r.Context(), "select add_payment_account_bank($1, $2, $3)", company.Id, form.Name, form.IBAN)
|
||||
case AccountTypeCard:
|
||||
date, err := form.ExpirationDate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conn.MustExec(r.Context(), "select add_payment_account_card($1, $2, $3, $4)", company.Id, form.Name, form.LastFourDigits, date)
|
||||
case AccountTypeCash:
|
||||
conn.MustExec(r.Context(), "select add_payment_account_cash($1, $2)", company.Id, form.Name)
|
||||
case AccountTypeOther:
|
||||
conn.MustExec(r.Context(), "select add_payment_account_other($1, $2)", company.Id, form.Name)
|
||||
}
|
||||
htmxRedirect(w, r, companyURI(company, "/payment-accounts"))
|
||||
}
|
||||
|
||||
func handleEditPaymentAccount(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
conn := getConn(r)
|
||||
locale := getLocale(r)
|
||||
company := mustGetCompany(r)
|
||||
form := newPaymentAccountForm(r.Context(), conn, locale, company)
|
||||
form.Slug = params[0].Value
|
||||
if !ValidUuid(form.Slug) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if err := form.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := verifyCsrfTokenValid(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if !form.Validate(r.Context(), conn) {
|
||||
if !IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
form.MustRender(w, r)
|
||||
return
|
||||
}
|
||||
var found string
|
||||
switch form.Type.Selected {
|
||||
case AccountTypeBank:
|
||||
found = conn.MustGetText(r.Context(), "", "select edit_payment_account_bank($1, $2, $3)", form.Slug, form.Name, form.IBAN)
|
||||
case AccountTypeCard:
|
||||
date, err := form.ExpirationDate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
found = conn.MustGetText(r.Context(), "", "select edit_payment_account_card($1, $2, $3, $4)", form.Slug, form.Name, form.LastFourDigits, date)
|
||||
case AccountTypeCash:
|
||||
found = conn.MustGetText(r.Context(), "", "select edit_payment_account_cash($1, $2)", form.Slug, form.Name)
|
||||
case AccountTypeOther:
|
||||
found = conn.MustGetText(r.Context(), "", "select edit_payment_account_other($1, $2)", form.Slug, form.Name)
|
||||
}
|
||||
if found == "" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
htmxRedirect(w, r, companyURI(company, "/payment-accounts"))
|
||||
}
|
43
pkg/form.go
43
pkg/form.go
|
@ -295,6 +295,33 @@ func (field *RadioField) isValidOption(selected string) bool {
|
|||
return field.FindOption(selected) != nil
|
||||
}
|
||||
|
||||
func (field *RadioField) HasValidOption() bool {
|
||||
return field.isValidOption(field.Selected)
|
||||
}
|
||||
|
||||
func MustGetRadioOptions(ctx context.Context, conn *Conn, sql string, args ...interface{}) []*RadioOption {
|
||||
rows, err := conn.Query(ctx, sql, args...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var options []*RadioOption
|
||||
for rows.Next() {
|
||||
option := &RadioOption{}
|
||||
err = rows.Scan(&option.Value, &option.Label)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
options = append(options, option)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
panic(rows.Err())
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
type CheckField struct {
|
||||
Name string
|
||||
Label string
|
||||
|
@ -452,6 +479,10 @@ func (v *FormValidator) CheckInputMinLength(field *InputField, min int, message
|
|||
return v.checkInput(field, len(field.Val) >= min, message)
|
||||
}
|
||||
|
||||
func (v *FormValidator) CheckInputLength(field *InputField, length int, message string) bool {
|
||||
return v.checkInput(field, len(field.Val) == length, message)
|
||||
}
|
||||
|
||||
func (v *FormValidator) CheckValidEmailInput(field *InputField, message string) bool {
|
||||
_, err := mail.ParseAddress(field.Val)
|
||||
return v.checkInput(field, err == nil, message)
|
||||
|
@ -481,6 +512,10 @@ func (v *FormValidator) CheckValidSelectOption(field *SelectField, message strin
|
|||
return v.checkSelect(field, field.HasValidOptions(), message)
|
||||
}
|
||||
|
||||
func (v *FormValidator) CheckValidRadioOption(field *RadioField, message string) bool {
|
||||
return v.checkRadio(field, field.HasValidOption(), message)
|
||||
}
|
||||
|
||||
func (v *FormValidator) CheckAtMostOneOfEachGroup(field *SelectField, message string) bool {
|
||||
repeated := false
|
||||
groups := map[string]bool{}
|
||||
|
@ -539,3 +574,11 @@ func (v *FormValidator) checkSelect(field *SelectField, ok bool, message string)
|
|||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func (v *FormValidator) checkRadio(field *RadioField, ok bool, message string) bool {
|
||||
if !ok {
|
||||
field.Errors = append(field.Errors, errors.New(message))
|
||||
v.Valid = false
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
package pkg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"html/template"
|
||||
"math"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func servePaymentIndex(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
conn := getConn(r)
|
||||
locale := getLocale(r)
|
||||
|
||||
page := NewPaymentIndexPage(r.Context(), conn, locale)
|
||||
page.MustRender(w, r)
|
||||
}
|
||||
|
||||
type PaymentIndexPage struct {
|
||||
Payments []*PaymentEntry
|
||||
}
|
||||
|
||||
func NewPaymentIndexPage(ctx context.Context, conn *Conn, locale *Locale) *PaymentIndexPage {
|
||||
return &PaymentIndexPage{
|
||||
Payments: mustCollectPaymentEntries(ctx, conn, locale),
|
||||
}
|
||||
}
|
||||
|
||||
func (page *PaymentIndexPage) MustRender(w http.ResponseWriter, r *http.Request) {
|
||||
mustRenderMainTemplate(w, r, "payments/index.gohtml", page)
|
||||
}
|
||||
|
||||
type PaymentEntry struct {
|
||||
ID int
|
||||
Slug string
|
||||
PaymentDate time.Time
|
||||
Description string
|
||||
Total string
|
||||
Tags []string
|
||||
Status string
|
||||
StatusLabel string
|
||||
}
|
||||
|
||||
func mustCollectPaymentEntries(ctx context.Context, conn *Conn, locale *Locale) []*PaymentEntry {
|
||||
rows := conn.MustQuery(ctx, `
|
||||
select payment_id
|
||||
, payment.slug
|
||||
, payment_date
|
||||
, description
|
||||
, to_price(payment.amount, decimal_digits) as total
|
||||
, payment.tags
|
||||
, payment.payment_status
|
||||
, psi18n.name
|
||||
from payment
|
||||
join payment_status_i18n psi18n on payment.payment_status = psi18n.payment_status and psi18n.lang_tag = $1
|
||||
join currency using (currency_code)
|
||||
order by payment_date desc, total desc
|
||||
`, locale.Language)
|
||||
defer rows.Close()
|
||||
|
||||
var entries []*PaymentEntry
|
||||
for rows.Next() {
|
||||
entry := &PaymentEntry{}
|
||||
if err := rows.Scan(&entry.ID, &entry.Slug, &entry.PaymentDate, &entry.Description, &entry.Total, &entry.Tags, &entry.Status, &entry.StatusLabel); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
panic(rows.Err())
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func servePaymentForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
locale := getLocale(r)
|
||||
conn := getConn(r)
|
||||
company := mustGetCompany(r)
|
||||
form := newPaymentForm(r.Context(), conn, locale, company)
|
||||
slug := params[0].Value
|
||||
if slug == "new" {
|
||||
form.PaymentDate.Val = time.Now().Format("2006-01-02")
|
||||
form.MustRender(w, r)
|
||||
return
|
||||
}
|
||||
if !ValidUuid(slug) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if !form.MustFillFromDatabase(r.Context(), conn, slug) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
form.MustRender(w, r)
|
||||
}
|
||||
|
||||
type PaymentForm struct {
|
||||
locale *Locale
|
||||
company *Company
|
||||
Slug string
|
||||
Description *InputField
|
||||
PaymentDate *InputField
|
||||
PaymentAccount *SelectField
|
||||
Amount *InputField
|
||||
Tags *TagsField
|
||||
}
|
||||
|
||||
func newPaymentForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *PaymentForm {
|
||||
return &PaymentForm{
|
||||
locale: locale,
|
||||
company: company,
|
||||
Description: &InputField{
|
||||
Name: "description",
|
||||
Label: pgettext("input", "Description", locale),
|
||||
Required: true,
|
||||
Type: "text",
|
||||
},
|
||||
PaymentDate: &InputField{
|
||||
Name: "payment_date",
|
||||
Label: pgettext("input", "Invoice Date", locale),
|
||||
Required: true,
|
||||
Type: "date",
|
||||
},
|
||||
PaymentAccount: &SelectField{
|
||||
Name: "payment_account",
|
||||
Label: pgettext("input", "Account", locale),
|
||||
Required: true,
|
||||
Options: MustGetOptions(ctx, conn, "select payment_account_id::text, name from payment_account order by name"),
|
||||
},
|
||||
Amount: &InputField{
|
||||
Name: "amount",
|
||||
Label: pgettext("input", "Amount", locale),
|
||||
Type: "number",
|
||||
Required: true,
|
||||
Attributes: []template.HTMLAttr{
|
||||
`min="0"`,
|
||||
template.HTMLAttr(fmt.Sprintf(`step="%v"`, company.MinCents())),
|
||||
},
|
||||
},
|
||||
Tags: &TagsField{
|
||||
Name: "tags",
|
||||
Label: pgettext("input", "Tags", locale),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *PaymentForm) MustRender(w http.ResponseWriter, r *http.Request) {
|
||||
if f.Slug == "" {
|
||||
f.PaymentAccount.EmptyLabel = gettext("Select an account.", f.locale)
|
||||
mustRenderMainTemplate(w, r, "payments/new.gohtml", f)
|
||||
} else {
|
||||
mustRenderMainTemplate(w, r, "payments/edit.gohtml", f)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *PaymentForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slug string) bool {
|
||||
selectedPaymentAccount := f.PaymentAccount.Selected
|
||||
f.PaymentAccount.Clear()
|
||||
if notFoundErrorOrPanic(conn.QueryRow(ctx, `
|
||||
select description
|
||||
, payment_date
|
||||
, payment_account_id::text
|
||||
, to_price(amount, decimal_digits)
|
||||
, tags
|
||||
from payment
|
||||
join currency using (currency_code)
|
||||
where payment.slug = $1
|
||||
`, slug).Scan(
|
||||
f.Description,
|
||||
f.PaymentDate,
|
||||
f.PaymentAccount,
|
||||
f.Amount,
|
||||
f.Tags)) {
|
||||
f.PaymentAccount.Selected = selectedPaymentAccount
|
||||
return false
|
||||
}
|
||||
f.Slug = slug
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *PaymentForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Description.FillValue(r)
|
||||
f.PaymentDate.FillValue(r)
|
||||
f.PaymentAccount.FillValue(r)
|
||||
f.Amount.FillValue(r)
|
||||
f.Tags.FillValue(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *PaymentForm) Validate() bool {
|
||||
validator := newFormValidator()
|
||||
validator.CheckRequiredInput(f.Description, gettext("Description can not be empty.", f.locale))
|
||||
validator.CheckValidSelectOption(f.PaymentAccount, gettext("Selected payment account is not valid.", f.locale))
|
||||
validator.CheckValidDate(f.PaymentDate, gettext("Payment date must be a valid date.", f.locale))
|
||||
if validator.CheckRequiredInput(f.Amount, gettext("Amount can not be empty.", f.locale)) {
|
||||
validator.CheckValidDecimal(f.Amount, f.company.MinCents(), math.MaxFloat64, gettext("Amount must be a number greater than zero.", f.locale))
|
||||
}
|
||||
return validator.AllOK()
|
||||
}
|
||||
|
||||
func handleAddPayment(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
locale := getLocale(r)
|
||||
conn := getConn(r)
|
||||
company := mustGetCompany(r)
|
||||
form := newPaymentForm(r.Context(), conn, locale, company)
|
||||
if err := form.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := verifyCsrfTokenValid(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if !form.Validate() {
|
||||
if !IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
form.MustRender(w, r)
|
||||
return
|
||||
}
|
||||
conn.MustExec(r.Context(), "select add_payment($1, $2, $3, $4, $5, $6, $7)", company.Id, nil, form.PaymentDate, form.PaymentAccount, form.Description, form.Amount, form.Tags)
|
||||
htmxRedirect(w, r, companyURI(company, "/payments"))
|
||||
}
|
||||
|
||||
func handleEditPayment(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
conn := getConn(r)
|
||||
locale := getLocale(r)
|
||||
company := mustGetCompany(r)
|
||||
form := newPaymentForm(r.Context(), conn, locale, company)
|
||||
form.Slug = params[0].Value
|
||||
if !ValidUuid(form.Slug) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if err := form.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := verifyCsrfTokenValid(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if !form.Validate() {
|
||||
if !IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
form.MustRender(w, r)
|
||||
return
|
||||
}
|
||||
if found := conn.MustGetText(r.Context(), "", "select edit_payment($1, $2, $3, $4, $5, $6)", form.Slug, form.PaymentDate, form.PaymentAccount, form.Description, form.Amount, form.Tags); found == "" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
htmxRedirect(w, r, companyURI(company, "/payments"))
|
||||
}
|
|
@ -58,6 +58,14 @@ func NewRouter(db *Db, demo bool) http.Handler {
|
|||
companyRouter.PUT("/expenses/:slug/tags", HandleUpdateExpenseTags)
|
||||
companyRouter.GET("/expenses/:slug/tags/edit", ServeEditExpenseTags)
|
||||
companyRouter.GET("/expenses/:slug/download/:filename", ServeExpenseAttachment)
|
||||
companyRouter.GET("/payments", servePaymentIndex)
|
||||
companyRouter.POST("/payments", handleAddPayment)
|
||||
companyRouter.GET("/payments/:slug", servePaymentForm)
|
||||
companyRouter.PUT("/payments/:slug", handleEditPayment)
|
||||
companyRouter.GET("/payment-accounts", servePaymentAccountIndex)
|
||||
companyRouter.POST("/payment-accounts", handleAddPaymentAccount)
|
||||
companyRouter.GET("/payment-accounts/:slug", servePaymentAccountForm)
|
||||
companyRouter.PUT("/payment-accounts/:slug", handleEditPaymentAccount)
|
||||
companyRouter.GET("/", ServeDashboard)
|
||||
|
||||
router := httprouter.New()
|
||||
|
|
270
po/ca.po
270
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2024-07-20 22:51+0200\n"
|
||||
"POT-Creation-Date: 2024-08-10 04:08+0200\n"
|
||||
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Catalan <ca@dodds.net>\n"
|
||||
|
@ -35,6 +35,11 @@ msgstr "Afegeix productes a la factura"
|
|||
#: web/template/expenses/index.gohtml:10 web/template/expenses/edit.gohtml:10
|
||||
#: web/template/tax-details.gohtml:9 web/template/products/new.gohtml:9
|
||||
#: web/template/products/index.gohtml:9 web/template/products/edit.gohtml:10
|
||||
#: web/template/payments/new.gohtml:10 web/template/payments/index.gohtml:10
|
||||
#: web/template/payments/edit.gohtml:10
|
||||
#: web/template/payments/accounts/new.gohtml:10
|
||||
#: web/template/payments/accounts/index.gohtml:10
|
||||
#: web/template/payments/accounts/edit.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "Home"
|
||||
msgstr "Inici"
|
||||
|
@ -61,6 +66,7 @@ msgstr "Tots"
|
|||
#: web/template/invoices/products.gohtml:49
|
||||
#: web/template/switch-company.gohtml:22 web/template/quotes/products.gohtml:49
|
||||
#: web/template/products/index.gohtml:45
|
||||
#: web/template/payments/accounts/index.gohtml:25
|
||||
msgctxt "title"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
@ -107,7 +113,7 @@ msgstr "Subtotal"
|
|||
#: web/template/quotes/new.gohtml:74 web/template/quotes/view.gohtml:82
|
||||
#: web/template/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:75
|
||||
#: web/template/expenses/new.gohtml:47 web/template/expenses/index.gohtml:74
|
||||
#: web/template/expenses/edit.gohtml:49
|
||||
#: web/template/expenses/edit.gohtml:49 web/template/payments/index.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
@ -115,6 +121,8 @@ msgstr "Total"
|
|||
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93
|
||||
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
|
||||
#: web/template/expenses/new.gohtml:57 web/template/expenses/edit.gohtml:59
|
||||
#: web/template/payments/edit.gohtml:35
|
||||
#: web/template/payments/accounts/edit.gohtml:38
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
msgstr "Actualitza"
|
||||
|
@ -124,6 +132,8 @@ msgstr "Actualitza"
|
|||
#: web/template/contacts/new.gohtml:49 web/template/contacts/edit.gohtml:53
|
||||
#: web/template/expenses/new.gohtml:60 web/template/expenses/edit.gohtml:62
|
||||
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
|
||||
#: web/template/payments/new.gohtml:33
|
||||
#: web/template/payments/accounts/new.gohtml:41
|
||||
msgctxt "action"
|
||||
msgid "Save"
|
||||
msgstr "Desa"
|
||||
|
@ -180,14 +190,14 @@ msgid "Customer"
|
|||
msgstr "Client"
|
||||
|
||||
#: web/template/invoices/index.gohtml:70 web/template/quotes/index.gohtml:70
|
||||
#: web/template/expenses/index.gohtml:68
|
||||
#: web/template/expenses/index.gohtml:68 web/template/payments/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estat"
|
||||
|
||||
#: web/template/invoices/index.gohtml:71 web/template/quotes/index.gohtml:71
|
||||
#: web/template/contacts/index.gohtml:50 web/template/expenses/index.gohtml:69
|
||||
#: web/template/products/index.gohtml:46
|
||||
#: web/template/products/index.gohtml:46 web/template/payments/index.gohtml:28
|
||||
msgctxt "title"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
@ -326,7 +336,7 @@ msgctxt "input"
|
|||
msgid "(Max. %s)"
|
||||
msgstr "(Màx. %s)"
|
||||
|
||||
#: web/template/form.gohtml:200
|
||||
#: web/template/form.gohtml:202
|
||||
msgctxt "action"
|
||||
msgid "Filters"
|
||||
msgstr "Filtra"
|
||||
|
@ -489,15 +499,20 @@ msgstr "Despeses"
|
|||
|
||||
#: web/template/app.gohtml:57
|
||||
msgctxt "nav"
|
||||
msgid "Payments"
|
||||
msgstr "Pagaments"
|
||||
|
||||
#: web/template/app.gohtml:58
|
||||
msgctxt "nav"
|
||||
msgid "Products"
|
||||
msgstr "Productes"
|
||||
|
||||
#: web/template/app.gohtml:58
|
||||
#: web/template/app.gohtml:59
|
||||
msgctxt "nav"
|
||||
msgid "Contacts"
|
||||
msgstr "Contactes"
|
||||
|
||||
#: web/template/app.gohtml:66
|
||||
#: web/template/app.gohtml:67
|
||||
msgid "<a href=\"https://numerus.cat/\">Numerus</a> Version: %s"
|
||||
msgstr "<a href=\"https://numerus.cat/\">Numerus</a> versió: %s"
|
||||
|
||||
|
@ -731,6 +746,84 @@ msgctxt "title"
|
|||
msgid "Edit Product “%s”"
|
||||
msgstr "Edició del producte «%s»"
|
||||
|
||||
#: web/template/payments/new.gohtml:3 web/template/payments/new.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "New Payment"
|
||||
msgstr "Nou pagament"
|
||||
|
||||
#: web/template/payments/new.gohtml:11 web/template/payments/index.gohtml:3
|
||||
#: web/template/payments/index.gohtml:11 web/template/payments/edit.gohtml:11
|
||||
msgctxt "title"
|
||||
msgid "Payments"
|
||||
msgstr "Pagaments"
|
||||
|
||||
#: web/template/payments/index.gohtml:16
|
||||
msgctxt "action"
|
||||
msgid "New payment"
|
||||
msgstr "Nou pagament"
|
||||
|
||||
#: web/template/payments/index.gohtml:25
|
||||
msgctxt "title"
|
||||
msgid "Payment Date"
|
||||
msgstr "Data del pagament"
|
||||
|
||||
#: web/template/payments/index.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Description"
|
||||
msgstr "Descripció"
|
||||
|
||||
#: web/template/payments/index.gohtml:54
|
||||
msgid "No payments added yet."
|
||||
msgstr "No hi ha cap pagament."
|
||||
|
||||
#: web/template/payments/edit.gohtml:3
|
||||
msgctxt "title"
|
||||
msgid "Edit Payment “%s”"
|
||||
msgstr "Edició del pagament «%s»"
|
||||
|
||||
#: web/template/payments/accounts/new.gohtml:3
|
||||
#: web/template/payments/accounts/new.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "New Payment Account"
|
||||
msgstr "Nou compte de pagament"
|
||||
|
||||
#: web/template/payments/accounts/new.gohtml:11
|
||||
#: web/template/payments/accounts/index.gohtml:3
|
||||
#: web/template/payments/accounts/index.gohtml:11
|
||||
#: web/template/payments/accounts/edit.gohtml:11
|
||||
msgctxt "title"
|
||||
msgid "Payment Accounts"
|
||||
msgstr "Comptes de pagament"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:16
|
||||
msgctxt "action"
|
||||
msgid "New payment account"
|
||||
msgstr "Nou compte de pagament"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Type"
|
||||
msgstr "Tipus"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:28
|
||||
msgctxt "title"
|
||||
msgid "Expiration Date"
|
||||
msgstr "Data de caducitat"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:51
|
||||
msgid "No payment accounts added yet."
|
||||
msgstr "No hi ha cap compte de pagament."
|
||||
|
||||
#: web/template/payments/accounts/edit.gohtml:3
|
||||
msgctxt "title"
|
||||
msgid "Edit Payment Account “%s”"
|
||||
msgstr "Edició del compte de pagament «%s»"
|
||||
|
||||
#: pkg/ods.go:62 pkg/ods.go:106
|
||||
msgctxt "title"
|
||||
msgid "VAT number"
|
||||
|
@ -762,49 +855,50 @@ msgstr "No podeu deixar la contrasenya en blanc."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||
|
||||
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901
|
||||
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901 pkg/accounts.go:138
|
||||
#: pkg/invoices.go:1147 pkg/contacts.go:149 pkg/contacts.go:262
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
|
||||
#: pkg/expenses.go:343 pkg/expenses.go:511 pkg/invoices.go:177
|
||||
#: pkg/invoices.go:877 pkg/invoices.go:1462 pkg/contacts.go:154
|
||||
#: pkg/contacts.go:362
|
||||
#: pkg/payments.go:145 pkg/expenses.go:343 pkg/expenses.go:510
|
||||
#: pkg/invoices.go:177 pkg/invoices.go:877 pkg/invoices.go:1462
|
||||
#: pkg/contacts.go:154 pkg/contacts.go:362
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:521 pkg/invoices.go:181
|
||||
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:520 pkg/invoices.go:181
|
||||
#: pkg/contacts.go:158
|
||||
msgctxt "input"
|
||||
msgid "Tags Condition"
|
||||
msgstr "Condició de les etiquetes"
|
||||
|
||||
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:525 pkg/invoices.go:185
|
||||
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:524 pkg/invoices.go:185
|
||||
#: pkg/contacts.go:162
|
||||
msgctxt "tag condition"
|
||||
msgid "All"
|
||||
msgstr "Totes"
|
||||
|
||||
#: pkg/products.go:186 pkg/expenses.go:526 pkg/invoices.go:186
|
||||
#: pkg/products.go:186 pkg/expenses.go:525 pkg/invoices.go:186
|
||||
#: pkg/contacts.go:163
|
||||
msgid "Invoices must have all the specified labels."
|
||||
msgstr "Les factures han de tenir totes les etiquetes."
|
||||
|
||||
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:530 pkg/invoices.go:190
|
||||
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:529 pkg/invoices.go:190
|
||||
#: pkg/contacts.go:167
|
||||
msgctxt "tag condition"
|
||||
msgid "Any"
|
||||
msgstr "Qualsevol"
|
||||
|
||||
#: pkg/products.go:191 pkg/expenses.go:531 pkg/invoices.go:191
|
||||
#: pkg/products.go:191 pkg/expenses.go:530 pkg/invoices.go:191
|
||||
#: pkg/contacts.go:168
|
||||
msgid "Invoices must have at least one of the specified labels."
|
||||
msgstr "Les factures han de tenir com a mínim una de les etiquetes."
|
||||
|
||||
#: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1161
|
||||
#: pkg/products.go:282 pkg/quote.go:915 pkg/payments.go:117
|
||||
#: pkg/invoices.go:1161
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripció"
|
||||
|
@ -1062,7 +1156,7 @@ msgctxt "input"
|
|||
msgid "Quotation Status"
|
||||
msgstr "Estat del pressupost"
|
||||
|
||||
#: pkg/quote.go:154 pkg/expenses.go:516 pkg/invoices.go:157
|
||||
#: pkg/quote.go:154 pkg/expenses.go:515 pkg/invoices.go:157
|
||||
msgid "All status"
|
||||
msgstr "Tots els estats"
|
||||
|
||||
|
@ -1071,12 +1165,12 @@ msgctxt "input"
|
|||
msgid "Quotation Number"
|
||||
msgstr "Número de pressupost"
|
||||
|
||||
#: pkg/quote.go:164 pkg/expenses.go:501 pkg/invoices.go:167
|
||||
#: pkg/quote.go:164 pkg/expenses.go:500 pkg/invoices.go:167
|
||||
msgctxt "input"
|
||||
msgid "From Date"
|
||||
msgstr "A partir de la data"
|
||||
|
||||
#: pkg/quote.go:169 pkg/expenses.go:506 pkg/invoices.go:172
|
||||
#: pkg/quote.go:169 pkg/expenses.go:505 pkg/invoices.go:172
|
||||
msgctxt "input"
|
||||
msgid "To Date"
|
||||
msgstr "Fins la data"
|
||||
|
@ -1097,8 +1191,8 @@ msgstr "pressuposts.zip"
|
|||
msgid "quotations.ods"
|
||||
msgstr "pressuposts.ods"
|
||||
|
||||
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:720
|
||||
#: pkg/expenses.go:750 pkg/invoices.go:684 pkg/invoices.go:1437
|
||||
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:719
|
||||
#: pkg/expenses.go:749 pkg/invoices.go:684 pkg/invoices.go:1437
|
||||
#: pkg/invoices.go:1445
|
||||
msgid "Invalid action"
|
||||
msgstr "Acció invàlida."
|
||||
|
@ -1218,6 +1312,101 @@ msgstr "La confirmació no és igual a la contrasenya."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "Heu seleccionat un idioma que no és vàlid."
|
||||
|
||||
#: pkg/payments.go:123 pkg/expenses.go:305 pkg/invoices.go:866
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Data de factura"
|
||||
|
||||
#: pkg/payments.go:129
|
||||
msgctxt "input"
|
||||
msgid "Account"
|
||||
msgstr "Compte"
|
||||
|
||||
#: pkg/payments.go:135 pkg/expenses.go:320
|
||||
msgctxt "input"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: pkg/payments.go:152
|
||||
msgid "Select an account."
|
||||
msgstr "Escolliu un compte."
|
||||
|
||||
#: pkg/payments.go:198
|
||||
msgid "Description can not be empty."
|
||||
msgstr "No podeu deixar la descripció en blanc."
|
||||
|
||||
#: pkg/payments.go:199
|
||||
msgid "Selected payment account is not valid."
|
||||
msgstr "Heu seleccionat un compte de pagament que no és vàlid."
|
||||
|
||||
#: pkg/payments.go:200
|
||||
msgid "Payment date must be a valid date."
|
||||
msgstr "La data de pagament ha de ser vàlida."
|
||||
|
||||
#: pkg/payments.go:201 pkg/expenses.go:381
|
||||
msgid "Amount can not be empty."
|
||||
msgstr "No podeu deixar l’import en blanc."
|
||||
|
||||
#: pkg/payments.go:202 pkg/expenses.go:382
|
||||
msgid "Amount must be a number greater than zero."
|
||||
msgstr "L’import ha de ser un número major a zero."
|
||||
|
||||
#: pkg/accounts.go:129
|
||||
msgctxt "input"
|
||||
msgid "Type"
|
||||
msgstr "Tipus"
|
||||
|
||||
#: pkg/accounts.go:144 pkg/contacts.go:352
|
||||
msgctxt "input"
|
||||
msgid "IBAN"
|
||||
msgstr "IBAN"
|
||||
|
||||
#: pkg/accounts.go:150
|
||||
msgctxt "input"
|
||||
msgid "Card’s last four digits"
|
||||
msgstr "Els quatre darrers dígits de la targeta"
|
||||
|
||||
#: pkg/accounts.go:161
|
||||
msgctxt "input"
|
||||
msgid "Expiration date"
|
||||
msgstr "Data de caducitat"
|
||||
|
||||
#: pkg/accounts.go:227
|
||||
msgid "Selected payment account type is not valid."
|
||||
msgstr "Heu seleccionat un tipus de compte de pagament que no és vàlid."
|
||||
|
||||
#: pkg/accounts.go:230
|
||||
msgid "IBAN can not be empty."
|
||||
msgstr "No podeu deixar l’IBAN en blanc."
|
||||
|
||||
#: pkg/accounts.go:231
|
||||
msgid "This value is not a valid IBAN."
|
||||
msgstr "Aquest valor no és un IBAN vàlid."
|
||||
|
||||
#: pkg/accounts.go:234
|
||||
msgid "Last four digits can not be empty."
|
||||
msgstr "No podeu deixar el quatre darrers dígits en blanc."
|
||||
|
||||
#: pkg/accounts.go:235
|
||||
msgid "You must enter the card’s last four digits"
|
||||
msgstr "Heu d’entrar els quatre darrers dígits de la targeta"
|
||||
|
||||
#: pkg/accounts.go:236
|
||||
msgid "Last four digits must be a number."
|
||||
msgstr "El quatre darrera dígits han de ser un número."
|
||||
|
||||
#: pkg/accounts.go:239
|
||||
msgid "Expiration date can not be empty."
|
||||
msgstr "No podeu deixar la data de pagament en blanc."
|
||||
|
||||
#: pkg/accounts.go:241
|
||||
msgid "Expiration date should be a valid date in format MM/YY (e.g., 08/24)."
|
||||
msgstr "La data de caducitat has de ser vàlida i en format MM/AA (p. ex., 08/24)."
|
||||
|
||||
#: pkg/accounts.go:245
|
||||
msgid "Payment account name can not be empty."
|
||||
msgstr "No podeu deixar el nom del compte de pagament en blanc."
|
||||
|
||||
#: pkg/dashboard.go:138
|
||||
msgctxt "input"
|
||||
msgid "Period"
|
||||
|
@ -1257,7 +1446,7 @@ msgstr "Any anterior"
|
|||
msgid "Select a contact."
|
||||
msgstr "Escolliu un contacte."
|
||||
|
||||
#: pkg/expenses.go:294 pkg/expenses.go:490
|
||||
#: pkg/expenses.go:294 pkg/expenses.go:489
|
||||
msgctxt "input"
|
||||
msgid "Contact"
|
||||
msgstr "Contacte"
|
||||
|
@ -1267,22 +1456,12 @@ msgctxt "input"
|
|||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:305 pkg/invoices.go:866
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Data de factura"
|
||||
|
||||
#: pkg/expenses.go:320
|
||||
msgctxt "input"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: pkg/expenses.go:331 pkg/invoices.go:888
|
||||
msgctxt "input"
|
||||
msgid "File"
|
||||
msgstr "Fitxer"
|
||||
|
||||
#: pkg/expenses.go:337 pkg/expenses.go:515
|
||||
#: pkg/expenses.go:337 pkg/expenses.go:514
|
||||
msgctxt "input"
|
||||
msgid "Expense Status"
|
||||
msgstr "Estat de la despesa"
|
||||
|
@ -1295,28 +1474,20 @@ msgstr "Heu seleccionat un contacte que no és vàlid."
|
|||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La data de facturació ha de ser vàlida."
|
||||
|
||||
#: pkg/expenses.go:381
|
||||
msgid "Amount can not be empty."
|
||||
msgstr "No podeu deixar l’import en blanc."
|
||||
|
||||
#: pkg/expenses.go:382
|
||||
msgid "Amount must be a number greater than zero."
|
||||
msgstr "L’import ha de ser un número major a zero."
|
||||
|
||||
#: pkg/expenses.go:384
|
||||
msgid "Selected expense status is not valid."
|
||||
msgstr "Heu seleccionat un estat de despesa que no és vàlid."
|
||||
|
||||
#: pkg/expenses.go:491
|
||||
#: pkg/expenses.go:490
|
||||
msgid "All contacts"
|
||||
msgstr "Tots els contactes"
|
||||
|
||||
#: pkg/expenses.go:496 pkg/invoices.go:162
|
||||
#: pkg/expenses.go:495 pkg/invoices.go:162
|
||||
msgctxt "input"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:748
|
||||
#: pkg/expenses.go:747
|
||||
msgid "expenses.ods"
|
||||
msgstr "despeses.ods"
|
||||
|
||||
|
@ -1364,11 +1535,6 @@ msgctxt "input"
|
|||
msgid "Need to input tax details"
|
||||
msgstr "Necessito poder facturar aquest contacte"
|
||||
|
||||
#: pkg/contacts.go:352
|
||||
msgctxt "input"
|
||||
msgid "IBAN"
|
||||
msgstr "IBAN"
|
||||
|
||||
#: pkg/contacts.go:357
|
||||
msgctxt "bic"
|
||||
msgid "BIC"
|
||||
|
@ -1445,10 +1611,6 @@ msgstr "Fitxer Excel del Holded"
|
|||
#~ msgid "Product ID can not be empty."
|
||||
#~ msgstr "No podeu deixar l’identificador del producte en blanc."
|
||||
|
||||
#~ msgctxt "input"
|
||||
#~ msgid "Number"
|
||||
#~ msgstr "Número"
|
||||
|
||||
#~ msgctxt "title"
|
||||
#~ msgid "Label"
|
||||
#~ msgstr "Etiqueta"
|
||||
|
|
270
po/es.po
270
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2024-07-20 22:51+0200\n"
|
||||
"POT-Creation-Date: 2024-08-10 04:08+0200\n"
|
||||
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
|
||||
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
|
||||
"Language-Team: Spanish <es@tp.org.es>\n"
|
||||
|
@ -35,6 +35,11 @@ msgstr "Añadir productos a la factura"
|
|||
#: web/template/expenses/index.gohtml:10 web/template/expenses/edit.gohtml:10
|
||||
#: web/template/tax-details.gohtml:9 web/template/products/new.gohtml:9
|
||||
#: web/template/products/index.gohtml:9 web/template/products/edit.gohtml:10
|
||||
#: web/template/payments/new.gohtml:10 web/template/payments/index.gohtml:10
|
||||
#: web/template/payments/edit.gohtml:10
|
||||
#: web/template/payments/accounts/new.gohtml:10
|
||||
#: web/template/payments/accounts/index.gohtml:10
|
||||
#: web/template/payments/accounts/edit.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "Home"
|
||||
msgstr "Inicio"
|
||||
|
@ -61,6 +66,7 @@ msgstr "Todos"
|
|||
#: web/template/invoices/products.gohtml:49
|
||||
#: web/template/switch-company.gohtml:22 web/template/quotes/products.gohtml:49
|
||||
#: web/template/products/index.gohtml:45
|
||||
#: web/template/payments/accounts/index.gohtml:25
|
||||
msgctxt "title"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
@ -107,7 +113,7 @@ msgstr "Subtotal"
|
|||
#: web/template/quotes/new.gohtml:74 web/template/quotes/view.gohtml:82
|
||||
#: web/template/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:75
|
||||
#: web/template/expenses/new.gohtml:47 web/template/expenses/index.gohtml:74
|
||||
#: web/template/expenses/edit.gohtml:49
|
||||
#: web/template/expenses/edit.gohtml:49 web/template/payments/index.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
@ -115,6 +121,8 @@ msgstr "Total"
|
|||
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93
|
||||
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
|
||||
#: web/template/expenses/new.gohtml:57 web/template/expenses/edit.gohtml:59
|
||||
#: web/template/payments/edit.gohtml:35
|
||||
#: web/template/payments/accounts/edit.gohtml:38
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
msgstr "Actualizar"
|
||||
|
@ -124,6 +132,8 @@ msgstr "Actualizar"
|
|||
#: web/template/contacts/new.gohtml:49 web/template/contacts/edit.gohtml:53
|
||||
#: web/template/expenses/new.gohtml:60 web/template/expenses/edit.gohtml:62
|
||||
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
|
||||
#: web/template/payments/new.gohtml:33
|
||||
#: web/template/payments/accounts/new.gohtml:41
|
||||
msgctxt "action"
|
||||
msgid "Save"
|
||||
msgstr "Guardad"
|
||||
|
@ -180,14 +190,14 @@ msgid "Customer"
|
|||
msgstr "Cliente"
|
||||
|
||||
#: web/template/invoices/index.gohtml:70 web/template/quotes/index.gohtml:70
|
||||
#: web/template/expenses/index.gohtml:68
|
||||
#: web/template/expenses/index.gohtml:68 web/template/payments/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estado"
|
||||
|
||||
#: web/template/invoices/index.gohtml:71 web/template/quotes/index.gohtml:71
|
||||
#: web/template/contacts/index.gohtml:50 web/template/expenses/index.gohtml:69
|
||||
#: web/template/products/index.gohtml:46
|
||||
#: web/template/products/index.gohtml:46 web/template/payments/index.gohtml:28
|
||||
msgctxt "title"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
@ -326,7 +336,7 @@ msgctxt "input"
|
|||
msgid "(Max. %s)"
|
||||
msgstr "(Máx. %s)"
|
||||
|
||||
#: web/template/form.gohtml:200
|
||||
#: web/template/form.gohtml:202
|
||||
msgctxt "action"
|
||||
msgid "Filters"
|
||||
msgstr "Filtrar"
|
||||
|
@ -489,15 +499,20 @@ msgstr "Gastos"
|
|||
|
||||
#: web/template/app.gohtml:57
|
||||
msgctxt "nav"
|
||||
msgid "Payments"
|
||||
msgstr "Pagos"
|
||||
|
||||
#: web/template/app.gohtml:58
|
||||
msgctxt "nav"
|
||||
msgid "Products"
|
||||
msgstr "Productos"
|
||||
|
||||
#: web/template/app.gohtml:58
|
||||
#: web/template/app.gohtml:59
|
||||
msgctxt "nav"
|
||||
msgid "Contacts"
|
||||
msgstr "Contactos"
|
||||
|
||||
#: web/template/app.gohtml:66
|
||||
#: web/template/app.gohtml:67
|
||||
msgid "<a href=\"https://numerus.cat/\">Numerus</a> Version: %s"
|
||||
msgstr "<a href=\"https://numerus.cat/\">Numerus</a> versión: %s"
|
||||
|
||||
|
@ -731,6 +746,84 @@ msgctxt "title"
|
|||
msgid "Edit Product “%s”"
|
||||
msgstr "Edición del producto «%s»"
|
||||
|
||||
#: web/template/payments/new.gohtml:3 web/template/payments/new.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "New Payment"
|
||||
msgstr "Nuevo pago"
|
||||
|
||||
#: web/template/payments/new.gohtml:11 web/template/payments/index.gohtml:3
|
||||
#: web/template/payments/index.gohtml:11 web/template/payments/edit.gohtml:11
|
||||
msgctxt "title"
|
||||
msgid "Payments"
|
||||
msgstr "Pagos"
|
||||
|
||||
#: web/template/payments/index.gohtml:16
|
||||
msgctxt "action"
|
||||
msgid "New payment"
|
||||
msgstr "Nuevo pago"
|
||||
|
||||
#: web/template/payments/index.gohtml:25
|
||||
msgctxt "title"
|
||||
msgid "Payment Date"
|
||||
msgstr "Fecha del pago"
|
||||
|
||||
#: web/template/payments/index.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: web/template/payments/index.gohtml:54
|
||||
msgid "No payments added yet."
|
||||
msgstr "No hay pagos."
|
||||
|
||||
#: web/template/payments/edit.gohtml:3
|
||||
msgctxt "title"
|
||||
msgid "Edit Payment “%s”"
|
||||
msgstr "Edición del pago «%s»"
|
||||
|
||||
#: web/template/payments/accounts/new.gohtml:3
|
||||
#: web/template/payments/accounts/new.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "New Payment Account"
|
||||
msgstr "Nueva cuenta de pago"
|
||||
|
||||
#: web/template/payments/accounts/new.gohtml:11
|
||||
#: web/template/payments/accounts/index.gohtml:3
|
||||
#: web/template/payments/accounts/index.gohtml:11
|
||||
#: web/template/payments/accounts/edit.gohtml:11
|
||||
msgctxt "title"
|
||||
msgid "Payment Accounts"
|
||||
msgstr "Cuenta de pago"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:16
|
||||
msgctxt "action"
|
||||
msgid "New payment account"
|
||||
msgstr "Nuevo cuenta de pago"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:28
|
||||
msgctxt "title"
|
||||
msgid "Expiration Date"
|
||||
msgstr "Fecha de caducidad"
|
||||
|
||||
#: web/template/payments/accounts/index.gohtml:51
|
||||
msgid "No payment accounts added yet."
|
||||
msgstr "No hay cuentas de pago."
|
||||
|
||||
#: web/template/payments/accounts/edit.gohtml:3
|
||||
msgctxt "title"
|
||||
msgid "Edit Payment Account “%s”"
|
||||
msgstr "Edición de la cuenta de pago «%s»"
|
||||
|
||||
#: pkg/ods.go:62 pkg/ods.go:106
|
||||
msgctxt "title"
|
||||
msgid "VAT number"
|
||||
|
@ -762,49 +855,50 @@ msgstr "No podéis dejar la contraseña en blanco."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nombre de usuario o contraseña inválido."
|
||||
|
||||
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901
|
||||
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901 pkg/accounts.go:138
|
||||
#: pkg/invoices.go:1147 pkg/contacts.go:149 pkg/contacts.go:262
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
|
||||
#: pkg/expenses.go:343 pkg/expenses.go:511 pkg/invoices.go:177
|
||||
#: pkg/invoices.go:877 pkg/invoices.go:1462 pkg/contacts.go:154
|
||||
#: pkg/contacts.go:362
|
||||
#: pkg/payments.go:145 pkg/expenses.go:343 pkg/expenses.go:510
|
||||
#: pkg/invoices.go:177 pkg/invoices.go:877 pkg/invoices.go:1462
|
||||
#: pkg/contacts.go:154 pkg/contacts.go:362
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:521 pkg/invoices.go:181
|
||||
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:520 pkg/invoices.go:181
|
||||
#: pkg/contacts.go:158
|
||||
msgctxt "input"
|
||||
msgid "Tags Condition"
|
||||
msgstr "Condición de las etiquetas"
|
||||
|
||||
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:525 pkg/invoices.go:185
|
||||
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:524 pkg/invoices.go:185
|
||||
#: pkg/contacts.go:162
|
||||
msgctxt "tag condition"
|
||||
msgid "All"
|
||||
msgstr "Todas"
|
||||
|
||||
#: pkg/products.go:186 pkg/expenses.go:526 pkg/invoices.go:186
|
||||
#: pkg/products.go:186 pkg/expenses.go:525 pkg/invoices.go:186
|
||||
#: pkg/contacts.go:163
|
||||
msgid "Invoices must have all the specified labels."
|
||||
msgstr "Las facturas deben tener todas las etiquetas."
|
||||
|
||||
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:530 pkg/invoices.go:190
|
||||
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:529 pkg/invoices.go:190
|
||||
#: pkg/contacts.go:167
|
||||
msgctxt "tag condition"
|
||||
msgid "Any"
|
||||
msgstr "Cualquiera"
|
||||
|
||||
#: pkg/products.go:191 pkg/expenses.go:531 pkg/invoices.go:191
|
||||
#: pkg/products.go:191 pkg/expenses.go:530 pkg/invoices.go:191
|
||||
#: pkg/contacts.go:168
|
||||
msgid "Invoices must have at least one of the specified labels."
|
||||
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
|
||||
|
||||
#: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1161
|
||||
#: pkg/products.go:282 pkg/quote.go:915 pkg/payments.go:117
|
||||
#: pkg/invoices.go:1161
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
@ -1062,7 +1156,7 @@ msgctxt "input"
|
|||
msgid "Quotation Status"
|
||||
msgstr "Estado del presupuesto"
|
||||
|
||||
#: pkg/quote.go:154 pkg/expenses.go:516 pkg/invoices.go:157
|
||||
#: pkg/quote.go:154 pkg/expenses.go:515 pkg/invoices.go:157
|
||||
msgid "All status"
|
||||
msgstr "Todos los estados"
|
||||
|
||||
|
@ -1071,12 +1165,12 @@ msgctxt "input"
|
|||
msgid "Quotation Number"
|
||||
msgstr "Número de presupuesto"
|
||||
|
||||
#: pkg/quote.go:164 pkg/expenses.go:501 pkg/invoices.go:167
|
||||
#: pkg/quote.go:164 pkg/expenses.go:500 pkg/invoices.go:167
|
||||
msgctxt "input"
|
||||
msgid "From Date"
|
||||
msgstr "A partir de la fecha"
|
||||
|
||||
#: pkg/quote.go:169 pkg/expenses.go:506 pkg/invoices.go:172
|
||||
#: pkg/quote.go:169 pkg/expenses.go:505 pkg/invoices.go:172
|
||||
msgctxt "input"
|
||||
msgid "To Date"
|
||||
msgstr "Hasta la fecha"
|
||||
|
@ -1097,8 +1191,8 @@ msgstr "presupuestos.zip"
|
|||
msgid "quotations.ods"
|
||||
msgstr "presupuestos.ods"
|
||||
|
||||
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:720
|
||||
#: pkg/expenses.go:750 pkg/invoices.go:684 pkg/invoices.go:1437
|
||||
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:719
|
||||
#: pkg/expenses.go:749 pkg/invoices.go:684 pkg/invoices.go:1437
|
||||
#: pkg/invoices.go:1445
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción inválida."
|
||||
|
@ -1218,6 +1312,101 @@ msgstr "La confirmación no corresponde con la contraseña."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "Habéis escogido un idioma que no es válido."
|
||||
|
||||
#: pkg/payments.go:123 pkg/expenses.go:305 pkg/invoices.go:866
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Fecha de factura"
|
||||
|
||||
#: pkg/payments.go:129
|
||||
msgctxt "input"
|
||||
msgid "Account"
|
||||
msgstr "Cuenta"
|
||||
|
||||
#: pkg/payments.go:135 pkg/expenses.go:320
|
||||
msgctxt "input"
|
||||
msgid "Amount"
|
||||
msgstr "Importe"
|
||||
|
||||
#: pkg/payments.go:152
|
||||
msgid "Select an account."
|
||||
msgstr "Escoged una cuenta."
|
||||
|
||||
#: pkg/payments.go:198
|
||||
msgid "Description can not be empty."
|
||||
msgstr "No podéis dejar la descripción en blanco."
|
||||
|
||||
#: pkg/payments.go:199
|
||||
msgid "Selected payment account is not valid."
|
||||
msgstr "Habéis escogido una cuenta de pago que no es válida."
|
||||
|
||||
#: pkg/payments.go:200
|
||||
msgid "Payment date must be a valid date."
|
||||
msgstr "La fecha de pago debe ser válida."
|
||||
|
||||
#: pkg/payments.go:201 pkg/expenses.go:381
|
||||
msgid "Amount can not be empty."
|
||||
msgstr "No podéis dejar el importe en blanco."
|
||||
|
||||
#: pkg/payments.go:202 pkg/expenses.go:382
|
||||
msgid "Amount must be a number greater than zero."
|
||||
msgstr "El importe tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/accounts.go:129
|
||||
msgctxt "input"
|
||||
msgid "Type"
|
||||
msgstr "Tipo"
|
||||
|
||||
#: pkg/accounts.go:144 pkg/contacts.go:352
|
||||
msgctxt "input"
|
||||
msgid "IBAN"
|
||||
msgstr "IBAN"
|
||||
|
||||
#: pkg/accounts.go:150
|
||||
msgctxt "input"
|
||||
msgid "Card’s last four digits"
|
||||
msgstr "Últimos cuatro dígitos de la tarjeta"
|
||||
|
||||
#: pkg/accounts.go:161
|
||||
msgctxt "input"
|
||||
msgid "Expiration date"
|
||||
msgstr "Fecha de caducidad"
|
||||
|
||||
#: pkg/accounts.go:227
|
||||
msgid "Selected payment account type is not valid."
|
||||
msgstr "Habéis escogido un tipo de cuenta de pago que no es válido."
|
||||
|
||||
#: pkg/accounts.go:230
|
||||
msgid "IBAN can not be empty."
|
||||
msgstr "No podéis dejar el IBAN en blanco."
|
||||
|
||||
#: pkg/accounts.go:231
|
||||
msgid "This value is not a valid IBAN."
|
||||
msgstr "Este valor no es un IBAN válido."
|
||||
|
||||
#: pkg/accounts.go:234
|
||||
msgid "Last four digits can not be empty."
|
||||
msgstr "No podéis dejar los cuatro últimos dígitos en blanco."
|
||||
|
||||
#: pkg/accounts.go:235
|
||||
msgid "You must enter the card’s last four digits"
|
||||
msgstr "Debéis entrar los cuatro últimos dígitos de la tarjeta"
|
||||
|
||||
#: pkg/accounts.go:236
|
||||
msgid "Last four digits must be a number."
|
||||
msgstr "Los cuatro últimos dígitos tienen que ser un número."
|
||||
|
||||
#: pkg/accounts.go:239
|
||||
msgid "Expiration date can not be empty."
|
||||
msgstr "No podéis dejar la fecha de caducidad en blanco."
|
||||
|
||||
#: pkg/accounts.go:241
|
||||
msgid "Expiration date should be a valid date in format MM/YY (e.g., 08/24)."
|
||||
msgstr "La fecha de caducidad tiene que ser válida y en formato MM/AA (p. ej., 08/24)."
|
||||
|
||||
#: pkg/accounts.go:245
|
||||
msgid "Payment account name can not be empty."
|
||||
msgstr "No podéis dejar el nombre de la cuenta de pago en blanco."
|
||||
|
||||
#: pkg/dashboard.go:138
|
||||
msgctxt "input"
|
||||
msgid "Period"
|
||||
|
@ -1257,7 +1446,7 @@ msgstr "Año anterior"
|
|||
msgid "Select a contact."
|
||||
msgstr "Escoged un contacto"
|
||||
|
||||
#: pkg/expenses.go:294 pkg/expenses.go:490
|
||||
#: pkg/expenses.go:294 pkg/expenses.go:489
|
||||
msgctxt "input"
|
||||
msgid "Contact"
|
||||
msgstr "Contacto"
|
||||
|
@ -1267,22 +1456,12 @@ msgctxt "input"
|
|||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:305 pkg/invoices.go:866
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Fecha de factura"
|
||||
|
||||
#: pkg/expenses.go:320
|
||||
msgctxt "input"
|
||||
msgid "Amount"
|
||||
msgstr "Importe"
|
||||
|
||||
#: pkg/expenses.go:331 pkg/invoices.go:888
|
||||
msgctxt "input"
|
||||
msgid "File"
|
||||
msgstr "Archivo"
|
||||
|
||||
#: pkg/expenses.go:337 pkg/expenses.go:515
|
||||
#: pkg/expenses.go:337 pkg/expenses.go:514
|
||||
msgctxt "input"
|
||||
msgid "Expense Status"
|
||||
msgstr "Estado del gasto"
|
||||
|
@ -1295,28 +1474,20 @@ msgstr "Habéis escogido un contacto que no es válido."
|
|||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La fecha de factura debe ser válida."
|
||||
|
||||
#: pkg/expenses.go:381
|
||||
msgid "Amount can not be empty."
|
||||
msgstr "No podéis dejar el importe en blanco."
|
||||
|
||||
#: pkg/expenses.go:382
|
||||
msgid "Amount must be a number greater than zero."
|
||||
msgstr "El importe tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/expenses.go:384
|
||||
msgid "Selected expense status is not valid."
|
||||
msgstr "Habéis escogido un estado de gasto que no es válido."
|
||||
|
||||
#: pkg/expenses.go:491
|
||||
#: pkg/expenses.go:490
|
||||
msgid "All contacts"
|
||||
msgstr "Todos los contactos"
|
||||
|
||||
#: pkg/expenses.go:496 pkg/invoices.go:162
|
||||
#: pkg/expenses.go:495 pkg/invoices.go:162
|
||||
msgctxt "input"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:748
|
||||
#: pkg/expenses.go:747
|
||||
msgid "expenses.ods"
|
||||
msgstr "gastos.ods"
|
||||
|
||||
|
@ -1364,11 +1535,6 @@ msgctxt "input"
|
|||
msgid "Need to input tax details"
|
||||
msgstr "Necesito facturar este contacto"
|
||||
|
||||
#: pkg/contacts.go:352
|
||||
msgctxt "input"
|
||||
msgid "IBAN"
|
||||
msgstr "IBAN"
|
||||
|
||||
#: pkg/contacts.go:357
|
||||
msgctxt "bic"
|
||||
msgid "BIC"
|
||||
|
@ -1441,10 +1607,6 @@ msgstr "Archivo Excel de Holded"
|
|||
#~ msgid "Product ID can not be empty."
|
||||
#~ msgstr "No podéis dejar el identificador de producto en blanco."
|
||||
|
||||
#~ msgctxt "input"
|
||||
#~ msgid "Number"
|
||||
#~ msgstr "Número"
|
||||
|
||||
#~ msgctxt "title"
|
||||
#~ msgid "Label"
|
||||
#~ msgstr "Etiqueta"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:add_payment from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.add_payment(integer, integer, date, integer, text, text, numerus.tag_name[]);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:add_payment_account_bank from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.add_payment_account_bank(integer, text, iban);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:add_payment_account_card from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.add_payment_account_card(integer, text, text, date);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:add_payment_account_cash from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.add_payment_account_cash(integer, text);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:add_payment_account_other from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.add_payment_account_other(integer, text);
|
||||
|
||||
commit;
|
|
@ -1,10 +1,13 @@
|
|||
-- Revert numerus:available_expense_status from pg
|
||||
-- Deploy numerus:available_expense_status to pg
|
||||
-- requires: schema_numerus
|
||||
-- requires: expense_status
|
||||
-- requires: expense_status_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
delete from expense_status_i18n;
|
||||
delete from expense_status;
|
||||
delete from expense_status_i18n where expense_status = 'partial';
|
||||
delete from expense_status where expense_status = 'partial';
|
||||
|
||||
commit;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
-- Revert numerus:available_expense_status from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
delete from expense_status_i18n;
|
||||
delete from expense_status;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,10 @@
|
|||
-- Revert numerus:available_payment_account_types from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
delete from payment_account_type_i18n;
|
||||
delete from payment_account_type;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,10 @@
|
|||
-- Revert numerus:available_payment_status from pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
delete from payment_status_i18n;
|
||||
delete from payment_status;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:edit_payment from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.edit_payment(uuid, date, integer, text, text, numerus.tag_name[]);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:edit_payment_account_bank from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.edit_payment_account_bank(uuid, text, iban);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:edit_payment_account_card from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.edit_payment_account_card(uuid, text, text, date);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:edit_payment_account_cash from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.edit_payment_account_cash(uuid, text);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:edit_payment_account_other from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.edit_payment_account_other(uuid, text);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:expense_payment from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.expense_payment;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment_account from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment_account;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment_account_bank from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment_account_bank;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment_account_card from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment_account_card;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment_account_type from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment_account_type;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment_account_type_i18n from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment_account_type_i18n;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment_status from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment_status;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:payment_status_i18n from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.payment_status_i18n;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:update_expense_payment_status from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.update_expense_payment_status(integer, integer, integer);
|
||||
|
||||
commit;
|
25
sqitch.plan
25
sqitch.plan
|
@ -127,3 +127,28 @@ attach_to_invoice [schema_numerus roles invoice invoice_attachment] 2023-07-12T1
|
|||
new_expense_amount [schema_numerus] 2023-07-13T17:45:33Z jordi fita mas <jordi@tandem.blog> # Add type to return when computing new expense amounts
|
||||
compute_new_expense_amount [schema_numerus roles company tax new_expense_amount] 2023-07-13T17:34:12Z jordi fita mas <jordi@tandem.blog> # Add function to compute the taxes and total for a new expense
|
||||
parse_price [parse_price@v1] 2023-08-25T11:59:54Z jordi fita mas <jordi@tandem.blog> # Throw when subtotal is empty string
|
||||
@v2 2024-08-04T05:21:18Z jordi fita mas <jordi@tandem.blog> # Tag version 2
|
||||
|
||||
payment_account_type [roles schema_numerus] 2024-08-01T23:00:18Z jordi fita mas <jordi@tandem.blog> # Add payment_account_type enum
|
||||
payment_account_type_i18n [roles schema_numerus payment_account_type language] 2024-08-08T17:05:28Z jordi fita mas <jordi@tandem.blog> # Add relation for payment account type’s translations
|
||||
available_payment_account_types [schema_numerus payment_account_type payment_account_type_i18n] 2024-08-08T17:09:24Z jordi fita mas <jordi@tandem.blog> # Add the list of available payment account types
|
||||
payment_account [roles schema_numerus company payment_account_type extension_pgcrypto] 2024-08-01T23:11:54Z jordi fita mas <jordi@tandem.blog> # Add relation of payment account
|
||||
payment_account_bank [roles schema_numerus payment_account extension_iban] 2024-08-01T23:40:34Z jordi fita mas <jordi@tandem.blog> # Add relation for payment account bank
|
||||
payment_account_card [roles schema_numerus payment_account] 2024-08-02T00:10:54Z jordi fita mas <jordi@tandem.blog> # Add relation for payment account card
|
||||
add_payment_account_bank [roles schema_numerus payment_account payment_account_bank] 2024-08-03T00:24:18Z jordi fita mas <jordi@tandem.blog> # Add function to insert bank payment accounts
|
||||
edit_payment_account_bank [roles schema_numerus payment_account payment_account_bank extension_pgcrypto extension_iban] 2024-08-10T00:04:03Z jordi fita mas <jordi@tandem.blog> # Add function to update bank payment accounts
|
||||
add_payment_account_card [roles schema_numerus payment_account payment_account_card] 2024-08-03T00:41:18Z jordi fita mas <jordi@tandem.blog> # Add function to insert card payment accounts
|
||||
edit_payment_account_card [roles schema_numerus payment_account payment_account_card extension_pgcrypto] 2024-08-10T00:22:37Z jordi fita mas <jordi@tandem.blog> # Add function to update card payment accounts
|
||||
add_payment_account_cash [roles schema_numerus payment_account] 2024-08-03T00:49:07Z jordi fita mas <jordi@tandem.blog> # Add function to insert cash payment accounts
|
||||
edit_payment_account_cash [roles schema_numerus payment_account extension_pgcrypto] 2024-08-10T00:34:19Z jordi fita mas <jordi@tandem.blog> # Add function to update cash payment accounts
|
||||
add_payment_account_other [roles schema_numerus payment_account] 2024-08-03T00:49:15Z jordi fita mas <jordi@tandem.blog> # Add function to insert other payment accounts
|
||||
edit_payment_account_other [roles schema_numerus payment_account extension_pgcrypto] 2024-08-10T00:41:38Z jordi fita mas <jordi@tandem.blog> # Add function to update other payment accounts
|
||||
payment_status [roles schema_numerus] 2024-08-04T03:02:06Z jordi fita mas <jordi@tandem.blog> # Add relation of payment status
|
||||
payment_status_i18n [roles schema_numerus payment_status language] 2024-08-04T03:05:41Z jordi fita mas <jordi@tandem.blog> # Add relation of payment status’ translated name
|
||||
available_payment_status [schema_numerus payment_status payment_status_i18n] 2024-08-04T03:08:42Z jordi fita mas <jordi@tandem.blog> # Add the list of available payment status
|
||||
payment [roles schema_numerus company payment_account currency tag_name payment_status extension_pgcrypto] 2024-08-01T01:28:59Z jordi fita mas <jordi@tandem.blog> # Add relation for accounts payable
|
||||
expense_payment [roles schema_numerus expense payment] 2024-08-04T03:44:30Z jordi fita mas <jordi@tandem.blog> # Add relation of expense payments
|
||||
available_expense_status [available_expense_status@v2] 2024-08-04T05:24:08Z jordi fita mas <jordi@tandem.blog> # Add “partial” expense status
|
||||
update_expense_payment_status [roles schema_numerus expense payment expense_payment available_expense_status available_payment_status] 2024-08-04T06:36:00Z jordi fita mas <jordi@tandem.blog> # Add function to update payment and expense status
|
||||
add_payment [roles schema_numerus payment expense_payment company currency parse_price tag_name update_expense_payment_status] 2024-08-04T03:16:55Z jordi fita mas <jordi@tandem.blog> # Add function to insert new payments
|
||||
edit_payment [roles schema_numerus payment expense_payment currency parse_price tag_name update_expense_payment_status] 2024-08-04T03:31:45Z jordi fita mas <jordi@tandem.blog> # Add function to update payments
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
-- Test add_payment
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(17);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_function('numerus', 'add_payment', array['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]']);
|
||||
select function_lang_is('numerus', 'add_payment', array['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'plpgsql');
|
||||
select function_returns('numerus', 'add_payment', array['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'uuid');
|
||||
select isnt_definer('numerus', 'add_payment', array['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]']);
|
||||
select volatility_is('numerus', 'add_payment', array['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'add_payment', array ['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'add_payment', array ['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment', array ['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment', array ['integer', 'integer', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate expense_payment cascade;
|
||||
truncate payment cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate expense cascade;
|
||||
truncate contact cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 222)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values ( 9, 1, 'Customer 1')
|
||||
, (10, 2, 'Customer 2')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, invoice_number, contact_id, invoice_date, amount, currency_code)
|
||||
values (12, 1, 'REF123', 9, '2011-01-11', 111, 'EUR')
|
||||
, (13, 2, 'INV001', 10, '2011-01-11', 111, 'USD')
|
||||
, (14, 2, 'INV002', 10, '2022-02-22', 222, 'USD')
|
||||
, (15, 2, 'INV003', 10, '2022-02-22', 222, 'USD')
|
||||
;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, payment_account_type, name)
|
||||
values (11, 1, 'other', 'Other 1')
|
||||
, (22, 2, 'cash', 'Cash 2')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment(1, null, '2023-05-02', 11, '“Protection”', '11.11', array['tag1', 'tag2']) $$,
|
||||
'Should be able to insert a payment, unrelated to any expense, for the first company'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment(2, 13, '2023-05-03', 22, 'Payment of INV001', '1.11', array[]::tag_name[]) $$,
|
||||
'Should be able to insert a complete payment for the first expense'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment(2, 14, '2023-05-04', 22, 'First payment of INV002', '1.00', array[]::tag_name[]) $$,
|
||||
'Should be able to insert a partial payment for the second expense'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment(2, 14, '2023-05-05', 22, 'Second payment of INV002', '1.22', array[]::tag_name[]) $$,
|
||||
'Should be able to insert a partial, and final, payment for the second expense'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment(2, 15, '2023-05-06', 22, 'Partial payment of INV003', '1.11', array[]::tag_name[]) $$,
|
||||
'Should be able to insert a partial payment for the third expense'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, description, payment_date::text, payment_account_id, amount, currency_code, payment_status, tags::text, created_at from payment $$,
|
||||
$$ values (1, '“Protection”', '2023-05-02', 11, 1111, 'EUR', 'complete', '{tag1,tag2}', current_timestamp)
|
||||
, (2, 'Payment of INV001', '2023-05-03', 22, 111, 'USD', 'complete', '{}', current_timestamp)
|
||||
, (2, 'First payment of INV002', '2023-05-04', 22, 100, 'USD', 'partial', '{}', current_timestamp)
|
||||
, (2, 'Second payment of INV002', '2023-05-05', 22, 122, 'USD', 'partial', '{}', current_timestamp)
|
||||
, (2, 'Partial payment of INV003', '2023-05-06', 22, 111, 'USD', 'partial', '{}', current_timestamp)
|
||||
$$,
|
||||
'Should have created all payments'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select expense_id, description from expense_payment join payment using (payment_id) $$,
|
||||
$$ values (13, 'Payment of INV001')
|
||||
, (14, 'First payment of INV002')
|
||||
, (14, 'Second payment of INV002')
|
||||
, (15, 'Partial payment of INV003')
|
||||
$$,
|
||||
'Should have linked all expenses to payments'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select expense_id, expense_status from expense $$,
|
||||
$$ values (12, 'pending')
|
||||
, (13, 'paid')
|
||||
, (14, 'paid')
|
||||
, (15, 'partial')
|
||||
$$,
|
||||
'Should have updated the status of expenses'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,67 @@
|
|||
-- Test add_payment_account_bank
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_function('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban']);
|
||||
select function_lang_is('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban'], 'plpgsql');
|
||||
select function_returns('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban'], 'uuid');
|
||||
select isnt_definer('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban']);
|
||||
select volatility_is('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_bank', array['integer', 'text', 'iban'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account_bank cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 222)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment_account_bank(1, 'Bank A', 'ES2820958297603648596978') $$,
|
||||
'Should be able to insert a first bank payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select add_payment_account_bank(1, 'Bank 2', 'DE68500105178297336485') $$,
|
||||
'Should be able to insert a second bank payment account'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, payment_account_type::text, name, iban::text from payment_account join payment_account_bank using (payment_account_id, payment_account_type) $$,
|
||||
$$ values (1, 'bank', 'Bank A', 'ES2820958297603648596978')
|
||||
, (1, 'bank', 'Bank 2', 'DE68500105178297336485')
|
||||
$$,
|
||||
'Should have created all payment accounts'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,66 @@
|
|||
-- Test add_payment_account_card
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date']);
|
||||
select function_lang_is('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date'], 'plpgsql');
|
||||
select function_returns('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date'], 'uuid');
|
||||
select isnt_definer('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date']);
|
||||
select volatility_is('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_card', array['integer', 'text', 'text', 'date'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account_card cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 222)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment_account_card(1, 'Card A', '1234', '2024-04-04') $$,
|
||||
'Should be able to insert a first card payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select add_payment_account_card(1, 'Card 2', '2345', '2025-05-05') $$,
|
||||
'Should be able to insert a second card payment account'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, payment_account_type::text, name, last_four_digits, expiration_date::text from payment_account join payment_account_card using (payment_account_id, payment_account_type) $$,
|
||||
$$ values (1, 'card', 'Card A', '1234', '2024-04-04')
|
||||
, (1, 'card', 'Card 2', '2345', '2025-05-05')
|
||||
$$,
|
||||
'Should have created all payment accounts'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,65 @@
|
|||
-- Test add_payment_account_cash
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'add_payment_account_cash', array['integer', 'text']);
|
||||
select function_lang_is('numerus', 'add_payment_account_cash', array['integer', 'text'], 'sql');
|
||||
select function_returns('numerus', 'add_payment_account_cash', array['integer', 'text'], 'uuid');
|
||||
select isnt_definer('numerus', 'add_payment_account_cash', array['integer', 'text']);
|
||||
select volatility_is('numerus', 'add_payment_account_cash', array['integer', 'text'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'add_payment_account_cash', array['integer', 'text'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'add_payment_account_cash', array['integer', 'text'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_cash', array['integer', 'text'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_cash', array['integer', 'text'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 222)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment_account_cash(1, 'Cash A') $$,
|
||||
'Should be able to insert a first cash payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select add_payment_account_cash(1, 'Cash 2') $$,
|
||||
'Should be able to insert a second cash payment account'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, payment_account_type::text, name from payment_account $$,
|
||||
$$ values (1, 'cash', 'Cash A')
|
||||
, (1, 'cash', 'Cash 2')
|
||||
$$,
|
||||
'Should have created all payment accounts'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,65 @@
|
|||
-- Test add_payment_account_other
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'add_payment_account_other', array['integer', 'text']);
|
||||
select function_lang_is('numerus', 'add_payment_account_other', array['integer', 'text'], 'sql');
|
||||
select function_returns('numerus', 'add_payment_account_other', array['integer', 'text'], 'uuid');
|
||||
select isnt_definer('numerus', 'add_payment_account_other', array['integer', 'text']);
|
||||
select volatility_is('numerus', 'add_payment_account_other', array['integer', 'text'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'add_payment_account_other', array['integer', 'text'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'add_payment_account_other', array['integer', 'text'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_other', array['integer', 'text'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'add_payment_account_other', array['integer', 'text'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 222)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_payment_account_other(1, 'Other A') $$,
|
||||
'Should be able to insert a first payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select add_payment_account_other(1, 'Other 2') $$,
|
||||
'Should be able to insert a second payment account'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, payment_account_type::text, name from payment_account $$,
|
||||
$$ values (1, 'other', 'Other A')
|
||||
, (1, 'other', 'Other 2')
|
||||
$$,
|
||||
'Should have created all payment accounts'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,115 @@
|
|||
-- Test edit_payment
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(14);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'edit_payment', array['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]']);
|
||||
select function_lang_is('numerus', 'edit_payment', array['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'plpgsql');
|
||||
select function_returns('numerus', 'edit_payment', array['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'uuid');
|
||||
select isnt_definer('numerus', 'edit_payment', array['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]']);
|
||||
select volatility_is('numerus', 'edit_payment', array['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'edit_payment', array ['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'edit_payment', array ['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment', array ['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment', array ['uuid', 'date', 'integer', 'text', 'text', 'tag_name[]'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate expense_payment cascade;
|
||||
truncate payment cascade;
|
||||
truncate expense cascade;
|
||||
truncate contact cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values ( 9, 1, 'Customer 1')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, invoice_number, contact_id, invoice_date, amount, currency_code, expense_status)
|
||||
values (13, 1, 'INV001', 9, '2011-01-11', 111, 'EUR', 'paid')
|
||||
, (14, 1, 'INV002', 9, '2022-02-22', 222, 'EUR', 'paid')
|
||||
, (15, 1, 'INV003', 9, '2022-02-22', 333, 'EUR', 'partial')
|
||||
;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, payment_account_type, name)
|
||||
values (11, 1, 'cash', 'Cash 1')
|
||||
, (12, 1, 'cash', 'Cash 2')
|
||||
, (13, 1, 'other', 'Other')
|
||||
;
|
||||
|
||||
insert into payment (payment_id, company_id, slug, description, payment_date, payment_account_id, amount, currency_code, payment_status, tags)
|
||||
values (16, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Payment INV001', '2023-05-04', 12, 111, 'EUR', 'complete', '{tag1}')
|
||||
, (17, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'First INV002', '2023-05-05', 13, 100, 'EUR', 'partial', '{tag2}')
|
||||
, (18, 1, '3bdad7a8-4a1e-4ae0-b5c6-015e51ee0502', 'Second INV002', '2023-05-06', 13, 122, 'EUR', 'partial', '{tag1,tag3}')
|
||||
, (19, 1, '5a524bee-8311-4d13-9adf-ef6310b26990', 'Partial INV003', '2023-05-07', 11, 123, 'EUR', 'partial', '{}')
|
||||
;
|
||||
|
||||
insert into expense_payment (expense_id, payment_id)
|
||||
values (13, 16)
|
||||
, (14, 17)
|
||||
, (14, 18)
|
||||
, (15, 19)
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_payment('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', '2023-05-06', 13, 'Partial INV001', '1.00', array['tag1']) $$,
|
||||
'Should be able to change a complete payment to partial'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_payment('b57b980b-247b-4be4-a0b7-03a7819c53ae', '2023-05-07', 12, 'First INV002', '0.50', array['tag1', 'tag3']) $$,
|
||||
'Should be able to adjust a partial payment, that is still partial, and the expense now becomes partial'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_payment('5a524bee-8311-4d13-9adf-ef6310b26990', '2023-05-01', 11, 'Complete INV003', '3.33', array[]::tag_name[]) $$,
|
||||
'Should be able to complete a previously partial payment'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select description, payment_date::text, payment_account_id, amount, payment_status, tags::text from payment $$,
|
||||
$$ values ('Partial INV001', '2023-05-06', 13, 100, 'partial', '{tag1}')
|
||||
, ('First INV002', '2023-05-07', 12, 50, 'partial', '{tag1,tag3}')
|
||||
, ('Second INV002', '2023-05-06', 13, 122, 'partial', '{tag1,tag3}')
|
||||
, ('Complete INV003', '2023-05-01', 11, 333, 'complete', '{}')
|
||||
$$,
|
||||
'Should have updated all payments'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select expense_id, expense_status from expense $$,
|
||||
$$ values (13, 'partial')
|
||||
, (14, 'partial')
|
||||
, (15, 'paid')
|
||||
$$,
|
||||
'Should have updated expenses too'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,84 @@
|
|||
-- Test edit_payment_account_bank
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban']);
|
||||
select function_lang_is('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban'], 'plpgsql');
|
||||
select function_returns('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban'], 'uuid');
|
||||
select isnt_definer('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban']);
|
||||
select volatility_is('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_bank', array['uuid', 'text', 'iban'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account_bank cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, slug, payment_account_type, name)
|
||||
values (11, 1, '81c478ab-395c-44aa-a5b4-3489e5349a0b', 'bank', 'Bank 1')
|
||||
, (12, 1, '84e84788-54f1-4796-b93d-dfa2c9adc072', 'bank', 'Bank 2')
|
||||
, (13, 1, 'afb774a5-0c38-480c-bef2-fe03465071cd', 'other', 'Other')
|
||||
;
|
||||
|
||||
insert into payment_account_bank (payment_account_id, iban)
|
||||
values (11, 'MR1765838885255712383711884')
|
||||
, (12, 'UA709147419869457296562612646')
|
||||
;
|
||||
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_payment_account_bank('81c478ab-395c-44aa-a5b4-3489e5349a0b', 'Piggy Bank', 'ES2820958297603648596978') $$,
|
||||
'Should be able to edit the first bank payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select edit_payment_account_bank('84e84788-54f1-4796-b93d-dfa2c9adc072', 'Totally Secure', 'DE68500105178297336485') $$,
|
||||
'Should be able to edit the second bank payment account'
|
||||
);
|
||||
|
||||
select results_eq (
|
||||
$$ select edit_payment_account_bank('afb774a5-0c38-480c-bef2-fe03465071cd', 'Huh?', 'ES3720951423184941926287')::text $$,
|
||||
$$ values (null::text) $$,
|
||||
'Should do nothing to an account that is not of type bank'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select payment_account_id, payment_account_type::text, name, iban::text from payment_account left join payment_account_bank using (payment_account_id, payment_account_type) $$,
|
||||
$$ values (11, 'bank', 'Piggy Bank', 'ES2820958297603648596978')
|
||||
, (12, 'bank', 'Totally Secure', 'DE68500105178297336485')
|
||||
, (13, 'other', 'Other', null)
|
||||
$$,
|
||||
'Should have update all bank payment accounts'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,84 @@
|
|||
-- Test edit_payment_account_card
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date']);
|
||||
select function_lang_is('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date'], 'plpgsql');
|
||||
select function_returns('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date'], 'uuid');
|
||||
select isnt_definer('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date']);
|
||||
select volatility_is('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_card', array['uuid', 'text', 'text', 'date'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account_card cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, slug, payment_account_type, name)
|
||||
values (11, 1, '81c478ab-395c-44aa-a5b4-3489e5349a0b', 'card', 'Card 1')
|
||||
, (12, 1, '84e84788-54f1-4796-b93d-dfa2c9adc072', 'card', 'Card 2')
|
||||
, (13, 1, 'afb774a5-0c38-480c-bef2-fe03465071cd', 'other', 'Other')
|
||||
;
|
||||
|
||||
insert into payment_account_card (payment_account_id, last_four_digits, expiration_date)
|
||||
values (11, '1234', '2022-04-02')
|
||||
, (12, '2345', '2024-05-02')
|
||||
;
|
||||
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_payment_account_card('81c478ab-395c-44aa-a5b4-3489e5349a0b', 'Gold Card', '9999', '2022-12-22') $$,
|
||||
'Should be able to edit the first card payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select edit_payment_account_card('84e84788-54f1-4796-b93d-dfa2c9adc072', 'Scurvy Card', '7777', '2025-08-25') $$,
|
||||
'Should be able to edit the second card payment account'
|
||||
);
|
||||
|
||||
select results_eq (
|
||||
$$ select edit_payment_account_card('afb774a5-0c38-480c-bef2-fe03465071cd', 'Huh?', '1234', '2006-01-02')::text $$,
|
||||
$$ values (null::text) $$,
|
||||
'Should do nothing to an account that is not of type card'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select payment_account_id, payment_account_type::text, name, last_four_digits, expiration_date::text from payment_account left join payment_account_card using (payment_account_id, payment_account_type) $$,
|
||||
$$ values (11, 'card', 'Gold Card', '9999', '2022-12-22')
|
||||
, (12, 'card', 'Scurvy Card', '7777', '2025-08-25')
|
||||
, (13, 'other', 'Other', null::text, null::text)
|
||||
$$,
|
||||
'Should have update all card payment accounts'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,77 @@
|
|||
-- Test edit_payment_account_cash
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'edit_payment_account_cash', array['uuid', 'text']);
|
||||
select function_lang_is('numerus', 'edit_payment_account_cash', array['uuid', 'text'], 'sql');
|
||||
select function_returns('numerus', 'edit_payment_account_cash', array['uuid', 'text'], 'uuid');
|
||||
select isnt_definer('numerus', 'edit_payment_account_cash', array['uuid', 'text']);
|
||||
select volatility_is('numerus', 'edit_payment_account_cash', array['uuid', 'text'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'edit_payment_account_cash', array['uuid', 'text'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'edit_payment_account_cash', array['uuid', 'text'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_cash', array['uuid', 'text'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_cash', array['uuid', 'text'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, slug, payment_account_type, name)
|
||||
values (11, 1, '81c478ab-395c-44aa-a5b4-3489e5349a0b', 'cash', 'Cash 1')
|
||||
, (12, 1, '84e84788-54f1-4796-b93d-dfa2c9adc072', 'cash', 'Cash 2')
|
||||
, (13, 1, 'afb774a5-0c38-480c-bef2-fe03465071cd', 'other', 'Other')
|
||||
;
|
||||
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_payment_account_cash('81c478ab-395c-44aa-a5b4-3489e5349a0b', 'My stash') $$,
|
||||
'Should be able to edit the first cash payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select edit_payment_account_cash('84e84788-54f1-4796-b93d-dfa2c9adc072', 'Under mattress') $$,
|
||||
'Should be able to edit the second cash payment account'
|
||||
);
|
||||
|
||||
select results_eq (
|
||||
$$ select edit_payment_account_cash('afb774a5-0c38-480c-bef2-fe03465071cd', 'Huh?')::text $$,
|
||||
$$ values (null::text) $$,
|
||||
'Should do nothing to an account that is not of type cash'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select payment_account_id, payment_account_type::text, name from payment_account $$,
|
||||
$$ values (11, 'cash', 'My stash')
|
||||
, (12, 'cash', 'Under mattress')
|
||||
, (13, 'other', 'Other')
|
||||
$$,
|
||||
'Should have update all cash payment accounts'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,77 @@
|
|||
-- Test edit_payment_account_other
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(13);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'edit_payment_account_other', array['uuid', 'text']);
|
||||
select function_lang_is('numerus', 'edit_payment_account_other', array['uuid', 'text'], 'sql');
|
||||
select function_returns('numerus', 'edit_payment_account_other', array['uuid', 'text'], 'uuid');
|
||||
select isnt_definer('numerus', 'edit_payment_account_other', array['uuid', 'text']);
|
||||
select volatility_is('numerus', 'edit_payment_account_other', array['uuid', 'text'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'edit_payment_account_other', array['uuid', 'text'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'edit_payment_account_other', array['uuid', 'text'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_other', array['uuid', 'text'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'edit_payment_account_other', array['uuid', 'text'], 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (111, 1, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, slug, payment_account_type, name)
|
||||
values (11, 1, '81c478ab-395c-44aa-a5b4-3489e5349a0b', 'other', 'Other 1')
|
||||
, (12, 1, '84e84788-54f1-4796-b93d-dfa2c9adc072', 'other', 'Other 2')
|
||||
, (13, 1, 'afb774a5-0c38-480c-bef2-fe03465071cd', 'cash', 'Cash')
|
||||
;
|
||||
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_payment_account_other('81c478ab-395c-44aa-a5b4-3489e5349a0b', 'Something') $$,
|
||||
'Should be able to edit the first other payment account'
|
||||
);
|
||||
|
||||
select lives_ok (
|
||||
$$ select edit_payment_account_other('84e84788-54f1-4796-b93d-dfa2c9adc072', 'And something else') $$,
|
||||
'Should be able to edit the second other payment account'
|
||||
);
|
||||
|
||||
select results_eq (
|
||||
$$ select edit_payment_account_other('afb774a5-0c38-480c-bef2-fe03465071cd', 'Huh?')::text $$,
|
||||
$$ values (null::text) $$,
|
||||
'Should do nothing to an account that is not of type other'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select payment_account_id, payment_account_type::text, name from payment_account $$,
|
||||
$$ values (11, 'other', 'Something')
|
||||
, (12, 'other', 'And something else')
|
||||
, (13, 'cash', 'Cash')
|
||||
$$,
|
||||
'Should have update all other payment accounts'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,136 @@
|
|||
-- Test expense_payment
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(23);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('expense_payment');
|
||||
select has_pk('expense_payment');
|
||||
select col_is_pk('expense_payment', array['expense_id', 'payment_id']);
|
||||
select table_privs_are('expense_payment', 'guest', array []::text[]);
|
||||
select table_privs_are('expense_payment', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('expense_payment', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('expense_payment', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('expense_payment', 'expense_id');
|
||||
select col_is_fk('expense_payment', 'expense_id');
|
||||
select fk_ok('expense_payment', 'expense_id', 'expense', 'expense_id');
|
||||
select col_type_is('expense_payment', 'expense_id', 'integer');
|
||||
select col_not_null('expense_payment', 'expense_id');
|
||||
select col_hasnt_default('expense_payment', 'expense_id');
|
||||
|
||||
select has_column('expense_payment', 'payment_id');
|
||||
select col_is_fk('expense_payment', 'payment_id');
|
||||
select fk_ok('expense_payment', 'payment_id', 'payment', 'payment_id');
|
||||
select col_type_is('expense_payment', 'payment_id', 'integer');
|
||||
select col_not_null('expense_payment', 'payment_id');
|
||||
select col_hasnt_default('expense_payment', 'payment_id');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate expense_payment cascade;
|
||||
truncate payment cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate expense cascade;
|
||||
truncate contact cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into contact (contact_id, company_id, name)
|
||||
values ( 9, 2, 'Customer 1')
|
||||
, (10, 4, 'Customer 2')
|
||||
;
|
||||
|
||||
insert into expense (expense_id, company_id, invoice_number, contact_id, invoice_date, amount, currency_code)
|
||||
values (13, 2, 'INV001', 9, '2011-01-11', 111, 'EUR')
|
||||
, (14, 4, 'INV002', 10, '2022-02-22', 222, 'EUR')
|
||||
;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, payment_account_type, name)
|
||||
values (17, 2, 'cash', 'Cash 2')
|
||||
, (18, 4, 'cash', 'Cash 4')
|
||||
;
|
||||
|
||||
insert into payment (payment_id, company_id, description, payment_date, payment_account_id, amount, currency_code)
|
||||
values (21, 2, 'Payment INV001', '2022-01-11', 17, 111, 'EUR')
|
||||
, (22, 4, 'Payment INV002', '2022-02-23', 18, 222, 'EUR')
|
||||
;
|
||||
|
||||
insert into expense_payment (expense_id, payment_id)
|
||||
values (13, 21)
|
||||
, (14, 22)
|
||||
;
|
||||
|
||||
prepare expense_payment_data as
|
||||
select expense_id, payment_id
|
||||
from expense_payment
|
||||
order by expense_id, payment_id;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('expense_payment_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'expense_payment_data',
|
||||
$$ values (13, 21)
|
||||
$$,
|
||||
'Should only list tax of products of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'expense_payment_data',
|
||||
$$ values (14, 22)
|
||||
$$,
|
||||
'Should only list tax of products of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'expense_payment_data',
|
||||
'42501', 'permission denied for table expense_payment',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
-- Test payment
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(68);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('payment');
|
||||
select has_pk('payment');
|
||||
select table_privs_are('payment', 'guest', array []::text[]);
|
||||
select table_privs_are('payment', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('payment', 'payment_id');
|
||||
select col_type_is('payment', 'payment_id', 'integer');
|
||||
select col_not_null('payment', 'payment_id');
|
||||
select col_hasnt_default('payment', 'payment_id');
|
||||
|
||||
select has_column('payment', 'company_id');
|
||||
select col_is_fk('payment', 'company_id');
|
||||
select fk_ok('payment', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('payment', 'company_id', 'integer');
|
||||
select col_not_null('payment', 'company_id');
|
||||
select col_hasnt_default('payment', 'company_id');
|
||||
|
||||
select has_column('payment', 'slug');
|
||||
select col_is_unique('payment', 'slug');
|
||||
select col_type_is('payment', 'slug', 'uuid');
|
||||
select col_not_null('payment', 'slug');
|
||||
select col_has_default('payment', 'slug');
|
||||
select col_default_is('payment', 'slug', 'gen_random_uuid()');
|
||||
|
||||
select has_column('payment', 'description');
|
||||
select col_type_is('payment', 'description', 'text');
|
||||
select col_not_null('payment', 'description');
|
||||
select col_hasnt_default('payment', 'description');
|
||||
|
||||
select has_column('payment', 'payment_date');
|
||||
select col_type_is('payment', 'payment_date', 'date');
|
||||
select col_not_null('payment', 'payment_date');
|
||||
select col_has_default('payment', 'payment_date');
|
||||
select col_default_is('payment', 'payment_date', 'CURRENT_DATE');
|
||||
|
||||
select has_column('payment', 'payment_account_id');
|
||||
select col_is_fk('payment', 'payment_account_id');
|
||||
select fk_ok('payment', 'payment_account_id', 'payment_account', 'payment_account_id');
|
||||
select col_type_is('payment', 'payment_account_id', 'integer');
|
||||
select col_not_null('payment', 'payment_account_id');
|
||||
select col_hasnt_default('payment', 'payment_account_id');
|
||||
|
||||
select has_column('payment', 'amount');
|
||||
select col_type_is('payment', 'amount', 'integer');
|
||||
select col_not_null('payment', 'amount');
|
||||
select col_hasnt_default('payment', 'amount');
|
||||
|
||||
select has_column('payment', 'currency_code');
|
||||
select col_is_fk('payment', 'currency_code');
|
||||
select fk_ok('payment', 'currency_code', 'currency', 'currency_code');
|
||||
select col_type_is('payment', 'currency_code', 'text');
|
||||
select col_not_null('payment', 'currency_code');
|
||||
select col_hasnt_default('payment', 'currency_code');
|
||||
|
||||
select has_column('payment', 'tags');
|
||||
select col_type_is('payment', 'tags', 'tag_name[]');
|
||||
select col_not_null('payment', 'tags');
|
||||
select col_has_default('payment', 'tags');
|
||||
select col_default_is('payment', 'tags', '{}');
|
||||
|
||||
select has_column('payment', 'payment_status');
|
||||
select col_is_fk('payment', 'payment_status');
|
||||
select fk_ok('payment', 'payment_status', 'payment_status', 'payment_status');
|
||||
select col_type_is('payment', 'payment_status', 'text');
|
||||
select col_not_null('payment', 'payment_status');
|
||||
select col_has_default('payment', 'payment_status');
|
||||
select col_default_is('payment', 'payment_status', 'complete');
|
||||
|
||||
select has_column('payment', 'created_at');
|
||||
select col_type_is('payment', 'created_at', 'timestamp with time zone');
|
||||
select col_not_null('payment', 'created_at');
|
||||
select col_has_default('payment', 'created_at');
|
||||
select col_default_is('payment', 'created_at', 'CURRENT_TIMESTAMP');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment cascade;
|
||||
truncate payment_account 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, role, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, payment_account_type, name)
|
||||
values (221, 2, 'other', 'Other 2')
|
||||
, (441, 4, 'other', 'Other 4')
|
||||
;
|
||||
|
||||
insert into payment (company_id, description, payment_account_id, amount, currency_code)
|
||||
values (2, 'Payment 20001', 221, 333, 'EUR')
|
||||
, (4, 'Payment 40001', 441, 555, 'EUR')
|
||||
;
|
||||
|
||||
|
||||
prepare payment_data as
|
||||
select company_id, description
|
||||
from payment
|
||||
order by company_id, description;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('payment_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_data',
|
||||
$$ values (2, 'Payment 20001')
|
||||
$$,
|
||||
'Should only list payments from the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_data',
|
||||
$$ values (4, 'Payment 40001')
|
||||
$$,
|
||||
'Should only list payments from the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'payment_data',
|
||||
'42501', 'permission denied for table payment',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
-- Test payment_account
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(38);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('payment_account');
|
||||
select has_pk('payment_account');
|
||||
select table_privs_are('payment_account', 'guest', array []::text[]);
|
||||
select table_privs_are('payment_account', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment_account', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment_account', 'authenticator', array []::text[]);
|
||||
|
||||
|
||||
select has_column('payment_account', 'payment_account_id');
|
||||
select col_is_pk('payment_account', 'payment_account_id');
|
||||
select col_type_is('payment_account', 'payment_account_id', 'integer');
|
||||
select col_not_null('payment_account', 'payment_account_id');
|
||||
select col_hasnt_default('payment_account', 'payment_account_id');
|
||||
|
||||
select has_column('payment_account', 'company_id');
|
||||
select col_is_fk('payment_account', 'company_id');
|
||||
select fk_ok('payment_account', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('payment_account', 'company_id', 'integer');
|
||||
select col_not_null('payment_account', 'company_id');
|
||||
select col_hasnt_default('payment_account', 'company_id');
|
||||
|
||||
select has_column('payment_account', 'slug');
|
||||
select col_is_unique('payment_account', 'slug');
|
||||
select col_type_is('payment_account', 'slug', 'uuid');
|
||||
select col_not_null('payment_account', 'slug');
|
||||
select col_has_default('payment_account', 'slug');
|
||||
select col_default_is('payment_account', 'slug', 'gen_random_uuid()');
|
||||
|
||||
select has_column('payment_account', 'payment_account_type');
|
||||
select col_type_is('payment_account', 'payment_account_type', 'text');
|
||||
select col_is_fk('payment_account', 'payment_account_type');
|
||||
select fk_ok('payment_account', 'payment_account_type', 'payment_account_type', 'payment_account_type');
|
||||
select col_not_null('payment_account', 'payment_account_type');
|
||||
select col_hasnt_default('payment_account', 'payment_account_type');
|
||||
|
||||
select has_column('payment_account', 'name');
|
||||
select col_type_is('payment_account', 'name', 'text');
|
||||
select col_not_null('payment_account', 'name');
|
||||
select col_hasnt_default('payment_account', 'name');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account 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, role, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into payment_account (company_id, payment_account_type, name)
|
||||
values (2, 'bank', 'Bank A')
|
||||
, (4, 'cash', 'Cash')
|
||||
;
|
||||
|
||||
|
||||
prepare payment_account_data as
|
||||
select company_id, name
|
||||
from payment_account
|
||||
order by company_id, name;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('payment_account_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_account_data',
|
||||
$$ values (2, 'Bank A')
|
||||
$$,
|
||||
'Should only list payment_accounts from the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_account_data',
|
||||
$$ values (4, 'Cash')
|
||||
$$,
|
||||
'Should only list payment_accounts from the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'payment_account_data',
|
||||
'42501', 'permission denied for table payment_account',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select throws_ok( $$
|
||||
insert into payment_account (company_id, payment_account_type, name)
|
||||
values (2, 'cash', ' ')
|
||||
$$,
|
||||
'23514', 'new row for relation "payment_account" violates check constraint "payment_account_name_not_empty"',
|
||||
'Should not allow payment accounts with a blank name'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
-- Test payment_account_bank
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(29);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('payment_account_bank');
|
||||
select has_pk('payment_account_bank');
|
||||
select table_privs_are('payment_account_bank', 'guest', array []::text[]);
|
||||
select table_privs_are('payment_account_bank', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment_account_bank', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment_account_bank', 'authenticator', array []::text[]);
|
||||
|
||||
select col_is_fk('payment_account_bank', array['payment_account_id', 'payment_account_type']);
|
||||
select fk_ok('payment_account_bank', array['payment_account_id', 'payment_account_type'], 'payment_account', array['payment_account_id', 'payment_account_type']);
|
||||
|
||||
select has_column('payment_account_bank', 'payment_account_id');
|
||||
select col_is_pk('payment_account_bank', 'payment_account_id');
|
||||
select col_type_is('payment_account_bank', 'payment_account_id', 'integer');
|
||||
select col_not_null('payment_account_bank', 'payment_account_id');
|
||||
select col_hasnt_default('payment_account_bank', 'payment_account_id');
|
||||
|
||||
select has_column('payment_account_bank', 'payment_account_type');
|
||||
select col_type_is('payment_account_bank', 'payment_account_type', 'text');
|
||||
select col_not_null('payment_account_bank', 'payment_account_type');
|
||||
select col_has_default('payment_account_bank', 'payment_account_type');
|
||||
select col_default_is('payment_account_bank', 'payment_account_type', 'bank');
|
||||
|
||||
select has_column('payment_account_bank', 'iban');
|
||||
select col_type_is('payment_account_bank', 'iban', 'iban');
|
||||
select col_not_null('payment_account_bank', 'iban');
|
||||
select col_hasnt_default('payment_account_bank', 'iban');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account_bank cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, payment_account_type, name)
|
||||
values ( 6, 2, 'bank', 'Bank 2')
|
||||
, ( 7, 2, 'card', 'Credit card 2')
|
||||
, ( 8, 2, 'cash', 'Cash 2')
|
||||
, ( 9, 2, 'other', 'Other 2')
|
||||
, (10, 4, 'bank', 'Bank 4')
|
||||
;
|
||||
|
||||
insert into payment_account_bank (payment_account_id, iban)
|
||||
values ( 6, 'NL35INGB5262865534')
|
||||
, (10, 'MT47JQRS54557143744629565915326')
|
||||
;
|
||||
|
||||
prepare payment_account_data as
|
||||
select company_id, iban::text
|
||||
from payment_account
|
||||
join payment_account_bank using (payment_account_id)
|
||||
order by payment_account_id, iban;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('payment_account_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_account_data',
|
||||
$$ values (2, 'NL35INGB5262865534')
|
||||
$$,
|
||||
'Should only list payment_accounts of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_account_data',
|
||||
$$ values (4, 'MT47JQRS54557143744629565915326')
|
||||
$$,
|
||||
'Should only list payment_accounts of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'payment_account_data',
|
||||
'42501', 'permission denied for table payment_account',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select throws_ok( $$
|
||||
insert into payment_account_bank (payment_account_id, payment_account_type, iban)
|
||||
values (7, 'card', 'ES2820958297603648596978')
|
||||
$$,
|
||||
'23514', 'new row for relation "payment_account_bank" violates check constraint "payment_account_type_is_bank"',
|
||||
'Should not allow payment accounts of type card'
|
||||
);
|
||||
|
||||
select throws_ok( $$
|
||||
insert into payment_account_bank (payment_account_id, payment_account_type, iban)
|
||||
values (8, 'cash', 'ES2820958297603648596978')
|
||||
$$,
|
||||
'23514', 'new row for relation "payment_account_bank" violates check constraint "payment_account_type_is_bank"',
|
||||
'Should not allow payment accounts of type cash'
|
||||
);
|
||||
|
||||
select throws_ok( $$
|
||||
insert into payment_account_bank (payment_account_id, payment_account_type, iban)
|
||||
values (9, 'other', 'ES2820958297603648596978')
|
||||
$$,
|
||||
'23514', 'new row for relation "payment_account_bank" violates check constraint "payment_account_type_is_bank"',
|
||||
'Should not allow payment accounts of type other'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
-- Test payment_account_card
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(39);
|
||||
|
||||
set search_path to numerus, auth, public;
|
||||
|
||||
select has_table('payment_account_card');
|
||||
select has_pk('payment_account_card');
|
||||
select table_privs_are('payment_account_card', 'guest', array []::text[]);
|
||||
select table_privs_are('payment_account_card', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment_account_card', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('payment_account_card', 'authenticator', array []::text[]);
|
||||
|
||||
select col_is_fk('payment_account_card', array['payment_account_id', 'payment_account_type']);
|
||||
select fk_ok('payment_account_card', array['payment_account_id', 'payment_account_type'], 'payment_account', array['payment_account_id', 'payment_account_type']);
|
||||
|
||||
select has_column('payment_account_card', 'payment_account_id');
|
||||
select col_is_pk('payment_account_card', 'payment_account_id');
|
||||
select col_type_is('payment_account_card', 'payment_account_id', 'integer');
|
||||
select col_not_null('payment_account_card', 'payment_account_id');
|
||||
select col_hasnt_default('payment_account_card', 'payment_account_id');
|
||||
|
||||
select has_column('payment_account_card', 'payment_account_type');
|
||||
select col_type_is('payment_account_card', 'payment_account_type', 'text');
|
||||
select col_not_null('payment_account_card', 'payment_account_type');
|
||||
select col_has_default('payment_account_card', 'payment_account_type');
|
||||
select col_default_is('payment_account_card', 'payment_account_type', 'card');
|
||||
|
||||
select has_column('payment_account_card', 'last_four_digits');
|
||||
select col_type_is('payment_account_card', 'last_four_digits', 'text');
|
||||
select col_not_null('payment_account_card', 'last_four_digits');
|
||||
select col_hasnt_default('payment_account_card', 'last_four_digits');
|
||||
|
||||
select has_column('payment_account_card', 'expiration_date');
|
||||
select col_type_is('payment_account_card', 'expiration_date', 'date');
|
||||
select col_not_null('payment_account_card', 'expiration_date');
|
||||
select col_hasnt_default('payment_account_card', 'expiration_date');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate payment_account_card cascade;
|
||||
truncate payment_account cascade;
|
||||
truncate company_user cascade;
|
||||
truncate payment_method cascade;
|
||||
truncate company cascade;
|
||||
truncate auth."user" cascade;
|
||||
reset client_min_messages;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" deferred;
|
||||
|
||||
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
|
||||
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
|
||||
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
|
||||
;
|
||||
|
||||
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
|
||||
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
|
||||
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
|
||||
;
|
||||
|
||||
insert into payment_method (payment_method_id, company_id, name, instructions)
|
||||
values (444, 4, 'cash', 'cash')
|
||||
, (222, 2, 'cash', 'cash')
|
||||
;
|
||||
|
||||
set constraints "company_default_payment_method_id_fkey" immediate;
|
||||
|
||||
insert into company_user (company_id, user_id)
|
||||
values (2, 1)
|
||||
, (4, 5)
|
||||
;
|
||||
|
||||
insert into payment_account (payment_account_id, company_id, payment_account_type, name)
|
||||
values ( 6, 2, 'bank', 'Bank 2')
|
||||
, ( 7, 2, 'card', 'Credit card 2')
|
||||
, ( 8, 2, 'cash', 'Cash 2')
|
||||
, ( 9, 2, 'other', 'Other 2')
|
||||
, (10, 4, 'card', 'Card 4')
|
||||
;
|
||||
|
||||
insert into payment_account_card (payment_account_id, last_four_digits, expiration_date)
|
||||
values ( 7, '1234', '2024-07-09')
|
||||
, (10, '4321', '2025-09-07')
|
||||
;
|
||||
|
||||
prepare payment_account_data as
|
||||
select company_id, last_four_digits
|
||||
from payment_account
|
||||
join payment_account_card using (payment_account_id)
|
||||
order by payment_account_id, last_four_digits;
|
||||
|
||||
set role invoicer;
|
||||
select is_empty('payment_account_data', 'Should show no data when cookie is not set yet');
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_account_data',
|
||||
$$ values (2, '1234')
|
||||
$$,
|
||||
'Should only list payment_accounts of the companies where demo@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
|
||||
select bag_eq(
|
||||
'payment_account_data',
|
||||
$$ values (4, '4321')
|
||||
$$,
|
||||
'Should only list payment_accounts of the companies where admin@tandem.blog is user of'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('not-a-cookie');
|
||||
select throws_ok(
|
||||
'payment_account_data',
|
||||
'42501', 'permission denied for table payment_account',
|
||||
'Should not allow select to guest users'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select throws_ok( $$
|
||||
insert into payment_account_card (payment_account_id, payment_account_type, last_four_digits, expiration_date)
|
||||
values (6, 'bank', '1111', '2025-05-05')
|
||||
$$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "payment_account_type_is_card"',
|
||||
'Should not allow payment accounts of type bank'
|
||||
);
|
||||
|
||||
select throws_ok( $$
|
||||
insert into payment_account_card (payment_account_id, payment_account_type, last_four_digits, expiration_date)
|
||||
values (8, 'cash', '1111', '2025-05-05')
|
||||
$$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "payment_account_type_is_card"',
|
||||
'Should not allow payment accounts of type cash'
|
||||
);
|
||||
|
||||
select throws_ok( $$
|
||||
insert into payment_account_card (payment_account_id, payment_account_type, last_four_digits, expiration_date)
|
||||
values (9, 'other', '1111', '2025-05-05')
|
||||
$$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "payment_account_type_is_card"',
|
||||
'Should not allow payment accounts of type other'
|
||||
);
|
||||
|
||||
select throws_ok (
|
||||
$$ update payment_account_card set last_four_digits = 'a234' where payment_account_id = 7 $$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "last_four_digits_are_digits"',
|
||||
'Last four digits of a credit card number should be all digits'
|
||||
);
|
||||
|
||||
select throws_ok (
|
||||
$$ update payment_account_card set last_four_digits = '1a34' where payment_account_id = 7 $$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "last_four_digits_are_digits"',
|
||||
'Last four digits of a credit card number should be all digits'
|
||||
);
|
||||
|
||||
select throws_ok (
|
||||
$$ update payment_account_card set last_four_digits = '12a4' where payment_account_id = 7 $$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "last_four_digits_are_digits"',
|
||||
'Last four digits of a credit card number should be all digits'
|
||||
);
|
||||
|
||||
select throws_ok (
|
||||
$$ update payment_account_card set last_four_digits = '123a' where payment_account_id = 7 $$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "last_four_digits_are_digits"',
|
||||
'Last four digits of a credit card number should be all digits'
|
||||
);
|
||||
|
||||
select throws_ok (
|
||||
$$ update payment_account_card set last_four_digits = '12345' where payment_account_id = 7 $$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "last_four_digits_are_digits"',
|
||||
'Last four digits of a credit card number should be not have more than four digits'
|
||||
);
|
||||
|
||||
select throws_ok (
|
||||
$$ update payment_account_card set last_four_digits = '123' where payment_account_id = 7 $$,
|
||||
'23514', 'new row for relation "payment_account_card" violates check constraint "last_four_digits_are_digits"',
|
||||
'Last four digits of a credit card number should be not have less than four digits'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
-- Test payment_account_type
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(15);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_table('payment_account_type');
|
||||
select has_pk('payment_account_type' );
|
||||
select table_privs_are('payment_account_type', 'guest', array []::text[]);
|
||||
select table_privs_are('payment_account_type', 'invoicer', array ['SELECT']);
|
||||
select table_privs_are('payment_account_type', 'admin', array ['SELECT']);
|
||||
select table_privs_are('payment_account_type', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('payment_account_type', 'payment_account_type');
|
||||
select col_is_pk('payment_account_type', 'payment_account_type');
|
||||
select col_type_is('payment_account_type', 'payment_account_type', 'text');
|
||||
select col_not_null('payment_account_type', 'payment_account_type');
|
||||
select col_hasnt_default('payment_account_type', 'payment_account_type');
|
||||
|
||||
select has_column('payment_account_type', 'name');
|
||||
select col_type_is('payment_account_type', 'name', 'text');
|
||||
select col_not_null('payment_account_type', 'name');
|
||||
select col_hasnt_default('payment_account_type', 'name');
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,44 @@
|
|||
-- Test payment_account_type_i18n
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(23);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_table('payment_account_type_i18n');
|
||||
select has_pk('payment_account_type_i18n');
|
||||
select col_is_pk('payment_account_type_i18n', array['payment_account_type', 'lang_tag']);
|
||||
select table_privs_are('payment_account_type_i18n', 'guest', array []::text[]);
|
||||
select table_privs_are('payment_account_type_i18n', 'invoicer', array ['SELECT']);
|
||||
select table_privs_are('payment_account_type_i18n', 'admin', array ['SELECT']);
|
||||
select table_privs_are('payment_account_type_i18n', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('payment_account_type_i18n', 'payment_account_type');
|
||||
select col_is_fk('payment_account_type_i18n', 'payment_account_type');
|
||||
select fk_ok('payment_account_type_i18n', 'payment_account_type', 'payment_account_type', 'payment_account_type');
|
||||
select col_type_is('payment_account_type_i18n', 'payment_account_type', 'text');
|
||||
select col_not_null('payment_account_type_i18n', 'payment_account_type');
|
||||
select col_hasnt_default('payment_account_type_i18n', 'payment_account_type');
|
||||
|
||||
select has_column('payment_account_type_i18n', 'lang_tag');
|
||||
select col_is_fk('payment_account_type_i18n', 'lang_tag');
|
||||
select fk_ok('payment_account_type_i18n', 'lang_tag', 'language', 'lang_tag');
|
||||
select col_type_is('payment_account_type_i18n', 'lang_tag', 'text');
|
||||
select col_not_null('payment_account_type_i18n', 'lang_tag');
|
||||
select col_hasnt_default('payment_account_type_i18n', 'lang_tag');
|
||||
|
||||
select has_column('payment_account_type_i18n', 'name');
|
||||
select col_type_is('payment_account_type_i18n', 'name', 'text');
|
||||
select col_not_null('payment_account_type_i18n', 'name');
|
||||
select col_hasnt_default('payment_account_type_i18n', 'name');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
-- Test payment_status
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(15);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_table('payment_status');
|
||||
select has_pk('payment_status');
|
||||
select table_privs_are('payment_status', 'guest', array []::text[]);
|
||||
select table_privs_are('payment_status', 'invoicer', array ['SELECT']);
|
||||
select table_privs_are('payment_status', 'admin', array ['SELECT']);
|
||||
select table_privs_are('payment_status', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('payment_status', 'payment_status');
|
||||
select col_is_pk('payment_status', 'payment_status');
|
||||
select col_type_is('payment_status', 'payment_status', 'text');
|
||||
select col_not_null('payment_status', 'payment_status');
|
||||
select col_hasnt_default('payment_status', 'payment_status');
|
||||
|
||||
select has_column('payment_status', 'name');
|
||||
select col_type_is('payment_status', 'name', 'text');
|
||||
select col_not_null('payment_status', 'name');
|
||||
select col_hasnt_default('payment_status', 'name');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
-- Test payment_status_i18n
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(23);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_table('payment_status_i18n');
|
||||
select has_pk('payment_status_i18n');
|
||||
select col_is_pk('payment_status_i18n', array['payment_status', 'lang_tag']);
|
||||
select table_privs_are('payment_status_i18n', 'guest', array []::text[]);
|
||||
select table_privs_are('payment_status_i18n', 'invoicer', array ['SELECT']);
|
||||
select table_privs_are('payment_status_i18n', 'admin', array ['SELECT']);
|
||||
select table_privs_are('payment_status_i18n', 'authenticator', array []::text[]);
|
||||
|
||||
select has_column('payment_status_i18n', 'payment_status');
|
||||
select col_is_fk('payment_status_i18n', 'payment_status');
|
||||
select fk_ok('payment_status_i18n', 'payment_status', 'payment_status', 'payment_status');
|
||||
select col_type_is('payment_status_i18n', 'payment_status', 'text');
|
||||
select col_not_null('payment_status_i18n', 'payment_status');
|
||||
select col_hasnt_default('payment_status_i18n', 'payment_status');
|
||||
|
||||
select has_column('payment_status_i18n', 'lang_tag');
|
||||
select col_is_fk('payment_status_i18n', 'lang_tag');
|
||||
select fk_ok('payment_status_i18n', 'lang_tag', 'language', 'lang_tag');
|
||||
select col_type_is('payment_status_i18n', 'lang_tag', 'text');
|
||||
select col_not_null('payment_status_i18n', 'lang_tag');
|
||||
select col_hasnt_default('payment_status_i18n', 'lang_tag');
|
||||
|
||||
select has_column('payment_status_i18n', 'name');
|
||||
select col_type_is('payment_status_i18n', 'name', 'text');
|
||||
select col_not_null('payment_status_i18n', 'name');
|
||||
select col_hasnt_default('payment_status_i18n', 'name');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
-- Test update_expense_payment_status
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(9);
|
||||
|
||||
set search_path to numerus, public;
|
||||
|
||||
select has_function('numerus', 'update_expense_payment_status', array['integer', 'integer', 'integer']);
|
||||
select function_lang_is('numerus', 'update_expense_payment_status', array['integer', 'integer', 'integer'], 'sql');
|
||||
select function_returns('numerus', 'update_expense_payment_status', array['integer', 'integer', 'integer'], 'void');
|
||||
select isnt_definer('numerus', 'update_expense_payment_status', array['integer', 'integer', 'integer']);
|
||||
select volatility_is('numerus', 'update_expense_payment_status', array['integer', 'integer', 'integer'], 'volatile');
|
||||
|
||||
select function_privs_are('numerus', 'update_expense_payment_status', array ['integer', 'integer', 'integer'], 'guest', array []::text[]);
|
||||
select function_privs_are('numerus', 'update_expense_payment_status', array ['integer', 'integer', 'integer'], 'invoicer', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'update_expense_payment_status', array ['integer', 'integer', 'integer'], 'admin', array ['EXECUTE']);
|
||||
select function_privs_are('numerus', 'update_expense_payment_status', array ['integer', 'integer', 'integer'], 'authenticator', array []::text[]);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:add_payment on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.add_payment(integer, integer, date, integer, text, text, numerus.tag_name[])', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:add_payment_account_bank on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.add_payment_account_bank(integer, text, iban)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:add_payment_account_card on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.add_payment_account_card(integer, text, text, date)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:add_payment_account_cash on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.add_payment_account_cash(integer, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:add_payment_account_other on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.add_payment_account_other(integer, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -5,10 +5,13 @@ begin;
|
|||
set search_path to numerus;
|
||||
|
||||
select 1 / count(*) from expense_status where expense_status = 'pending' and name ='Pending';
|
||||
select 1 / count(*) from expense_status where expense_status = 'partial' and name ='Partial';
|
||||
select 1 / count(*) from expense_status where expense_status = 'paid' and name ='Paid';
|
||||
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'pending' and name ='Pendent' and lang_tag = 'ca';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'pending' and name ='Pendiente' and lang_tag = 'es';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'partial' and name ='Parcial' and lang_tag = 'ca';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'partial' and name ='Parcial' and lang_tag = 'es';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'paid' and name ='Pagada' and lang_tag= 'ca';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'paid' and name ='Pagada' and lang_tag= 'es';
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
-- Verify numerus:available_expense_status on pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
select 1 / count(*) from expense_status where expense_status = 'pending' and name ='Pending';
|
||||
select 1 / count(*) from expense_status where expense_status = 'paid' and name ='Paid';
|
||||
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'pending' and name ='Pendent' and lang_tag = 'ca';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'pending' and name ='Pendiente' and lang_tag = 'es';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'paid' and name ='Pagada' and lang_tag= 'ca';
|
||||
select 1 / count(*) from expense_status_i18n where expense_status = 'paid' and name ='Pagada' and lang_tag= 'es';
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,21 @@
|
|||
-- Verify numerus:available_payment_account_types on pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
select 1 / count(*) from payment_account_type where payment_account_type = 'bank' and name ='Bank';
|
||||
select 1 / count(*) from payment_account_type where payment_account_type = 'card' and name ='Credit Card';
|
||||
select 1 / count(*) from payment_account_type where payment_account_type = 'cash' and name ='Cash';
|
||||
select 1 / count(*) from payment_account_type where payment_account_type = 'other' and name ='Other';
|
||||
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'bank' and name ='Banc' and lang_tag = 'ca';
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'bank' and name ='Banco' and lang_tag = 'es';
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'card' and name ='Targeta de crèdit' and lang_tag= 'ca';
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'card' and name ='Tarjeta de crédito' and lang_tag= 'es';
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'cash' and name ='Efectiu' and lang_tag= 'ca';
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'cash' and name ='Efectivo' and lang_tag= 'es';
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'other' and name ='Altres' and lang_tag= 'ca';
|
||||
select 1 / count(*) from payment_account_type_i18n where payment_account_type = 'other' and name ='Otros' and lang_tag= 'es';
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,15 @@
|
|||
-- Verify numerus:available_payment_status on pg
|
||||
|
||||
BEGIN;
|
||||
|
||||
set search_path to numerus;
|
||||
|
||||
select 1 / count(*) from payment_status where payment_status = 'partial' and name ='Partial';
|
||||
select 1 / count(*) from payment_status where payment_status = 'complete' and name ='Complete';
|
||||
|
||||
select 1 / count(*) from payment_status_i18n where payment_status = 'partial' and name ='Parcial' and lang_tag = 'ca';
|
||||
select 1 / count(*) from payment_status_i18n where payment_status = 'partial' and name ='Parcial' and lang_tag = 'es';
|
||||
select 1 / count(*) from payment_status_i18n where payment_status = 'complete' and name ='Complet' and lang_tag= 'ca';
|
||||
select 1 / count(*) from payment_status_i18n where payment_status = 'complete' and name ='Completo' and lang_tag= 'es';
|
||||
|
||||
ROLLBACK;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:edit_payment on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.edit_payment(uuid, date, integer, text, text, numerus.tag_name[])', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:edit_payment_account_bank on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.edit_payment_account_bank(uuid, text, iban)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:edit_payment_account_card on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.edit_payment_account_card(uuid, text, text, date)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:edit_payment_account_cash on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.edit_payment_account_cash(uuid, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:edit_payment_account_other on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.edit_payment_account_other(uuid, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,13 @@
|
|||
-- Verify numerus:expense_payment on pg
|
||||
|
||||
begin;
|
||||
|
||||
select expense_id
|
||||
, payment_id
|
||||
from numerus.expense_payment
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.expense_payment'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.expense_payment'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,22 @@
|
|||
-- Verify numerus:payment on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_id
|
||||
, company_id
|
||||
, slug
|
||||
, description
|
||||
, payment_date
|
||||
, payment_account_id
|
||||
, amount
|
||||
, currency_code
|
||||
, tags
|
||||
, payment_status
|
||||
, created_at
|
||||
from numerus.payment
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.payment'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.payment'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,16 @@
|
|||
-- Verify numerus:payment_account on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_account_id
|
||||
, company_id
|
||||
, slug
|
||||
, payment_account_type
|
||||
, name
|
||||
from numerus.payment_account
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.payment_account'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.payment_account'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,14 @@
|
|||
-- Verify numerus:payment_account_bank on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_account_id
|
||||
, payment_account_type
|
||||
, iban
|
||||
from numerus.payment_account_bank
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.payment_account_bank'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.payment_account_bank'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,15 @@
|
|||
-- Verify numerus:payment_account_card on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_account_id
|
||||
, payment_account_type
|
||||
, last_four_digits
|
||||
, expiration_date
|
||||
from numerus.payment_account_card
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'numerus.payment_account_card'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.payment_account_card'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,10 @@
|
|||
-- Verify numerus:payment_account_type on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_account_type
|
||||
, name
|
||||
from numerus.payment_account_type
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,11 @@
|
|||
-- Verify numerus:payment_account_type_i18n on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_account_type
|
||||
, lang_tag
|
||||
, name
|
||||
from numerus.payment_account_type_i18n
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,10 @@
|
|||
-- Verify numerus:payment_status on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_status
|
||||
, name
|
||||
from numerus.payment_status
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,11 @@
|
|||
-- Verify numerus:payment_status_i18n on pg
|
||||
|
||||
begin;
|
||||
|
||||
select payment_status
|
||||
, lang_tag
|
||||
, name
|
||||
from numerus.payment_status_i18n
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:update_expense_payment_status on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.update_expense_payment_status(integer, integer, integer)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -54,6 +54,7 @@
|
|||
<li><a{{if requestURIHasPrefix (companyURI "/quotes") }} aria-current="page"{{ end }} href="{{ companyURI "/quotes" }}">{{( pgettext "Quotations" "nav" )}}</a></li>
|
||||
<li><a{{if requestURIHasPrefix (companyURI "/invoices") }} aria-current="page"{{ end }} href="{{ companyURI "/invoices" }}">{{( pgettext "Invoices" "nav" )}}</a></li>
|
||||
<li><a{{if requestURIHasPrefix (companyURI "/expenses") }} aria-current="page"{{ end }} href="{{ companyURI "/expenses" }}">{{( pgettext "Expenses" "nav" )}}</a></li>
|
||||
<li><a{{if requestURIHasPrefix (companyURI "/payments") }} aria-current="page"{{ end }} href="{{ companyURI "/payments" }}">{{( pgettext "Payments" "nav" )}}</a></li>
|
||||
<li><a{{if requestURIHasPrefix (companyURI "/products") }} aria-current="page"{{ end }} href="{{ companyURI "/products" }}">{{( pgettext "Products" "nav" )}}</a></li>
|
||||
<li><a{{if requestURIHasPrefix (companyURI "/contacts") }} aria-current="page"{{ end }} href="{{ companyURI "/contacts" }}">{{( pgettext "Contacts" "nav" )}}</a></li>
|
||||
</ul>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue