Compare commits

..

3 Commits

Author SHA1 Message Date
jordi fita mas ca0298213e Filter out nulls in tax array when viewing invoice
This is for the unusual case of products without taxes, that PostgreSQL
by default would generate an array with a single null value in it, but
then pgx would not be able to set that null to the string variable.
2023-04-01 16:07:26 +02:00
jordi fita mas 57f29fc883 Remove <fieldset> around hidden products when adding to invoice 2023-04-01 15:58:50 +02:00
jordi fita mas c453715ee1 Remove the number field from new invoice form
Initially, this field was meant to be left almost always blank, except
for when we deleted invoiced and had to “replace” its number with a new
invoice; using the automatic numbering in this cas would not “fill in”
the missing number in the sequence.

However, we decide to not allow removing invoicer not edit their
numbers, therefore, if everything goes as planned, there should not be
any gap in the sequence, and that field is rendered useless.

Oriol suggested making it a read-only field, both for new and edit
forms, but i do not think it makes sense to have a field if you can not
edit it at all, specially in the new invoice dialog, where it would
always be blank.  In the edit form we already show the number in the
title and breadcrumbs, thus no need for the read-only field as
reference.

I still keep a Number member to the form struct, but is now a string
(kind of “a read-only field”, in a way) and just to be written in the
title or breadcrumbs.  I did not like the idea of adding a new SQL
query just for that value.
2023-04-01 15:57:56 +02:00
9 changed files with 52 additions and 88 deletions

View File

