Compare commits

...

2 Commits

Author SHA1 Message Date
jordi fita mas 90982b49ff Move the product_id field from invoice_product to a separate table
We are going to allow invoices with products that are not (yet) inserted
into the products table.

We always allowed to have products in invoices with a totally different
name, description, price, and whatnot, but until now we had the product
id in these invoice lines for statistics purposes.

However, Oriol raised the concern that this requires for the products
to be inserted before we can create an invoice with them, and we do not
plan to have a “create product while invoicing” feature, thus it would
mean that people would need to cancel the new invoice, create the new
product, and then start the invoice again from scratch.

The compromise is to allow products in the invoice that do not have a
product_id, meaning that at the time the invoice was created they were
not (yet) in the products table.  Oriol sees this stop-invoice-create-
product issue more important than the accurate statistics of product
sales, as it will probably be only one or two units off, anyway.

I did not want to allow NULL values to the invoice product’s product_id
field, because NULL means “dunno” instead of “no product”, so i had to
split that field to a separate table that relates an invoice product
with a registered product.
2023-04-19 19:30:12 +02:00
jordi fita mas 835bab357e Only return HTTP 422 while validating the invoice’s form if not HTMx 2023-04-19 19:18:29 +02:00
17 changed files with 201 additions and 113 deletions

View File

@ -7,6 +7,7 @@
-- requires: new_invoice_product -- requires: new_invoice_product
-- requires: tax -- requires: tax
-- requires: invoice_product -- requires: invoice_product
-- requires: invoice_product_product
-- requires: invoice_product_tax -- requires: invoice_product_tax
-- requires: next_invoice_number -- requires: next_invoice_number
-- requires: tag_name -- requires: tag_name
@ -40,9 +41,8 @@ begin
foreach product in array products foreach product in array products
loop loop
insert into invoice_product (invoice_id, product_id, name, description, price, quantity, discount_rate) insert into invoice_product (invoice_id, name, description, price, quantity, discount_rate)
select iid select iid
, product.product_id
, product.name , product.name
, coalesce(product.description, '') , coalesce(product.description, '')
, parse_price(product.price, currency.decimal_digits) , parse_price(product.price, currency.decimal_digits)
@ -53,6 +53,11 @@ begin
returning invoice_product_id returning invoice_product_id
into ipid; into ipid;
if product.product_id is not null then
insert into invoice_product_product (invoice_product_id, product_id)
values (ipid, product.product_id);
end if;
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate) insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
select ipid, tax_id, tax.rate select ipid, tax_id, tax.rate
from tax from tax

View File

@ -42,9 +42,8 @@ begin
foreach product in array products foreach product in array products
loop loop
if product.invoice_product_id is null then if product.invoice_product_id is null then
insert into invoice_product (invoice_id, product_id, name, description, price, quantity, discount_rate) insert into invoice_product (invoice_id, name, description, price, quantity, discount_rate)
select iid select iid
, product.product_id
, product.name , product.name
, coalesce(product.description, '') , coalesce(product.description, '')
, parse_price(product.price, currency.decimal_digits) , parse_price(product.price, currency.decimal_digits)
@ -58,8 +57,7 @@ begin
ipid := product.invoice_product_id; ipid := product.invoice_product_id;
update invoice_product update invoice_product
set product_id = product.product_id set name = product.name
, name = product.name
, description = coalesce(product.description, '') , description = coalesce(product.description, '')
, price = parse_price(product.price, currency.decimal_digits) , price = parse_price(product.price, currency.decimal_digits)
, quantity = product.quantity , quantity = product.quantity
@ -70,6 +68,15 @@ begin
end if; end if;
products_to_keep := array_append(products_to_keep, ipid); products_to_keep := array_append(products_to_keep, ipid);
if product.product_id is null then
delete from invoice_product_product where invoice_product_id = ipid;
else
insert into invoice_product_product (invoice_product_id, product_id)
values (ipid, product.product_id)
on conflict (invoice_product_id) do update
set product_id = product.product_id;
end if;
delete from invoice_product_tax where invoice_product_id = ipid; delete from invoice_product_tax where invoice_product_id = ipid;
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate) insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
@ -86,6 +93,7 @@ begin
if array_length(products_to_delete, 1) > 0 then if array_length(products_to_delete, 1) > 0 then
delete from invoice_product_tax where invoice_product_id = any(products_to_delete); delete from invoice_product_tax where invoice_product_id = any(products_to_delete);
delete from invoice_product_product where invoice_product_id = any(products_to_delete);
delete from invoice_product where invoice_product_id = any(products_to_delete); delete from invoice_product where invoice_product_id = any(products_to_delete);
end if; end if;

