Compare commits

..

No commits in common. "e626c7b4bd1636aa96ecfd94a1c229e11ad33c18" and "c95f1724994836e8aa14f50d3b9640185c60de8d" have entirely different histories.

24 changed files with 249 additions and 464 deletions

View File

@ -121,51 +121,28 @@ update invoice set invoice_status = 'paid' where invoice_id not in (154, 155, 15
update invoice set invoice_status = 'unpaid' where invoice_id = 155;
update invoice set invoice_status = 'sent' where invoice_id = 156;
alter table payment_account alter column payment_account_id restart with 123;
select add_payment_account_bank(123, 'Guardiola', 'ES2820958297603648596978');
select add_payment_account_cash(123, 'Matalàs');
alter sequence expense_expense_id_seq restart with 123;
select add_expense(123, (date_trunc('month', current_date) - '11 months + 14 day'::interval)::date, 130, 'ABC123', '256.12', '{124}', '{}');
select add_payment(123, 123, (date_trunc('month', current_date) - '11 months + 04 day'::interval)::date, 123, 'Pagament dABC123', '256.12', '{}');
select add_expense(123, (date_trunc('month', current_date) - '11 months + 8 day'::interval)::date, 131, '123ABC', '1023.17', '{124}', '{}');
select add_payment(123, 124, (date_trunc('month', current_date) - '11 months'::interval)::date, 123, 'Pagament d123ABC', '1023.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '11 months + 1 day'::interval)::date, 129, 'N CMDPGGNZG', '299.17', '{}', array['autonoms']);
select add_payment(123, 125, (date_trunc('month', current_date) - '10 months + 15 day'::interval)::date, 123, 'Pagament dN CMDPGGNZG', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '10 months + 20 day'::interval)::date, 131, '123XYZ', '23.17', '{124}', '{}');
select add_payment(123, 126, (date_trunc('month', current_date) - '10 months + 15 day'::interval)::date, 124, 'Pagament d123XYZ', '23.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '10 months + 1 day'::interval)::date, 129, 'N QHVLDAN29', '299.17', '{}', array['autonoms']);
select add_payment(123, 127, (date_trunc('month', current_date) - '10 months'::interval)::date, 123, 'Pagament dN QHVLDAN29', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '9 months + 2 day'::interval)::date, 130, 'XYZ123', '62.21', '{124}', '{}');
select add_payment(123, 128, (date_trunc('month', current_date) - '8 months + 28 day'::interval)::date, 123, 'Pagament dXYZ123', '62.21', '{}');
select add_expense(123, (date_trunc('month', current_date) - '9 months + 1 day'::interval)::date, 129, 'N WXMHH1R5Q', '299.17', '{}', array['autonoms']);
select add_payment(123, 129, (date_trunc('month', current_date) - '8 months + 28 day'::interval)::date, 123, 'Pagament dN WXMHH1R5Q', '299.17', '{}');
select add_expense(123, (current_date - '9 months'::interval)::date, 132, '00/0001', '117.74', '{124}', array['gestor']);
select add_payment(123, 130, (date_trunc('month', current_date) - '8 months + 15 day'::interval)::date, 124, 'Pagament de 00/0001', '117.74', '{}');
select add_expense(123, (date_trunc('month', current_date) - '8 months + 1 day'::interval)::date, 129, 'N NRP28PWY8', '299.17', '{}', array['autonoms']);
select add_payment(123, 131, (date_trunc('month', current_date) - '8 months'::interval)::date, 123, 'Pagament dN NRP28PWY8', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '7 months + 1 day'::interval)::date, 129, 'N D256225DF', '299.17', '{}', array['autonoms']);
select add_payment(123, 132, (date_trunc('month', current_date) - '6 months + 15 day'::interval)::date, 123, 'Pagament dN D256225DF', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '6 months + 15 day'::interval)::date, 130, 'ZZZ888', '162.21', '{124}', '{}');
select add_payment(123, 133, (date_trunc('month', current_date) - '6 months + 15 day'::interval)::date, 124, 'Pagament de ZZZ888', '80.00', '{}');
select add_expense(123, (date_trunc('month', current_date) - '6 months + 1 day'::interval)::date, 129, 'N K90XS7C3Q', '299.17', '{}', array['autonoms']);
select add_payment(123, 134, (date_trunc('month', current_date) - '5 months + 25 day'::interval)::date, 123, 'Pagament dN K90XS7C3Q', '299.17', '{}');
select add_expense(123, (current_date - '6 months'::interval)::date, 132, '00/0054', '117.74', '{124}', array['gestor']);
select add_payment(123, 135, (date_trunc('month', current_date) - '5 months + 29 day'::interval)::date, 123, 'Pagament dN ', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '5 months + 1 day'::interval)::date, 129, 'N MCPDGGZNG', '299.17', '{}', array['autonoms']);
select add_payment(123, 136, (date_trunc('month', current_date) - '4 months + 29 day'::interval)::date, 123, 'Pagament dN MCPDGGZNG', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '4 months + 1 day'::interval)::date, 129, 'N HQLVAD2N9', '299.17', '{}', array['autonoms']);
select add_payment(123, 137, (date_trunc('month', current_date) - '4 months + 1 day'::interval)::date, 123, 'Pagament dN HQLVAD2N9', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '3 months + 1 day'::interval)::date, 129, 'N QXWHM1H5R', '299.17', '{}', array['autonoms']);
select add_payment(123, 138, (date_trunc('month', current_date) - '2 months + 15 day'::interval)::date, 123, 'Pagament dN QXWHM1H5R', '299.17', '{}');
select add_expense(123, (current_date - '3 months'::interval)::date, 132, '00/0331', '117.74', '{124}', array['gestor']);
select add_expense(123, (date_trunc('month', current_date) - '2 months + 1 day'::interval)::date, 129, 'N 8RN2PP8YW', '299.17', '{}', array['autonoms']);
select add_payment(123, 140, (date_trunc('month', current_date) - '1 months'::interval)::date, 123, 'Pagament dN 8RN2PP8YW', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '1 months + 1 day'::interval)::date, 129, 'N F2D6522D5', '299.17', '{}', array['autonoms']);
select add_payment(123, 141, (date_trunc('month', current_date) - '1 months + 1 day'::interval)::date, 123, 'Pagament dN F2D6522D5', '299.17', '{}');
select add_expense(123, (date_trunc('month', current_date) - '1 day'::interval)::date, 129, 'N F2D6522D5', '299.17', '{}', array['autonoms']);
select add_payment(123, 142, (date_trunc('month', current_date) - '18 day'::interval)::date, 123, 'Pagament dN F2D6522D5', '299.17', '{}');
select add_expense(123, (current_date - '22 day'::interval)::date, 131, '321ABC', '1023.17', '{124}', '{}');
select add_expense(123, (current_date - '11 day'::interval)::date, 130, 'ABC321', '256.12', '{124}', '{}');
select add_expense(123, 'paid', (date_trunc('month', current_date) - '11 months + 14 day'::interval)::date, 130, 'ABC123', '256.12', '{124}', '{}');
select add_expense(123, 'paid', (date_trunc('month', current_date) - '11 months + 8 day'::interval)::date, 131, '123ABC', '1023.17', '{124}', '{}');
select add_expense(123, 'paid', (date_trunc('month', current_date) - '11 months + 1 day'::interval)::date, 129, 'N CMDPGGNZG', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '10 months + 20 day'::interval)::date, 131, '123XYZ', '23.17', '{124}', '{}');
select add_expense(123, 'paid', (date_trunc('month', current_date) - '10 months + 1 day'::interval)::date, 129, 'N QHVLDAN29', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '9 months + 2 day'::interval)::date, 130, 'XYZ123', '62.21', '{124}', '{}');
select add_expense(123, 'paid', (date_trunc('month', current_date) - '9 months + 1 day'::interval)::date, 129, 'N WXMHH1R5Q', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (current_date - '9 months'::interval)::date, 132, '00/0001', '117.74', '{124}', array['gestor']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '8 months + 1 day'::interval)::date, 129, 'N NRP28PWY8', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '7 months + 1 day'::interval)::date, 129, 'N D256225DF', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '6 months + 15 day'::interval)::date, 130, 'ZZZ888', '162.21', '{124}', '{}');
select add_expense(123, 'paid', (date_trunc('month', current_date) - '6 months + 1 day'::interval)::date, 129, 'N K90XS7C3Q', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (current_date - '6 months'::interval)::date, 132, '00/0054', '117.74', '{124}', array['gestor']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '5 months + 1 day'::interval)::date, 129, 'N MCPDGGZNG', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '4 months + 1 day'::interval)::date, 129, 'N HQLVAD2N9', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '3 months + 1 day'::interval)::date, 129, 'N QXWHM1H5R', '299.17', '{}', array['autonoms']);
select add_expense(123, 'pending', (current_date - '3 months'::interval)::date, 132, '00/0331', '117.74', '{124}', array['gestor']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '2 months + 1 day'::interval)::date, 129, 'N 8RN2PP8YW', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '1 months + 1 day'::interval)::date, 129, 'N F2D6522D5', '299.17', '{}', array['autonoms']);
select add_expense(123, 'paid', (date_trunc('month', current_date) - '1 day'::interval)::date, 129, 'N F2D6522D5', '299.17', '{}', array['autonoms']);
select add_expense(123, 'pending', (current_date - '22 day'::interval)::date, 131, '321ABC', '1023.17', '{124}', '{}');
select add_expense(123, 'pending', (current_date - '11 day'::interval)::date, 130, 'ABC321', '256.12', '{124}', '{}');
commit;

