Compare commits

..

No commits in common. "bb7af20a1761c84956649cc95ae122034b266a5d" and "b48a9740865397b61139725d25af86a7ee94d194" have entirely different histories.

17 changed files with 197 additions and 622 deletions

View File

@ -1,30 +0,0 @@
-- Deploy numerus:attach_to_invoice to pg
-- requires: schema_numerus
-- requires: roles
-- requires: invoice
-- requires: invoice_attachment
begin;
set search_path to numerus, public;
create or replace function attach_to_invoice(invoice_slug uuid, original_filename text, mime_type text, content bytea) returns void as
$$
insert into invoice_attachment (invoice_id, original_filename, mime_type, content)
select invoice_id, original_filename, mime_type, content
from invoice
where slug = invoice_slug
on conflict (invoice_id) do update
set original_filename = excluded.original_filename
, mime_type = excluded.mime_type
, content = excluded.content
;
$$
language sql
;
revoke execute on function attach_to_invoice(uuid, text, text, bytea) from public;
grant execute on function attach_to_invoice(uuid, text, text, bytea) to invoicer;
grant execute on function attach_to_invoice(uuid, text, text, bytea) to admin;
commit;

View File

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

View File

@ -349,7 +349,6 @@ type invoice struct {
HasDiscounts bool HasDiscounts bool
Total string Total string
LegalDisclaimer string LegalDisclaimer string
OriginalFileName string
} }
type taxDetails struct { type taxDetails struct {
@ -395,13 +394,11 @@ func mustGetInvoice(ctx context.Context, conn *Conn, company *Company, slug stri
, postal_code , postal_code
, to_price(subtotal, decimal_digits) , to_price(subtotal, decimal_digits)
, to_price(total, decimal_digits) , to_price(total, decimal_digits)
, coalesce(attachment.original_filename, '')
from invoice from invoice
join payment_method using (payment_method_id) join payment_method using (payment_method_id)
join contact_tax_details using (contact_id) join contact_tax_details using (contact_id)
join invoice_amount using (invoice_id) join invoice_amount using (invoice_id)
join currency using (currency_code) join currency using (currency_code)
left join invoice_attachment as attachment using (invoice_id)
where invoice.slug = $1`, slug).Scan( where invoice.slug = $1`, slug).Scan(
&invoiceId, &invoiceId,
&decimalDigits, &decimalDigits,
@ -416,8 +413,7 @@ func mustGetInvoice(ctx context.Context, conn *Conn, company *Company, slug stri
&inv.Invoicee.Province, &inv.Invoicee.Province,
&inv.Invoicee.PostalCode, &inv.Invoicee.PostalCode,
&inv.Subtotal, &inv.Subtotal,
&inv.Total, &inv.Total)) {
&inv.OriginalFileName)) {
return nil return nil
} }
if err := conn.QueryRow(ctx, ` if err := conn.QueryRow(ctx, `
@ -609,9 +605,6 @@ func HandleAddInvoice(w http.ResponseWriter, r *http.Request, _ httprouter.Param
return return
} }
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)) 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 len(form.File.Content) > 0 {
conn.MustQuery(r.Context(), "select attach_to_invoice($1, $2, $3, $4)", slug, form.File.OriginalFileName, form.File.ContentType, form.File.Content)
}
htmxRedirect(w, r, companyURI(company, "/invoices/"+slug)) htmxRedirect(w, r, companyURI(company, "/invoices/"+slug))
} }
@ -687,7 +680,6 @@ type invoiceForm struct {
Tags *TagsField Tags *TagsField
Products []*invoiceProductForm Products []*invoiceProductForm
RemovedProduct *invoiceProductForm RemovedProduct *invoiceProductForm
File *FileField
} }
func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *invoiceForm { func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *invoiceForm {
@ -729,11 +721,6 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
Selected: []string{mustGetDefaultPaymentMethod(ctx, conn, company)}, Selected: []string{mustGetDefaultPaymentMethod(ctx, conn, company)},
Options: mustGetPaymentMethodOptions(ctx, conn, company), Options: mustGetPaymentMethodOptions(ctx, conn, company),
}, },
File: &FileField{
Name: "file",
Label: pgettext("input", "File", locale),
MaxSize: 1 << 20,
},
} }
} }
@ -748,7 +735,7 @@ func mustGetInvoiceStatusOptions(ctx context.Context, conn *Conn, locale *Locale
} }
func (form *invoiceForm) Parse(r *http.Request) error { func (form *invoiceForm) Parse(r *http.Request) error {
if err := r.ParseMultipartForm(form.File.MaxSize); err != nil { if err := r.ParseForm(); err != nil {
return err return err
} }
form.InvoiceStatus.FillValue(r) form.InvoiceStatus.FillValue(r)
@ -757,9 +744,6 @@ func (form *invoiceForm) Parse(r *http.Request) error {
form.Notes.FillValue(r) form.Notes.FillValue(r)
form.Tags.FillValue(r) form.Tags.FillValue(r)
form.PaymentMethod.FillValue(r) form.PaymentMethod.FillValue(r)
if err := form.File.FillValue(r); err != nil {
return err
}
if _, ok := r.Form["product.id.0"]; ok { if _, ok := r.Form["product.id.0"]; ok {
taxOptions := mustGetTaxOptions(r.Context(), getConn(r), form.company) taxOptions := mustGetTaxOptions(r.Context(), getConn(r), form.company)
for index := 0; true; index++ { for index := 0; true; index++ {
@ -1162,9 +1146,6 @@ func HandleUpdateInvoice(w http.ResponseWriter, r *http.Request, params httprout
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
if len(form.File.Content) > 0 {
conn.MustQuery(r.Context(), "select attach_to_invoice($1, $2, $3, $4)", slug, form.File.OriginalFileName, form.File.ContentType, form.File.Content)
}
htmxRedirect(w, r, companyURI(company, "/invoices/"+slug)) htmxRedirect(w, r, companyURI(company, "/invoices/"+slug))
} }
} }
@ -1337,24 +1318,3 @@ func HandleUpdateInvoiceTags(w http.ResponseWriter, r *http.Request, params http
} }
mustRenderStandaloneTemplate(w, r, "tags/view.gohtml", form) mustRenderStandaloneTemplate(w, r, "tags/view.gohtml", form)
} }
func ServeInvoiceAttachment(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
slug := params[0].Value
conn := getConn(r)
var contentType string
var content []byte
if notFoundErrorOrPanic(conn.QueryRow(r.Context(), `
select mime_type
, content
from invoice
join invoice_attachment using (invoice_id)
where slug = $1
`, slug).Scan(&contentType, &content)) {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", contentType)
w.Header().Set("Content-Length", strconv.FormatInt(int64(len(content)), 10))
w.WriteHeader(http.StatusOK)
w.Write(content)
}

View File

@ -38,7 +38,6 @@ func NewRouter(db *Db) http.Handler {
companyRouter.POST("/invoices/:slug/edit", HandleEditInvoiceAction) companyRouter.POST("/invoices/:slug/edit", HandleEditInvoiceAction)
companyRouter.PUT("/invoices/:slug/tags", HandleUpdateInvoiceTags) companyRouter.PUT("/invoices/:slug/tags", HandleUpdateInvoiceTags)
companyRouter.GET("/invoices/:slug/tags/edit", ServeEditInvoiceTags) companyRouter.GET("/invoices/:slug/tags/edit", ServeEditInvoiceTags)
companyRouter.GET("/invoices/:slug/download/:filename", ServeInvoiceAttachment)
companyRouter.GET("/quotes", IndexQuotes) companyRouter.GET("/quotes", IndexQuotes)
companyRouter.POST("/quotes", HandleAddQuote) companyRouter.POST("/quotes", HandleAddQuote)
companyRouter.GET("/quotes/:slug", ServeQuote) companyRouter.GET("/quotes/:slug", ServeQuote)

208
po/ca.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-07-12 20:01+0200\n" "POT-Creation-Date: 2023-07-06 11:45+0200\n"
"PO-Revision-Date: 2023-01-18 17:08+0100\n" "PO-Revision-Date: 2023-01-18 17:08+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n" "Language-Team: Catalan <ca@dodds.net>\n"
@ -65,7 +65,7 @@ msgid "Name"
msgstr "Nom" msgstr "Nom"
#: web/template/invoices/products.gohtml:50 #: web/template/invoices/products.gohtml:50
#: web/template/invoices/view.gohtml:66 web/template/quotes/products.gohtml:50 #: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
#: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42 #: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42
msgctxt "title" msgctxt "title"
msgid "Price" msgid "Price"
@ -76,8 +76,8 @@ msgstr "Preu"
msgid "No products added yet." msgid "No products added yet."
msgstr "No hi ha cap producte." msgstr "No hi ha cap producte."
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:88 #: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:87
#: web/template/invoices/edit.gohtml:89 web/template/quotes/products.gohtml:72 #: web/template/invoices/edit.gohtml:88 web/template/quotes/products.gohtml:72
#: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89 #: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89
msgctxt "action" msgctxt "action"
msgid "Add products" msgid "Add products"
@ -94,31 +94,31 @@ msgctxt "action"
msgid "Undo" msgid "Undo"
msgstr "Desfes" msgstr "Desfes"
#: web/template/invoices/new.gohtml:64 web/template/invoices/view.gohtml:71 #: web/template/invoices/new.gohtml:63 web/template/invoices/view.gohtml:67
#: web/template/invoices/edit.gohtml:65 web/template/quotes/new.gohtml:64 #: web/template/invoices/edit.gohtml:64 web/template/quotes/new.gohtml:64
#: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:65 #: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:65
msgctxt "title" msgctxt "title"
msgid "Subtotal" msgid "Subtotal"
msgstr "Subtotal" msgstr "Subtotal"
#: web/template/invoices/new.gohtml:74 web/template/invoices/view.gohtml:75 #: web/template/invoices/new.gohtml:73 web/template/invoices/view.gohtml:71
#: web/template/invoices/view.gohtml:115 web/template/invoices/edit.gohtml:75 #: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:74
#: web/template/quotes/new.gohtml:74 web/template/quotes/view.gohtml:82 #: 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/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:75
msgctxt "title" msgctxt "title"
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93 #: web/template/invoices/new.gohtml:91 web/template/invoices/edit.gohtml:92
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93 #: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
msgctxt "action" msgctxt "action"
msgid "Update" msgid "Update"
msgstr "Actualitza" msgstr "Actualitza"
#: web/template/invoices/new.gohtml:95 web/template/invoices/edit.gohtml:96 #: web/template/invoices/new.gohtml:94 web/template/invoices/edit.gohtml:95
#: web/template/quotes/new.gohtml:95 web/template/quotes/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/contacts/new.gohtml:49 web/template/contacts/edit.gohtml:53
#: web/template/expenses/new.gohtml:34 web/template/expenses/edit.gohtml:40 #: web/template/expenses/new.gohtml:33 web/template/expenses/edit.gohtml:38
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36 #: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
msgctxt "action" msgctxt "action"
msgid "Save" msgid "Save"
@ -136,7 +136,7 @@ msgstr "Nova factura"
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23 #: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:36 #: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:36
#: web/template/expenses/index.gohtml:37 web/template/products/index.gohtml:34 #: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
msgctxt "action" msgctxt "action"
msgid "Filter" msgid "Filter"
msgstr "Filtra" msgstr "Filtra"
@ -146,7 +146,7 @@ msgctxt "invoice"
msgid "All" msgid "All"
msgstr "Totes" msgstr "Totes"
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:38 #: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:34
#: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:37 #: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:37
msgctxt "title" msgctxt "title"
msgid "Date" msgid "Date"
@ -163,32 +163,31 @@ msgid "Customer"
msgstr "Client" msgstr "Client"
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53 #: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
#: web/template/expenses/index.gohtml:46
msgctxt "title" msgctxt "title"
msgid "Status" msgid "Status"
msgstr "Estat" msgstr "Estat"
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54 #: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:47 #: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:45
#: web/template/products/index.gohtml:41 #: web/template/products/index.gohtml:41
msgctxt "title" msgctxt "title"
msgid "Tags" msgid "Tags"
msgstr "Etiquetes" msgstr "Etiquetes"
#: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55 #: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55
#: web/template/expenses/index.gohtml:48 #: web/template/expenses/index.gohtml:46
msgctxt "title" msgctxt "title"
msgid "Amount" msgid "Amount"
msgstr "Import" msgstr "Import"
#: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56 #: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56
#: web/template/expenses/index.gohtml:49 #: web/template/expenses/index.gohtml:47
msgctxt "title" msgctxt "title"
msgid "Download" msgid "Download"
msgstr "Descàrrega" msgstr "Descàrrega"
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57 #: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:50 #: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:48
#: web/template/products/index.gohtml:43 #: web/template/products/index.gohtml:43
msgctxt "title" msgctxt "title"
msgid "Actions" msgid "Actions"
@ -210,7 +209,7 @@ msgstr "Accions per la factura %s"
#: web/template/invoices/index.gohtml:121 web/template/invoices/view.gohtml:19 #: web/template/invoices/index.gohtml:121 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:120 web/template/quotes/view.gohtml:22 #: web/template/quotes/index.gohtml:120 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:77 web/template/expenses/index.gohtml:111 #: web/template/contacts/index.gohtml:77 web/template/expenses/index.gohtml:87
#: web/template/products/index.gohtml:73 #: web/template/products/index.gohtml:73
msgctxt "action" msgctxt "action"
msgid "Edit" msgid "Edit"
@ -227,11 +226,11 @@ msgid "No invoices added yet."
msgstr "No hi ha cap factura." msgstr "No hi ha cap factura."
#: web/template/invoices/index.gohtml:146 web/template/quotes/index.gohtml:153 #: web/template/invoices/index.gohtml:146 web/template/quotes/index.gohtml:153
#: web/template/expenses/index.gohtml:128 #: web/template/expenses/index.gohtml:104
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:37 #: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:33
msgctxt "title" msgctxt "title"
msgid "Invoice %s" msgid "Invoice %s"
msgstr "Factura %s" msgstr "Factura %s"
@ -241,27 +240,22 @@ msgctxt "action"
msgid "Download invoice" msgid "Download invoice"
msgstr "Descarrega factura" msgstr "Descarrega factura"
#: web/template/invoices/view.gohtml:25 #: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:72
msgctxt "action"
msgid "Download invoice attachment"
msgstr "Descarrega ladjunt de la factura"
#: web/template/invoices/view.gohtml:65 web/template/quotes/view.gohtml:72
msgctxt "title" msgctxt "title"
msgid "Concept" msgid "Concept"
msgstr "Concepte" msgstr "Concepte"
#: web/template/invoices/view.gohtml:68 web/template/quotes/view.gohtml:75 #: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:75
msgctxt "title" msgctxt "title"
msgid "Discount" msgid "Discount"
msgstr "Descompte" msgstr "Descompte"
#: web/template/invoices/view.gohtml:70 web/template/quotes/view.gohtml:77 #: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:77
msgctxt "title" msgctxt "title"
msgid "Units" msgid "Units"
msgstr "Unitats" msgstr "Unitats"
#: web/template/invoices/view.gohtml:105 web/template/quotes/view.gohtml:112 #: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:112
msgctxt "title" msgctxt "title"
msgid "Tax Base" msgid "Tax Base"
msgstr "Base imposable" msgstr "Base imposable"
@ -479,7 +473,7 @@ msgctxt "action"
msgid "New contact" msgid "New contact"
msgstr "Nou contacte" msgstr "Nou contacte"
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:43 #: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:42
msgctxt "title" msgctxt "title"
msgid "Contact" msgid "Contact"
msgstr "Contacte" msgstr "Contacte"
@ -565,21 +559,21 @@ msgctxt "action"
msgid "New expense" msgid "New expense"
msgstr "Nova despesa" msgstr "Nova despesa"
#: web/template/expenses/index.gohtml:44 #: web/template/expenses/index.gohtml:43
msgctxt "title" msgctxt "title"
msgid "Invoice Date" msgid "Invoice Date"
msgstr "Data de factura" msgstr "Data de factura"
#: web/template/expenses/index.gohtml:45 #: web/template/expenses/index.gohtml:44
msgctxt "title" msgctxt "title"
msgid "Invoice Number" msgid "Invoice Number"
msgstr "Número de factura" msgstr "Número de factura"
#: web/template/expenses/index.gohtml:103 #: web/template/expenses/index.gohtml:79
msgid "Actions for expense %s" msgid "Actions for expense %s"
msgstr "Accions per la despesa %s" msgstr "Accions per la despesa %s"
#: web/template/expenses/index.gohtml:121 #: web/template/expenses/index.gohtml:97
msgid "No expenses added yet." msgid "No expenses added yet."
msgstr "No hi ha cap despesa." msgstr "No hi ha cap despesa."
@ -712,84 +706,83 @@ msgstr "No podeu deixar la contrasenya en blanc."
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nom dusuari o contrasenya incorrectes." msgstr "Nom dusuari o contrasenya incorrectes."
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:878 pkg/invoices.go:993 #: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
#: pkg/contacts.go:140 pkg/contacts.go:248 #: pkg/contacts.go:140 pkg/contacts.go:248
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:685 #: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
#: pkg/expenses.go:228 pkg/expenses.go:417 pkg/invoices.go:174 #: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
#: pkg/invoices.go:723 pkg/invoices.go:1295 pkg/contacts.go:145 #: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:145
#: pkg/contacts.go:348 #: pkg/contacts.go:348
msgctxt "input" msgctxt "input"
msgid "Tags" msgid "Tags"
msgstr "Etiquetes" msgstr "Etiquetes"
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:427 pkg/invoices.go:178 #: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
#: pkg/contacts.go:149 #: pkg/contacts.go:149
msgctxt "input" msgctxt "input"
msgid "Tags Condition" msgid "Tags Condition"
msgstr "Condició de les etiquetes" msgstr "Condició de les etiquetes"
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:431 pkg/invoices.go:182 #: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
#: pkg/contacts.go:153 #: pkg/contacts.go:153
msgctxt "tag condition" msgctxt "tag condition"
msgid "All" msgid "All"
msgstr "Totes" msgstr "Totes"
#: pkg/products.go:178 pkg/expenses.go:432 pkg/invoices.go:183 #: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
#: pkg/contacts.go:154 #: pkg/contacts.go:154
msgid "Invoices must have all the specified labels." msgid "Invoices must have all the specified labels."
msgstr "Les factures han de tenir totes les etiquetes." msgstr "Les factures han de tenir totes les etiquetes."
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:436 pkg/invoices.go:187 #: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
#: pkg/contacts.go:158 #: pkg/contacts.go:158
msgctxt "tag condition" msgctxt "tag condition"
msgid "Any" msgid "Any"
msgstr "Qualsevol" msgstr "Qualsevol"
#: pkg/products.go:183 pkg/expenses.go:437 pkg/invoices.go:188 #: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
#: pkg/contacts.go:159 #: pkg/contacts.go:159
msgid "Invoices must have at least one of the specified labels." 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." msgstr "Les factures han de tenir com a mínim una de les etiquetes."
#: pkg/products.go:269 pkg/quote.go:892 pkg/invoices.go:1007 #: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:923
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripció" msgstr "Descripció"
#: pkg/products.go:274 pkg/quote.go:896 pkg/invoices.go:1011 #: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:927
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Preu" msgstr "Preu"
#: pkg/products.go:284 pkg/quote.go:925 pkg/expenses.go:200 #: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:956
#: pkg/invoices.go:1040
msgctxt "input" msgctxt "input"
msgid "Taxes" msgid "Taxes"
msgstr "Imposts" msgstr "Imposts"
#: pkg/products.go:309 pkg/quote.go:974 pkg/profile.go:92 pkg/invoices.go:1089 #: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
#: pkg/contacts.go:398 #: pkg/contacts.go:398
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc." msgstr "No podeu deixar el nom en blanc."
#: pkg/products.go:310 pkg/quote.go:975 pkg/invoices.go:1090 #: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:1006
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc." msgstr "No podeu deixar el preu en blanc."
#: pkg/products.go:311 pkg/quote.go:976 pkg/invoices.go:1091 #: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:1007
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El preu ha de ser un número major a zero." msgstr "El preu ha de ser un número major a zero."
#: pkg/products.go:313 pkg/quote.go:984 pkg/expenses.go:264 pkg/expenses.go:269 #: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
#: pkg/invoices.go:1099 #: pkg/invoices.go:1015
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid." msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/products.go:314 pkg/quote.go:985 pkg/expenses.go:265 pkg/expenses.go:270 #: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
#: pkg/invoices.go:1100 #: pkg/invoices.go:1016
msgid "You can only select a tax of each class." msgid "You can only select a tax of each class."
msgstr "Només podeu seleccionar un impost de cada classe." msgstr "Només podeu seleccionar un impost de cada classe."
@ -998,7 +991,7 @@ msgstr "No podeu deixar el nom del mètode de pagament en blanc."
msgid "Payment instructions can not be empty." msgid "Payment instructions can not be empty."
msgstr "No podeu deixar les instruccions de pagament en blanc." msgstr "No podeu deixar les instruccions de pagament en blanc."
#: pkg/quote.go:147 pkg/quote.go:663 pkg/invoices.go:147 pkg/invoices.go:706 #: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:640
msgctxt "input" msgctxt "input"
msgid "Customer" msgid "Customer"
msgstr "Client" msgstr "Client"
@ -1007,12 +1000,12 @@ msgstr "Client"
msgid "All customers" msgid "All customers"
msgstr "Tots els clients" msgstr "Tots els clients"
#: pkg/quote.go:153 pkg/quote.go:657 #: pkg/quote.go:153 pkg/quote.go:602
msgctxt "input" msgctxt "input"
msgid "Quotation Status" msgid "Quotation Status"
msgstr "Estat del pressupost" msgstr "Estat del pressupost"
#: pkg/quote.go:154 pkg/expenses.go:422 pkg/invoices.go:154 #: pkg/quote.go:154 pkg/invoices.go:154
msgid "All status" msgid "All status"
msgstr "Tots els estats" msgstr "Tots els estats"
@ -1021,12 +1014,12 @@ msgctxt "input"
msgid "Quotation Number" msgid "Quotation Number"
msgstr "Número de pressupost" msgstr "Número de pressupost"
#: pkg/quote.go:164 pkg/expenses.go:407 pkg/invoices.go:164 #: pkg/quote.go:164 pkg/expenses.go:337 pkg/invoices.go:164
msgctxt "input" msgctxt "input"
msgid "From Date" msgid "From Date"
msgstr "A partir de la data" msgstr "A partir de la data"
#: pkg/quote.go:169 pkg/expenses.go:412 pkg/invoices.go:169 #: pkg/quote.go:169 pkg/expenses.go:342 pkg/invoices.go:169
msgctxt "input" msgctxt "input"
msgid "To Date" msgid "To Date"
msgstr "Fins la data" msgstr "Fins la data"
@ -1039,99 +1032,99 @@ msgstr "Els pressuposts han de tenir totes les etiquetes."
msgid "Quotations must have at least one of the specified labels." msgid "Quotations must have at least one of the specified labels."
msgstr "Els pressuposts han de tenir com a mínim una de les etiquetes." msgstr "Els pressuposts han de tenir com a mínim una de les etiquetes."
#: pkg/quote.go:605 #: pkg/quote.go:550
msgid "quotations.zip" msgid "quotations.zip"
msgstr "pressuposts.zip" msgstr "pressuposts.zip"
#: pkg/quote.go:611 pkg/quote.go:1140 pkg/quote.go:1148 pkg/invoices.go:654 #: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:589
#: pkg/invoices.go:1270 pkg/invoices.go:1278 #: pkg/invoices.go:1183 pkg/invoices.go:1191
msgid "Invalid action" msgid "Invalid action"
msgstr "Acció invàlida." msgstr "Acció invàlida."
#: pkg/quote.go:664 #: pkg/quote.go:609
msgid "Select a customer to quote." msgid "Select a customer to quote."
msgstr "Escolliu un client a pressupostar." msgstr "Escolliu un client a pressupostar."
#: pkg/quote.go:669 #: pkg/quote.go:614
msgctxt "input" msgctxt "input"
msgid "Quotation Date" msgid "Quotation Date"
msgstr "Data del pressupost" msgstr "Data del pressupost"
#: pkg/quote.go:675 #: pkg/quote.go:620
msgctxt "input" msgctxt "input"
msgid "Terms and conditions" msgid "Terms and conditions"
msgstr "Condicions dacceptació" msgstr "Condicions dacceptació"
#: pkg/quote.go:680 pkg/invoices.go:718 #: pkg/quote.go:625 pkg/invoices.go:652
msgctxt "input" msgctxt "input"
msgid "Notes" msgid "Notes"
msgstr "Notes" msgstr "Notes"
#: pkg/quote.go:689 pkg/invoices.go:728 #: pkg/quote.go:634 pkg/invoices.go:662
msgctxt "input" msgctxt "input"
msgid "Payment Method" msgid "Payment Method"
msgstr "Mètode de pagament" msgstr "Mètode de pagament"
#: pkg/quote.go:690 #: pkg/quote.go:635
msgid "Select a payment method." msgid "Select a payment method."
msgstr "Escolliu un mètode de pagament." msgstr "Escolliu un mètode de pagament."
#: pkg/quote.go:726 #: pkg/quote.go:671
msgid "Selected quotation status is not valid." msgid "Selected quotation status is not valid."
msgstr "Heu seleccionat un estat de pressupost que no és vàlid." msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
#: pkg/quote.go:728 pkg/invoices.go:783 #: pkg/quote.go:673 pkg/invoices.go:699
msgid "Selected customer is not valid." msgid "Selected customer is not valid."
msgstr "Heu seleccionat un client que no és vàlid." msgstr "Heu seleccionat un client que no és vàlid."
#: pkg/quote.go:730 #: pkg/quote.go:675
msgid "Quotation date can not be empty." msgid "Quotation date can not be empty."
msgstr "No podeu deixar la data del pressupost en blanc." msgstr "No podeu deixar la data del pressupost en blanc."
#: pkg/quote.go:731 #: pkg/quote.go:676
msgid "Quotation date must be a valid date." msgid "Quotation date must be a valid date."
msgstr "La data del pressupost ha de ser vàlida." msgstr "La data del pressupost ha de ser vàlida."
#: pkg/quote.go:734 pkg/invoices.go:787 #: pkg/quote.go:679 pkg/invoices.go:703
msgid "Selected payment method is not valid." msgid "Selected payment method is not valid."
msgstr "Heu seleccionat un mètode de pagament que no és vàlid." msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
#: pkg/quote.go:868 pkg/quote.go:873 pkg/invoices.go:983 pkg/invoices.go:988 #: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:899 pkg/invoices.go:904
msgctxt "input" msgctxt "input"
msgid "Id" msgid "Id"
msgstr "Identificador" msgstr "Identificador"
#: pkg/quote.go:906 pkg/invoices.go:1021 #: pkg/quote.go:851 pkg/invoices.go:937
msgctxt "input" msgctxt "input"
msgid "Quantity" msgid "Quantity"
msgstr "Quantitat" msgstr "Quantitat"
#: pkg/quote.go:915 pkg/invoices.go:1030 #: pkg/quote.go:860 pkg/invoices.go:946
msgctxt "input" msgctxt "input"
msgid "Discount (%)" msgid "Discount (%)"
msgstr "Descompte (%)" msgstr "Descompte (%)"
#: pkg/quote.go:969 #: pkg/quote.go:914
msgid "Quotation product ID must be a number greater than zero." msgid "Quotation product ID must be a number greater than zero."
msgstr "LID del producte de pressupost ha de ser un número major a zero." msgstr "LID del producte de pressupost ha de ser un número major a zero."
#: pkg/quote.go:972 pkg/invoices.go:1087 #: pkg/quote.go:917 pkg/invoices.go:1003
msgid "Product ID must be a positive number or zero." msgid "Product ID must be a positive number or zero."
msgstr "LID del producte ha de ser un número positiu o zero." msgstr "LID del producte ha de ser un número positiu o zero."
#: pkg/quote.go:978 pkg/invoices.go:1093 #: pkg/quote.go:923 pkg/invoices.go:1009
msgid "Quantity can not be empty." msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc." msgstr "No podeu deixar la quantitat en blanc."
#: pkg/quote.go:979 pkg/invoices.go:1094 #: pkg/quote.go:924 pkg/invoices.go:1010
msgid "Quantity must be a number greater than zero." msgid "Quantity must be a number greater than zero."
msgstr "La quantitat ha de ser un número major a zero." msgstr "La quantitat ha de ser un número major a zero."
#: pkg/quote.go:981 pkg/invoices.go:1096 #: pkg/quote.go:926 pkg/invoices.go:1012
msgid "Discount can not be empty." msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc." msgstr "No podeu deixar el descompte en blanc."
#: pkg/quote.go:982 pkg/invoices.go:1097 #: pkg/quote.go:927 pkg/invoices.go:1013
msgid "Discount must be a percentage between 0 and 100." msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100." msgstr "El descompte ha de ser un percentatge entre 0 i 100."
@ -1198,101 +1191,92 @@ msgctxt "period option"
msgid "Previous year" msgid "Previous year"
msgstr "Any anterior" msgstr "Any anterior"
#: pkg/expenses.go:147 #: pkg/expenses.go:115
msgid "Select a contact." msgid "Select a contact."
msgstr "Escolliu un contacte." msgstr "Escolliu un contacte."
#: pkg/expenses.go:183 pkg/expenses.go:396 #: pkg/expenses.go:150 pkg/expenses.go:326
msgctxt "input" msgctxt "input"
msgid "Contact" msgid "Contact"
msgstr "Contacte" msgstr "Contacte"
#: pkg/expenses.go:189 #: pkg/expenses.go:156
msgctxt "input" msgctxt "input"
msgid "Invoice number" msgid "Invoice number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/expenses.go:194 pkg/invoices.go:712 #: pkg/expenses.go:161 pkg/invoices.go:646
msgctxt "input" msgctxt "input"
msgid "Invoice Date" msgid "Invoice Date"
msgstr "Data de factura" msgstr "Data de factura"
#: pkg/expenses.go:206 #: pkg/expenses.go:173
msgctxt "input" msgctxt "input"
msgid "Amount" msgid "Amount"
msgstr "Import" msgstr "Import"
#: pkg/expenses.go:216 pkg/invoices.go:734 #: pkg/expenses.go:183
msgctxt "input" msgctxt "input"
msgid "File" msgid "File"
msgstr "Fitxer" msgstr "Fitxer"
#: pkg/expenses.go:222 pkg/expenses.go:421 #: pkg/expenses.go:211
msgctxt "input"
msgid "Expense Status"
msgstr "Estat de la despesa"
#: pkg/expenses.go:262
msgid "Selected contact is not valid." msgid "Selected contact is not valid."
msgstr "Heu seleccionat un contacte que no és vàlid." msgstr "Heu seleccionat un contacte que no és vàlid."
#: pkg/expenses.go:263 pkg/invoices.go:785 #: pkg/expenses.go:212 pkg/invoices.go:701
msgid "Invoice date must be a valid date." msgid "Invoice date must be a valid date."
msgstr "La data de facturació ha de ser vàlida." msgstr "La data de facturació ha de ser vàlida."
#: pkg/expenses.go:266 #: pkg/expenses.go:215
msgid "Amount can not be empty." msgid "Amount can not be empty."
msgstr "No podeu deixar limport en blanc." msgstr "No podeu deixar limport en blanc."
#: pkg/expenses.go:267 #: pkg/expenses.go:216
msgid "Amount must be a number greater than zero." msgid "Amount must be a number greater than zero."
msgstr "Limport ha de ser un número major a zero." msgstr "Limport ha de ser un número major a zero."
#: pkg/expenses.go:271 #: pkg/expenses.go:327
msgid "Selected expense status is not valid."
msgstr "Heu seleccionat un estat de despesa que no és vàlid."
#: pkg/expenses.go:397
msgid "All contacts" msgid "All contacts"
msgstr "Tots els contactes" msgstr "Tots els contactes"
#: pkg/expenses.go:402 pkg/invoices.go:159 #: pkg/expenses.go:332 pkg/invoices.go:159
msgctxt "input" msgctxt "input"
msgid "Invoice Number" msgid "Invoice Number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/invoices.go:153 pkg/invoices.go:700 #: pkg/invoices.go:153 pkg/invoices.go:634
msgctxt "input" msgctxt "input"
msgid "Invoice Status" msgid "Invoice Status"
msgstr "Estat de la factura" msgstr "Estat de la factura"
#: pkg/invoices.go:544 #: pkg/invoices.go:482
msgid "Select a customer to bill." msgid "Select a customer to bill."
msgstr "Escolliu un client a facturar." msgstr "Escolliu un client a facturar."
#: pkg/invoices.go:648 #: pkg/invoices.go:583
msgid "invoices.zip" msgid "invoices.zip"
msgstr "factures.zip" msgstr "factures.zip"
#: pkg/invoices.go:782 #: pkg/invoices.go:698
msgid "Selected invoice status is not valid." msgid "Selected invoice status is not valid."
msgstr "Heu seleccionat un estat de factura que no és vàlid." msgstr "Heu seleccionat un estat de factura que no és vàlid."
#: pkg/invoices.go:784 #: pkg/invoices.go:700
msgid "Invoice date can not be empty." msgid "Invoice date can not be empty."
msgstr "No podeu deixar la data de la factura en blanc." msgstr "No podeu deixar la data de la factura en blanc."
#: pkg/invoices.go:920 #: pkg/invoices.go:836
#, c-format #, c-format
msgid "Re: quotation #%s of %s" msgid "Re: quotation #%s of %s"
msgstr "Ref: pressupost núm. %s del %s" msgstr "Ref: pressupost núm. %s del %s"
#: pkg/invoices.go:921 #: pkg/invoices.go:837
msgctxt "to_char" msgctxt "to_char"
msgid "MM/DD/YYYY" msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY" msgstr "DD/MM/YYYY"
#: pkg/invoices.go:1084 #: pkg/invoices.go:1000
msgid "Invoice product ID must be a number greater than zero." msgid "Invoice product ID must be a number greater than zero."
msgstr "LID del producte de factura ha de ser un número major a zero." msgstr "LID del producte de factura ha de ser un número major a zero."

208
po/es.po
View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-07-12 20:01+0200\n" "POT-Creation-Date: 2023-07-06 11:45+0200\n"
"PO-Revision-Date: 2023-01-18 17:45+0100\n" "PO-Revision-Date: 2023-01-18 17:45+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n" "Language-Team: Spanish <es@tp.org.es>\n"
@ -65,7 +65,7 @@ msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#: web/template/invoices/products.gohtml:50 #: web/template/invoices/products.gohtml:50
#: web/template/invoices/view.gohtml:66 web/template/quotes/products.gohtml:50 #: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
#: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42 #: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42
msgctxt "title" msgctxt "title"
msgid "Price" msgid "Price"
@ -76,8 +76,8 @@ msgstr "Precio"
msgid "No products added yet." msgid "No products added yet."
msgstr "No hay productos." msgstr "No hay productos."
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:88 #: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:87
#: web/template/invoices/edit.gohtml:89 web/template/quotes/products.gohtml:72 #: web/template/invoices/edit.gohtml:88 web/template/quotes/products.gohtml:72
#: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89 #: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89
msgctxt "action" msgctxt "action"
msgid "Add products" msgid "Add products"
@ -94,31 +94,31 @@ msgctxt "action"
msgid "Undo" msgid "Undo"
msgstr "Deshacer" msgstr "Deshacer"
#: web/template/invoices/new.gohtml:64 web/template/invoices/view.gohtml:71 #: web/template/invoices/new.gohtml:63 web/template/invoices/view.gohtml:67
#: web/template/invoices/edit.gohtml:65 web/template/quotes/new.gohtml:64 #: web/template/invoices/edit.gohtml:64 web/template/quotes/new.gohtml:64
#: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:65 #: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:65
msgctxt "title" msgctxt "title"
msgid "Subtotal" msgid "Subtotal"
msgstr "Subtotal" msgstr "Subtotal"
#: web/template/invoices/new.gohtml:74 web/template/invoices/view.gohtml:75 #: web/template/invoices/new.gohtml:73 web/template/invoices/view.gohtml:71
#: web/template/invoices/view.gohtml:115 web/template/invoices/edit.gohtml:75 #: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:74
#: web/template/quotes/new.gohtml:74 web/template/quotes/view.gohtml:82 #: 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/quotes/view.gohtml:122 web/template/quotes/edit.gohtml:75
msgctxt "title" msgctxt "title"
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93 #: web/template/invoices/new.gohtml:91 web/template/invoices/edit.gohtml:92
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93 #: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
msgctxt "action" msgctxt "action"
msgid "Update" msgid "Update"
msgstr "Actualizar" msgstr "Actualizar"
#: web/template/invoices/new.gohtml:95 web/template/invoices/edit.gohtml:96 #: web/template/invoices/new.gohtml:94 web/template/invoices/edit.gohtml:95
#: web/template/quotes/new.gohtml:95 web/template/quotes/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/contacts/new.gohtml:49 web/template/contacts/edit.gohtml:53
#: web/template/expenses/new.gohtml:34 web/template/expenses/edit.gohtml:40 #: web/template/expenses/new.gohtml:33 web/template/expenses/edit.gohtml:38
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36 #: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
msgctxt "action" msgctxt "action"
msgid "Save" msgid "Save"
@ -136,7 +136,7 @@ msgstr "Nueva factura"
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23 #: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:36 #: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:36
#: web/template/expenses/index.gohtml:37 web/template/products/index.gohtml:34 #: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
msgctxt "action" msgctxt "action"
msgid "Filter" msgid "Filter"
msgstr "Filtrar" msgstr "Filtrar"
@ -146,7 +146,7 @@ msgctxt "invoice"
msgid "All" msgid "All"
msgstr "Todas" msgstr "Todas"
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:38 #: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:34
#: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:37 #: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:37
msgctxt "title" msgctxt "title"
msgid "Date" msgid "Date"
@ -163,32 +163,31 @@ msgid "Customer"
msgstr "Cliente" msgstr "Cliente"
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53 #: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
#: web/template/expenses/index.gohtml:46
msgctxt "title" msgctxt "title"
msgid "Status" msgid "Status"
msgstr "Estado" msgstr "Estado"
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54 #: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:47 #: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:45
#: web/template/products/index.gohtml:41 #: web/template/products/index.gohtml:41
msgctxt "title" msgctxt "title"
msgid "Tags" msgid "Tags"
msgstr "Etiquetes" msgstr "Etiquetes"
#: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55 #: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55
#: web/template/expenses/index.gohtml:48 #: web/template/expenses/index.gohtml:46
msgctxt "title" msgctxt "title"
msgid "Amount" msgid "Amount"
msgstr "Importe" msgstr "Importe"
#: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56 #: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56
#: web/template/expenses/index.gohtml:49 #: web/template/expenses/index.gohtml:47
msgctxt "title" msgctxt "title"
msgid "Download" msgid "Download"
msgstr "Descargar" msgstr "Descargar"
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57 #: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:50 #: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:48
#: web/template/products/index.gohtml:43 #: web/template/products/index.gohtml:43
msgctxt "title" msgctxt "title"
msgid "Actions" msgid "Actions"
@ -210,7 +209,7 @@ msgstr "Acciones para la factura %s"
#: web/template/invoices/index.gohtml:121 web/template/invoices/view.gohtml:19 #: web/template/invoices/index.gohtml:121 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:120 web/template/quotes/view.gohtml:22 #: web/template/quotes/index.gohtml:120 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:77 web/template/expenses/index.gohtml:111 #: web/template/contacts/index.gohtml:77 web/template/expenses/index.gohtml:87
#: web/template/products/index.gohtml:73 #: web/template/products/index.gohtml:73
msgctxt "action" msgctxt "action"
msgid "Edit" msgid "Edit"
@ -227,11 +226,11 @@ msgid "No invoices added yet."
msgstr "No hay facturas." msgstr "No hay facturas."
#: web/template/invoices/index.gohtml:146 web/template/quotes/index.gohtml:153 #: web/template/invoices/index.gohtml:146 web/template/quotes/index.gohtml:153
#: web/template/expenses/index.gohtml:128 #: web/template/expenses/index.gohtml:104
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:37 #: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:33
msgctxt "title" msgctxt "title"
msgid "Invoice %s" msgid "Invoice %s"
msgstr "Factura %s" msgstr "Factura %s"
@ -241,27 +240,22 @@ msgctxt "action"
msgid "Download invoice" msgid "Download invoice"
msgstr "Descargar factura" msgstr "Descargar factura"
#: web/template/invoices/view.gohtml:25 #: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:72
msgctxt "action"
msgid "Download invoice attachment"
msgstr "Descargar adjunto de factura"
#: web/template/invoices/view.gohtml:65 web/template/quotes/view.gohtml:72
msgctxt "title" msgctxt "title"
msgid "Concept" msgid "Concept"
msgstr "Concepto" msgstr "Concepto"
#: web/template/invoices/view.gohtml:68 web/template/quotes/view.gohtml:75 #: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:75
msgctxt "title" msgctxt "title"
msgid "Discount" msgid "Discount"
msgstr "Descuento" msgstr "Descuento"
#: web/template/invoices/view.gohtml:70 web/template/quotes/view.gohtml:77 #: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:77
msgctxt "title" msgctxt "title"
msgid "Units" msgid "Units"
msgstr "Unidades" msgstr "Unidades"
#: web/template/invoices/view.gohtml:105 web/template/quotes/view.gohtml:112 #: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:112
msgctxt "title" msgctxt "title"
msgid "Tax Base" msgid "Tax Base"
msgstr "Base imponible" msgstr "Base imponible"
@ -479,7 +473,7 @@ msgctxt "action"
msgid "New contact" msgid "New contact"
msgstr "Nuevo contacto" msgstr "Nuevo contacto"
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:43 #: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:42
msgctxt "title" msgctxt "title"
msgid "Contact" msgid "Contact"
msgstr "Contacto" msgstr "Contacto"
@ -565,21 +559,21 @@ msgctxt "action"
msgid "New expense" msgid "New expense"
msgstr "Nuevo gasto" msgstr "Nuevo gasto"
#: web/template/expenses/index.gohtml:44 #: web/template/expenses/index.gohtml:43
msgctxt "title" msgctxt "title"
msgid "Invoice Date" msgid "Invoice Date"
msgstr "Fecha de factura" msgstr "Fecha de factura"
#: web/template/expenses/index.gohtml:45 #: web/template/expenses/index.gohtml:44
msgctxt "title" msgctxt "title"
msgid "Invoice Number" msgid "Invoice Number"
msgstr "Número de factura" msgstr "Número de factura"
#: web/template/expenses/index.gohtml:103 #: web/template/expenses/index.gohtml:79
msgid "Actions for expense %s" msgid "Actions for expense %s"
msgstr "Acciones para el gasto %s" msgstr "Acciones para el gasto %s"
#: web/template/expenses/index.gohtml:121 #: web/template/expenses/index.gohtml:97
msgid "No expenses added yet." msgid "No expenses added yet."
msgstr "No hay gastos." msgstr "No hay gastos."
@ -712,84 +706,83 @@ msgstr "No podéis dejar la contraseña en blanco."
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nombre de usuario o contraseña inválido." msgstr "Nombre de usuario o contraseña inválido."
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:878 pkg/invoices.go:993 #: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
#: pkg/contacts.go:140 pkg/contacts.go:248 #: pkg/contacts.go:140 pkg/contacts.go:248
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:685 #: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
#: pkg/expenses.go:228 pkg/expenses.go:417 pkg/invoices.go:174 #: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
#: pkg/invoices.go:723 pkg/invoices.go:1295 pkg/contacts.go:145 #: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:145
#: pkg/contacts.go:348 #: pkg/contacts.go:348
msgctxt "input" msgctxt "input"
msgid "Tags" msgid "Tags"
msgstr "Etiquetes" msgstr "Etiquetes"
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:427 pkg/invoices.go:178 #: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
#: pkg/contacts.go:149 #: pkg/contacts.go:149
msgctxt "input" msgctxt "input"
msgid "Tags Condition" msgid "Tags Condition"
msgstr "Condición de las etiquetas" msgstr "Condición de las etiquetas"
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:431 pkg/invoices.go:182 #: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
#: pkg/contacts.go:153 #: pkg/contacts.go:153
msgctxt "tag condition" msgctxt "tag condition"
msgid "All" msgid "All"
msgstr "Todas" msgstr "Todas"
#: pkg/products.go:178 pkg/expenses.go:432 pkg/invoices.go:183 #: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
#: pkg/contacts.go:154 #: pkg/contacts.go:154
msgid "Invoices must have all the specified labels." msgid "Invoices must have all the specified labels."
msgstr "Las facturas deben tener todas las etiquetas." msgstr "Las facturas deben tener todas las etiquetas."
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:436 pkg/invoices.go:187 #: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
#: pkg/contacts.go:158 #: pkg/contacts.go:158
msgctxt "tag condition" msgctxt "tag condition"
msgid "Any" msgid "Any"
msgstr "Cualquiera" msgstr "Cualquiera"
#: pkg/products.go:183 pkg/expenses.go:437 pkg/invoices.go:188 #: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
#: pkg/contacts.go:159 #: pkg/contacts.go:159
msgid "Invoices must have at least one of the specified labels." msgid "Invoices must have at least one of the specified labels."
msgstr "Las facturas deben tener como mínimo una de las etiquetas." msgstr "Las facturas deben tener como mínimo una de las etiquetas."
#: pkg/products.go:269 pkg/quote.go:892 pkg/invoices.go:1007 #: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:923
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripción" msgstr "Descripción"
#: pkg/products.go:274 pkg/quote.go:896 pkg/invoices.go:1011 #: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:927
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Precio" msgstr "Precio"
#: pkg/products.go:284 pkg/quote.go:925 pkg/expenses.go:200 #: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:956
#: pkg/invoices.go:1040
msgctxt "input" msgctxt "input"
msgid "Taxes" msgid "Taxes"
msgstr "Impuestos" msgstr "Impuestos"
#: pkg/products.go:309 pkg/quote.go:974 pkg/profile.go:92 pkg/invoices.go:1089 #: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
#: pkg/contacts.go:398 #: pkg/contacts.go:398
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco." msgstr "No podéis dejar el nombre en blanco."
#: pkg/products.go:310 pkg/quote.go:975 pkg/invoices.go:1090 #: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:1006
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco." msgstr "No podéis dejar el precio en blanco."
#: pkg/products.go:311 pkg/quote.go:976 pkg/invoices.go:1091 #: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:1007
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El precio tiene que ser un número mayor a cero." msgstr "El precio tiene que ser un número mayor a cero."
#: pkg/products.go:313 pkg/quote.go:984 pkg/expenses.go:264 pkg/expenses.go:269 #: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
#: pkg/invoices.go:1099 #: pkg/invoices.go:1015
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido." msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/products.go:314 pkg/quote.go:985 pkg/expenses.go:265 pkg/expenses.go:270 #: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
#: pkg/invoices.go:1100 #: pkg/invoices.go:1016
msgid "You can only select a tax of each class." msgid "You can only select a tax of each class."
msgstr "Solo podéis escoger un impuesto de cada clase." msgstr "Solo podéis escoger un impuesto de cada clase."
@ -998,7 +991,7 @@ msgstr "No podéis dejar el nombre del método de pago en blanco."
msgid "Payment instructions can not be empty." msgid "Payment instructions can not be empty."
msgstr "No podéis dejar las instrucciones de pago en blanco." msgstr "No podéis dejar las instrucciones de pago en blanco."
#: pkg/quote.go:147 pkg/quote.go:663 pkg/invoices.go:147 pkg/invoices.go:706 #: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:640
msgctxt "input" msgctxt "input"
msgid "Customer" msgid "Customer"
msgstr "Cliente" msgstr "Cliente"
@ -1007,12 +1000,12 @@ msgstr "Cliente"
msgid "All customers" msgid "All customers"
msgstr "Todos los clientes" msgstr "Todos los clientes"
#: pkg/quote.go:153 pkg/quote.go:657 #: pkg/quote.go:153 pkg/quote.go:602
msgctxt "input" msgctxt "input"
msgid "Quotation Status" msgid "Quotation Status"
msgstr "Estado del presupuesto" msgstr "Estado del presupuesto"
#: pkg/quote.go:154 pkg/expenses.go:422 pkg/invoices.go:154 #: pkg/quote.go:154 pkg/invoices.go:154
msgid "All status" msgid "All status"
msgstr "Todos los estados" msgstr "Todos los estados"
@ -1021,12 +1014,12 @@ msgctxt "input"
msgid "Quotation Number" msgid "Quotation Number"
msgstr "Número de presupuesto" msgstr "Número de presupuesto"
#: pkg/quote.go:164 pkg/expenses.go:407 pkg/invoices.go:164 #: pkg/quote.go:164 pkg/expenses.go:337 pkg/invoices.go:164
msgctxt "input" msgctxt "input"
msgid "From Date" msgid "From Date"
msgstr "A partir de la fecha" msgstr "A partir de la fecha"
#: pkg/quote.go:169 pkg/expenses.go:412 pkg/invoices.go:169 #: pkg/quote.go:169 pkg/expenses.go:342 pkg/invoices.go:169
msgctxt "input" msgctxt "input"
msgid "To Date" msgid "To Date"
msgstr "Hasta la fecha" msgstr "Hasta la fecha"
@ -1039,99 +1032,99 @@ msgstr "Los presupuestos deben tener todas las etiquetas."
msgid "Quotations must have at least one of the specified labels." msgid "Quotations must have at least one of the specified labels."
msgstr "Los presupuestos deben tener como mínimo una de las etiquetas." msgstr "Los presupuestos deben tener como mínimo una de las etiquetas."
#: pkg/quote.go:605 #: pkg/quote.go:550
msgid "quotations.zip" msgid "quotations.zip"
msgstr "presupuestos.zip" msgstr "presupuestos.zip"
#: pkg/quote.go:611 pkg/quote.go:1140 pkg/quote.go:1148 pkg/invoices.go:654 #: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:589
#: pkg/invoices.go:1270 pkg/invoices.go:1278 #: pkg/invoices.go:1183 pkg/invoices.go:1191
msgid "Invalid action" msgid "Invalid action"
msgstr "Acción inválida." msgstr "Acción inválida."
#: pkg/quote.go:664 #: pkg/quote.go:609
msgid "Select a customer to quote." msgid "Select a customer to quote."
msgstr "Escoged un cliente a presupuestar." msgstr "Escoged un cliente a presupuestar."
#: pkg/quote.go:669 #: pkg/quote.go:614
msgctxt "input" msgctxt "input"
msgid "Quotation Date" msgid "Quotation Date"
msgstr "Fecha del presupuesto" msgstr "Fecha del presupuesto"
#: pkg/quote.go:675 #: pkg/quote.go:620
msgctxt "input" msgctxt "input"
msgid "Terms and conditions" msgid "Terms and conditions"
msgstr "Condiciones de aceptación" msgstr "Condiciones de aceptación"
#: pkg/quote.go:680 pkg/invoices.go:718 #: pkg/quote.go:625 pkg/invoices.go:652
msgctxt "input" msgctxt "input"
msgid "Notes" msgid "Notes"
msgstr "Notas" msgstr "Notas"
#: pkg/quote.go:689 pkg/invoices.go:728 #: pkg/quote.go:634 pkg/invoices.go:662
msgctxt "input" msgctxt "input"
msgid "Payment Method" msgid "Payment Method"
msgstr "Método de pago" msgstr "Método de pago"
#: pkg/quote.go:690 #: pkg/quote.go:635
msgid "Select a payment method." msgid "Select a payment method."
msgstr "Escoged un método e pago." msgstr "Escoged un método e pago."
#: pkg/quote.go:726 #: pkg/quote.go:671
msgid "Selected quotation status is not valid." msgid "Selected quotation status is not valid."
msgstr "Habéis escogido un estado de presupuesto que no es válido." msgstr "Habéis escogido un estado de presupuesto que no es válido."
#: pkg/quote.go:728 pkg/invoices.go:783 #: pkg/quote.go:673 pkg/invoices.go:699
msgid "Selected customer is not valid." msgid "Selected customer is not valid."
msgstr "Habéis escogido un cliente que no es válido." msgstr "Habéis escogido un cliente que no es válido."
#: pkg/quote.go:730 #: pkg/quote.go:675
msgid "Quotation date can not be empty." msgid "Quotation date can not be empty."
msgstr "No podéis dejar la fecha del presupuesto en blanco." msgstr "No podéis dejar la fecha del presupuesto en blanco."
#: pkg/quote.go:731 #: pkg/quote.go:676
msgid "Quotation date must be a valid date." msgid "Quotation date must be a valid date."
msgstr "La fecha de presupuesto debe ser válida." msgstr "La fecha de presupuesto debe ser válida."
#: pkg/quote.go:734 pkg/invoices.go:787 #: pkg/quote.go:679 pkg/invoices.go:703
msgid "Selected payment method is not valid." msgid "Selected payment method is not valid."
msgstr "Habéis escogido un método de pago que no es válido." msgstr "Habéis escogido un método de pago que no es válido."
#: pkg/quote.go:868 pkg/quote.go:873 pkg/invoices.go:983 pkg/invoices.go:988 #: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:899 pkg/invoices.go:904
msgctxt "input" msgctxt "input"
msgid "Id" msgid "Id"
msgstr "Identificador" msgstr "Identificador"
#: pkg/quote.go:906 pkg/invoices.go:1021 #: pkg/quote.go:851 pkg/invoices.go:937
msgctxt "input" msgctxt "input"
msgid "Quantity" msgid "Quantity"
msgstr "Cantidad" msgstr "Cantidad"
#: pkg/quote.go:915 pkg/invoices.go:1030 #: pkg/quote.go:860 pkg/invoices.go:946
msgctxt "input" msgctxt "input"
msgid "Discount (%)" msgid "Discount (%)"
msgstr "Descuento (%)" msgstr "Descuento (%)"
#: pkg/quote.go:969 #: pkg/quote.go:914
msgid "Quotation product ID must be a number greater than zero." msgid "Quotation product ID must be a number greater than zero."
msgstr "El ID de producto de presupuesto tiene que ser un número mayor a cero." msgstr "El ID de producto de presupuesto tiene que ser un número mayor a cero."
#: pkg/quote.go:972 pkg/invoices.go:1087 #: pkg/quote.go:917 pkg/invoices.go:1003
msgid "Product ID must be a positive number or zero." msgid "Product ID must be a positive number or zero."
msgstr "El ID de producto tiene que ser un número positivo o cero." msgstr "El ID de producto tiene que ser un número positivo o cero."
#: pkg/quote.go:978 pkg/invoices.go:1093 #: pkg/quote.go:923 pkg/invoices.go:1009
msgid "Quantity can not be empty." msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco." msgstr "No podéis dejar la cantidad en blanco."
#: pkg/quote.go:979 pkg/invoices.go:1094 #: pkg/quote.go:924 pkg/invoices.go:1010
msgid "Quantity must be a number greater than zero." msgid "Quantity must be a number greater than zero."
msgstr "La cantidad tiene que ser un número mayor a cero." msgstr "La cantidad tiene que ser un número mayor a cero."
#: pkg/quote.go:981 pkg/invoices.go:1096 #: pkg/quote.go:926 pkg/invoices.go:1012
msgid "Discount can not be empty." msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco." msgstr "No podéis dejar el descuento en blanco."
#: pkg/quote.go:982 pkg/invoices.go:1097 #: pkg/quote.go:927 pkg/invoices.go:1013
msgid "Discount must be a percentage between 0 and 100." msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100." msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
@ -1198,101 +1191,92 @@ msgctxt "period option"
msgid "Previous year" msgid "Previous year"
msgstr "Año anterior" msgstr "Año anterior"
#: pkg/expenses.go:147 #: pkg/expenses.go:115
msgid "Select a contact." msgid "Select a contact."
msgstr "Escoged un contacto" msgstr "Escoged un contacto"
#: pkg/expenses.go:183 pkg/expenses.go:396 #: pkg/expenses.go:150 pkg/expenses.go:326
msgctxt "input" msgctxt "input"
msgid "Contact" msgid "Contact"
msgstr "Contacto" msgstr "Contacto"
#: pkg/expenses.go:189 #: pkg/expenses.go:156
msgctxt "input" msgctxt "input"
msgid "Invoice number" msgid "Invoice number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/expenses.go:194 pkg/invoices.go:712 #: pkg/expenses.go:161 pkg/invoices.go:646
msgctxt "input" msgctxt "input"
msgid "Invoice Date" msgid "Invoice Date"
msgstr "Fecha de factura" msgstr "Fecha de factura"
#: pkg/expenses.go:206 #: pkg/expenses.go:173
msgctxt "input" msgctxt "input"
msgid "Amount" msgid "Amount"
msgstr "Importe" msgstr "Importe"
#: pkg/expenses.go:216 pkg/invoices.go:734 #: pkg/expenses.go:183
msgctxt "input" msgctxt "input"
msgid "File" msgid "File"
msgstr "Archivo" msgstr "Archivo"
#: pkg/expenses.go:222 pkg/expenses.go:421 #: pkg/expenses.go:211
msgctxt "input"
msgid "Expense Status"
msgstr "Estado del gasto"
#: pkg/expenses.go:262
msgid "Selected contact is not valid." msgid "Selected contact is not valid."
msgstr "Habéis escogido un contacto que no es válido." msgstr "Habéis escogido un contacto que no es válido."
#: pkg/expenses.go:263 pkg/invoices.go:785 #: pkg/expenses.go:212 pkg/invoices.go:701
msgid "Invoice date must be a valid date." msgid "Invoice date must be a valid date."
msgstr "La fecha de factura debe ser válida." msgstr "La fecha de factura debe ser válida."
#: pkg/expenses.go:266 #: pkg/expenses.go:215
msgid "Amount can not be empty." msgid "Amount can not be empty."
msgstr "No podéis dejar el importe en blanco." msgstr "No podéis dejar el importe en blanco."
#: pkg/expenses.go:267 #: pkg/expenses.go:216
msgid "Amount must be a number greater than zero." msgid "Amount must be a number greater than zero."
msgstr "El importe tiene que ser un número mayor a cero." msgstr "El importe tiene que ser un número mayor a cero."
#: pkg/expenses.go:271 #: pkg/expenses.go:327
msgid "Selected expense status is not valid."
msgstr "Habéis escogido un estado de gasto que no es válido."
#: pkg/expenses.go:397
msgid "All contacts" msgid "All contacts"
msgstr "Todos los contactos" msgstr "Todos los contactos"
#: pkg/expenses.go:402 pkg/invoices.go:159 #: pkg/expenses.go:332 pkg/invoices.go:159
msgctxt "input" msgctxt "input"
msgid "Invoice Number" msgid "Invoice Number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/invoices.go:153 pkg/invoices.go:700 #: pkg/invoices.go:153 pkg/invoices.go:634
msgctxt "input" msgctxt "input"
msgid "Invoice Status" msgid "Invoice Status"
msgstr "Estado de la factura" msgstr "Estado de la factura"
#: pkg/invoices.go:544 #: pkg/invoices.go:482
msgid "Select a customer to bill." msgid "Select a customer to bill."
msgstr "Escoged un cliente a facturar." msgstr "Escoged un cliente a facturar."
#: pkg/invoices.go:648 #: pkg/invoices.go:583
msgid "invoices.zip" msgid "invoices.zip"
msgstr "facturas.zip" msgstr "facturas.zip"
#: pkg/invoices.go:782 #: pkg/invoices.go:698
msgid "Selected invoice status is not valid." msgid "Selected invoice status is not valid."
msgstr "Habéis escogido un estado de factura que no es válido." msgstr "Habéis escogido un estado de factura que no es válido."
#: pkg/invoices.go:784 #: pkg/invoices.go:700
msgid "Invoice date can not be empty." msgid "Invoice date can not be empty."
msgstr "No podéis dejar la fecha de la factura en blanco." msgstr "No podéis dejar la fecha de la factura en blanco."
#: pkg/invoices.go:920 #: pkg/invoices.go:836
#, c-format #, c-format
msgid "Re: quotation #%s of %s" msgid "Re: quotation #%s of %s"
msgstr "Ref: presupuesto n.º %s del %s" msgstr "Ref: presupuesto n.º %s del %s"
#: pkg/invoices.go:921 #: pkg/invoices.go:837
msgctxt "to_char" msgctxt "to_char"
msgid "MM/DD/YYYY" msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY" msgstr "DD/MM/YYYY"
#: pkg/invoices.go:1084 #: pkg/invoices.go:1000
msgid "Invoice product ID must be a number greater than zero." msgid "Invoice product ID must be a number greater than zero."
msgstr "El ID de producto de factura tiene que ser un número mayor a cero." msgstr "El ID de producto de factura tiene que ser un número mayor a cero."

View File

@ -1,7 +0,0 @@
-- Revert numerus:attach_to_invoice from pg
begin;
drop function if exists numerus.attach_to_invoice(uuid, text, text, bytea);
commit;

View File

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

View File

@ -122,5 +122,3 @@ available_expense_status [schema_numerus expense_status expense_status_i18n] 202
expense_expense_status [expense] 2023-07-11T12:28:58Z jordi fita mas <jordi@tandem.blog> # Add expense_status to expense relation expense_expense_status [expense] 2023-07-11T12:28:58Z jordi fita mas <jordi@tandem.blog> # Add expense_status to expense relation
add_expense [add_expense@v1 expense_status expense_expense_status] 2023-07-11T13:16:16Z jordi fita mas <jordi@tandem.blog> # Add expense_status parameter to add_expense add_expense [add_expense@v1 expense_status expense_expense_status] 2023-07-11T13:16:16Z jordi fita mas <jordi@tandem.blog> # Add expense_status parameter to add_expense
edit_expense [edit_expense@v1 expense_status expense_expense_status] 2023-07-11T13:21:17Z jordi fita mas <jordi@tandem.blog> # Add expense_status parameter to edit_expense edit_expense [edit_expense@v1 expense_status expense_expense_status] 2023-07-11T13:21:17Z jordi fita mas <jordi@tandem.blog> # Add expense_status parameter to edit_expense
invoice_attachment [schema_numerus roles invoice] 2023-07-12T17:10:58Z jordi fita mas <jordi@tandem.blog> # Add relation for invoice attachment
attach_to_invoice [schema_numerus roles invoice invoice_attachment] 2023-07-12T17:21:19Z jordi fita mas <jordi@tandem.blog> # Add function to attachment a document to invoices

View File

@ -1,97 +0,0 @@
-- Test attach_to_invoice
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
set search_path to auth, numerus, public;
select plan(12);
select has_function('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea']);
select function_lang_is('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea'], 'sql');
select function_returns('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea'], 'void');
select isnt_definer('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea']);
select volatility_is('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea'], 'volatile');
select function_privs_are('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea'], 'guest', array []::text[]);
select function_privs_are('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea'], 'invoicer', array ['EXECUTE']);
select function_privs_are('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea'], 'admin', array ['EXECUTE']);
select function_privs_are('numerus', 'attach_to_invoice', array ['uuid', 'text', 'text', 'bytea'], 'authenticator', array []::text[]);
set client_min_messages to warning;
truncate invoice_attachment cascade;
truncate invoice cascade;
truncate contact_tax_details cascade;
truncate contact cascade;
truncate tax cascade;
truncate tax_class cascade;
truncate payment_method cascade;
truncate company cascade;
reset client_min_messages;
set constraints "company_default_payment_method_id_fkey" deferred;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 111)
;
insert into payment_method (payment_method_id, company_id, name, instructions)
values (111, 1, 'cash', 'cash')
, (112, 1, 'bank', 'send money to my bank account')
;
set constraints "company_default_payment_method_id_fkey" immediate;
insert into tax_class (tax_class_id, company_id, name)
values (11, 1, 'tax')
;
insert into tax (tax_id, company_id, tax_class_id, name, rate)
values (3, 1, 11, 'IRPF -15 %', -0.15)
, (4, 1, 11, 'IVA 21 %', 0.21)
;
insert into contact (contact_id, company_id, name)
values (12, 1, 'Contact 2.1')
, (13, 1, 'Contact 2.2')
;
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
values (12, 'Customer 2.1 Ltd', 'XXX123', 'Fake St.', 'City', 'Province', '17480', 'ES')
, (13, 'Customer 2.2 Ltd', 'XXX234', 'Fake St.', 'City', 'Province', '17480', 'ES')
;
insert into invoice (invoice_id, company_id, slug, invoice_number, invoice_date, contact_id, payment_method_id, currency_code, tags)
values (15, 1, '7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'INV1', '2023-05-04', 12, 111, 'EUR', '{tag1}')
, (16, 1, 'b57b980b-247b-4be4-a0b7-03a7819c53ae', 'INV2', '2023-05-05', 13, 112, 'EUR', '{tag2}')
;
insert into invoice_attachment (invoice_id, original_filename, mime_type, content)
values (16, 'something.txt', 'text/plain', convert_to('Once upon a time…', 'UTF-8'))
;
select lives_ok(
$$ select attach_to_invoice('7ac3ae0e-b0c1-4206-a19b-0be20835edd4', 'invoice.txt', 'text/plain', convert_to('Total 42€', 'UTF-8')) $$,
'Should be able to attach a document to the first invoice'
);
select lives_ok(
$$ select attach_to_invoice('b57b980b-247b-4be4-a0b7-03a7819c53ae', 'invoice.html', 'text/html', convert_to('<html><p>Total 42€</p></html>', 'UTF-8')) $$,
'Should be able to replace the second invoices attachment with a new document'
);
select bag_eq(
$$ select invoice_id, original_filename, mime_type, content from invoice_attachment $$,
$$ values (15, 'invoice.txt', 'text/plain', convert_to('Total 42€', 'UTF-8'))
, (16, 'invoice.html', 'text/html', convert_to('<html><p>Total 42€</p></html>', 'UTF-8'))
$$,
'Should have attached all documents'
);
select *
from finish();
rollback;

View File

@ -43,6 +43,7 @@ select col_hasnt_default('expense_attachment', 'content');
set client_min_messages to warning; set client_min_messages to warning;
truncate expense_attachment cascade; truncate expense_attachment cascade;
truncate expense cascade; truncate expense cascade;
truncate invoice cascade;
truncate tax cascade; truncate tax cascade;
truncate tax_class cascade; truncate tax_class cascade;
truncate contact cascade; truncate contact cascade;

View File

@ -1,150 +0,0 @@
-- Test invoice_attachment
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(29);
set search_path to numerus, auth, public;
select has_table('invoice_attachment');
select has_pk('invoice_attachment' );
select table_privs_are('invoice_attachment', 'guest', array []::text[]);
select table_privs_are('invoice_attachment', 'invoicer', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('invoice_attachment', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('invoice_attachment', 'authenticator', array []::text[]);
select has_column('invoice_attachment', 'invoice_id');
select col_is_pk('invoice_attachment', 'invoice_id');
select col_is_fk('invoice_attachment', 'invoice_id');
select fk_ok('invoice_attachment', 'invoice_id', 'invoice', 'invoice_id');
select col_type_is('invoice_attachment', 'invoice_id', 'integer');
select col_not_null('invoice_attachment', 'invoice_id');
select col_hasnt_default('invoice_attachment', 'invoice_id');
select has_column('invoice_attachment', 'original_filename');
select col_type_is('invoice_attachment', 'original_filename', 'text');
select col_not_null('invoice_attachment', 'original_filename');
select col_hasnt_default('invoice_attachment', 'original_filename');
select has_column('invoice_attachment', 'mime_type');
select col_type_is('invoice_attachment', 'mime_type', 'text');
select col_not_null('invoice_attachment', 'mime_type');
select col_hasnt_default('invoice_attachment', 'mime_type');
select has_column('invoice_attachment', 'content');
select col_type_is('invoice_attachment', 'content', 'bytea');
select col_not_null('invoice_attachment', 'content');
select col_hasnt_default('invoice_attachment', 'content');
set client_min_messages to warning;
truncate invoice_attachment cascade;
truncate invoice cascade;
truncate tax cascade;
truncate tax_class cascade;
truncate contact_tax_details cascade;
truncate contact cascade;
truncate company_user cascade;
truncate payment_method cascade;
truncate company cascade;
truncate auth."user" cascade;
reset client_min_messages;
insert into auth."user" (user_id, email, name, password, role, cookie, cookie_expires_at)
values (1, 'demo@tandem.blog', 'Demo', 'test', 'invoicer', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
, (5, 'admin@tandem.blog', 'Demo', 'test', 'admin', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
;
set constraints "company_default_payment_method_id_fkey" deferred;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, country_code, currency_code, default_payment_method_id)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 222)
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', 'FR', 'USD', 444)
;
insert into payment_method (payment_method_id, company_id, name, instructions)
values (444, 4, 'cash', 'cash')
, (222, 2, 'cash', 'cash')
;
set constraints "company_default_payment_method_id_fkey" immediate;
insert into company_user (company_id, user_id)
values (2, 1)
, (4, 5)
;
insert into tax_class (tax_class_id, company_id, name)
values (22, 2, 'vat')
, (44, 4, 'vat')
;
insert into tax (tax_id, company_id, tax_class_id, name, rate)
values (3, 2, 22, 'IVA 21 %', 0.21)
, (6, 4, 44, 'IVA 10 %', 0.10)
;
insert into contact (contact_id, company_id, name)
values ( 9, 2, 'Customer 1')
, (10, 4, 'Customer 2')
;
insert into contact_tax_details (contact_id, business_name, vatin, address, city, province, postal_code, country_code)
values ( 9, 'Customer 1 Ltd', 'XXX123', 'Fake St.', 'City', 'Province', '17480', 'ES')
, (10, 'Customer 2 Ltd', 'XXX234', 'Fake St.', 'City', 'Province', '17480', 'ES')
;
insert into invoice (invoice_id, company_id, invoice_number, contact_id, invoice_date, payment_method_id, currency_code)
values (13, 2, 'INV001', 9, '2011-01-11', 222, 'EUR')
, (14, 4, 'INV002', 10, '2022-02-22', 444, 'EUR')
;
insert into invoice_attachment (invoice_id, original_filename, mime_type, content)
values (13, 'invoice.txt', 'text/plain', convert_to('IOU 42', 'UTF8'))
, (14, 'invoice.html', 'text/html', convert_to('<html>IOU <em>42</em></html>', 'UTF8'))
;
prepare invoice_attachment_data as
select invoice_id, original_filename
from invoice_attachment
order by invoice_id, original_filename;
set role invoicer;
select is_empty('invoice_attachment_data', 'Should show no data when cookie is not set yet');
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog');
select bag_eq(
'invoice_attachment_data',
$$ values (13, 'invoice.txt')
$$,
'Should only list tax of products of the companies where demo@tandem.blog is user of'
);
reset role;
select set_cookie('12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524/admin@tandem.blog');
select bag_eq(
'invoice_attachment_data',
$$ values (14, 'invoice.html')
$$,
'Should only list tax of products of the companies where admin@tandem.blog is user of'
);
reset role;
select set_cookie('not-a-cookie');
select throws_ok(
'invoice_attachment_data',
'42501', 'permission denied for table invoice_attachment',
'Should not allow select to guest users'
);
reset role;
select *
from finish();
rollback;

View File

@ -1,7 +0,0 @@
-- Verify numerus:attach_to_invoice on pg
begin;
select has_function_privilege('numerus.attach_to_invoice(uuid, text, text, bytea)', 'execute');
rollback;

View File

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

View File

@ -17,7 +17,7 @@
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editInvoicePage*/ -}} {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editInvoicePage*/ -}}
<section id="invoice-dialog-content" data-hx-target="main"> <section id="invoice-dialog-content" data-hx-target="main">
<h2>{{ printf (pgettext "Edit Invoice “%s”" "title") .Number }}</h2> <h2>{{ printf (pgettext "Edit Invoice “%s”" "title") .Number }}</h2>
<form enctype="multipart/form-data" method="POST" action="{{ companyURI "/invoices/" }}{{ .Slug }}/edit" <form method="POST" action="{{ companyURI "/invoices/" }}{{ .Slug }}/edit"
data-hx-boost="true" data-hx-boost="true"
data-hx-swap="innerHTML show:false" data-hx-swap="innerHTML show:false"
> >
@ -50,7 +50,6 @@
{{ template "tags-field" .Tags }} {{ template "tags-field" .Tags }}
{{ template "select-field" .PaymentMethod }} {{ template "select-field" .PaymentMethod }}
{{ template "select-field" .InvoiceStatus }} {{ template "select-field" .InvoiceStatus }}
{{ template "file-field" .File }}
{{ template "input-field" .Notes }} {{ template "input-field" .Notes }}
</div> </div>

