Add a column for each tax type when exporting invoices and expenses

In the HTML tables i only compute the aggregated amount by tax class
(e.g., IVA, IRPF), but here we need the actual tax (e.g., IVA 4 %)
because this spreadsheet is intended for accountants.

I can easily extract the amounts from invoice_tax_amount and
expense_tax_amount, but i also need to add the columns to the
spreadsheet, and always with the same order—does not matter much which,
only the same—, that’s why i had to sort the tax IDs when exporting, as
Go does not guarantee an order for maps.

Closes #92
This commit is contained in:
jordi fita mas 2024-01-26 02:29:51 +01:00
parent 6fcc19bebf
commit 65413637ac
5 changed files with 324 additions and 173 deletions

View File

@ -13,6 +13,7 @@ import (
) )
type ExpenseEntry struct { type ExpenseEntry struct {
ID int
Slug string Slug string
InvoiceDate time.Time InvoiceDate time.Time
InvoiceNumber string InvoiceNumber string
@ -58,7 +59,8 @@ func IndexExpenses(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
func mustCollectExpenseEntries(ctx context.Context, conn *Conn, locale *Locale, filters *expenseFilterForm) []*ExpenseEntry { func mustCollectExpenseEntries(ctx context.Context, conn *Conn, locale *Locale, filters *expenseFilterForm) []*ExpenseEntry {
where, args := filters.BuildQuery([]interface{}{locale.Language.String()}) where, args := filters.BuildQuery([]interface{}{locale.Language.String()})
rows := conn.MustQuery(ctx, fmt.Sprintf(` rows := conn.MustQuery(ctx, fmt.Sprintf(`
select expense.slug select expense_id
, expense.slug
, invoice_date , invoice_date
, invoice_number , invoice_number
, to_price(expense.amount, decimal_digits) as amount , to_price(expense.amount, decimal_digits) as amount
@ -78,7 +80,8 @@ func mustCollectExpenseEntries(ctx context.Context, conn *Conn, locale *Locale,
join expense_status_i18n esi18n on expense.expense_status = esi18n.expense_status and esi18n.lang_tag = $1 join expense_status_i18n esi18n on expense.expense_status = esi18n.expense_status and esi18n.lang_tag = $1
join currency using (currency_code) join currency using (currency_code)
where (%s) where (%s)
group by expense.slug group by expense_id
, expense.slug
, invoice_date , invoice_date
, invoice_number , invoice_number
, expense.amount , expense.amount
@ -98,7 +101,7 @@ func mustCollectExpenseEntries(ctx context.Context, conn *Conn, locale *Locale,
Taxes: make(map[string]string), Taxes: make(map[string]string),
} }
var taxes [][]string var taxes [][]string
if err := rows.Scan(&entry.Slug, &entry.InvoiceDate, &entry.InvoiceNumber, &entry.Amount, &taxes, &entry.Total, &entry.InvoicerName, &entry.OriginalFileName, &entry.Tags, &entry.Status, &entry.StatusLabel); err != nil { if err := rows.Scan(&entry.ID, &entry.Slug, &entry.InvoiceDate, &entry.InvoiceNumber, &entry.Amount, &taxes, &entry.Total, &entry.InvoicerName, &entry.OriginalFileName, &entry.Tags, &entry.Status, &entry.StatusLabel); err != nil {
panic(err) panic(err)
} }
for _, tax := range taxes { for _, tax := range taxes {
@ -737,9 +740,27 @@ func HandleBatchExpenseAction(w http.ResponseWriter, r *http.Request, _ httprout
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
ods := mustWriteExpensesOds(mustCollectExpenseEntries(r.Context(), conn, locale, filters), locale, company) entries := mustCollectExpenseEntries(r.Context(), conn, locale, filters)
taxes := mustCollectExpenseEntriesTaxes(r.Context(), conn, entries)
taxColumns := mustCollectTaxColumns(r.Context(), conn, company)
ods := mustWriteExpensesOds(entries, taxes, taxColumns, locale, company)
writeOdsResponse(w, ods, gettext("expenses.ods", locale)) writeOdsResponse(w, ods, gettext("expenses.ods", locale))
default: default:
http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest) http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest)
} }
} }
func mustCollectExpenseEntriesTaxes(ctx context.Context, conn *Conn, entries []*ExpenseEntry) map[int]taxMap {
ids := mustMakeIDArray(entries, func(entry *ExpenseEntry) int {
return entry.ID
})
return mustMakeTaxMap(ctx, conn, ids, `
select expense_id
, tax_id
, to_price(tax.amount, decimal_digits)
from expense_tax_amount as tax
join expense using (expense_id)
join currency using (currency_code)
where expense_id = any ($1)
`)
}

View File

@ -6,6 +6,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/jackc/pgtype"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"html/template" "html/template"
"io" "io"
@ -23,6 +24,7 @@ import (
const removedProductSuffix = ".removed" const removedProductSuffix = ".removed"
type InvoiceEntry struct { type InvoiceEntry struct {
ID int
Slug string Slug string
Date time.Time Date time.Time
Number string Number string
@ -61,7 +63,8 @@ func IndexInvoices(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, locale *Locale, filters *invoiceFilterForm) []*InvoiceEntry { func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, locale *Locale, filters *invoiceFilterForm) []*InvoiceEntry {
where, args := filters.BuildQuery([]interface{}{locale.Language.String()}) where, args := filters.BuildQuery([]interface{}{locale.Language.String()})
rows := conn.MustQuery(ctx, fmt.Sprintf(` rows := conn.MustQuery(ctx, fmt.Sprintf(`
select invoice.slug select invoice_id
, invoice.slug
, invoice_date , invoice_date
, invoice_number , invoice_number
, contact.name , contact.name
@ -83,7 +86,7 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, locale *Locale,
var entries []*InvoiceEntry var entries []*InvoiceEntry
for rows.Next() { for rows.Next() {
entry := &InvoiceEntry{} entry := &InvoiceEntry{}
if err := rows.Scan(&entry.Slug, &entry.Date, &entry.Number, &entry.CustomerName, &entry.Tags, &entry.Status, &entry.StatusLabel, &entry.Total); err != nil { if err := rows.Scan(&entry.ID, &entry.Slug, &entry.Date, &entry.Number, &entry.CustomerName, &entry.Tags, &entry.Status, &entry.StatusLabel, &entry.Total); err != nil {
panic(err) panic(err)
} }
entries = append(entries, entry) entries = append(entries, entry)
@ -671,13 +674,103 @@ func HandleBatchInvoiceAction(w http.ResponseWriter, r *http.Request, _ httprout
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
ods := mustWriteInvoicesOds(mustCollectInvoiceEntries(r.Context(), conn, locale, filters), locale, company) entries := mustCollectInvoiceEntries(r.Context(), conn, locale, filters)
taxes := mustCollectInvoiceEntriesTaxes(r.Context(), conn, entries)
taxColumns := mustCollectTaxColumns(r.Context(), conn, company)
ods := mustWriteInvoicesOds(entries, taxes, taxColumns, locale, company)
writeOdsResponse(w, ods, gettext("invoices.ods", locale)) writeOdsResponse(w, ods, gettext("invoices.ods", locale))
default: default:
http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest) http.Error(w, gettext("Invalid action", locale), http.StatusBadRequest)
} }
} }
func mustCollectTaxColumns(ctx context.Context, conn *Conn, company *Company) map[int]string {
rows, err := conn.Query(ctx, `
select tax_id
, name
from tax
where company_id = $1
`, company.Id)
if err != nil {
panic(err)
}
defer rows.Close()
columns := make(map[int]string)
for rows.Next() {
var taxID int
var name string
err = rows.Scan(&taxID, &name)
if err != nil {
panic(err)
}
columns[taxID] = name
}
return columns
}
type taxMap map[int]string
func mustCollectInvoiceEntriesTaxes(ctx context.Context, conn *Conn, entries []*InvoiceEntry) map[int]taxMap {
ids := mustMakeIDArray(entries, func(entry *InvoiceEntry) int {
return entry.ID
})
return mustMakeTaxMap(ctx, conn, ids, `
select invoice_id
, tax_id
, to_price(amount, decimal_digits)
from invoice_tax_amount
join invoice using (invoice_id)
join currency using (currency_code)
where invoice_id = any ($1)
`)
}
func mustMakeIDArray[T any](entries []*T, id func(entry *T) int) *pgtype.Int4Array {
ids := make([]int, len(entries))
i := 0
for _, entry := range entries {
ids[i] = id(entry)
i++
}
idArray := &pgtype.Int4Array{}
if err := idArray.Set(ids); err != nil {
panic(err)
}
return idArray
}
func mustMakeTaxMap(ctx context.Context, conn *Conn, ids *pgtype.Int4Array, sql string) map[int]taxMap {
rows, err := conn.Query(ctx, sql, ids)
if err != nil {
panic(err)
}
defer rows.Close()
taxes := make(map[int]taxMap)
for rows.Next() {
var entryID int
var taxID int
var amount string
err := rows.Scan(&entryID, &taxID, &amount)
if err != nil {
panic(err)
}
entryTaxes := taxes[entryID]
if entryTaxes == nil {
entryTaxes = make(taxMap)
taxes[entryID] = entryTaxes
}
entryTaxes[taxID] = amount
}
if rows.Err() != nil {
panic(rows.Err())
}
return taxes
}
func mustWriteInvoicesPdf(r *http.Request, slugs []string) []byte { func mustWriteInvoicesPdf(r *http.Request, slugs []string) []byte {
conn := getConn(r) conn := getConn(r)
company := mustGetCompany(r) company := mustGetCompany(r)

View File

@ -6,6 +6,7 @@ import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"net/http" "net/http"
"sort"
"strings" "strings"
"time" "time"
) )
@ -41,22 +42,39 @@ const (
` `
) )
func mustWriteInvoicesOds(invoices []*InvoiceEntry, locale *Locale, company *Company) []byte { func extractTaxIDs(taxColumns map[int]string) []int {
columns := []string{ taxIDs := make([]int, len(taxColumns))
"Date", i := 0
"Invoice Num.", for k := range taxColumns {
"Customer", taxIDs[i] = k
"Status", i++
"Tags",
"Amount",
} }
sort.Ints(taxIDs[:])
return taxIDs
}
func mustWriteInvoicesOds(invoices []*InvoiceEntry, taxes map[int]taxMap, taxColumns map[int]string, locale *Locale, company *Company) []byte {
taxIDs := extractTaxIDs(taxColumns)
columns := make([]string, 6+len(taxIDs))
columns[0] = "Date"
columns[1] = "Invoice Num."
columns[2] = "Customer"
columns[3] = "Status"
i := 4
for _, taxID := range taxIDs {
columns[i] = taxColumns[taxID]
i++
}
columns[i] = "Amount"
columns[i+1] = "Tags"
return mustWriteTableOds(invoices, columns, locale, func(sb *strings.Builder, invoice *InvoiceEntry) { return mustWriteTableOds(invoices, columns, locale, func(sb *strings.Builder, invoice *InvoiceEntry) {
writeCellDate(sb, invoice.Date) writeCellDate(sb, invoice.Date)
writeCellString(sb, invoice.Number) writeCellString(sb, invoice.Number)
writeCellString(sb, invoice.CustomerName) writeCellString(sb, invoice.CustomerName)
writeCellString(sb, invoice.StatusLabel) writeCellString(sb, invoice.StatusLabel)
writeCellString(sb, strings.Join(invoice.Tags, ",")) writeTaxes(sb, taxes[invoice.ID], taxIDs, locale, company)
writeCellFloat(sb, invoice.Total, locale, company) writeCellFloat(sb, invoice.Total, locale, company)
writeCellString(sb, strings.Join(invoice.Tags, ","))
}) })
} }
@ -79,22 +97,31 @@ func mustWriteQuotesOds(quotes []*QuoteEntry, locale *Locale, company *Company)
}) })
} }
func mustWriteExpensesOds(expenses []*ExpenseEntry, locale *Locale, company *Company) []byte { func mustWriteExpensesOds(expenses []*ExpenseEntry, taxes map[int]taxMap, taxColumns map[int]string, locale *Locale, company *Company) []byte {
columns := []string{ taxIDs := extractTaxIDs(taxColumns)
"Contact", columns := make([]string, 7+len(taxIDs))
"Invoice Date", columns[0] = "Contact"
"Invoice Number", columns[1] = "Invoice Date"
"Status", columns[2] = "Invoice Number"
"Tags", columns[3] = "Status"
"Amount", columns[4] = "Amount"
i := 5
for _, taxID := range taxIDs {
columns[i] = taxColumns[taxID]
i++
} }
columns[i] = "Total"
columns[i+1] = "Tags"
return mustWriteTableOds(expenses, columns, locale, func(sb *strings.Builder, expense *ExpenseEntry) { return mustWriteTableOds(expenses, columns, locale, func(sb *strings.Builder, expense *ExpenseEntry) {
writeCellString(sb, expense.InvoicerName) writeCellString(sb, expense.InvoicerName)
writeCellDate(sb, expense.InvoiceDate) writeCellDate(sb, expense.InvoiceDate)
writeCellString(sb, expense.InvoiceNumber) writeCellString(sb, expense.InvoiceNumber)
writeCellString(sb, expense.StatusLabel) writeCellString(sb, expense.StatusLabel)
writeCellString(sb, strings.Join(expense.Tags, ","))
writeCellFloat(sb, expense.Amount, locale, company) writeCellFloat(sb, expense.Amount, locale, company)
writeTaxes(sb, taxes[expense.ID], taxIDs, locale, company)
writeCellFloat(sb, expense.Total, locale, company)
writeCellString(sb, strings.Join(expense.Tags, ","))
}) })
} }
@ -211,3 +238,13 @@ func writeOdsResponse(w http.ResponseWriter, ods []byte, filename string) {
panic(err) panic(err)
} }
} }
func writeTaxes(sb *strings.Builder, taxes taxMap, taxIDs []int, locale *Locale, company *Company) {
for _, taxID := range taxIDs {
var amount string
if taxes != nil {
amount = taxes[taxID]
}
writeCellFloat(sb, amount, locale, company)
}
}