@ -61,12 +61,12 @@ select add_product(1, 'Teia', 'Fusta resinosa de pi i daltres arbres, provine
alter sequence invoice_invoice_id_seq restart; alter sequence invoice_invoice_id_seq restart;
alter sequence invoice_product_invoice_product_id_seq restart; alter sequence invoice_product_invoice_product_id_seq restart;
select add_invoice(1, '', (current_date - '28 days'::interval)::date, 6, 'Vol esmorzar!', 1, '{producte}','{"(1,Teia,\"Fusta resinosa de pi i daltres arbres, provinent sobretot del cor de larbre, que crema amb molta facilitat.\",7.00,1,0.0,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.64,1,0.0,{2})"}'); select add_invoice(1, (current_date - '28 days'::interval)::date, 6, 'Vol esmorzar!', 1, '{producte}','{"(1,Teia,\"Fusta resinosa de pi i daltres arbres, provinent sobretot del cor de larbre, que crema amb molta facilitat.\",7.00,1,0.0,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.64,1,0.0,{2})"}');
select add_invoice(1, '', (current_date - '24 days'::interval)::date, 5, '', 1, '{producte,bestia}','{"(1,Palla,Tija seca dels cereals després que el gra o llavor ha estat separat mitjançant la trilla.,25.00,25,0.0,{3})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.64,1,0.0,{2})"}'); select add_invoice(1, (current_date - '24 days'::interval)::date, 5, '', 1, '{producte,bestia}','{"(1,Palla,Tija seca dels cereals després que el gra o llavor ha estat separat mitjançant la trilla.,25.00,25,0.0,{3})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.64,1,0.0,{2})"}');
select add_invoice(1, '', (current_date - '17 days'::interval)::date, 4, '', 1, '{producte,higiene}','{"(1,\"Paper higiènic (pack de 32 U)\",Paper que susa per mantenir la higiene personal després de defecar o orinar.,7.99,10,0.0,{4})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.64,1,0.0,{2})"}'); select add_invoice(1, (current_date - '17 days'::interval)::date, 4, '', 1, '{producte,higiene}','{"(1,\"Paper higiènic (pack de 32 U)\",Paper que susa per mantenir la higiene personal després de defecar o orinar.,7.99,10,0.0,{4})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.64,1,0.0,{2})"}');
select add_invoice(1, '', (current_date - '7 days'::interval)::date, 3, '', 1, '{producte,mag}','{"(1,Mirra,Goma resinosa aromàtica de color gris groguenc i gust amargant.,7.22,144,0.05,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",4.45,1,0.0,{2})"}'); select add_invoice(1, (current_date - '7 days'::interval)::date, 3, '', 1, '{producte,mag}','{"(1,Mirra,Goma resinosa aromàtica de color gris groguenc i gust amargant.,7.22,144,0.05,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",4.45,1,0.0,{2})"}');
select add_invoice(1, '', (current_date - '4 days'::interval)::date, 2, '', 1, '{producte,mag}','{"(1,Encens,Goma resina fragrant que desprèn una olor característica quan es crema.,2.26,460,0.05,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",4.53,1,0.0,{2})"}'); select add_invoice(1, (current_date - '4 days'::interval)::date, 2, '', 1, '{producte,mag}','{"(1,Encens,Goma resina fragrant que desprèn una olor característica quan es crema.,2.26,460,0.05,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",4.53,1,0.0,{2})"}');
select add_invoice(1, '', (current_date - '1 days'::interval)::date, 1, '', 1, '{producte,mag}','{"(1,Or,\"Metall de transició tou, brillant, groc, pesant, mal·leable, dúctil i que no reacciona amb la majoria de productes químics, però és sensible al clor i a laigua règia.\",57.82,18,0.05,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.43,1,0.0,{2})"}'); select add_invoice(1, (current_date - '1 days'::interval)::date, 1, '', 1, '{producte,mag}','{"(1,Or,\"Metall de transició tou, brillant, groc, pesant, mal·leable, dúctil i que no reacciona amb la majoria de productes químics, però és sensible al clor i a laigua règia.\",57.82,18,0.05,{2})","(5,Cavall Fort,\"Revista quinzenal en llengua catalana i de còmic en català, destinada a infants i joves.\",3.43,1,0.0,{2})"}');
update invoice set invoice_status = 'paid' where invoice_id in (1, 5); update invoice set invoice_status = 'paid' where invoice_id in (1, 5);
update invoice set invoice_status = 'unpaid' where invoice_id = 3; update invoice set invoice_status = 'unpaid' where invoice_id = 3;

View File

@ -16,7 +16,7 @@ begin;
set search_path to numerus, public; set search_path to numerus, public;
create or replace function add_invoice(company integer, invoice_number text, invoice_date date, contact_id integer, notes text, payment_method_id integer, tags tag_name[], products new_invoice_product[]) returns uuid as create or replace function add_invoice(company integer, invoice_date date, contact_id integer, notes text, payment_method_id integer, tags tag_name[], products new_invoice_product[]) returns uuid as
$$ $$
declare declare
iid integer; iid integer;
@ -25,13 +25,9 @@ declare
ccode text; ccode text;
ipid integer; ipid integer;
begin begin
if invoice_number is null or length(trim(invoice_number)) = 0 then
invoice_number = next_invoice_number(company, invoice_date);
end if;
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, currency_code, payment_method_id)
select company_id select company_id
, invoice_number , next_invoice_number(add_invoice.company, invoice_date)
, invoice_date , invoice_date
, contact_id , contact_id
, notes , notes
@ -70,8 +66,8 @@ end;
$$ $$
language plpgsql; language plpgsql;
revoke execute on function add_invoice(integer, text, date, integer, text, integer, tag_name[], new_invoice_product[]) from public; revoke execute on function add_invoice(integer, date, integer, text, integer, tag_name[], new_invoice_product[]) from public;
grant execute on function add_invoice(integer, text, date, integer, text, integer, tag_name[], new_invoice_product[]) to invoicer; grant execute on function add_invoice(integer, date, integer, text, integer, tag_name[], new_invoice_product[]) to invoicer;
grant execute on function add_invoice(integer, text, date, integer, text, integer, tag_name[], new_invoice_product[]) to admin; grant execute on function add_invoice(integer, date, integer, text, integer, tag_name[], new_invoice_product[]) to admin;
commit; commit;

View File

