Replace tag relations with array attributes

It all started when i wanted to try to filter invoices by multiple tags
using an “AND”, instead of “OR” as it was doing until now.  But
something felt off and seemed to me that i was doing thing much more
complex than needed, all to be able to list the tags as a suggestion
in the input field—which i am not doing yet.

I found this article series[0] exploring different approaches for
tagging, which includes the one i was using, and comparing their
performance.  I have not actually tested it, but it seems that i have
chosen the worst option, in both query time and storage.

I attempted to try using an array attribute to each table, which is more
or less the same they did in the articles but without using a separate
relation for tags, and i found out that all the queries were way easier
to write, and needed two joins less, so it was a no-brainer.

[0]: http://www.databasesoup.com/2015/01/tag-all-things.html
This commit is contained in:
jordi fita mas 2023-04-07 21:31:35 +02:00
parent 4b4d7ad87d
commit bc48dd4089
59 changed files with 114 additions and 1590 deletions

View File

@ -41,7 +41,6 @@ values (1, 1, 'Retenció 15 %', -0.15)
, (1, 2, 'IVA 4 %', 0.04)
;
alter sequence tag_tag_id_seq restart;
alter sequence contact_contact_id_seq restart;
select add_contact (1, 'Melcior', '1', 'Rei Blanc', '0732621', 'melcio@reismags.cat', '', 'C/ Principal, 1', 'Shiraz', 'Fars', '1', 'IR', array['pesebre', 'mag']);
select add_contact (1, 'Gaspar', '2', 'Rei Ros', '111', 'gaspar@reismags.cat', '', 'C/ Principal, 2', 'Nova Delhi', 'Delhi', '2', 'IN', array['pesebre', 'mag']);

View File

@ -18,13 +18,11 @@ declare
cid integer;
cslug uuid;
begin
insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (add_contact.company_id, add_contact.business_name, (add_contact.country_code || add_contact.vatin)::vatin, add_contact.trade_name, parse_packed_phone_number(add_contact.phone, add_contact.country_code), add_contact.email, add_contact.web, add_contact.address, add_contact.city, add_contact.province, add_contact.postal_code, add_contact.country_code)
insert into contact (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags)
values (add_contact.company_id, add_contact.business_name, (add_contact.country_code || add_contact.vatin)::vatin, add_contact.trade_name, parse_packed_phone_number(add_contact.phone, add_contact.country_code), add_contact.email, add_contact.web, add_contact.address, add_contact.city, add_contact.province, add_contact.postal_code, add_contact.country_code, add_contact.tags)
returning contact_id, slug
into cid, cslug;
perform tag_contact(company_id, cid, tags);
return cslug;
end
$$

View File

@ -10,7 +10,6 @@
-- requires: invoice_product_tax
-- requires: next_invoice_number
-- requires: tag_name
-- requires: tag_invoice
begin;
@ -25,12 +24,13 @@ declare
ccode text;
ipid integer;
begin
insert into invoice (company_id, invoice_number, invoice_date, contact_id, notes, currency_code, payment_method_id)
insert into invoice (company_id, invoice_number, invoice_date, contact_id, notes, tags, currency_code, payment_method_id)
select company_id
, next_invoice_number(add_invoice.company, invoice_date)
, invoice_date
, contact_id
, notes
, tags
, currency_code
, add_invoice.payment_method_id
from company
@ -59,8 +59,6 @@ begin
join unnest(product.tax) as ptax(tax_id) using (tax_id);
end loop;
perform tag_invoice(company, iid, tags);
return pslug;
end;
$$

View File

@ -6,7 +6,6 @@
-- requires: company
-- requires: currency
-- requires: tag_name
-- requires: tag_product
begin;
@ -18,8 +17,8 @@ declare
pid integer;
pslug uuid;
begin
insert into product (company_id, name, description, price)
select add_product.company_id, add_product.name, add_product.description, parse_price(add_product.price, decimal_digits)
insert into product (company_id, name, description, price, tags)
select add_product.company_id, add_product.name, add_product.description, parse_price(add_product.price, decimal_digits), add_product.tags
from company
join currency using (currency_code)
where company.company_id = add_product.company_id
@ -30,8 +29,6 @@ begin
select pid, tax_id
from unnest(taxes) as tax(tax_id);
perform tag_product(company_id, pid, tags);
return pslug;
end;
$$ language plpgsql;

View File

@ -7,6 +7,7 @@
-- requires: extension_uri
-- requires: country_code
-- requires: country
-- requires: tag_name
begin;
@ -27,9 +28,12 @@ create table contact (
province text not null,
postal_code text not null,
country_code country_code not null references country,
tags tag_name[] not null default '{}',
created_at timestamptz not null default current_timestamp
);
create index on contact using gin (tags);
grant select, insert, update, delete on table contact to invoicer;
grant select, insert, update, delete on table contact to admin;

View File

@ -1,31 +0,0 @@
-- Deploy numerus:contact_tag to pg
-- requires: schema_numerus
-- requires: tag
-- requires: contact
begin;
set search_path to numerus, public;
create table contact_tag (
contact_id integer not null references contact,
tag_id integer not null references tag,
primary key (contact_id, tag_id)
);
grant select, insert, update, delete on table contact_tag to invoicer;
grant select, insert, update, delete on table contact_tag to admin;
alter table contact_tag enable row level security;
create policy company_policy
on contact_tag
using (
exists(
select 1
from contact
where contact.contact_id = contact_tag.contact_id
)
);
commit;

View File

@ -5,7 +5,6 @@
-- requires: country_code
-- requires: tag_name
-- requires: contact
-- requires: tag_contact
-- requires: extension_vat
-- requires: extension_pg_libphonenumber
@ -31,6 +30,7 @@ begin
, province = edit_contact.province
, postal_code = edit_contact.postal_code
, country_code = edit_contact.country_code
, tags = edit_contact.tags
where slug = contact_slug
returning contact_id, company_id
into cid, company
@ -40,8 +40,6 @@ begin
return null;
end if;
perform tag_contact(company, cid, tags);
return contact_slug;
end
$$

View File

@ -8,7 +8,6 @@
-- requires: invoice_product
-- requires: invoice_product_tax
-- requires: tag_name
-- requires: tag_invoice
begin;
@ -30,6 +29,7 @@ begin
, invoice_status = edit_invoice.invoice_status
, notes = edit_invoice.notes
, payment_method_id = edit_invoice.payment_method_id
, tags = edit_invoice.tags
where slug = invoice_slug
returning invoice_id, company_id, currency_code
into iid, company, ccode
@ -89,8 +89,6 @@ begin
delete from invoice_product where invoice_product_id = any(products_to_delete);
end if;
perform tag_invoice(company, iid, tags);
return invoice_slug;
end;
$$

View File

@ -6,7 +6,6 @@
-- requires: company
-- requires: currency
-- requires: tag_name
-- requires: tag_product
begin;
@ -22,6 +21,7 @@ begin
set name = edit_product.name
, description = edit_product.description
, price = parse_price(edit_product.price, decimal_digits)
, tags = edit_product.tags
from company
join currency using (currency_code)
where product.company_id = company.company_id
@ -39,8 +39,6 @@ begin
select pid, tax_id
from unnest(taxes) as tax(tax_id);
perform tag_product(company, pid, tags);
return true;
end;
$$ language plpgsql;

View File

@ -4,6 +4,7 @@
-- requires: contact
-- requires: invoice_status
-- requires: currency
-- requires: tag_name
begin;
@ -18,11 +19,14 @@ create table invoice (
contact_id integer not null references contact,
invoice_status text not null default 'created' references invoice_status,
notes text not null default '',
tags tag_name[] not null default '{}',
payment_method_id integer not null references payment_method,
currency_code text not null references currency,
created_at timestamptz not null default current_timestamp
);
create index on invoice using gin (tags);
grant select, insert, update, delete on table invoice to invoicer;
grant select, insert, update, delete on table invoice to admin;

View File

@ -1,31 +0,0 @@
-- Deploy numerus:invoice_tag to pg
-- requires: schema_numerus
-- requires: tag
-- requires: invoice
begin;
set search_path to numerus, public;
create table invoice_tag (
invoice_id integer not null references invoice,
tag_id integer not null references tag,
primary key (invoice_id, tag_id)
);
grant select, insert, update, delete on table invoice_tag to invoicer;
grant select, insert, update, delete on table invoice_tag to admin;
alter table invoice_tag enable row level security;
create policy company_policy
on invoice_tag
using (
exists(
select 1
from invoice
where invoice.invoice_id = invoice_tag.invoice_id
)
);
commit;