View File

@ -10,7 +10,6 @@ set search_path to numerus, public;
create table invoice_product ( create table invoice_product (
invoice_product_id serial primary key, invoice_product_id serial primary key,
invoice_id integer not null references invoice, invoice_id integer not null references invoice,
product_id integer not null references product,
name text not null constraint name_not_empty check(length(trim(name)) > 0), name text not null constraint name_not_empty check(length(trim(name)) > 0),
description text not null default '', description text not null default '',
price integer not null, price integer not null,

View File

@ -0,0 +1,18 @@
-- Deploy numerus:invoice_product_product to pg
-- requires: schema_numerus
-- requires: invoice_product
-- requires: product
begin;
set search_path to numerus;
create table invoice_product_product (
invoice_product_id integer primary key references invoice_product,
product_id integer not null references product
);
grant select, insert, update, delete on table invoice_product_product to invoicer;
grant select, insert, update, delete on table invoice_product_product to admin;
commit;

View File

@ -463,7 +463,9 @@ func HandleAddInvoice(w http.ResponseWriter, r *http.Request, _ httprouter.Param
return return
} }
if !form.Validate() { if !form.Validate() {
w.WriteHeader(http.StatusUnprocessableEntity) if !IsHTMxRequest(r) {
w.WriteHeader(http.StatusUnprocessableEntity)
}
mustRenderNewInvoiceForm(w, r, form) mustRenderNewInvoiceForm(w, r, form)
return return
} }
@ -693,7 +695,7 @@ func (form *invoiceForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
return false return false
} }
form.Products = []*invoiceProductForm{} form.Products = []*invoiceProductForm{}
form.mustAddProductsFromQuery(ctx, conn, "select invoice_product_id::text, product_id, name, description, to_price(price, $2), quantity, (discount_rate * 100)::integer, array_remove(array_agg(tax_id), null) from invoice_product left join invoice_product_tax using (invoice_product_id) where invoice_id = $1 group by invoice_product_id, product_id, name, description, discount_rate, price, quantity", invoiceId, form.company.DecimalDigits) form.mustAddProductsFromQuery(ctx, conn, "select invoice_product_id::text, coalesce(product_id, 0), name, description, to_price(price, $2), quantity, (discount_rate * 100)::integer, array_remove(array_agg(tax_id), null) from invoice_product left join invoice_product_product using (invoice_product_id) left join invoice_product_tax using (invoice_product_id) where invoice_id = $1 group by invoice_product_id, coalesce(product_id, 0), name, description, discount_rate, price, quantity", invoiceId, form.company.DecimalDigits)
return true return true
} }
@ -840,7 +842,9 @@ func HandleUpdateInvoice(w http.ResponseWriter, r *http.Request, params httprout
} else { } else {
slug := params[0].Value slug := params[0].Value
if !form.Validate() { if !form.Validate() {
w.WriteHeader(http.StatusUnprocessableEntity) if !IsHTMxRequest(r) {
w.WriteHeader(http.StatusUnprocessableEntity)
}
mustRenderEditInvoiceForm(w, r, slug, form) mustRenderEditInvoiceForm(w, r, slug, form)
return return
} }

View File

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

View File

@ -53,13 +53,14 @@ product_tax [schema_numerus product tax] 2023-02-08T11:36:49Z jordi fita mas <jo
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 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 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 invoice_product [schema_numerus invoice discount_rate] 2023-02-10T17:07:08Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice product
invoice_product_product [schema_numerus invoice_product product] 2023-04-19T16:06:30Z jordi fita mas <jordi@tandem.blog> # Add relation of invoice products and registered 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 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 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 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 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 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 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
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 add_invoice [schema_numerus invoice company currency parse_price new_invoice_product tax invoice_product invoice_product_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_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_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 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

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin; begin;
select plan(15); select plan(16);
set search_path to auth, numerus, public; set search_path to auth, numerus, public;
@ -91,7 +91,7 @@ select lives_ok(
); );
select lives_ok( select lives_ok(
$$ select add_invoice(2, '2023-02-14', 15, 'Notes 3', 222, '{tag3}','{"(11,Product 4.3,,11.11,1,0.0,{6})"}') $$, $$ select add_invoice(2, '2023-02-14', 15, 'Notes 3', 222, '{tag3}','{"(11,Product 4.3,,11.11,1,0.0,{6})","(,Product 4.4,Description 4.4,22.22,3,0.05,{})"}') $$,
'Should be able to insert an invoice for the second company with a product' 'Should be able to insert an invoice for the second company with a product'
); );
@ -105,21 +105,33 @@ select bag_eq(
); );
select bag_eq( select bag_eq(
$$ select invoice_number, product_id, name, description, price, quantity, discount_rate from invoice_product join invoice using (invoice_id) $$, $$ select invoice_number, name, description, price, quantity, discount_rate from invoice_product join invoice using (invoice_id) $$,
$$ values ('F20230006', 7, 'Product 1', 'Description 1', 1224, 2, 0.00) $$ values ('F20230006', 'Product 1', 'Description 1', 1224, 2, 0.00)
, ('F20230007', 7, 'Product 1 bis', 'Description 1 bis', 3333, 1, 0.50) , ('F20230007', 'Product 1 bis', 'Description 1 bis', 3333, 1, 0.50)
, ('F20230007', 8, 'Product 2', 'Description 2', 2400, 3, 0.75) , ('F20230007', 'Product 2', 'Description 2', 2400, 3, 0.75)
, ('INV056-23', 11, 'Product 4.3', '', 1111, 1, 0.0) , ('INV056-23', 'Product 4.3', '', 1111, 1, 0.0)
, ('INV056-23', 'Product 4.4', 'Description 4.4', 2222, 3, 0.05)
$$, $$,
'Should have created all invoice products' 'Should have created all invoice products'
); );
select bag_eq( select bag_eq(
$$ select invoice_number, product_id, tax_id, tax_rate from invoice_product_tax join invoice_product using (invoice_product_id) join invoice using (invoice_id) $$, $$ select invoice_number, product_id, name from invoice_product left join invoice_product_product using (invoice_product_id) join invoice using (invoice_id) $$,
$$ values ('F20230006', 7, 4, 0.21) $$ values ('F20230006', 7, 'Product 1')
, ('F20230007', 7, 4, 0.21) , ('F20230007', 7, 'Product 1 bis')
, ('F20230007', 7, 3, -0.15) , ('F20230007', 8, 'Product 2')
, ('INV056-23', 11, 6, 0.10) , ('INV056-23', 11, 'Product 4.3')
, ('INV056-23', NULL, 'Product 4.4')
$$,
'Should have linked all invoice products'
);
select bag_eq(
$$ select invoice_number, name, tax_id, tax_rate from invoice_product_tax join invoice_product using (invoice_product_id) join invoice using (invoice_id) $$,
$$ values ('F20230006', 'Product 1', 4, 0.21)
, ('F20230007', 'Product 1 bis', 4, 0.21)
, ('F20230007', 'Product 1 bis', 3, -0.15)
, ('INV056-23', 'Product 4.3', 6, 0.10)
$$, $$,
'Should have created all invoice product taxes' 'Should have created all invoice product taxes'
); );

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin; begin;
select plan(14); select plan(15);
set search_path to auth, numerus, public; set search_path to auth, numerus, public;
@ -71,11 +71,18 @@ values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-03-10', 12,
, (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-03-09', 13, 111, 'EUR', '{tag2}') , (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) insert into invoice_product (invoice_product_id, invoice_id, name, price)
values (19, 15, 7, 'P1.0', 1100) values (19, 15, 'P1.0', 1100)
, (20, 15, 8, 'P2.0', 2200) , (20, 15, 'P2.0', 2200)
, (21, 16, 7, 'P1.1', 1111) , (21, 16, 'P1.1', 1111)
, (22, 16, 8, 'P2.1', 2211) , (22, 16, 'P2.1', 2211)
;
insert into invoice_product_product (invoice_product_id, product_id)
values (19, 7)
, (20, 8)
, (21, 7)
, (22, 8)
; ;
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate) insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)
@ -87,7 +94,7 @@ values (19, 4, 0.21)
; ;
select lives_ok( select lives_ok(
$$ select edit_invoice('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'paid', 13, 'Notes 1', 112, array['tag1'], '{"(20,7,p1.0,D1,11.01,2,0.50,{4})"}') $$, $$ select edit_invoice('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'paid', 13, 'Notes 1', 112, array['tag1'], '{"(20,,p1.0,D1,11.01,2,0.50,{4})","(,,p1.3,D3,33.33,3,0.05,{3})"}') $$,
'Should be able to edit the first invoice' 'Should be able to edit the first invoice'
); );
@ -105,22 +112,35 @@ select bag_eq(
); );
select bag_eq( select bag_eq(
$$ select invoice_number, product_id, name, description, price, quantity, discount_rate from invoice_product join invoice using (invoice_id) $$, $$ select invoice_number, name, description, price, quantity, discount_rate from invoice_product join invoice using (invoice_id) $$,
$$ values ('INV1', 7, 'p1.0', 'D1', 1101, 2, 0.50) $$ values ('INV1', 'p1.0', 'D1', 1101, 2, 0.50)
, ('INV2', 7, 'P1.1', '', 1111, 1, 0.00) , ('INV1', 'p1.3', 'D3', 3333, 3, 0.05)
, ('INV2', 8, 'p2.1', 'D2', 2400, 3, 0.75) , ('INV2', 'P1.1', '', 1111, 1, 0.00)
, ('INV2', 9, 'p3.3', '', 3636, 2, 0.05) , ('INV2', 'p2.1', 'D2', 2400, 3, 0.75)
, ('INV2', 'p3.3', '', 3636, 2, 0.05)
$$, $$,
'Should have updated all existing invoice products, added new ones, and removed the ones not give to the function' 'Should have updated all existing invoice products, added new ones, and removed the ones not give to the function'
); );
select bag_eq( select bag_eq(
$$ select invoice_number, product_id, tax_id, tax_rate from invoice_product_tax join invoice_product using (invoice_product_id) join invoice using (invoice_id) $$, $$ select invoice_number, product_id, name from invoice_product left join invoice_product_product using (invoice_product_id) join invoice using (invoice_id) $$,
$$ values ('INV1', 7, 4, 0.21) $$ values ('INV1', NULL, 'p1.0')
, ('INV2', 7, 3, -0.15) , ('INV1', NULL, 'p1.3')
, ('INV2', 8, 3, -0.15) , ('INV2', 7, 'P1.1')
, ('INV2', 8, 4, 0.21) , ('INV2', 8, 'p2.1')
, ('INV2', 9, 4, 0.21) , ('INV2', 9, 'p3.3')
$$,
'Should have updated all existing invoice products id, added new ones, and removed the ones not give to the function'
);
select bag_eq(
$$ select invoice_number, name, tax_id, tax_rate from invoice_product_tax join invoice_product using (invoice_product_id) join invoice using (invoice_id) $$,
$$ values ('INV1', 'p1.0', 4, 0.21)
, ('INV1', 'p1.3', 3, -0.15)
, ('INV2', 'P1.1', 3, -0.15)
, ('INV2', 'p2.1', 3, -0.15)
, ('INV2', 'p2.1', 4, 0.21)
, ('INV2', 'p3.3', 4, 0.21)
$$, $$,
'Should have updated all invoice product taxes, added new ones, and removed the ones not given to the function' 'Should have updated all invoice product taxes, added new ones, and removed the ones not given to the function'
); );

