Add filters form for invoices
Instead of using links in the invoice tags, that we will replace with a “click-to-edit field”, with Oriol agreed to add a form with filters that includes not only the tags but also dates, customer, status, and the invoice number. This means i now need dynamic SQL, and i do not think this belongs to the database (i.e., no PL/pgSQL function for that). I have looked at query builder libraries for Golang, and did not find anything that suited me: either they wanted to manage not only the SQL query but also all structs, or they managed to confuse Goland’s SQL analyzer. For now, at least, i am using a very simple approach with arrays, that still confuses Goland’s analyzer, but just in a very specific part, which i find tolerable—not that their analyzer is that great to begin with, but that’s a story for another day.
This commit is contained in:
parent
3f092cd0d0
commit
b7881c505f
10
pkg/form.go
10
pkg/form.go
|
@ -116,10 +116,14 @@ func (field *SelectField) Scan(value interface{}) error {
|
|||
}
|
||||
|
||||
func (field *SelectField) Value() (driver.Value, error) {
|
||||
if field.Selected == nil {
|
||||
return "", nil
|
||||
return field.String(), nil
|
||||
}
|
||||
return field.Selected[0], nil
|
||||
|
||||
func (field *SelectField) String() string {
|
||||
if field.Selected == nil {
|
||||
return ""
|
||||
}
|
||||
return field.Selected[0]
|
||||
}
|
||||
|
||||
func (field *SelectField) FillValue(r *http.Request) {
|
||||
|
|
109
pkg/invoices.go
109
pkg/invoices.go
|
@ -33,22 +33,55 @@ type InvoiceEntry struct {
|
|||
|
||||
type InvoicesIndexPage struct {
|
||||
Invoices []*InvoiceEntry
|
||||
Filters *invoiceFilterForm
|
||||
InvoiceStatuses map[string]string
|
||||
}
|
||||
|
||||
func IndexInvoices(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
conn := getConn(r)
|
||||
locale := getLocale(r)
|
||||
tag := r.URL.Query().Get("tag")
|
||||
company := mustGetCompany(r)
|
||||
filters := newInvoiceFilterForm(r.Context(), conn, locale, company)
|
||||
if err := filters.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
page := &InvoicesIndexPage{
|
||||
Invoices: mustCollectInvoiceEntries(r.Context(), conn, mustGetCompany(r), locale, tag),
|
||||
Invoices: mustCollectInvoiceEntries(r.Context(), conn, company, locale, 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, tag string) []*InvoiceEntry {
|
||||
rows := conn.MustQuery(ctx, `
|
||||
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 {
|
||||
appendWhere("exists (select 1 from invoice_tag join tag using (tag_id) where invoice_tag.invoice_id = invoice.invoice_id and tag.name = any($%d))", filters.Tags)
|
||||
}
|
||||
rows := conn.MustQuery(ctx, fmt.Sprintf(`
|
||||
select invoice.slug
|
||||
, invoice_date
|
||||
, invoice_number
|
||||
|
@ -61,10 +94,10 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company
|
|||
left join invoice_tag using (invoice_id)
|
||||
left join tag using(tag_id)
|
||||
join contact using (contact_id)
|
||||
join invoice_status_i18n isi18n on invoice.invoice_status = isi18n.invoice_status and isi18n.lang_tag = $2
|
||||
join invoice_status_i18n isi18n on invoice.invoice_status = isi18n.invoice_status and isi18n.lang_tag = $1
|
||||
join invoice_amount using (invoice_id)
|
||||
join currency using (currency_code)
|
||||
where invoice.company_id = $1 and (($3 = '') or (tag.name = $3))
|
||||
where (%s)
|
||||
group by invoice.slug
|
||||
, invoice_date
|
||||
, invoice_number
|
||||
|
@ -75,7 +108,7 @@ func mustCollectInvoiceEntries(ctx context.Context, conn *Conn, company *Company
|
|||
, decimal_digits
|
||||
order by invoice_date desc
|
||||
, invoice_number desc
|
||||
`, company.Id, locale.Language.String(), tag)
|
||||
`, strings.Join(where, ") AND (")), args...)
|
||||
defer rows.Close()
|
||||
|
||||
var entries []*InvoiceEntry
|
||||
|
@ -112,6 +145,68 @@ func mustCollectInvoiceStatuses(ctx context.Context, conn *Conn, locale *Locale)
|
|||
return statuses
|
||||
}
|
||||
|
||||
type invoiceFilterForm struct {
|
||||
locale *Locale
|
||||
company *Company
|
||||
Customer *SelectField
|
||||
InvoiceStatus *SelectField
|
||||
InvoiceNumber *InputField
|
||||
FromDate *InputField
|
||||
ToDate *InputField
|
||||
Tags *TagsField
|
||||
}
|
||||
|
||||
func newInvoiceFilterForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *invoiceFilterForm {
|
||||
return &invoiceFilterForm{
|
||||
locale: locale,
|
||||
company: company,
|
||||
Customer: &SelectField{
|
||||
Name: "customer",
|
||||
Label: pgettext("input", "Customer", locale),
|
||||
EmptyLabel: gettext("All customers", locale),
|
||||
Options: MustGetOptions(ctx, conn, "select contact_id::text, business_name from contact where company_id = $1 order by business_name", company.Id),
|
||||
},
|
||||
InvoiceStatus: &SelectField{
|
||||
Name: "invoice_status",
|
||||
Label: pgettext("input", "Invoice Status", locale),
|
||||
EmptyLabel: gettext("All status", locale),
|
||||
Options: MustGetOptions(ctx, conn, "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()),
|
||||
},
|
||||
InvoiceNumber: &InputField{
|
||||
Name: "number",
|
||||
Label: pgettext("input", "Invoice Number", locale),
|
||||
Type: "text",
|
||||
},
|
||||
FromDate: &InputField{
|
||||
Name: "from_date",
|
||||
Label: pgettext("input", "From Date", locale),
|
||||
Type: "date",
|
||||
},
|
||||
ToDate: &InputField{
|
||||
Name: "to_date",
|
||||
Label: pgettext("input", "To Date", locale),
|
||||
Type: "date",
|
||||
},
|
||||
Tags: &TagsField{
|
||||
Name: "tags",
|
||||
Label: pgettext("input", "Tags", locale),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (form *invoiceFilterForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
form.Customer.FillValue(r)
|
||||
form.InvoiceStatus.FillValue(r)
|
||||
form.InvoiceNumber.FillValue(r)
|
||||
form.FromDate.FillValue(r)
|
||||
form.ToDate.FillValue(r)
|
||||
form.Tags.FillValue(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func ServeInvoice(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
conn := getConn(r)
|
||||
company := mustGetCompany(r)
|
||||
|
|
151
po/ca.po
151
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-03-27 09:43+0200\n"
|
||||
"POT-Creation-Date: 2023-03-29 16:08+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"
|
||||
|
@ -103,68 +103,73 @@ msgctxt "action"
|
|||
msgid "Download invoices"
|
||||
msgstr "Descarrega factures"
|
||||
|
||||
#: web/template/invoices/index.gohtml:31
|
||||
#: web/template/invoices/index.gohtml:38
|
||||
msgctxt "action"
|
||||
msgid "Filter"
|
||||
msgstr "Filtra"
|
||||
|
||||
#: web/template/invoices/index.gohtml:44
|
||||
msgctxt "invoice"
|
||||
msgid "All"
|
||||
msgstr "Totes"
|
||||
|
||||
#: web/template/invoices/index.gohtml:32 web/template/invoices/view.gohtml:31
|
||||
#: web/template/invoices/index.gohtml:45 web/template/invoices/view.gohtml:31
|
||||
msgctxt "title"
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
#: web/template/invoices/index.gohtml:33
|
||||
#: web/template/invoices/index.gohtml:46
|
||||
msgctxt "title"
|
||||
msgid "Invoice Num."
|
||||
msgstr "Núm. factura"
|
||||
|
||||
#: web/template/invoices/index.gohtml:34 web/template/contacts/index.gohtml:26
|
||||
#: web/template/invoices/index.gohtml:47 web/template/contacts/index.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: web/template/invoices/index.gohtml:35
|
||||
#: web/template/invoices/index.gohtml:48
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estat"
|
||||
|
||||
#: web/template/invoices/index.gohtml:36 web/template/contacts/index.gohtml:29
|
||||
#: web/template/invoices/index.gohtml:49 web/template/contacts/index.gohtml:29
|
||||
#: web/template/products/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: web/template/invoices/index.gohtml:37
|
||||
#: web/template/invoices/index.gohtml:50
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Import"
|
||||
|
||||
#: web/template/invoices/index.gohtml:38
|
||||
#: web/template/invoices/index.gohtml:51
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descàrrega"
|
||||
|
||||
#: web/template/invoices/index.gohtml:39
|
||||
#: web/template/invoices/index.gohtml:52
|
||||
msgctxt "title"
|
||||
msgid "Actions"
|
||||
msgstr "Accions"
|
||||
|
||||
#: web/template/invoices/index.gohtml:46
|
||||
#: web/template/invoices/index.gohtml:59
|
||||
msgctxt "action"
|
||||
msgid "Select invoice %v"
|
||||
msgstr "Selecciona factura %v"
|
||||
|
||||
#: web/template/invoices/index.gohtml:95 web/template/invoices/view.gohtml:16
|
||||
#: web/template/invoices/index.gohtml:109 web/template/invoices/view.gohtml:16
|
||||
msgctxt "action"
|
||||
msgid "Edit"
|
||||
msgstr "Edita"
|
||||
|
||||
#: web/template/invoices/index.gohtml:101 web/template/invoices/view.gohtml:15
|
||||
#: web/template/invoices/index.gohtml:115 web/template/invoices/view.gohtml:15
|
||||
msgctxt "action"
|
||||
msgid "Duplicate"
|
||||
msgstr "Duplica"
|
||||
|
||||
#: web/template/invoices/index.gohtml:111
|
||||
#: web/template/invoices/index.gohtml:125
|
||||
msgid "No invoices added yet."
|
||||
msgstr "No hi ha cap factura."
|
||||
|
||||
|
@ -451,48 +456,49 @@ msgstr "No podeu deixar la contrasenya en blanc."
|
|||
msgid "Invalid user or password."
|
||||
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||
|
||||
#: pkg/products.go:209 pkg/invoices.go:636
|
||||
#: pkg/products.go:209 pkg/invoices.go:728
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: pkg/products.go:215 pkg/invoices.go:641
|
||||
#: pkg/products.go:215 pkg/invoices.go:733
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripció"
|
||||
|
||||
#: pkg/products.go:220 pkg/invoices.go:645
|
||||
#: pkg/products.go:220 pkg/invoices.go:737
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Preu"
|
||||
|
||||
#: pkg/products.go:230 pkg/invoices.go:671
|
||||
#: pkg/products.go:230 pkg/invoices.go:763
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Imposts"
|
||||
|
||||
#: pkg/products.go:236 pkg/invoices.go:479 pkg/contacts.go:273
|
||||
#: pkg/products.go:236 pkg/invoices.go:192 pkg/invoices.go:571
|
||||
#: pkg/contacts.go:273
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: pkg/products.go:255 pkg/profile.go:92 pkg/invoices.go:710
|
||||
#: pkg/products.go:255 pkg/profile.go:92 pkg/invoices.go:802
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podeu deixar el nom en blanc."
|
||||
|
||||
#: pkg/products.go:256 pkg/invoices.go:711
|
||||
#: pkg/products.go:256 pkg/invoices.go:803
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podeu deixar el preu en blanc."
|
||||
|
||||
#: pkg/products.go:257 pkg/invoices.go:712
|
||||
#: pkg/products.go:257 pkg/invoices.go:804
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El preu ha de ser un número major a zero."
|
||||
|
||||
#: pkg/products.go:259 pkg/invoices.go:720
|
||||
#: pkg/products.go:259 pkg/invoices.go:812
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Heu seleccionat un impost que no és vàlid."
|
||||
|
||||
#: pkg/products.go:260 pkg/invoices.go:721
|
||||
#: pkg/products.go:260 pkg/invoices.go:813
|
||||
msgid "You can only select a tax of each class."
|
||||
msgstr "Només podeu seleccionar un impost de cada classe."
|
||||
|
||||
|
@ -600,100 +606,123 @@ msgstr "La confirmació no és igual a la contrasenya."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "Heu seleccionat un idioma que no és vàlid."
|
||||
|
||||
#: pkg/invoices.go:302
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escolliu un client a facturar."
|
||||
|
||||
#: pkg/invoices.go:401
|
||||
msgid "invoices.zip"
|
||||
msgstr "factures.zip"
|
||||
|
||||
#: pkg/invoices.go:407 pkg/invoices.go:829
|
||||
msgid "Invalid action"
|
||||
msgstr "Acció invàlida."
|
||||
|
||||
#: pkg/invoices.go:451
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estat de la factura"
|
||||
|
||||
#: pkg/invoices.go:457
|
||||
#: pkg/invoices.go:165 pkg/invoices.go:549
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Client"
|
||||
|
||||
#: pkg/invoices.go:463
|
||||
#: pkg/invoices.go:166
|
||||
msgid "All customers"
|
||||
msgstr "Tots els clients"
|
||||
|
||||
#: pkg/invoices.go:171 pkg/invoices.go:543
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estat de la factura"
|
||||
|
||||
#: pkg/invoices.go:172
|
||||
msgid "All status"
|
||||
msgstr "Tots els estats"
|
||||
|
||||
#: pkg/invoices.go:177
|
||||
msgctxt "input"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/invoices.go:182
|
||||
msgctxt "input"
|
||||
msgid "From Date"
|
||||
msgstr "De la data"
|
||||
|
||||
#: pkg/invoices.go:187
|
||||
msgctxt "input"
|
||||
msgid "To Date"
|
||||
msgstr "A la data"
|
||||
|
||||
#: pkg/invoices.go:394
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escolliu un client a facturar."
|
||||
|
||||
#: pkg/invoices.go:493
|
||||
msgid "invoices.zip"
|
||||
msgstr "factures.zip"
|
||||
|
||||
#: pkg/invoices.go:499 pkg/invoices.go:921
|
||||
msgid "Invalid action"
|
||||
msgstr "Acció invàlida."
|
||||
|
||||
#: pkg/invoices.go:555
|
||||
msgctxt "input"
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: pkg/invoices.go:468
|
||||
#: pkg/invoices.go:560
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Data de factura"
|
||||
|
||||
#: pkg/invoices.go:474
|
||||
#: pkg/invoices.go:566
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notes"
|
||||
|
||||
#: pkg/invoices.go:484
|
||||
#: pkg/invoices.go:576
|
||||
msgctxt "input"
|
||||
msgid "Payment Method"
|
||||
msgstr "Mètode de pagament"
|
||||
|
||||
#: pkg/invoices.go:521
|
||||
#: pkg/invoices.go:613
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "Heu seleccionat un estat de factura que no és vàlid."
|
||||
|
||||
#: pkg/invoices.go:522
|
||||
#: pkg/invoices.go:614
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "Heu seleccionat un client que no és vàlid."
|
||||
|
||||
#: pkg/invoices.go:523
|
||||
#: pkg/invoices.go:615
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podeu deixar la data de la factura en blanc."
|
||||
|
||||
#: pkg/invoices.go:524
|
||||
#: pkg/invoices.go:616
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La data de facturació ha de ser vàlida."
|
||||
|
||||
#: pkg/invoices.go:526
|
||||
#: pkg/invoices.go:618
|
||||
msgid "Selected payment method is not valid."
|
||||
msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
|
||||
|
||||
#: pkg/invoices.go:626 pkg/invoices.go:631
|
||||
#: pkg/invoices.go:718 pkg/invoices.go:723
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/invoices.go:654
|
||||
#: pkg/invoices.go:746
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Quantitat"
|
||||
|
||||
#: pkg/invoices.go:662
|
||||
#: pkg/invoices.go:754
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descompte (%)"
|
||||
|
||||
#: pkg/invoices.go:709
|
||||
#: pkg/invoices.go:801
|
||||
msgid "Product ID can not be empty."
|
||||
msgstr "No podeu deixar l’identificador del producte en blanc."
|
||||
|
||||
#: pkg/invoices.go:714
|
||||
#: pkg/invoices.go:806
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podeu deixar la quantitat en blanc."
|
||||
|
||||
#: pkg/invoices.go:715
|
||||
#: pkg/invoices.go:807
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La quantitat ha de ser un número major a zero."
|
||||
|
||||
#: pkg/invoices.go:717
|
||||
#: pkg/invoices.go:809
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podeu deixar el descompte en blanc."
|
||||
|
||||
#: pkg/invoices.go:718
|
||||
#: pkg/invoices.go:810
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
|
||||
|
||||
|
@ -806,10 +835,6 @@ msgstr "Aquest valor no és un codi postal vàlid."
|
|||
#~ msgid "Tax"
|
||||
#~ msgstr "Impost"
|
||||
|
||||
#~ msgctxt "nav"
|
||||
#~ msgid "Customers"
|
||||
#~ msgstr "Clients"
|
||||
|
||||
#~ msgctxt "title"
|
||||
#~ msgid "Customers"
|
||||
#~ msgstr "Clients"
|
||||
|
|
151
po/es.po
151
po/es.po
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: numerus\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-03-27 09:43+0200\n"
|
||||
"POT-Creation-Date: 2023-03-29 16:08+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"
|
||||
|
@ -103,68 +103,73 @@ msgctxt "action"
|
|||
msgid "Download invoices"
|
||||
msgstr "Descargar facturas"
|
||||
|
||||
#: web/template/invoices/index.gohtml:31
|
||||
#: web/template/invoices/index.gohtml:38
|
||||
msgctxt "action"
|
||||
msgid "Filter"
|
||||
msgstr "Filtrar"
|
||||
|
||||
#: web/template/invoices/index.gohtml:44
|
||||
msgctxt "invoice"
|
||||
msgid "All"
|
||||
msgstr "Todas"
|
||||
|
||||
#: web/template/invoices/index.gohtml:32 web/template/invoices/view.gohtml:31
|
||||
#: web/template/invoices/index.gohtml:45 web/template/invoices/view.gohtml:31
|
||||
msgctxt "title"
|
||||
msgid "Date"
|
||||
msgstr "Fecha"
|
||||
|
||||
#: web/template/invoices/index.gohtml:33
|
||||
#: web/template/invoices/index.gohtml:46
|
||||
msgctxt "title"
|
||||
msgid "Invoice Num."
|
||||
msgstr "Nº factura"
|
||||
|
||||
#: web/template/invoices/index.gohtml:34 web/template/contacts/index.gohtml:26
|
||||
#: web/template/invoices/index.gohtml:47 web/template/contacts/index.gohtml:26
|
||||
msgctxt "title"
|
||||
msgid "Customer"
|
||||
msgstr "Cliente"
|
||||
|
||||
#: web/template/invoices/index.gohtml:35
|
||||
#: web/template/invoices/index.gohtml:48
|
||||
msgctxt "title"
|
||||
msgid "Status"
|
||||
msgstr "Estado"
|
||||
|
||||
#: web/template/invoices/index.gohtml:36 web/template/contacts/index.gohtml:29
|
||||
#: web/template/invoices/index.gohtml:49 web/template/contacts/index.gohtml:29
|
||||
#: web/template/products/index.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: web/template/invoices/index.gohtml:37
|
||||
#: web/template/invoices/index.gohtml:50
|
||||
msgctxt "title"
|
||||
msgid "Amount"
|
||||
msgstr "Importe"
|
||||
|
||||
#: web/template/invoices/index.gohtml:38
|
||||
#: web/template/invoices/index.gohtml:51
|
||||
msgctxt "title"
|
||||
msgid "Download"
|
||||
msgstr "Descargar"
|
||||
|
||||
#: web/template/invoices/index.gohtml:39
|
||||
#: web/template/invoices/index.gohtml:52
|
||||
msgctxt "title"
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
#: web/template/invoices/index.gohtml:46
|
||||
#: web/template/invoices/index.gohtml:59
|
||||
msgctxt "action"
|
||||
msgid "Select invoice %v"
|
||||
msgstr "Seleccionar factura %v"
|
||||
|
||||
#: web/template/invoices/index.gohtml:95 web/template/invoices/view.gohtml:16
|
||||
#: web/template/invoices/index.gohtml:109 web/template/invoices/view.gohtml:16
|
||||
msgctxt "action"
|
||||
msgid "Edit"
|
||||
msgstr "Editar"
|
||||
|
||||
#: web/template/invoices/index.gohtml:101 web/template/invoices/view.gohtml:15
|
||||
#: web/template/invoices/index.gohtml:115 web/template/invoices/view.gohtml:15
|
||||
msgctxt "action"
|
||||
msgid "Duplicate"
|
||||
msgstr "Duplicar"
|
||||
|
||||
#: web/template/invoices/index.gohtml:111
|
||||
#: web/template/invoices/index.gohtml:125
|
||||
msgid "No invoices added yet."
|
||||
msgstr "No hay facturas."
|
||||
|
||||
|
@ -451,48 +456,49 @@ 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:209 pkg/invoices.go:636
|
||||
#: pkg/products.go:209 pkg/invoices.go:728
|
||||
msgctxt "input"
|
||||
msgid "Name"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: pkg/products.go:215 pkg/invoices.go:641
|
||||
#: pkg/products.go:215 pkg/invoices.go:733
|
||||
msgctxt "input"
|
||||
msgid "Description"
|
||||
msgstr "Descripción"
|
||||
|
||||
#: pkg/products.go:220 pkg/invoices.go:645
|
||||
#: pkg/products.go:220 pkg/invoices.go:737
|
||||
msgctxt "input"
|
||||
msgid "Price"
|
||||
msgstr "Precio"
|
||||
|
||||
#: pkg/products.go:230 pkg/invoices.go:671
|
||||
#: pkg/products.go:230 pkg/invoices.go:763
|
||||
msgctxt "input"
|
||||
msgid "Taxes"
|
||||
msgstr "Impuestos"
|
||||
|
||||
#: pkg/products.go:236 pkg/invoices.go:479 pkg/contacts.go:273
|
||||
#: pkg/products.go:236 pkg/invoices.go:192 pkg/invoices.go:571
|
||||
#: pkg/contacts.go:273
|
||||
msgctxt "input"
|
||||
msgid "Tags"
|
||||
msgstr "Etiquetes"
|
||||
|
||||
#: pkg/products.go:255 pkg/profile.go:92 pkg/invoices.go:710
|
||||
#: pkg/products.go:255 pkg/profile.go:92 pkg/invoices.go:802
|
||||
msgid "Name can not be empty."
|
||||
msgstr "No podéis dejar el nombre en blanco."
|
||||
|
||||
#: pkg/products.go:256 pkg/invoices.go:711
|
||||
#: pkg/products.go:256 pkg/invoices.go:803
|
||||
msgid "Price can not be empty."
|
||||
msgstr "No podéis dejar el precio en blanco."
|
||||
|
||||
#: pkg/products.go:257 pkg/invoices.go:712
|
||||
#: pkg/products.go:257 pkg/invoices.go:804
|
||||
msgid "Price must be a number greater than zero."
|
||||
msgstr "El precio tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/products.go:259 pkg/invoices.go:720
|
||||
#: pkg/products.go:259 pkg/invoices.go:812
|
||||
msgid "Selected tax is not valid."
|
||||
msgstr "Habéis escogido un impuesto que no es válido."
|
||||
|
||||
#: pkg/products.go:260 pkg/invoices.go:721
|
||||
#: pkg/products.go:260 pkg/invoices.go:813
|
||||
msgid "You can only select a tax of each class."
|
||||
msgstr "Solo podéis escoger un impuesto de cada clase."
|
||||
|
||||
|
@ -600,100 +606,123 @@ msgstr "La confirmación no corresponde con la contraseña."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "Habéis escogido un idioma que no es válido."
|
||||
|
||||
#: pkg/invoices.go:302
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escoged un cliente a facturar."
|
||||
|
||||
#: pkg/invoices.go:401
|
||||
msgid "invoices.zip"
|
||||
msgstr "facturas.zip"
|
||||
|
||||
#: pkg/invoices.go:407 pkg/invoices.go:829
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción inválida."
|
||||
|
||||
#: pkg/invoices.go:451
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estado de la factura"
|
||||
|
||||
#: pkg/invoices.go:457
|
||||
#: pkg/invoices.go:165 pkg/invoices.go:549
|
||||
msgctxt "input"
|
||||
msgid "Customer"
|
||||
msgstr "Cliente"
|
||||
|
||||
#: pkg/invoices.go:463
|
||||
#: pkg/invoices.go:166
|
||||
msgid "All customers"
|
||||
msgstr "Todos los clientes"
|
||||
|
||||
#: pkg/invoices.go:171 pkg/invoices.go:543
|
||||
msgctxt "input"
|
||||
msgid "Invoice Status"
|
||||
msgstr "Estado de la factura"
|
||||
|
||||
#: pkg/invoices.go:172
|
||||
msgid "All status"
|
||||
msgstr "Todos los estados"
|
||||
|
||||
#: pkg/invoices.go:177
|
||||
msgctxt "input"
|
||||
msgid "Invoice Number"
|
||||
msgstr "Número de factura"
|
||||
|
||||
#: pkg/invoices.go:182
|
||||
msgctxt "input"
|
||||
msgid "From Date"
|
||||
msgstr "De la fecha"
|
||||
|
||||
#: pkg/invoices.go:187
|
||||
msgctxt "input"
|
||||
msgid "To Date"
|
||||
msgstr "A la fecha"
|
||||
|
||||
#: pkg/invoices.go:394
|
||||
msgid "Select a customer to bill."
|
||||
msgstr "Escoged un cliente a facturar."
|
||||
|
||||
#: pkg/invoices.go:493
|
||||
msgid "invoices.zip"
|
||||
msgstr "facturas.zip"
|
||||
|
||||
#: pkg/invoices.go:499 pkg/invoices.go:921
|
||||
msgid "Invalid action"
|
||||
msgstr "Acción inválida."
|
||||
|
||||
#: pkg/invoices.go:555
|
||||
msgctxt "input"
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: pkg/invoices.go:468
|
||||
#: pkg/invoices.go:560
|
||||
msgctxt "input"
|
||||
msgid "Invoice Date"
|
||||
msgstr "Fecha de factura"
|
||||
|
||||
#: pkg/invoices.go:474
|
||||
#: pkg/invoices.go:566
|
||||
msgctxt "input"
|
||||
msgid "Notes"
|
||||
msgstr "Notas"
|
||||
|
||||
#: pkg/invoices.go:484
|
||||
#: pkg/invoices.go:576
|
||||
msgctxt "input"
|
||||
msgid "Payment Method"
|
||||
msgstr "Método de pago"
|
||||
|
||||
#: pkg/invoices.go:521
|
||||
#: pkg/invoices.go:613
|
||||
msgid "Selected invoice status is not valid."
|
||||
msgstr "Habéis escogido un estado de factura que no es válido."
|
||||
|
||||
#: pkg/invoices.go:522
|
||||
#: pkg/invoices.go:614
|
||||
msgid "Selected customer is not valid."
|
||||
msgstr "Habéis escogido un cliente que no es válido."
|
||||
|
||||
#: pkg/invoices.go:523
|
||||
#: pkg/invoices.go:615
|
||||
msgid "Invoice date can not be empty."
|
||||
msgstr "No podéis dejar la fecha de la factura en blanco."
|
||||
|
||||
#: pkg/invoices.go:524
|
||||
#: pkg/invoices.go:616
|
||||
msgid "Invoice date must be a valid date."
|
||||
msgstr "La fecha de factura debe ser válida."
|
||||
|
||||
#: pkg/invoices.go:526
|
||||
#: pkg/invoices.go:618
|
||||
msgid "Selected payment method is not valid."
|
||||
msgstr "Habéis escogido un método de pago que no es válido."
|
||||
|
||||
#: pkg/invoices.go:626 pkg/invoices.go:631
|
||||
#: pkg/invoices.go:718 pkg/invoices.go:723
|
||||
msgctxt "input"
|
||||
msgid "Id"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pkg/invoices.go:654
|
||||
#: pkg/invoices.go:746
|
||||
msgctxt "input"
|
||||
msgid "Quantity"
|
||||
msgstr "Cantidad"
|
||||
|
||||
#: pkg/invoices.go:662
|
||||
#: pkg/invoices.go:754
|
||||
msgctxt "input"
|
||||
msgid "Discount (%)"
|
||||
msgstr "Descuento (%)"
|
||||
|
||||
#: pkg/invoices.go:709
|
||||
#: pkg/invoices.go:801
|
||||
msgid "Product ID can not be empty."
|
||||
msgstr "No podéis dejar el identificador de producto en blanco."
|
||||
|
||||
#: pkg/invoices.go:714
|
||||
#: pkg/invoices.go:806
|
||||
msgid "Quantity can not be empty."
|
||||
msgstr "No podéis dejar la cantidad en blanco."
|
||||
|
||||
#: pkg/invoices.go:715
|
||||
#: pkg/invoices.go:807
|
||||
msgid "Quantity must be a number greater than zero."
|
||||
msgstr "La cantidad tiene que ser un número mayor a cero."
|
||||
|
||||
#: pkg/invoices.go:717
|
||||
#: pkg/invoices.go:809
|
||||
msgid "Discount can not be empty."
|
||||
msgstr "No podéis dejar el descuento en blanco."
|
||||
|
||||
#: pkg/invoices.go:718
|
||||
#: pkg/invoices.go:810
|
||||
msgid "Discount must be a percentage between 0 and 100."
|
||||
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
|
||||
|
||||
|
@ -806,10 +835,6 @@ msgstr "Este valor no es un código postal válido válido."
|
|||
#~ msgid "Tax"
|
||||
#~ msgstr "Impuesto"
|
||||
|
||||
#~ msgctxt "nav"
|
||||
#~ msgid "Customers"
|
||||
#~ msgstr "Clientes"
|
||||
|
||||
#~ msgctxt "title"
|
||||
#~ msgid "Customers"
|
||||
#~ msgstr "Clientes"
|
||||
|
|
|
@ -25,6 +25,19 @@
|
|||
|
||||
{{ define "content" }}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.InvoicesIndexPage*/ -}}
|
||||
<div aria-label="{{( pgettext "Filters" "title" )}}">
|
||||
<form method="GET" action="{{ companyURI "/invoices"}}" data-hx-target="main" data-hx-boost="true">
|
||||
{{ with .Filters }}
|
||||
{{ template "select-field" .Customer }}
|
||||
{{ template "select-field" .InvoiceStatus }}
|
||||
{{ template "input-field" .FromDate }}
|
||||
{{ template "input-field" .ToDate }}
|
||||
{{ template "input-field" .InvoiceNumber }}
|
||||
{{ template "tags-field" .Tags }}
|
||||
{{ end }}
|
||||
<button type="submit">{{( pgettext "Filter" "action" )}}</button>
|
||||
</form>
|
||||
</div>
|
||||
<table class="no-padding">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -49,7 +62,8 @@
|
|||
aria-label="{{ $title }}"
|
||||
title="{{ $title }}"/></td>
|
||||
<td>{{ .Date|formatDate }}</td>
|
||||
<td><a href="{{ companyURI "/invoices/"}}{{ .Slug }}" data-hx-target="main" data-hx-boost="true">{{ .Number }}</a></td>
|
||||
<td><a href="{{ companyURI "/invoices/"}}{{ .Slug }}" data-hx-target="main"
|
||||
data-hx-boost="true">{{ .Number }}</a></td>
|
||||
<td>{{ .CustomerName }}</td>
|
||||
<td>
|
||||
<details class="invoice-status menu">
|
||||
|
@ -76,7 +90,7 @@
|
|||
<td>
|
||||
{{- range $index, $tag := .Tags }}
|
||||
{{- if gt $index 0 }}, {{ end -}}
|
||||
<a href="?tag={{ . }}" data-hx-target="main" data-hx-boost="true">{{ . }}</a>
|
||||
{{ . }}
|
||||
{{- end }}
|
||||
</td>
|
||||
<td class="numeric">{{ .Total|formatPrice }}</td>
|
||||
|
|
Loading…
Reference in New Issue