View File

@ -2,6 +2,7 @@
-- requires: schema_numerus
-- requires: company
-- requires: tax
-- requires: tag_name
begin;
@ -14,12 +15,15 @@ create table product (
name text not null constraint name_not_empty check(length(trim(name)) > 0),
description text not null default '',
price integer not null,
tags tag_name[] not null default '{}',
created_at timestamptz not null default current_timestamp
);
comment on column product.price is
'Price is stored in cents.';
create index on product using gin (tags);
grant select, insert, update, delete on table product to invoicer;
grant select, insert, update, delete on table product to admin;

View File

@ -1,31 +0,0 @@
-- Deploy numerus:product_tag to pg
-- requires: schema_numerus
-- requires: tag
-- requires: product
begin;
set search_path to numerus, public;
create table product_tag (
product_id integer not null references product,
tag_id integer not null references tag,
primary key (product_id, tag_id)
);
grant select, insert, update, delete on table product_tag to invoicer;
grant select, insert, update, delete on table product_tag to admin;
alter table product_tag enable row level security;
create policy company_policy
on product_tag
using (
exists(
select 1
from product
where product.product_id = product_tag.product_id
)
);
commit;

View File

@ -1,35 +0,0 @@
-- Deploy numerus:tag to pg
-- requires: schema_numerus
-- requires: tag_name
begin;
set search_path to numerus, public;
create table tag (
tag_id serial primary key,
company_id integer not null references company,
name tag_name not null,
unique (company_id, name)
);
grant select, insert, update, delete on table tag to invoicer;
grant select, insert, update, delete on table tag to admin;
grant usage on sequence tag_tag_id_seq to invoicer;
grant usage on sequence tag_tag_id_seq to admin;
alter table tag enable row level security;
create policy company_policy
on tag
using (
exists(
select 1
from company_user
join user_profile using (user_id)
where company_user.company_id = tag.company_id
)
);
commit;

View File

@ -1,22 +0,0 @@
-- Deploy numerus:tag_contact to pg
-- requires: schema_numerus
-- requires: tag_name
-- requires: tag_relation
-- requires: contact_tag
begin;
set search_path to numerus, public;
create or replace function tag_contact (company_id integer, contact_id integer, tags tag_name[]) returns void as
$$
select tag_relation('contact_tag', 'contact_id', company_id, contact_id, tags);
$$
language sql
;
revoke execute on function tag_contact(integer, integer, tag_name[]) from public;
grant execute on function tag_contact(integer, integer, tag_name[]) to invoicer;
grant execute on function tag_contact(integer, integer, tag_name[]) to admin;
commit;

View File

@ -1,21 +0,0 @@
-- Deploy numerus:tag_invoice to pg
-- requires: schema_numerus
-- requires: tag_name
-- requires: tag_invoice
-- requires: invoice_tag
begin;
set search_path to numerus, public;
create or replace function tag_invoice(company_id integer, invoice_id integer, tags tag_name[]) returns void as
$$
select tag_relation('invoice_tag', 'invoice_id', company_id, invoice_id, tags);
$$
language sql;
revoke execute on function tag_invoice(integer, integer, tag_name[]) from public;
grant execute on function tag_invoice(integer, integer, tag_name[]) to invoicer;
grant execute on function tag_invoice(integer, integer, tag_name[]) to admin;
commit;

View File

@ -1,22 +0,0 @@
-- Deploy numerus:tag_product to pg
-- requires: schema_numerus
-- requires: tag_name
-- requires: tag_relation
-- requires: product_tag
begin;
set search_path to numerus, public;
create or replace function tag_product (company_id integer, product_id integer, tags tag_name[]) returns void as
$$
select tag_relation('product_tag', 'product_id', company_id, product_id, tags);
$$
language sql
;
revoke execute on function tag_product(integer, integer, tag_name[]) from public;
grant execute on function tag_product(integer, integer, tag_name[]) to invoicer;
grant execute on function tag_product(integer, integer, tag_name[]) to admin;
commit;

View File

@ -1,32 +0,0 @@
-- Deploy numerus:tag_relation to pg
-- requires: schema_numerus
-- requires: tag
-- requires: tag_name
begin;
set search_path to numerus, public;
create or replace function tag_relation(relname regclass, attname name, company integer, rowid integer, tags tag_name[]) returns void as
$$
begin
execute format('delete from %I where %I = $1', relname, attname) USING rowid;
if array_length(tags, 1) > 0 then
insert into tag (company_id, name)
select company, new_tag.name
from unnest (tags) as new_tag(name)
on conflict (company_id, name) do nothing
;
execute format('insert into %I (%I, tag_id) select $1, tag_id from tag join unnest ($2) as new_tag(name) on company_id = $3 and tag.name = new_tag.name', relname, attname) USING rowid, tags, company;
end if;
end
$$
language plpgsql;
revoke execute on function tag_relation(regclass, name, integer, integer, tag_name[]) from public;
grant execute on function tag_relation(regclass, name, integer, integer, tag_name[]) to invoicer;
grant execute on function tag_relation(regclass, name, integer, integer, tag_name[]) to admin;
commit;

View File

