Add option to export the list of quotes, invoices, and expenses to ODS

This was requested by a potential user, as they want to be able to do
whatever they want to do to these lists with a spreadsheet.

In fact, they requested to be able to export to CSV, but, as always,
using CSV is a minefield because of Microsoft: since their Excel product
is fucking unable to write and read CSV from different locales, even if
using the same exact Excel product, i can not also create a CSV file
that is guaranteed to work on all locales.  If i used the non-standard
sep=; thing to tell Excel that it is a fucking stupid application, then
proper applications would show that line as a row, which is the correct
albeit undesirable behaviour.

The solution is to use a spreadsheet file format that does not have this
issue.  As far as I know, by default Excel is able to read XLSX and ODS
files, but i refuse to use the artificially complex, not the actually
used in Excel, and lobbied standard that Microsoft somehow convinced ISO
to publish, as i am using a different format because of the mess they
made, and i do not want to bend over in front of them, so ODS it is.

ODS is neither an elegant or good format by any means, but at least i
can write them using simple strings, because there is no ODS library
in Debian and i am not going to write yet another DEB package for an
overengineered package to write a simple table—all i want is to say
“here are these n columns, and these m columns; have a good day!”.

Part of #51.
This commit is contained in:
jordi fita mas 2023-07-18 13:29:36 +02:00
parent 835e52dbcb
commit 0c4ef97dff
10 changed files with 683 additions and 336 deletions

View File

@ -593,6 +593,10 @@ func ServeExpenseAttachment(w http.ResponseWriter, r *http.Request, params httpr
func HandleEditExpenseAction(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
slug := params[0].Value
switch slug {
case "batch":
HandleBatchExpenseAction(w, r, params)
default:
if !ValidUuid(slug) {
http.NotFound(w, r)
return
@ -601,6 +605,7 @@ func HandleEditExpenseAction(w http.ResponseWriter, r *http.Request, params http
handleExpenseAction(w, r, actionUri, func(w http.ResponseWriter, r *http.Request, form *expenseForm) {
mustRenderEditExpenseForm(w, r, slug, form)
})
}
}
func HandleNewExpenseAction(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
@ -646,3 +651,29 @@ func handleExpenseAction(w http.ResponseWriter, r *http.Request, action string,
http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest)
}
}
func HandleBatchExpenseAction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := verifyCsrfTokenValid(r); err != nil {
http.Error(w, err.Error(), http.StatusForbidden)
return
}
locale := getLocale(r)
switch r.Form.Get("action") {
case "export":
conn := getConn(r)
company := getCompany(r)
filters := newExpenseFilterForm(r.Context(), conn, locale, company)
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ods := mustWriteExpensesOds(mustCollectExpenseEntries(r.Context(), conn, locale, filters), locale, company)
writeOdsResponse(w, ods, gettext("expenses.ods", locale))
default:
http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest)
}
}

View File

@ -428,6 +428,10 @@ func (field *ToggleField) FillValue(r *http.Request) {
}
}
func (field *ToggleField) String() string {
return field.Selected
}
type FormValidator struct {
Valid bool
}

View File