148
po/ca.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-01-19 22:33+0100\n" "POT-Creation-Date: 2024-01-26 02:25+0100\n"
"PO-Revision-Date: 2023-01-18 17:08+0100\n" "PO-Revision-Date: 2023-01-18 17:08+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n" "Language-Team: Catalan <ca@dodds.net>\n"
@ -288,34 +288,34 @@ msgctxt "title"
msgid "Edit Invoice “%s”" msgid "Edit Invoice “%s”"
msgstr "Edició de la factura «%s»" msgstr "Edició de la factura «%s»"
#: web/template/web.gohtml:14 #: web/template/web.gohtml:15
msgctxt "link" msgctxt "link"
msgid "Login" msgid "Login"
msgstr "Entrada" msgstr "Entrada"
#: web/template/web.gohtml:15 #: web/template/web.gohtml:16
msgctxt "link" msgctxt "link"
msgid "Demo" msgid "Demo"
msgstr "Demo" msgstr "Demo"
#: web/template/web.gohtml:16 #: web/template/web.gohtml:17
msgctxt "link" msgctxt "link"
msgid "Code" msgid "Code"
msgstr "Codi" msgstr "Codi"
#: web/template/web.gohtml:27 web/template/legal.gohtml:2 #: web/template/web.gohtml:28 web/template/legal.gohtml:2
#: web/template/legal.gohtml:7 #: web/template/legal.gohtml:7
msgctxt "title" msgctxt "title"
msgid "Legal Disclaimer" msgid "Legal Disclaimer"
msgstr "Avís legal" msgstr "Avís legal"
#: web/template/web.gohtml:28 web/template/privacy.gohtml:2 #: web/template/web.gohtml:29 web/template/privacy.gohtml:2
#: web/template/privacy.gohtml:7 #: web/template/privacy.gohtml:7
msgctxt "title" msgctxt "title"
msgid "Privacy Policy" msgid "Privacy Policy"
msgstr "Política de privacitat" msgstr "Política de privacitat"
#: web/template/web.gohtml:29 web/template/cookies.gohtml:2 #: web/template/web.gohtml:30 web/template/cookies.gohtml:2
#: web/template/cookies.gohtml:7 #: web/template/cookies.gohtml:7
msgctxt "title" msgctxt "title"
msgid "Cookies Policy" msgid "Cookies Policy"
@ -741,100 +741,100 @@ msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contrasenya" msgstr "Contrasenya"
#: pkg/login.go:70 pkg/company.go:283 pkg/profile.go:89 #: pkg/login.go:75 pkg/company.go:283 pkg/profile.go:89
msgid "Email can not be empty." msgid "Email can not be empty."
msgstr "No podeu deixar el correu-e en blanc." msgstr "No podeu deixar el correu-e en blanc."
#: pkg/login.go:71 pkg/company.go:284 pkg/profile.go:90 pkg/contacts.go:420 #: pkg/login.go:76 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." 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." msgstr "Aquest valor no és un correu-e vàlid. Hauria de ser similar a nom@domini.cat."
#: pkg/login.go:73 #: pkg/login.go:78
msgid "Password can not be empty." msgid "Password can not be empty."
msgstr "No podeu deixar la contrasenya en blanc." msgstr "No podeu deixar la contrasenya en blanc."
#: pkg/login.go:109 #: pkg/login.go:114
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nom dusuari o contrasenya incorrectes." msgstr "Nom dusuari o contrasenya incorrectes."
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901 #: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901
#: pkg/invoices.go:1016 pkg/contacts.go:149 pkg/contacts.go:262 #: pkg/invoices.go:1109 pkg/contacts.go:149 pkg/contacts.go:262
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708 #: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
#: pkg/expenses.go:340 pkg/expenses.go:508 pkg/invoices.go:174 #: pkg/expenses.go:343 pkg/expenses.go:511 pkg/invoices.go:177
#: pkg/invoices.go:746 pkg/invoices.go:1331 pkg/contacts.go:154 #: pkg/invoices.go:839 pkg/invoices.go:1424 pkg/contacts.go:154
#: pkg/contacts.go:362 #: pkg/contacts.go:362
msgctxt "input" msgctxt "input"
msgid "Tags" msgid "Tags"
msgstr "Etiquetes" msgstr "Etiquetes"
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:518 pkg/invoices.go:178 #: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:521 pkg/invoices.go:181
#: pkg/contacts.go:158 #: pkg/contacts.go:158
msgctxt "input" msgctxt "input"
msgid "Tags Condition" msgid "Tags Condition"
msgstr "Condició de les etiquetes" msgstr "Condició de les etiquetes"
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:522 pkg/invoices.go:182 #: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:525 pkg/invoices.go:185
#: pkg/contacts.go:162 #: pkg/contacts.go:162
msgctxt "tag condition" msgctxt "tag condition"
msgid "All" msgid "All"
msgstr "Totes" msgstr "Totes"
#: pkg/products.go:186 pkg/expenses.go:523 pkg/invoices.go:183 #: pkg/products.go:186 pkg/expenses.go:526 pkg/invoices.go:186
#: pkg/contacts.go:163 #: pkg/contacts.go:163
msgid "Invoices must have all the specified labels." msgid "Invoices must have all the specified labels."
msgstr "Les factures han de tenir totes les etiquetes." msgstr "Les factures han de tenir totes les etiquetes."
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:527 pkg/invoices.go:187 #: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:530 pkg/invoices.go:190
#: pkg/contacts.go:167 #: pkg/contacts.go:167
msgctxt "tag condition" msgctxt "tag condition"
msgid "Any" msgid "Any"
msgstr "Qualsevol" msgstr "Qualsevol"
#: pkg/products.go:191 pkg/expenses.go:528 pkg/invoices.go:188 #: pkg/products.go:191 pkg/expenses.go:531 pkg/invoices.go:191
#: pkg/contacts.go:168 #: pkg/contacts.go:168
msgid "Invoices must have at least one of the specified labels." msgid "Invoices must have at least one of the specified labels."
msgstr "Les factures han de tenir com a mínim una de les etiquetes." msgstr "Les factures han de tenir com a mínim una de les etiquetes."
#: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1030 #: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1123
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripció" msgstr "Descripció"
#: pkg/products.go:287 pkg/quote.go:919 pkg/invoices.go:1034 #: pkg/products.go:287 pkg/quote.go:919 pkg/invoices.go:1127
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Preu" msgstr "Preu"
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:308 #: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:311
#: pkg/invoices.go:1063 #: pkg/invoices.go:1156
msgctxt "input" msgctxt "input"
msgid "Taxes" msgid "Taxes"
msgstr "Imposts" msgstr "Imposts"
#: pkg/products.go:322 pkg/quote.go:997 pkg/profile.go:92 pkg/invoices.go:1112 #: pkg/products.go:322 pkg/quote.go:997 pkg/profile.go:92 pkg/invoices.go:1205
#: pkg/contacts.go:412 #: pkg/contacts.go:412
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc." msgstr "No podeu deixar el nom en blanc."
#: pkg/products.go:323 pkg/quote.go:998 pkg/invoices.go:1113 #: pkg/products.go:323 pkg/quote.go:998 pkg/invoices.go:1206
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc." msgstr "No podeu deixar el preu en blanc."
#: pkg/products.go:324 pkg/quote.go:999 pkg/invoices.go:1114 #: pkg/products.go:324 pkg/quote.go:999 pkg/invoices.go:1207
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El preu ha de ser un número major a zero." msgstr "El preu ha de ser un número major a zero."
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:376 #: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:379
#: pkg/invoices.go:1122 #: pkg/invoices.go:1215
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid." msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:377 #: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:380
#: pkg/invoices.go:1123 #: pkg/invoices.go:1216
msgid "You can only select a tax of each class." msgid "You can only select a tax of each class."
msgstr "Només podeu seleccionar un impost de cada classe." msgstr "Només podeu seleccionar un impost de cada classe."
@ -1043,12 +1043,12 @@ msgstr "No podeu deixar el nom del mètode de pagament en blanc."
msgid "Payment instructions can not be empty." msgid "Payment instructions can not be empty."
msgstr "No podeu deixar les instruccions de pagament en blanc." msgstr "No podeu deixar les instruccions de pagament en blanc."
#: pkg/quote.go:147 pkg/quote.go:686 pkg/invoices.go:147 pkg/invoices.go:729 #: pkg/quote.go:147 pkg/quote.go:686 pkg/invoices.go:150 pkg/invoices.go:822
msgctxt "input" msgctxt "input"
msgid "Customer" msgid "Customer"
msgstr "Client" msgstr "Client"
#: pkg/quote.go:148 pkg/invoices.go:148 #: pkg/quote.go:148 pkg/invoices.go:151
msgid "All customers" msgid "All customers"
msgstr "Tots els clients" msgstr "Tots els clients"
@ -1057,7 +1057,7 @@ msgctxt "input"
msgid "Quotation Status" msgid "Quotation Status"
msgstr "Estat del pressupost" msgstr "Estat del pressupost"
#: pkg/quote.go:154 pkg/expenses.go:513 pkg/invoices.go:154 #: pkg/quote.go:154 pkg/expenses.go:516 pkg/invoices.go:157
msgid "All status" msgid "All status"
msgstr "Tots els estats" msgstr "Tots els estats"
@ -1066,12 +1066,12 @@ msgctxt "input"
msgid "Quotation Number" msgid "Quotation Number"
msgstr "Número de pressupost" msgstr "Número de pressupost"
#: pkg/quote.go:164 pkg/expenses.go:498 pkg/invoices.go:164 #: pkg/quote.go:164 pkg/expenses.go:501 pkg/invoices.go:167
msgctxt "input" msgctxt "input"
msgid "From Date" msgid "From Date"
msgstr "A partir de la data" msgstr "A partir de la data"
#: pkg/quote.go:169 pkg/expenses.go:503 pkg/invoices.go:169 #: pkg/quote.go:169 pkg/expenses.go:506 pkg/invoices.go:172
msgctxt "input" msgctxt "input"
msgid "To Date" msgid "To Date"
msgstr "Fins la data" msgstr "Fins la data"
@ -1092,9 +1092,9 @@ msgstr "pressuposts.zip"
msgid "quotations.ods" msgid "quotations.ods"
msgstr "pressuposts.ods" msgstr "pressuposts.ods"
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:717 #: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:720
#: pkg/expenses.go:743 pkg/invoices.go:677 pkg/invoices.go:1306 #: pkg/expenses.go:749 pkg/invoices.go:683 pkg/invoices.go:1399
#: pkg/invoices.go:1314 #: pkg/invoices.go:1407
msgid "Invalid action" msgid "Invalid action"
msgstr "Acció invàlida." msgstr "Acció invàlida."
@ -1112,12 +1112,12 @@ msgctxt "input"
msgid "Terms and conditions" msgid "Terms and conditions"
msgstr "Condicions dacceptació" msgstr "Condicions dacceptació"
#: pkg/quote.go:703 pkg/invoices.go:741 #: pkg/quote.go:703 pkg/invoices.go:834
msgctxt "input" msgctxt "input"
msgid "Notes" msgid "Notes"
msgstr "Notes" msgstr "Notes"
#: pkg/quote.go:712 pkg/invoices.go:751 #: pkg/quote.go:712 pkg/invoices.go:844
msgctxt "input" msgctxt "input"
msgid "Payment Method" msgid "Payment Method"
msgstr "Mètode de pagament" msgstr "Mètode de pagament"
@ -1130,7 +1130,7 @@ msgstr "Escolliu un mètode de pagament."
msgid "Selected quotation status is not valid." msgid "Selected quotation status is not valid."
msgstr "Heu seleccionat un estat de pressupost que no és vàlid." msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
#: pkg/quote.go:751 pkg/invoices.go:806 #: pkg/quote.go:751 pkg/invoices.go:899
msgid "Selected customer is not valid." msgid "Selected customer is not valid."
msgstr "Heu seleccionat un client que no és vàlid." msgstr "Heu seleccionat un client que no és vàlid."
@ -1142,21 +1142,21 @@ msgstr "No podeu deixar la data del pressupost en blanc."
msgid "Quotation date must be a valid date." msgid "Quotation date must be a valid date."
msgstr "La data del pressupost ha de ser vàlida." msgstr "La data del pressupost ha de ser vàlida."
#: pkg/quote.go:757 pkg/invoices.go:810 #: pkg/quote.go:757 pkg/invoices.go:903
msgid "Selected payment method is not valid." msgid "Selected payment method is not valid."
msgstr "Heu seleccionat un mètode de pagament que no és vàlid." msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
#: pkg/quote.go:891 pkg/quote.go:896 pkg/invoices.go:1006 pkg/invoices.go:1011 #: pkg/quote.go:891 pkg/quote.go:896 pkg/invoices.go:1099 pkg/invoices.go:1104
msgctxt "input" msgctxt "input"
msgid "Id" msgid "Id"
msgstr "Identificador" msgstr "Identificador"
#: pkg/quote.go:929 pkg/invoices.go:1044 #: pkg/quote.go:929 pkg/invoices.go:1137
msgctxt "input" msgctxt "input"
msgid "Quantity" msgid "Quantity"
msgstr "Quantitat" msgstr "Quantitat"
#: pkg/quote.go:938 pkg/invoices.go:1053 #: pkg/quote.go:938 pkg/invoices.go:1146
msgctxt "input" msgctxt "input"
msgid "Discount (%)" msgid "Discount (%)"
msgstr "Descompte (%)" msgstr "Descompte (%)"
@ -1165,23 +1165,23 @@ msgstr "Descompte (%)"
msgid "Quotation product ID must be a number greater than zero." msgid "Quotation product ID must be a number greater than zero."
msgstr "LID del producte de pressupost ha de ser un número major a zero." msgstr "LID del producte de pressupost ha de ser un número major a zero."
#: pkg/quote.go:995 pkg/invoices.go:1110 #: pkg/quote.go:995 pkg/invoices.go:1203
msgid "Product ID must be a positive number or zero." msgid "Product ID must be a positive number or zero."
msgstr "LID del producte ha de ser un número positiu o zero." msgstr "LID del producte ha de ser un número positiu o zero."
#: pkg/quote.go:1001 pkg/invoices.go:1116 #: pkg/quote.go:1001 pkg/invoices.go:1209
msgid "Quantity can not be empty." msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc." msgstr "No podeu deixar la quantitat en blanc."
#: pkg/quote.go:1002 pkg/invoices.go:1117 #: pkg/quote.go:1002 pkg/invoices.go:1210
msgid "Quantity must be a number greater than zero." msgid "Quantity must be a number greater than zero."
msgstr "La quantitat ha de ser un número major a zero." msgstr "La quantitat ha de ser un número major a zero."
#: pkg/quote.go:1004 pkg/invoices.go:1119 #: pkg/quote.go:1004 pkg/invoices.go:1212
msgid "Discount can not be empty." msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc." msgstr "No podeu deixar el descompte en blanc."
#: pkg/quote.go:1005 pkg/invoices.go:1120 #: pkg/quote.go:1005 pkg/invoices.go:1213
msgid "Discount must be a percentage between 0 and 100." msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100." msgstr "El descompte ha de ser un percentatge entre 0 i 100."
@ -1248,109 +1248,109 @@ msgctxt "period option"
msgid "Previous year" msgid "Previous year"
msgstr "Any anterior" msgstr "Any anterior"
#: pkg/expenses.go:234 #: pkg/expenses.go:237
msgid "Select a contact." msgid "Select a contact."
msgstr "Escolliu un contacte." msgstr "Escolliu un contacte."
#: pkg/expenses.go:291 pkg/expenses.go:487 #: pkg/expenses.go:294 pkg/expenses.go:490
msgctxt "input" msgctxt "input"
msgid "Contact" msgid "Contact"
msgstr "Contacte" msgstr "Contacte"
#: pkg/expenses.go:297 #: pkg/expenses.go:300
msgctxt "input" msgctxt "input"
msgid "Invoice number" msgid "Invoice number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/expenses.go:302 pkg/invoices.go:735 #: pkg/expenses.go:305 pkg/invoices.go:828
msgctxt "input" msgctxt "input"
msgid "Invoice Date" msgid "Invoice Date"
msgstr "Data de factura" msgstr "Data de factura"
#: pkg/expenses.go:317 #: pkg/expenses.go:320
msgctxt "input" msgctxt "input"
msgid "Amount" msgid "Amount"
msgstr "Import" msgstr "Import"
#: pkg/expenses.go:328 pkg/invoices.go:757 #: pkg/expenses.go:331 pkg/invoices.go:850
msgctxt "input" msgctxt "input"
msgid "File" msgid "File"
msgstr "Fitxer" msgstr "Fitxer"
#: pkg/expenses.go:334 pkg/expenses.go:512 #: pkg/expenses.go:337 pkg/expenses.go:515
msgctxt "input" msgctxt "input"
msgid "Expense Status" msgid "Expense Status"
msgstr "Estat de la despesa" msgstr "Estat de la despesa"
#: pkg/expenses.go:374 #: pkg/expenses.go:377
msgid "Selected contact is not valid." msgid "Selected contact is not valid."
msgstr "Heu seleccionat un contacte que no és vàlid." msgstr "Heu seleccionat un contacte que no és vàlid."
#: pkg/expenses.go:375 pkg/invoices.go:808 #: pkg/expenses.go:378 pkg/invoices.go:901
msgid "Invoice date must be a valid date." msgid "Invoice date must be a valid date."
msgstr "La data de facturació ha de ser vàlida." msgstr "La data de facturació ha de ser vàlida."
#: pkg/expenses.go:378 #: pkg/expenses.go:381
msgid "Amount can not be empty." msgid "Amount can not be empty."
msgstr "No podeu deixar limport en blanc." msgstr "No podeu deixar limport en blanc."
#: pkg/expenses.go:379 #: pkg/expenses.go:382
msgid "Amount must be a number greater than zero." msgid "Amount must be a number greater than zero."
msgstr "Limport ha de ser un número major a zero." msgstr "Limport ha de ser un número major a zero."
#: pkg/expenses.go:381 #: pkg/expenses.go:384
msgid "Selected expense status is not valid." msgid "Selected expense status is not valid."
msgstr "Heu seleccionat un estat de despesa que no és vàlid." msgstr "Heu seleccionat un estat de despesa que no és vàlid."
#: pkg/expenses.go:488 #: pkg/expenses.go:491
msgid "All contacts" msgid "All contacts"
msgstr "Tots els contactes" msgstr "Tots els contactes"
#: pkg/expenses.go:493 pkg/invoices.go:159 #: pkg/expenses.go:496 pkg/invoices.go:162
msgctxt "input" msgctxt "input"
msgid "Invoice Number" msgid "Invoice Number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/expenses.go:741 #: pkg/expenses.go:747
msgid "expenses.ods" msgid "expenses.ods"
msgstr "despeses.ods" msgstr "despeses.ods"
#: pkg/invoices.go:153 pkg/invoices.go:723 #: pkg/invoices.go:156 pkg/invoices.go:816
msgctxt "input" msgctxt "input"
msgid "Invoice Status" msgid "Invoice Status"
msgstr "Estat de la factura" msgstr "Estat de la factura"
#: pkg/invoices.go:557 #: pkg/invoices.go:560
msgid "Select a customer to bill." msgid "Select a customer to bill."
msgstr "Escolliu un client a facturar." msgstr "Escolliu un client a facturar."
#: pkg/invoices.go:661 #: pkg/invoices.go:664
msgid "invoices.zip" msgid "invoices.zip"
msgstr "factures.zip" msgstr "factures.zip"
#: pkg/invoices.go:675 #: pkg/invoices.go:681
msgid "invoices.ods" msgid "invoices.ods"
msgstr "factures.ods" msgstr "factures.ods"
#: pkg/invoices.go:805 #: pkg/invoices.go:898
msgid "Selected invoice status is not valid." msgid "Selected invoice status is not valid."
msgstr "Heu seleccionat un estat de factura que no és vàlid." msgstr "Heu seleccionat un estat de factura que no és vàlid."
#: pkg/invoices.go:807 #: pkg/invoices.go:900
msgid "Invoice date can not be empty." msgid "Invoice date can not be empty."
msgstr "No podeu deixar la data de la factura en blanc." msgstr "No podeu deixar la data de la factura en blanc."
#: pkg/invoices.go:943 #: pkg/invoices.go:1036
#, c-format #, c-format
msgid "Re: quotation #%s of %s" msgid "Re: quotation #%s of %s"
msgstr "Ref: pressupost núm. %s del %s" msgstr "Ref: pressupost núm. %s del %s"
#: pkg/invoices.go:944 #: pkg/invoices.go:1037
msgctxt "to_char" msgctxt "to_char"
msgid "MM/DD/YYYY" msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY" msgstr "DD/MM/YYYY"
#: pkg/invoices.go:1107 #: pkg/invoices.go:1200
msgid "Invoice product ID must be a number greater than zero." msgid "Invoice product ID must be a number greater than zero."
msgstr "LID del producte de factura ha de ser un número major a zero." msgstr "LID del producte de factura ha de ser un número major a zero."