@ -217,7 +217,6 @@ func ServeInvoice(w http.ResponseWriter, r *http.Request, params httprouter.Para
if invoiceToDuplicate := r.URL.Query().Get("duplicate"); invoiceToDuplicate != "" { if invoiceToDuplicate := r.URL.Query().Get("duplicate"); invoiceToDuplicate != "" {
form.MustFillFromDatabase(r.Context(), conn, invoiceToDuplicate) form.MustFillFromDatabase(r.Context(), conn, invoiceToDuplicate)
form.InvoiceStatus.Selected = []string{"created"} form.InvoiceStatus.Selected = []string{"created"}
form.Number.Val = ""
} }
form.Date.Val = time.Now().Format("2006-01-02") form.Date.Val = time.Now().Format("2006-01-02")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
@ -337,7 +336,7 @@ func mustGetInvoice(ctx context.Context, conn *Conn, company *Company, slug stri
if err := conn.QueryRow(ctx, "select array_agg(array[name, to_price(amount, $2)]) from invoice_tax_amount join tax using (tax_id) where invoice_id = $1", invoiceId, decimalDigits).Scan(&inv.Taxes); err != nil { if err := conn.QueryRow(ctx, "select array_agg(array[name, to_price(amount, $2)]) from invoice_tax_amount join tax using (tax_id) where invoice_id = $1", invoiceId, decimalDigits).Scan(&inv.Taxes); err != nil {
panic(err) panic(err)
} }
rows := conn.MustQuery(ctx, "select invoice_product.name, description, to_price(price, $2), (discount_rate * 100)::integer, quantity, to_price(subtotal, $2), to_price(total, $2), array_agg(array[tax_class.name, (tax_rate * 100)::integer::text]) from invoice_product join invoice_product_amount using (invoice_product_id) left join invoice_product_tax using (invoice_product_id) left join tax using (tax_id) left join tax_class using (tax_class_id) where invoice_id = $1 group by invoice_product.name, description, discount_rate, price, quantity, subtotal, total", invoiceId, decimalDigits) rows := conn.MustQuery(ctx, "select invoice_product.name, description, to_price(price, $2), (discount_rate * 100)::integer, quantity, to_price(subtotal, $2), to_price(total, $2), array_agg(array[tax_class.name, (tax_rate * 100)::integer::text]) filter (where tax_rate is not null) from invoice_product join invoice_product_amount using (invoice_product_id) left join invoice_product_tax using (invoice_product_id) left join tax using (tax_id) left join tax_class using (tax_class_id) where invoice_id = $1 group by invoice_product.name, description, discount_rate, price, quantity, subtotal, total", invoiceId, decimalDigits)
defer rows.Close() defer rows.Close()
taxClasses := map[string]bool{} taxClasses := map[string]bool{}
for rows.Next() { for rows.Next() {
@ -456,7 +455,7 @@ func HandleAddInvoice(w http.ResponseWriter, r *http.Request, _ httprouter.Param
mustRenderNewInvoiceForm(w, r, form) mustRenderNewInvoiceForm(w, r, form)
return return
} }
slug := conn.MustGetText(r.Context(), "", "select add_invoice($1, $2, $3, $4, $5, $6, $7, $8)", company.Id, form.Number, form.Date, form.Customer, form.Notes, form.PaymentMethod, form.Tags, NewInvoiceProductArray(form.Products)) slug := conn.MustGetText(r.Context(), "", "select add_invoice($1, $2, $3, $4, $5, $6, $7)", company.Id, form.Date, form.Customer, form.Notes, form.PaymentMethod, form.Tags, NewInvoiceProductArray(form.Products))
if IsHTMxRequest(r) { if IsHTMxRequest(r) {
w.Header().Set("HX-Trigger", "closeModal") w.Header().Set("HX-Trigger", "closeModal")
w.Header().Set("HX-Refresh", "true") w.Header().Set("HX-Refresh", "true")
@ -529,9 +528,9 @@ func mustWriteInvoicesPdf(r *http.Request, slugs []string) []byte {
type invoiceForm struct { type invoiceForm struct {
locale *Locale locale *Locale
company *Company company *Company
Number string
InvoiceStatus *SelectField InvoiceStatus *SelectField
Customer *SelectField Customer *SelectField
Number *InputField
Date *InputField Date *InputField
Notes *InputField Notes *InputField
PaymentMethod *SelectField PaymentMethod *SelectField
@ -556,11 +555,6 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
Required: true, Required: true,
Options: MustGetOptions(ctx, conn, "select contact_id::text, business_name from contact where company_id = $1 order by business_name", company.Id), Options: MustGetOptions(ctx, conn, "select contact_id::text, business_name from contact where company_id = $1 order by business_name", company.Id),
}, },
Number: &InputField{
Name: "number",
Label: pgettext("input", "Number", locale),
Type: "text",
},
Date: &InputField{ Date: &InputField{
Name: "date", Name: "date",
Label: pgettext("input", "Invoice Date", locale), Label: pgettext("input", "Invoice Date", locale),
@ -592,7 +586,6 @@ func (form *invoiceForm) Parse(r *http.Request) error {
} }
form.InvoiceStatus.FillValue(r) form.InvoiceStatus.FillValue(r)
form.Customer.FillValue(r) form.Customer.FillValue(r)
form.Number.FillValue(r)
form.Date.FillValue(r) form.Date.FillValue(r)
form.Notes.FillValue(r) form.Notes.FillValue(r)
form.Tags.FillValue(r) form.Tags.FillValue(r)
@ -689,7 +682,7 @@ func (form *invoiceForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
, invoice_date , invoice_date
, notes , notes
, payment_method_id , payment_method_id
`, slug).Scan(&invoiceId, form.InvoiceStatus, form.Customer, form.Number, form.Date, form.Notes, form.PaymentMethod, form.Tags)) { `, slug).Scan(&invoiceId, form.InvoiceStatus, form.Customer, &form.Number, form.Date, form.Notes, form.PaymentMethod, form.Tags)) {
form.PaymentMethod.Selected = selectedPaymentMethod form.PaymentMethod.Selected = selectedPaymentMethod
form.InvoiceStatus.Selected = selectedInvoiceStatus form.InvoiceStatus.Selected = selectedInvoiceStatus
return false return false
@ -890,7 +883,7 @@ func newEditInvoicePage(slug string, form *invoiceForm, r *http.Request) *editIn
return &editInvoicePage{ return &editInvoicePage{
newNewInvoicePage(form, r), newNewInvoicePage(form, r),
slug, slug,
form.Number.String(), form.Number,
} }
} }

View File

@ -2,6 +2,6 @@
begin; begin;
drop function if exists numerus.add_invoice(integer, text, date, integer, text, integer, numerus.tag_name[], numerus.new_invoice_product[]); drop function if exists numerus.add_invoice(integer, date, integer, text, integer, numerus.tag_name[], numerus.new_invoice_product[]);
commit; commit;

View File

@ -5,19 +5,19 @@ reset client_min_messages;
begin; begin;
select plan(19); select plan(17);
set search_path to auth, numerus, public; set search_path to auth, numerus, public;
select has_function('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]']); select has_function('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]']);
select function_lang_is('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'plpgsql'); select function_lang_is('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'plpgsql');
select function_returns('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'uuid'); select function_returns('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'uuid');
select isnt_definer('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]']); select isnt_definer('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]']);
select volatility_is('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'volatile'); select volatility_is('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'volatile');
select function_privs_are('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'guest', array []::text[]); select function_privs_are('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'invoicer', array ['EXECUTE']); select function_privs_are('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'admin', array ['EXECUTE']); select function_privs_are('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'add_invoice', array ['integer', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'authenticator', array []::text[]); select function_privs_are('numerus', 'add_invoice', array ['integer', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'authenticator', array []::text[]);
set client_min_messages to warning; set client_min_messages to warning;
@ -83,59 +83,45 @@ values (12, 1, 'Contact 2.1', 'XX555', '', '777-777-777', 'c@c', '', '', '', '',
select lives_ok( select lives_ok(
$$ select add_invoice(1, 'INV001', '2023-02-15', 12, 'Notes 1', 111, '{tag1,tag2}','{"(7,Product 1,Description 1,12.24,2,0.0,{4})"}') $$, $$ select add_invoice(1, '2023-02-15', 12, 'Notes 1', 111, '{tag1,tag2}','{"(7,Product 1,Description 1,12.24,2,0.0,{4})"}') $$,
'Should be able to insert an invoice for the first company with a product' 'Should be able to insert an invoice for the first company with a product'
); );
select lives_ok( select lives_ok(
$$ select add_invoice(1, 'INV002', '2023-02-16', 13, 'Notes 2', 111, '{}', '{"(7,Product 1 bis,Description 1 bis,33.33,1,0.50,\"{4,3}\")","(8,Product 2,Description 2,24.00,3,0.75,{})"}') $$, $$ select add_invoice(1, '2023-02-16', 13, 'Notes 2', 111, '{}', '{"(7,Product 1 bis,Description 1 bis,33.33,1,0.50,\"{4,3}\")","(8,Product 2,Description 2,24.00,3,0.75,{})"}') $$,
'Should be able to insert a second invoice for the first company with two product' 'Should be able to insert a second invoice for the first company with two product'
); );
select lives_ok( select lives_ok(
$$ select add_invoice(2, 'INV101', '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})"}') $$,
'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'
); );
select lives_ok(
$$ select add_invoice(1, NULL, '2023-03-15', 13, '', 111, '{tag2}', '{"(7,PA1,DA1,44.33,1,0.50,{})"}') $$,
'Should be able to insert an invoice with an autogenerated number'
);
select lives_ok(
$$ select add_invoice(2, ' ', '2023-04-16', 14, '', 222, '{tag2,tag3,tag4}','{"(11,PA2,DA2,55.33,10,0.75,{})"}') $$,
'Should consider non-null, but otherwise empty numbers the same as null and autogenerate it'
);
select bag_eq( select bag_eq(
$$ select company_id, invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id, currency_code, created_at from invoice $$, $$ select company_id, invoice_number, invoice_date, contact_id, invoice_status, notes, payment_method_id, currency_code, created_at from invoice $$,
$$ values (1, 'INV001', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', current_timestamp) $$ values (1, 'F20230006', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', current_timestamp)
, (1, 'INV002', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', current_timestamp) , (1, 'F20230007', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', current_timestamp)
, (2, 'INV101', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', current_timestamp) , (2, 'INV056-23', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', current_timestamp)
, (1, 'F20230006', '2023-03-15'::date, 13, 'created', '', 111, 'EUR', current_timestamp)
, (2, 'INV056-23', '2023-04-16'::date, 14, 'created', '', 222, 'USD', current_timestamp)
$$, $$,
'Should have created all invoices' 'Should have created all invoices'
); );
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, product_id, name, description, price, quantity, discount_rate from invoice_product join invoice using (invoice_id) $$,
$$ values ('INV001', 7, 'Product 1', 'Description 1', 1224, 2, 0.00) $$ values ('F20230006', 7, 'Product 1', 'Description 1', 1224, 2, 0.00)
, ('INV002', 7, 'Product 1 bis', 'Description 1 bis', 3333, 1, 0.50) , ('F20230007', 7, 'Product 1 bis', 'Description 1 bis', 3333, 1, 0.50)
, ('INV002', 8, 'Product 2', 'Description 2', 2400, 3, 0.75) , ('F20230007', 8, 'Product 2', 'Description 2', 2400, 3, 0.75)
, ('INV101', 11, 'Product 4.3', '', 1111, 1, 0.0) , ('INV056-23', 11, 'Product 4.3', '', 1111, 1, 0.0)
, ('F20230006', 7, 'PA1', 'DA1', 4433, 1, 0.50)
, ('INV056-23', 11, 'PA2', 'DA2', 5533, 10, 0.75)
$$, $$,
'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, tax_id, tax_rate from invoice_product_tax join invoice_product using (invoice_product_id) join invoice using (invoice_id) $$,
$$ values ('INV001', 7, 4, 0.21) $$ values ('F20230006', 7, 4, 0.21)
, ('INV002', 7, 4, 0.21) , ('F20230007', 7, 4, 0.21)
, ('INV002', 7, 3, -0.15) , ('F20230007', 7, 3, -0.15)
, ('INV101', 11, 6, 0.10) , ('INV056-23', 11, 6, 0.10)
$$, $$,
'Should have created all invoice product taxes' 'Should have created all invoice product taxes'
); );
@ -144,22 +130,16 @@ select bag_eq(
$$ select company_id, name from tag $$, $$ select company_id, name from tag $$,
$$ values (1, 'tag1') $$ values (1, 'tag1')
, (1, 'tag2') , (1, 'tag2')
, (2, 'tag2')
, (2, 'tag3') , (2, 'tag3')
, (2, 'tag4')
$$, $$,
'Should have added all new tags once' 'Should have added all new tags once'
); );
select bag_eq( select bag_eq(
$$ select invoice_number, tag.name from invoice_tag join invoice using (invoice_id) join tag using (tag_id) $$, $$ select invoice_number, tag.name from invoice_tag join invoice using (invoice_id) join tag using (tag_id) $$,
$$ values ('INV001', 'tag1') $$ values ('F20230006', 'tag1')
, ('INV001', 'tag2')
, ('INV101', 'tag3')
, ('F20230006', 'tag2') , ('F20230006', 'tag2')
, ('INV056-23', 'tag2')
, ('INV056-23', 'tag3') , ('INV056-23', 'tag3')
, ('INV056-23', 'tag4')
$$, $$,
'Should have assigned the tags to invoices' 'Should have assigned the tags to invoices'
); );

View File

@ -2,6 +2,6 @@
begin; begin;
select has_function_privilege('numerus.add_invoice(integer, text, date, integer, text, integer, numerus.tag_name[], numerus.new_invoice_product[])', 'execute'); select has_function_privilege('numerus.add_invoice(integer, date, integer, text, integer, numerus.tag_name[], numerus.new_invoice_product[])', 'execute');
rollback; rollback;

View File

@ -22,7 +22,6 @@
{{ with .Form -}} {{ with .Form -}}
{{ template "select-field" .Customer }} {{ template "select-field" .Customer }}
{{ template "hidden-field" .Number }}
{{ template "hidden-field" .Date }} {{ template "hidden-field" .Date }}
{{ template "tags-field" .Tags }} {{ template "tags-field" .Tags }}
{{ template "select-field" .PaymentMethod }} {{ template "select-field" .PaymentMethod }}

View File

@ -23,7 +23,6 @@
{{ with .Form -}} {{ with .Form -}}
{{ template "hidden-select-field" .InvoiceStatus }} {{ template "hidden-select-field" .InvoiceStatus }}
{{ template "select-field" .Customer }} {{ template "select-field" .Customer }}
{{ template "input-field" .Number }}
{{ template "input-field" .Date }} {{ template "input-field" .Date }}
{{ template "tags-field" .Tags }} {{ template "tags-field" .Tags }}
{{ template "select-field" .PaymentMethod }} {{ template "select-field" .PaymentMethod }}

View File

@ -8,7 +8,7 @@
<p> <p>
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> / <a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a href="{{ companyURI "/invoices"}}">{{( pgettext "Invoices" "title" )}}</a> / <a href="{{ companyURI "/invoices"}}">{{( pgettext "Invoices" "title" )}}</a> /
{{ if eq .Form.Number.Val "" }} {{ if eq .Form.Number "" }}
<a>{{( pgettext "New Invoice" "title" )}}</a> <a>{{( pgettext "New Invoice" "title" )}}</a>
{{ else }} {{ else }}
<a>{{ .Form.Number }}</a> <a>{{ .Form.Number }}</a>
@ -26,13 +26,11 @@
{{- with .Form }} {{- with .Form }}
{{ template "hidden-select-field" .Customer }} {{ template "hidden-select-field" .Customer }}
{{ template "hidden-field" .Number }}
{{ template "hidden-field" .Date }} {{ template "hidden-field" .Date }}
{{ template "hidden-field" .Notes }} {{ template "hidden-field" .Notes }}
{{ template "hidden-field" .Tags }} {{ template "hidden-field" .Tags }}
{{- range $product := .Products }} {{- range $product := .Products }}
<fieldset>
{{ template "hidden-field" .InvoiceProductId }} {{ template "hidden-field" .InvoiceProductId }}
{{ template "hidden-field" .ProductId }} {{ template "hidden-field" .ProductId }}
{{ template "hidden-field" .Name }} {{ template "hidden-field" .Name }}
@ -41,7 +39,6 @@
{{ template "hidden-field" .Quantity }} {{ template "hidden-field" .Quantity }}
{{ template "hidden-field" .Discount }} {{ template "hidden-field" .Discount }}
{{ template "hidden-select-field" .Tax }} {{ template "hidden-select-field" .Tax }}
</fieldset>
{{- end }} {{- end }}
{{- end }} {{- end }}