diff --git a/demo/demo.sql b/demo/demo.sql index 5296d7e..167c223 100644 --- a/demo/demo.sql +++ b/demo/demo.sql @@ -61,12 +61,12 @@ select add_product(1, 'Teia', 'Fusta resinosa de pi i d’altres arbres, provine alter sequence invoice_invoice_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 d’altres arbres, provinent sobretot del cor de l’arbre, 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 - '17 days'::interval)::date, 4, '', 1, '{producte,higiene}','{"(1,\"Paper higiènic (pack de 32 U)\",Paper que s’usa 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 - '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 l’aigua 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 - '28 days'::interval)::date, 6, 'Vol esmorzar!', 1, '{producte}','{"(1,Teia,\"Fusta resinosa de pi i d’altres arbres, provinent sobretot del cor de l’arbre, 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 - '17 days'::interval)::date, 4, '', 1, '{producte,higiene}','{"(1,\"Paper higiènic (pack de 32 U)\",Paper que s’usa 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 - '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 l’aigua 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 = 'unpaid' where invoice_id = 3; diff --git a/deploy/add_invoice.sql b/deploy/add_invoice.sql index e39b331..6a224ac 100644 --- a/deploy/add_invoice.sql +++ b/deploy/add_invoice.sql @@ -16,7 +16,7 @@ begin; 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 iid integer; @@ -25,13 +25,9 @@ declare ccode text; ipid integer; 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) select company_id - , invoice_number + , next_invoice_number(add_invoice.company, invoice_date) , invoice_date , contact_id , notes @@ -70,8 +66,8 @@ end; $$ language plpgsql; -revoke execute on function add_invoice(integer, text, 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, text, date, integer, text, integer, tag_name[], new_invoice_product[]) to admin; +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, 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 admin; commit; diff --git a/pkg/invoices.go b/pkg/invoices.go index 03755d1..c257a25 100644 --- a/pkg/invoices.go +++ b/pkg/invoices.go @@ -217,7 +217,6 @@ func ServeInvoice(w http.ResponseWriter, r *http.Request, params httprouter.Para if invoiceToDuplicate := r.URL.Query().Get("duplicate"); invoiceToDuplicate != "" { form.MustFillFromDatabase(r.Context(), conn, invoiceToDuplicate) form.InvoiceStatus.Selected = []string{"created"} - form.Number.Val = "" } form.Date.Val = time.Now().Format("2006-01-02") w.WriteHeader(http.StatusOK) @@ -456,7 +455,7 @@ func HandleAddInvoice(w http.ResponseWriter, r *http.Request, _ httprouter.Param mustRenderNewInvoiceForm(w, r, form) 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) { w.Header().Set("HX-Trigger", "closeModal") w.Header().Set("HX-Refresh", "true") @@ -529,9 +528,9 @@ func mustWriteInvoicesPdf(r *http.Request, slugs []string) []byte { type invoiceForm struct { locale *Locale company *Company + Number string InvoiceStatus *SelectField Customer *SelectField - Number *InputField Date *InputField Notes *InputField PaymentMethod *SelectField @@ -556,11 +555,6 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co Required: true, 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{ Name: "date", Label: pgettext("input", "Invoice Date", locale), @@ -592,7 +586,6 @@ func (form *invoiceForm) Parse(r *http.Request) error { } form.InvoiceStatus.FillValue(r) form.Customer.FillValue(r) - form.Number.FillValue(r) form.Date.FillValue(r) form.Notes.FillValue(r) form.Tags.FillValue(r) @@ -689,7 +682,7 @@ func (form *invoiceForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s , invoice_date , notes , 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.InvoiceStatus.Selected = selectedInvoiceStatus return false @@ -890,7 +883,7 @@ func newEditInvoicePage(slug string, form *invoiceForm, r *http.Request) *editIn return &editInvoicePage{ newNewInvoicePage(form, r), slug, - form.Number.String(), + form.Number, } } diff --git a/revert/add_invoice.sql b/revert/add_invoice.sql index 7a200d9..00655e4 100644 --- a/revert/add_invoice.sql +++ b/revert/add_invoice.sql @@ -2,6 +2,6 @@ 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; diff --git a/test/add_invoice.sql b/test/add_invoice.sql index df31966..f7120d5 100644 --- a/test/add_invoice.sql +++ b/test/add_invoice.sql @@ -5,19 +5,19 @@ reset client_min_messages; begin; -select plan(19); +select plan(17); 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 function_lang_is('numerus', 'add_invoice', array ['integer', 'text', '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 isnt_definer('numerus', 'add_invoice', array ['integer', 'text', '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 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', 'text', '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', 'text', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'authenticator', array []::text[]); +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', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'plpgsql'); +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', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]']); +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', '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[]'], 'invoicer', 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', 'date', 'integer', 'text', 'integer', 'tag_name[]', 'new_invoice_product[]'], 'authenticator', array []::text[]); 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 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' ); 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' ); 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' ); -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 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) - , (1, 'INV002', '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) - , (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) + $$ values (1, 'F20230006', '2023-02-15'::date, 12, 'created', 'Notes 1', 111, 'EUR', current_timestamp) + , (1, 'F20230007', '2023-02-16'::date, 13, 'created', 'Notes 2', 111, 'EUR', current_timestamp) + , (2, 'INV056-23', '2023-02-14'::date, 15, 'created', 'Notes 3', 222, 'USD', current_timestamp) $$, 'Should have created all invoices' ); select bag_eq( $$ select invoice_number, 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) - , ('INV002', 7, 'Product 1 bis', 'Description 1 bis', 3333, 1, 0.50) - , ('INV002', 8, 'Product 2', 'Description 2', 2400, 3, 0.75) - , ('INV101', 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) + $$ values ('F20230006', 7, 'Product 1', 'Description 1', 1224, 2, 0.00) + , ('F20230007', 7, 'Product 1 bis', 'Description 1 bis', 3333, 1, 0.50) + , ('F20230007', 8, 'Product 2', 'Description 2', 2400, 3, 0.75) + , ('INV056-23', 11, 'Product 4.3', '', 1111, 1, 0.0) $$, 'Should have created all invoice products' ); 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) $$, - $$ values ('INV001', 7, 4, 0.21) - , ('INV002', 7, 4, 0.21) - , ('INV002', 7, 3, -0.15) - , ('INV101', 11, 6, 0.10) + $$ values ('F20230006', 7, 4, 0.21) + , ('F20230007', 7, 4, 0.21) + , ('F20230007', 7, 3, -0.15) + , ('INV056-23', 11, 6, 0.10) $$, 'Should have created all invoice product taxes' ); @@ -144,22 +130,16 @@ select bag_eq( $$ select company_id, name from tag $$, $$ values (1, 'tag1') , (1, 'tag2') - , (2, 'tag2') , (2, 'tag3') - , (2, 'tag4') $$, '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 ('INV001', 'tag1') - , ('INV001', 'tag2') - , ('INV101', 'tag3') + $$ values ('F20230006', 'tag1') , ('F20230006', 'tag2') - , ('INV056-23', 'tag2') , ('INV056-23', 'tag3') - , ('INV056-23', 'tag4') $$, 'Should have assigned the tags to invoices' ); diff --git a/verify/add_invoice.sql b/verify/add_invoice.sql index c8e6807..e2788a5 100644 --- a/verify/add_invoice.sql +++ b/verify/add_invoice.sql @@ -2,6 +2,6 @@ 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; diff --git a/web/template/invoices/edit.gohtml b/web/template/invoices/edit.gohtml index b30627a..5354565 100644 --- a/web/template/invoices/edit.gohtml +++ b/web/template/invoices/edit.gohtml @@ -22,7 +22,6 @@ {{ with .Form -}} {{ template "select-field" .Customer }} - {{ template "hidden-field" .Number }} {{ template "hidden-field" .Date }} {{ template "tags-field" .Tags }} {{ template "select-field" .PaymentMethod }} diff --git a/web/template/invoices/new.gohtml b/web/template/invoices/new.gohtml index e5bae6d..afd0042 100644 --- a/web/template/invoices/new.gohtml +++ b/web/template/invoices/new.gohtml @@ -23,7 +23,6 @@ {{ with .Form -}} {{ template "hidden-select-field" .InvoiceStatus }} {{ template "select-field" .Customer }} - {{ template "input-field" .Number }} {{ template "input-field" .Date }} {{ template "tags-field" .Tags }} {{ template "select-field" .PaymentMethod }} diff --git a/web/template/invoices/products.gohtml b/web/template/invoices/products.gohtml index 9b3a7e7..cdad7f3 100644 --- a/web/template/invoices/products.gohtml +++ b/web/template/invoices/products.gohtml @@ -8,7 +8,7 @@

{{( pgettext "Home" "title" )}} / {{( pgettext "Invoices" "title" )}} / - {{ if eq .Form.Number.Val "" }} + {{ if eq .Form.Number "" }} {{( pgettext "New Invoice" "title" )}} {{ else }} {{ .Form.Number }} @@ -26,7 +26,6 @@ {{- with .Form }} {{ template "hidden-select-field" .Customer }} - {{ template "hidden-field" .Number }} {{ template "hidden-field" .Date }} {{ template "hidden-field" .Notes }} {{ template "hidden-field" .Tags }}