148
po/es.po
View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: numerus\n" "Project-Id-Version: numerus\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2024-01-19 22:33+0100\n" "POT-Creation-Date: 2024-01-26 02:25+0100\n"
"PO-Revision-Date: 2023-01-18 17:45+0100\n" "PO-Revision-Date: 2023-01-18 17:45+0100\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n" "Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n" "Language-Team: Spanish <es@tp.org.es>\n"
@ -288,34 +288,34 @@ msgctxt "title"
msgid "Edit Invoice “%s”" msgid "Edit Invoice “%s”"
msgstr "Edición de la factura «%s»" msgstr "Edición de la factura «%s»"
#: web/template/web.gohtml:14 #: web/template/web.gohtml:15
msgctxt "link" msgctxt "link"
msgid "Login" msgid "Login"
msgstr "Entrada" msgstr "Entrada"
#: web/template/web.gohtml:15 #: web/template/web.gohtml:16
msgctxt "link" msgctxt "link"
msgid "Demo" msgid "Demo"
msgstr "Demo" msgstr "Demo"
#: web/template/web.gohtml:16 #: web/template/web.gohtml:17
msgctxt "link" msgctxt "link"
msgid "Code" msgid "Code"
msgstr "Código" msgstr "Código"
#: web/template/web.gohtml:27 web/template/legal.gohtml:2 #: web/template/web.gohtml:28 web/template/legal.gohtml:2
#: web/template/legal.gohtml:7 #: web/template/legal.gohtml:7
msgctxt "title" msgctxt "title"
msgid "Legal Disclaimer" msgid "Legal Disclaimer"
msgstr "Aviso legal" msgstr "Aviso legal"
#: web/template/web.gohtml:28 web/template/privacy.gohtml:2 #: web/template/web.gohtml:29 web/template/privacy.gohtml:2
#: web/template/privacy.gohtml:7 #: web/template/privacy.gohtml:7
msgctxt "title" msgctxt "title"
msgid "Privacy Policy" msgid "Privacy Policy"
msgstr "Política de privacidad" msgstr "Política de privacidad"
#: web/template/web.gohtml:29 web/template/cookies.gohtml:2 #: web/template/web.gohtml:30 web/template/cookies.gohtml:2
#: web/template/cookies.gohtml:7 #: web/template/cookies.gohtml:7
msgctxt "title" msgctxt "title"
msgid "Cookies Policy" msgid "Cookies Policy"
@ -741,100 +741,100 @@ msgctxt "input"
msgid "Password" msgid "Password"
msgstr "Contraseña" msgstr "Contraseña"
#: pkg/login.go:70 pkg/company.go:283 pkg/profile.go:89 #: pkg/login.go:75 pkg/company.go:283 pkg/profile.go:89
msgid "Email can not be empty." msgid "Email can not be empty."
msgstr "No podéis dejar el correo-e en blanco." 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:420 #: pkg/login.go:76 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." 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." msgstr "Este valor no es un correo-e válido. Tiene que ser parecido a nombre@dominio.es."
#: pkg/login.go:73 #: pkg/login.go:78
msgid "Password can not be empty." msgid "Password can not be empty."
msgstr "No podéis dejar la contraseña en blanco." msgstr "No podéis dejar la contraseña en blanco."
#: pkg/login.go:109 #: pkg/login.go:114
msgid "Invalid user or password." msgid "Invalid user or password."
msgstr "Nombre de usuario o contraseña inválido." msgstr "Nombre de usuario o contraseña inválido."
#: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901 #: pkg/products.go:172 pkg/products.go:276 pkg/quote.go:901
#: pkg/invoices.go:1016 pkg/contacts.go:149 pkg/contacts.go:262 #: pkg/invoices.go:1109 pkg/contacts.go:149 pkg/contacts.go:262
msgctxt "input" msgctxt "input"
msgid "Name" msgid "Name"
msgstr "Nombre" msgstr "Nombre"
#: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708 #: pkg/products.go:177 pkg/products.go:303 pkg/quote.go:174 pkg/quote.go:708
#: pkg/expenses.go:340 pkg/expenses.go:508 pkg/invoices.go:174 #: pkg/expenses.go:343 pkg/expenses.go:511 pkg/invoices.go:177
#: pkg/invoices.go:746 pkg/invoices.go:1331 pkg/contacts.go:154 #: pkg/invoices.go:839 pkg/invoices.go:1424 pkg/contacts.go:154
#: pkg/contacts.go:362 #: pkg/contacts.go:362
msgctxt "input" msgctxt "input"
msgid "Tags" msgid "Tags"
msgstr "Etiquetes" msgstr "Etiquetes"
#: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:518 pkg/invoices.go:178 #: pkg/products.go:181 pkg/quote.go:178 pkg/expenses.go:521 pkg/invoices.go:181
#: pkg/contacts.go:158 #: pkg/contacts.go:158
msgctxt "input" msgctxt "input"
msgid "Tags Condition" msgid "Tags Condition"
msgstr "Condición de las etiquetas" msgstr "Condición de las etiquetas"
#: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:522 pkg/invoices.go:182 #: pkg/products.go:185 pkg/quote.go:182 pkg/expenses.go:525 pkg/invoices.go:185
#: pkg/contacts.go:162 #: pkg/contacts.go:162
msgctxt "tag condition" msgctxt "tag condition"
msgid "All" msgid "All"
msgstr "Todas" msgstr "Todas"
#: pkg/products.go:186 pkg/expenses.go:523 pkg/invoices.go:183 #: pkg/products.go:186 pkg/expenses.go:526 pkg/invoices.go:186
#: pkg/contacts.go:163 #: pkg/contacts.go:163
msgid "Invoices must have all the specified labels." msgid "Invoices must have all the specified labels."
msgstr "Las facturas deben tener todas las etiquetas." msgstr "Las facturas deben tener todas las etiquetas."
#: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:527 pkg/invoices.go:187 #: pkg/products.go:190 pkg/quote.go:187 pkg/expenses.go:530 pkg/invoices.go:190
#: pkg/contacts.go:167 #: pkg/contacts.go:167
msgctxt "tag condition" msgctxt "tag condition"
msgid "Any" msgid "Any"
msgstr "Cualquiera" msgstr "Cualquiera"
#: pkg/products.go:191 pkg/expenses.go:528 pkg/invoices.go:188 #: pkg/products.go:191 pkg/expenses.go:531 pkg/invoices.go:191
#: pkg/contacts.go:168 #: pkg/contacts.go:168
msgid "Invoices must have at least one of the specified labels." msgid "Invoices must have at least one of the specified labels."
msgstr "Las facturas deben tener como mínimo una de las etiquetas." msgstr "Las facturas deben tener como mínimo una de las etiquetas."
#: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1030 #: pkg/products.go:282 pkg/quote.go:915 pkg/invoices.go:1123
msgctxt "input" msgctxt "input"
msgid "Description" msgid "Description"
msgstr "Descripción" msgstr "Descripción"
#: pkg/products.go:287 pkg/quote.go:919 pkg/invoices.go:1034 #: pkg/products.go:287 pkg/quote.go:919 pkg/invoices.go:1127
msgctxt "input" msgctxt "input"
msgid "Price" msgid "Price"
msgstr "Precio" msgstr "Precio"
#: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:308 #: pkg/products.go:297 pkg/quote.go:948 pkg/expenses.go:311
#: pkg/invoices.go:1063 #: pkg/invoices.go:1156
msgctxt "input" msgctxt "input"
msgid "Taxes" msgid "Taxes"
msgstr "Impuestos" msgstr "Impuestos"
#: pkg/products.go:322 pkg/quote.go:997 pkg/profile.go:92 pkg/invoices.go:1112 #: pkg/products.go:322 pkg/quote.go:997 pkg/profile.go:92 pkg/invoices.go:1205
#: pkg/contacts.go:412 #: pkg/contacts.go:412
msgid "Name can not be empty." msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco." msgstr "No podéis dejar el nombre en blanco."
#: pkg/products.go:323 pkg/quote.go:998 pkg/invoices.go:1113 #: pkg/products.go:323 pkg/quote.go:998 pkg/invoices.go:1206
msgid "Price can not be empty." msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco." msgstr "No podéis dejar el precio en blanco."
#: pkg/products.go:324 pkg/quote.go:999 pkg/invoices.go:1114 #: pkg/products.go:324 pkg/quote.go:999 pkg/invoices.go:1207
msgid "Price must be a number greater than zero." msgid "Price must be a number greater than zero."
msgstr "El precio tiene que ser un número mayor a cero." msgstr "El precio tiene que ser un número mayor a cero."
#: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:376 #: pkg/products.go:326 pkg/quote.go:1007 pkg/expenses.go:379
#: pkg/invoices.go:1122 #: pkg/invoices.go:1215
msgid "Selected tax is not valid." msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido." msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:377 #: pkg/products.go:327 pkg/quote.go:1008 pkg/expenses.go:380
#: pkg/invoices.go:1123 #: pkg/invoices.go:1216
msgid "You can only select a tax of each class." msgid "You can only select a tax of each class."
msgstr "Solo podéis escoger un impuesto de cada clase." msgstr "Solo podéis escoger un impuesto de cada clase."
@ -1043,12 +1043,12 @@ msgstr "No podéis dejar el nombre del método de pago en blanco."
msgid "Payment instructions can not be empty." msgid "Payment instructions can not be empty."
msgstr "No podéis dejar las instrucciones de pago en blanco." msgstr "No podéis dejar las instrucciones de pago en blanco."
#: pkg/quote.go:147 pkg/quote.go:686 pkg/invoices.go:147 pkg/invoices.go:729 #: pkg/quote.go:147 pkg/quote.go:686 pkg/invoices.go:150 pkg/invoices.go:822
msgctxt "input" msgctxt "input"
msgid "Customer" msgid "Customer"
msgstr "Cliente" msgstr "Cliente"
#: pkg/quote.go:148 pkg/invoices.go:148 #: pkg/quote.go:148 pkg/invoices.go:151
msgid "All customers" msgid "All customers"
msgstr "Todos los clientes" msgstr "Todos los clientes"
@ -1057,7 +1057,7 @@ msgctxt "input"
msgid "Quotation Status" msgid "Quotation Status"
msgstr "Estado del presupuesto" msgstr "Estado del presupuesto"
#: pkg/quote.go:154 pkg/expenses.go:513 pkg/invoices.go:154 #: pkg/quote.go:154 pkg/expenses.go:516 pkg/invoices.go:157
msgid "All status" msgid "All status"
msgstr "Todos los estados" msgstr "Todos los estados"
@ -1066,12 +1066,12 @@ msgctxt "input"
msgid "Quotation Number" msgid "Quotation Number"
msgstr "Número de presupuesto" msgstr "Número de presupuesto"
#: pkg/quote.go:164 pkg/expenses.go:498 pkg/invoices.go:164 #: pkg/quote.go:164 pkg/expenses.go:501 pkg/invoices.go:167
msgctxt "input" msgctxt "input"
msgid "From Date" msgid "From Date"
msgstr "A partir de la fecha" msgstr "A partir de la fecha"
#: pkg/quote.go:169 pkg/expenses.go:503 pkg/invoices.go:169 #: pkg/quote.go:169 pkg/expenses.go:506 pkg/invoices.go:172
msgctxt "input" msgctxt "input"
msgid "To Date" msgid "To Date"
msgstr "Hasta la fecha" msgstr "Hasta la fecha"
@ -1092,9 +1092,9 @@ msgstr "presupuestos.zip"
msgid "quotations.ods" msgid "quotations.ods"
msgstr "presupuestos.ods" msgstr "presupuestos.ods"
#: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:717 #: pkg/quote.go:634 pkg/quote.go:1176 pkg/quote.go:1184 pkg/expenses.go:720
#: pkg/expenses.go:743 pkg/invoices.go:677 pkg/invoices.go:1306 #: pkg/expenses.go:749 pkg/invoices.go:683 pkg/invoices.go:1399
#: pkg/invoices.go:1314 #: pkg/invoices.go:1407
msgid "Invalid action" msgid "Invalid action"
msgstr "Acción inválida." msgstr "Acción inválida."
@ -1112,12 +1112,12 @@ msgctxt "input"
msgid "Terms and conditions" msgid "Terms and conditions"
msgstr "Condiciones de aceptación" msgstr "Condiciones de aceptación"
#: pkg/quote.go:703 pkg/invoices.go:741 #: pkg/quote.go:703 pkg/invoices.go:834
msgctxt "input" msgctxt "input"
msgid "Notes" msgid "Notes"
msgstr "Notas" msgstr "Notas"
#: pkg/quote.go:712 pkg/invoices.go:751 #: pkg/quote.go:712 pkg/invoices.go:844
msgctxt "input" msgctxt "input"
msgid "Payment Method" msgid "Payment Method"
msgstr "Método de pago" msgstr "Método de pago"
@ -1130,7 +1130,7 @@ msgstr "Escoged un método e pago."
msgid "Selected quotation status is not valid." msgid "Selected quotation status is not valid."
msgstr "Habéis escogido un estado de presupuesto que no es válido." msgstr "Habéis escogido un estado de presupuesto que no es válido."
#: pkg/quote.go:751 pkg/invoices.go:806 #: pkg/quote.go:751 pkg/invoices.go:899
msgid "Selected customer is not valid." msgid "Selected customer is not valid."
msgstr "Habéis escogido un cliente que no es válido." msgstr "Habéis escogido un cliente que no es válido."
@ -1142,21 +1142,21 @@ msgstr "No podéis dejar la fecha del presupuesto en blanco."
msgid "Quotation date must be a valid date." msgid "Quotation date must be a valid date."
msgstr "La fecha de presupuesto debe ser válida." msgstr "La fecha de presupuesto debe ser válida."
#: pkg/quote.go:757 pkg/invoices.go:810 #: pkg/quote.go:757 pkg/invoices.go:903
msgid "Selected payment method is not valid." msgid "Selected payment method is not valid."
msgstr "Habéis escogido un método de pago que no es válido." msgstr "Habéis escogido un método de pago que no es válido."
#: pkg/quote.go:891 pkg/quote.go:896 pkg/invoices.go:1006 pkg/invoices.go:1011 #: pkg/quote.go:891 pkg/quote.go:896 pkg/invoices.go:1099 pkg/invoices.go:1104
msgctxt "input" msgctxt "input"
msgid "Id" msgid "Id"
msgstr "Identificador" msgstr "Identificador"
#: pkg/quote.go:929 pkg/invoices.go:1044 #: pkg/quote.go:929 pkg/invoices.go:1137
msgctxt "input" msgctxt "input"
msgid "Quantity" msgid "Quantity"
msgstr "Cantidad" msgstr "Cantidad"
#: pkg/quote.go:938 pkg/invoices.go:1053 #: pkg/quote.go:938 pkg/invoices.go:1146
msgctxt "input" msgctxt "input"
msgid "Discount (%)" msgid "Discount (%)"
msgstr "Descuento (%)" msgstr "Descuento (%)"
@ -1165,23 +1165,23 @@ msgstr "Descuento (%)"
msgid "Quotation product ID must be a number greater than zero." msgid "Quotation product ID must be a number greater than zero."
msgstr "El ID de producto de presupuesto tiene que ser un número mayor a cero." msgstr "El ID de producto de presupuesto tiene que ser un número mayor a cero."
#: pkg/quote.go:995 pkg/invoices.go:1110 #: pkg/quote.go:995 pkg/invoices.go:1203
msgid "Product ID must be a positive number or zero." msgid "Product ID must be a positive number or zero."
msgstr "El ID de producto tiene que ser un número positivo o cero." msgstr "El ID de producto tiene que ser un número positivo o cero."
#: pkg/quote.go:1001 pkg/invoices.go:1116 #: pkg/quote.go:1001 pkg/invoices.go:1209
msgid "Quantity can not be empty." msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco." msgstr "No podéis dejar la cantidad en blanco."
#: pkg/quote.go:1002 pkg/invoices.go:1117 #: pkg/quote.go:1002 pkg/invoices.go:1210
msgid "Quantity must be a number greater than zero." msgid "Quantity must be a number greater than zero."
msgstr "La cantidad tiene que ser un número mayor a cero." msgstr "La cantidad tiene que ser un número mayor a cero."
#: pkg/quote.go:1004 pkg/invoices.go:1119 #: pkg/quote.go:1004 pkg/invoices.go:1212
msgid "Discount can not be empty." msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco." msgstr "No podéis dejar el descuento en blanco."
#: pkg/quote.go:1005 pkg/invoices.go:1120 #: pkg/quote.go:1005 pkg/invoices.go:1213
msgid "Discount must be a percentage between 0 and 100." msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100." msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
@ -1248,109 +1248,109 @@ msgctxt "period option"
msgid "Previous year" msgid "Previous year"
msgstr "Año anterior" msgstr "Año anterior"
#: pkg/expenses.go:234 #: pkg/expenses.go:237
msgid "Select a contact." msgid "Select a contact."
msgstr "Escoged un contacto" msgstr "Escoged un contacto"
#: pkg/expenses.go:291 pkg/expenses.go:487 #: pkg/expenses.go:294 pkg/expenses.go:490
msgctxt "input" msgctxt "input"
msgid "Contact" msgid "Contact"
msgstr "Contacto" msgstr "Contacto"
#: pkg/expenses.go:297 #: pkg/expenses.go:300
msgctxt "input" msgctxt "input"
msgid "Invoice number" msgid "Invoice number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/expenses.go:302 pkg/invoices.go:735 #: pkg/expenses.go:305 pkg/invoices.go:828
msgctxt "input" msgctxt "input"
msgid "Invoice Date" msgid "Invoice Date"
msgstr "Fecha de factura" msgstr "Fecha de factura"
#: pkg/expenses.go:317 #: pkg/expenses.go:320
msgctxt "input" msgctxt "input"
msgid "Amount" msgid "Amount"
msgstr "Importe" msgstr "Importe"
#: pkg/expenses.go:328 pkg/invoices.go:757 #: pkg/expenses.go:331 pkg/invoices.go:850
msgctxt "input" msgctxt "input"
msgid "File" msgid "File"
msgstr "Archivo" msgstr "Archivo"
#: pkg/expenses.go:334 pkg/expenses.go:512 #: pkg/expenses.go:337 pkg/expenses.go:515
msgctxt "input" msgctxt "input"
msgid "Expense Status" msgid "Expense Status"
msgstr "Estado del gasto" msgstr "Estado del gasto"
#: pkg/expenses.go:374 #: pkg/expenses.go:377
msgid "Selected contact is not valid." msgid "Selected contact is not valid."
msgstr "Habéis escogido un contacto que no es válido." msgstr "Habéis escogido un contacto que no es válido."
#: pkg/expenses.go:375 pkg/invoices.go:808 #: pkg/expenses.go:378 pkg/invoices.go:901
msgid "Invoice date must be a valid date." msgid "Invoice date must be a valid date."
msgstr "La fecha de factura debe ser válida." msgstr "La fecha de factura debe ser válida."
#: pkg/expenses.go:378 #: pkg/expenses.go:381
msgid "Amount can not be empty." msgid "Amount can not be empty."
msgstr "No podéis dejar el importe en blanco." msgstr "No podéis dejar el importe en blanco."
#: pkg/expenses.go:379 #: pkg/expenses.go:382
msgid "Amount must be a number greater than zero." msgid "Amount must be a number greater than zero."
msgstr "El importe tiene que ser un número mayor a cero." msgstr "El importe tiene que ser un número mayor a cero."
#: pkg/expenses.go:381 #: pkg/expenses.go:384
msgid "Selected expense status is not valid." msgid "Selected expense status is not valid."
msgstr "Habéis escogido un estado de gasto que no es válido." msgstr "Habéis escogido un estado de gasto que no es válido."
#: pkg/expenses.go:488 #: pkg/expenses.go:491
msgid "All contacts" msgid "All contacts"
msgstr "Todos los contactos" msgstr "Todos los contactos"
#: pkg/expenses.go:493 pkg/invoices.go:159 #: pkg/expenses.go:496 pkg/invoices.go:162
msgctxt "input" msgctxt "input"
msgid "Invoice Number" msgid "Invoice Number"
msgstr "Número de factura" msgstr "Número de factura"
#: pkg/expenses.go:741 #: pkg/expenses.go:747
msgid "expenses.ods" msgid "expenses.ods"
msgstr "gastos.ods" msgstr "gastos.ods"
#: pkg/invoices.go:153 pkg/invoices.go:723 #: pkg/invoices.go:156 pkg/invoices.go:816
msgctxt "input" msgctxt "input"
msgid "Invoice Status" msgid "Invoice Status"
msgstr "Estado de la factura" msgstr "Estado de la factura"
#: pkg/invoices.go:557 #: pkg/invoices.go:560
msgid "Select a customer to bill." msgid "Select a customer to bill."
msgstr "Escoged un cliente a facturar." msgstr "Escoged un cliente a facturar."
#: pkg/invoices.go:661 #: pkg/invoices.go:664
msgid "invoices.zip" msgid "invoices.zip"
msgstr "facturas.zip" msgstr "facturas.zip"
#: pkg/invoices.go:675 #: pkg/invoices.go:681
msgid "invoices.ods" msgid "invoices.ods"
msgstr "facturas.ods" msgstr "facturas.ods"
#: pkg/invoices.go:805 #: pkg/invoices.go:898
msgid "Selected invoice status is not valid." msgid "Selected invoice status is not valid."
msgstr "Habéis escogido un estado de factura que no es válido." msgstr "Habéis escogido un estado de factura que no es válido."
#: pkg/invoices.go:807 #: pkg/invoices.go:900
msgid "Invoice date can not be empty." msgid "Invoice date can not be empty."
msgstr "No podéis dejar la fecha de la factura en blanco." msgstr "No podéis dejar la fecha de la factura en blanco."
#: pkg/invoices.go:943 #: pkg/invoices.go:1036
#, c-format #, c-format
msgid "Re: quotation #%s of %s" msgid "Re: quotation #%s of %s"
msgstr "Ref: presupuesto n.º %s del %s" msgstr "Ref: presupuesto n.º %s del %s"
#: pkg/invoices.go:944 #: pkg/invoices.go:1037
msgctxt "to_char" msgctxt "to_char"
msgid "MM/DD/YYYY" msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY" msgstr "DD/MM/YYYY"
#: pkg/invoices.go:1107 #: pkg/invoices.go:1200
msgid "Invoice product ID must be a number greater than zero." msgid "Invoice product ID must be a number greater than zero."
msgstr "El ID de producto de factura tiene que ser un número mayor a cero." msgstr "El ID de producto de factura tiene que ser un número mayor a cero."