View File

@ -15,21 +15,20 @@ begin;
set search_path to numerus, public;
drop function if exists add_expense(integer, text, date, integer, text, text, integer[], tag_name[]);
create or replace function add_expense(company integer, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
create or replace function add_expense(company integer, status text, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
eslug uuid;
begin
insert into expense (company_id, contact_id, invoice_number, invoice_date, amount, currency_code, tags)
insert into expense (company_id, contact_id, invoice_number, invoice_date, amount, currency_code, expense_status, tags)
select company_id
, contact_id
, invoice_number
, invoice_date
, parse_price(amount, currency.decimal_digits)
, currency_code
, status
, tags
from company
join currency using (currency_code)
@ -47,8 +46,10 @@ end;
$$
language plpgsql;
revoke execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) to admin;
revoke execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) to admin;
drop function if exists add_expense(integer, date, integer, text, text, integer[], tag_name[]);
commit;

View File

@ -1,55 +0,0 @@
-- Deploy numerus:add_expense to pg
-- requires: schema_numerus
-- requires: expense
-- requires: expense_tax
-- requires: tax
-- requires: company
-- requires: currency
-- requires: parse_price
-- requires: tax
-- requires: tag_name
-- requires: expense_status
-- requires: expense_expense_status
begin;
set search_path to numerus, public;
create or replace function add_expense(company integer, status text, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
eslug uuid;
begin
insert into expense (company_id, contact_id, invoice_number, invoice_date, amount, currency_code, expense_status, tags)
select company_id
, contact_id
, invoice_number
, invoice_date
, parse_price(amount, currency.decimal_digits)
, currency_code
, status
, tags
from company
join currency using (currency_code)
where company.company_id = add_expense.company
returning expense_id, slug
into eid, eslug;
insert into expense_tax (expense_id, tax_id, tax_rate)
select eid, tax_id, tax.rate
from tax
join unnest(taxes) as etax(tax_id) using (tax_id);
return eslug;
end;
$$
language plpgsql;
revoke execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) to admin;
drop function if exists add_expense(integer, date, integer, text, text, integer[], tag_name[]);
commit;

View File

@ -12,9 +12,7 @@ begin;
set search_path to numerus, public;
drop function if exists edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]);
create or replace function edit_expense(expense_slug uuid, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
create or replace function edit_expense(expense_slug uuid, status text, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
@ -24,6 +22,7 @@ begin
, contact_id = edit_expense.contact_id
, invoice_number = edit_expense.invoice_number
, amount = parse_price(edit_expense.amount, decimal_digits)
, expense_status = status
, tags = edit_expense.tags
from currency
where slug = expense_slug
@ -47,8 +46,8 @@ end;
$$
language plpgsql;
revoke execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) to admin;
revoke execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) to admin;
commit;

View File

@ -1,53 +0,0 @@
-- Deploy numerus:edit_expense to pg
-- requires: schema_numerus
-- requires: expense
-- requires: currency
-- requires: parse_price
-- requires: tax
-- requires: tag_name
-- requires: expense_status
-- requires: expense_expense_status
begin;
set search_path to numerus, public;
create or replace function edit_expense(expense_slug uuid, status text, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
begin
update expense
set invoice_date = edit_expense.invoice_date
, contact_id = edit_expense.contact_id
, invoice_number = edit_expense.invoice_number
, amount = parse_price(edit_expense.amount, decimal_digits)
, expense_status = status
, tags = edit_expense.tags
from currency
where slug = expense_slug
and currency.currency_code = expense.currency_code
returning expense_id
into eid;
if eid is null then
return null;
end if;
delete from expense_tax where expense_id = eid;
insert into expense_tax (expense_id, tax_id, tax_rate)
select eid, tax_id, tax.rate
from tax
join unnest(taxes) as etax(tax_id) using (tax_id);
return expense_slug;
end;
$$
language plpgsql;
revoke execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) to admin;
commit;