View File

@ -17,7 +17,7 @@
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoicePage*/ -}} {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoicePage*/ -}}
<section id="invoice-dialog-content" data-hx-target="main"> <section id="invoice-dialog-content" data-hx-target="main">
<h2>{{(pgettext "New Invoice" "title")}}</h2> <h2>{{(pgettext "New Invoice" "title")}}</h2>
<form enctype="multipart/form-data" method="POST" action="{{ companyURI "/invoices/new" }}" <form method="POST" action="{{ companyURI "/invoices/new" }}"
data-hx-boost="true" data-hx-boost="true"
data-hx-swap="innerHTML show:false" data-hx-swap="innerHTML show:false"
> >
@ -50,7 +50,6 @@
{{ template "input-field" .Date }} {{ template "input-field" .Date }}
{{ template "tags-field" .Tags }} {{ template "tags-field" .Tags }}
{{ template "select-field" .PaymentMethod }} {{ template "select-field" .PaymentMethod }}
{{ template "file-field" .File }}
{{ template "input-field" .Notes }} {{ template "input-field" .Notes }}
</div> </div>
{{- range $product := .Products }} {{- range $product := .Products }}

View File

@ -20,10 +20,6 @@
<a class="primary button" <a class="primary button"
href="{{ companyURI "/invoices/" }}{{ .Slug }}.pdf" href="{{ companyURI "/invoices/" }}{{ .Slug }}.pdf"
download="{{ .Number}}-{{ .Invoicee.Name | slugify }}.pdf">{{( pgettext "Download invoice" "action" )}}</a> download="{{ .Number}}-{{ .Invoicee.Name | slugify }}.pdf">{{( pgettext "Download invoice" "action" )}}</a>
{{ if .OriginalFileName }}
<a class="primary button"
href="{{ companyURI "/invoices/"}}{{ .Slug }}/download/{{.OriginalFileName}}">{{( pgettext "Download invoice attachment" "action" )}}</a>
{{ end }}
</p> </p>
</nav> </nav>
{{- end }} {{- end }}