View File

@ -30,7 +30,6 @@ truncate invoice_product_tax cascade;
truncate invoice_product cascade; truncate invoice_product cascade;
truncate invoice cascade; truncate invoice cascade;
truncate contact cascade; truncate contact cascade;
truncate product cascade;
truncate tax cascade; truncate tax cascade;
truncate tax_class cascade; truncate tax_class cascade;
truncate payment_method cascade; truncate payment_method cascade;
@ -60,10 +59,6 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
, (5, 1, 11, 'IVA 21 %', 0.21) , (5, 1, 11, 'IVA 21 %', 0.21)
; ;
insert into product (product_id, company_id, name, price)
values (6, 1, 'Product', 1212)
;
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
; ;
@ -75,14 +70,14 @@ values ( 8, 1, 'I1', current_date, 7, 'EUR', '111')
, (11, 1, 'I4', current_date, 7, 'EUR', '111') , (11, 1, 'I4', current_date, 7, 'EUR', '111')
; ;
insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price, quantity, discount_rate) insert into invoice_product (invoice_product_id, invoice_id, name, price, quantity, discount_rate)
values (12, 8, 6, 'P', 100, 1, 0.0) values (12, 8, 'P', 100, 1, 0.0)
, (13, 8, 6, 'P', 200, 2, 0.1) , (13, 8, 'P', 200, 2, 0.1)
, (14, 9, 6, 'P', 222, 3, 0.0) , (14, 9, 'P', 222, 3, 0.0)
, (15, 9, 6, 'P', 333, 4, 0.2) , (15, 9, 'P', 333, 4, 0.2)
, (16, 10, 6, 'P', 444, 5, 0.0) , (16, 10, 'P', 444, 5, 0.0)
, (17, 10, 6, 'P', 555, 6, 0.1) , (17, 10, 'P', 555, 6, 0.1)
, (18, 11, 6, 'P', 777, 8, 0.0) , (18, 11, 'P', 777, 8, 0.0)
; ;
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate) insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)

