Add total amount for quotes, invoices, and expenses tables

We have shown the application to a potential user, and they told us that
it would be very useful to have a total in the table’s footer, so that
they can verify the amount with the bank’s extracts.
This commit is contained in:
jordi fita mas 2023-06-20 11:33:28 +02:00
parent 8a4f80783d
commit 07c1071975
9 changed files with 367 additions and 263 deletions

View File

@ -23,8 +23,9 @@ type ExpenseEntry struct {
}
type expensesIndexPage struct {
Expenses []*ExpenseEntry
Filters *expenseFilterForm
Expenses []*ExpenseEntry
TotalAmount string
Filters *expenseFilterForm
}
func IndexExpenses(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
@ -37,42 +38,15 @@ func IndexExpenses(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
return
}
page := &expensesIndexPage{
Expenses: mustCollectExpenseEntries(r.Context(), conn, company, filters),
Filters: filters,
Expenses: mustCollectExpenseEntries(r.Context(), conn, company, filters),
TotalAmount: mustComputeExpensesTotalAmount(r.Context(), conn, filters),
Filters: filters,
}
mustRenderMainTemplate(w, r, "expenses/index.gohtml", page)
}
func mustCollectExpenseEntries(ctx context.Context, conn *Conn, company *Company, filters *expenseFilterForm) []*ExpenseEntry {
args := []interface{}{company.Id}
where := []string{"expense.company_id = $1"}
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
maybeAppendWhere("contact_id = $%d", filters.Contact.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(filters.Contact.Selected[0])
return customerId
})
maybeAppendWhere("invoice_number = $%d", filters.InvoiceNumber.String(), nil)
maybeAppendWhere("invoice_date >= $%d", filters.FromDate.String(), nil)
maybeAppendWhere("invoice_date <= $%d", filters.ToDate.String(), nil)
if len(filters.Tags.Tags) > 0 {
if filters.TagsCondition.Selected == "and" {
appendWhere("expense.tags @> $%d", filters.Tags)
} else {
appendWhere("expense.tags && $%d", filters.Tags)
}
}
where, args := filters.BuildQuery(nil)
rows := conn.MustQuery(ctx, fmt.Sprintf(`
select expense.slug
, invoice_date
@ -87,7 +61,7 @@ func mustCollectExpenseEntries(ctx context.Context, conn *Conn, company *Company
join currency using (currency_code)
where (%s)
order by invoice_date
`, strings.Join(where, ") AND (")), args...)
`, where), args...)
defer rows.Close()
var entries []*ExpenseEntry
@ -104,6 +78,18 @@ func mustCollectExpenseEntries(ctx context.Context, conn *Conn, company *Company
return entries
}
func mustComputeExpensesTotalAmount(ctx context.Context, conn *Conn, filters *expenseFilterForm) string {
where, args := filters.BuildQuery(nil)
return conn.MustGetText(ctx, "0", fmt.Sprintf(`
select to_price(sum(amount)::integer, decimal_digits)
from expense
join currency using (currency_code)
where (%s)
group by decimal_digits
`, where), args...)
}
func ServeExpenseForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
locale := getLocale(r)
conn := getConn(r)
@ -391,6 +377,41 @@ func (form *expenseFilterForm) Parse(r *http.Request) error {
return nil
}
func (form *expenseFilterForm) BuildQuery(args []interface{}) (string, []interface{}) {
var where []string
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
appendWhere("expense.company_id = $%d", form.company.Id)
maybeAppendWhere("contact_id = $%d", form.Contact.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(form.Contact.Selected[0])
return customerId
})
maybeAppendWhere("invoice_number = $%d", form.InvoiceNumber.String(), nil)
maybeAppendWhere("invoice_date >= $%d", form.FromDate.String(), nil)
maybeAppendWhere("invoice_date <= $%d", form.ToDate.String(), nil)
if len(form.Tags.Tags) > 0 {
if form.TagsCondition.Selected == "and" {
appendWhere("expense.tags @> $%d", form.Tags)
} else {
appendWhere("expense.tags && $%d", form.Tags)
}
}
return strings.Join(where, ") AND ("), args
}
func ServeEditExpenseTags(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
conn := getConn(r)
locale := getLocale(r)

View File

@ -35,6 +35,7 @@ type InvoiceEntry struct {
type InvoicesIndexPage struct {
Invoices []*InvoiceEntry
TotalAmount string
Filters *invoiceFilterForm
InvoiceStatuses map[string]string
}
@ -49,44 +50,16 @@ func IndexInvoices(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
return
}
page := &InvoicesIndexPage{
Invoices: mustCollectInvoiceEntries(r.Context(), conn, company, locale, filters),
Invoices: mustCollectInvoiceEntries(r.Context(), conn, locale, filters),
TotalAmount: mustComputeInvoicesTotalAmount(r.Context(), conn, filters),
Filters: filters,
InvoiceStatuses: mustCollectInvoiceStatuses(r.Context(), conn, locale),
}
mustRenderMainTemplate(w, r, "invoices/index.gohtml", page)
}
func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company, locale *Locale, filters *invoiceFilterForm) []*InvoiceEntry {
args := []interface{}{locale.Language.String(), company.Id}
where := []string{"invoice.company_id = $2"}
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
maybeAppendWhere("contact_id = $%d", filters.Customer.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(filters.Customer.Selected[0])
return customerId
})
maybeAppendWhere("invoice.invoice_status = $%d", filters.InvoiceStatus.String(), nil)
maybeAppendWhere("invoice_number = $%d", filters.InvoiceNumber.String(), nil)
maybeAppendWhere("invoice_date >= $%d", filters.FromDate.String(), nil)
maybeAppendWhere("invoice_date <= $%d", filters.ToDate.String(), nil)
if len(filters.Tags.Tags) > 0 {
if filters.TagsCondition.Selected == "and" {
appendWhere("invoice.tags @> $%d", filters.Tags)
} else {
appendWhere("invoice.tags && $%d", filters.Tags)
}
}
func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, locale *Locale, filters *invoiceFilterForm) []*InvoiceEntry {
where, args := filters.BuildQuery([]interface{}{locale.Language.String()})
rows := conn.MustQuery(ctx, fmt.Sprintf(`
select invoice.slug
, invoice_date
@ -104,7 +77,7 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company
where (%s)
order by invoice_date desc
, invoice_number desc
`, strings.Join(where, ") AND (")), args...)
`, where), args...)
defer rows.Close()
var entries []*InvoiceEntry
@ -122,6 +95,18 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company
return entries
}
func mustComputeInvoicesTotalAmount(ctx context.Context, conn *Conn, filters *invoiceFilterForm) string {
where, args := filters.BuildQuery(nil)
return conn.MustGetText(ctx, "0", fmt.Sprintf(`
select to_price(sum(total)::integer, decimal_digits)
from invoice
join invoice_amount using (invoice_id)
join currency using (currency_code)
where (%s)
group by decimal_digits
`, where), args...)
}
func mustCollectInvoiceStatuses(ctx context.Context, conn *Conn, locale *Locale) map[string]string {
rows := conn.MustQuery(ctx, "select invoice_status.invoice_status, isi18n.name from invoice_status join invoice_status_i18n isi18n using(invoice_status) where isi18n.lang_tag = $1 order by invoice_status", locale.Language.String())
defer rows.Close()
@ -159,7 +144,7 @@ func newInvoiceFilterForm(ctx context.Context, conn *Conn, locale *Locale, compa
company: company,
Customer: &SelectField{
Name: "customer",
Label: pgettext("input", "Customer", locale),
Label: pgettext("input", "Contact", locale),
EmptyLabel: gettext("All customers", locale),
Options: mustGetContactOptions(ctx, conn, company),
},
@ -220,6 +205,41 @@ func (form *invoiceFilterForm) Parse(r *http.Request) error {
return nil
}
func (form *invoiceFilterForm) BuildQuery(args []interface{}) (string, []interface{}) {
var where []string
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
appendWhere("invoice.company_id = $%d", form.company.Id)
maybeAppendWhere("contact_id = $%d", form.Customer.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(form.Customer.Selected[0])
return customerId
})
maybeAppendWhere("invoice.invoice_status = $%d", form.InvoiceStatus.String(), nil)
maybeAppendWhere("invoice_number = $%d", form.InvoiceNumber.String(), nil)
maybeAppendWhere("invoice_date >= $%d", form.FromDate.String(), nil)
maybeAppendWhere("invoice_date <= $%d", form.ToDate.String(), nil)
if len(form.Tags.Tags) > 0 {
if form.TagsCondition.Selected == "and" {
appendWhere("invoice.tags @> $%d", form.Tags)
} else {
appendWhere("invoice.tags && $%d", form.Tags)
}
}
return strings.Join(where, ") AND ("), args
}
func ServeInvoice(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
conn := getConn(r)
company := mustGetCompany(r)
@ -583,7 +603,7 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
},
Customer: &SelectField{
Name: "customer",
Label: pgettext("input", "Customer", locale),
Label: pgettext("input", "Contact", locale),
Required: true,
Options: mustGetContactOptions(ctx, conn, company),
},

View File

@ -33,6 +33,7 @@ type QuoteEntry struct {
type QuotesIndexPage struct {
Quotes []*QuoteEntry
TotalAmount string
Filters *quoteFilterForm
QuoteStatuses map[string]string
}
@ -47,44 +48,16 @@ func IndexQuotes(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
return
}
page := &QuotesIndexPage{
Quotes: mustCollectQuoteEntries(r.Context(), conn, company, locale, filters),
Quotes: mustCollectQuoteEntries(r.Context(), conn, locale, filters),
TotalAmount: mustComputeQuotesTotalAmount(r.Context(), conn, filters),
Filters: filters,
QuoteStatuses: mustCollectQuoteStatuses(r.Context(), conn, locale),
}
mustRenderMainTemplate(w, r, "quotes/index.gohtml", page)
}
func mustCollectQuoteEntries(ctx context.Context, conn *Conn, company *Company, locale *Locale, filters *quoteFilterForm) []*QuoteEntry {
args := []interface{}{locale.Language.String(), company.Id}
where := []string{"quote.company_id = $2"}
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
maybeAppendWhere("contact_id = $%d", filters.Customer.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(filters.Customer.Selected[0])
return customerId
})
maybeAppendWhere("quote.quote_status = $%d", filters.QuoteStatus.String(), nil)
maybeAppendWhere("quote_number = $%d", filters.QuoteNumber.String(), nil)
maybeAppendWhere("quote_date >= $%d", filters.FromDate.String(), nil)
maybeAppendWhere("quote_date <= $%d", filters.ToDate.String(), nil)
if len(filters.Tags.Tags) > 0 {
if filters.TagsCondition.Selected == "and" {
appendWhere("quote.tags @> $%d", filters.Tags)
} else {
appendWhere("quote.tags && $%d", filters.Tags)
}
}
func mustCollectQuoteEntries(ctx context.Context, conn *Conn, locale *Locale, filters *quoteFilterForm) []*QuoteEntry {
where, args := filters.BuildQuery([]interface{}{locale.Language.String()})
rows := conn.MustQuery(ctx, fmt.Sprintf(`
select quote.slug
, quote_date
@ -103,7 +76,7 @@ func mustCollectQuoteEntries(ctx context.Context, conn *Conn, company *Company,
where (%s)
order by quote_date desc
, quote_number desc
`, strings.Join(where, ") AND (")), args...)
`, where), args...)
defer rows.Close()
var entries []*QuoteEntry
@ -121,6 +94,19 @@ func mustCollectQuoteEntries(ctx context.Context, conn *Conn, company *Company,
return entries
}
func mustComputeQuotesTotalAmount(ctx context.Context, conn *Conn, filters *quoteFilterForm) string {
where, args := filters.BuildQuery(nil)
return conn.MustGetText(ctx, "0", fmt.Sprintf(`
select to_price(sum(total)::integer, decimal_digits)
from quote
left join quote_contact using (quote_id)
join quote_amount using (quote_id)
join currency using (currency_code)
where (%s)
group by decimal_digits
`, where), args...)
}
func mustCollectQuoteStatuses(ctx context.Context, conn *Conn, locale *Locale) map[string]string {
rows := conn.MustQuery(ctx, "select quote_status.quote_status, isi18n.name from quote_status join quote_status_i18n isi18n using(quote_status) where isi18n.lang_tag = $1 order by quote_status", locale.Language.String())
defer rows.Close()
@ -158,7 +144,7 @@ func newQuoteFilterForm(ctx context.Context, conn *Conn, locale *Locale, company
company: company,
Customer: &SelectField{
Name: "customer",
Label: pgettext("input", "Customer", locale),
Label: pgettext("input", "Contact", locale),
EmptyLabel: gettext("All customers", locale),
Options: mustGetContactOptions(ctx, conn, company),
},
@ -219,6 +205,42 @@ func (form *quoteFilterForm) Parse(r *http.Request) error {
return nil
}
func (form *quoteFilterForm) BuildQuery(args []interface{}) (string, []interface{}) {
var where []string
appendWhere := func(expression string, value interface{}) {
args = append(args, value)
where = append(where, fmt.Sprintf(expression, len(args)))
}
maybeAppendWhere := func(expression string, value string, conv func(string) interface{}) {
if value != "" {
if conv == nil {
appendWhere(expression, value)
} else {
appendWhere(expression, conv(value))
}
}
}
appendWhere("quote.company_id = $%d", form.company.Id)
maybeAppendWhere("contact_id = $%d", form.Customer.String(), func(v string) interface{} {
customerId, _ := strconv.Atoi(form.Customer.Selected[0])
return customerId
})
maybeAppendWhere("quote.quote_status = $%d", form.QuoteStatus.String(), nil)
maybeAppendWhere("quote_number = $%d", form.QuoteNumber.String(), nil)
maybeAppendWhere("quote_date >= $%d", form.FromDate.String(), nil)
maybeAppendWhere("quote_date <= $%d", form.ToDate.String(), nil)
if len(form.Tags.Tags) > 0 {
if form.TagsCondition.Selected == "and" {
appendWhere("quote.tags @> $%d", form.Tags)
} else {
appendWhere("quote.tags && $%d", form.Tags)
}
}
return strings.Join(where, ") AND ("), args
}
func ServeQuote(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
conn := getConn(r)
company := mustGetCompany(r)
@ -583,7 +605,7 @@ func newQuoteForm(ctx context.Context, conn *Conn, locale *Locale, company *Comp
},
Customer: &SelectField{
Name: "customer",
Label: pgettext("input", "Customer", locale),
Label: pgettext("input", "Contact", locale),
EmptyLabel: gettext("Select a customer to quote.", locale),
Options: mustGetContactOptions(ctx, conn, company),
},

161
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-06-20 10:48+0200\n"
"POT-Creation-Date: 2023-06-20 11:29+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"
@ -157,10 +157,9 @@ msgid "Invoice Num."
msgstr "Núm. factura"
#: web/template/invoices/index.gohtml:52 web/template/quotes/index.gohtml:52
#: web/template/contacts/index.gohtml:40 web/template/expenses/index.gohtml:43
msgctxt "title"
msgid "Contact"
msgstr "Contacte"
msgid "Customer"
msgstr "Client"
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
msgctxt "title"
@ -216,6 +215,11 @@ msgstr "Duplica"
msgid "No invoices added yet."
msgstr "No hi ha cap factura."
#: web/template/invoices/index.gohtml:144 web/template/quotes/index.gohtml:152
#: web/template/expenses/index.gohtml:105
msgid "Total"
msgstr "Total"
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:33
msgctxt "title"
msgid "Invoice %s"
@ -449,6 +453,11 @@ msgctxt "action"
msgid "New contact"
msgstr "Nou contacte"
#: web/template/contacts/index.gohtml:40 web/template/expenses/index.gohtml:43
msgctxt "title"
msgid "Contact"
msgstr "Contacte"
#: web/template/contacts/index.gohtml:41
msgctxt "title"
msgid "Email"
@ -665,82 +674,82 @@ 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:263 pkg/quote.go:801 pkg/invoices.go:851
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:871
#: pkg/contacts.go:135
msgctxt "input"
msgid "Name"
msgstr "Nom"
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:608
#: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189
#: pkg/invoices.go:603 pkg/invoices.go:1150 pkg/contacts.go:140
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
#: pkg/invoices.go:623 pkg/invoices.go:1170 pkg/contacts.go:140
#: pkg/contacts.go:325
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:173 pkg/quote.go:192 pkg/expenses.go:365 pkg/invoices.go:193
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
#: pkg/contacts.go:144
msgctxt "input"
msgid "Tags Condition"
msgstr "Condició de les etiquetes"
#: pkg/products.go:177 pkg/quote.go:196 pkg/expenses.go:369 pkg/invoices.go:197
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
#: pkg/contacts.go:148
msgctxt "tag condition"
msgid "All"
msgstr "Totes"
#: pkg/products.go:178 pkg/expenses.go:370 pkg/invoices.go:198
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
#: pkg/contacts.go:149
msgid "Invoices must have all the specified labels."
msgstr "Les factures han de tenir totes les etiquetes."
#: pkg/products.go:182 pkg/quote.go:201 pkg/expenses.go:374 pkg/invoices.go:202
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
#: pkg/contacts.go:153
msgctxt "tag condition"
msgid "Any"
msgstr "Qualsevol"
#: pkg/products.go:183 pkg/expenses.go:375 pkg/invoices.go:203
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
#: pkg/contacts.go:154
msgid "Invoices must have at least one of the specified labels."
msgstr "Les factures han de tenir com a mínim una de les etiquetes."
#: pkg/products.go:269 pkg/quote.go:815 pkg/invoices.go:865
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:885
msgctxt "input"
msgid "Description"
msgstr "Descripció"
#: pkg/products.go:274 pkg/quote.go:819 pkg/invoices.go:869
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:889
msgctxt "input"
msgid "Price"
msgstr "Preu"
#: pkg/products.go:284 pkg/quote.go:848 pkg/expenses.go:181 pkg/invoices.go:898
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:918
msgctxt "input"
msgid "Taxes"
msgstr "Imposts"
#: pkg/products.go:309 pkg/quote.go:897 pkg/profile.go:92 pkg/invoices.go:947
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:967
msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc."
#: pkg/products.go:310 pkg/quote.go:898 pkg/invoices.go:948
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:968
msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc."
#: pkg/products.go:311 pkg/quote.go:899 pkg/invoices.go:949
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:969
msgid "Price must be a number greater than zero."
msgstr "El preu ha de ser un número major a zero."
#: pkg/products.go:313 pkg/quote.go:907 pkg/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:957
#: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
#: pkg/invoices.go:977
msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/products.go:314 pkg/quote.go:908 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:958
#: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
#: pkg/invoices.go:978
msgid "You can only select a tax of each class."
msgstr "Només podeu seleccionar un impost de cada classe."
@ -847,141 +856,141 @@ 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:148 pkg/quote.go:609 pkg/expenses.go:164 pkg/expenses.go:340
#: pkg/quote.go:147 pkg/quote.go:608 pkg/expenses.go:150 pkg/expenses.go:326
#: pkg/invoices.go:147 pkg/invoices.go:606
msgctxt "input"
msgid "Contact"
msgstr "Contacte"
#: pkg/quote.go:162 pkg/expenses.go:341 pkg/invoices.go:163
#: pkg/quote.go:148 pkg/invoices.go:148
msgid "All customers"
msgstr "Tots els clients"
#: pkg/quote.go:167 pkg/quote.go:580
#: pkg/quote.go:153 pkg/quote.go:602
msgctxt "input"
msgid "Quotation Status"
msgstr "Estat del pressupost"
#: pkg/quote.go:168 pkg/invoices.go:169
#: pkg/quote.go:154 pkg/invoices.go:154
msgid "All status"
msgstr "Tots els estats"
#: pkg/quote.go:173
#: pkg/quote.go:159
msgctxt "input"
msgid "Quotation Number"
msgstr "Número de pressupost"
#: pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:179
#: pkg/quote.go:164 pkg/expenses.go:337 pkg/invoices.go:164
msgctxt "input"
msgid "From Date"
msgstr "A partir de la data"
#: pkg/quote.go:183 pkg/expenses.go:356 pkg/invoices.go:184
#: pkg/quote.go:169 pkg/expenses.go:342 pkg/invoices.go:169
msgctxt "input"
msgid "To Date"
msgstr "Fins la data"
#: pkg/quote.go:197
#: pkg/quote.go:183
msgid "Quotations must have all the specified labels."
msgstr "Els pressuposts han de tenir totes les etiquetes."
#: pkg/quote.go:202
#: pkg/quote.go:188
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:528
#: pkg/quote.go:550
msgid "quotations.zip"
msgstr "pressuposts.zip"
#: pkg/quote.go:534 pkg/quote.go:1063 pkg/quote.go:1071 pkg/invoices.go:535
#: pkg/invoices.go:1125 pkg/invoices.go:1133
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:555
#: pkg/invoices.go:1145 pkg/invoices.go:1153
msgid "Invalid action"
msgstr "Acció invàlida."
#: pkg/quote.go:587
#: pkg/quote.go:609
msgid "Select a customer to quote."
msgstr "Escolliu un client a pressupostar."
#: pkg/quote.go:592
#: pkg/quote.go:614
msgctxt "input"
msgid "Quotation Date"
msgstr "Data del pressupost"
#: pkg/quote.go:598
#: pkg/quote.go:620
msgctxt "input"
msgid "Terms and conditions"
msgstr "Condicions dacceptació"
#: pkg/quote.go:603 pkg/invoices.go:598
#: pkg/quote.go:625 pkg/invoices.go:618
msgctxt "input"
msgid "Notes"
msgstr "Notes"
#: pkg/quote.go:612 pkg/invoices.go:608
#: pkg/quote.go:634 pkg/invoices.go:628
msgctxt "input"
msgid "Payment Method"
msgstr "Mètode de pagament"
#: pkg/quote.go:613
#: pkg/quote.go:635
msgid "Select a payment method."
msgstr "Escolliu un mètode de pagament."
#: pkg/quote.go:649
#: pkg/quote.go:671
msgid "Selected quotation status is not valid."
msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
#: pkg/quote.go:651 pkg/invoices.go:645
#: pkg/quote.go:673 pkg/invoices.go:665
msgid "Selected customer is not valid."
msgstr "Heu seleccionat un client que no és vàlid."
#: pkg/quote.go:653
#: pkg/quote.go:675
msgid "Quotation date can not be empty."
msgstr "No podeu deixar la data del pressupost en blanc."
#: pkg/quote.go:654
#: pkg/quote.go:676
msgid "Quotation date must be a valid date."
msgstr "La data del pressupost ha de ser vàlida."
#: pkg/quote.go:657 pkg/invoices.go:649
#: pkg/quote.go:679 pkg/invoices.go:669
msgid "Selected payment method is not valid."
msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
#: pkg/quote.go:791 pkg/quote.go:796 pkg/invoices.go:841 pkg/invoices.go:846
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:861 pkg/invoices.go:866
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/quote.go:829 pkg/invoices.go:879
#: pkg/quote.go:851 pkg/invoices.go:899
msgctxt "input"
msgid "Quantity"
msgstr "Quantitat"
#: pkg/quote.go:838 pkg/invoices.go:888
#: pkg/quote.go:860 pkg/invoices.go:908
msgctxt "input"
msgid "Discount (%)"
msgstr "Descompte (%)"
#: pkg/quote.go:892
#: pkg/quote.go:914
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:895 pkg/invoices.go:945
#: pkg/quote.go:917 pkg/invoices.go:965
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:901 pkg/invoices.go:951
#: pkg/quote.go:923 pkg/invoices.go:971
msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc."
#: pkg/quote.go:902 pkg/invoices.go:952
#: pkg/quote.go:924 pkg/invoices.go:972
msgid "Quantity must be a number greater than zero."
msgstr "La quantitat ha de ser un número major a zero."
#: pkg/quote.go:904 pkg/invoices.go:954
#: pkg/quote.go:926 pkg/invoices.go:974
msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc."
#: pkg/quote.go:905 pkg/invoices.go:955
#: pkg/quote.go:927 pkg/invoices.go:975
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
@ -1048,87 +1057,87 @@ msgctxt "period option"
msgid "Previous year"
msgstr "Any anterior"
#: pkg/expenses.go:129
#: pkg/expenses.go:115
msgid "Select a contact."
msgstr "Escolliu un contacte."
#: pkg/expenses.go:170
#: pkg/expenses.go:156
msgctxt "input"
msgid "Invoice number"
msgstr "Número de factura"
#: pkg/expenses.go:175 pkg/invoices.go:592
#: pkg/expenses.go:161 pkg/invoices.go:612
msgctxt "input"
msgid "Invoice Date"
msgstr "Data de factura"
#: pkg/expenses.go:187
#: pkg/expenses.go:173
msgctxt "input"
msgid "Amount"
msgstr "Import"
#: pkg/expenses.go:197
#: pkg/expenses.go:183
msgctxt "input"
msgid "File"
msgstr "Fitxer"
#: pkg/expenses.go:225
#: pkg/expenses.go:211
msgid "Selected contact is not valid."
msgstr "Heu seleccionat un contacte que no és vàlid."
#: pkg/expenses.go:226 pkg/invoices.go:647
#: pkg/expenses.go:212 pkg/invoices.go:667
msgid "Invoice date must be a valid date."
msgstr "La data de facturació ha de ser vàlida."
#: pkg/expenses.go:229
#: pkg/expenses.go:215
msgid "Amount can not be empty."
msgstr "No podeu deixar limport en blanc."
#: pkg/expenses.go:230
#: pkg/expenses.go:216
msgid "Amount must be a number greater than zero."
msgstr "Limport ha de ser un número major a zero."
#: pkg/expenses.go:341
#: pkg/expenses.go:327
msgid "All contacts"
msgstr "Tots els contactes"
#: pkg/expenses.go:346 pkg/invoices.go:159
#: pkg/expenses.go:332 pkg/invoices.go:159
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/invoices.go:168 pkg/invoices.go:580
#: pkg/invoices.go:153 pkg/invoices.go:600
msgctxt "input"
msgid "Invoice Status"
msgstr "Estat de la factura"
#: pkg/invoices.go:428
#: pkg/invoices.go:448
msgid "Select a customer to bill."
msgstr "Escolliu un client a facturar."
#: pkg/invoices.go:529
#: pkg/invoices.go:549
msgid "invoices.zip"
msgstr "factures.zip"
#: pkg/invoices.go:644
#: pkg/invoices.go:664
msgid "Selected invoice status is not valid."
msgstr "Heu seleccionat un estat de factura que no és vàlid."
#: pkg/invoices.go:646
#: pkg/invoices.go:666
msgid "Invoice date can not be empty."
msgstr "No podeu deixar la data de la factura en blanc."
#: pkg/invoices.go:782
#: pkg/invoices.go:802
#, c-format
msgid "Re: quotation #%s of %s"
msgstr "Ref: pressupost núm. %s del %s"
#: pkg/invoices.go:783
#: pkg/invoices.go:803
msgctxt "to_char"
msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY"
#: pkg/invoices.go:942
#: pkg/invoices.go:962
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."
@ -1234,10 +1243,6 @@ msgstr "No podeu deixar el codi postal en blanc."
msgid "This value is not a valid postal code."
msgstr "Aquest valor no és un codi postal vàlid."
#~ msgctxt "title"
#~ msgid "Customer"
#~ msgstr "Client"
#~ msgctxt "input"
#~ msgid "Customer"
#~ msgstr "Client"

161
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-06-20 10:48+0200\n"
"POT-Creation-Date: 2023-06-20 11:29+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"
@ -157,10 +157,9 @@ msgid "Invoice Num."
msgstr "N.º factura"
#: web/template/invoices/index.gohtml:52 web/template/quotes/index.gohtml:52
#: web/template/contacts/index.gohtml:40 web/template/expenses/index.gohtml:43
msgctxt "title"
msgid "Contact"
msgstr "Contacto"
msgid "Customer"
msgstr "Cliente"
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
msgctxt "title"
@ -216,6 +215,11 @@ msgstr "Duplicar"
msgid "No invoices added yet."
msgstr "No hay facturas."
#: web/template/invoices/index.gohtml:144 web/template/quotes/index.gohtml:152
#: web/template/expenses/index.gohtml:105
msgid "Total"
msgstr "Total"
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:33
msgctxt "title"
msgid "Invoice %s"
@ -449,6 +453,11 @@ msgctxt "action"
msgid "New contact"
msgstr "Nuevo contacto"
#: web/template/contacts/index.gohtml:40 web/template/expenses/index.gohtml:43
msgctxt "title"
msgid "Contact"
msgstr "Contacto"
#: web/template/contacts/index.gohtml:41
msgctxt "title"
msgid "Email"
@ -665,82 +674,82 @@ msgstr "No podéis dejar la contraseña en blanco."
msgid "Invalid user or password."
msgstr "Nombre de usuario o contraseña inválido."
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:801 pkg/invoices.go:851
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:823 pkg/invoices.go:871
#: pkg/contacts.go:135
msgctxt "input"
msgid "Name"
msgstr "Nombre"
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:608
#: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189
#: pkg/invoices.go:603 pkg/invoices.go:1150 pkg/contacts.go:140
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:174 pkg/quote.go:630
#: pkg/expenses.go:188 pkg/expenses.go:347 pkg/invoices.go:174
#: pkg/invoices.go:623 pkg/invoices.go:1170 pkg/contacts.go:140
#: pkg/contacts.go:325
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:173 pkg/quote.go:192 pkg/expenses.go:365 pkg/invoices.go:193
#: pkg/products.go:173 pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:178
#: pkg/contacts.go:144
msgctxt "input"
msgid "Tags Condition"
msgstr "Condición de las etiquetas"
#: pkg/products.go:177 pkg/quote.go:196 pkg/expenses.go:369 pkg/invoices.go:197
#: pkg/products.go:177 pkg/quote.go:182 pkg/expenses.go:355 pkg/invoices.go:182
#: pkg/contacts.go:148
msgctxt "tag condition"
msgid "All"
msgstr "Todas"
#: pkg/products.go:178 pkg/expenses.go:370 pkg/invoices.go:198
#: pkg/products.go:178 pkg/expenses.go:356 pkg/invoices.go:183
#: pkg/contacts.go:149
msgid "Invoices must have all the specified labels."
msgstr "Las facturas deben tener todas las etiquetas."
#: pkg/products.go:182 pkg/quote.go:201 pkg/expenses.go:374 pkg/invoices.go:202
#: pkg/products.go:182 pkg/quote.go:187 pkg/expenses.go:360 pkg/invoices.go:187
#: pkg/contacts.go:153
msgctxt "tag condition"
msgid "Any"
msgstr "Cualquiera"
#: pkg/products.go:183 pkg/expenses.go:375 pkg/invoices.go:203
#: pkg/products.go:183 pkg/expenses.go:361 pkg/invoices.go:188
#: pkg/contacts.go:154
msgid "Invoices must have at least one of the specified labels."
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
#: pkg/products.go:269 pkg/quote.go:815 pkg/invoices.go:865
#: pkg/products.go:269 pkg/quote.go:837 pkg/invoices.go:885
msgctxt "input"
msgid "Description"
msgstr "Descripción"
#: pkg/products.go:274 pkg/quote.go:819 pkg/invoices.go:869
#: pkg/products.go:274 pkg/quote.go:841 pkg/invoices.go:889
msgctxt "input"
msgid "Price"
msgstr "Precio"
#: pkg/products.go:284 pkg/quote.go:848 pkg/expenses.go:181 pkg/invoices.go:898
#: pkg/products.go:284 pkg/quote.go:870 pkg/expenses.go:167 pkg/invoices.go:918
msgctxt "input"
msgid "Taxes"
msgstr "Impuestos"
#: pkg/products.go:309 pkg/quote.go:897 pkg/profile.go:92 pkg/invoices.go:947
#: pkg/products.go:309 pkg/quote.go:919 pkg/profile.go:92 pkg/invoices.go:967
msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco."
#: pkg/products.go:310 pkg/quote.go:898 pkg/invoices.go:948
#: pkg/products.go:310 pkg/quote.go:920 pkg/invoices.go:968
msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco."
#: pkg/products.go:311 pkg/quote.go:899 pkg/invoices.go:949
#: pkg/products.go:311 pkg/quote.go:921 pkg/invoices.go:969
msgid "Price must be a number greater than zero."
msgstr "El precio tiene que ser un número mayor a cero."
#: pkg/products.go:313 pkg/quote.go:907 pkg/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:957
#: pkg/products.go:313 pkg/quote.go:929 pkg/expenses.go:213 pkg/expenses.go:218
#: pkg/invoices.go:977
msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/products.go:314 pkg/quote.go:908 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:958
#: pkg/products.go:314 pkg/quote.go:930 pkg/expenses.go:214 pkg/expenses.go:219
#: pkg/invoices.go:978
msgid "You can only select a tax of each class."
msgstr "Solo podéis escoger un impuesto de cada clase."
@ -847,141 +856,141 @@ 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:148 pkg/quote.go:609 pkg/expenses.go:164 pkg/expenses.go:340
#: pkg/quote.go:147 pkg/quote.go:608 pkg/expenses.go:150 pkg/expenses.go:326
#: pkg/invoices.go:147 pkg/invoices.go:606
msgctxt "input"
msgid "Contact"
msgstr "Contacto"
#: pkg/quote.go:162 pkg/expenses.go:341 pkg/invoices.go:163
#: pkg/quote.go:148 pkg/invoices.go:148
msgid "All customers"
msgstr "Todos los clientes"
#: pkg/quote.go:167 pkg/quote.go:580
#: pkg/quote.go:153 pkg/quote.go:602
msgctxt "input"
msgid "Quotation Status"
msgstr "Estado del presupuesto"
#: pkg/quote.go:168 pkg/invoices.go:169
#: pkg/quote.go:154 pkg/invoices.go:154
msgid "All status"
msgstr "Todos los estados"
#: pkg/quote.go:173
#: pkg/quote.go:159
msgctxt "input"
msgid "Quotation Number"
msgstr "Número de presupuesto"
#: pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:179
#: pkg/quote.go:164 pkg/expenses.go:337 pkg/invoices.go:164
msgctxt "input"
msgid "From Date"
msgstr "A partir de la fecha"
#: pkg/quote.go:183 pkg/expenses.go:356 pkg/invoices.go:184
#: pkg/quote.go:169 pkg/expenses.go:342 pkg/invoices.go:169
msgctxt "input"
msgid "To Date"
msgstr "Hasta la fecha"
#: pkg/quote.go:197
#: pkg/quote.go:183
msgid "Quotations must have all the specified labels."
msgstr "Los presupuestos deben tener todas las etiquetas."
#: pkg/quote.go:202
#: pkg/quote.go:188
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:528
#: pkg/quote.go:550
msgid "quotations.zip"
msgstr "presupuestos.zip"
#: pkg/quote.go:534 pkg/quote.go:1063 pkg/quote.go:1071 pkg/invoices.go:535
#: pkg/invoices.go:1125 pkg/invoices.go:1133
#: pkg/quote.go:556 pkg/quote.go:1085 pkg/quote.go:1093 pkg/invoices.go:555
#: pkg/invoices.go:1145 pkg/invoices.go:1153
msgid "Invalid action"
msgstr "Acción inválida."
#: pkg/quote.go:587
#: pkg/quote.go:609
msgid "Select a customer to quote."
msgstr "Escoged un cliente a presupuestar."
#: pkg/quote.go:592
#: pkg/quote.go:614
msgctxt "input"
msgid "Quotation Date"
msgstr "Fecha del presupuesto"
#: pkg/quote.go:598
#: pkg/quote.go:620
msgctxt "input"
msgid "Terms and conditions"
msgstr "Condiciones de aceptación"
#: pkg/quote.go:603 pkg/invoices.go:598
#: pkg/quote.go:625 pkg/invoices.go:618
msgctxt "input"
msgid "Notes"
msgstr "Notas"
#: pkg/quote.go:612 pkg/invoices.go:608
#: pkg/quote.go:634 pkg/invoices.go:628
msgctxt "input"
msgid "Payment Method"
msgstr "Método de pago"
#: pkg/quote.go:613
#: pkg/quote.go:635
msgid "Select a payment method."
msgstr "Escoged un método e pago."
#: pkg/quote.go:649
#: pkg/quote.go:671
msgid "Selected quotation status is not valid."
msgstr "Habéis escogido un estado de presupuesto que no es válido."
#: pkg/quote.go:651 pkg/invoices.go:645
#: pkg/quote.go:673 pkg/invoices.go:665
msgid "Selected customer is not valid."
msgstr "Habéis escogido un cliente que no es válido."
#: pkg/quote.go:653
#: pkg/quote.go:675
msgid "Quotation date can not be empty."
msgstr "No podéis dejar la fecha del presupuesto en blanco."
#: pkg/quote.go:654
#: pkg/quote.go:676
msgid "Quotation date must be a valid date."
msgstr "La fecha de presupuesto debe ser válida."
#: pkg/quote.go:657 pkg/invoices.go:649
#: pkg/quote.go:679 pkg/invoices.go:669
msgid "Selected payment method is not valid."
msgstr "Habéis escogido un método de pago que no es válido."
#: pkg/quote.go:791 pkg/quote.go:796 pkg/invoices.go:841 pkg/invoices.go:846
#: pkg/quote.go:813 pkg/quote.go:818 pkg/invoices.go:861 pkg/invoices.go:866
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/quote.go:829 pkg/invoices.go:879
#: pkg/quote.go:851 pkg/invoices.go:899
msgctxt "input"
msgid "Quantity"
msgstr "Cantidad"
#: pkg/quote.go:838 pkg/invoices.go:888
#: pkg/quote.go:860 pkg/invoices.go:908
msgctxt "input"
msgid "Discount (%)"
msgstr "Descuento (%)"
#: pkg/quote.go:892
#: pkg/quote.go:914
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:895 pkg/invoices.go:945
#: pkg/quote.go:917 pkg/invoices.go:965
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:901 pkg/invoices.go:951
#: pkg/quote.go:923 pkg/invoices.go:971
msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco."
#: pkg/quote.go:902 pkg/invoices.go:952
#: pkg/quote.go:924 pkg/invoices.go:972
msgid "Quantity must be a number greater than zero."
msgstr "La cantidad tiene que ser un número mayor a cero."
#: pkg/quote.go:904 pkg/invoices.go:954
#: pkg/quote.go:926 pkg/invoices.go:974
msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco."
#: pkg/quote.go:905 pkg/invoices.go:955
#: pkg/quote.go:927 pkg/invoices.go:975
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
@ -1048,87 +1057,87 @@ msgctxt "period option"
msgid "Previous year"
msgstr "Año anterior"
#: pkg/expenses.go:129
#: pkg/expenses.go:115
msgid "Select a contact."
msgstr "Escoged un contacto"
#: pkg/expenses.go:170
#: pkg/expenses.go:156
msgctxt "input"
msgid "Invoice number"
msgstr "Número de factura"
#: pkg/expenses.go:175 pkg/invoices.go:592
#: pkg/expenses.go:161 pkg/invoices.go:612
msgctxt "input"
msgid "Invoice Date"
msgstr "Fecha de factura"
#: pkg/expenses.go:187
#: pkg/expenses.go:173
msgctxt "input"
msgid "Amount"
msgstr "Importe"
#: pkg/expenses.go:197
#: pkg/expenses.go:183
msgctxt "input"
msgid "File"
msgstr "Archivo"
#: pkg/expenses.go:225
#: pkg/expenses.go:211
msgid "Selected contact is not valid."
msgstr "Habéis escogido un contacto que no es válido."
#: pkg/expenses.go:226 pkg/invoices.go:647
#: pkg/expenses.go:212 pkg/invoices.go:667
msgid "Invoice date must be a valid date."
msgstr "La fecha de factura debe ser válida."
#: pkg/expenses.go:229
#: pkg/expenses.go:215
msgid "Amount can not be empty."
msgstr "No podéis dejar el importe en blanco."
#: pkg/expenses.go:230
#: pkg/expenses.go:216
msgid "Amount must be a number greater than zero."
msgstr "El importe tiene que ser un número mayor a cero."
#: pkg/expenses.go:341
#: pkg/expenses.go:327
msgid "All contacts"
msgstr "Todos los contactos"
#: pkg/expenses.go:346 pkg/invoices.go:159
#: pkg/expenses.go:332 pkg/invoices.go:159
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/invoices.go:168 pkg/invoices.go:580
#: pkg/invoices.go:153 pkg/invoices.go:600
msgctxt "input"
msgid "Invoice Status"
msgstr "Estado de la factura"
#: pkg/invoices.go:428
#: pkg/invoices.go:448
msgid "Select a customer to bill."
msgstr "Escoged un cliente a facturar."
#: pkg/invoices.go:529
#: pkg/invoices.go:549
msgid "invoices.zip"
msgstr "facturas.zip"
#: pkg/invoices.go:644
#: pkg/invoices.go:664
msgid "Selected invoice status is not valid."
msgstr "Habéis escogido un estado de factura que no es válido."
#: pkg/invoices.go:646
#: pkg/invoices.go:666
msgid "Invoice date can not be empty."
msgstr "No podéis dejar la fecha de la factura en blanco."
#: pkg/invoices.go:782
#: pkg/invoices.go:802
#, c-format
msgid "Re: quotation #%s of %s"
msgstr "Ref: presupuesto n.º %s del %s"
#: pkg/invoices.go:783
#: pkg/invoices.go:803
msgctxt "to_char"
msgid "MM/DD/YYYY"
msgstr "DD/MM/YYYY"
#: pkg/invoices.go:942
#: pkg/invoices.go:962
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."
@ -1234,10 +1243,6 @@ msgstr "No podéis dejar el código postal en blanco."
msgid "This value is not a valid postal code."
msgstr "Este valor no es un código postal válido válido."
#~ msgctxt "title"
#~ msgid "Customer"
#~ msgstr "Cliente"
#~ msgctxt "input"
#~ msgid "Customer"
#~ msgstr "Cliente"

View File

@ -263,6 +263,10 @@ tbody tr:nth-child(even) {
background-color: var(--numerus--header--background-color);
}
tfoot th {
text-align: right;
}
div[role="alert"].error {
padding: 1.25em;
background-color: var(--numerus--color--red);

View File

@ -99,5 +99,14 @@
</tr>
{{ end }}
</tbody>
{{ if .Expenses }}
<tfoot>
<tr>
<th scope="row" colspan="5">{{( gettext "Total" )}}</th>
<td class="numeric">{{ .TotalAmount|formatPrice }}</td>
<td colspan="2"></td>
</tr>
</tfoot>
{{ end }}
</table>
{{- end }}

View File

@ -138,5 +138,14 @@
</tr>
{{ end }}
</tbody>
{{ if .Invoices }}
<tfoot>
<tr>
<th scope="row" colspan="6">{{( gettext "Total" )}}</th>
<td class="numeric">{{ .TotalAmount|formatPrice }}</td>
<td colspan="2"></td>
</tr>
</tfoot>
{{ end }}
</table>
{{- end }}

View File

@ -103,9 +103,9 @@
</td>
<td class="numeric">{{ .Total|formatPrice }}</td>
<td class="quote-download"><a href="{{ companyURI "/quotes/"}}{{ .Slug }}.pdf"
download="{{ .Number}}.pdf"
title="{{( pgettext "Download quotation" "action" )}}"
aria-label="{{( pgettext "Download quotation" "action" )}}"><i
download="{{ .Number}}.pdf"
title="{{( pgettext "Download quotation" "action" )}}"
aria-label="{{( pgettext "Download quotation" "action" )}}"><i
class="ri-download-line"></i></a></td>
<td class="actions">
<details class="menu">
@ -146,5 +146,14 @@
</tr>
{{ end }}
</tbody>
{{ if .Quotes }}
<tfoot>
<tr>
<th scope="row" colspan="6">{{( gettext "Total" )}}</th>
<td class="numeric">{{ .TotalAmount|formatPrice }}</td>
<td colspan="2"></td>
</tr>
</tfoot>
{{ end }}
</table>
{{- end }}