Add attachments to invoices
Works exactly the same as for expenses, and this is sometimes convenient for keeping transfer slips from customers and such. I actually did not know where to add the download from this attachment, because if add a column to the index it can easily be confused with the download icon for the actual invoice. Part of #66.
This commit is contained in:
parent
66ab3b4bf7
commit
bb7af20a17
|
@ -0,0 +1,30 @@
|
|||
-- 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;
|
|
@ -0,0 +1,32 @@
|
|||
-- 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;
|
|
@ -349,6 +349,7 @@ type invoice struct {
|
|||
HasDiscounts bool
|
||||
Total string
|
||||
LegalDisclaimer string
|
||||
OriginalFileName string
|
||||
}
|
||||
|
||||
type taxDetails struct {
|
||||
|
@ -394,11 +395,13 @@ func mustGetInvoice(ctx context.Context, conn *Conn, company *Company, slug stri
|
|||
, postal_code
|
||||
, to_price(subtotal, decimal_digits)
|
||||
, to_price(total, decimal_digits)
|
||||
, coalesce(attachment.original_filename, '')
|
||||
from invoice
|
||||
join payment_method using (payment_method_id)
|
||||
join contact_tax_details using (contact_id)
|
||||
join invoice_amount using (invoice_id)
|
||||
join currency using (currency_code)
|
||||
left join invoice_attachment as attachment using (invoice_id)
|
||||
where invoice.slug = $1`, slug).Scan(
|
||||
&invoiceId,
|
||||
&decimalDigits,
|
||||
|
@ -413,7 +416,8 @@ func mustGetInvoice(ctx context.Context, conn *Conn, company *Company, slug stri
|
|||
&inv.Invoicee.Province,
|
||||
&inv.Invoicee.PostalCode,
|
||||
&inv.Subtotal,
|
||||
&inv.Total)) {
|
||||
&inv.Total,
|
||||
&inv.OriginalFileName)) {
|
||||
return nil
|
||||
}
|
||||
if err := conn.QueryRow(ctx, `
|
||||
|
@ -605,6 +609,9 @@ func HandleAddInvoice(w http.ResponseWriter, r *http.Request, _ httprouter.Param
|
|||
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))
|
||||
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))
|
||||
}
|
||||
|
||||
|
@ -680,6 +687,7 @@ type invoiceForm struct {
|
|||
Tags *TagsField
|
||||
Products []*invoiceProductForm
|
||||
RemovedProduct *invoiceProductForm
|
||||
File *FileField
|
||||
}
|
||||
|
||||
func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *invoiceForm {
|
||||
|
@ -721,6 +729,11 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
|
|||
Selected: []string{mustGetDefaultPaymentMethod(ctx, conn, company)},
|
||||
Options: mustGetPaymentMethodOptions(ctx, conn, company),
|
||||
},
|
||||
File: &FileField{
|
||||
Name: "file",
|
||||
Label: pgettext("input", "File", locale),
|
||||
MaxSize: 1 << 20,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -735,7 +748,7 @@ func mustGetInvoiceStatusOptions(ctx context.Context, conn *Conn, locale *Locale
|
|||
}
|
||||
|
||||
func (form *invoiceForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
if err := r.ParseMultipartForm(form.File.MaxSize); err != nil {
|
||||
return err
|
||||
}
|
||||
form.InvoiceStatus.FillValue(r)
|
||||
|
@ -744,6 +757,9 @@ func (form *invoiceForm) Parse(r *http.Request) error {
|
|||
form.Notes.FillValue(r)
|
||||
form.Tags.FillValue(r)
|
||||
form.PaymentMethod.FillValue(r)
|
||||
if err := form.File.FillValue(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := r.Form["product.id.0"]; ok {
|
||||
taxOptions := mustGetTaxOptions(r.Context(), getConn(r), form.company)
|
||||
for index := 0; true; index++ {
|
||||
|
@ -1146,6 +1162,9 @@ func HandleUpdateInvoice(w http.ResponseWriter, r *http.Request, params httprout
|
|||
http.NotFound(w, r)
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
@ -1318,3 +1337,24 @@ func HandleUpdateInvoiceTags(w http.ResponseWriter, r *http.Request, params http
|
|||
}
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ func NewRouter(db *Db) http.Handler {
|
|||
companyRouter.POST("/invoices/:slug/edit", HandleEditInvoiceAction)
|
||||
companyRouter.PUT("/invoices/:slug/tags", HandleUpdateInvoiceTags)
|
||||
companyRouter.GET("/invoices/:slug/tags/edit", ServeEditInvoiceTags)
|
||||
companyRouter.GET("/invoices/:slug/download/:filename", ServeInvoiceAttachment)
|
||||
companyRouter.GET("/quotes", IndexQuotes)
|
||||
companyRouter.POST("/quotes", HandleAddQuote)
|
||||
companyRouter.GET("/quotes/:slug", ServeQuote)
|
||||
|
|
208
po/ca.po
208
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-07-06 11:45+0200\n"
|
||||
"POT-Creation-Date: 2023-07-12 20:01+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"
|
||||
|
@ -65,7 +65,7 @@ msgid "Name"
|
|||
msgstr "Nom"
|
||||
|
||||
#: web/template/invoices/products.gohtml:50
|
||||
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/products.gohtml:50
|
||||
#: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42
|
||||
msgctxt "title"
|
||||
msgid "Price"
|
||||
|
@ -76,8 +76,8 @@ msgstr "Preu"
|
|||
msgid "No products added yet."
|
||||
msgstr "No hi ha cap producte."
|
||||
|
||||
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:87
|
||||
#: web/template/invoices/edit.gohtml:88 web/template/quotes/products.gohtml:72
|
||||
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:88
|
||||
#: web/template/invoices/edit.gohtml:89 web/template/quotes/products.gohtml:72
|
||||
#: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89
|
||||
msgctxt "action"
|
||||
msgid "Add products"
|
||||
|
@ -94,31 +94,31 @@ msgctxt "action"
|
|||
msgid "Undo"
|
||||
msgstr "Desfes"
|
||||
|
||||
#: web/template/invoices/new.gohtml:63 web/template/invoices/view.gohtml:67
|
||||
#: web/template/invoices/edit.gohtml:64 web/template/quotes/new.gohtml:64
|
||||
#: web/template/invoices/new.gohtml:64 web/template/invoices/view.gohtml:71
|
||||
#: web/template/invoices/edit.gohtml:65 web/template/quotes/new.gohtml:64
|
||||
#: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:65
|
||||
msgctxt "title"
|
||||
msgid "Subtotal"
|
||||
msgstr "Subtotal"
|
||||
|
||||
#: web/template/invoices/new.gohtml:73 web/template/invoices/view.gohtml:71
|
||||
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:74
|
||||
#: web/template/invoices/new.gohtml:74 web/template/invoices/view.gohtml:75
|
||||
#: 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
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: web/template/invoices/new.gohtml:91 web/template/invoices/edit.gohtml:92
|
||||
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93
|
||||
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
msgstr "Actualitza"
|
||||
|
||||
#: web/template/invoices/new.gohtml:94 web/template/invoices/edit.gohtml:95
|
||||
#: 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:33 web/template/expenses/edit.gohtml:38
|
||||
#: web/template/expenses/new.gohtml:34 web/template/expenses/edit.gohtml:40
|
||||
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
|
||||
msgctxt "action"
|
||||
msgid "Save"
|
||||
|
@ -136,7 +136,7 @@ msgstr "Nova factura"
|
|||
|
||||
#: 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/expenses/index.gohtml:36 web/template/products/index.gohtml:34
|
||||
#: web/template/expenses/index.gohtml:37 web/template/products/index.gohtml:34
|
||||
msgctxt "action"
|
||||
msgid "Filter"
|
||||
msgstr "Filtra"
|
||||
|
@ -146,7 +146,7 @@ msgctxt "invoice"
|
|||
msgid "All"
|
||||
msgstr "Totes"
|
||||
|
||||
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:34
|
||||
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:38
|
||||
#: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:37
|
||||
msgctxt "title"
|
||||
msgid "Date"
|
||||
|
@ -163,31 +163,32 @@ msgid "Customer"
|
|||
msgstr "Client"
|
||||
|
||||
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
|
||||
#: web/template/expenses/index.gohtml:46
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estat"
|
||||
|
||||
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
|
||||
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:45
|
||||
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:47
|
||||
#: web/template/products/index.gohtml:41
|
||||
msgctxt "title"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55
|
||||
#: web/template/expenses/index.gohtml:46
|
||||
#: web/template/expenses/index.gohtml:48
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56
|
||||
#: web/template/expenses/index.gohtml:47
|
||||
#: web/template/expenses/index.gohtml:49
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descàrrega"
|
||||
|
||||
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
|
||||
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:48
|
||||
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:50
|
||||
#: web/template/products/index.gohtml:43
|
||||
msgctxt "title"
|
||||
msgid "Actions"
|
||||
|
@ -209,7 +210,7 @@ msgstr "Accions per la factura %s"
|
|||
|
||||
#: 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/contacts/index.gohtml:77 web/template/expenses/index.gohtml:87
|
||||
#: web/template/contacts/index.gohtml:77 web/template/expenses/index.gohtml:111
|
||||
#: web/template/products/index.gohtml:73
|
||||
msgctxt "action"
|
||||
msgid "Edit"
|
||||
|
@ -226,11 +227,11 @@ msgid "No invoices added yet."
|
|||
msgstr "No hi ha cap factura."
|
||||
|
||||
#: web/template/invoices/index.gohtml:146 web/template/quotes/index.gohtml:153
|
||||
#: web/template/expenses/index.gohtml:104
|
||||
#: web/template/expenses/index.gohtml:128
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:33
|
||||
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:37
|
||||
msgctxt "title"
|
||||
msgid "Invoice %s"
|
||||
msgstr "Factura %s"
|
||||
|
@ -240,22 +241,27 @@ msgctxt "action"
|
|||
msgid "Download invoice"
|
||||
msgstr "Descarrega factura"
|
||||
|
||||
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:72
|
||||
#: web/template/invoices/view.gohtml:25
|
||||
msgctxt "action"
|
||||
msgid "Download invoice attachment"
|
||||
msgstr "Descarrega l’adjunt de la factura"
|
||||
|
||||
#: web/template/invoices/view.gohtml:65 web/template/quotes/view.gohtml:72
|
||||
msgctxt "title"
|
||||
msgid "Concept"
|
||||
msgstr "Concepte"
|
||||
|
||||
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:75
|
||||
#: web/template/invoices/view.gohtml:68 web/template/quotes/view.gohtml:75
|
||||
msgctxt "title"
|
||||
msgid "Discount"
|
||||
msgstr "Descompte"
|
||||
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:77
|
||||
#: web/template/invoices/view.gohtml:70 web/template/quotes/view.gohtml:77
|
||||
msgctxt "title"
|
||||
msgid "Units"
|
||||
msgstr "Unitats"
|
||||
|
||||
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:112
|
||||
#: web/template/invoices/view.gohtml:105 web/template/quotes/view.gohtml:112
|
||||
msgctxt "title"
|
||||
msgid "Tax Base"
|
||||
msgstr "Base imposable"
|
||||
|
@ -473,7 +479,7 @@ msgctxt "action"
|
|||
msgid "New contact"
|
||||
msgstr "Nou contacte"
|
||||
|
||||
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:42
|
||||
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:43
|
||||
msgctxt "title"
|
||||
msgid "Contact"
|
||||
msgstr "Contacte"
|
||||
|
@ -559,21 +565,21 @@ msgctxt "action"
|
|||
msgid "New expense"
|
||||
msgstr "Nova despesa"
|
||||
|
||||
#: web/template/expenses/index.gohtml:43
|
||||
#: web/template/expenses/index.gohtml:44
|
||||
msgctxt "title"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Data de factura"
|
||||
|
||||
#: web/template/expenses/index.gohtml:44
|
||||
#: web/template/expenses/index.gohtml:45
|
||||
msgctxt "title"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: web/template/expenses/index.gohtml:79
|
||||
#: web/template/expenses/index.gohtml:103
|
||||
msgid "Actions for expense %s"
|
||||
msgstr "Accions per la despesa %s"
|
||||
|
||||
#: web/template/expenses/index.gohtml:97
|
||||
#: web/template/expenses/index.gohtml:121
|
||||
msgid "No expenses added yet."
|
||||
msgstr "No hi ha cap despesa."
|
||||
|
||||
|
@ -706,83 +712,84 @@ msgstr "No podeu deixar la contrasenya en blanc."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:878 pkg/invoices.go:993
|
||||
#: pkg/contacts.go:140 pkg/contacts.go:248
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
||||
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
||||
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:145
|
||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:685
|
||||
#: pkg/expenses.go:228 pkg/expenses.go:417 pkg/invoices.go:174
|
||||
#: pkg/invoices.go:723 pkg/invoices.go:1295 pkg/contacts.go:145
|
||||
#: pkg/contacts.go:348
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
|
||||
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:427 pkg/invoices.go:178
|
||||
#: pkg/contacts.go:149
|
||||
msgctxt "input"
|
||||
msgid "Tags Condition"
|
||||
msgstr "Condició de les etiquetes"
|
||||
|
||||
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
|
||||
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:431 pkg/invoices.go:182
|
||||
#: pkg/contacts.go:153
|
||||
msgctxt "tag condition"
|
||||
msgid "All"
|
||||
msgstr "Totes"
|
||||
|
||||
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
|
||||
#: pkg/products.go:178 pkg/expenses.go:432 pkg/invoices.go:183
|
||||
#: pkg/contacts.go:154
|
||||
msgid "Invoices must have all the specified labels."
|
||||
msgstr "Les factures han de tenir totes les etiquetes."
|
||||
|
||||
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
|
||||
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:436 pkg/invoices.go:187
|
||||
#: pkg/contacts.go:158
|
||||
msgctxt "tag condition"
|
||||
msgid "Any"
|
||||
msgstr "Qualsevol"
|
||||
|
||||
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
|
||||
#: pkg/products.go:183 pkg/expenses.go:437 pkg/invoices.go:188
|
||||
#: pkg/contacts.go:159
|
||||
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."
|
||||
|
||||
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:923
|
||||
#: pkg/products.go:269 pkg/quote.go:892 pkg/invoices.go:1007
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripció"
|
||||
|
||||
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:927
|
||||
#: pkg/products.go:274 pkg/quote.go:896 pkg/invoices.go:1011
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
||||
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:956
|
||||
#: pkg/products.go:284 pkg/quote.go:925 pkg/expenses.go:200
|
||||
#: pkg/invoices.go:1040
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Imposts"
|
||||
|
||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
||||
#: pkg/products.go:309 pkg/quote.go:974 pkg/profile.go:92 pkg/invoices.go:1089
|
||||
#: pkg/contacts.go:398
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podeu deixar el nom en blanc."
|
||||
|
||||
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:1006
|
||||
#: pkg/products.go:310 pkg/quote.go:975 pkg/invoices.go:1090
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podeu deixar el preu en blanc."
|
||||
|
||||
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:1007
|
||||
#: pkg/products.go:311 pkg/quote.go:976 pkg/invoices.go:1091
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El preu ha de ser un número major a zero."
|
||||
|
||||
#: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
|
||||
#: pkg/invoices.go:1015
|
||||
#: pkg/products.go:313 pkg/quote.go:984 pkg/expenses.go:264 pkg/expenses.go:269
|
||||
#: pkg/invoices.go:1099
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Heu seleccionat un impost que no és vàlid."
|
||||
|
||||
#: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
|
||||
#: pkg/invoices.go:1016
|
||||
#: pkg/products.go:314 pkg/quote.go:985 pkg/expenses.go:265 pkg/expenses.go:270
|
||||
#: pkg/invoices.go:1100
|
||||
msgid "You can only select a tax of each class."
|
||||
msgstr "Només podeu seleccionar un impost de cada classe."
|
||||
|
||||
|
@ -991,7 +998,7 @@ msgstr "No podeu deixar el nom del mètode de pagament en blanc."
|
|||
msgid "Payment instructions can not be empty."
|
||||
msgstr "No podeu deixar les instruccions de pagament en blanc."
|
||||
|
||||
#: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:640
|
||||
#: pkg/quote.go:147 pkg/quote.go:663 pkg/invoices.go:147 pkg/invoices.go:706
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
@ -1000,12 +1007,12 @@ msgstr "Client"
|
|||
msgid "All customers"
|
||||
msgstr "Tots els clients"
|
||||
|
||||
#: pkg/quote.go:153 pkg/quote.go:602
|
||||
#: pkg/quote.go:153 pkg/quote.go:657
|
||||
msgctxt "input"
|
||||
msgid "Quotation Status"
|
||||
msgstr "Estat del pressupost"
|
||||
|
||||
#: pkg/quote.go:154 pkg/invoices.go:154
|
||||
#: pkg/quote.go:154 pkg/expenses.go:422 pkg/invoices.go:154
|
||||
msgid "All status"
|
||||
msgstr "Tots els estats"
|
||||
|
||||
|
@ -1014,12 +1021,12 @@ msgctxt "input"
|
|||
msgid "Quotation Number"
|
||||
msgstr "Número de pressupost"
|
||||
|
||||
#: pkg/quote.go:164 pkg/expenses.go:337 pkg/invoices.go:164
|
||||
#: pkg/quote.go:164 pkg/expenses.go:407 pkg/invoices.go:164
|
||||
msgctxt "input"
|
||||
msgid "From Date"
|
||||
msgstr "A partir de la data"
|
||||
|
||||
#: pkg/quote.go:169 pkg/expenses.go:342 pkg/invoices.go:169
|
||||
#: pkg/quote.go:169 pkg/expenses.go:412 pkg/invoices.go:169
|
||||
msgctxt "input"
|
||||
msgid "To Date"
|
||||
msgstr "Fins la data"
|
||||
|
@ -1032,99 +1039,99 @@ msgstr "Els pressuposts han de tenir totes les etiquetes."
|
|||
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."
|
||||
|
||||
#: pkg/quote.go:550
|
||||
#: pkg/quote.go:605
|
||||
msgid "quotations.zip"
|
||||
msgstr "pressuposts.zip"
|
||||
|
||||
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:589
|
||||
#: pkg/invoices.go:1183 pkg/invoices.go:1191
|
||||
#: pkg/quote.go:611 pkg/quote.go:1140 pkg/quote.go:1148 pkg/invoices.go:654
|
||||
#: pkg/invoices.go:1270 pkg/invoices.go:1278
|
||||
msgid "Invalid action"
|
||||
msgstr "Acció invàlida."
|
||||
|
||||
#: pkg/quote.go:609
|
||||
#: pkg/quote.go:664
|
||||
msgid "Select a customer to quote."
|
||||
msgstr "Escolliu un client a pressupostar."
|
||||
|
||||
#: pkg/quote.go:614
|
||||
#: pkg/quote.go:669
|
||||
msgctxt "input"
|
||||
msgid "Quotation Date"
|
||||
msgstr "Data del pressupost"
|
||||
|
||||
#: pkg/quote.go:620
|
||||
#: pkg/quote.go:675
|
||||
msgctxt "input"
|
||||
msgid "Terms and conditions"
|
||||
msgstr "Condicions d’acceptació"
|
||||
|
||||
#: pkg/quote.go:625 pkg/invoices.go:652
|
||||
#: pkg/quote.go:680 pkg/invoices.go:718
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notes"
|
||||
|
||||
#: pkg/quote.go:634 pkg/invoices.go:662
|
||||
#: pkg/quote.go:689 pkg/invoices.go:728
|
||||
msgctxt "input"
|
||||
msgid "Payment Method"
|
||||
msgstr "Mètode de pagament"
|
||||
|
||||
#: pkg/quote.go:635
|
||||
#: pkg/quote.go:690
|
||||
msgid "Select a payment method."
|
||||
msgstr "Escolliu un mètode de pagament."
|
||||
|
||||
#: pkg/quote.go:671
|
||||
#: pkg/quote.go:726
|
||||
msgid "Selected quotation status is not valid."
|
||||
msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
|
||||
|
||||
#: pkg/quote.go:673 pkg/invoices.go:699
|
||||
#: pkg/quote.go:728 pkg/invoices.go:783
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "Heu seleccionat un client que no és vàlid."
|
||||
|
||||
#: pkg/quote.go:675
|
||||
#: pkg/quote.go:730
|
||||
msgid "Quotation date can not be empty."
|
||||
msgstr "No podeu deixar la data del pressupost en blanc."
|
||||
|
||||
#: pkg/quote.go:676
|
||||
#: pkg/quote.go:731
|
||||
msgid "Quotation date must be a valid date."
|
||||
msgstr "La data del pressupost ha de ser vàlida."
|
||||
|
||||
#: pkg/quote.go:679 pkg/invoices.go:703
|
||||
#: pkg/quote.go:734 pkg/invoices.go:787
|
||||
msgid "Selected payment method is not valid."
|
||||
msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
|
||||
|
||||
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:899 pkg/invoices.go:904
|
||||
#: pkg/quote.go:868 pkg/quote.go:873 pkg/invoices.go:983 pkg/invoices.go:988
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/quote.go:851 pkg/invoices.go:937
|
||||
#: pkg/quote.go:906 pkg/invoices.go:1021
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantitat"
|
||||
|
||||
#: pkg/quote.go:860 pkg/invoices.go:946
|
||||
#: pkg/quote.go:915 pkg/invoices.go:1030
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descompte (%)"
|
||||
|
||||
#: pkg/quote.go:914
|
||||
#: pkg/quote.go:969
|
||||
msgid "Quotation product ID must be a number greater than zero."
|
||||
msgstr "L’ID del producte de pressupost ha de ser un número major a zero."
|
||||
|
||||
#: pkg/quote.go:917 pkg/invoices.go:1003
|
||||
#: pkg/quote.go:972 pkg/invoices.go:1087
|
||||
msgid "Product ID must be a positive number or zero."
|
||||
msgstr "L’ID del producte ha de ser un número positiu o zero."
|
||||
|
||||
#: pkg/quote.go:923 pkg/invoices.go:1009
|
||||
#: pkg/quote.go:978 pkg/invoices.go:1093
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podeu deixar la quantitat en blanc."
|
||||
|
||||
#: pkg/quote.go:924 pkg/invoices.go:1010
|
||||
#: pkg/quote.go:979 pkg/invoices.go:1094
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La quantitat ha de ser un número major a zero."
|
||||
|
||||
#: pkg/quote.go:926 pkg/invoices.go:1012
|
||||
#: pkg/quote.go:981 pkg/invoices.go:1096
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podeu deixar el descompte en blanc."
|
||||
|
||||
#: pkg/quote.go:927 pkg/invoices.go:1013
|
||||
#: pkg/quote.go:982 pkg/invoices.go:1097
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
|
||||
|
||||
|
@ -1191,92 +1198,101 @@ msgctxt "period option"
|
|||
msgid "Previous year"
|
||||
msgstr "Any anterior"
|
||||
|
||||
#: pkg/expenses.go:115
|
||||
#: pkg/expenses.go:147
|
||||
msgid "Select a contact."
|
||||
msgstr "Escolliu un contacte."
|
||||
|
||||
#: pkg/expenses.go:150 pkg/expenses.go:326
|
||||
#: pkg/expenses.go:183 pkg/expenses.go:396
|
||||
msgctxt "input"
|
||||
msgid "Contact"
|
||||
msgstr "Contacte"
|
||||
|
||||
#: pkg/expenses.go:156
|
||||
#: pkg/expenses.go:189
|
||||
msgctxt "input"
|
||||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:161 pkg/invoices.go:646
|
||||
#: pkg/expenses.go:194 pkg/invoices.go:712
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Data de factura"
|
||||
|
||||
#: pkg/expenses.go:173
|
||||
#: pkg/expenses.go:206
|
||||
msgctxt "input"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: pkg/expenses.go:183
|
||||
#: pkg/expenses.go:216 pkg/invoices.go:734
|
||||
msgctxt "input"
|
||||
msgid "File"
|
||||
msgstr "Fitxer"
|
||||
|
||||
#: pkg/expenses.go:211
|
||||
#: pkg/expenses.go:222 pkg/expenses.go:421
|
||||
msgctxt "input"
|
||||
msgid "Expense Status"
|
||||
msgstr "Estat de la despesa"
|
||||
|
||||
#: pkg/expenses.go:262
|
||||
msgid "Selected contact is not valid."
|
||||
msgstr "Heu seleccionat un contacte que no és vàlid."
|
||||
|
||||
#: pkg/expenses.go:212 pkg/invoices.go:701
|
||||
#: pkg/expenses.go:263 pkg/invoices.go:785
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La data de facturació ha de ser vàlida."
|
||||
|
||||
#: pkg/expenses.go:215
|
||||
#: pkg/expenses.go:266
|
||||
msgid "Amount can not be empty."
|
||||
msgstr "No podeu deixar l’import en blanc."
|
||||
|
||||
#: pkg/expenses.go:216
|
||||
#: pkg/expenses.go:267
|
||||
msgid "Amount must be a number greater than zero."
|
||||
msgstr "L’import ha de ser un número major a zero."
|
||||
|
||||
#: pkg/expenses.go:327
|
||||
#: pkg/expenses.go:271
|
||||
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"
|
||||
msgstr "Tots els contactes"
|
||||
|
||||
#: pkg/expenses.go:332 pkg/invoices.go:159
|
||||
#: pkg/expenses.go:402 pkg/invoices.go:159
|
||||
msgctxt "input"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:634
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:700
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estat de la factura"
|
||||
|
||||
#: pkg/invoices.go:482
|
||||
#: pkg/invoices.go:544
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escolliu un client a facturar."
|
||||
|
||||
#: pkg/invoices.go:583
|
||||
#: pkg/invoices.go:648
|
||||
msgid "invoices.zip"
|
||||
msgstr "factures.zip"
|
||||
|
||||
#: pkg/invoices.go:698
|
||||
#: pkg/invoices.go:782
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "Heu seleccionat un estat de factura que no és vàlid."
|
||||
|
||||
#: pkg/invoices.go:700
|
||||
#: pkg/invoices.go:784
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podeu deixar la data de la factura en blanc."
|
||||
|
||||
#: pkg/invoices.go:836
|
||||
#: pkg/invoices.go:920
|
||||
#, c-format
|
||||
msgid "Re: quotation #%s of %s"
|
||||
msgstr "Ref: pressupost núm. %s del %s"
|
||||
|
||||
#: pkg/invoices.go:837
|
||||
#: pkg/invoices.go:921
|
||||
msgctxt "to_char"
|
||||
msgid "MM/DD/YYYY"
|
||||
msgstr "DD/MM/YYYY"
|
||||
|
||||
#: pkg/invoices.go:1000
|
||||
#: pkg/invoices.go:1084
|
||||
msgid "Invoice product ID must be a number greater than zero."
|
||||
msgstr "L’ID del producte de factura ha de ser un número major a zero."
|
||||
|
||||
|
|
208
po/es.po
208
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-07-06 11:45+0200\n"
|
||||
"POT-Creation-Date: 2023-07-12 20:01+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"
|
||||
|
@ -65,7 +65,7 @@ msgid "Name"
|
|||
msgstr "Nombre"
|
||||
|
||||
#: web/template/invoices/products.gohtml:50
|
||||
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/products.gohtml:50
|
||||
#: web/template/quotes/view.gohtml:73 web/template/products/index.gohtml:42
|
||||
msgctxt "title"
|
||||
msgid "Price"
|
||||
|
@ -76,8 +76,8 @@ msgstr "Precio"
|
|||
msgid "No products added yet."
|
||||
msgstr "No hay productos."
|
||||
|
||||
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:87
|
||||
#: web/template/invoices/edit.gohtml:88 web/template/quotes/products.gohtml:72
|
||||
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:88
|
||||
#: web/template/invoices/edit.gohtml:89 web/template/quotes/products.gohtml:72
|
||||
#: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89
|
||||
msgctxt "action"
|
||||
msgid "Add products"
|
||||
|
@ -94,31 +94,31 @@ msgctxt "action"
|
|||
msgid "Undo"
|
||||
msgstr "Deshacer"
|
||||
|
||||
#: web/template/invoices/new.gohtml:63 web/template/invoices/view.gohtml:67
|
||||
#: web/template/invoices/edit.gohtml:64 web/template/quotes/new.gohtml:64
|
||||
#: web/template/invoices/new.gohtml:64 web/template/invoices/view.gohtml:71
|
||||
#: web/template/invoices/edit.gohtml:65 web/template/quotes/new.gohtml:64
|
||||
#: web/template/quotes/view.gohtml:78 web/template/quotes/edit.gohtml:65
|
||||
msgctxt "title"
|
||||
msgid "Subtotal"
|
||||
msgstr "Subtotal"
|
||||
|
||||
#: web/template/invoices/new.gohtml:73 web/template/invoices/view.gohtml:71
|
||||
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:74
|
||||
#: web/template/invoices/new.gohtml:74 web/template/invoices/view.gohtml:75
|
||||
#: 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
|
||||
msgctxt "title"
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: web/template/invoices/new.gohtml:91 web/template/invoices/edit.gohtml:92
|
||||
#: web/template/invoices/new.gohtml:92 web/template/invoices/edit.gohtml:93
|
||||
#: web/template/quotes/new.gohtml:92 web/template/quotes/edit.gohtml:93
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
msgstr "Actualizar"
|
||||
|
||||
#: web/template/invoices/new.gohtml:94 web/template/invoices/edit.gohtml:95
|
||||
#: 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:33 web/template/expenses/edit.gohtml:38
|
||||
#: web/template/expenses/new.gohtml:34 web/template/expenses/edit.gohtml:40
|
||||
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
|
||||
msgctxt "action"
|
||||
msgid "Save"
|
||||
|
@ -136,7 +136,7 @@ msgstr "Nueva factura"
|
|||
|
||||
#: 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/expenses/index.gohtml:36 web/template/products/index.gohtml:34
|
||||
#: web/template/expenses/index.gohtml:37 web/template/products/index.gohtml:34
|
||||
msgctxt "action"
|
||||
msgid "Filter"
|
||||
msgstr "Filtrar"
|
||||
|
@ -146,7 +146,7 @@ msgctxt "invoice"
|
|||
msgid "All"
|
||||
msgstr "Todas"
|
||||
|
||||
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:34
|
||||
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:38
|
||||
#: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:37
|
||||
msgctxt "title"
|
||||
msgid "Date"
|
||||
|
@ -163,31 +163,32 @@ msgid "Customer"
|
|||
msgstr "Cliente"
|
||||
|
||||
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
|
||||
#: web/template/expenses/index.gohtml:46
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estado"
|
||||
|
||||
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
|
||||
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:45
|
||||
#: web/template/contacts/index.gohtml:45 web/template/expenses/index.gohtml:47
|
||||
#: web/template/products/index.gohtml:41
|
||||
msgctxt "title"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55
|
||||
#: web/template/expenses/index.gohtml:46
|
||||
#: web/template/expenses/index.gohtml:48
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Importe"
|
||||
|
||||
#: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56
|
||||
#: web/template/expenses/index.gohtml:47
|
||||
#: web/template/expenses/index.gohtml:49
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descargar"
|
||||
|
||||
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
|
||||
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:48
|
||||
#: web/template/contacts/index.gohtml:46 web/template/expenses/index.gohtml:50
|
||||
#: web/template/products/index.gohtml:43
|
||||
msgctxt "title"
|
||||
msgid "Actions"
|
||||
|
@ -209,7 +210,7 @@ msgstr "Acciones para la factura %s"
|
|||
|
||||
#: 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/contacts/index.gohtml:77 web/template/expenses/index.gohtml:87
|
||||
#: web/template/contacts/index.gohtml:77 web/template/expenses/index.gohtml:111
|
||||
#: web/template/products/index.gohtml:73
|
||||
msgctxt "action"
|
||||
msgid "Edit"
|
||||
|
@ -226,11 +227,11 @@ msgid "No invoices added yet."
|
|||
msgstr "No hay facturas."
|
||||
|
||||
#: web/template/invoices/index.gohtml:146 web/template/quotes/index.gohtml:153
|
||||
#: web/template/expenses/index.gohtml:104
|
||||
#: web/template/expenses/index.gohtml:128
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:33
|
||||
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:37
|
||||
msgctxt "title"
|
||||
msgid "Invoice %s"
|
||||
msgstr "Factura %s"
|
||||
|
@ -240,22 +241,27 @@ msgctxt "action"
|
|||
msgid "Download invoice"
|
||||
msgstr "Descargar factura"
|
||||
|
||||
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:72
|
||||
#: web/template/invoices/view.gohtml:25
|
||||
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"
|
||||
msgid "Concept"
|
||||
msgstr "Concepto"
|
||||
|
||||
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:75
|
||||
#: web/template/invoices/view.gohtml:68 web/template/quotes/view.gohtml:75
|
||||
msgctxt "title"
|
||||
msgid "Discount"
|
||||
msgstr "Descuento"
|
||||
|
||||
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:77
|
||||
#: web/template/invoices/view.gohtml:70 web/template/quotes/view.gohtml:77
|
||||
msgctxt "title"
|
||||
msgid "Units"
|
||||
msgstr "Unidades"
|
||||
|
||||
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:112
|
||||
#: web/template/invoices/view.gohtml:105 web/template/quotes/view.gohtml:112
|
||||
msgctxt "title"
|
||||
msgid "Tax Base"
|
||||
msgstr "Base imponible"
|
||||
|
@ -473,7 +479,7 @@ msgctxt "action"
|
|||
msgid "New contact"
|
||||
msgstr "Nuevo contacto"
|
||||
|
||||
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:42
|
||||
#: web/template/contacts/index.gohtml:42 web/template/expenses/index.gohtml:43
|
||||
msgctxt "title"
|
||||
msgid "Contact"
|
||||
msgstr "Contacto"
|
||||
|
@ -559,21 +565,21 @@ msgctxt "action"
|
|||
msgid "New expense"
|
||||
msgstr "Nuevo gasto"
|
||||
|
||||
#: web/template/expenses/index.gohtml:43
|
||||
#: web/template/expenses/index.gohtml:44
|
||||
msgctxt "title"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Fecha de factura"
|
||||
|
||||
#: web/template/expenses/index.gohtml:44
|
||||
#: web/template/expenses/index.gohtml:45
|
||||
msgctxt "title"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: web/template/expenses/index.gohtml:79
|
||||
#: web/template/expenses/index.gohtml:103
|
||||
msgid "Actions for expense %s"
|
||||
msgstr "Acciones para el gasto %s"
|
||||
|
||||
#: web/template/expenses/index.gohtml:97
|
||||
#: web/template/expenses/index.gohtml:121
|
||||
msgid "No expenses added yet."
|
||||
msgstr "No hay gastos."
|
||||
|
||||
|
@ -706,83 +712,84 @@ msgstr "No podéis dejar la contraseña en blanco."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nombre de usuario o contraseña inválido."
|
||||
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:909
|
||||
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:878 pkg/invoices.go:993
|
||||
#: pkg/contacts.go:140 pkg/contacts.go:248
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
|
||||
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
|
||||
#: pkg/invoices.go:657 pkg/invoices.go:1208 pkg/contacts.go:145
|
||||
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:685
|
||||
#: pkg/expenses.go:228 pkg/expenses.go:417 pkg/invoices.go:174
|
||||
#: pkg/invoices.go:723 pkg/invoices.go:1295 pkg/contacts.go:145
|
||||
#: pkg/contacts.go:348
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
|
||||
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:427 pkg/invoices.go:178
|
||||
#: pkg/contacts.go:149
|
||||
msgctxt "input"
|
||||
msgid "Tags Condition"
|
||||
msgstr "Condición de las etiquetas"
|
||||
|
||||
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
|
||||
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:431 pkg/invoices.go:182
|
||||
#: pkg/contacts.go:153
|
||||
msgctxt "tag condition"
|
||||
msgid "All"
|
||||
msgstr "Todas"
|
||||
|
||||
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
|
||||
#: pkg/products.go:178 pkg/expenses.go:432 pkg/invoices.go:183
|
||||
#: pkg/contacts.go:154
|
||||
msgid "Invoices must have all the specified labels."
|
||||
msgstr "Las facturas deben tener todas las etiquetas."
|
||||
|
||||
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
|
||||
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:436 pkg/invoices.go:187
|
||||
#: pkg/contacts.go:158
|
||||
msgctxt "tag condition"
|
||||
msgid "Any"
|
||||
msgstr "Cualquiera"
|
||||
|
||||
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
|
||||
#: pkg/products.go:183 pkg/expenses.go:437 pkg/invoices.go:188
|
||||
#: pkg/contacts.go:159
|
||||
msgid "Invoices must have at least one of the specified labels."
|
||||
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
|
||||
|
||||
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:923
|
||||
#: pkg/products.go:269 pkg/quote.go:892 pkg/invoices.go:1007
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:927
|
||||
#: pkg/products.go:274 pkg/quote.go:896 pkg/invoices.go:1011
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Precio"
|
||||
|
||||
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:956
|
||||
#: pkg/products.go:284 pkg/quote.go:925 pkg/expenses.go:200
|
||||
#: pkg/invoices.go:1040
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Impuestos"
|
||||
|
||||
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:1005
|
||||
#: pkg/products.go:309 pkg/quote.go:974 pkg/profile.go:92 pkg/invoices.go:1089
|
||||
#: pkg/contacts.go:398
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podéis dejar el nombre en blanco."
|
||||
|
||||
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:1006
|
||||
#: pkg/products.go:310 pkg/quote.go:975 pkg/invoices.go:1090
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podéis dejar el precio en blanco."
|
||||
|
||||
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:1007
|
||||
#: pkg/products.go:311 pkg/quote.go:976 pkg/invoices.go:1091
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El precio tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
|
||||
#: pkg/invoices.go:1015
|
||||
#: pkg/products.go:313 pkg/quote.go:984 pkg/expenses.go:264 pkg/expenses.go:269
|
||||
#: pkg/invoices.go:1099
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Habéis escogido un impuesto que no es válido."
|
||||
|
||||
#: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
|
||||
#: pkg/invoices.go:1016
|
||||
#: pkg/products.go:314 pkg/quote.go:985 pkg/expenses.go:265 pkg/expenses.go:270
|
||||
#: pkg/invoices.go:1100
|
||||
msgid "You can only select a tax of each class."
|
||||
msgstr "Solo podéis escoger un impuesto de cada clase."
|
||||
|
||||
|
@ -991,7 +998,7 @@ msgstr "No podéis dejar el nombre del método de pago en blanco."
|
|||
msgid "Payment instructions can not be empty."
|
||||
msgstr "No podéis dejar las instrucciones de pago en blanco."
|
||||
|
||||
#: pkg/quote.go:147 pkg/quote.go:608 pkg/invoices.go:147 pkg/invoices.go:640
|
||||
#: pkg/quote.go:147 pkg/quote.go:663 pkg/invoices.go:147 pkg/invoices.go:706
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Cliente"
|
||||
|
@ -1000,12 +1007,12 @@ msgstr "Cliente"
|
|||
msgid "All customers"
|
||||
msgstr "Todos los clientes"
|
||||
|
||||
#: pkg/quote.go:153 pkg/quote.go:602
|
||||
#: pkg/quote.go:153 pkg/quote.go:657
|
||||
msgctxt "input"
|
||||
msgid "Quotation Status"
|
||||
msgstr "Estado del presupuesto"
|
||||
|
||||
#: pkg/quote.go:154 pkg/invoices.go:154
|
||||
#: pkg/quote.go:154 pkg/expenses.go:422 pkg/invoices.go:154
|
||||
msgid "All status"
|
||||
msgstr "Todos los estados"
|
||||
|
||||
|
@ -1014,12 +1021,12 @@ msgctxt "input"
|
|||
msgid "Quotation Number"
|
||||
msgstr "Número de presupuesto"
|
||||
|
||||
#: pkg/quote.go:164 pkg/expenses.go:337 pkg/invoices.go:164
|
||||
#: pkg/quote.go:164 pkg/expenses.go:407 pkg/invoices.go:164
|
||||
msgctxt "input"
|
||||
msgid "From Date"
|
||||
msgstr "A partir de la fecha"
|
||||
|
||||
#: pkg/quote.go:169 pkg/expenses.go:342 pkg/invoices.go:169
|
||||
#: pkg/quote.go:169 pkg/expenses.go:412 pkg/invoices.go:169
|
||||
msgctxt "input"
|
||||
msgid "To Date"
|
||||
msgstr "Hasta la fecha"
|
||||
|
@ -1032,99 +1039,99 @@ msgstr "Los presupuestos deben tener todas las etiquetas."
|
|||
msgid "Quotations must have at least one of the specified labels."
|
||||
msgstr "Los presupuestos deben tener como mínimo una de las etiquetas."
|
||||
|
||||
#: pkg/quote.go:550
|
||||
#: pkg/quote.go:605
|
||||
msgid "quotations.zip"
|
||||
msgstr "presupuestos.zip"
|
||||
|
||||
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:589
|
||||
#: pkg/invoices.go:1183 pkg/invoices.go:1191
|
||||
#: pkg/quote.go:611 pkg/quote.go:1140 pkg/quote.go:1148 pkg/invoices.go:654
|
||||
#: pkg/invoices.go:1270 pkg/invoices.go:1278
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción inválida."
|
||||
|
||||
#: pkg/quote.go:609
|
||||
#: pkg/quote.go:664
|
||||
msgid "Select a customer to quote."
|
||||
msgstr "Escoged un cliente a presupuestar."
|
||||
|
||||
#: pkg/quote.go:614
|
||||
#: pkg/quote.go:669
|
||||
msgctxt "input"
|
||||
msgid "Quotation Date"
|
||||
msgstr "Fecha del presupuesto"
|
||||
|
||||
#: pkg/quote.go:620
|
||||
#: pkg/quote.go:675
|
||||
msgctxt "input"
|
||||
msgid "Terms and conditions"
|
||||
msgstr "Condiciones de aceptación"
|
||||
|
||||
#: pkg/quote.go:625 pkg/invoices.go:652
|
||||
#: pkg/quote.go:680 pkg/invoices.go:718
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notas"
|
||||
|
||||
#: pkg/quote.go:634 pkg/invoices.go:662
|
||||
#: pkg/quote.go:689 pkg/invoices.go:728
|
||||
msgctxt "input"
|
||||
msgid "Payment Method"
|
||||
msgstr "Método de pago"
|
||||
|
||||
#: pkg/quote.go:635
|
||||
#: pkg/quote.go:690
|
||||
msgid "Select a payment method."
|
||||
msgstr "Escoged un método e pago."
|
||||
|
||||
#: pkg/quote.go:671
|
||||
#: pkg/quote.go:726
|
||||
msgid "Selected quotation status is not valid."
|
||||
msgstr "Habéis escogido un estado de presupuesto que no es válido."
|
||||
|
||||
#: pkg/quote.go:673 pkg/invoices.go:699
|
||||
#: pkg/quote.go:728 pkg/invoices.go:783
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "Habéis escogido un cliente que no es válido."
|
||||
|
||||
#: pkg/quote.go:675
|
||||
#: pkg/quote.go:730
|
||||
msgid "Quotation date can not be empty."
|
||||
msgstr "No podéis dejar la fecha del presupuesto en blanco."
|
||||
|
||||
#: pkg/quote.go:676
|
||||
#: pkg/quote.go:731
|
||||
msgid "Quotation date must be a valid date."
|
||||
msgstr "La fecha de presupuesto debe ser válida."
|
||||
|
||||
#: pkg/quote.go:679 pkg/invoices.go:703
|
||||
#: pkg/quote.go:734 pkg/invoices.go:787
|
||||
msgid "Selected payment method is not valid."
|
||||
msgstr "Habéis escogido un método de pago que no es válido."
|
||||
|
||||
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:899 pkg/invoices.go:904
|
||||
#: pkg/quote.go:868 pkg/quote.go:873 pkg/invoices.go:983 pkg/invoices.go:988
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/quote.go:851 pkg/invoices.go:937
|
||||
#: pkg/quote.go:906 pkg/invoices.go:1021
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Cantidad"
|
||||
|
||||
#: pkg/quote.go:860 pkg/invoices.go:946
|
||||
#: pkg/quote.go:915 pkg/invoices.go:1030
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descuento (%)"
|
||||
|
||||
#: pkg/quote.go:914
|
||||
#: pkg/quote.go:969
|
||||
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."
|
||||
|
||||
#: pkg/quote.go:917 pkg/invoices.go:1003
|
||||
#: pkg/quote.go:972 pkg/invoices.go:1087
|
||||
msgid "Product ID must be a positive number or zero."
|
||||
msgstr "El ID de producto tiene que ser un número positivo o cero."
|
||||
|
||||
#: pkg/quote.go:923 pkg/invoices.go:1009
|
||||
#: pkg/quote.go:978 pkg/invoices.go:1093
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podéis dejar la cantidad en blanco."
|
||||
|
||||
#: pkg/quote.go:924 pkg/invoices.go:1010
|
||||
#: pkg/quote.go:979 pkg/invoices.go:1094
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La cantidad tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/quote.go:926 pkg/invoices.go:1012
|
||||
#: pkg/quote.go:981 pkg/invoices.go:1096
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podéis dejar el descuento en blanco."
|
||||
|
||||
#: pkg/quote.go:927 pkg/invoices.go:1013
|
||||
#: pkg/quote.go:982 pkg/invoices.go:1097
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
|
||||
|
||||
|
@ -1191,92 +1198,101 @@ msgctxt "period option"
|
|||
msgid "Previous year"
|
||||
msgstr "Año anterior"
|
||||
|
||||
#: pkg/expenses.go:115
|
||||
#: pkg/expenses.go:147
|
||||
msgid "Select a contact."
|
||||
msgstr "Escoged un contacto"
|
||||
|
||||
#: pkg/expenses.go:150 pkg/expenses.go:326
|
||||
#: pkg/expenses.go:183 pkg/expenses.go:396
|
||||
msgctxt "input"
|
||||
msgid "Contact"
|
||||
msgstr "Contacto"
|
||||
|
||||
#: pkg/expenses.go:156
|
||||
#: pkg/expenses.go:189
|
||||
msgctxt "input"
|
||||
msgid "Invoice number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/expenses.go:161 pkg/invoices.go:646
|
||||
#: pkg/expenses.go:194 pkg/invoices.go:712
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Fecha de factura"
|
||||
|
||||
#: pkg/expenses.go:173
|
||||
#: pkg/expenses.go:206
|
||||
msgctxt "input"
|
||||
msgid "Amount"
|
||||
msgstr "Importe"
|
||||
|
||||
#: pkg/expenses.go:183
|
||||
#: pkg/expenses.go:216 pkg/invoices.go:734
|
||||
msgctxt "input"
|
||||
msgid "File"
|
||||
msgstr "Archivo"
|
||||
|
||||
#: pkg/expenses.go:211
|
||||
#: pkg/expenses.go:222 pkg/expenses.go:421
|
||||
msgctxt "input"
|
||||
msgid "Expense Status"
|
||||
msgstr "Estado del gasto"
|
||||
|
||||
#: pkg/expenses.go:262
|
||||
msgid "Selected contact is not valid."
|
||||
msgstr "Habéis escogido un contacto que no es válido."
|
||||
|
||||
#: pkg/expenses.go:212 pkg/invoices.go:701
|
||||
#: pkg/expenses.go:263 pkg/invoices.go:785
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La fecha de factura debe ser válida."
|
||||
|
||||
#: pkg/expenses.go:215
|
||||
#: pkg/expenses.go:266
|
||||
msgid "Amount can not be empty."
|
||||
msgstr "No podéis dejar el importe en blanco."
|
||||
|
||||
#: pkg/expenses.go:216
|
||||
#: pkg/expenses.go:267
|
||||
msgid "Amount must be a number greater than zero."
|
||||
msgstr "El importe tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/expenses.go:327
|
||||
#: pkg/expenses.go:271
|
||||
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"
|
||||
msgstr "Todos los contactos"
|
||||
|
||||
#: pkg/expenses.go:332 pkg/invoices.go:159
|
||||
#: pkg/expenses.go:402 pkg/invoices.go:159
|
||||
msgctxt "input"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:634
|
||||
#: pkg/invoices.go:153 pkg/invoices.go:700
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estado de la factura"
|
||||
|
||||
#: pkg/invoices.go:482
|
||||
#: pkg/invoices.go:544
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escoged un cliente a facturar."
|
||||
|
||||
#: pkg/invoices.go:583
|
||||
#: pkg/invoices.go:648
|
||||
msgid "invoices.zip"
|
||||
msgstr "facturas.zip"
|
||||
|
||||
#: pkg/invoices.go:698
|
||||
#: pkg/invoices.go:782
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "Habéis escogido un estado de factura que no es válido."
|
||||
|
||||
#: pkg/invoices.go:700
|
||||
#: pkg/invoices.go:784
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podéis dejar la fecha de la factura en blanco."
|
||||
|
||||
#: pkg/invoices.go:836
|
||||
#: pkg/invoices.go:920
|
||||
#, c-format
|
||||
msgid "Re: quotation #%s of %s"
|
||||
msgstr "Ref: presupuesto n.º %s del %s"
|
||||
|
||||
#: pkg/invoices.go:837
|
||||
#: pkg/invoices.go:921
|
||||
msgctxt "to_char"
|
||||
msgid "MM/DD/YYYY"
|
||||
msgstr "DD/MM/YYYY"
|
||||
|
||||
#: pkg/invoices.go:1000
|
||||
#: pkg/invoices.go:1084
|
||||
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."
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:attach_to_invoice from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists numerus.attach_to_invoice(uuid, text, text, bytea);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert numerus:invoice_attachment from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists numerus.invoice_attachment;
|
||||
|
||||
commit;
|
|
@ -122,3 +122,5 @@ 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
|
||||
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
|
||||
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
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
-- 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 invoice’s 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;
|
|
@ -0,0 +1,150 @@
|
|||
-- 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;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify numerus:attach_to_invoice on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('numerus.attach_to_invoice(uuid, text, text, bytea)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,15 @@
|
|||
-- 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;
|
|
@ -17,7 +17,7 @@
|
|||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editInvoicePage*/ -}}
|
||||
<section id="invoice-dialog-content" data-hx-target="main">
|
||||
<h2>{{ printf (pgettext "Edit Invoice “%s”" "title") .Number }}</h2>
|
||||
<form method="POST" action="{{ companyURI "/invoices/" }}{{ .Slug }}/edit"
|
||||
<form enctype="multipart/form-data" method="POST" action="{{ companyURI "/invoices/" }}{{ .Slug }}/edit"
|
||||
data-hx-boost="true"
|
||||
data-hx-swap="innerHTML show:false"
|
||||
>
|
||||
|
@ -50,6 +50,7 @@
|
|||
{{ template "tags-field" .Tags }}
|
||||
{{ template "select-field" .PaymentMethod }}
|
||||
{{ template "select-field" .InvoiceStatus }}
|
||||
{{ template "file-field" .File }}
|
||||
{{ template "input-field" .Notes }}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newInvoicePage*/ -}}
|
||||
<section id="invoice-dialog-content" data-hx-target="main">
|
||||
<h2>{{(pgettext "New Invoice" "title")}}</h2>
|
||||
<form method="POST" action="{{ companyURI "/invoices/new" }}"
|
||||
<form enctype="multipart/form-data" method="POST" action="{{ companyURI "/invoices/new" }}"
|
||||
data-hx-boost="true"
|
||||
data-hx-swap="innerHTML show:false"
|
||||
>
|
||||
|
@ -50,6 +50,7 @@
|
|||
{{ template "input-field" .Date }}
|
||||
{{ template "tags-field" .Tags }}
|
||||
{{ template "select-field" .PaymentMethod }}
|
||||
{{ template "file-field" .File }}
|
||||
{{ template "input-field" .Notes }}
|
||||
</div>
|
||||
{{- range $product := .Products }}
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
<a class="primary button"
|
||||
href="{{ companyURI "/invoices/" }}{{ .Slug }}.pdf"
|
||||
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>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
|
Loading…
Reference in New Issue