View File

@ -5,7 +5,7 @@ reset client_min_messages;
begin; begin;
select plan(57); select plan(51);
set search_path to numerus, auth, public; set search_path to numerus, auth, public;
@ -36,13 +36,6 @@ select col_type_is('invoice_product', 'invoice_id', 'integer');
select col_not_null('invoice_product', 'invoice_id'); select col_not_null('invoice_product', 'invoice_id');
select col_hasnt_default('invoice_product', 'invoice_id'); select col_hasnt_default('invoice_product', 'invoice_id');
select has_column('invoice_product', 'product_id');
select col_is_fk('invoice_product', 'product_id');
select fk_ok('invoice_product', 'product_id', 'product', 'product_id');
select col_type_is('invoice_product', 'product_id', 'integer');
select col_not_null('invoice_product', 'product_id');
select col_hasnt_default('invoice_product', 'product_id');
select has_column('invoice_product', 'name'); select has_column('invoice_product', 'name');
select col_type_is('invoice_product', 'name', 'text'); select col_type_is('invoice_product', 'name', 'text');
select col_not_null('invoice_product', 'name'); select col_not_null('invoice_product', 'name');
@ -75,7 +68,6 @@ select col_default_is('invoice_product', 'discount_rate', '0.0');
set client_min_messages to warning; set client_min_messages to warning;
truncate invoice_product cascade; truncate invoice_product cascade;
truncate invoice cascade; truncate invoice cascade;
truncate product cascade;
truncate contact cascade; truncate contact cascade;
truncate company_user cascade; truncate company_user cascade;
truncate payment_method cascade; truncate payment_method cascade;
@ -117,21 +109,16 @@ values (10, 2, 'INV020001', 6, 'EUR', 222)
, (12, 4, 'INV040001', 8, 'EUR', 444) , (12, 4, 'INV040001', 8, 'EUR', 444)
; ;
insert into product (product_id, company_id, name, description, price) insert into invoice_product (invoice_id, name, description, price, quantity)
values (14, 2, 'Product 1', 'Description 1', 1200) values (10, 'product 1', 'description 1', 1212, 1)
, (16, 4, 'Product 2', 'Description 2', 2400) , (12, 'product 2', 'description 2', 2424, 2)
;
insert into invoice_product (invoice_id, product_id, name, description, price, quantity)
values (10, 14, 'product 1', 'description 1', 1212, 1)
, (12, 16, 'product 2', 'description 2', 2424, 2)
; ;
prepare invoice_product_data as prepare invoice_product_data as
select invoice_id, product_id, name, price, quantity select invoice_id, name, price, quantity
from invoice_product from invoice_product
order by invoice_id, product_id; order by invoice_id;
set role invoicer; set role invoicer;
select is_empty('invoice_product_data', 'Should show no data when cookie is not set yet'); select is_empty('invoice_product_data', 'Should show no data when cookie is not set yet');
@ -140,7 +127,7 @@ reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog'); select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
select bag_eq( select bag_eq(
'invoice_product_data', 'invoice_product_data',
$$ values (10, 14, 'product 1', 1212, 1) $$ values (10, 'product 1', 1212, 1)
$$, $$,
'Should only list products of invoices of the companies where demo@tandem.blog is user of' 'Should only list products of invoices of the companies where demo@tandem.blog is user of'
); );
@ -149,7 +136,7 @@ reset role;
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog'); select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
select bag_eq( select bag_eq(
'invoice_product_data', 'invoice_product_data',
$$ values (12, 16, 'product 2', 2424, 2) $$ values (12, 'product 2', 2424, 2)
$$, $$,
'Should only list products of invoices of the companies where admin@tandem.blog is user of' 'Should only list products of invoices of the companies where admin@tandem.blog is user of'
); );
@ -165,8 +152,8 @@ reset role;
select throws_ok( $$ select throws_ok( $$
insert into invoice_product (invoice_id, product_id, name, description, price, quantity) insert into invoice_product (invoice_id, name, description, price, quantity)
values (10, 14, ' ', '', 1212, 1) values (10, ' ', '', 1212, 1)
$$, $$,
'23514', 'new row for relation "invoice_product" violates check constraint "name_not_empty"', '23514', 'new row for relation "invoice_product" violates check constraint "name_not_empty"',
'Should not allow invoice products with blank name' 'Should not allow invoice products with blank name'

View File

@ -30,7 +30,6 @@ truncate invoice_product_tax cascade;
truncate invoice_product cascade; truncate invoice_product cascade;
truncate invoice cascade; truncate invoice cascade;
truncate contact cascade; truncate contact cascade;
truncate product cascade;
truncate tax cascade; truncate tax cascade;
truncate tax_class cascade; truncate tax_class cascade;
truncate payment_method cascade; truncate payment_method cascade;
@ -60,10 +59,6 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
, (5, 1, 11, 'IVA 21 %', 0.21) , (5, 1, 11, 'IVA 21 %', 0.21)
; ;
insert into product (product_id, company_id, name, price)
values (6, 1, 'Product', 1212)
;
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
; ;
@ -75,14 +70,14 @@ values ( 8, 1, 'I1', current_date, 7, 'EUR', 111)
, (11, 1, 'I4', current_date, 7, 'EUR', 111) , (11, 1, 'I4', current_date, 7, 'EUR', 111)
; ;
insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price, quantity, discount_rate) insert into invoice_product (invoice_product_id, invoice_id, name, price, quantity, discount_rate)
values (12, 8, 6, 'P', 100, 1, 0.0) values (12, 8, 'P', 100, 1, 0.0)
, (13, 8, 6, 'P', 200, 2, 0.1) , (13, 8, 'P', 200, 2, 0.1)
, (14, 9, 6, 'P', 222, 3, 0.0) , (14, 9, 'P', 222, 3, 0.0)
, (15, 9, 6, 'P', 333, 4, 0.2) , (15, 9, 'P', 333, 4, 0.2)
, (16, 10, 6, 'P', 444, 5, 0.0) , (16, 10, 'P', 444, 5, 0.0)
, (17, 10, 6, 'P', 555, 6, 0.1) , (17, 10, 'P', 555, 6, 0.1)
, (18, 11, 6, 'P', 777, 8, 0.0) , (18, 11, 'P', 777, 8, 0.0)
; ;
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate) insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)