@ -131,15 +131,9 @@ func mustCollectContactEntries(ctx context.Context, conn *Conn, company *Company
, business_name
, email
, phone
, array_agg(coalesce(tag.name::text, ''))
, tags
from contact
left join contact_tag using (contact_id)
left join tag using(tag_id)
where contact.company_id = $1 and (($2 = '') or (tag.name = $2))
group by slug
, business_name
, email
, phone
where contact.company_id = $1 and (($2 = '') or (tags @> array[$2]::tag_name[]))
order by business_name
`, company.Id, tag)
if err != nil {
@ -337,22 +331,9 @@ func (form *contactForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
, province
, postal_code
, country_code
, string_agg(tag.name, ',')
, array_to_string(tags, ',')
from contact
left join contact_tag using (contact_id)
left join tag using(tag_id)
where slug = $1
group by business_name
, substr(vatin::text, 3)
, trade_name
, phone
, email
, web
, address
, city
, province
, postal_code
, country_code
`, slug).Scan(
form.BusinessName,
form.VATIN,

View File

@ -79,33 +79,23 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company
maybeAppendWhere("invoice_date >= $%d", filters.FromDate.String(), nil)
maybeAppendWhere("invoice_date <= $%d", filters.ToDate.String(), nil)
if len(filters.Tags.Tags) > 0 {
appendWhere("exists (select 1 from invoice_tag join tag using (tag_id) where invoice_tag.invoice_id = invoice.invoice_id and tag.name = any($%d))", filters.Tags)
appendWhere("invoice.tags && $%d", filters.Tags)
}
rows := conn.MustQuery(ctx, fmt.Sprintf(`
select invoice.slug
, invoice_date
, invoice_number
, contact.business_name
, array_agg(coalesce(tag.name::text, ''))
, invoice.tags
, invoice.invoice_status
, isi18n.name
, to_price(total, decimal_digits)
from invoice
left join invoice_tag using (invoice_id)
left join tag using(tag_id)
join contact using (contact_id)
join invoice_status_i18n isi18n on invoice.invoice_status = isi18n.invoice_status and isi18n.lang_tag = $1
join invoice_amount using (invoice_id)
join currency using (currency_code)
where (%s)
group by invoice.slug
, invoice_date
, invoice_number
, contact.business_name
, invoice.invoice_status
, isi18n.name
, total
, decimal_digits
order by invoice_date desc
, invoice_number desc
`, strings.Join(where, ") AND (")), args...)
@ -673,16 +663,9 @@ func (form *invoiceForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
, invoice_date
, notes
, payment_method_id
, string_agg(tag.name, ',')
, array_to_string(tags, ',')
from invoice
left join invoice_tag using (invoice_id)
left join tag using(tag_id) where slug = $1
group by invoice_id
, contact_id
, invoice_number
, invoice_date
, notes
, payment_method_id
where slug = $1
`, slug).Scan(&invoiceId, form.InvoiceStatus, form.Customer, &form.Number, form.Date, form.Notes, form.PaymentMethod, form.Tags)) {
form.PaymentMethod.Selected = selectedPaymentMethod
form.InvoiceStatus.Selected = selectedInvoiceStatus

View File

@ -72,6 +72,16 @@ func registerPgTypes(ctx context.Context, conn *pgx.Conn) error {
if _, err := conn.Exec(ctx, "set role to admin"); err != nil {
return err
}
tagNameOID, err := registerPgType(ctx, conn, &pgtype.Text{}, "tag_name")
if err != nil {
return err
}
tagNameArray := pgtype.NewArrayType("tag_name[]", tagNameOID, func() pgtype.ValueTranscoder {
return &pgtype.Text{}
})
if _, err := registerPgType(ctx, conn, tagNameArray, tagNameArray.TypeName()); err != nil {
return err
}
discountRateOID, err := registerPgType(ctx, conn, &pgtype.Numeric{}, "discount_rate")
if err != nil {
return err

View File

@ -157,16 +157,11 @@ func mustCollectProductEntries(ctx context.Context, conn *Conn, company *Company
select product.slug
, product.name
, to_price(price, decimal_digits)
, array_agg(coalesce(tag.name::text, ''))
, tags
from product
join company using (company_id)
join currency using (currency_code)
left join product_tag using (product_id)
left join tag using(tag_id)
where product.company_id = $1 and (($2 = '') or (tag.name = $2))
group by product.slug
, product.name
, to_price(price, decimal_digits)
where product.company_id = $1 and (($2 = '') or (tags @> array[$2]::tag_name[]))
order by name
`, company.Id, tag)
if err != nil {
@ -267,11 +262,9 @@ func (form *productForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
, product.description
, to_price(price, decimal_digits)
, array_agg(tax_id)
, string_agg(tag.name, ',')
, array_to_string(tags, ',')
from product
left join product_tax using (product_id)
left join product_tag using (product_id)
left join tag using(company_id, tag_id)
join company using (company_id)
join currency using (currency_code)
where product.slug = $1
@ -279,6 +272,7 @@ func (form *productForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
, product.name
, product.description
, price
, tags
, decimal_digits
`, slug).Scan(
form.Name,

View File

@ -1,7 +0,0 @@
-- Revert numerus:contact_tag from pg
begin;
drop table if exists numerus.contact_tag;
commit;

View File

@ -1,7 +0,0 @@
-- Revert numerus:invoice_tag from pg
begin;
drop table if exists numerus.invoice_tag;
commit;

View File

@ -1,7 +0,0 @@
-- Revert numerus:product_tag from pg
begin;
drop table if exists numerus.product_tag;
commit;

View File

@ -1,7 +0,0 @@
-- Revert numerus:tag from pg
begin;
drop table if exists numerus.tag;
commit;

View File

@ -1,7 +0,0 @@
-- Revert numerus:tag_contact from pg
begin;
drop function if exists numerus.tag_contact(integer, integer, numerus.tag_name[]);
commit;

View File

@ -1,7 +0,0 @@
-- Revert numerus:tag_invoice from pg
begin;
drop function if exists numerus.tag_invoice(integer, integer, numerus.tag_name[]);
commit;

View File

@ -1,7 +0,0 @@
-- Revert numerus:tag_product from pg
begin;
drop function if exists numerus.tag_product(integer, integer, numerus.tag_name[]);
commit;

View File

@ -1,7 +0,0 @@
-- Revert numerus:tag_relation from pg
begin;
drop function if exists numerus.tag_relation(regclass, name, integer, integer, numerus.tag_name[]);
commit;

View File

@ -41,39 +41,31 @@ company_default_payment_method [schema_numerus company payment_method] 2023-03-0
tax_class [schema_numerus company] 2023-02-28T10:13:14Z jordi fita mas <jordi@tandem.blog> # Add the relation for tax classes
tax_rate [schema_numerus] 2023-01-28T11:33:39Z jordi fita mas <jordi@tandem.blog> # Add domain for tax rates
tax [schema_numerus company tax_rate tax_class] 2023-01-28T11:45:47Z jordi fita mas <jordi@tandem.blog> # Add relation for taxes
contact [schema_numerus company extension_vat email extension_pg_libphonenumber extension_uri country_code country] 2023-01-29T12:59:18Z jordi fita mas <jordi@tandem.blog> # Add the relation for contacts
product [schema_numerus company tax] 2023-02-04T09:17:24Z jordi fita mas <jordi@tandem.blog> # Add relation for products
tag_name [schema_numerus] 2023-03-10T11:06:11Z jordi fita mas <jordi@tandem.blog> # Add domain for tag names
contact [schema_numerus company extension_vat email extension_pg_libphonenumber extension_uri country_code country tag_name] 2023-01-29T12:59:18Z jordi fita mas <jordi@tandem.blog> # Add the relation for contacts
product [schema_numerus company tax tag_name] 2023-02-04T09:17:24Z jordi fita mas <jordi@tandem.blog> # Add relation for products
parse_price [schema_public] 2023-02-05T11:04:54Z jordi fita mas <jordi@tandem.blog> # Add function to convert from price to cents
to_price [schema_numerus] 2023-02-05T11:46:31Z jordi fita mas <jordi@tandem.blog> # Add function to format cents to prices
invoice_status [schema_numerus] 2023-02-07T14:50:26Z jordi fita mas <jordi@tandem.blog> # A relation of invoice status
invoice_status_i18n [schema_numerus invoice_status language] 2023-02-07T14:56:18Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice status translatable texts
available_invoice_status [schema_numerus invoice_status invoice_status_i18n] 2023-02-07T15:07:06Z jordi fita mas <jordi@tandem.blog> # Add the list of available invoice status
product_tax [schema_numerus product tax] 2023-02-08T11:36:49Z jordi fita mas <jordi@tandem.blog> # Add relation of product taxes
invoice [schema_numerus company contact invoice_status payment_method currency] 2023-02-09T09:52:21Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice
invoice [schema_numerus company contact invoice_status payment_method currency tag_name] 2023-02-09T09:52:21Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice
discount_rate [schema_numerus] 2023-02-10T17:22:40Z jordi fita mas <jordi@tandem.blog> # Add domain for discount rates
invoice_product [schema_numerus invoice discount_rate] 2023-02-10T17:07:08Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice product
tag_name [schema_numerus] 2023-03-10T11:06:11Z jordi fita mas <jordi@tandem.blog> # Add domain for tag names
tag [schema_numerus tag_name] 2023-03-10T11:04:24Z jordi fita mas <jordi@tandem.blog> # Add relation for tags
tag_relation [schema_numerus tag tag_name] 2023-03-25T17:40:52Z jordi fita mas <jordi@tandem.blog> # Add function to tag “relations”
product_tag [schema_numerus tag product] 2023-03-26T10:54:23Z jordi fita mas <jordi@tandem.blog> # Add relation for product tag
tag_product [schema_numerus tag_name tag_relation product_tag] 2023-03-26T11:01:17Z jordi fita mas <jordi@tandem.blog> # Add function to tag products
add_product [schema_numerus product product_tax parse_price company currency tag_name tag_product] 2023-02-14T10:32:18Z jordi fita mas <jordi@tandem.blog> # Add function to add new products
edit_product [schema_numerus product product_tax parse_price company currency tag_name tag_product] 2023-02-14T11:06:03Z jordi fita mas <jordi@tandem.blog> # Add function to edit products
add_product [schema_numerus product product_tax parse_price company currency tag_name] 2023-02-14T10:32:18Z jordi fita mas <jordi@tandem.blog> # Add function to add new products
edit_product [schema_numerus product product_tax parse_price company currency tag_name] 2023-02-14T11:06:03Z jordi fita mas <jordi@tandem.blog> # Add function to edit products
invoice_product_tax [schema_numerus invoice_product tax tax_rate] 2023-02-15T13:20:30Z jordi fita mas <jordi@tandem.blog> # Add relation for taxes in invoice products
new_invoice_product [schema_numerus discount_rate] 2023-02-16T21:06:01Z jordi fita mas <jordi@tandem.blog> # Add type for passing products to new invoices
invoice_number_counter [schema_numerus company] 2023-02-17T13:04:48Z jordi fita mas <jordi@tandem.blog> # Add relation to count invoice numbers
next_invoice_number [schema_numerus invoice_number_counter] 2023-02-17T13:21:48Z jordi fita mas <jordi@tandem.blog> # Add function to retrieve the next invoice number
invoice_tag [schema_numerus tag invoice] 2023-03-10T11:37:43Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice tag
tag_invoice [schema_numerus tag_name tag_relation invoice_tag] 2023-03-25T18:04:02Z jordi fita mas <jordi@tandem.blog> # Add function to tag invoices
add_invoice [schema_numerus invoice company currency parse_price new_invoice_product tax invoice_product invoice_product_tax next_invoice_number tag_name tag_invoice] 2023-02-16T21:12:46Z jordi fita mas <jordi@tandem.blog> # Add function to create new invoices
add_invoice [schema_numerus invoice company currency parse_price new_invoice_product tax invoice_product invoice_product_tax next_invoice_number tag_name] 2023-02-16T21:12:46Z jordi fita mas <jordi@tandem.blog> # Add function to create new invoices
invoice_tax_amount [schema_numerus invoice_product invoice_product_tax] 2023-02-22T12:08:35Z jordi fita mas <jordi@tandem.blog> # Add view for invoice tax amount
invoice_product_amount [schema_numerus invoice_product invoice_product_tax] 2023-03-01T11:18:05Z jordi fita mas <jordi@tandem.blog> # Add view for invoice product subtotal and total
invoice_amount [schema_numerus invoice_product invoice_product_amount] 2023-02-22T12:58:46Z jordi fita mas <jordi@tandem.blog> # Add view to compute subtotal and total for invoices
new_invoice_amount [schema_numerus] 2023-02-23T12:08:25Z jordi fita mas <jordi@tandem.blog> # Add type to return when computing new invoice amounts
compute_new_invoice_amount [schema_numerus company currency tax new_invoice_product new_invoice_amount] 2023-02-23T12:20:13Z jordi fita mas <jordi@tandem.blog> # Add function to compute the subtotal, taxes, and total amounts for a new invoice
edited_invoice_product [schema_numerus discount_rate] 2023-03-11T19:22:24Z jordi fita mas <jordi@tandem.blog> # Add typo for passing products to edited invoices
edit_invoice [schema_numerus invoice currency parse_price edited_invoice_product tax invoice_product invoice_product_tax tag_name tag_invoice] 2023-03-11T18:30:50Z jordi fita mas <jordi@tandem.blog> # Add function to edit invoices
contact_tag [schema_numerus tag contact] 2023-03-24T22:20:51Z jordi fita mas <jordi@tandem.blog> # Add relation for contact tag
tag_contact [schema_numerus tag_name tag_relation contact_tag] 2023-03-25T22:16:42Z jordi fita mas <jordi@tandem.blog> # Add function to tag contacts
edit_invoice [schema_numerus invoice currency parse_price edited_invoice_product tax invoice_product invoice_product_tax tag_name] 2023-03-11T18:30:50Z jordi fita mas <jordi@tandem.blog> # Add function to edit invoices
add_contact [schema_numerus extension_vat email extension_pg_libphonenumber extension_uri country_code tag_name contact] 2023-03-25T22:32:37Z jordi fita mas <jordi@tandem.blog> # Add function to create new contacts
edit_contact [schema_numerus email extension_uri country_code tag_name contact tag_contact extension_vat extension_pg_libphonenumber] 2023-03-25T23:20:27Z jordi fita mas <jordi@tandem.blog> # Add function to edit contacts
edit_contact [schema_numerus email extension_uri country_code tag_name contact extension_vat extension_pg_libphonenumber] 2023-03-25T23:20:27Z jordi fita mas <jordi@tandem.blog> # Add function to edit contacts

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(16);
select plan(14);
set search_path to auth, numerus, public;
@ -21,8 +21,6 @@ select function_privs_are('numerus', 'add_contact', array ['integer', 'text', 't
set client_min_messages to warning;
truncate contact_tag cascade;
truncate tag cascade;
truncate contact cascade;
truncate payment_method cascade;
truncate company cascade;
@ -63,34 +61,15 @@ select lives_ok(
);
select bag_eq(
$$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, created_at from contact $$,
$$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 777 77 77 77', 'c@c', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '17486', 'ES', CURRENT_TIMESTAMP)
, (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 888 88 88 88', 'd@d', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17487', 'ES', CURRENT_TIMESTAMP)
, (2, 'Contact 4.1', 'ES42424242Y', '', '+34 999 99 99 99', 'e@e', '', 'Yet Another Fake St., 123', 'City 4.1', 'Province 4.1', '17488', 'ES', CURRENT_TIMESTAMP)
, (1, 'Contact 2.3', 'ES43434343Q', '', '+34 000000000', 'f@f', '', 'The Last Fake St., 123', '', '', '', 'ES', CURRENT_TIMESTAMP)
$$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, tags, created_at from contact $$,
$$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 777 77 77 77', 'c@c', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '17486', 'ES', '{tag1,tag2}'::tag_name[], CURRENT_TIMESTAMP)
, (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 888 88 88 88', 'd@d', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17487', 'ES', '{}'::tag_name[], CURRENT_TIMESTAMP)
, (2, 'Contact 4.1', 'ES42424242Y', '', '+34 999 99 99 99', 'e@e', '', 'Yet Another Fake St., 123', 'City 4.1', 'Province 4.1', '17488', 'ES', '{tag2}'::tag_name[], CURRENT_TIMESTAMP)
, (1, 'Contact 2.3', 'ES43434343Q', '', '+34 000000000', 'f@f', '', 'The Last Fake St., 123', '', '', '', 'ES', '{tag2}'::tag_name[], CURRENT_TIMESTAMP)
$$,
'Should have created all contacts'
);
select bag_eq(
$$ select company_id, name from tag $$,
$$ values (1, 'tag1')
, (1, 'tag2')
, (2, 'tag2')
$$,
'Should have added all new tags once'
);
select bag_eq(
$$ select business_name, tag.name from contact_tag join contact using (contact_id) join tag using (tag_id) $$,
$$ values ('Contact 2.1', 'tag1')
, ('Contact 2.1', 'tag2')
, ('Contact 4.1', 'tag2')
, ('Contact 2.3', 'tag2')
$$,
'Should have assigned the tags to contacts'
);
select *
from finish();

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(17);
select plan(15);
set search_path to auth, numerus, public;
@ -21,8 +21,6 @@ select function_privs_are('numerus', 'add_invoice', array ['integer', 'date', 'i
set client_min_messages to warning;
truncate invoice_tag cascade;
truncate tag cascade;
truncate invoice_number_counter cascade;
truncate invoice_product_tax cascade;
truncate invoice_product cascade;
@ -98,10 +96,10 @@ select lives_ok(
);
select bag_eq(
$$ select company_id, invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id, currency_code, created_at from invoice $$,
$$ values (1, 'F20230006', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', current_timestamp)
, (1, 'F20230007', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', current_timestamp)
, (2, 'INV056-23', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', current_timestamp)
$$ select company_id, invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id, currency_code, tags, created_at from invoice $$,
$$ values (1, 'F20230006', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', '{tag1,tag2}'::tag_name[], current_timestamp)
, (1, 'F20230007', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', '{}'::tag_name[], current_timestamp)
, (2, 'INV056-23', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', '{tag3}'::tag_name[], current_timestamp)
$$,
'Should have created all invoices'
);
@ -126,24 +124,6 @@ select bag_eq(
'Should have created all invoice product taxes'
);
select bag_eq(
$$ select company_id, name from tag $$,
$$ values (1, 'tag1')
, (1, 'tag2')
, (2, 'tag3')
$$,
'Should have added all new tags once'
);
select bag_eq(
$$ select invoice_number, tag.name from invoice_tag join invoice using (invoice_id) join tag using (tag_id) $$,
$$ values ('F20230006', 'tag1')
, ('F20230006', 'tag2')
, ('INV056-23', 'tag3')
$$,
'Should have assigned the tags to invoices'
);
select *
from finish();

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(16);
select plan(14);
set search_path to auth, numerus, public;
@ -21,8 +21,6 @@ select function_privs_are('numerus', 'add_product', array ['integer', 'text', 't
set client_min_messages to warning;
truncate product_tag cascade;
truncate tag cascade;
truncate product_tax cascade;
truncate product cascade;
truncate tax cascade;
@ -74,10 +72,10 @@ select lives_ok(
);
select bag_eq(
$$ select company_id, name, description, price, created_at from product $$,
$$ values (1, 'Product 1', 'Description 1', 1212, current_timestamp)
, (2, 'Product 2', 'Description 2', 2424, current_timestamp)
, (2, 'Product 3', 'Description 3', 3636, current_timestamp)
$$ select company_id, name, description, price, tags, created_at from product $$,
$$ values (1, 'Product 1', 'Description 1', 1212, '{tag1}'::tag_name[], current_timestamp)
, (2, 'Product 2', 'Description 2', 2424, '{tag1,tag2}'::tag_name[], current_timestamp)
, (2, 'Product 3', 'Description 3', 3636, '{}'::tag_name[], current_timestamp)
$$,
'Should have added all three products'
);
@ -91,24 +89,6 @@ select bag_eq(
'Should have added the taxes for the products we told to'
);
select bag_eq(
$$ select company_id, name from tag $$,
$$ values (1, 'tag1')
, (2, 'tag1')
, (2, 'tag2')
$$,
'Should have added all new tags once'
);
select bag_eq(
$$ select product.name as product_name, tag.name as tag_name from product_tag join product using (product_id) join tag using (tag_id) $$,
$$ values ('Product 1', 'tag1')
, ('Product 2', 'tag1')
, ('Product 2', 'tag2')
$$,
'Should have assigned the tags to products'
);
select *
from finish();

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(85);
select plan(90);
set search_path to numerus, auth, public;
@ -100,6 +100,12 @@ select col_type_is('contact', 'country_code', 'country_code');
select col_not_null('contact', 'country_code');
select col_hasnt_default('contact', 'country_code');
select has_column('contact', 'tags');
select col_type_is('contact', 'tags', 'tag_name[]');
select col_not_null('contact', 'tags');
select col_has_default('contact', 'tags');
select col_default_is('contact', 'tags', '{}');
select has_column('contact', 'created_at');
select col_type_is('contact', 'created_at', 'timestamp with time zone');
select col_not_null('contact', 'created_at');

View File

@ -1,130 +0,0 @@
-- Test contact_tag
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('contact_tag');
select has_pk('contact_tag' );
select col_is_pk('contact_tag', array['contact_id', 'tag_id']);
select table_privs_are('contact_tag', 'guest', array []::text[]);
select table_privs_are('contact_tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('contact_tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('contact_tag', 'authenticator', array []::text[]);
select has_column('contact_tag', 'contact_id');
select col_is_fk('contact_tag', 'contact_id');
select fk_ok('contact_tag', 'contact_id', 'contact', 'contact_id');
select col_type_is('contact_tag', 'contact_id', 'integer');
select col_not_null('contact_tag', 'contact_id');
select col_hasnt_default('contact_tag', 'contact_id');
select has_column('contact_tag', 'tag_id');
select col_is_fk('contact_tag', 'tag_id');
select fk_ok('contact_tag', 'tag_id', 'tag', 'tag_id');
select col_type_is('contact_tag', 'tag_id', 'integer');
select col_not_null('contact_tag', 'tag_id');
select col_hasnt_default('contact_tag', 'tag_id');
set client_min_messages to warning;
truncate contact_tag cascade;
truncate contact cascade;
truncate tag cascade;
truncate contact 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 contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (6, 2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
, (8, 4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
;
insert into tag (tag_id, company_id, name)
values (14, 2, 'web')
, (15, 2, 'design')
, (16, 4, 'product')
, (17, 4, 'development')
, (18, 4, 'something-else')
, (19, 4, 'design')
;
insert into contact_tag (contact_id, tag_id)
values (6, 14)
, (6, 15)
, (8, 18)
;
prepare contact_tag_data as
select contact_id, tag_id
from contact_tag
;
set role invoicer;
select is_empty('contact_tag_data', 'Should show no data when cookie is not set yet');
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
select bag_eq(
'contact_tag_data',
$$ values ( 6, 14 )
, ( 6, 15 )
$$,
'Should only list contact tags of the companies where demo@tandem.blog is user of'
);
reset role;
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
select bag_eq(
'contact_tag_data',
$$ values ( 8, 18 )
$$,
'Should only list contact tags of the companies where admin@tandem.blog is user of'
);
reset role;
select set_cookie('not-a-cookie');
select throws_ok(
'contact_tag_data',
'42501', 'permission denied for table contact_tag',
'Should not allow select to guest users'
);
select *
from finish();
rollback;

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(14);
select plan(12);
set search_path to auth, numerus, public;
@ -21,8 +21,6 @@ select function_privs_are('numerus', 'edit_contact', array ['uuid', 'text', 'tex
set client_min_messages to warning;
truncate contact_tag cascade;
truncate tag cascade;
truncate contact cascade;
truncate payment_method cascade;
truncate company cascade;
@ -42,22 +40,11 @@ values (111, 1, 'cash', 'cash')
set constraints "company_default_payment_method_id_fkey" immediate;
insert into tag (tag_id, company_id, name)
values (10, 1, 'tag1')
, (11, 1, 'tag2')
;
-- edit_contact uses the sequence and sometimes it would confict
alter sequence tag_tag_id_seq restart with 15;
insert into contact (contact_id, company_id, slug, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
, (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
insert into contact (contact_id, company_id, slug, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, tags)
values (12, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES', '{tag1}')
, (13, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES', '{tag2}')
;
insert into contact_tag (contact_id, tag_id)
values (12, 10)
, (13, 11)
;
select lives_ok(
$$ select edit_contact('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'Contact 2.1', '40404040D', 'Trade Contact 2.1', '999-999-999', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', array['tag1']) $$,
@ -70,30 +57,13 @@ select lives_ok(
);
select bag_eq(
$$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, created_at from contact $$,
$$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 999 99 99 99', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', CURRENT_TIMESTAMP)
, (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 111111111', 'd2@d2', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES', CURRENT_TIMESTAMP)
$$ select company_id, business_name, vatin::text, trade_name, phone::text, email::text, web::text, address, city, province, postal_code, country_code::text, tags, created_at from contact $$,
$$ values (1, 'Contact 2.1', 'ES40404040D', 'Trade Contact 2.1', '+34 999 99 99 99', 'c1@c1', 'https://c', 'Fake St., 123', 'City 2.1', 'Province 2.1', '19486', 'ES', '{tag1}'::tag_name[], CURRENT_TIMESTAMP)
, (1, 'Contact 2.2', 'ES41414141L', 'Trade Contact 2.2', '+34 111111111', 'd2@d2', 'https://d', 'Another Fake St., 123', 'City 2.2', 'Province 2.2', '17417', 'ES', '{tag1,tag3}'::tag_name[], CURRENT_TIMESTAMP)
$$,
'Should have updated all contacts'
);
select bag_eq(
$$ select company_id, name from tag $$,
$$ values (1, 'tag1')
, (1, 'tag2')
, (1, 'tag3')
$$,
'Should have added all new tags'
);
select bag_eq(
$$ select business_name, tag.name from contact_tag join contact using (contact_id) join tag using (tag_id) $$,
$$ values ('Contact 2.1', 'tag1')
, ('Contact 2.2', 'tag1')
, ('Contact 2.2', 'tag3')
$$,
'Should have assigned the tags to contacts'
);
select *
from finish();

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(16);
select plan(14);
set search_path to auth, numerus, public;
@ -21,8 +21,6 @@ select function_privs_are('numerus', 'edit_invoice', array ['uuid', 'text', 'int
set client_min_messages to warning;
truncate invoice_tag cascade;
truncate tag cascade;
truncate invoice_product_tax cascade;
truncate invoice_product cascade;
truncate invoice cascade;
@ -63,26 +61,14 @@ values ( 7, 1, 'Product 1.1', 1212)
, ( 9, 1, 'Product 3.3', 3636)
;
insert into tag (tag_id, company_id, name)
values (10, 1, 'tag1')
, (11, 1, 'tag2')
;
-- edit_invoice uses the sequence and sometimes it would confict
alter sequence tag_tag_id_seq restart with 15;
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
;
insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code)
values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12, 111, 'EUR')
, (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR')
;
insert into invoice_tag (invoice_id, tag_id)
values (15, 10)
, (16, 11)
insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code, tags)
values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12, 111, 'EUR', '{tag1}')
, (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR', '{tag2}')
;
insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price)
@ -111,9 +97,9 @@ select lives_ok(
);
select bag_eq(
$$ select invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id from invoice $$,
$$ values ('INV1', '2023-03-10'::date, 13, 'paid', 'Notes 1', 112)
, ('INV2', '2023-03-09'::date, 12, 'sent', 'Notes 2', 111)
$$ select invoice_number, invoice_date, contact_id, invoice_status, notes, tags, payment_method_id from invoice $$,
$$ values ('INV1', '2023-03-10'::date, 13, 'paid', 'Notes 1', '{tag1}'::tag_name[], 112)
, ('INV2', '2023-03-09'::date, 12, 'sent', 'Notes 2', '{tag1,tag3}'::tag_name[], 111)
$$,
'Should have updated all invoices'
);
@ -139,24 +125,6 @@ select bag_eq(
'Should have updated all invoice product taxes, added new ones, and removed the ones not given to the function'
);
select bag_eq(
$$ select company_id, name from tag $$,
$$ values (1, 'tag1')
, (1, 'tag2')
, (1, 'tag3')
$$,
'Should have added all new tags'
);
select bag_eq(
$$ select invoice_number, tag.name from invoice_tag join invoice using (invoice_id) join tag using (tag_id) $$,
$$ values ('INV1', 'tag1')
, ('INV2', 'tag1')
, ('INV2', 'tag3')
$$,
'Should have assigned the tags to invoices'
);
select *
from finish();

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(17);
select plan(15);
set search_path to auth, numerus, public;
@ -21,8 +21,6 @@ select function_privs_are('numerus', 'edit_product', array ['uuid', 'text', 'tex
set client_min_messages to warning;
truncate product_tag cascade;
truncate tag cascade;
truncate product_tax cascade;
truncate product cascade;
truncate tax cascade;
@ -57,10 +55,10 @@ values (3, 1, 11, 'IRPF -15 %', -0.15)
, (6, 2, 22, 'IVA 10 %', 0.10)
;
insert into product (product_id, company_id, slug, name, description, price)
values (7, 1, 'd2300404-bd23-48b3-8e2b-2bbf86dc7bd6', 'Product 01', 'Description01', 1200)
, (8, 2, '2f085b8b-da90-41fe-b8cf-6ba8d94cfa38', 'Product 02', 'Description02', 2400)
, (9, 2, '84044d0b-af33-442a-95a6-21efc77260d5', 'Product 03', 'Description03', 3600)
insert into product (product_id, company_id, slug, name, description, price, tags)
values (7, 1, 'd2300404-bd23-48b3-8e2b-2bbf86dc7bd6', 'Product 01', 'Description01', 1200, '{tag1}')
, (8, 2, '2f085b8b-da90-41fe-b8cf-6ba8d94cfa38', 'Product 02', 'Description02', 2400, '{tag2}')
, (9, 2, '84044d0b-af33-442a-95a6-21efc77260d5', 'Product 03', 'Description03', 3600, '{tag2}')
;
insert into product_tax (product_id, tax_id)
@ -70,19 +68,6 @@ values (7, 3)
, (9, 6)
;
insert into tag (tag_id, company_id, name)
values (10, 1, 'tag1')
, (11, 2, 'tag2')
;
-- edit_product uses the sequence and sometimes it would confict
alter sequence tag_tag_id_seq restart with 15;
insert into product_tag (product_id, tag_id)
values (7, 10)
, (8, 11)
, (9, 11)
;
select is(
edit_product('d2300404-bd23-48b3-8e2b-2bbf86dc7bd6', 'Product 1', 'Description 1', '12.12', array[3, 4], array['tag1']),
@ -109,10 +94,10 @@ select is(
);
select bag_eq(
$$ select product_id, company_id, name, description, price from product $$,
$$ values (7, 1, 'Product 1', 'Description 1', 1212)
, (8, 2, 'Product 2', 'Description 2', 2424)
, (9, 2, 'Product 3', 'Description 3', 3636)
$$ select product_id, company_id, name, description, price, tags from product $$,
$$ values (7, 1, 'Product 1', 'Description 1', 1212, '{tag1}'::tag_name[])
, (8, 2, 'Product 2', 'Description 2', 2424, '{tag1,tag3}'::tag_name[])
, (9, 2, 'Product 3', 'Description 3', 3636, '{}'::tag_name[])
$$,
'Should have edited all three products'
);
@ -126,24 +111,6 @@ select bag_eq(
'Should have updated the taxes for the products we told to'
);
select bag_eq(
$$ select company_id, name from tag $$,
$$ values (1, 'tag1')
, (2, 'tag1')
, (2, 'tag2')
, (2, 'tag3')
$$,
'Should have added all new tags'
);
select bag_eq(
$$ select product.name as product_name, tag.name as tag_name from product_tag join product using (product_id) join tag using (tag_id) $$,
$$ values ('Product 1', 'tag1')
, ('Product 2', 'tag1')
, ('Product 2', 'tag3')
$$,
'Should have assigned the tags to products'
);
select *
from finish();

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(78);
select plan(83);
set search_path to numerus, auth, public;
@ -75,6 +75,12 @@ select col_not_null('invoice', 'notes');
select col_has_default('invoice', 'notes');
select col_default_is('invoice', 'notes', '');
select has_column('invoice', 'tags');
select col_type_is('invoice', 'tags', 'tag_name[]');
select col_not_null('invoice', 'tags');
select col_has_default('invoice', 'tags');
select col_default_is('invoice', 'tags', '{}');
select has_column('invoice', 'payment_method_id');
select col_is_fk('invoice', 'payment_method_id');
select fk_ok('invoice', 'payment_method_id', 'payment_method', 'payment_method_id');

View File

@ -1,136 +0,0 @@
-- Test invoice_tag
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('invoice_tag');
select has_pk('invoice_tag' );
select col_is_pk('invoice_tag', array['invoice_id', 'tag_id']);
select table_privs_are('invoice_tag', 'guest', array []::text[]);
select table_privs_are('invoice_tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('invoice_tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('invoice_tag', 'authenticator', array []::text[]);
select has_column('invoice_tag', 'invoice_id');
select col_is_fk('invoice_tag', 'invoice_id');
select fk_ok('invoice_tag', 'invoice_id', 'invoice', 'invoice_id');
select col_type_is('invoice_tag', 'invoice_id', 'integer');
select col_not_null('invoice_tag', 'invoice_id');
select col_hasnt_default('invoice_tag', 'invoice_id');
select has_column('invoice_tag', 'tag_id');
select col_is_fk('invoice_tag', 'tag_id');
select fk_ok('invoice_tag', 'tag_id', 'tag', 'tag_id');
select col_type_is('invoice_tag', 'tag_id', 'integer');
select col_not_null('invoice_tag', 'tag_id');
select col_hasnt_default('invoice_tag', 'tag_id');
set client_min_messages to warning;
truncate invoice_tag cascade;
truncate invoice cascade;
truncate tag cascade;
truncate contact 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 contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (6, 2, 'Contact 1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
, (8, 4, 'Contact 2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
;
insert into invoice (invoice_id, company_id, invoice_number, contact_id, currency_code, payment_method_id)
values (10, 2, 'INV020001', 6, 'EUR', 222)
, (12, 4, 'INV040001', 8, 'EUR', 444)
;
insert into tag (tag_id, company_id, name)
values (14, 2, 'web')
, (15, 2, 'design')
, (16, 4, 'product')
, (17, 4, 'development')
, (18, 4, 'something-else')
, (19, 4, 'design')
;
insert into invoice_tag (invoice_id, tag_id)
values (10, 14)
, (10, 15)
, (12, 18)
;
prepare invoice_tag_data as
select invoice_id, tag_id
from invoice_tag
;
set role invoicer;
select is_empty('invoice_tag_data', 'Should show no data when cookie is not set yet');
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
select bag_eq(
'invoice_tag_data',
$$ values ( 10, 14 )
, ( 10, 15 )
$$,
'Should only list invoice tags of the companies where demo@tandem.blog is user of'
);
reset role;
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
select bag_eq(
'invoice_tag_data',
$$ values ( 12, 18 )
$$,
'Should only list invoice tags of the companies where admin@tandem.blog is user of'
);
reset role;
select set_cookie('not-a-cookie');
select throws_ok(
'invoice_tag_data',
'42501', 'permission denied for table invoice_tag',
'Should not allow select to guest users'
);
reset role;
select *
from finish();
rollback;

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin;
select plan(51);
select plan(56);
set search_path to numerus, auth, public;
@ -58,6 +58,12 @@ select col_type_is('product', 'price', 'integer');
select col_not_null('product', 'price');
select col_hasnt_default('product', 'price');
select has_column('product', 'tags');
select col_type_is('product', 'tags', 'tag_name[]');
select col_not_null('product', 'tags');
select col_has_default('product', 'tags');
select col_default_is('product', 'tags', '{}');
select has_column('product', 'created_at');
select col_type_is('product', 'created_at', 'timestamp with time zone');
select col_not_null('product', 'created_at');

View File

@ -1,129 +0,0 @@
-- Test product_tag
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('product_tag');
select has_pk('product_tag');
select col_is_pk('product_tag', array['product_id', 'tag_id']);
select table_privs_are('product_tag', 'guest', array []::text[]);
select table_privs_are('product_tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('product_tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('product_tag', 'authenticator', array []::text[]);
select has_column('product_tag', 'product_id');
select col_is_fk('product_tag', 'product_id');
select fk_ok('product_tag', 'product_id', 'product', 'product_id');
select col_type_is('product_tag', 'product_id', 'integer');
select col_not_null('product_tag', 'product_id');
select col_hasnt_default('product_tag', 'product_id');
select has_column('product_tag', 'tag_id');
select col_is_fk('product_tag', 'tag_id');
select fk_ok('product_tag', 'tag_id', 'tag', 'tag_id');
select col_type_is('product_tag', 'tag_id', 'integer');
select col_not_null('product_tag', 'tag_id');
select col_hasnt_default('product_tag', 'tag_id');
set client_min_messages to warning;
truncate product_tag cascade;
truncate product cascade;
truncate tag 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 product (product_id, company_id, name, description, price)
values (6, 2, 'Product 1', 'Description 1', 1200)
, (8, 4, 'Product 2', 'Description 2', 2400)
;
insert into tag (tag_id, company_id, name)
values (14, 2, 'web')
, (15, 2, 'design')
, (16, 4, 'product')
, (17, 4, 'development')
, (18, 4, 'something-else')
, (19, 4, 'design')
;
insert into product_tag (product_id, tag_id)
values (6, 14)
, (6, 15)
, (8, 18)
;
prepare product_tag_data as
select product_id, tag_id
from product_tag
;
set role invoicer;
select is_empty('product_tag_data', 'Should show no data when cookie is not set yet');
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
select bag_eq(
'product_tag_data',
$$ values ( 6, 14 )
, ( 6, 15 )
$$,
'Should only list product tags of the companies where demo@tandem.blog is user of'
);
reset role;
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
select bag_eq(
'product_tag_data',
$$ values ( 8, 18 )
$$,
'Should only list product tags of the companies where admin@tandem.blog is user of'
);
reset role;
select set_cookie('not-a-cookie');
select throws_ok(
'product_tag_data',
'42501', 'permission denied for table product_tag',
'Should not allow select to guest users'
);
select *
from finish();
rollback;

View File

@ -1,139 +0,0 @@
-- Test tag
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(33);
set search_path to numerus, auth, public;
select has_table('tag');
select has_pk('tag' );
select table_privs_are('tag', 'guest', array []::text[]);
select table_privs_are('tag', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('tag', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('tag', 'authenticator', array []::text[]);
select has_sequence('tag_tag_id_seq');
select sequence_privs_are('tag_tag_id_seq', 'guest', array[]::text[]);
select sequence_privs_are('tag_tag_id_seq', 'invoicer', array['USAGE']);
select sequence_privs_are('tag_tag_id_seq', 'admin', array['USAGE']);
select sequence_privs_are('tag_tag_id_seq', 'authenticator', array[]::text[]);
select has_column('tag', 'tag_id');
select col_is_pk('tag', 'tag_id');
select col_type_is('tag', 'tag_id', 'integer');
select col_not_null('tag', 'tag_id');
select col_has_default('tag', 'tag_id');
select col_default_is('tag', 'tag_id', 'nextval(''tag_tag_id_seq''::regclass)');
select has_column('tag', 'company_id');
select col_is_fk('tag', 'company_id');
select fk_ok('tag', 'company_id', 'company', 'company_id');
select col_type_is('tag', 'company_id', 'integer');
select col_not_null('tag', 'company_id');
select col_hasnt_default('tag', 'company_id');
select has_column('tag', 'name');
select col_type_is('tag', 'name', 'tag_name');
select col_not_null('tag', 'name');
select col_hasnt_default('tag', 'name');
select col_is_unique('tag', array['company_id', 'name']);
set client_min_messages to warning;
truncate tag 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 tag (company_id, name)
values (2, 'web')
, (2, 'design')
, (4, 'product')
, (4, 'development')
, (4, 'something-else')
, (4, 'design')
;
prepare tag_data as
select company_id, name
from tag
;
set role invoicer;
select is_empty('tag_data', 'Should show no data when cookie is not set yet');
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
select bag_eq(
'tag_data',
$$ values ( 2, 'web' )
, ( 2, 'design' )
$$,
'Should only list tags of the companies where demo@tandem.blog is user of'
);
reset role;
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
select bag_eq(
'tag_data',
$$ values (4, 'product' )
, (4, 'development' )
, (4, 'something-else' )
, (4, 'design' )
$$,
'Should only list tags of the companies where admin@tandem.blog is user of'
);
reset role;
select set_cookie('not-a-cookie');
select throws_ok(
'tag_data',
'42501', 'permission denied for table tag',
'Should not allow select to guest users'
);
reset role;
select throws_ok( $$
insert into tag (company_id, name)
values (2, 'web')
$$,
'23505', 'duplicate key value violates unique constraint "tag_company_id_name_key"',
'Should not allow repeated tag names within the same company'
);
select *
from finish();
rollback;

View File

@ -1,124 +0,0 @@
-- Test tag_contact
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', 'tag_contact', array ['integer', 'integer', 'tag_name[]']);
select function_lang_is('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'sql');
select function_returns('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'void');
select isnt_definer('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]']);
select volatility_is('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_contact', array ['integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]);
set client_min_messages to warning;
truncate contact_tag cascade;
truncate tag 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)
;
insert into payment_method (payment_method_id, company_id, name, instructions)
values (111, 1, 'cash', 'cash')
, (112, 1, 'bank', 'send money to my bank account')
;
set constraints "company_default_payment_method_id_fkey" immediate;
insert into tag (tag_id, company_id, name)
values (10, 1, 'tag1')
, (11, 1, 'tag2')
;
-- tag_contact uses the sequence and sometimes it would confict
alter sequence tag_tag_id_seq restart with 15;
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
;
insert into contact_tag (contact_id, tag_id)
values (12, 10)
, (13, 11)
;
prepare current_tags as
select contact_id, tag.name
from contact
join contact_tag using (contact_id)
join tag using (tag_id);
select lives_ok(
$$ select tag_contact(1, 12, array['tag1']) $$,
'Should be able to keep the same tags to the contact'
);
select bag_eq(
'current_tags',
$$ values (12, 'tag1')
, (13, 'tag2')
$$,
'Should not have changed any contact tag'
);
select lives_ok(
$$ select tag_contact(1, 12, array['tag1', 'tag2']) $$,
'Should be able to add tag2 contact'
);
select bag_eq(
'current_tags',
$$ values (12, 'tag1')
, (12, 'tag2')
, (13, 'tag2')
$$,
'Should have added tag2 to contact'
);
select lives_ok(
$$ select tag_contact(1, 13, array['tag3']) $$,
'Should be able to replace all tags of an contact with a new one'
);
select bag_eq(
'current_tags',
$$ values (12, 'tag1')
, (12, 'tag2')
, (13, 'tag3')
$$,
'Should have set tag3 to contact'
);
select lives_ok(
$$ select tag_contact(1, 12, array[]::tag_name[]) $$,
'Should be able to remove all tags from an contact'
);
select bag_eq(
'current_tags',
$$ values (13, 'tag3')
$$,
'Should have remove all tags from contact'
);
select *
from finish();
rollback;

View File

@ -1,130 +0,0 @@
-- Test tag_invoice
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', 'tag_invoice', array ['integer', 'integer', 'tag_name[]']);
select function_lang_is('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'sql');
select function_returns('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'void');
select isnt_definer('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]']);
select volatility_is('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_invoice', array ['integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]);
set client_min_messages to warning;
truncate invoice_tag cascade;
truncate tag cascade;
truncate invoice 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)
;
insert into payment_method (payment_method_id, company_id, name, instructions)
values (111, 1, 'cash', 'cash')
, (112, 1, 'bank', 'send money to my bank account')
;
set constraints "company_default_payment_method_id_fkey" immediate;
insert into tag (tag_id, company_id, name)
values (10, 1, 'tag1')
, (11, 1, 'tag2')
;
-- tag_invoice uses the sequence and sometimes it would confict
alter sequence tag_tag_id_seq restart with 15;
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
, (13, 1, 'Contact 2.2', 'XX666', '', '888-888-888', 'd@d', '', '', '', '', '', 'ES')
;
insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code)
values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12, 111, 'EUR')
, (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR')
;
insert into invoice_tag (invoice_id, tag_id)
values (15, 10)
, (16, 11)
;
prepare current_tags as
select invoice_id, tag.name
from invoice
join invoice_tag using (invoice_id)
join tag using (tag_id);
select lives_ok(
$$ select tag_invoice(1, 15, array['tag1']) $$,
'Should be able to keep the same tags to the invoice'
);
select bag_eq(
'current_tags',
$$ values (15, 'tag1')
, (16, 'tag2')
$$,
'Should not have changed any invoice tag'
);
select lives_ok(
$$ select tag_invoice(1, 15, array['tag1', 'tag2']) $$,
'Should be able to add tag2 invoice'
);
select bag_eq(
'current_tags',
$$ values (15, 'tag1')
, (15, 'tag2')
, (16, 'tag2')
$$,
'Should have added tag2 to invoice'
);
select lives_ok(
$$ select tag_invoice(1, 16, array['tag3']) $$,
'Should be able to replace all tags of an invoice with a new one'
);
select bag_eq(
'current_tags',
$$ values (15, 'tag1')
, (15, 'tag2')
, (16, 'tag3')
$$,
'Should have set tag3 to invoice'
);
select lives_ok(
$$ select tag_invoice(1, 15, array[]::tag_name[]) $$,
'Should be able to remove all tags from an invoice'
);
select bag_eq(
'current_tags',
$$ values (16, 'tag3')
$$,
'Should have remove all tags from invoice'
);
select *
from finish();
rollback;

View File

@ -1,124 +0,0 @@
-- Test tag_product
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', 'tag_product', array ['integer', 'integer', 'tag_name[]']);
select function_lang_is('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'sql');
select function_returns('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'void');
select isnt_definer('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]']);
select volatility_is('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_product', array ['integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]);
set client_min_messages to warning;
truncate product_tag cascade;
truncate tag cascade;
truncate product 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')
, (112, 1, 'bank', 'send money to my bank account')
;
set constraints "company_default_payment_method_id_fkey" immediate;
insert into tag (tag_id, company_id, name)
values (10, 1, 'tag1')
, (11, 1, 'tag2')
;
-- tag_product uses the sequence and sometimes it would confict
alter sequence tag_tag_id_seq restart with 15;
insert into product (product_id, company_id, name, description, price)
values (12, 1, 'Product 1', 'Description 1', 1200)
, (13, 1, 'Product 2', 'Description 2', 2400)
;
insert into product_tag (product_id, tag_id)
values (12, 10)
, (13, 11)
;
prepare current_tags as
select product_id, tag.name
from product
join product_tag using (product_id)
join tag using (tag_id);
select lives_ok(
$$ select tag_product(1, 12, array['tag1']) $$,
'Should be able to keep the same tags to the product'
);
select bag_eq(
'current_tags',
$$ values (12, 'tag1')
, (13, 'tag2')
$$,
'Should not have changed any product tag'
);
select lives_ok(
$$ select tag_product(1, 12, array['tag1', 'tag2']) $$,
'Should be able to add tag2 product'
);
select bag_eq(
'current_tags',
$$ values (12, 'tag1')
, (12, 'tag2')
, (13, 'tag2')
$$,
'Should have added tag2 to product'
);
select lives_ok(
$$ select tag_product(1, 13, array['tag3']) $$,
'Should be able to replace all tags of an product with a new one'
);
select bag_eq(
'current_tags',
$$ values (12, 'tag1')
, (12, 'tag2')
, (13, 'tag3')
$$,
'Should have set tag3 to product'
);
select lives_ok(
$$ select tag_product(1, 12, array[]::tag_name[]) $$,
'Should be able to remove all tags from an product'
);
select bag_eq(
'current_tags',
$$ values (13, 'tag3')
$$,
'Should have remove all tags from product'
);
select *
from finish();
rollback;

View File

@ -1,25 +0,0 @@
-- Test tag_relation
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
set search_path to numerus, public;
select plan(9);
select has_function('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]']);
select function_lang_is('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'plpgsql');
select function_returns('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'void');
select isnt_definer('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]']);
select volatility_is('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'tag_relation', array ['regclass', 'name', 'integer', 'integer', 'tag_name[]'], 'authenticator', array []::text[]);
select *
from finish();
rollback;

View File

@ -16,6 +16,7 @@ select contact_id
, province
, postal_code
, country_code
, tags
, created_at
from numerus.contact
where false;

View File

@ -1,13 +0,0 @@
-- Verify numerus:contact_tag on pg
begin;
select contact_id
, tag_id
from numerus.contact_tag
where false;
select 1 / count(*) from pg_class where oid = 'numerus.contact_tag'::regclass and relrowsecurity;
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.contact_tag'::regclass;
rollback;

View File

@ -10,6 +10,7 @@ select invoice_id
, contact_id
, invoice_status
, notes
, tags
, payment_method_id
, currency_code
, created_at

View File

@ -1,13 +0,0 @@
-- Verify numerus:invoice_tag on pg
begin;
select invoice_id
, tag_id
from numerus.invoice_tag
where false;
select 1 / count(*) from pg_class where oid = 'numerus.invoice_tag'::regclass and relrowsecurity;
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.invoice_tag'::regclass;
rollback;

View File

@ -8,6 +8,7 @@ select product_id
, name
, description
, price
, tags
, created_at
from numerus.product
where false;

View File

@ -1,13 +0,0 @@
-- Verify numerus:product_tag on pg
begin;
select product_id
, tag_id
from numerus.product_tag
where false;
select 1 / count(*) from pg_class where oid = 'numerus.product_tag'::regclass and relrowsecurity;
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.product_tag'::regclass;
rollback;

View File

@ -1,14 +0,0 @@
-- Verify numerus:tag on pg
begin;
select tag_id
, company_id
, name
from numerus.tag
where false;
select 1 / count(*) from pg_class where oid = 'numerus.tag'::regclass and relrowsecurity;
select 1 / count(*) from pg_policy where polname = 'company_policy' and polrelid = 'numerus.tag'::regclass;
rollback;

View File

@ -1,7 +0,0 @@
-- Verify numerus:tag_contact on pg
begin;
select has_function_privilege('numerus.tag_contact(integer, integer, numerus.tag_name[])', 'execute');
rollback;

View File

@ -1,7 +0,0 @@
-- Verify numerus:tag_invoice on pg
begin;
select has_function_privilege('numerus.tag_invoice(integer, integer, numerus.tag_name[])', 'execute');
rollback;

View File

@ -1,7 +0,0 @@
-- Verify numerus:tag_product on pg
begin;
select has_function_privilege('numerus.tag_product(integer, integer, numerus.tag_name[])', 'execute');
rollback;

View File

@ -1,7 +0,0 @@
-- Verify numerus:tag_relation on pg
begin;
select has_function_privilege('numerus.tag_relation(regclass, name, integer, integer, numerus.tag_name[])', 'execute');
rollback;