View File

@ -280,6 +280,7 @@ type expenseForm struct {
Tax *SelectField
Amount *InputField
File *FileField
ExpenseStatus *SelectField
Tags *TagsField
}
@ -330,6 +331,13 @@ func newExpenseForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
Label: pgettext("input", "File", locale),
MaxSize: 1 << 20,
},
ExpenseStatus: &SelectField{
Name: "expense_status",
Required: true,
Label: pgettext("input", "Expense Status", locale),
Selected: []string{"pending"},
Options: mustGetExpenseStatusOptions(ctx, conn, locale),
},
Tags: &TagsField{
Name: "tags",
Label: pgettext("input", "Tags", locale),
@ -359,6 +367,7 @@ func (form *expenseForm) Parse(r *http.Request) error {
if err := form.File.FillValue(r); err != nil {
return err
}
form.ExpenseStatus.FillValue(r)
form.Tags.FillValue(r)
return nil
}
@ -372,16 +381,20 @@ func (form *expenseForm) Validate() bool {
if validator.CheckRequiredInput(form.Amount, gettext("Amount can not be empty.", form.locale)) {
validator.CheckValidDecimal(form.Amount, form.company.MinCents(), math.MaxFloat64, gettext("Amount must be a number greater than zero.", form.locale))
}
validator.CheckValidSelectOption(form.ExpenseStatus, gettext("Selected expense status is not valid.", form.locale))
return validator.AllOK()
}
func (form *expenseForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slug string) bool {
selectedExpenseStatus := form.ExpenseStatus.Selected
form.ExpenseStatus.Clear()
if notFoundErrorOrPanic(conn.QueryRow(ctx, `
select contact_id
, invoice_number
, invoice_date
, to_price(amount, decimal_digits)
, array_agg(tax_id) filter ( where tax_id is not null )
, expense_status
, tags
from expense
left join expense_tax using (expense_id)
@ -392,6 +405,7 @@ func (form *expenseForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
, invoice_date
, amount
, decimal_digits
, expense_status
, tags
`, slug).Scan(
form.Invoicer,
@ -399,7 +413,9 @@ func (form *expenseForm) MustFillFromDatabase(ctx context.Context, conn *Conn, s
form.InvoiceDate,
form.Amount,
form.Tax,
form.ExpenseStatus,
form.Tags)) {
form.ExpenseStatus.Selected = selectedExpenseStatus
return false
}
if len(form.Tax.Selected) == 1 && form.Tax.Selected[0] == "" {
@ -425,22 +441,31 @@ func HandleUpdateExpense(w http.ResponseWriter, r *http.Request, params httprout
http.NotFound(w, r)
return
}
if !form.Validate() {
if !IsHTMxRequest(r) {
w.WriteHeader(http.StatusUnprocessableEntity)
if r.FormValue("quick") == "status" {
slug = conn.MustGetText(r.Context(), "", "update expense set expense_status = $1 where slug = $2 returning slug", form.ExpenseStatus, slug)
if slug == "" {
http.NotFound(w, r)
return
}
mustRenderEditExpenseForm(w, r, slug, form)
return
htmxRedirect(w, r, companyURI(mustGetCompany(r), "/expenses"))
} else {
if !form.Validate() {
if !IsHTMxRequest(r) {
w.WriteHeader(http.StatusUnprocessableEntity)
}
mustRenderEditExpenseForm(w, r, slug, form)
return
}
taxes := mustSliceAtoi(form.Tax.Selected)
if found := conn.MustGetText(r.Context(), "", "select edit_expense($1, $2, $3, $4, $5, $6, $7, $8)", slug, form.ExpenseStatus, form.InvoiceDate, form.Invoicer, form.InvoiceNumber, form.Amount, taxes, form.Tags); found == "" {
http.NotFound(w, r)
return
}
if len(form.File.Content) > 0 {
conn.MustQuery(r.Context(), "select attach_to_expense($1, $2, $3, $4)", slug, form.File.OriginalFileName, form.File.ContentType, form.File.Content)
}
htmxRedirect(w, r, companyURI(company, "/expenses"))
}
taxes := mustSliceAtoi(form.Tax.Selected)
if found := conn.MustGetText(r.Context(), "", "select edit_expense($1, $2, $3, $4, $5, $6, $7)", slug, form.InvoiceDate, form.Invoicer, form.InvoiceNumber, form.Amount, taxes, form.Tags); found == "" {
http.NotFound(w, r)
return
}
if len(form.File.Content) > 0 {
conn.MustQuery(r.Context(), "select attach_to_expense($1, $2, $3, $4)", slug, form.File.OriginalFileName, form.File.ContentType, form.File.Content)
}
htmxRedirect(w, r, companyURI(company, "/expenses"))
}
type expenseFilterForm struct {
@ -670,7 +695,7 @@ func handleExpenseAction(w http.ResponseWriter, r *http.Request, action string,
return
}
taxes := mustSliceAtoi(form.Tax.Selected)
slug := conn.MustGetText(r.Context(), "", "select add_expense($1, $2, $3, $4, $5, $6, $7)", company.Id, form.InvoiceDate, form.Invoicer, form.InvoiceNumber, form.Amount, taxes, form.Tags)
slug := conn.MustGetText(r.Context(), "", "select add_expense($1, $2, $3, $4, $5, $6, $7, $8)", company.Id, form.ExpenseStatus, form.InvoiceDate, form.Invoicer, form.InvoiceNumber, form.Amount, taxes, form.Tags)
if len(form.File.Content) > 0 {
conn.MustQuery(r.Context(), "select attach_to_expense($1, $2, $3, $4)", slug, form.File.OriginalFileName, form.File.ContentType, form.File.Content)
}

105
po/ca.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-08-13 02:06+0200\n"
"POT-Creation-Date: 2024-08-12 00:06+0200\n"
"PO-Revision-Date: 2023-01-18 17:08+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -112,15 +112,15 @@ msgstr "Subtotal"
#: web/template/invoices/view.gohtml:115 web/template/invoices/edit.gohtml:75
#: web/template/quotes/new.gohtml:74 web/template/quotes/view.gohtml:82
#: web/template/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:75
#: web/template/expenses/new.gohtml:46 web/template/expenses/index.gohtml:74
#: web/template/expenses/edit.gohtml:48 web/template/payments/index.gohtml:29
#: web/template/expenses/new.gohtml:47 web/template/expenses/index.gohtml:74
#: web/template/expenses/edit.gohtml:49 web/template/payments/index.gohtml:29
msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
#: web/template/expenses/new.gohtml:56 web/template/expenses/edit.gohtml:58
#: web/template/expenses/new.gohtml:57 web/template/expenses/edit.gohtml:59
#: web/template/payments/edit.gohtml:37
#: web/template/payments/accounts/edit.gohtml:38
msgctxt "action"
@ -130,7 +130,7 @@ msgstr "Actualitza"
#: web/template/invoices/new.gohtml:95 web/template/invoices/edit.gohtml:96
#: web/template/quotes/new.gohtml:95 web/template/quotes/edit.gohtml:96
#: web/template/contacts/new.gohtml:49 web/template/contacts/edit.gohtml:53
#: web/template/expenses/new.gohtml:59 web/template/expenses/edit.gohtml:61
#: web/template/expenses/new.gohtml:60 web/template/expenses/edit.gohtml:62
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
#: web/template/payments/new.gohtml:35
#: web/template/payments/accounts/new.gohtml:41
@ -238,7 +238,7 @@ msgstr "Accions per la factura %s"
#: web/template/invoices/index.gohtml:139 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:137 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:121
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:143
#: web/template/products/index.gohtml:78 web/template/payments/index.gohtml:71
msgctxt "action"
msgid "Edit"
@ -255,7 +255,7 @@ msgid "No invoices added yet."
msgstr "No hi ha cap factura."
#: web/template/invoices/index.gohtml:164 web/template/quotes/index.gohtml:170
#: web/template/expenses/index.gohtml:138
#: web/template/expenses/index.gohtml:160
msgid "Total"
msgstr "Total"
@ -636,11 +636,11 @@ msgctxt "title"
msgid "Invoice Number"
msgstr "Número de factura"
#: web/template/expenses/index.gohtml:113
#: web/template/expenses/index.gohtml:135
msgid "Actions for expense %s"
msgstr "Accions per la despesa %s"
#: web/template/expenses/index.gohtml:131
#: web/template/expenses/index.gohtml:153
msgid "No expenses added yet."
msgstr "No hi ha cap despesa."
@ -876,37 +876,37 @@ msgid "Name"
msgstr "Nom"
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
#: pkg/payments.go:154 pkg/expenses.go:335 pkg/expenses.go:485
#: pkg/payments.go:154 pkg/expenses.go:343 pkg/expenses.go:510
#: pkg/invoices.go:177 pkg/invoices.go:877 pkg/invoices.go:1462
#: pkg/contacts.go:154 pkg/contacts.go:362
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:495 pkg/invoices.go:181
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:520 pkg/invoices.go:181
#: pkg/contacts.go:158
msgctxt "input"
msgid "Tags Condition"
msgstr "Condició de les etiquetes"
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:499 pkg/invoices.go:185
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:524 pkg/invoices.go:185
#: pkg/contacts.go:162
msgctxt "tag condition"
msgid "All"
msgstr "Totes"
#: pkg/products.go:186 pkg/expenses.go:500 pkg/invoices.go:186
#: pkg/products.go:186 pkg/expenses.go:525 pkg/invoices.go:186
#: pkg/contacts.go:163
msgid "Invoices must have all the specified labels."
msgstr "Les factures han de tenir totes les etiquetes."
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:504 pkg/invoices.go:190
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:529 pkg/invoices.go:190
#: pkg/contacts.go:167
msgctxt "tag condition"
msgid "Any"
msgstr "Qualsevol"
#: pkg/products.go:191 pkg/expenses.go:505 pkg/invoices.go:191
#: pkg/products.go:191 pkg/expenses.go:530 pkg/invoices.go:191
#: pkg/contacts.go:168
msgid "Invoices must have at least one of the specified labels."
msgstr "Les factures han de tenir com a mínim una de les etiquetes."
@ -922,7 +922,7 @@ msgctxt "input"
msgid "Price"
msgstr "Preu"
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:310
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:311
#: pkg/invoices.go:1194
msgctxt "input"
msgid "Taxes"
@ -941,12 +941,12 @@ msgstr "No podeu deixar el preu en blanc."
msgid "Price must be a number greater than zero."
msgstr "El preu ha de ser un número major a zero."
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:370
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:379
#: pkg/invoices.go:1253
msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:371
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:380
#: pkg/invoices.go:1254
msgid "You can only select a tax of each class."
msgstr "Només podeu seleccionar un impost de cada classe."
@ -1170,7 +1170,7 @@ msgctxt "input"
msgid "Quotation Status"
msgstr "Estat del pressupost"
#: pkg/quote.go:154 pkg/expenses.go:490 pkg/invoices.go:157
#: pkg/quote.go:154 pkg/expenses.go:515 pkg/invoices.go:157
msgid "All status"
msgstr "Tots els estats"
@ -1179,12 +1179,12 @@ msgctxt "input"
msgid "Quotation Number"
msgstr "Número de pressupost"
#: pkg/quote.go:164 pkg/expenses.go:475 pkg/invoices.go:167
#: pkg/quote.go:164 pkg/expenses.go:500 pkg/invoices.go:167
msgctxt "input"
msgid "From Date"
msgstr "A partir de la data"
#: pkg/quote.go:169 pkg/expenses.go:480 pkg/invoices.go:172
#: pkg/quote.go:169 pkg/expenses.go:505 pkg/invoices.go:172
msgctxt "input"
msgid "To Date"
msgstr "Fins la data"
@ -1205,8 +1205,8 @@ msgstr "pressuposts.zip"
msgid "quotations.ods"
msgstr "pressuposts.ods"
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:679
#: pkg/expenses.go:709 pkg/invoices.go:684 pkg/invoices.go:1437
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:704
#: pkg/expenses.go:734 pkg/invoices.go:684 pkg/invoices.go:1437
#: pkg/invoices.go:1445
msgid "Invalid action"
msgstr "Acció invàlida."
@ -1326,7 +1326,7 @@ msgstr "La confirmació no és igual a la contrasenya."
msgid "Selected language is not valid."
msgstr "Heu seleccionat un idioma que no és vàlid."
#: pkg/payments.go:127 pkg/expenses.go:304 pkg/invoices.go:866
#: pkg/payments.go:127 pkg/expenses.go:305 pkg/invoices.go:866
msgctxt "input"
msgid "Invoice Date"
msgstr "Data de factura"
@ -1336,12 +1336,12 @@ msgctxt "input"
msgid "Account"
msgstr "Compte"
#: pkg/payments.go:139 pkg/expenses.go:319
#: pkg/payments.go:139 pkg/expenses.go:320
msgctxt "input"
msgid "Amount"
msgstr "Import"
#: pkg/payments.go:149 pkg/expenses.go:330 pkg/invoices.go:888
#: pkg/payments.go:149 pkg/expenses.go:331 pkg/invoices.go:888
msgctxt "input"
msgid "File"
msgstr "Fitxer"
@ -1362,11 +1362,11 @@ msgstr "Heu seleccionat un compte de pagament que no és vàlid."
msgid "Payment date must be a valid date."
msgstr "La data de pagament ha de ser vàlida."
#: pkg/payments.go:213 pkg/expenses.go:372
#: pkg/payments.go:213 pkg/expenses.go:381
msgid "Amount can not be empty."
msgstr "No podeu deixar limport en blanc."
#: pkg/payments.go:214 pkg/expenses.go:373
#: pkg/payments.go:214 pkg/expenses.go:382
msgid "Amount must be a number greater than zero."
msgstr "Limport ha de ser un número major a zero."
@ -1465,39 +1465,43 @@ msgstr "Any anterior"
msgid "Select a contact."
msgstr "Escolliu un contacte."
#: pkg/expenses.go:293 pkg/expenses.go:464
#: pkg/expenses.go:294 pkg/expenses.go:489
msgctxt "input"
msgid "Contact"
msgstr "Contacte"
#: pkg/expenses.go:299
#: pkg/expenses.go:300
msgctxt "input"
msgid "Invoice number"
msgstr "Número de factura"
#: pkg/expenses.go:368
msgid "Selected contact is not valid."
msgstr "Heu seleccionat un contacte que no és vàlid."
#: pkg/expenses.go:369 pkg/invoices.go:939
msgid "Invoice date must be a valid date."
msgstr "La data de facturació ha de ser vàlida."
#: pkg/expenses.go:465
msgid "All contacts"
msgstr "Tots els contactes"
#: pkg/expenses.go:470 pkg/invoices.go:162
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/expenses.go:489
#: pkg/expenses.go:337 pkg/expenses.go:514
msgctxt "input"
msgid "Expense Status"
msgstr "Estat de la despesa"
#: pkg/expenses.go:707
#: pkg/expenses.go:377
msgid "Selected contact is not valid."
msgstr "Heu seleccionat un contacte que no és vàlid."
#: pkg/expenses.go:378 pkg/invoices.go:939
msgid "Invoice date must be a valid date."
msgstr "La data de facturació ha de ser vàlida."
#: pkg/expenses.go:384
msgid "Selected expense status is not valid."
msgstr "Heu seleccionat un estat de despesa que no és vàlid."
#: pkg/expenses.go:490
msgid "All contacts"
msgstr "Tots els contactes"
#: pkg/expenses.go:495 pkg/invoices.go:162
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/expenses.go:732
msgid "expenses.ods"
msgstr "despeses.ods"
@ -1567,9 +1571,6 @@ msgctxt "input"
msgid "Holded Excel file"
msgstr "Fitxer Excel del Holded"
#~ msgid "Selected expense status is not valid."
#~ msgstr "Heu seleccionat un estat de despesa que no és vàlid."
#~ msgctxt "link"
#~ msgid "login"
#~ msgstr "Entrada"

105
po/es.po
View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-08-13 02:06+0200\n"
"POT-Creation-Date: 2024-08-12 00:06+0200\n"
"PO-Revision-Date: 2023-01-18 17:45+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -112,15 +112,15 @@ msgstr "Subtotal"
#: web/template/invoices/view.gohtml:115 web/template/invoices/edit.gohtml:75
#: web/template/quotes/new.gohtml:74 web/template/quotes/view.gohtml:82
#: web/template/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:75
#: web/template/expenses/new.gohtml:46 web/template/expenses/index.gohtml:74
#: web/template/expenses/edit.gohtml:48 web/template/payments/index.gohtml:29
#: web/template/expenses/new.gohtml:47 web/template/expenses/index.gohtml:74
#: web/template/expenses/edit.gohtml:49 web/template/payments/index.gohtml:29
msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
#: web/template/expenses/new.gohtml:56 web/template/expenses/edit.gohtml:58
#: web/template/expenses/new.gohtml:57 web/template/expenses/edit.gohtml:59
#: web/template/payments/edit.gohtml:37
#: web/template/payments/accounts/edit.gohtml:38
msgctxt "action"
@ -130,7 +130,7 @@ msgstr "Actualizar"
#: web/template/invoices/new.gohtml:95 web/template/invoices/edit.gohtml:96
#: web/template/quotes/new.gohtml:95 web/template/quotes/edit.gohtml:96
#: web/template/contacts/new.gohtml:49 web/template/contacts/edit.gohtml:53
#: web/template/expenses/new.gohtml:59 web/template/expenses/edit.gohtml:61
#: web/template/expenses/new.gohtml:60 web/template/expenses/edit.gohtml:62
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
#: web/template/payments/new.gohtml:35
#: web/template/payments/accounts/new.gohtml:41
@ -238,7 +238,7 @@ msgstr "Acciones para la factura %s"
#: web/template/invoices/index.gohtml:139 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:137 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:121
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:143
#: web/template/products/index.gohtml:78 web/template/payments/index.gohtml:71
msgctxt "action"
msgid "Edit"
@ -255,7 +255,7 @@ msgid "No invoices added yet."
msgstr "No hay facturas."
#: web/template/invoices/index.gohtml:164 web/template/quotes/index.gohtml:170
#: web/template/expenses/index.gohtml:138
#: web/template/expenses/index.gohtml:160
msgid "Total"
msgstr "Total"
@ -636,11 +636,11 @@ msgctxt "title"
msgid "Invoice Number"
msgstr "Número de factura"
#: web/template/expenses/index.gohtml:113
#: web/template/expenses/index.gohtml:135
msgid "Actions for expense %s"
msgstr "Acciones para el gasto %s"
#: web/template/expenses/index.gohtml:131
#: web/template/expenses/index.gohtml:153
msgid "No expenses added yet."
msgstr "No hay gastos."
@ -876,37 +876,37 @@ msgid "Name"
msgstr "Nombre"
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
#: pkg/payments.go:154 pkg/expenses.go:335 pkg/expenses.go:485
#: pkg/payments.go:154 pkg/expenses.go:343 pkg/expenses.go:510
#: pkg/invoices.go:177 pkg/invoices.go:877 pkg/invoices.go:1462
#: pkg/contacts.go:154 pkg/contacts.go:362
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:495 pkg/invoices.go:181
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:520 pkg/invoices.go:181
#: pkg/contacts.go:158
msgctxt "input"
msgid "Tags Condition"
msgstr "Condición de las etiquetas"
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:499 pkg/invoices.go:185
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:524 pkg/invoices.go:185
#: pkg/contacts.go:162
msgctxt "tag condition"
msgid "All"
msgstr "Todas"
#: pkg/products.go:186 pkg/expenses.go:500 pkg/invoices.go:186
#: pkg/products.go:186 pkg/expenses.go:525 pkg/invoices.go:186
#: pkg/contacts.go:163
msgid "Invoices must have all the specified labels."
msgstr "Las facturas deben tener todas las etiquetas."
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:504 pkg/invoices.go:190
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:529 pkg/invoices.go:190
#: pkg/contacts.go:167
msgctxt "tag condition"
msgid "Any"
msgstr "Cualquiera"
#: pkg/products.go:191 pkg/expenses.go:505 pkg/invoices.go:191
#: pkg/products.go:191 pkg/expenses.go:530 pkg/invoices.go:191
#: pkg/contacts.go:168
msgid "Invoices must have at least one of the specified labels."
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
@ -922,7 +922,7 @@ msgctxt "input"
msgid "Price"
msgstr "Precio"
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:310
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:311
#: pkg/invoices.go:1194
msgctxt "input"
msgid "Taxes"
@ -941,12 +941,12 @@ msgstr "No podéis dejar el precio en blanco."
msgid "Price must be a number greater than zero."
msgstr "El precio tiene que ser un número mayor a cero."
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:370
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:379
#: pkg/invoices.go:1253
msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:371
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:380
#: pkg/invoices.go:1254
msgid "You can only select a tax of each class."
msgstr "Solo podéis escoger un impuesto de cada clase."
@ -1170,7 +1170,7 @@ msgctxt "input"
msgid "Quotation Status"
msgstr "Estado del presupuesto"
#: pkg/quote.go:154 pkg/expenses.go:490 pkg/invoices.go:157
#: pkg/quote.go:154 pkg/expenses.go:515 pkg/invoices.go:157
msgid "All status"
msgstr "Todos los estados"
@ -1179,12 +1179,12 @@ msgctxt "input"
msgid "Quotation Number"
msgstr "Número de presupuesto"
#: pkg/quote.go:164 pkg/expenses.go:475 pkg/invoices.go:167
#: pkg/quote.go:164 pkg/expenses.go:500 pkg/invoices.go:167
msgctxt "input"
msgid "From Date"
msgstr "A partir de la fecha"
#: pkg/quote.go:169 pkg/expenses.go:480 pkg/invoices.go:172
#: pkg/quote.go:169 pkg/expenses.go:505 pkg/invoices.go:172
msgctxt "input"
msgid "To Date"
msgstr "Hasta la fecha"
@ -1205,8 +1205,8 @@ msgstr "presupuestos.zip"
msgid "quotations.ods"
msgstr "presupuestos.ods"
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:679
#: pkg/expenses.go:709 pkg/invoices.go:684 pkg/invoices.go:1437
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:704
#: pkg/expenses.go:734 pkg/invoices.go:684 pkg/invoices.go:1437
#: pkg/invoices.go:1445
msgid "Invalid action"
msgstr "Acción inválida."
@ -1326,7 +1326,7 @@ msgstr "La confirmación no corresponde con la contraseña."
msgid "Selected language is not valid."
msgstr "Habéis escogido un idioma que no es válido."
#: pkg/payments.go:127 pkg/expenses.go:304 pkg/invoices.go:866
#: pkg/payments.go:127 pkg/expenses.go:305 pkg/invoices.go:866
msgctxt "input"
msgid "Invoice Date"
msgstr "Fecha de factura"
@ -1336,12 +1336,12 @@ msgctxt "input"
msgid "Account"
msgstr "Cuenta"
#: pkg/payments.go:139 pkg/expenses.go:319
#: pkg/payments.go:139 pkg/expenses.go:320
msgctxt "input"
msgid "Amount"
msgstr "Importe"
#: pkg/payments.go:149 pkg/expenses.go:330 pkg/invoices.go:888
#: pkg/payments.go:149 pkg/expenses.go:331 pkg/invoices.go:888
msgctxt "input"
msgid "File"
msgstr "Archivo"
@ -1362,11 +1362,11 @@ msgstr "Habéis escogido una cuenta de pago que no es válida."
msgid "Payment date must be a valid date."
msgstr "La fecha de pago debe ser válida."
#: pkg/payments.go:213 pkg/expenses.go:372
#: pkg/payments.go:213 pkg/expenses.go:381
msgid "Amount can not be empty."
msgstr "No podéis dejar el importe en blanco."
#: pkg/payments.go:214 pkg/expenses.go:373
#: pkg/payments.go:214 pkg/expenses.go:382
msgid "Amount must be a number greater than zero."
msgstr "El importe tiene que ser un número mayor a cero."
@ -1465,39 +1465,43 @@ msgstr "Año anterior"
msgid "Select a contact."
msgstr "Escoged un contacto"
#: pkg/expenses.go:293 pkg/expenses.go:464
#: pkg/expenses.go:294 pkg/expenses.go:489
msgctxt "input"
msgid "Contact"
msgstr "Contacto"
#: pkg/expenses.go:299
#: pkg/expenses.go:300
msgctxt "input"
msgid "Invoice number"
msgstr "Número de factura"
#: pkg/expenses.go:368
msgid "Selected contact is not valid."
msgstr "Habéis escogido un contacto que no es válido."
#: pkg/expenses.go:369 pkg/invoices.go:939
msgid "Invoice date must be a valid date."
msgstr "La fecha de factura debe ser válida."
#: pkg/expenses.go:465
msgid "All contacts"
msgstr "Todos los contactos"
#: pkg/expenses.go:470 pkg/invoices.go:162
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/expenses.go:489
#: pkg/expenses.go:337 pkg/expenses.go:514
msgctxt "input"
msgid "Expense Status"
msgstr "Estado del gasto"
#: pkg/expenses.go:707
#: pkg/expenses.go:377
msgid "Selected contact is not valid."
msgstr "Habéis escogido un contacto que no es válido."
#: pkg/expenses.go:378 pkg/invoices.go:939
msgid "Invoice date must be a valid date."
msgstr "La fecha de factura debe ser válida."
#: pkg/expenses.go:384
msgid "Selected expense status is not valid."
msgstr "Habéis escogido un estado de gasto que no es válido."
#: pkg/expenses.go:490
msgid "All contacts"
msgstr "Todos los contactos"
#: pkg/expenses.go:495 pkg/invoices.go:162
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/expenses.go:732
msgid "expenses.ods"
msgstr "gastos.ods"
@ -1567,9 +1571,6 @@ msgctxt "input"
msgid "Holded Excel file"
msgstr "Archivo Excel de Holded"
#~ msgid "Selected expense status is not valid."
#~ msgstr "Habéis escogido un estado de gasto que no es válido."
#~ msgid "If you want to sign in, just head to %sthe login page%s and enter your credentials in the form."
#~ msgstr "Si quieres acceder a tu usuario solo tienes que ir a %sla página de entrada% y anotar tus credenciales a su sitio."

View File

@ -8,29 +8,26 @@
-- requires: parse_price
-- requires: tax
-- requires: tag_name
-- requires: expense_status
-- requires: expense_expense_status
begin;
set search_path to numerus, public;
drop function if exists add_expense(integer, date, integer, text, text, integer[], tag_name[]);
drop function if exists add_expense(integer, text, date, integer, text, text, integer[], tag_name[]);
create or replace function add_expense(company integer, status text, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
create or replace function add_expense(company integer, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
eslug uuid;
begin
insert into expense (company_id, contact_id, invoice_number, invoice_date, amount, currency_code, expense_status, tags)
insert into expense (company_id, contact_id, invoice_number, invoice_date, amount, currency_code, tags)
select company_id
, contact_id
, invoice_number
, invoice_date
, parse_price(amount, currency.decimal_digits)
, currency_code
, status
, tags
from company
join currency using (currency_code)
@ -48,8 +45,8 @@ end;
$$
language plpgsql;
revoke execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function add_expense(integer, text, date, integer, text, text, integer[], tag_name[]) to admin;
revoke execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) to admin;
commit;

View File

@ -1,52 +0,0 @@
-- Deploy numerus:add_expense to pg
-- requires: schema_numerus
-- requires: expense
-- requires: expense_tax
-- requires: tax
-- requires: company
-- requires: currency
-- requires: parse_price
-- requires: tax
-- requires: tag_name
begin;
set search_path to numerus, public;
drop function if exists add_expense(integer, text, date, integer, text, text, integer[], tag_name[]);
create or replace function add_expense(company integer, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
eslug uuid;
begin
insert into expense (company_id, contact_id, invoice_number, invoice_date, amount, currency_code, tags)
select company_id
, contact_id
, invoice_number
, invoice_date
, parse_price(amount, currency.decimal_digits)
, currency_code
, tags
from company
join currency using (currency_code)
where company.company_id = add_expense.company
returning expense_id, slug
into eid, eslug;
insert into expense_tax (expense_id, tax_id, tax_rate)
select eid, tax_id, tax.rate
from tax
join unnest(taxes) as etax(tax_id) using (tax_id);
return eslug;
end;
$$
language plpgsql;
revoke execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function add_expense(integer, date, integer, text, text, integer[], tag_name[]) to admin;
commit;

View File

@ -7,7 +7,6 @@ begin;
set search_path to numerus;
update expense set expense_status = 'pending' where expense_status = 'partial';
delete from expense_status_i18n where expense_status = 'partial';
delete from expense_status where expense_status = 'partial';

View File

@ -5,16 +5,14 @@
-- requires: parse_price
-- requires: tax
-- requires: tag_name
-- requires: expense_status
-- requires: expense_expense_status
begin;
set search_path to numerus, public;
drop function if exists edit_expense(uuid, date, integer, text, text, integer[], tag_name[]);
drop function if exists edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]);
create or replace function edit_expense(expense_slug uuid, status text, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
create or replace function edit_expense(expense_slug uuid, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
@ -24,7 +22,6 @@ begin
, contact_id = edit_expense.contact_id
, invoice_number = edit_expense.invoice_number
, amount = parse_price(edit_expense.amount, decimal_digits)
, expense_status = status
, tags = edit_expense.tags
from currency
where slug = expense_slug
@ -48,8 +45,8 @@ end;
$$
language plpgsql;
revoke execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]) to admin;
revoke execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) to admin;
commit;

View File

@ -1,52 +0,0 @@
-- Deploy numerus:edit_expense to pg
-- requires: schema_numerus
-- requires: expense
-- requires: currency
-- requires: parse_price
-- requires: tax
-- requires: tag_name
begin;
set search_path to numerus, public;
drop function if exists edit_expense(uuid, text, date, integer, text, text, integer[], tag_name[]);
create or replace function edit_expense(expense_slug uuid, invoice_date date, contact_id integer, invoice_number text, amount text, taxes integer[], tags tag_name[]) returns uuid as
$$
declare
eid integer;
begin
update expense
set invoice_date = edit_expense.invoice_date
, contact_id = edit_expense.contact_id
, invoice_number = edit_expense.invoice_number
, amount = parse_price(edit_expense.amount, decimal_digits)
, tags = edit_expense.tags
from currency
where slug = expense_slug
and currency.currency_code = expense.currency_code
returning expense_id
into eid;
if eid is null then
return null;
end if;
delete from expense_tax where expense_id = eid;
insert into expense_tax (expense_id, tax_id, tax_rate)
select eid, tax_id, tax.rate
from tax
join unnest(taxes) as etax(tax_id) using (tax_id);
return expense_slug;
end;
$$
language plpgsql;
revoke execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) from public;
grant execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) to invoicer;
grant execute on function edit_expense(uuid, date, integer, text, text, integer[], tag_name[]) to admin;
commit;

View File

@ -155,5 +155,3 @@ edit_payment [roles schema_numerus payment expense_payment currency parse_price
payment_attachment [roles schema_numerus payment] 2024-08-11T21:01:50Z jordi fita mas <jordi@tandem.blog> # Add relation of payment attachments
attach_to_payment [roles schema_numerus payment payment_attachment] 2024-08-11T21:33:36Z jordi fita mas <jordi@tandem.blog> # Add function to attach files to payments
remove_payment [roles schema_numerus expense_payment payment payment_attachment update_expense_payment_status] 2024-08-11T00:30:58Z jordi fita mas <jordi@tandem.blog> # Add function to remove payments
add_expense [add_expense@v2] 2024-08-12T23:48:07Z jordi fita mas <jordi@tandem.blog> # Remove the status parameter from add_expense
edit_expense [edit_expense@v2] 2024-08-12T23:53:48Z jordi fita mas <jordi@tandem.blog> # Remove the expense_status from edit_expense

View File

@ -9,15 +9,15 @@ select plan(14);
set search_path to auth, numerus, public;
select has_function('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select function_lang_is('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'plpgsql');
select function_returns('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'uuid');
select isnt_definer('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select volatility_is('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'add_expense', array ['integer', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'authenticator', array []::text[]);
select has_function('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select function_lang_is('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'plpgsql');
select function_returns('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'uuid');
select isnt_definer('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select volatility_is('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'add_expense', array ['integer', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'authenticator', array []::text[]);
set client_min_messages to warning;
@ -64,24 +64,24 @@ values (12, 1, 'Contact 2.1')
select lives_ok(
$$ select add_expense(1, '2023-05-02', 12, 'Invoice 1', '11.11', '{4}', '{tag1,tag2}') $$,
$$ select add_expense(1, 'pending', '2023-05-02', 12, 'Invoice 1', '11.11', '{4}', '{tag1,tag2}') $$,
'Should be able to insert an expense for the first company with a tax'
);
select lives_ok(
$$ select add_expense(1, '2023-05-03', 13, 'Invoice 2', '22.22', '{4,3}', '{}') $$,
$$ select add_expense(1, 'paid', '2023-05-03', 13, 'Invoice 2', '22.22', '{4,3}', '{}') $$,
'Should be able to insert a second expense for the first company with two taxes'
);
select lives_ok(
$$ select add_expense(2, '2023-05-04', 15, 'Invoice 3', '33.33', '{}', '{tag3}') $$,
$$ select add_expense(2, 'pending', '2023-05-04', 15, 'Invoice 3', '33.33', '{}', '{tag3}') $$,
'Should be able to insert an invoice for the second company with no tax'
);
select bag_eq(
$$ select company_id, invoice_number, invoice_date, contact_id, amount, currency_code, expense_status, tags, created_at from expense $$,
$$ values (1, 'Invoice 1', '2023-05-02'::date, 12, 1111, 'EUR', 'pending', '{tag1,tag2}'::tag_name[], current_timestamp)
, (1, 'Invoice 2', '2023-05-03'::date, 13, 2222, 'EUR', 'pending', '{}'::tag_name[], current_timestamp)
, (1, 'Invoice 2', '2023-05-03'::date, 13, 2222, 'EUR', 'paid', '{}'::tag_name[], current_timestamp)
, (2, 'Invoice 3', '2023-05-04'::date, 15, 3333, 'USD', 'pending', '{tag3}'::tag_name[], current_timestamp)
$$,
'Should have created all expenses'

View File

@ -9,15 +9,15 @@ select plan(13);
set search_path to auth, numerus, public;
select has_function('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select function_lang_is('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'plpgsql');
select function_returns('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'uuid');
select isnt_definer('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select volatility_is('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'authenticator', array []::text[]);
select has_function('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select function_lang_is('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'plpgsql');
select function_returns('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'uuid');
select isnt_definer('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]']);
select volatility_is('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'volatile');
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'guest', array []::text[]);
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'edit_expense', array ['uuid', 'text', 'date', 'integer', 'text', 'text', 'integer[]', 'tag_name[]'], 'authenticator', array []::text[]);
set client_min_messages to warning;
@ -70,19 +70,19 @@ values (15, 4, 0.21)
;
select lives_ok(
$$ select edit_expense('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', '2023-05-06', 13, 'INV11', '1.12', '{4}', array['tag1']) $$,
$$ select edit_expense('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'paid', '2023-05-06', 13, 'INV11', '1.12', '{4}', array['tag1']) $$,
'Should be able to edit the first expense'
);
select lives_ok(
$$ select edit_expense('b57b980b-247b-4be4-a0b7-03a7819c53ae', '2023-05-07', 12, 'INV22', '3.33', '{4,3}', array['tag1', 'tag3']) $$,
$$ select edit_expense('b57b980b-247b-4be4-a0b7-03a7819c53ae', 'pending', '2023-05-07', 12, 'INV22', '3.33', '{4,3}', array['tag1', 'tag3']) $$,
'Should be able to edit the second expense'
);
select bag_eq(
$$ select invoice_number, invoice_date, contact_id, amount, expense_status, tags from expense $$,
$$ values ('INV11', '2023-05-06'::date, 13, 112, 'pending', '{tag1}'::tag_name[])
, ('INV22', '2023-05-07'::date, 12, 333, 'paid', '{tag1,tag3}'::tag_name[])
$$ values ('INV11', '2023-05-06'::date, 13, 112, 'paid', '{tag1}'::tag_name[])
, ('INV22', '2023-05-07'::date, 12, 333, 'pending', '{tag1,tag3}'::tag_name[])
$$,
'Should have updated all expenses'
);

View File

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

View File

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

View File

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

View File

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

View File

@ -675,16 +675,11 @@ main > nav {
min-width: 15rem;
}
[class^='payment-status-'],
[class^='quote-status-'],
[class^='expense-status-'],
[class^='invoice-status-'] {
text-align: center;
text-transform: uppercase;
}
[class^='quote-status-'],
[class^='invoice-status-'] {
cursor: pointer;
}
@ -693,14 +688,11 @@ main > nav {
background-color: var(--numerus--color--light-blue);
}
.payment-status-partial,
.expense-status-partial,
.quote-status-sent,
.invoice-status-sent {
background-color: var(--numerus--color--hay);
}
.payment-status-complete,
.quote-status-accepted,
.expense-status-paid,
.invoice-status-paid {

View File

@ -32,6 +32,7 @@
{{ template "input-field" .Amount }}
{{ template "select-field" .Tax }}
{{ template "tags-field" .Tags }}
{{ template "select-field" .ExpenseStatus }}
{{ template "file-field" .File }}
</div>
{{- end }}

View File

@ -83,7 +83,29 @@
<td>{{ .InvoicerName }}</td>
<td>{{ .InvoiceDate|formatDate }}</td>
<td>{{ .InvoiceNumber }}</td>
<td class="expense-status-{{ .Status }}">{{ .StatusLabel }}</td>
<td>
<details class="expense-status menu">
<summary class="expense-status-{{ .Status }}">{{ .StatusLabel }}</summary>
<form action="{{companyURI "/expenses/"}}{{ .Slug }}" enctype="multipart/form-data"
method="POST" data-hx-boost="true">
{{ csrfToken }}
{{ putMethod }}
<input type="hidden" name="quick" value="status">
<ul role="menu">
{{- range $status, $name := $.ExpenseStatuses }}
{{- if ne $status $expense.Status }}
<li role="presentation">
<button role="menuitem" type="submit"
name="expense_status" value="{{ $status }}"
class="expense-status-{{ $status }}"
>{{ $name }}</button>
</li>
{{- end }}
{{- end }}
</ul>
</form>
</details>
</td>
<td
data-hx-get="{{companyURI "/expenses/"}}{{ .Slug }}/tags/edit"
data-hx-target="this"

View File

@ -1,10 +1,10 @@
{{ define "title" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newExpensePage*/ -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expenseForm*/ -}}
{{( pgettext "New Expense" "title" )}}
{{- end }}
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newExpensePage*/ -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expenseForm*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
@ -15,7 +15,7 @@
{{- end }}
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newExpensePage*/ -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expenseForm*/ -}}
<section id="new-expense-dialog-content" data-hx-target="main">
<h2>{{(pgettext "New Expense" "title")}}</h2>
<form enctype="multipart/form-data" method="POST" action="{{ companyURI "/expenses" }}"
@ -30,6 +30,7 @@
{{ template "input-field" .Amount }}
{{ template "select-field" .Tax }}
{{ template "tags-field" .Tags }}
{{ template "select-field" .ExpenseStatus }}
{{ template "file-field" .File }}
</div>
{{ end }}
@ -48,7 +49,7 @@
</tr>
</tbody>
</table>
<fieldset class="button-bar">
<button formnovalidate
id="recompute-button"