View File

@ -0,0 +1,39 @@
-- Test invoice_product_product
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(19);
set search_path to numerus, public;
select has_table('invoice_product_product');
select has_pk('invoice_product_product' );
select table_privs_are('invoice_product_product', 'guest', array []::text[]);
select table_privs_are('invoice_product_product', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('invoice_product_product', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('invoice_product_product', 'authenticator', array []::text[]);
select has_column('invoice_product_product', 'invoice_product_id');
select col_is_pk('invoice_product_product', 'invoice_product_id');
select col_is_fk('invoice_product_product', 'invoice_product_id');
select fk_ok('invoice_product_product', 'invoice_product_id', 'invoice_product', 'invoice_product_id');
select col_type_is('invoice_product_product', 'invoice_product_id', 'integer');
select col_not_null('invoice_product_product', 'invoice_product_id');
select col_hasnt_default('invoice_product_product', 'invoice_product_id');
select has_column('invoice_product_product', 'product_id');
select col_is_fk('invoice_product_product', 'product_id');
select fk_ok('invoice_product_product', 'product_id', 'product', 'product_id');
select col_type_is('invoice_product_product', 'product_id', 'integer');
select col_not_null('invoice_product_product', 'product_id');
select col_hasnt_default('invoice_product_product', 'product_id');
select *
from finish();
rollback;

View File

@ -41,7 +41,6 @@ set client_min_messages to warning;
truncate invoice_product_tax cascade; truncate invoice_product_tax cascade;
truncate invoice_product cascade; truncate invoice_product cascade;
truncate invoice cascade; truncate invoice cascade;
truncate product cascade;
truncate tax cascade; truncate tax cascade;
truncate tax_class cascade; truncate tax_class cascade;
truncate contact cascade; truncate contact cascade;
@ -85,11 +84,6 @@ values (3, 2, 22, 'IVA 21 %', 0.21)
, (6, 4, 44, 'IVA 10 %', 0.10) , (6, 4, 44, 'IVA 10 %', 0.10)
; ;
insert into product (product_id, company_id, name, price)
values (7, 2, 'Product 1', 1200)
, (8, 4, 'Product 2', 2400)
;
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values ( 9, 2, 'Customer 1', 'XX555', '', '777-777-777', 'c1@e', '', '', '', '', '', 'ES') values ( 9, 2, 'Customer 1', 'XX555', '', '777-777-777', 'c1@e', '', '', '', '', '', 'ES')
, (10, 4, 'Customer 2', 'XX666', '', '888-888-888', 'c2@e', '', '', '', '', '', 'ES') , (10, 4, 'Customer 2', 'XX666', '', '888-888-888', 'c2@e', '', '', '', '', '', 'ES')
@ -100,9 +94,9 @@ values (11, 2, 'INV001', 9, 'EUR', 222)
, (12, 4, 'INV002', 10, 'EUR', 444) , (12, 4, 'INV002', 10, 'EUR', 444)
; ;
insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price) insert into invoice_product (invoice_product_id, invoice_id, name, price)
values (13, 11, 7, 'Product 1', 1200) values (13, 11, 'Product 1', 1200)
, (14, 12, 8, 'Product 2', 2400) , (14, 12, 'Product 2', 2400)
; ;
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate) insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)