@ -648,14 +648,14 @@ func HandleBatchInvoiceAction(w http.ResponseWriter, r *http.Request, _ httprout
http.Error(w, err.Error(), http.StatusForbidden)
return
}
locale := getLocale(r)
switch r.Form.Get("action") {
case "download":
slugs := r.Form["invoice"]
if len(slugs) == 0 {
http.Redirect(w, r, companyURI(mustGetCompany(r), "/invoices"), http.StatusSeeOther)
return
}
locale := getLocale(r)
switch r.Form.Get("action") {
case "download":
invoices := mustWriteInvoicesPdf(r, slugs)
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", gettext("invoices.zip", locale)))
@ -663,6 +663,16 @@ func HandleBatchInvoiceAction(w http.ResponseWriter, r *http.Request, _ httprout
if _, err := w.Write(invoices); err != nil {
panic(err)
}
case "export":
conn := getConn(r)
company := getCompany(r)
filters := newInvoiceFilterForm(r.Context(), conn, locale, company)
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ods := mustWriteInvoicesOds(mustCollectInvoiceEntries(r.Context(), conn, locale, filters), locale, company)
writeOdsResponse(w, ods, gettext("invoices.ods", locale))
default:
http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest)
}

213
pkg/ods.go Normal file
View File

@ -0,0 +1,213 @@
package pkg
import (
"archive/zip"
"bytes"
"encoding/xml"
"fmt"
"net/http"
"strings"
"time"
)
const (
mimetype = "application/vnd.oasis.opendocument.spreadsheet"
metaDashInfManifestXml = `<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest
xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
manifest:version="1.3">
<manifest:file-entry manifest:full-path="/" manifest:version="1.3" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>
<manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>
</manifest:manifest>
`
metaXml = `<?xml version="1.0" encoding="UTF-8"?>
<office:document-meta
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
office:version="1.3">
<office:meta>
<meta:creation-date></meta:creation-date>
<meta:generator>Numerus</meta:generator>
</office:meta>
</office:document-meta>
`
stylesXml = `<?xml version="1.0" encoding="UTF-8"?>
<office:document-styles
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
office:version="1.3">
</office:document-styles>
`
)
func mustWriteInvoicesOds(invoices []*InvoiceEntry, locale *Locale, company *Company) []byte {
columns := []string{
"Date",
"Invoice Num.",
"Customer",
"Status",
"Tags",
"Amount",
}
return mustWriteTableOds(invoices, columns, locale, func(sb *strings.Builder, invoice *InvoiceEntry) {
writeCellDate(sb, invoice.Date)
writeCellString(sb, invoice.Number)
writeCellString(sb, invoice.CustomerName)
writeCellString(sb, invoice.StatusLabel)
writeCellString(sb, strings.Join(invoice.Tags, ","))
writeCellFloat(sb, invoice.Total, locale, company)
})
}
func mustWriteQuotesOds(quotes []*QuoteEntry, locale *Locale, company *Company) []byte {
columns := []string{
"Date",
"Quotation Num.",
"Customer",
"Status",
"Tags",
"Amount",
}
return mustWriteTableOds(quotes, columns, locale, func(sb *strings.Builder, quote *QuoteEntry) {
writeCellDate(sb, quote.Date)
writeCellString(sb, quote.Number)
writeCellString(sb, quote.CustomerName)
writeCellString(sb, quote.StatusLabel)
writeCellString(sb, strings.Join(quote.Tags, ","))
writeCellFloat(sb, quote.Total, locale, company)
})
}
func mustWriteExpensesOds(expenses []*ExpenseEntry, locale *Locale, company *Company) []byte {
columns := []string{
"Contact",
"Invoice Date",
"Invoice Number",
"Status",
"Tags",
"Amount",
}
return mustWriteTableOds(expenses, columns, locale, func(sb *strings.Builder, expense *ExpenseEntry) {
writeCellString(sb, expense.InvoicerName)
writeCellDate(sb, expense.InvoiceDate)
writeCellString(sb, expense.InvoiceNumber)
writeCellString(sb, expense.StatusLabel)
writeCellString(sb, strings.Join(expense.Tags, ","))
writeCellFloat(sb, expense.Amount, locale, company)
})
}
func mustWriteTableOds[K interface{}](rows []*K, columns []string, locale *Locale, writeRow func(*strings.Builder, *K)) []byte {
var sb strings.Builder
sb.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
<office:document-content
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
office:version="1.3">
<office:scripts/>
<office:font-face-decls>
<style:font-face style:name="Liberation Sans" svg:font-family="'Liberation Sans'" style:font-family-generic="swiss" style:font-pitch="variable"/>
</office:font-face-decls>
<office:automatic-styles>
<style:style style:name="co1" style:family="table-column">
<style:table-column-properties fo:break-before="auto" style:column-width="0.889in"/>
</style:style>
<style:style style:name="ro1" style:family="table-row">
<style:table-row-properties style:row-height="0.178in" fo:break-before="auto" style:use-optimal-row-height="true"/>
</style:style>
<style:style style:name="ta1" style:family="table" style:master-page-name="Default">
<style:table-properties table:display="true" style:writing-mode="lr-tb"/>
</style:style>
<number:date-style style:name="N37" number:automatic-order="true">
<number:day number:style="long"/>
<number:text>/</number:text>
<number:month number:style="long"/>
<number:text>/</number:text>
<number:year/>
</number:date-style>
<style:style style:name="ce1" style:family="table-cell" style:parent-style-name="Default" style:data-style-name="N37"/>
</office:automatic-styles>
<office:body>
<office:spreadsheet>
<table:calculation-settings table:automatic-find-labels="false" table:use-regular-expressions="false" table:use-wildcards="true"/>
<table:table table:name="Sheet1" table:style-name="ta1">
`)
sb.WriteString(fmt.Sprintf(" <table:table-column table:style-name=\"co1\" table:number-columns-repeated=\"%d\" table:default-cell-style-name=\"Default\"/>\n", len(columns)))
sb.WriteString(` <table:table-row table:style-name="ro1">
`)
for _, t := range columns {
writeCellString(&sb, locale.GetC(t, "title"))
}
sb.WriteString(" </table:table-row>\n")
for _, row := range rows {
sb.WriteString(" <table:table-row table:style-name=\"ro1\">\n")
writeRow(&sb, row)
sb.WriteString(" </table:table-row>\n")
}
sb.WriteString(` </table:table>
<table:named-expressions/>
</office:spreadsheet>
</office:body>
</office:document-content>
`)
return mustWriteOds(sb.String())
}
func mustWriteOds(content string) []byte {
buf := new(bytes.Buffer)
ods := zip.NewWriter(buf)
mustWriteOdsFile(ods, "mimetype", mimetype, zip.Store)
mustWriteOdsFile(ods, "META-INF/manifest.xml", metaDashInfManifestXml, zip.Deflate)
mustWriteOdsFile(ods, "meta.xml", metaXml, zip.Deflate)
mustWriteOdsFile(ods, "styles.xml", stylesXml, zip.Deflate)
mustWriteOdsFile(ods, "content.xml", content, zip.Deflate)
mustClose(ods)
return buf.Bytes()
}
func mustWriteOdsFile(ods *zip.Writer, name string, content string, method uint16) {
f, err := ods.CreateHeader(&zip.FileHeader{
Name: name,
Method: method,
Modified: time.Now(),
})
if err != nil {
panic(err)
}
if _, err = f.Write([]byte(content)); err != nil {
panic(err)
}
}
func writeCellString(sb *strings.Builder, s string) {
sb.WriteString(` <table:table-cell office:value-type="string" calcext:value-type="string"><text:p>`)
if err := xml.EscapeText(sb, []byte(s)); err != nil {
panic(err)
}
sb.WriteString("</text:p></table:table-cell>\n")
}
func writeCellDate(sb *strings.Builder, t time.Time) {
sb.WriteString(fmt.Sprintf(" <table:table-cell table:style-name=\"ce1\" office:value-type=\"date\" office:date-value=\"%s\" calcext:value-type=\"date\"><text:p>%s</text:p></table:table-cell>\n", t.Format("2006-01-02"), t.Format("02/01/06")))
}
func writeCellFloat(sb *strings.Builder, s string, locale *Locale, company *Company) {
sb.WriteString(fmt.Sprintf(" <table:table-cell office:value-type=\"float\" office:value=\"%s\" calcext:value-type=\"float\"><text:p>%s</text:p></table:table-cell>\n", s, formatPrice(s, locale.Language, "%.[1]*[2]f", company.DecimalDigits, "")))
}
func writeOdsResponse(w http.ResponseWriter, ods []byte, filename string) {
w.Header().Set("Content-Type", "application/vnd.oasis.opendocument.spreadsheet")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
w.WriteHeader(http.StatusOK)
if _, err := w.Write(ods); err != nil {
panic(err)
}
}

View File

@ -605,14 +605,14 @@ func HandleBatchQuoteAction(w http.ResponseWriter, r *http.Request, _ httprouter
http.Error(w, err.Error(), http.StatusForbidden)
return
}
locale := getLocale(r)
switch r.Form.Get("action") {
case "download":
slugs := r.Form["quote"]
if len(slugs) == 0 {
http.Redirect(w, r, companyURI(mustGetCompany(r), "/quotes"), http.StatusSeeOther)
return
}
locale := getLocale(r)
switch r.Form.Get("action") {
case "download":
quotes := mustWriteQuotesPdf(r, slugs)
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", gettext("quotations.zip", locale)))
@ -620,6 +620,16 @@ func HandleBatchQuoteAction(w http.ResponseWriter, r *http.Request, _ httprouter
if _, err := w.Write(quotes); err != nil {
panic(err)
}
case "export":
conn := getConn(r)
company := getCompany(r)
filters := newQuoteFilterForm(r.Context(), conn, locale, company)
if err := filters.Parse(r); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ods := mustWriteQuotesOds(mustCollectQuoteEntries(r.Context(), conn, locale, filters), locale, company)
writeOdsResponse(w, ods, gettext("quotations.ods", locale))
default:
http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest)
}

331
po/ca.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-07-16 20:50+0200\n"
"POT-Creation-Date: 2023-07-18 13:12+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"
@ -126,117 +126,123 @@ msgctxt "action"
msgid "Save"
msgstr "Desa"
#: web/template/invoices/index.gohtml:19
#: web/template/invoices/index.gohtml:28
msgctxt "action"
msgid "Download invoices"
msgstr "Descarrega factures"
#: web/template/invoices/index.gohtml:21
#: web/template/invoices/index.gohtml:31 web/template/quotes/index.gohtml:31
#: web/template/expenses/index.gohtml:29
msgctxt "action"
msgid "Export list"
msgstr "Exporta llistat"
#: web/template/invoices/index.gohtml:33
msgctxt "action"
msgid "New invoice"
msgstr "Nova factura"
#: web/template/invoices/index.gohtml:45 web/template/dashboard.gohtml:23
#: web/template/quotes/index.gohtml:45 web/template/contacts/index.gohtml:38
#: web/template/expenses/index.gohtml:39 web/template/products/index.gohtml:36
#: web/template/invoices/index.gohtml:57 web/template/dashboard.gohtml:23
#: web/template/quotes/index.gohtml:57 web/template/contacts/index.gohtml:38
#: web/template/expenses/index.gohtml:56 web/template/products/index.gohtml:36
msgctxt "action"
msgid "Filter"
msgstr "Filtra"
#: web/template/invoices/index.gohtml:48 web/template/quotes/index.gohtml:48
#: web/template/contacts/index.gohtml:41 web/template/expenses/index.gohtml:42
#: web/template/invoices/index.gohtml:60 web/template/quotes/index.gohtml:60
#: web/template/contacts/index.gohtml:41 web/template/expenses/index.gohtml:59
#: web/template/products/index.gohtml:39
msgctxt "action"
msgid "Reset"
msgstr "Restableix"
#: web/template/invoices/index.gohtml:54
#: web/template/invoices/index.gohtml:66
msgctxt "invoice"
msgid "All"
msgstr "Totes"
#: web/template/invoices/index.gohtml:55 web/template/invoices/view.gohtml:38
#: web/template/quotes/index.gohtml:55 web/template/quotes/view.gohtml:37
#: web/template/invoices/index.gohtml:67 web/template/invoices/view.gohtml:38
#: web/template/quotes/index.gohtml:67 web/template/quotes/view.gohtml:37
msgctxt "title"
msgid "Date"
msgstr "Data"
#: web/template/invoices/index.gohtml:56
#: web/template/invoices/index.gohtml:68
msgctxt "title"
msgid "Invoice Num."
msgstr "Núm. factura"
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
#: web/template/invoices/index.gohtml:69 web/template/quotes/index.gohtml:69
msgctxt "title"
msgid "Customer"
msgstr "Client"
#: web/template/invoices/index.gohtml:58 web/template/quotes/index.gohtml:58
#: web/template/expenses/index.gohtml:51
#: web/template/invoices/index.gohtml:70 web/template/quotes/index.gohtml:70
#: web/template/expenses/index.gohtml:68
msgctxt "title"
msgid "Status"
msgstr "Estat"
#: web/template/invoices/index.gohtml:59 web/template/quotes/index.gohtml:59
#: web/template/contacts/index.gohtml:50 web/template/expenses/index.gohtml:52
#: web/template/invoices/index.gohtml:71 web/template/quotes/index.gohtml:71
#: web/template/contacts/index.gohtml:50 web/template/expenses/index.gohtml:69
#: web/template/products/index.gohtml:46
msgctxt "title"
msgid "Tags"
msgstr "Etiquetes"
#: web/template/invoices/index.gohtml:60 web/template/quotes/index.gohtml:60
#: web/template/expenses/index.gohtml:53
#: web/template/invoices/index.gohtml:72 web/template/quotes/index.gohtml:72
#: web/template/expenses/index.gohtml:70
msgctxt "title"
msgid "Amount"
msgstr "Import"
#: web/template/invoices/index.gohtml:61 web/template/quotes/index.gohtml:61
#: web/template/expenses/index.gohtml:54
#: web/template/invoices/index.gohtml:73 web/template/quotes/index.gohtml:73
#: web/template/expenses/index.gohtml:71
msgctxt "title"
msgid "Download"
msgstr "Descàrrega"
#: web/template/invoices/index.gohtml:62 web/template/quotes/index.gohtml:62
#: web/template/contacts/index.gohtml:51 web/template/expenses/index.gohtml:55
#: web/template/invoices/index.gohtml:74 web/template/quotes/index.gohtml:74
#: web/template/contacts/index.gohtml:51 web/template/expenses/index.gohtml:72
#: web/template/products/index.gohtml:48
msgctxt "title"
msgid "Actions"
msgstr "Accions"
#: web/template/invoices/index.gohtml:69
#: web/template/invoices/index.gohtml:81
msgctxt "action"
msgid "Select invoice %v"
msgstr "Selecciona factura %v"
#: web/template/invoices/index.gohtml:110
#: web/template/invoices/index.gohtml:122
msgctxt "action"
msgid "Download invoice %s"
msgstr "Descarrega factura %s"
#: web/template/invoices/index.gohtml:118
#: web/template/invoices/index.gohtml:130
msgid "Actions for invoice %s"
msgstr "Accions per la factura %s"
#: web/template/invoices/index.gohtml:126 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:125 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:117
#: web/template/invoices/index.gohtml:138 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:137 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:134
#: web/template/products/index.gohtml:78
msgctxt "action"
msgid "Edit"
msgstr "Edita"
#: web/template/invoices/index.gohtml:134 web/template/invoices/view.gohtml:16
#: web/template/quotes/index.gohtml:133 web/template/quotes/view.gohtml:19
#: web/template/invoices/index.gohtml:146 web/template/invoices/view.gohtml:16
#: web/template/quotes/index.gohtml:145 web/template/quotes/view.gohtml:19
msgctxt "action"
msgid "Duplicate"
msgstr "Duplica"
#: web/template/invoices/index.gohtml:144
#: web/template/invoices/index.gohtml:156
msgid "No invoices added yet."
msgstr "No hi ha cap factura."
#: web/template/invoices/index.gohtml:151 web/template/quotes/index.gohtml:158
#: web/template/expenses/index.gohtml:134
#: web/template/invoices/index.gohtml:163 web/template/quotes/index.gohtml:170
#: web/template/expenses/index.gohtml:151
msgid "Total"
msgstr "Total"
@ -363,41 +369,41 @@ msgctxt "title"
msgid "New Quotation"
msgstr "Nou pressupost"
#: web/template/quotes/index.gohtml:19
#: web/template/quotes/index.gohtml:28
msgctxt "action"
msgid "Download quotations"
msgstr "Descarrega pressuposts"
#: web/template/quotes/index.gohtml:21
#: web/template/quotes/index.gohtml:33
msgctxt "action"
msgid "New quotation"
msgstr "Nou pressupost"
#: web/template/quotes/index.gohtml:54
#: web/template/quotes/index.gohtml:66
msgctxt "quote"
msgid "All"
msgstr "Tots"
#: web/template/quotes/index.gohtml:56
#: web/template/quotes/index.gohtml:68
msgctxt "title"
msgid "Quotation Num."
msgstr "Núm. pressupost"
#: web/template/quotes/index.gohtml:69
#: web/template/quotes/index.gohtml:81
msgctxt "action"
msgid "Select quotation %v"
msgstr "Selecciona pressupost %v"
#: web/template/quotes/index.gohtml:117
#: web/template/quotes/index.gohtml:129
msgid "Actions for quote %s"
msgstr "Accions pel pressupost %s"
#: web/template/quotes/index.gohtml:141 web/template/quotes/view.gohtml:16
#: web/template/quotes/index.gohtml:153 web/template/quotes/view.gohtml:16
msgctxt "action"
msgid "Create invoice"
msgstr "Crea factura"
#: web/template/quotes/index.gohtml:151
#: web/template/quotes/index.gohtml:163
msgid "No quotations added yet."
msgstr "No hi ha cap pressupost."
@ -488,7 +494,7 @@ msgctxt "action"
msgid "New contact"
msgstr "Nou contacte"
#: web/template/contacts/index.gohtml:47 web/template/expenses/index.gohtml:48
#: web/template/contacts/index.gohtml:47 web/template/expenses/index.gohtml:65
msgctxt "title"
msgid "Contact"
msgstr "Contacte"
@ -569,26 +575,26 @@ msgctxt "title"
msgid "Expenses"
msgstr "Despeses"
#: web/template/expenses/index.gohtml:16
#: web/template/expenses/index.gohtml:32
msgctxt "action"
msgid "New expense"
msgstr "Nova despesa"
#: web/template/expenses/index.gohtml:49
#: web/template/expenses/index.gohtml:66
msgctxt "title"
msgid "Invoice Date"
msgstr "Data de factura"
#: web/template/expenses/index.gohtml:50
#: web/template/expenses/index.gohtml:67
msgctxt "title"
msgid "Invoice Number"
msgstr "Número de factura"
#: web/template/expenses/index.gohtml:109
#: web/template/expenses/index.gohtml:126
msgid "Actions for expense %s"
msgstr "Accions per la despesa %s"
#: web/template/expenses/index.gohtml:127
#: web/template/expenses/index.gohtml:144
msgid "No expenses added yet."
msgstr "No hi ha cap despesa."
@ -695,7 +701,7 @@ msgctxt "title"
msgid "Edit Product “%s”"
msgstr "Edició del producte «%s»"
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:267
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:276
msgctxt "input"
msgid "Email"
msgstr "Correu-e"
@ -709,7 +715,7 @@ msgstr "Contrasenya"
msgid "Email can not be empty."
msgstr "No podeu deixar el correu-e en blanc."
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:411
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:420
msgid "This value is not a valid email. It should be like name@domain.com."
msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
@ -721,84 +727,84 @@ msgstr "No podeu deixar la contrasenya en blanc."
msgid "Invalid user or password."
msgstr "Nom dusuari o contrasenya incorrectes."
#: pkg/products.go:164 pkg/products.go:268 pkg/quote.go:887
#: pkg/invoices.go:1002 pkg/contacts.go:140 pkg/contacts.go:253
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901
#: pkg/invoices.go:1016 pkg/contacts.go:149 pkg/contacts.go:262
msgctxt "input"
msgid "Name"
msgstr "Nom"
#: pkg/products.go:169 pkg/products.go:295 pkg/quote.go:174 pkg/quote.go:694
#: pkg/expenses.go:274 pkg/expenses.go:433 pkg/invoices.go:174
#: pkg/invoices.go:732 pkg/invoices.go:1304 pkg/contacts.go:145
#: pkg/contacts.go:353
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
#: pkg/expenses.go:278 pkg/expenses.go:442 pkg/invoices.go:174
#: pkg/invoices.go:746 pkg/invoices.go:1331 pkg/contacts.go:154
#: pkg/contacts.go:362
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:443 pkg/invoices.go:178
#: pkg/contacts.go:149
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:452 pkg/invoices.go:178
#: pkg/contacts.go:158
msgctxt "input"
msgid "Tags Condition"
msgstr "Condició de les etiquetes"
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:447 pkg/invoices.go:182
#: pkg/contacts.go:153
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:456 pkg/invoices.go:182
#: pkg/contacts.go:162
msgctxt "tag condition"
msgid "All"
msgstr "Totes"
#: pkg/products.go:178 pkg/expenses.go:448 pkg/invoices.go:183
#: pkg/contacts.go:154
#: pkg/products.go:186 pkg/expenses.go:457 pkg/invoices.go:183
#: pkg/contacts.go:163
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:452 pkg/invoices.go:187
#: pkg/contacts.go:158
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:461 pkg/invoices.go:187
#: pkg/contacts.go:167
msgctxt "tag condition"
msgid "Any"
msgstr "Qualsevol"
#: pkg/products.go:183 pkg/expenses.go:453 pkg/invoices.go:188
#: pkg/contacts.go:159
#: pkg/products.go:191 pkg/expenses.go:462 pkg/invoices.go:188
#: pkg/contacts.go:168
msgid "Invoices must have at least one of the specified labels."
msgstr "Les factures han de tenir com a mínim una de les etiquetes."
#: pkg/products.go:274 pkg/quote.go:901 pkg/invoices.go:1016
#: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1030
msgctxt "input"
msgid "Description"
msgstr "Descripció"
#: pkg/products.go:279 pkg/quote.go:905 pkg/invoices.go:1020
#: pkg/products.go:287 pkg/quote.go:919 pkg/invoices.go:1034
msgctxt "input"
msgid "Price"
msgstr "Preu"
#: pkg/products.go:289 pkg/quote.go:934 pkg/expenses.go:242
#: pkg/invoices.go:1049
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:246
#: pkg/invoices.go:1063
msgctxt "input"
msgid "Taxes"
msgstr "Imposts"
#: pkg/products.go:314 pkg/quote.go:983 pkg/profile.go:92 pkg/invoices.go:1098
#: pkg/contacts.go:403
#: pkg/products.go:322 pkg/quote.go:997 pkg/profile.go:92 pkg/invoices.go:1112
#: pkg/contacts.go:412
msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc."
#: pkg/products.go:315 pkg/quote.go:984 pkg/invoices.go:1099
#: pkg/products.go:323 pkg/quote.go:998 pkg/invoices.go:1113
msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc."
#: pkg/products.go:316 pkg/quote.go:985 pkg/invoices.go:1100
#: pkg/products.go:324 pkg/quote.go:999 pkg/invoices.go:1114
msgid "Price must be a number greater than zero."
msgstr "El preu ha de ser un número major a zero."
#: pkg/products.go:318 pkg/quote.go:993 pkg/expenses.go:310
#: pkg/invoices.go:1108
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:314
#: pkg/invoices.go:1122
msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/products.go:319 pkg/quote.go:994 pkg/expenses.go:311
#: pkg/invoices.go:1109
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:315
#: pkg/invoices.go:1123
msgid "You can only select a tax of each class."
msgstr "Només podeu seleccionar un impost de cada classe."
@ -807,47 +813,47 @@ msgctxt "input"
msgid "Trade name"
msgstr "Nom comercial"
#: pkg/company.go:118 pkg/contacts.go:259
#: pkg/company.go:118 pkg/contacts.go:268
msgctxt "input"
msgid "Phone"
msgstr "Telèfon"
#: pkg/company.go:136 pkg/contacts.go:275
#: pkg/company.go:136 pkg/contacts.go:284
msgctxt "input"
msgid "Web"
msgstr "Web"
#: pkg/company.go:144 pkg/contacts.go:287
#: pkg/company.go:144 pkg/contacts.go:296
msgctxt "input"
msgid "Business name"
msgstr "Nom i cognoms"
#: pkg/company.go:154 pkg/contacts.go:297
#: pkg/company.go:154 pkg/contacts.go:306
msgctxt "input"
msgid "VAT number"
msgstr "DNI / NIF"
#: pkg/company.go:160 pkg/contacts.go:303
#: pkg/company.go:160 pkg/contacts.go:312
msgctxt "input"
msgid "Address"
msgstr "Adreça"
#: pkg/company.go:169 pkg/contacts.go:312
#: pkg/company.go:169 pkg/contacts.go:321
msgctxt "input"
msgid "City"
msgstr "Població"
#: pkg/company.go:175 pkg/contacts.go:318
#: pkg/company.go:175 pkg/contacts.go:327
msgctxt "input"
msgid "Province"
msgstr "Província"
#: pkg/company.go:181 pkg/contacts.go:324
#: pkg/company.go:181 pkg/contacts.go:333
msgctxt "input"
msgid "Postal code"
msgstr "Codi postal"
#: pkg/company.go:190 pkg/contacts.go:333
#: pkg/company.go:190 pkg/contacts.go:342
msgctxt "input"
msgid "Country"
msgstr "País"
@ -882,23 +888,23 @@ msgctxt "input"
msgid "Legal disclaimer"
msgstr "Nota legal"
#: pkg/company.go:271 pkg/contacts.go:385
#: pkg/company.go:271 pkg/contacts.go:394
msgid "Selected country is not valid."
msgstr "Heu seleccionat un país que no és vàlid."
#: pkg/company.go:275 pkg/contacts.go:388
#: pkg/company.go:275 pkg/contacts.go:397
msgid "Business name can not be empty."
msgstr "No podeu deixar el nom i els cognoms en blanc."
#: pkg/company.go:276 pkg/contacts.go:389
#: pkg/company.go:276 pkg/contacts.go:398
msgid "Business name must have at least two letters."
msgstr "Nom i cognoms han de tenir com a mínim dues lletres."
#: pkg/company.go:277 pkg/contacts.go:391
#: pkg/company.go:277 pkg/contacts.go:400
msgid "VAT number can not be empty."
msgstr "No podeu deixar el DNI o NIF en blanc."
#: pkg/company.go:278 pkg/contacts.go:392
#: pkg/company.go:278 pkg/contacts.go:401
msgid "This value is not a valid VAT number."
msgstr "Aquest valor no és un DNI o NIF vàlid."
@ -906,31 +912,31 @@ msgstr "Aquest valor no és un DNI o NIF vàlid."
msgid "Phone can not be empty."
msgstr "No podeu deixar el telèfon en blanc."
#: pkg/company.go:281 pkg/contacts.go:408
#: pkg/company.go:281 pkg/contacts.go:417
msgid "This value is not a valid phone number."
msgstr "Aquest valor no és un telèfon vàlid."
#: pkg/company.go:287 pkg/contacts.go:414
#: pkg/company.go:287 pkg/contacts.go:423
msgid "This value is not a valid web address. It should be like https://domain.com/."
msgstr "Aquest valor no és una adreça web vàlida. Hauria de ser similar a https://domini.cat/."
#: pkg/company.go:289 pkg/contacts.go:394
#: pkg/company.go:289 pkg/contacts.go:403
msgid "Address can not be empty."
msgstr "No podeu deixar ladreça en blanc."
#: pkg/company.go:290 pkg/contacts.go:395
#: pkg/company.go:290 pkg/contacts.go:404
msgid "City can not be empty."
msgstr "No podeu deixar la població en blanc."
#: pkg/company.go:291 pkg/contacts.go:396
#: pkg/company.go:291 pkg/contacts.go:405
msgid "Province can not be empty."
msgstr "No podeu deixar la província en blanc."
#: pkg/company.go:292 pkg/contacts.go:398
#: pkg/company.go:292 pkg/contacts.go:407
msgid "Postal code can not be empty."
msgstr "No podeu deixar el codi postal en blanc."
#: pkg/company.go:293 pkg/contacts.go:399
#: pkg/company.go:293 pkg/contacts.go:408
msgid "This value is not a valid postal code."
msgstr "Aquest valor no és un codi postal vàlid."
@ -1007,7 +1013,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:672 pkg/invoices.go:147 pkg/invoices.go:715
#: pkg/quote.go:147 pkg/quote.go:686 pkg/invoices.go:147 pkg/invoices.go:729
msgctxt "input"
msgid "Customer"
msgstr "Client"
@ -1016,12 +1022,12 @@ msgstr "Client"
msgid "All customers"
msgstr "Tots els clients"
#: pkg/quote.go:153 pkg/quote.go:666
#: pkg/quote.go:153 pkg/quote.go:680
msgctxt "input"
msgid "Quotation Status"
msgstr "Estat del pressupost"
#: pkg/quote.go:154 pkg/expenses.go:438 pkg/invoices.go:154
#: pkg/quote.go:154 pkg/expenses.go:447 pkg/invoices.go:154
msgid "All status"
msgstr "Tots els estats"
@ -1030,12 +1036,12 @@ msgctxt "input"
msgid "Quotation Number"
msgstr "Número de pressupost"
#: pkg/quote.go:164 pkg/expenses.go:423 pkg/invoices.go:164
#: pkg/quote.go:164 pkg/expenses.go:432 pkg/invoices.go:164
msgctxt "input"
msgid "From Date"
msgstr "A partir de la data"
#: pkg/quote.go:169 pkg/expenses.go:428 pkg/invoices.go:169
#: pkg/quote.go:169 pkg/expenses.go:437 pkg/invoices.go:169
msgctxt "input"
msgid "To Date"
msgstr "Fins la data"
@ -1048,99 +1054,104 @@ 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:614
#: pkg/quote.go:618
msgid "quotations.zip"
msgstr "pressuposts.zip"
#: pkg/quote.go:620 pkg/quote.go:1149 pkg/quote.go:1157 pkg/expenses.go:620
#: pkg/invoices.go:663 pkg/invoices.go:1279 pkg/invoices.go:1287
#: pkg/quote.go:632
msgid "quotations.ods"
msgstr "pressuposts.ods"
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:651
#: pkg/expenses.go:677 pkg/invoices.go:677 pkg/invoices.go:1306
#: pkg/invoices.go:1314
msgid "Invalid action"
msgstr "Acció invàlida."
#: pkg/quote.go:673
#: pkg/quote.go:687
msgid "Select a customer to quote."
msgstr "Escolliu un client a pressupostar."
#: pkg/quote.go:678
#: pkg/quote.go:692
msgctxt "input"
msgid "Quotation Date"
msgstr "Data del pressupost"
#: pkg/quote.go:684
#: pkg/quote.go:698
msgctxt "input"
msgid "Terms and conditions"
msgstr "Condicions dacceptació"
#: pkg/quote.go:689 pkg/invoices.go:727
#: pkg/quote.go:703 pkg/invoices.go:741
msgctxt "input"
msgid "Notes"
msgstr "Notes"
#: pkg/quote.go:698 pkg/invoices.go:737
#: pkg/quote.go:712 pkg/invoices.go:751
msgctxt "input"
msgid "Payment Method"
msgstr "Mètode de pagament"
#: pkg/quote.go:699
#: pkg/quote.go:713
msgid "Select a payment method."
msgstr "Escolliu un mètode de pagament."
#: pkg/quote.go:735
#: pkg/quote.go:749
msgid "Selected quotation status is not valid."
msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
#: pkg/quote.go:737 pkg/invoices.go:792
#: pkg/quote.go:751 pkg/invoices.go:806
msgid "Selected customer is not valid."
msgstr "Heu seleccionat un client que no és vàlid."
#: pkg/quote.go:739
#: pkg/quote.go:753
msgid "Quotation date can not be empty."
msgstr "No podeu deixar la data del pressupost en blanc."
#: pkg/quote.go:740
#: pkg/quote.go:754
msgid "Quotation date must be a valid date."
msgstr "La data del pressupost ha de ser vàlida."
#: pkg/quote.go:743 pkg/invoices.go:796
#: pkg/quote.go:757 pkg/invoices.go:810
msgid "Selected payment method is not valid."
msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
#: pkg/quote.go:877 pkg/quote.go:882 pkg/invoices.go:992 pkg/invoices.go:997
#: pkg/quote.go:891 pkg/quote.go:896 pkg/invoices.go:1006 pkg/invoices.go:1011
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/quote.go:915 pkg/invoices.go:1030
#: pkg/quote.go:929 pkg/invoices.go:1044
msgctxt "input"
msgid "Quantity"
msgstr "Quantitat"
#: pkg/quote.go:924 pkg/invoices.go:1039
#: pkg/quote.go:938 pkg/invoices.go:1053
msgctxt "input"
msgid "Discount (%)"
msgstr "Descompte (%)"
#: pkg/quote.go:978
#: pkg/quote.go:992
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."
#: pkg/quote.go:981 pkg/invoices.go:1096
#: pkg/quote.go:995 pkg/invoices.go:1110
msgid "Product ID must be a positive number or zero."
msgstr "LID del producte ha de ser un número positiu o zero."
#: pkg/quote.go:987 pkg/invoices.go:1102
#: pkg/quote.go:1001 pkg/invoices.go:1116
msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc."
#: pkg/quote.go:988 pkg/invoices.go:1103
#: pkg/quote.go:1002 pkg/invoices.go:1117
msgid "Quantity must be a number greater than zero."
msgstr "La quantitat ha de ser un número major a zero."
#: pkg/quote.go:990 pkg/invoices.go:1105
#: pkg/quote.go:1004 pkg/invoices.go:1119
msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc."
#: pkg/quote.go:991 pkg/invoices.go:1106
#: pkg/quote.go:1005 pkg/invoices.go:1120
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
@ -1207,132 +1218,140 @@ msgctxt "period option"
msgid "Previous year"
msgstr "Any anterior"
#: pkg/expenses.go:168
#: pkg/expenses.go:172
msgid "Select a contact."
msgstr "Escolliu un contacte."
#: pkg/expenses.go:225 pkg/expenses.go:412
#: pkg/expenses.go:229 pkg/expenses.go:421
msgctxt "input"
msgid "Contact"
msgstr "Contacte"
#: pkg/expenses.go:231
#: pkg/expenses.go:235
msgctxt "input"
msgid "Invoice number"
msgstr "Número de factura"
#: pkg/expenses.go:236 pkg/invoices.go:721
#: pkg/expenses.go:240 pkg/invoices.go:735
msgctxt "input"
msgid "Invoice Date"
msgstr "Data de factura"
#: pkg/expenses.go:251
#: pkg/expenses.go:255
msgctxt "input"
msgid "Amount"
msgstr "Import"
#: pkg/expenses.go:262 pkg/invoices.go:743
#: pkg/expenses.go:266 pkg/invoices.go:757
msgctxt "input"
msgid "File"
msgstr "Fitxer"
#: pkg/expenses.go:268 pkg/expenses.go:437
#: pkg/expenses.go:272 pkg/expenses.go:446
msgctxt "input"
msgid "Expense Status"
msgstr "Estat de la despesa"
#: pkg/expenses.go:308
#: pkg/expenses.go:312
msgid "Selected contact is not valid."
msgstr "Heu seleccionat un contacte que no és vàlid."
#: pkg/expenses.go:309 pkg/invoices.go:794
#: pkg/expenses.go:313 pkg/invoices.go:808
msgid "Invoice date must be a valid date."
msgstr "La data de facturació ha de ser vàlida."
#: pkg/expenses.go:312
#: pkg/expenses.go:316
msgid "Amount can not be empty."
msgstr "No podeu deixar limport en blanc."
#: pkg/expenses.go:313
#: pkg/expenses.go:317
msgid "Amount must be a number greater than zero."
msgstr "Limport ha de ser un número major a zero."
#: pkg/expenses.go:315
#: pkg/expenses.go:319
msgid "Selected expense status is not valid."
msgstr "Heu seleccionat un estat de despesa que no és vàlid."
#: pkg/expenses.go:413
#: pkg/expenses.go:422
msgid "All contacts"
msgstr "Tots els contactes"
#: pkg/expenses.go:418 pkg/invoices.go:159
#: pkg/expenses.go:427 pkg/invoices.go:159
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/invoices.go:153 pkg/invoices.go:709
#: pkg/expenses.go:675
msgid "expenses.ods"
msgstr "despeses.ods"
#: pkg/invoices.go:153 pkg/invoices.go:723
msgctxt "input"
msgid "Invoice Status"
msgstr "Estat de la factura"
#: pkg/invoices.go:553
#: pkg/invoices.go:557
msgid "Select a customer to bill."
msgstr "Escolliu un client a facturar."
#: pkg/invoices.go:657
#: pkg/invoices.go:661
msgid "invoices.zip"
msgstr "factures.zip"
#: pkg/invoices.go:791
#: pkg/invoices.go:675
msgid "invoices.ods"
msgstr "factures.ods"
#: pkg/invoices.go:805
msgid "Selected invoice status is not valid."
msgstr "Heu seleccionat un estat de factura que no és vàlid."
#: pkg/invoices.go:793
#: pkg/invoices.go:807
msgid "Invoice date can not be empty."
msgstr "No podeu deixar la data de la factura en blanc."
#: pkg/invoices.go:929
#: pkg/invoices.go:943
#, c-format
msgid "Re: quotation #%s of %s"
msgstr "Ref: pressupost núm. %s del %s"
#: pkg/invoices.go:930
#: pkg/invoices.go:944
msgctxt "to_char"
msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY"
#: pkg/invoices.go:1093
#: pkg/invoices.go:1107
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."
#: pkg/contacts.go:283
#: pkg/contacts.go:292
msgctxt "input"
msgid "Need to input tax details"
msgstr "Necessito poder facturar aquest contacte"
#: pkg/contacts.go:343
#: pkg/contacts.go:352
msgctxt "input"
msgid "IBAN"
msgstr "IBAN"
#: pkg/contacts.go:348
#: pkg/contacts.go:357
msgctxt "bic"
msgid "BIC"
msgstr "BIC"
#: pkg/contacts.go:404
#: pkg/contacts.go:413
msgid "Name must have at least two letters."
msgstr "El nom ha de tenir com a mínim dues lletres."
#: pkg/contacts.go:417
#: pkg/contacts.go:426
msgid "This values is not a valid IBAN."
msgstr "Aquest valor no és un IBAN vàlid."
#: pkg/contacts.go:420
#: pkg/contacts.go:429
msgid "This values is not a valid BIC."
msgstr "Aquest valor no és un BIC vàlid."
#: pkg/contacts.go:534
#: pkg/contacts.go:551
msgctxt "input"
msgid "Holded Excel file"
msgstr "Fitxer Excel del Holded"

331
po/es.po
View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-07-16 20:50+0200\n"
"POT-Creation-Date: 2023-07-18 13:12+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"
@ -126,117 +126,123 @@ msgctxt "action"
msgid "Save"
msgstr "Guardad"
#: web/template/invoices/index.gohtml:19
#: web/template/invoices/index.gohtml:28
msgctxt "action"
msgid "Download invoices"
msgstr "Descargar facturas"
#: web/template/invoices/index.gohtml:21
#: web/template/invoices/index.gohtml:31 web/template/quotes/index.gohtml:31
#: web/template/expenses/index.gohtml:29
msgctxt "action"
msgid "Export list"
msgstr "Exportar listado"
#: web/template/invoices/index.gohtml:33
msgctxt "action"
msgid "New invoice"
msgstr "Nueva factura"
#: web/template/invoices/index.gohtml:45 web/template/dashboard.gohtml:23
#: web/template/quotes/index.gohtml:45 web/template/contacts/index.gohtml:38
#: web/template/expenses/index.gohtml:39 web/template/products/index.gohtml:36
#: web/template/invoices/index.gohtml:57 web/template/dashboard.gohtml:23
#: web/template/quotes/index.gohtml:57 web/template/contacts/index.gohtml:38
#: web/template/expenses/index.gohtml:56 web/template/products/index.gohtml:36
msgctxt "action"
msgid "Filter"
msgstr "Filtrar"
#: web/template/invoices/index.gohtml:48 web/template/quotes/index.gohtml:48
#: web/template/contacts/index.gohtml:41 web/template/expenses/index.gohtml:42
#: web/template/invoices/index.gohtml:60 web/template/quotes/index.gohtml:60
#: web/template/contacts/index.gohtml:41 web/template/expenses/index.gohtml:59
#: web/template/products/index.gohtml:39
msgctxt "action"
msgid "Reset"
msgstr "Restablecer"
#: web/template/invoices/index.gohtml:54
#: web/template/invoices/index.gohtml:66
msgctxt "invoice"
msgid "All"
msgstr "Todas"
#: web/template/invoices/index.gohtml:55 web/template/invoices/view.gohtml:38
#: web/template/quotes/index.gohtml:55 web/template/quotes/view.gohtml:37
#: web/template/invoices/index.gohtml:67 web/template/invoices/view.gohtml:38
#: web/template/quotes/index.gohtml:67 web/template/quotes/view.gohtml:37
msgctxt "title"
msgid "Date"
msgstr "Fecha"
#: web/template/invoices/index.gohtml:56
#: web/template/invoices/index.gohtml:68
msgctxt "title"
msgid "Invoice Num."
msgstr "N.º factura"
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
#: web/template/invoices/index.gohtml:69 web/template/quotes/index.gohtml:69
msgctxt "title"
msgid "Customer"
msgstr "Cliente"
#: web/template/invoices/index.gohtml:58 web/template/quotes/index.gohtml:58
#: web/template/expenses/index.gohtml:51
#: web/template/invoices/index.gohtml:70 web/template/quotes/index.gohtml:70
#: web/template/expenses/index.gohtml:68
msgctxt "title"
msgid "Status"
msgstr "Estado"
#: web/template/invoices/index.gohtml:59 web/template/quotes/index.gohtml:59
#: web/template/contacts/index.gohtml:50 web/template/expenses/index.gohtml:52
#: web/template/invoices/index.gohtml:71 web/template/quotes/index.gohtml:71
#: web/template/contacts/index.gohtml:50 web/template/expenses/index.gohtml:69
#: web/template/products/index.gohtml:46
msgctxt "title"
msgid "Tags"
msgstr "Etiquetes"
#: web/template/invoices/index.gohtml:60 web/template/quotes/index.gohtml:60
#: web/template/expenses/index.gohtml:53
#: web/template/invoices/index.gohtml:72 web/template/quotes/index.gohtml:72
#: web/template/expenses/index.gohtml:70
msgctxt "title"
msgid "Amount"
msgstr "Importe"
#: web/template/invoices/index.gohtml:61 web/template/quotes/index.gohtml:61
#: web/template/expenses/index.gohtml:54
#: web/template/invoices/index.gohtml:73 web/template/quotes/index.gohtml:73
#: web/template/expenses/index.gohtml:71
msgctxt "title"
msgid "Download"
msgstr "Descargar"
#: web/template/invoices/index.gohtml:62 web/template/quotes/index.gohtml:62
#: web/template/contacts/index.gohtml:51 web/template/expenses/index.gohtml:55
#: web/template/invoices/index.gohtml:74 web/template/quotes/index.gohtml:74
#: web/template/contacts/index.gohtml:51 web/template/expenses/index.gohtml:72
#: web/template/products/index.gohtml:48
msgctxt "title"
msgid "Actions"
msgstr "Acciones"
#: web/template/invoices/index.gohtml:69
#: web/template/invoices/index.gohtml:81
msgctxt "action"
msgid "Select invoice %v"
msgstr "Seleccionar factura %v"
#: web/template/invoices/index.gohtml:110
#: web/template/invoices/index.gohtml:122
msgctxt "action"
msgid "Download invoice %s"
msgstr "Descargar factura %s"
#: web/template/invoices/index.gohtml:118
#: web/template/invoices/index.gohtml:130
msgid "Actions for invoice %s"
msgstr "Acciones para la factura %s"
#: web/template/invoices/index.gohtml:126 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:125 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:117
#: web/template/invoices/index.gohtml:138 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:137 web/template/quotes/view.gohtml:22
#: web/template/contacts/index.gohtml:82 web/template/expenses/index.gohtml:134
#: web/template/products/index.gohtml:78
msgctxt "action"
msgid "Edit"
msgstr "Editar"
#: web/template/invoices/index.gohtml:134 web/template/invoices/view.gohtml:16
#: web/template/quotes/index.gohtml:133 web/template/quotes/view.gohtml:19
#: web/template/invoices/index.gohtml:146 web/template/invoices/view.gohtml:16
#: web/template/quotes/index.gohtml:145 web/template/quotes/view.gohtml:19
msgctxt "action"
msgid "Duplicate"
msgstr "Duplicar"
#: web/template/invoices/index.gohtml:144
#: web/template/invoices/index.gohtml:156
msgid "No invoices added yet."
msgstr "No hay facturas."
#: web/template/invoices/index.gohtml:151 web/template/quotes/index.gohtml:158
#: web/template/expenses/index.gohtml:134
#: web/template/invoices/index.gohtml:163 web/template/quotes/index.gohtml:170
#: web/template/expenses/index.gohtml:151
msgid "Total"
msgstr "Total"
@ -363,41 +369,41 @@ msgctxt "title"
msgid "New Quotation"
msgstr "Nuevo presupuesto"
#: web/template/quotes/index.gohtml:19
#: web/template/quotes/index.gohtml:28
msgctxt "action"
msgid "Download quotations"
msgstr "Descargar presupuestos"
#: web/template/quotes/index.gohtml:21
#: web/template/quotes/index.gohtml:33
msgctxt "action"
msgid "New quotation"
msgstr "Nuevo presupuesto"
#: web/template/quotes/index.gohtml:54
#: web/template/quotes/index.gohtml:66
msgctxt "quote"
msgid "All"
msgstr "Todos"
#: web/template/quotes/index.gohtml:56
#: web/template/quotes/index.gohtml:68
msgctxt "title"
msgid "Quotation Num."
msgstr "N.º de presupuesto"
#: web/template/quotes/index.gohtml:69
#: web/template/quotes/index.gohtml:81
msgctxt "action"
msgid "Select quotation %v"
msgstr "Seleccionar presupuesto %v"
#: web/template/quotes/index.gohtml:117
#: web/template/quotes/index.gohtml:129
msgid "Actions for quote %s"
msgstr "Acciones para el presupuesto %s"
#: web/template/quotes/index.gohtml:141 web/template/quotes/view.gohtml:16
#: web/template/quotes/index.gohtml:153 web/template/quotes/view.gohtml:16
msgctxt "action"
msgid "Create invoice"
msgstr "Crear factura"
#: web/template/quotes/index.gohtml:151
#: web/template/quotes/index.gohtml:163
msgid "No quotations added yet."
msgstr "No hay presupuestos."
@ -488,7 +494,7 @@ msgctxt "action"
msgid "New contact"
msgstr "Nuevo contacto"
#: web/template/contacts/index.gohtml:47 web/template/expenses/index.gohtml:48
#: web/template/contacts/index.gohtml:47 web/template/expenses/index.gohtml:65
msgctxt "title"
msgid "Contact"
msgstr "Contacto"
@ -569,26 +575,26 @@ msgctxt "title"
msgid "Expenses"
msgstr "Gastos"
#: web/template/expenses/index.gohtml:16
#: web/template/expenses/index.gohtml:32
msgctxt "action"
msgid "New expense"
msgstr "Nuevo gasto"
#: web/template/expenses/index.gohtml:49
#: web/template/expenses/index.gohtml:66
msgctxt "title"
msgid "Invoice Date"
msgstr "Fecha de factura"
#: web/template/expenses/index.gohtml:50
#: web/template/expenses/index.gohtml:67
msgctxt "title"
msgid "Invoice Number"
msgstr "Número de factura"
#: web/template/expenses/index.gohtml:109
#: web/template/expenses/index.gohtml:126
msgid "Actions for expense %s"
msgstr "Acciones para el gasto %s"
#: web/template/expenses/index.gohtml:127
#: web/template/expenses/index.gohtml:144
msgid "No expenses added yet."
msgstr "No hay gastos."
@ -695,7 +701,7 @@ msgctxt "title"
msgid "Edit Product “%s”"
msgstr "Edición del producto «%s»"
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:267
#: pkg/login.go:37 pkg/company.go:127 pkg/profile.go:40 pkg/contacts.go:276
msgctxt "input"
msgid "Email"
msgstr "Correo-e"
@ -709,7 +715,7 @@ msgstr "Contraseña"
msgid "Email can not be empty."
msgstr "No podéis dejar el correo-e en blanco."
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:411
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:420
msgid "This value is not a valid email. It should be like name@domain.com."
msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
@ -721,84 +727,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:268 pkg/quote.go:887
#: pkg/invoices.go:1002 pkg/contacts.go:140 pkg/contacts.go:253
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901
#: pkg/invoices.go:1016 pkg/contacts.go:149 pkg/contacts.go:262
msgctxt "input"
msgid "Name"
msgstr "Nombre"
#: pkg/products.go:169 pkg/products.go:295 pkg/quote.go:174 pkg/quote.go:694
#: pkg/expenses.go:274 pkg/expenses.go:433 pkg/invoices.go:174
#: pkg/invoices.go:732 pkg/invoices.go:1304 pkg/contacts.go:145
#: pkg/contacts.go:353
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
#: pkg/expenses.go:278 pkg/expenses.go:442 pkg/invoices.go:174
#: pkg/invoices.go:746 pkg/invoices.go:1331 pkg/contacts.go:154
#: pkg/contacts.go:362
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:443 pkg/invoices.go:178
#: pkg/contacts.go:149
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:452 pkg/invoices.go:178
#: pkg/contacts.go:158
msgctxt "input"
msgid "Tags Condition"
msgstr "Condición de las etiquetas"
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:447 pkg/invoices.go:182
#: pkg/contacts.go:153
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:456 pkg/invoices.go:182
#: pkg/contacts.go:162
msgctxt "tag condition"
msgid "All"
msgstr "Todas"
#: pkg/products.go:178 pkg/expenses.go:448 pkg/invoices.go:183
#: pkg/contacts.go:154
#: pkg/products.go:186 pkg/expenses.go:457 pkg/invoices.go:183
#: pkg/contacts.go:163
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:452 pkg/invoices.go:187
#: pkg/contacts.go:158
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:461 pkg/invoices.go:187
#: pkg/contacts.go:167
msgctxt "tag condition"
msgid "Any"
msgstr "Cualquiera"
#: pkg/products.go:183 pkg/expenses.go:453 pkg/invoices.go:188
#: pkg/contacts.go:159
#: pkg/products.go:191 pkg/expenses.go:462 pkg/invoices.go:188
#: pkg/contacts.go:168
msgid "Invoices must have at least one of the specified labels."
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
#: pkg/products.go:274 pkg/quote.go:901 pkg/invoices.go:1016
#: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1030
msgctxt "input"
msgid "Description"
msgstr "Descripción"
#: pkg/products.go:279 pkg/quote.go:905 pkg/invoices.go:1020
#: pkg/products.go:287 pkg/quote.go:919 pkg/invoices.go:1034
msgctxt "input"
msgid "Price"
msgstr "Precio"
#: pkg/products.go:289 pkg/quote.go:934 pkg/expenses.go:242
#: pkg/invoices.go:1049
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:246
#: pkg/invoices.go:1063
msgctxt "input"
msgid "Taxes"
msgstr "Impuestos"
#: pkg/products.go:314 pkg/quote.go:983 pkg/profile.go:92 pkg/invoices.go:1098
#: pkg/contacts.go:403
#: pkg/products.go:322 pkg/quote.go:997 pkg/profile.go:92 pkg/invoices.go:1112
#: pkg/contacts.go:412
msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco."
#: pkg/products.go:315 pkg/quote.go:984 pkg/invoices.go:1099
#: pkg/products.go:323 pkg/quote.go:998 pkg/invoices.go:1113
msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco."
#: pkg/products.go:316 pkg/quote.go:985 pkg/invoices.go:1100
#: pkg/products.go:324 pkg/quote.go:999 pkg/invoices.go:1114
msgid "Price must be a number greater than zero."
msgstr "El precio tiene que ser un número mayor a cero."
#: pkg/products.go:318 pkg/quote.go:993 pkg/expenses.go:310
#: pkg/invoices.go:1108
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:314
#: pkg/invoices.go:1122
msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/products.go:319 pkg/quote.go:994 pkg/expenses.go:311
#: pkg/invoices.go:1109
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:315
#: pkg/invoices.go:1123
msgid "You can only select a tax of each class."
msgstr "Solo podéis escoger un impuesto de cada clase."
@ -807,47 +813,47 @@ msgctxt "input"
msgid "Trade name"
msgstr "Nombre comercial"
#: pkg/company.go:118 pkg/contacts.go:259
#: pkg/company.go:118 pkg/contacts.go:268
msgctxt "input"
msgid "Phone"
msgstr "Teléfono"
#: pkg/company.go:136 pkg/contacts.go:275
#: pkg/company.go:136 pkg/contacts.go:284
msgctxt "input"
msgid "Web"
msgstr "Web"
#: pkg/company.go:144 pkg/contacts.go:287
#: pkg/company.go:144 pkg/contacts.go:296
msgctxt "input"
msgid "Business name"
msgstr "Nombre y apellidos"
#: pkg/company.go:154 pkg/contacts.go:297
#: pkg/company.go:154 pkg/contacts.go:306
msgctxt "input"
msgid "VAT number"
msgstr "DNI / NIF"
#: pkg/company.go:160 pkg/contacts.go:303
#: pkg/company.go:160 pkg/contacts.go:312
msgctxt "input"
msgid "Address"
msgstr "Dirección"
#: pkg/company.go:169 pkg/contacts.go:312
#: pkg/company.go:169 pkg/contacts.go:321
msgctxt "input"
msgid "City"
msgstr "Población"
#: pkg/company.go:175 pkg/contacts.go:318
#: pkg/company.go:175 pkg/contacts.go:327
msgctxt "input"
msgid "Province"
msgstr "Provincia"
#: pkg/company.go:181 pkg/contacts.go:324
#: pkg/company.go:181 pkg/contacts.go:333
msgctxt "input"
msgid "Postal code"
msgstr "Código postal"
#: pkg/company.go:190 pkg/contacts.go:333
#: pkg/company.go:190 pkg/contacts.go:342
msgctxt "input"
msgid "Country"
msgstr "País"
@ -882,23 +888,23 @@ msgctxt "input"
msgid "Legal disclaimer"
msgstr "Nota legal"
#: pkg/company.go:271 pkg/contacts.go:385
#: pkg/company.go:271 pkg/contacts.go:394
msgid "Selected country is not valid."
msgstr "Habéis escogido un país que no es válido."
#: pkg/company.go:275 pkg/contacts.go:388
#: pkg/company.go:275 pkg/contacts.go:397
msgid "Business name can not be empty."
msgstr "No podéis dejar el nombre y los apellidos en blanco."
#: pkg/company.go:276 pkg/contacts.go:389
#: pkg/company.go:276 pkg/contacts.go:398
msgid "Business name must have at least two letters."
msgstr "El nombre y los apellidos deben contener como mínimo dos letras."
#: pkg/company.go:277 pkg/contacts.go:391
#: pkg/company.go:277 pkg/contacts.go:400
msgid "VAT number can not be empty."
msgstr "No podéis dejar el DNI o NIF en blanco."
#: pkg/company.go:278 pkg/contacts.go:392
#: pkg/company.go:278 pkg/contacts.go:401
msgid "This value is not a valid VAT number."
msgstr "Este valor no es un DNI o NIF válido."
@ -906,31 +912,31 @@ msgstr "Este valor no es un DNI o NIF válido."
msgid "Phone can not be empty."
msgstr "No podéis dejar el teléfono en blanco."
#: pkg/company.go:281 pkg/contacts.go:408
#: pkg/company.go:281 pkg/contacts.go:417
msgid "This value is not a valid phone number."
msgstr "Este valor no es un teléfono válido."
#: pkg/company.go:287 pkg/contacts.go:414
#: pkg/company.go:287 pkg/contacts.go:423
msgid "This value is not a valid web address. It should be like https://domain.com/."
msgstr "Este valor no es una dirección web válida. Tiene que ser parecida a https://dominio.es/."
#: pkg/company.go:289 pkg/contacts.go:394
#: pkg/company.go:289 pkg/contacts.go:403
msgid "Address can not be empty."
msgstr "No podéis dejar la dirección en blanco."
#: pkg/company.go:290 pkg/contacts.go:395
#: pkg/company.go:290 pkg/contacts.go:404
msgid "City can not be empty."
msgstr "No podéis dejar la población en blanco."
#: pkg/company.go:291 pkg/contacts.go:396
#: pkg/company.go:291 pkg/contacts.go:405
msgid "Province can not be empty."
msgstr "No podéis dejar la provincia en blanco."
#: pkg/company.go:292 pkg/contacts.go:398
#: pkg/company.go:292 pkg/contacts.go:407
msgid "Postal code can not be empty."
msgstr "No podéis dejar el código postal en blanco."
#: pkg/company.go:293 pkg/contacts.go:399
#: pkg/company.go:293 pkg/contacts.go:408
msgid "This value is not a valid postal code."
msgstr "Este valor no es un código postal válido válido."
@ -1007,7 +1013,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:672 pkg/invoices.go:147 pkg/invoices.go:715
#: pkg/quote.go:147 pkg/quote.go:686 pkg/invoices.go:147 pkg/invoices.go:729
msgctxt "input"
msgid "Customer"
msgstr "Cliente"
@ -1016,12 +1022,12 @@ msgstr "Cliente"
msgid "All customers"
msgstr "Todos los clientes"
#: pkg/quote.go:153 pkg/quote.go:666
#: pkg/quote.go:153 pkg/quote.go:680
msgctxt "input"
msgid "Quotation Status"
msgstr "Estado del presupuesto"
#: pkg/quote.go:154 pkg/expenses.go:438 pkg/invoices.go:154
#: pkg/quote.go:154 pkg/expenses.go:447 pkg/invoices.go:154
msgid "All status"
msgstr "Todos los estados"
@ -1030,12 +1036,12 @@ msgctxt "input"
msgid "Quotation Number"
msgstr "Número de presupuesto"
#: pkg/quote.go:164 pkg/expenses.go:423 pkg/invoices.go:164
#: pkg/quote.go:164 pkg/expenses.go:432 pkg/invoices.go:164
msgctxt "input"
msgid "From Date"
msgstr "A partir de la fecha"
#: pkg/quote.go:169 pkg/expenses.go:428 pkg/invoices.go:169
#: pkg/quote.go:169 pkg/expenses.go:437 pkg/invoices.go:169
msgctxt "input"
msgid "To Date"
msgstr "Hasta la fecha"
@ -1048,99 +1054,104 @@ 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:614
#: pkg/quote.go:618
msgid "quotations.zip"
msgstr "presupuestos.zip"
#: pkg/quote.go:620 pkg/quote.go:1149 pkg/quote.go:1157 pkg/expenses.go:620
#: pkg/invoices.go:663 pkg/invoices.go:1279 pkg/invoices.go:1287
#: pkg/quote.go:632
msgid "quotations.ods"
msgstr "presupuestos.ods"
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:651
#: pkg/expenses.go:677 pkg/invoices.go:677 pkg/invoices.go:1306
#: pkg/invoices.go:1314
msgid "Invalid action"
msgstr "Acción inválida."
#: pkg/quote.go:673
#: pkg/quote.go:687
msgid "Select a customer to quote."
msgstr "Escoged un cliente a presupuestar."
#: pkg/quote.go:678
#: pkg/quote.go:692
msgctxt "input"
msgid "Quotation Date"
msgstr "Fecha del presupuesto"
#: pkg/quote.go:684
#: pkg/quote.go:698
msgctxt "input"
msgid "Terms and conditions"
msgstr "Condiciones de aceptación"
#: pkg/quote.go:689 pkg/invoices.go:727
#: pkg/quote.go:703 pkg/invoices.go:741
msgctxt "input"
msgid "Notes"
msgstr "Notas"
#: pkg/quote.go:698 pkg/invoices.go:737
#: pkg/quote.go:712 pkg/invoices.go:751
msgctxt "input"
msgid "Payment Method"
msgstr "Método de pago"
#: pkg/quote.go:699
#: pkg/quote.go:713
msgid "Select a payment method."
msgstr "Escoged un método e pago."
#: pkg/quote.go:735
#: pkg/quote.go:749
msgid "Selected quotation status is not valid."
msgstr "Habéis escogido un estado de presupuesto que no es válido."
#: pkg/quote.go:737 pkg/invoices.go:792
#: pkg/quote.go:751 pkg/invoices.go:806
msgid "Selected customer is not valid."
msgstr "Habéis escogido un cliente que no es válido."
#: pkg/quote.go:739
#: pkg/quote.go:753
msgid "Quotation date can not be empty."
msgstr "No podéis dejar la fecha del presupuesto en blanco."
#: pkg/quote.go:740
#: pkg/quote.go:754
msgid "Quotation date must be a valid date."
msgstr "La fecha de presupuesto debe ser válida."
#: pkg/quote.go:743 pkg/invoices.go:796
#: pkg/quote.go:757 pkg/invoices.go:810
msgid "Selected payment method is not valid."
msgstr "Habéis escogido un método de pago que no es válido."
#: pkg/quote.go:877 pkg/quote.go:882 pkg/invoices.go:992 pkg/invoices.go:997
#: pkg/quote.go:891 pkg/quote.go:896 pkg/invoices.go:1006 pkg/invoices.go:1011
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/quote.go:915 pkg/invoices.go:1030
#: pkg/quote.go:929 pkg/invoices.go:1044
msgctxt "input"
msgid "Quantity"
msgstr "Cantidad"
#: pkg/quote.go:924 pkg/invoices.go:1039
#: pkg/quote.go:938 pkg/invoices.go:1053
msgctxt "input"
msgid "Discount (%)"
msgstr "Descuento (%)"
#: pkg/quote.go:978
#: pkg/quote.go:992
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:981 pkg/invoices.go:1096
#: pkg/quote.go:995 pkg/invoices.go:1110
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:987 pkg/invoices.go:1102
#: pkg/quote.go:1001 pkg/invoices.go:1116
msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco."
#: pkg/quote.go:988 pkg/invoices.go:1103
#: pkg/quote.go:1002 pkg/invoices.go:1117
msgid "Quantity must be a number greater than zero."
msgstr "La cantidad tiene que ser un número mayor a cero."
#: pkg/quote.go:990 pkg/invoices.go:1105
#: pkg/quote.go:1004 pkg/invoices.go:1119
msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco."
#: pkg/quote.go:991 pkg/invoices.go:1106
#: pkg/quote.go:1005 pkg/invoices.go:1120
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
@ -1207,132 +1218,140 @@ msgctxt "period option"
msgid "Previous year"
msgstr "Año anterior"
#: pkg/expenses.go:168
#: pkg/expenses.go:172
msgid "Select a contact."
msgstr "Escoged un contacto"
#: pkg/expenses.go:225 pkg/expenses.go:412
#: pkg/expenses.go:229 pkg/expenses.go:421
msgctxt "input"
msgid "Contact"
msgstr "Contacto"
#: pkg/expenses.go:231
#: pkg/expenses.go:235
msgctxt "input"
msgid "Invoice number"
msgstr "Número de factura"
#: pkg/expenses.go:236 pkg/invoices.go:721
#: pkg/expenses.go:240 pkg/invoices.go:735
msgctxt "input"
msgid "Invoice Date"
msgstr "Fecha de factura"
#: pkg/expenses.go:251
#: pkg/expenses.go:255
msgctxt "input"
msgid "Amount"
msgstr "Importe"
#: pkg/expenses.go:262 pkg/invoices.go:743
#: pkg/expenses.go:266 pkg/invoices.go:757
msgctxt "input"
msgid "File"
msgstr "Archivo"
#: pkg/expenses.go:268 pkg/expenses.go:437
#: pkg/expenses.go:272 pkg/expenses.go:446
msgctxt "input"
msgid "Expense Status"
msgstr "Estado del gasto"
#: pkg/expenses.go:308
#: pkg/expenses.go:312
msgid "Selected contact is not valid."
msgstr "Habéis escogido un contacto que no es válido."
#: pkg/expenses.go:309 pkg/invoices.go:794
#: pkg/expenses.go:313 pkg/invoices.go:808
msgid "Invoice date must be a valid date."
msgstr "La fecha de factura debe ser válida."
#: pkg/expenses.go:312
#: pkg/expenses.go:316
msgid "Amount can not be empty."
msgstr "No podéis dejar el importe en blanco."
#: pkg/expenses.go:313
#: pkg/expenses.go:317
msgid "Amount must be a number greater than zero."
msgstr "El importe tiene que ser un número mayor a cero."
#: pkg/expenses.go:315
#: pkg/expenses.go:319
msgid "Selected expense status is not valid."
msgstr "Habéis escogido un estado de gasto que no es válido."
#: pkg/expenses.go:413
#: pkg/expenses.go:422
msgid "All contacts"
msgstr "Todos los contactos"
#: pkg/expenses.go:418 pkg/invoices.go:159
#: pkg/expenses.go:427 pkg/invoices.go:159
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/invoices.go:153 pkg/invoices.go:709
#: pkg/expenses.go:675
msgid "expenses.ods"
msgstr "gastos.ods"
#: pkg/invoices.go:153 pkg/invoices.go:723
msgctxt "input"
msgid "Invoice Status"
msgstr "Estado de la factura"
#: pkg/invoices.go:553
#: pkg/invoices.go:557
msgid "Select a customer to bill."
msgstr "Escoged un cliente a facturar."
#: pkg/invoices.go:657
#: pkg/invoices.go:661
msgid "invoices.zip"
msgstr "facturas.zip"
#: pkg/invoices.go:791
#: pkg/invoices.go:675
msgid "invoices.ods"
msgstr "facturas.ods"
#: pkg/invoices.go:805
msgid "Selected invoice status is not valid."
msgstr "Habéis escogido un estado de factura que no es válido."
#: pkg/invoices.go:793
#: pkg/invoices.go:807
msgid "Invoice date can not be empty."
msgstr "No podéis dejar la fecha de la factura en blanco."
#: pkg/invoices.go:929
#: pkg/invoices.go:943
#, c-format
msgid "Re: quotation #%s of %s"
msgstr "Ref: presupuesto n.º %s del %s"
#: pkg/invoices.go:930
#: pkg/invoices.go:944
msgctxt "to_char"
msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY"
#: pkg/invoices.go:1093
#: pkg/invoices.go:1107
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."
#: pkg/contacts.go:283
#: pkg/contacts.go:292
msgctxt "input"
msgid "Need to input tax details"
msgstr "Necesito facturar este contacto"
#: pkg/contacts.go:343
#: pkg/contacts.go:352
msgctxt "input"
msgid "IBAN"
msgstr "IBAN"
#: pkg/contacts.go:348
#: pkg/contacts.go:357
msgctxt "bic"
msgid "BIC"
msgstr "BIC"
#: pkg/contacts.go:404
#: pkg/contacts.go:413
msgid "Name must have at least two letters."
msgstr "El nombre debe contener como mínimo dos letras."
#: pkg/contacts.go:417
#: pkg/contacts.go:426
msgid "This values is not a valid IBAN."
msgstr "Este valor no es un IBAN válido."
#: pkg/contacts.go:420
#: pkg/contacts.go:429
msgid "This values is not a valid BIC."
msgstr "Este valor no es un BIC válido."
#: pkg/contacts.go:534
#: pkg/contacts.go:551
msgctxt "input"
msgid "Holded Excel file"
msgstr "Archivo Excel de Holded"

View File

@ -5,16 +5,33 @@
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expensesIndexPage*/ -}}
<nav data-hx-boost="true" data-hx-target="main">
<p>
<nav data-hx-target="main">
<p data-hx-boost="true">
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a>{{( pgettext "Expenses" "title" )}}</a>
</p>
<form id="batch-form" action="{{ companyURI "/expenses/batch" }}" method="post">
{{ csrfToken }}
{{ with .Filters }}
{{ template "hidden-select-field" .Contact }}
{{ template "hidden-select-field" .ExpenseStatus }}
{{ template "hidden-field" .FromDate }}
{{ template "hidden-field" .ToDate }}
{{ template "hidden-field" .InvoiceNumber }}
{{ template "hidden-field" .Tags }}
{{ template "hidden-field" .TagsCondition }}
{{ end }}
<p>
{{ template "filters-toggle" }}
<button type="submit"
name="action" value="export"
>{{( pgettext "Export list" "action" )}}</button>
<a class="primary button"
data-hx-boost="true"
href="{{ companyURI "/expenses/new" }}">{{( pgettext "New expense" "action" )}}</a>
</p>
</form>
</nav>
{{- end }}

View File

@ -12,11 +12,23 @@
<form id="batch-form" action="{{ companyURI "/invoices/batch" }}" method="post">
{{ csrfToken }}
{{ with .Filters }}
{{ template "hidden-select-field" .Customer }}
{{ template "hidden-select-field" .InvoiceStatus }}
{{ template "hidden-field" .FromDate }}
{{ template "hidden-field" .ToDate }}
{{ template "hidden-field" .InvoiceNumber }}
{{ template "hidden-field" .Tags }}
{{ template "hidden-field" .TagsCondition }}
{{ end }}
<p>
{{ template "filters-toggle" }}
<button type="submit"
name="action" value="download"
>{{( pgettext "Download invoices" "action" )}}</button>
<button type="submit"
name="action" value="export"
>{{( pgettext "Export list" "action" )}}</button>
<a class="primary button" data-hx-boost="true"
href="{{ companyURI "/invoices/new" }}">{{( pgettext "New invoice" "action" )}}</a>
</p>

View File

@ -12,11 +12,23 @@
<form id="batch-form" action="{{ companyURI "/quotes/batch" }}" method="post">
{{ csrfToken }}
{{ with .Filters }}
{{ template "hidden-field" .Customer }}
{{ template "hidden-select-field" .QuoteStatus }}
{{ template "hidden-field" .FromDate }}
{{ template "hidden-field" .ToDate }}
{{ template "hidden-field" .QuoteNumber }}
{{ template "hidden-field" .Tags }}
{{ template "hidden-field" .TagsCondition }}
{{ end }}
<p>
{{ template "filters-toggle" }}
<button type="submit"
name="action" value="download"
>{{( pgettext "Download quotations" "action" )}}</button>
<button type="submit"
name="action" value="export"
>{{( pgettext "Export list" "action" )}}</button>
<a class="primary button" data-hx-boost="true"
href="{{ companyURI "/quotes/new" }}">{{( pgettext "New quotation" "action" )}}</a>
</p>