Add invoice view, with print CSS
Had to group name and description rows in tbody because i do not want to break them on pagination. I also could not use tfoot for subtotal, taxes, and total because then they appear on every page. The disclaimer should appear only at the very bottom of the last page, but i do not know how to do that; using position fixed shows it on every page.
This commit is contained in:
parent
985f843e8e
commit
18fba2964f
|
@ -64,6 +64,73 @@ func GetInvoiceForm(w http.ResponseWriter, r *http.Request, params httprouter.Pa
|
||||||
mustRenderNewInvoiceForm(w, r, form)
|
mustRenderNewInvoiceForm(w, r, form)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invoice := mustGetInvoice(r.Context(), conn, company, slug)
|
||||||
|
if invoice == nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mustRenderAppTemplate(w, r, "invoices/view.gohtml", invoice)
|
||||||
|
}
|
||||||
|
|
||||||
|
type invoice struct {
|
||||||
|
Number string
|
||||||
|
Date time.Time
|
||||||
|
Invoicer taxDetails
|
||||||
|
Invoicee taxDetails
|
||||||
|
Notes string
|
||||||
|
Products []*invoiceProduct
|
||||||
|
Subtotal string
|
||||||
|
Taxes [][]string
|
||||||
|
Total string
|
||||||
|
}
|
||||||
|
|
||||||
|
type taxDetails struct {
|
||||||
|
Name string
|
||||||
|
VATIN string
|
||||||
|
Address string
|
||||||
|
City string
|
||||||
|
PostalCode string
|
||||||
|
Province string
|
||||||
|
Email string
|
||||||
|
Phone string
|
||||||
|
}
|
||||||
|
|
||||||
|
type invoiceProduct struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Price string
|
||||||
|
Quantity int
|
||||||
|
Total string
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustGetInvoice(ctx context.Context, conn *Conn, company *Company, slug string) *invoice {
|
||||||
|
inv := &invoice{}
|
||||||
|
var invoiceId int
|
||||||
|
var decimalDigits int
|
||||||
|
if notFoundErrorOrPanic(conn.QueryRow(ctx, "select invoice_id, decimal_digits, invoice_number, invoice_date, notes, business_name, vatin, phone, email, address, city, province, postal_code, to_price(subtotal, decimal_digits), to_price(total, decimal_digits) from invoice join contact using (contact_id) join invoice_amount using (invoice_id) join currency using (currency_code) where invoice.slug = $1", slug).Scan(&invoiceId, &decimalDigits, &inv.Number, &inv.Date, &inv.Notes, &inv.Invoicee.Name, &inv.Invoicee.VATIN, &inv.Invoicee.Phone, &inv.Invoicee.Email, &inv.Invoicee.Address, &inv.Invoicee.City, &inv.Invoicee.Province, &inv.Invoicee.PostalCode, &inv.Subtotal, &inv.Total)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := conn.QueryRow(ctx, "select business_name, vatin, phone, email, address, city, province, postal_code from company where company_id = $1", company.Id).Scan(&inv.Invoicer.Name, &inv.Invoicer.VATIN, &inv.Invoicer.Phone, &inv.Invoicer.Email, &inv.Invoicer.Address, &inv.Invoicer.City, &inv.Invoicer.Province, &inv.Invoicer.PostalCode); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := conn.QueryRow(ctx, "select array_agg(array[name, to_price(amount, $2)]) from invoice_tax_amount join tax using (tax_id) where invoice_id = $1", invoiceId, decimalDigits).Scan(&inv.Taxes); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rows := conn.MustQuery(ctx, "select name, description, to_price(price, $2), quantity, to_price(round(price * quantity * (1 - discount_rate))::integer, 2) from invoice_product where invoice_id = $1", invoiceId, decimalDigits)
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
product := &invoiceProduct{}
|
||||||
|
if err := rows.Scan(&product.Name, &product.Description, &product.Price, &product.Quantity, &product.Total); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
inv.Products = append(inv.Products, product)
|
||||||
|
}
|
||||||
|
if rows.Err() != nil {
|
||||||
|
panic(rows.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
return inv
|
||||||
}
|
}
|
||||||
|
|
||||||
type newInvoicePage struct {
|
type newInvoicePage struct {
|
||||||
|
|
|
@ -40,8 +40,8 @@ func mustRenderTemplate(wr io.Writer, r *http.Request, layout string, filename s
|
||||||
}
|
}
|
||||||
return p.Sprintf(locale.CurrencyPattern, company.DecimalDigits, number.Decimal(f), company.CurrencySymbol)
|
return p.Sprintf(locale.CurrencyPattern, company.DecimalDigits, number.Decimal(f), company.CurrencySymbol)
|
||||||
},
|
},
|
||||||
"formatDate": func(time time.Time) string {
|
"formatDate": func(time time.Time) template.HTML {
|
||||||
return time.Format("02/01/2006")
|
return template.HTML(`<time datetime="` + time.Format("2006-01-02") + `">` + time.Format("02/01/2006") + "</time>")
|
||||||
},
|
},
|
||||||
"csrfToken": func() template.HTML {
|
"csrfToken": func() template.HTML {
|
||||||
return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`, csrfTokenField, user.CsrfToken))
|
return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`, csrfTokenField, user.CsrfToken))
|
||||||
|
|
97
po/ca.po
97
po/ca.po
|
@ -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: 2023-02-22 14:35+0100\n"
|
"POT-Creation-Date: 2023-02-24 11:52+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"
|
||||||
|
@ -24,17 +24,18 @@ msgid "Add Products to Invoice"
|
||||||
msgstr "Afegeix productes a la factura"
|
msgstr "Afegeix productes a la factura"
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:9 web/template/invoices/new.gohtml:9
|
#: web/template/invoices/products.gohtml:9 web/template/invoices/new.gohtml:9
|
||||||
#: web/template/invoices/index.gohtml:8 web/template/contacts/new.gohtml:9
|
#: web/template/invoices/index.gohtml:8 web/template/invoices/view.gohtml:9
|
||||||
#: web/template/contacts/index.gohtml:8 web/template/contacts/edit.gohtml:9
|
#: web/template/contacts/new.gohtml:9 web/template/contacts/index.gohtml:8
|
||||||
#: web/template/profile.gohtml:9 web/template/tax-details.gohtml:8
|
#: web/template/contacts/edit.gohtml:9 web/template/profile.gohtml:9
|
||||||
#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:8
|
#: web/template/tax-details.gohtml:8 web/template/products/new.gohtml:9
|
||||||
#: web/template/products/edit.gohtml:9
|
#: web/template/products/index.gohtml:8 web/template/products/edit.gohtml:9
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Inici"
|
msgstr "Inici"
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:10 web/template/invoices/new.gohtml:10
|
#: web/template/invoices/products.gohtml:10 web/template/invoices/new.gohtml:10
|
||||||
#: web/template/invoices/index.gohtml:2 web/template/invoices/index.gohtml:9
|
#: web/template/invoices/index.gohtml:2 web/template/invoices/index.gohtml:9
|
||||||
|
#: web/template/invoices/view.gohtml:10
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Invoices"
|
msgid "Invoices"
|
||||||
msgstr "Factures"
|
msgstr "Factures"
|
||||||
|
@ -58,7 +59,7 @@ msgid "Name"
|
||||||
msgstr "Nom"
|
msgstr "Nom"
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:43
|
#: web/template/invoices/products.gohtml:43
|
||||||
#: web/template/products/index.gohtml:23
|
#: web/template/invoices/view.gohtml:42 web/template/products/index.gohtml:23
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Price"
|
msgid "Price"
|
||||||
msgstr "Preu"
|
msgstr "Preu"
|
||||||
|
@ -68,17 +69,28 @@ msgstr "Preu"
|
||||||
msgid "No products added yet."
|
msgid "No products added yet."
|
||||||
msgstr "No hi ha cap producte."
|
msgstr "No hi ha cap producte."
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:38
|
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:59
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Add products"
|
msgid "Add products"
|
||||||
msgstr "Afegeix productes"
|
msgstr "Afegeix productes"
|
||||||
|
|
||||||
#: web/template/invoices/new.gohtml:40
|
#: web/template/invoices/new.gohtml:41 web/template/invoices/view.gohtml:44
|
||||||
|
#: web/template/invoices/view.gohtml:71
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Subtotal"
|
||||||
|
msgstr "Subtotal"
|
||||||
|
|
||||||
|
#: web/template/invoices/new.gohtml:51 web/template/invoices/view.gohtml:81
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Total"
|
||||||
|
msgstr "Total"
|
||||||
|
|
||||||
|
#: web/template/invoices/new.gohtml:61
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Update"
|
msgid "Update"
|
||||||
msgstr "Actualitza"
|
msgstr "Actualitza"
|
||||||
|
|
||||||
#: web/template/invoices/new.gohtml:42 web/template/invoices/index.gohtml:13
|
#: web/template/invoices/new.gohtml:63 web/template/invoices/index.gohtml:13
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "New invoice"
|
msgid "New invoice"
|
||||||
msgstr "Nova factura"
|
msgstr "Nova factura"
|
||||||
|
@ -88,7 +100,7 @@ msgctxt "invoice"
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Totes"
|
msgstr "Totes"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:22
|
#: web/template/invoices/index.gohtml:22 web/template/invoices/view.gohtml:18
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Data"
|
msgstr "Data"
|
||||||
|
@ -127,6 +139,21 @@ msgstr "Descàrrega"
|
||||||
msgid "No invoices added yet."
|
msgid "No invoices added yet."
|
||||||
msgstr "No hi ha cap factura."
|
msgstr "No hi ha cap factura."
|
||||||
|
|
||||||
|
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:17
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Invoice %s"
|
||||||
|
msgstr "Factura %s"
|
||||||
|
|
||||||
|
#: web/template/invoices/view.gohtml:41
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Concept"
|
||||||
|
msgstr "Concepte"
|
||||||
|
|
||||||
|
#: web/template/invoices/view.gohtml:43
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Units"
|
||||||
|
msgstr "Unitats"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:2
|
#: web/template/dashboard.gohtml:2
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
|
@ -337,40 +364,40 @@ msgstr "No podeu deixar la contrasenya en blanc."
|
||||||
msgid "Invalid user or password."
|
msgid "Invalid user or password."
|
||||||
msgstr "Nom d’usuari o contrasenya incorrectes."
|
msgstr "Nom d’usuari o contrasenya incorrectes."
|
||||||
|
|
||||||
#: pkg/products.go:165 pkg/invoices.go:304
|
#: pkg/products.go:165 pkg/invoices.go:392
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nom"
|
msgstr "Nom"
|
||||||
|
|
||||||
#: pkg/products.go:171 pkg/invoices.go:309
|
#: pkg/products.go:171 pkg/invoices.go:397
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Descripció"
|
msgstr "Descripció"
|
||||||
|
|
||||||
#: pkg/products.go:176 pkg/invoices.go:313
|
#: pkg/products.go:176 pkg/invoices.go:401
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Price"
|
msgid "Price"
|
||||||
msgstr "Preu"
|
msgstr "Preu"
|
||||||
|
|
||||||
#: pkg/products.go:186 pkg/invoices.go:219 pkg/invoices.go:339
|
#: pkg/products.go:186 pkg/invoices.go:307 pkg/invoices.go:427
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Taxes"
|
msgid "Taxes"
|
||||||
msgstr "Imposts"
|
msgstr "Imposts"
|
||||||
|
|
||||||
#: pkg/products.go:206 pkg/profile.go:92 pkg/invoices.go:252
|
#: pkg/products.go:206 pkg/profile.go:92 pkg/invoices.go:340
|
||||||
#: pkg/invoices.go:375
|
#: pkg/invoices.go:463
|
||||||
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:207 pkg/invoices.go:376
|
#: pkg/products.go:207 pkg/invoices.go:464
|
||||||
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:208 pkg/invoices.go:377
|
#: pkg/products.go:208 pkg/invoices.go:465
|
||||||
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:210 pkg/invoices.go:256 pkg/invoices.go:385
|
#: pkg/products.go:210 pkg/invoices.go:344 pkg/invoices.go:473
|
||||||
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."
|
||||||
|
|
||||||
|
@ -433,70 +460,70 @@ msgstr "La confirmació no és igual a la contrasenya."
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "Heu seleccionat un idioma que no és vàlid."
|
msgstr "Heu seleccionat un idioma que no és vàlid."
|
||||||
|
|
||||||
#: pkg/invoices.go:71
|
#: pkg/invoices.go:158
|
||||||
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:145
|
#: pkg/invoices.go:233
|
||||||
msgid "Invalid action"
|
msgid "Invalid action"
|
||||||
msgstr "Acció invàlida."
|
msgstr "Acció invàlida."
|
||||||
|
|
||||||
#: pkg/invoices.go:196
|
#: pkg/invoices.go:284
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr "Client"
|
msgstr "Client"
|
||||||
|
|
||||||
#: pkg/invoices.go:202
|
#: pkg/invoices.go:290
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Número"
|
msgstr "Número"
|
||||||
|
|
||||||
#: pkg/invoices.go:208
|
#: pkg/invoices.go:296
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Invoice Date"
|
msgid "Invoice Date"
|
||||||
msgstr "Data de factura"
|
msgstr "Data de factura"
|
||||||
|
|
||||||
#: pkg/invoices.go:214
|
#: pkg/invoices.go:302
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Notes"
|
msgid "Notes"
|
||||||
msgstr "Notes"
|
msgstr "Notes"
|
||||||
|
|
||||||
#: pkg/invoices.go:253
|
#: pkg/invoices.go:341
|
||||||
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:254
|
#: pkg/invoices.go:342
|
||||||
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/invoices.go:299
|
#: pkg/invoices.go:387
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr "Identificador"
|
msgstr "Identificador"
|
||||||
|
|
||||||
#: pkg/invoices.go:322
|
#: pkg/invoices.go:410
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
msgstr "Quantitat"
|
msgstr "Quantitat"
|
||||||
|
|
||||||
#: pkg/invoices.go:330
|
#: pkg/invoices.go:418
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Discount (%)"
|
msgid "Discount (%)"
|
||||||
msgstr "Descompte (%)"
|
msgstr "Descompte (%)"
|
||||||
|
|
||||||
#: pkg/invoices.go:379
|
#: pkg/invoices.go:467
|
||||||
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/invoices.go:380
|
#: pkg/invoices.go:468
|
||||||
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/invoices.go:382
|
#: pkg/invoices.go:470
|
||||||
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/invoices.go:383
|
#: pkg/invoices.go:471
|
||||||
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."
|
||||||
|
|
||||||
|
|
97
po/es.po
97
po/es.po
|
@ -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: 2023-02-22 14:35+0100\n"
|
"POT-Creation-Date: 2023-02-24 11:52+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"
|
||||||
|
@ -24,17 +24,18 @@ msgid "Add Products to Invoice"
|
||||||
msgstr "Añadir productos a la factura"
|
msgstr "Añadir productos a la factura"
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:9 web/template/invoices/new.gohtml:9
|
#: web/template/invoices/products.gohtml:9 web/template/invoices/new.gohtml:9
|
||||||
#: web/template/invoices/index.gohtml:8 web/template/contacts/new.gohtml:9
|
#: web/template/invoices/index.gohtml:8 web/template/invoices/view.gohtml:9
|
||||||
#: web/template/contacts/index.gohtml:8 web/template/contacts/edit.gohtml:9
|
#: web/template/contacts/new.gohtml:9 web/template/contacts/index.gohtml:8
|
||||||
#: web/template/profile.gohtml:9 web/template/tax-details.gohtml:8
|
#: web/template/contacts/edit.gohtml:9 web/template/profile.gohtml:9
|
||||||
#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:8
|
#: web/template/tax-details.gohtml:8 web/template/products/new.gohtml:9
|
||||||
#: web/template/products/edit.gohtml:9
|
#: web/template/products/index.gohtml:8 web/template/products/edit.gohtml:9
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Inicio"
|
msgstr "Inicio"
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:10 web/template/invoices/new.gohtml:10
|
#: web/template/invoices/products.gohtml:10 web/template/invoices/new.gohtml:10
|
||||||
#: web/template/invoices/index.gohtml:2 web/template/invoices/index.gohtml:9
|
#: web/template/invoices/index.gohtml:2 web/template/invoices/index.gohtml:9
|
||||||
|
#: web/template/invoices/view.gohtml:10
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Invoices"
|
msgid "Invoices"
|
||||||
msgstr "Facturas"
|
msgstr "Facturas"
|
||||||
|
@ -58,7 +59,7 @@ msgid "Name"
|
||||||
msgstr "Nombre"
|
msgstr "Nombre"
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:43
|
#: web/template/invoices/products.gohtml:43
|
||||||
#: web/template/products/index.gohtml:23
|
#: web/template/invoices/view.gohtml:42 web/template/products/index.gohtml:23
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Price"
|
msgid "Price"
|
||||||
msgstr "Precio"
|
msgstr "Precio"
|
||||||
|
@ -68,17 +69,28 @@ msgstr "Precio"
|
||||||
msgid "No products added yet."
|
msgid "No products added yet."
|
||||||
msgstr "No hay productos."
|
msgstr "No hay productos."
|
||||||
|
|
||||||
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:38
|
#: web/template/invoices/products.gohtml:64 web/template/invoices/new.gohtml:59
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Add products"
|
msgid "Add products"
|
||||||
msgstr "Añadir productos"
|
msgstr "Añadir productos"
|
||||||
|
|
||||||
#: web/template/invoices/new.gohtml:40
|
#: web/template/invoices/new.gohtml:41 web/template/invoices/view.gohtml:44
|
||||||
|
#: web/template/invoices/view.gohtml:71
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Subtotal"
|
||||||
|
msgstr "Subtotal"
|
||||||
|
|
||||||
|
#: web/template/invoices/new.gohtml:51 web/template/invoices/view.gohtml:81
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Total"
|
||||||
|
msgstr "Total"
|
||||||
|
|
||||||
|
#: web/template/invoices/new.gohtml:61
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "Update"
|
msgid "Update"
|
||||||
msgstr "Actualizar"
|
msgstr "Actualizar"
|
||||||
|
|
||||||
#: web/template/invoices/new.gohtml:42 web/template/invoices/index.gohtml:13
|
#: web/template/invoices/new.gohtml:63 web/template/invoices/index.gohtml:13
|
||||||
msgctxt "action"
|
msgctxt "action"
|
||||||
msgid "New invoice"
|
msgid "New invoice"
|
||||||
msgstr "Nueva factura"
|
msgstr "Nueva factura"
|
||||||
|
@ -88,7 +100,7 @@ msgctxt "invoice"
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "Todas"
|
msgstr "Todas"
|
||||||
|
|
||||||
#: web/template/invoices/index.gohtml:22
|
#: web/template/invoices/index.gohtml:22 web/template/invoices/view.gohtml:18
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Fecha"
|
msgstr "Fecha"
|
||||||
|
@ -127,6 +139,21 @@ msgstr "Descargar"
|
||||||
msgid "No invoices added yet."
|
msgid "No invoices added yet."
|
||||||
msgstr "No hay facturas."
|
msgstr "No hay facturas."
|
||||||
|
|
||||||
|
#: web/template/invoices/view.gohtml:2 web/template/invoices/view.gohtml:17
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Invoice %s"
|
||||||
|
msgstr "Factura %s"
|
||||||
|
|
||||||
|
#: web/template/invoices/view.gohtml:41
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Concept"
|
||||||
|
msgstr "Concepto"
|
||||||
|
|
||||||
|
#: web/template/invoices/view.gohtml:43
|
||||||
|
msgctxt "title"
|
||||||
|
msgid "Units"
|
||||||
|
msgstr "Unidades"
|
||||||
|
|
||||||
#: web/template/dashboard.gohtml:2
|
#: web/template/dashboard.gohtml:2
|
||||||
msgctxt "title"
|
msgctxt "title"
|
||||||
msgid "Dashboard"
|
msgid "Dashboard"
|
||||||
|
@ -337,40 +364,40 @@ msgstr "No podéis dejar la contraseña en blanco."
|
||||||
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:165 pkg/invoices.go:304
|
#: pkg/products.go:165 pkg/invoices.go:392
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nombre"
|
msgstr "Nombre"
|
||||||
|
|
||||||
#: pkg/products.go:171 pkg/invoices.go:309
|
#: pkg/products.go:171 pkg/invoices.go:397
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Descripción"
|
msgstr "Descripción"
|
||||||
|
|
||||||
#: pkg/products.go:176 pkg/invoices.go:313
|
#: pkg/products.go:176 pkg/invoices.go:401
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Price"
|
msgid "Price"
|
||||||
msgstr "Precio"
|
msgstr "Precio"
|
||||||
|
|
||||||
#: pkg/products.go:186 pkg/invoices.go:219 pkg/invoices.go:339
|
#: pkg/products.go:186 pkg/invoices.go:307 pkg/invoices.go:427
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Taxes"
|
msgid "Taxes"
|
||||||
msgstr "Impuestos"
|
msgstr "Impuestos"
|
||||||
|
|
||||||
#: pkg/products.go:206 pkg/profile.go:92 pkg/invoices.go:252
|
#: pkg/products.go:206 pkg/profile.go:92 pkg/invoices.go:340
|
||||||
#: pkg/invoices.go:375
|
#: pkg/invoices.go:463
|
||||||
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:207 pkg/invoices.go:376
|
#: pkg/products.go:207 pkg/invoices.go:464
|
||||||
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:208 pkg/invoices.go:377
|
#: pkg/products.go:208 pkg/invoices.go:465
|
||||||
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:210 pkg/invoices.go:256 pkg/invoices.go:385
|
#: pkg/products.go:210 pkg/invoices.go:344 pkg/invoices.go:473
|
||||||
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."
|
||||||
|
|
||||||
|
@ -433,70 +460,70 @@ msgstr "La confirmación no corresponde con la contraseña."
|
||||||
msgid "Selected language is not valid."
|
msgid "Selected language is not valid."
|
||||||
msgstr "Habéis escogido un idioma que no es válido."
|
msgstr "Habéis escogido un idioma que no es válido."
|
||||||
|
|
||||||
#: pkg/invoices.go:71
|
#: pkg/invoices.go:158
|
||||||
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:145
|
#: pkg/invoices.go:233
|
||||||
msgid "Invalid action"
|
msgid "Invalid action"
|
||||||
msgstr "Acción inválida."
|
msgstr "Acción inválida."
|
||||||
|
|
||||||
#: pkg/invoices.go:196
|
#: pkg/invoices.go:284
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Customer"
|
msgid "Customer"
|
||||||
msgstr "Cliente"
|
msgstr "Cliente"
|
||||||
|
|
||||||
#: pkg/invoices.go:202
|
#: pkg/invoices.go:290
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Número"
|
msgstr "Número"
|
||||||
|
|
||||||
#: pkg/invoices.go:208
|
#: pkg/invoices.go:296
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Invoice Date"
|
msgid "Invoice Date"
|
||||||
msgstr "Fecha de factura"
|
msgstr "Fecha de factura"
|
||||||
|
|
||||||
#: pkg/invoices.go:214
|
#: pkg/invoices.go:302
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Notes"
|
msgid "Notes"
|
||||||
msgstr "Notas"
|
msgstr "Notas"
|
||||||
|
|
||||||
#: pkg/invoices.go:253
|
#: pkg/invoices.go:341
|
||||||
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:254
|
#: pkg/invoices.go:342
|
||||||
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/invoices.go:299
|
#: pkg/invoices.go:387
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Id"
|
msgid "Id"
|
||||||
msgstr "Identificador"
|
msgstr "Identificador"
|
||||||
|
|
||||||
#: pkg/invoices.go:322
|
#: pkg/invoices.go:410
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
msgstr "Cantidad"
|
msgstr "Cantidad"
|
||||||
|
|
||||||
#: pkg/invoices.go:330
|
#: pkg/invoices.go:418
|
||||||
msgctxt "input"
|
msgctxt "input"
|
||||||
msgid "Discount (%)"
|
msgid "Discount (%)"
|
||||||
msgstr "Descuento (%)"
|
msgstr "Descuento (%)"
|
||||||
|
|
||||||
#: pkg/invoices.go:379
|
#: pkg/invoices.go:467
|
||||||
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/invoices.go:380
|
#: pkg/invoices.go:468
|
||||||
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/invoices.go:382
|
#: pkg/invoices.go:470
|
||||||
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/invoices.go:383
|
#: pkg/invoices.go:471
|
||||||
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 percentage entre 0 y 100."
|
msgstr "El descuento tiene que ser un percentage entre 0 y 100."
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
.invoice {
|
||||||
|
display: flex;
|
||||||
|
gap: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice header {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 20rem;
|
||||||
|
padding: 0 1rem 0 0;
|
||||||
|
display: block;
|
||||||
|
background: initial;
|
||||||
|
border-right: 2px dashed lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice h1 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .invoicer {
|
||||||
|
margin-top: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 3;
|
||||||
|
gap: 5rem;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice th {
|
||||||
|
font-weight: normal;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice thead th {
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .tfoot th, .invoice .numeric {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice .notes, .invoice footer {
|
||||||
|
white-space: pre;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice tbody, .invoice footer {
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice tbody tr {
|
||||||
|
background-color: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.invoice tbody:not(:first-of-type) tr:first-child td, .invoice .tfoot th, .invoice .tfoot td {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice tbody .name td:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice tbody td:first-child {
|
||||||
|
max-width: 20rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice footer {
|
||||||
|
margin-top: 8rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
@page {
|
||||||
|
size: A4;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 62.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, h1, h2, h3, h4, h5, h6 {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > header, nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
{{ define "title" -}}
|
||||||
|
{{ .Number | printf ( pgettext "Invoice %s" "title" )}}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.invoice*/ -}}
|
||||||
|
<nav>
|
||||||
|
<p>
|
||||||
|
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
|
||||||
|
<a href="{{ companyURI "/invoices"}}">{{( pgettext "Invoices" "title" )}}</a> /
|
||||||
|
<a>{{ .Number }}</a>
|
||||||
|
</p>
|
||||||
|
</nav>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/invoice.css">
|
||||||
|
<article class="invoice">
|
||||||
|
<header>
|
||||||
|
<h1>{{ .Number | printf ( pgettext "Invoice %s" "title" )}}</h1>
|
||||||
|
<p class="date">{{( pgettext "Date" "title" )}} {{ .Date | formatDate }}</p>
|
||||||
|
|
||||||
|
<address class="invoicer">
|
||||||
|
{{ .Invoicer.Name }}<br>
|
||||||
|
{{ .Invoicer.VATIN }}<br>
|
||||||
|
{{ .Invoicer.Address }}<br>
|
||||||
|
{{ .Invoicer.City }} ({{ .Invoicer.PostalCode}}), {{ .Invoicer.Province }}<br>
|
||||||
|
{{ .Invoicer.Email }}<br>
|
||||||
|
{{ .Invoicer.Phone }}<br>
|
||||||
|
</address>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<address class="invoicee">
|
||||||
|
{{ .Invoicee.Name }}<br>
|
||||||
|
{{ .Invoicee.VATIN }}<br>
|
||||||
|
{{ .Invoicee.Address }}<br>
|
||||||
|
{{ .Invoicee.City }} ({{ .Invoicee.PostalCode}}), {{ .Invoicee.Province }}<br>
|
||||||
|
</address>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{( pgettext "Concept" "title" )}}</th>
|
||||||
|
<th class="numeric">{{( pgettext "Price" "title" )}}</th>
|
||||||
|
<th class="numeric">{{( pgettext "Units" "title" )}}</th>
|
||||||
|
<th class="numeric">{{( pgettext "Subtotal" "title" )}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{{ range $product := .Products -}}
|
||||||
|
{{ if .Description }}
|
||||||
|
<tbody>
|
||||||
|
<tr class="name">
|
||||||
|
<td colspan="4">{{ .Name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ .Description }}</td>
|
||||||
|
<td class="numeric">{{ .Price | formatPrice }}</td>
|
||||||
|
<td class="numeric">{{ .Quantity }}</td>
|
||||||
|
<td class="numeric">{{ .Total | formatPrice }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{{ else }}
|
||||||
|
<tbody>
|
||||||
|
<tr class="name">
|
||||||
|
<td>{{ .Name }}</td>
|
||||||
|
<td class="numeric">{{ .Price | formatPrice }}</td>
|
||||||
|
<td class="numeric">{{ .Quantity }}</td>
|
||||||
|
<td class="numeric">{{ .Total | formatPrice }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
<tbody class="tfoot">
|
||||||
|
<tr>
|
||||||
|
<th scope="row" colspan="3">{{( pgettext "Subtotal" "title" )}}</th>
|
||||||
|
<td class="numeric">{{ .Subtotal | formatPrice }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ range $tax := .Taxes -}}
|
||||||
|
<tr>
|
||||||
|
<th scope="row" colspan="3">{{ index . 0 }}</th>
|
||||||
|
<td class="numeric">{{ index . 1 | formatPrice }}</td>
|
||||||
|
</tr>
|
||||||
|
{{- end }}
|
||||||
|
<tr>
|
||||||
|
<th scope="row" colspan="3">{{( pgettext "Total" "title" )}}</th>
|
||||||
|
<td class="numeric">{{ .Total | formatPrice }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="notes">{{ .Notes }}</div>
|
||||||
|
|
||||||
|
<footer>Oriol Carbonell Pujolàs és Responsable del Tractament de les seves dades d'acord
|
||||||
|
amb el RGPD i la LOPDGDDGDD, i les tracta per a mantenir una relació
|
||||||
|
mercantil/comercial amb vostè. Les conservarà mentre es mantingui aquesta relació
|
||||||
|
i no es comunicaran a tercers. Pot exercir els drets d'accés, rectificació, portabilitat,
|
||||||
|
supressió, limitació i oposició a Oriol Carbonell pUJOLÀS, amb domicili Carrer dels
|
||||||
|
Sastres 14, 17800 Olot o enviant un correu electrònic a info@visavis.cat. Per a
|
||||||
|
qualsevol reclamació pot acudir a agpd.es. Per a més informació pot consultar la
|
||||||
|
nostra política de privacitat a www.visavis.cat.
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{- end}}
|
Loading…
Reference in New Issue