View File

@ -30,7 +30,6 @@ truncate invoice_product_tax cascade;
truncate invoice_product cascade; truncate invoice_product cascade;
truncate invoice cascade; truncate invoice cascade;
truncate contact cascade; truncate contact cascade;
truncate product cascade;
truncate tax cascade; truncate tax cascade;
truncate tax_class cascade; truncate tax_class cascade;
truncate payment_method cascade; truncate payment_method cascade;
@ -60,10 +59,6 @@ values (2, 1, 11, 'IRPF -15 %', -0.15)
, (5, 1, 11, 'IVA 21 %', 0.21) , (5, 1, 11, 'IVA 21 %', 0.21)
; ;
insert into product (product_id, company_id, name, price)
values (6, 1, 'Product', 1212)
;
insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code) insert into contact (contact_id, company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code)
values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES') values (7, 1, 'Contact', 'XX555', '', '777-777-777', 'c@c', '', '', '', '', '', 'ES')
; ;
@ -75,14 +70,14 @@ values ( 8, 1, 'I1', current_date, 7, 'EUR', 111)
, (11, 1, 'I4', current_date, 7, 'EUR', 111) , (11, 1, 'I4', current_date, 7, 'EUR', 111)
; ;
insert into invoice_product (invoice_product_id, invoice_id, product_id, name, price, quantity, discount_rate) insert into invoice_product (invoice_product_id, invoice_id, name, price, quantity, discount_rate)
values (12, 8, 6, 'P', 100, 1, 0.0) values (12, 8, 'P', 100, 1, 0.0)
, (13, 8, 6, 'P', 200, 2, 0.1) , (13, 8, 'P', 200, 2, 0.1)
, (14, 9, 6, 'P', 222, 3, 0.0) , (14, 9, 'P', 222, 3, 0.0)
, (15, 9, 6, 'P', 333, 4, 0.2) , (15, 9, 'P', 333, 4, 0.2)
, (16, 10, 6, 'P', 444, 5, 0.0) , (16, 10, 'P', 444, 5, 0.0)
, (17, 10, 6, 'P', 555, 6, 0.1) , (17, 10, 'P', 555, 6, 0.1)
, (18, 11, 6, 'P', 777, 8, 0.0) , (18, 11, 'P', 777, 8, 0.0)
; ;
insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate) insert into invoice_product_tax (invoice_product_id, tax_id, tax_rate)

View File

@ -4,7 +4,6 @@ begin;
select invoice_product_id select invoice_product_id
, invoice_id , invoice_id
, product_id
, name , name
, description , description
, price , price

View File

@ -0,0 +1,10 @@
-- Verify numerus:invoice_product_product on pg
begin;
select invoice_product_id
, product_id
from numerus.invoice_product_product
where false;
rollback;