Add HTTP controller and view to add quotes

It still does not support quotes without contact or payment.
This commit is contained in:
jordi fita mas 2023-06-07 16:35:31 +02:00
parent efbb4da07f
commit 9931796744
15 changed files with 2457 additions and 321 deletions

View File

@ -604,8 +604,8 @@ func newInvoiceForm(ctx context.Context, conn *Conn, locale *Locale, company *Co
Name: "payment_method",
Required: true,
Label: pgettext("input", "Payment Method", locale),
Selected: []string{conn.MustGetText(ctx, "", "select default_payment_method_id::text from company where company_id = $1", company.Id)},
Options: MustGetOptions(ctx, conn, "select payment_method_id::text, name from payment_method where company_id = $1", company.Id),
Selected: []string{mustGetDefaultPaymentMethod(ctx, conn, company)},
Options: mustGetPaymentMethodOptions(ctx, conn, company),
},
}
}
@ -782,6 +782,14 @@ func mustGetContactOptions(ctx context.Context, conn *Conn, company *Company) []
return MustGetOptions(ctx, conn, "select contact_id::text, business_name from contact where company_id = $1 order by business_name", company.Id)
}
func mustGetDefaultPaymentMethod(ctx context.Context, conn *Conn, company *Company) string {
return conn.MustGetText(ctx, "", "select default_payment_method_id::text from company where company_id = $1", company.Id)
}
func mustGetPaymentMethodOptions(ctx context.Context, conn *Conn, company *Company) []*SelectOption {
return MustGetOptions(ctx, conn, "select payment_method_id::text, name from payment_method where company_id = $1", company.Id)
}
type invoiceProductForm struct {
locale *Locale
company *Company
@ -1044,9 +1052,9 @@ func HandleEditInvoiceAction(w http.ResponseWriter, r *http.Request, params http
})
}
type renderFormFunc func(w http.ResponseWriter, r *http.Request, form *invoiceForm)
type renderInvoiceFormFunc func(w http.ResponseWriter, r *http.Request, form *invoiceForm)
func handleInvoiceAction(w http.ResponseWriter, r *http.Request, action string, renderForm renderFormFunc) {
func handleInvoiceAction(w http.ResponseWriter, r *http.Request, action string, renderForm renderInvoiceFormFunc) {
locale := getLocale(r)
conn := getConn(r)
company := mustGetCompany(r)

View File

@ -66,6 +66,65 @@ func (src EditedInvoiceProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byt
return array.EncodeBinary(ci, buf)
}
type NewQuoteProductArray []*quoteProductForm
func (src NewQuoteProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
typeName := "new_quote_product[]"
dt, ok := ci.DataTypeForName(typeName)
if !ok {
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
}
var values [][]interface{}
for _, form := range src {
var productId interface{} = form.ProductId.Val
if form.ProductId.Val == "" {
productId = nil
}
values = append(values, []interface{}{
productId,
form.Name.Val,
form.Description.Val,
form.Price.Val,
form.Quantity.Val,
form.Discount.Float64() / 100.0,
form.Tax.Selected,
})
}
array := pgtype.NewValue(dt.Value).(pgtype.ValueTranscoder)
if err := array.Set(values); err != nil {
return nil, err
}
return array.EncodeBinary(ci, buf)
}
type EditedQuoteProductArray []*quoteProductForm
func (src EditedQuoteProductArray) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
typeName := "edited_quote_product[]"
dt, ok := ci.DataTypeForName(typeName)
if !ok {
return nil, fmt.Errorf("unable to find oid for type name %v", typeName)
}
var values [][]interface{}
for _, form := range src {
values = append(values, []interface{}{
form.QuoteProductId.IntegerOrNil(),
form.ProductId.IntegerOrNil(),
form.Name.Val,
form.Description.Val,
form.Price.Val,
form.Quantity.Val,
form.Discount.Float64() / 100.0,
form.Tax.Selected,
})
}
array := pgtype.NewValue(dt.Value).(pgtype.ValueTranscoder)
if err := array.Set(values); err != nil {
return nil, err
}
return array.EncodeBinary(ci, buf)
}
func registerPgTypes(ctx context.Context, conn *pgx.Conn) error {
if _, err := conn.Exec(ctx, "set role to admin"); err != nil {
return err
@ -84,6 +143,7 @@ func registerPgTypes(ctx context.Context, conn *pgx.Conn) error {
if err != nil {
return err
}
newInvoiceProduct, err := pgtype.NewCompositeType(
"new_invoice_product",
[]pgtype.CompositeTypeField{
@ -143,6 +203,65 @@ func registerPgTypes(ctx context.Context, conn *pgx.Conn) error {
return err
}
newQuoteProduct, err := pgtype.NewCompositeType(
"new_quote_product",
[]pgtype.CompositeTypeField{
{"product_id", pgtype.Int4OID},
{"name", pgtype.TextOID},
{"description", pgtype.TextOID},
{"price", pgtype.TextOID},
{"quantity", pgtype.Int4OID},
{"discount_rate", discountRateOID},
{"tax", pgtype.Int4ArrayOID},
},
conn.ConnInfo(),
)
if err != nil {
return err
}
newQuoteProductOID, err := registerPgType(ctx, conn, newQuoteProduct, newQuoteProduct.TypeName())
if err != nil {
return err
}
newQuoteProductArray := pgtype.NewArrayType("new_quote_product[]", newQuoteProductOID, func() pgtype.ValueTranscoder {
value := newQuoteProduct.NewTypeValue()
return value.(pgtype.ValueTranscoder)
})
_, err = registerPgType(ctx, conn, newQuoteProductArray, newQuoteProductArray.TypeName())
if err != nil {
return err
}
editedQuoteProduct, err := pgtype.NewCompositeType(
"edited_quote_product",
[]pgtype.CompositeTypeField{
{"quote_product_id", pgtype.Int4OID},
{"product_id", pgtype.Int4OID},
{"name", pgtype.TextOID},
{"description", pgtype.TextOID},
{"price", pgtype.TextOID},
{"quantity", pgtype.Int4OID},
{"discount_rate", discountRateOID},
{"tax", pgtype.Int4ArrayOID},
},
conn.ConnInfo(),
)
if err != nil {
return err
}
editedQuoteProductOID, err := registerPgType(ctx, conn, editedQuoteProduct, editedQuoteProduct.TypeName())
if err != nil {
return err
}
editedQuoteProductArray := pgtype.NewArrayType("edited_quote_product[]", editedQuoteProductOID, func() pgtype.ValueTranscoder {
value := editedQuoteProduct.NewTypeValue()
return value.(pgtype.ValueTranscoder)
})
_, err = registerPgType(ctx, conn, editedQuoteProductArray, editedQuoteProductArray.TypeName())
if err != nil {
return err
}
_, err = conn.Exec(ctx, "reset role")
return err
}

1102
pkg/quote.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,15 @@ func NewRouter(db *Db) http.Handler {
companyRouter.POST("/invoices/:slug/edit", HandleEditInvoiceAction)
companyRouter.PUT("/invoices/:slug/tags", HandleUpdateInvoiceTags)
companyRouter.GET("/invoices/:slug/tags/edit", ServeEditInvoiceTags)
companyRouter.GET("/quotes", IndexQuotes)
companyRouter.POST("/quotes", HandleAddQuote)
companyRouter.GET("/quotes/:slug", ServeQuote)
companyRouter.PUT("/quotes/:slug", HandleUpdateQuote)
companyRouter.POST("/quotes/:slug", HandleNewQuoteAction)
companyRouter.GET("/quotes/:slug/edit", ServeEditQuote)
companyRouter.POST("/quotes/:slug/edit", HandleEditQuoteAction)
companyRouter.PUT("/quotes/:slug/tags", HandleUpdateQuoteTags)
companyRouter.GET("/quotes/:slug/tags/edit", ServeEditQuoteTags)
companyRouter.GET("/search/products", HandleProductSearch)
companyRouter.GET("/expenses", IndexExpenses)
companyRouter.POST("/expenses", HandleAddExpense)

466
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-05-29 00:02+0200\n"
"POT-Creation-Date: 2023-06-07 16:05+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"
@ -25,12 +25,15 @@ msgstr "Afegeix productes a la factura"
#: web/template/invoices/products.gohtml:9 web/template/invoices/new.gohtml:9
#: web/template/invoices/index.gohtml:9 web/template/invoices/view.gohtml:9
#: web/template/invoices/edit.gohtml:9 web/template/contacts/new.gohtml:9
#: web/template/contacts/index.gohtml:9 web/template/contacts/edit.gohtml:10
#: web/template/profile.gohtml:9 web/template/expenses/new.gohtml:10
#: web/template/expenses/index.gohtml:10 web/template/expenses/edit.gohtml:10
#: web/template/tax-details.gohtml:9 web/template/products/new.gohtml:9
#: web/template/products/index.gohtml:9 web/template/products/edit.gohtml:10
#: web/template/invoices/edit.gohtml:9 web/template/quotes/products.gohtml:9
#: web/template/quotes/new.gohtml:9 web/template/quotes/index.gohtml:9
#: web/template/quotes/view.gohtml:9 web/template/quotes/edit.gohtml:9
#: web/template/contacts/new.gohtml:9 web/template/contacts/index.gohtml:9
#: web/template/contacts/edit.gohtml:10 web/template/profile.gohtml:9
#: web/template/expenses/new.gohtml:10 web/template/expenses/index.gohtml:10
#: web/template/expenses/edit.gohtml:10 web/template/tax-details.gohtml:9
#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:9
#: web/template/products/edit.gohtml:10
msgctxt "title"
msgid "Home"
msgstr "Inici"
@ -49,60 +52,70 @@ msgid "New Invoice"
msgstr "Nova factura"
#: web/template/invoices/products.gohtml:48
#: web/template/quotes/products.gohtml:48
msgctxt "product"
msgid "All"
msgstr "Tots"
#: web/template/invoices/products.gohtml:49
#: web/template/products/index.gohtml:40
#: web/template/quotes/products.gohtml:49 web/template/products/index.gohtml:40
msgctxt "title"
msgid "Name"
msgstr "Nom"
#: web/template/invoices/products.gohtml:50
#: web/template/invoices/view.gohtml:62 web/template/products/index.gohtml:42
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
#: web/template/quotes/view.gohtml:62 web/template/products/index.gohtml:42
msgctxt "title"
msgid "Price"
msgstr "Preu"
#: web/template/invoices/products.gohtml:64
#: web/template/products/index.gohtml:82
#: web/template/quotes/products.gohtml:64 web/template/products/index.gohtml:82
msgid "No products added yet."
msgstr "No hi ha cap producte."
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:83
#: web/template/invoices/edit.gohtml:84
#: web/template/invoices/edit.gohtml:84 web/template/quotes/products.gohtml:72
#: web/template/quotes/new.gohtml:84 web/template/quotes/edit.gohtml:85
msgctxt "action"
msgid "Add products"
msgstr "Afegeix productes"
#: web/template/invoices/new.gohtml:27 web/template/invoices/edit.gohtml:27
#: web/template/quotes/new.gohtml:27 web/template/quotes/edit.gohtml:27
msgid "Product “%s” removed"
msgstr "Sha esborrat el producte «%s»"
#: web/template/invoices/new.gohtml:31 web/template/invoices/edit.gohtml:31
#: web/template/quotes/new.gohtml:31 web/template/quotes/edit.gohtml:31
msgctxt "action"
msgid "Undo"
msgstr "Desfes"
#: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67
#: web/template/invoices/edit.gohtml:61
#: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61
#: web/template/quotes/view.gohtml:67 web/template/quotes/edit.gohtml:62
msgctxt "title"
msgid "Subtotal"
msgstr "Subtotal"
#: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:71
#: web/template/quotes/view.gohtml:111 web/template/quotes/edit.gohtml:72
msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/template/invoices/new.gohtml:87 web/template/invoices/edit.gohtml:88
#: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89
msgctxt "action"
msgid "Update"
msgstr "Actualitza"
#: web/template/invoices/new.gohtml:90 web/template/invoices/edit.gohtml:91
#: web/template/quotes/new.gohtml:91 web/template/quotes/edit.gohtml:92
#: web/template/contacts/new.gohtml:39 web/template/contacts/edit.gohtml:43
#: web/template/expenses/new.gohtml:33 web/template/expenses/edit.gohtml:38
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
@ -121,8 +134,8 @@ msgid "New invoice"
msgstr "Nova factura"
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
#: web/template/contacts/index.gohtml:34 web/template/expenses/index.gohtml:36
#: web/template/products/index.gohtml:34
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:34
#: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
msgctxt "action"
msgid "Filter"
msgstr "Filtra"
@ -133,6 +146,7 @@ msgid "All"
msgstr "Totes"
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:34
#: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:34
msgctxt "title"
msgid "Date"
msgstr "Data"
@ -142,34 +156,39 @@ msgctxt "title"
msgid "Invoice Num."
msgstr "Núm. factura"
#: web/template/invoices/index.gohtml:52 web/template/contacts/index.gohtml:40
#: web/template/invoices/index.gohtml:52 web/template/quotes/index.gohtml:52
#: web/template/contacts/index.gohtml:40
msgctxt "title"
msgid "Customer"
msgstr "Client"
#: web/template/invoices/index.gohtml:53
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
msgctxt "title"
msgid "Status"
msgstr "Estat"
#: web/template/invoices/index.gohtml:54 web/template/contacts/index.gohtml:43
#: web/template/expenses/index.gohtml:46 web/template/products/index.gohtml:41
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
#: web/template/contacts/index.gohtml:43 web/template/expenses/index.gohtml:46
#: web/template/products/index.gohtml:41
msgctxt "title"
msgid "Tags"
msgstr "Etiquetes"
#: web/template/invoices/index.gohtml:55 web/template/expenses/index.gohtml:47
#: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55
#: web/template/expenses/index.gohtml:47
msgctxt "title"
msgid "Amount"
msgstr "Import"
#: web/template/invoices/index.gohtml:56 web/template/expenses/index.gohtml:48
#: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56
#: web/template/expenses/index.gohtml:48
msgctxt "title"
msgid "Download"
msgstr "Descàrrega"
#: web/template/invoices/index.gohtml:57 web/template/contacts/index.gohtml:44
#: web/template/expenses/index.gohtml:49 web/template/products/index.gohtml:43
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
#: web/template/contacts/index.gohtml:44 web/template/expenses/index.gohtml:49
#: web/template/products/index.gohtml:43
msgctxt "title"
msgid "Actions"
msgstr "Accions"
@ -180,6 +199,7 @@ msgid "Select invoice %v"
msgstr "Selecciona factura %v"
#: web/template/invoices/index.gohtml:119 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:119 web/template/quotes/view.gohtml:19
#: web/template/contacts/index.gohtml:74 web/template/expenses/index.gohtml:88
#: web/template/products/index.gohtml:72
msgctxt "action"
@ -187,6 +207,7 @@ msgid "Edit"
msgstr "Edita"
#: web/template/invoices/index.gohtml:127 web/template/invoices/view.gohtml:16
#: web/template/quotes/index.gohtml:127 web/template/quotes/view.gohtml:16
msgctxt "action"
msgid "Duplicate"
msgstr "Duplica"
@ -205,22 +226,22 @@ msgctxt "action"
msgid "Download invoice"
msgstr "Descarrega factura"
#: web/template/invoices/view.gohtml:61
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:61
msgctxt "title"
msgid "Concept"
msgstr "Concepte"
#: web/template/invoices/view.gohtml:64
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:64
msgctxt "title"
msgid "Discount"
msgstr "Descompte"
#: web/template/invoices/view.gohtml:66
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:66
msgctxt "title"
msgid "Units"
msgstr "Unitats"
#: web/template/invoices/view.gohtml:101
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:101
msgctxt "title"
msgid "Tax Base"
msgstr "Base imposable"
@ -235,7 +256,7 @@ msgctxt "input"
msgid "(Max. %s)"
msgstr "(Màx. %s)"
#: web/template/form.gohtml:171
#: web/template/form.gohtml:194
msgctxt "action"
msgid "Filters"
msgstr "Filtra"
@ -275,6 +296,68 @@ msgctxt "term"
msgid "Net Income"
msgstr "Ingressos nets"
#: web/template/quotes/products.gohtml:2 web/template/quotes/products.gohtml:23
msgctxt "title"
msgid "Add Products to Quotation"
msgstr "Afegeix productes al pressupost"
#: web/template/quotes/products.gohtml:10 web/template/quotes/new.gohtml:10
#: web/template/quotes/index.gohtml:2 web/template/quotes/index.gohtml:10
#: web/template/quotes/view.gohtml:10 web/template/quotes/edit.gohtml:10
msgctxt "title"
msgid "Quotations"
msgstr "Pressuposts"
#: web/template/quotes/products.gohtml:12 web/template/quotes/new.gohtml:2
#: web/template/quotes/new.gohtml:11 web/template/quotes/new.gohtml:19
msgctxt "title"
msgid "New Quotation"
msgstr "Nou pressupost"
#: web/template/quotes/index.gohtml:19
msgctxt "action"
msgid "Download quotations"
msgstr "Descarrega pressuposts"
#: web/template/quotes/index.gohtml:21
msgctxt "action"
msgid "New quotation"
msgstr "Nou pressupost"
#: web/template/quotes/index.gohtml:49
msgctxt "quote"
msgid "All"
msgstr "Tots"
#: web/template/quotes/index.gohtml:51
msgctxt "title"
msgid "Quotation Num."
msgstr "Núm. pressupost"
#: web/template/quotes/index.gohtml:64
msgctxt "action"
msgid "Select quotation %v"
msgstr "Selecciona pressupost %v"
#: web/template/quotes/index.gohtml:137
msgid "No quotations added yet."
msgstr "No hi ha cap pressupost."
#: web/template/quotes/view.gohtml:2 web/template/quotes/view.gohtml:33
msgctxt "title"
msgid "Quotation %s"
msgstr "Pressupost %s"
#: web/template/quotes/view.gohtml:22
msgctxt "action"
msgid "Download quotation"
msgstr "Descarrega pressupost"
#: web/template/quotes/edit.gohtml:2 web/template/quotes/edit.gohtml:19
msgctxt "title"
msgid "Edit Quotation “%s”"
msgstr "Edició del pressupost «%s»"
#: web/template/app.gohtml:23
msgctxt "menu"
msgid "Account"
@ -297,20 +380,25 @@ msgstr "Tauler"
#: web/template/app.gohtml:47
msgctxt "nav"
msgid "Quotations"
msgstr "Pressuposts"
#: web/template/app.gohtml:48
msgctxt "nav"
msgid "Invoices"
msgstr "Factures"
#: web/template/app.gohtml:48
#: web/template/app.gohtml:49
msgctxt "nav"
msgid "Expenses"
msgstr "Despeses"
#: web/template/app.gohtml:49
#: web/template/app.gohtml:50
msgctxt "nav"
msgid "Products"
msgstr "Productes"
#: web/template/app.gohtml:50
#: web/template/app.gohtml:51
msgctxt "nav"
msgid "Contacts"
msgstr "Contactes"
@ -382,7 +470,7 @@ msgctxt "title"
msgid "Language"
msgstr "Idioma"
#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:172
#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:173
msgctxt "action"
msgid "Save changes"
msgstr "Desa canvis"
@ -449,54 +537,54 @@ msgctxt "title"
msgid "Invoicing"
msgstr "Facturació"
#: web/template/tax-details.gohtml:53
#: web/template/tax-details.gohtml:54
msgid "Are you sure?"
msgstr "Nesteu segur?"
#: web/template/tax-details.gohtml:59
#: web/template/tax-details.gohtml:60
msgctxt "title"
msgid "Tax Name"
msgstr "Nom impost"
#: web/template/tax-details.gohtml:60
#: web/template/tax-details.gohtml:61
msgctxt "title"
msgid "Rate (%)"
msgstr "Percentatge"
#: web/template/tax-details.gohtml:61
#: web/template/tax-details.gohtml:62
msgctxt "title"
msgid "Class"
msgstr "Classe"
#: web/template/tax-details.gohtml:85
#: web/template/tax-details.gohtml:86
msgid "No taxes added yet."
msgstr "No hi ha cap impost."
#: web/template/tax-details.gohtml:91 web/template/tax-details.gohtml:152
#: web/template/tax-details.gohtml:92 web/template/tax-details.gohtml:153
msgctxt "title"
msgid "New Line"
msgstr "Nova línia"
#: web/template/tax-details.gohtml:105
#: web/template/tax-details.gohtml:106
msgctxt "action"
msgid "Add new tax"
msgstr "Afegeix nou impost"
#: web/template/tax-details.gohtml:121
#: web/template/tax-details.gohtml:122
msgctxt "title"
msgid "Payment Method"
msgstr "Mètode de pagament"
#: web/template/tax-details.gohtml:122
#: web/template/tax-details.gohtml:123
msgctxt "title"
msgid "Instructions"
msgstr "Instruccions"
#: web/template/tax-details.gohtml:146
#: web/template/tax-details.gohtml:147
msgid "No payment methods added yet."
msgstr "No hi ha cap mètode de pagament."
#: web/template/tax-details.gohtml:164
#: web/template/tax-details.gohtml:165
msgctxt "action"
msgid "Add new payment method"
msgstr "Afegeix nou mètode de pagament"
@ -553,26 +641,27 @@ 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/invoices.go:816
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:793 pkg/invoices.go:824
#: pkg/contacts.go:135
msgctxt "input"
msgid "Name"
msgstr "Nom"
#: pkg/products.go:169 pkg/products.go:290 pkg/expenses.go:202
#: pkg/expenses.go:361 pkg/invoices.go:189 pkg/invoices.go:601
#: pkg/invoices.go:1115 pkg/contacts.go:140 pkg/contacts.go:325
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:606
#: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189
#: pkg/invoices.go:601 pkg/invoices.go:1123 pkg/contacts.go:140
#: pkg/contacts.go:325
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:173 pkg/expenses.go:365 pkg/invoices.go:193
#: pkg/products.go:173 pkg/quote.go:192 pkg/expenses.go:365 pkg/invoices.go:193
#: pkg/contacts.go:144
msgctxt "input"
msgid "Tags Condition"
msgstr "Condició de les etiquetes"
#: pkg/products.go:177 pkg/expenses.go:369 pkg/invoices.go:197
#: pkg/products.go:177 pkg/quote.go:196 pkg/expenses.go:369 pkg/invoices.go:197
#: pkg/contacts.go:148
msgctxt "tag condition"
msgid "All"
@ -583,7 +672,7 @@ msgstr "Totes"
msgid "Invoices must have all the specified labels."
msgstr "Les factures han de tenir totes les etiquetes."
#: pkg/products.go:182 pkg/expenses.go:374 pkg/invoices.go:202
#: pkg/products.go:182 pkg/quote.go:201 pkg/expenses.go:374 pkg/invoices.go:202
#: pkg/contacts.go:153
msgctxt "tag condition"
msgid "Any"
@ -594,119 +683,262 @@ msgstr "Qualsevol"
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/invoices.go:830
#: pkg/products.go:269 pkg/quote.go:807 pkg/invoices.go:838
msgctxt "input"
msgid "Description"
msgstr "Descripció"
#: pkg/products.go:274 pkg/invoices.go:834
#: pkg/products.go:274 pkg/quote.go:811 pkg/invoices.go:842
msgctxt "input"
msgid "Price"
msgstr "Preu"
#: pkg/products.go:284 pkg/expenses.go:181 pkg/invoices.go:863
#: pkg/products.go:284 pkg/quote.go:840 pkg/expenses.go:181 pkg/invoices.go:871
msgctxt "input"
msgid "Taxes"
msgstr "Imposts"
#: pkg/products.go:309 pkg/profile.go:92 pkg/invoices.go:912
#: pkg/products.go:309 pkg/quote.go:889 pkg/profile.go:92 pkg/invoices.go:920
msgid "Name can not be empty."
msgstr "No podeu deixar el nom en blanc."
#: pkg/products.go:310 pkg/invoices.go:913
#: pkg/products.go:310 pkg/quote.go:890 pkg/invoices.go:921
msgid "Price can not be empty."
msgstr "No podeu deixar el preu en blanc."
#: pkg/products.go:311 pkg/invoices.go:914
#: pkg/products.go:311 pkg/quote.go:891 pkg/invoices.go:922
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/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:922
#: pkg/products.go:313 pkg/quote.go:899 pkg/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:930
msgid "Selected tax is not valid."
msgstr "Heu seleccionat un impost que no és vàlid."
#: pkg/products.go:314 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:923
#: pkg/products.go:314 pkg/quote.go:900 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:931
msgid "You can only select a tax of each class."
msgstr "Només podeu seleccionar un impost de cada classe."
#: pkg/company.go:98
#: pkg/company.go:100
msgctxt "input"
msgid "Currency"
msgstr "Moneda"
#: pkg/company.go:105
#: pkg/company.go:107
msgctxt "input"
msgid "Invoice number format"
msgstr "Format del número de factura"
#: pkg/company.go:111
#: pkg/company.go:113
msgctxt "input"
msgid "Next invoice number"
msgstr "Següent número de factura"
#: pkg/company.go:122
msgctxt "input"
msgid "Legal disclaimer"
msgstr "Nota legal"
#: pkg/company.go:129
#: pkg/company.go:141
msgid "Selected currency is not valid."
msgstr "Heu seleccionat una moneda que no és vàlida."
#: pkg/company.go:130
#: pkg/company.go:142
msgid "Invoice number format can not be empty."
msgstr "No podeu deixar el format del número de factura en blanc."
#: pkg/company.go:297
#: pkg/company.go:143
msgid "Next invoice number must be a number greater than zero."
msgstr "El següent número de factura ha de ser un número major a zero."
#: pkg/company.go:350
msgctxt "input"
msgid "Tax name"
msgstr "Nom impost"
#: pkg/company.go:303
#: pkg/company.go:356
msgctxt "input"
msgid "Tax Class"
msgstr "Classe dimpost"
#: pkg/company.go:306
#: pkg/company.go:359
msgid "Select a tax class"
msgstr "Escolliu una classe dimpost"
#: pkg/company.go:310
#: pkg/company.go:363
msgctxt "input"
msgid "Rate (%)"
msgstr "Percentatge"
#: pkg/company.go:333
#: pkg/company.go:386
msgid "Tax name can not be empty."
msgstr "No podeu deixar el nom de limpost en blanc."
#: pkg/company.go:334
#: pkg/company.go:387
msgid "Selected tax class is not valid."
msgstr "Heu seleccionat una classe dimpost que no és vàlida."
#: pkg/company.go:335
#: pkg/company.go:388
msgid "Tax rate can not be empty."
msgstr "No podeu deixar percentatge en blanc."
#: pkg/company.go:336
#: pkg/company.go:389
msgid "Tax rate must be an integer between -99 and 99."
msgstr "El percentatge ha de ser entre -99 i 99."
#: pkg/company.go:399
#: pkg/company.go:452
msgctxt "input"
msgid "Payment method name"
msgstr "Nom del mètode de pagament"
#: pkg/company.go:405
#: pkg/company.go:458
msgctxt "input"
msgid "Instructions"
msgstr "Instruccions"
#: pkg/company.go:423
#: pkg/company.go:476
msgid "Payment method name can not be empty."
msgstr "No podeu deixar el nom del mètode de pagament en blanc."
#: pkg/company.go:424
#: pkg/company.go:477
msgid "Payment instructions can not be empty."
msgstr "No podeu deixar les instruccions de pagament en blanc."
#: pkg/quote.go:161 pkg/quote.go:585 pkg/expenses.go:340 pkg/invoices.go:162
#: pkg/invoices.go:584
msgctxt "input"
msgid "Customer"
msgstr "Client"
#: pkg/quote.go:162 pkg/expenses.go:341 pkg/invoices.go:163
msgid "All customers"
msgstr "Tots els clients"
#: pkg/quote.go:167 pkg/quote.go:579
msgctxt "input"
msgid "Quotation Status"
msgstr "Estat del pressupost"
#: pkg/quote.go:168 pkg/invoices.go:169
msgid "All status"
msgstr "Tots els estats"
#: pkg/quote.go:173
msgctxt "input"
msgid "Quotation Number"
msgstr "Número de pressupost"
#: pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:179
msgctxt "input"
msgid "From Date"
msgstr "A partir de la data"
#: pkg/quote.go:183 pkg/expenses.go:356 pkg/invoices.go:184
msgctxt "input"
msgid "To Date"
msgstr "Fins la data"
#: pkg/quote.go:197
msgid "Quotations must have all the specified labels."
msgstr "Els pressuposts han de tenir totes les etiquetes."
#: pkg/quote.go:202
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:451
msgid "Select a customer to quote."
msgstr "Escolliu un client a pressupostar."
#: pkg/quote.go:527
msgid "quotations.zip"
msgstr "pressuposts.zip"
#: pkg/quote.go:533 pkg/quote.go:1055 pkg/quote.go:1063 pkg/invoices.go:533
#: pkg/invoices.go:1098 pkg/invoices.go:1106
msgid "Invalid action"
msgstr "Acció invàlida."
#: pkg/quote.go:590
msgctxt "input"
msgid "Quotation Date"
msgstr "Data del pressupost"
#: pkg/quote.go:596
msgctxt "input"
msgid "Terms and conditions"
msgstr "Condicions dacceptació"
#: pkg/quote.go:601 pkg/invoices.go:596
msgctxt "input"
msgid "Notes"
msgstr "Notes"
#: pkg/quote.go:610 pkg/invoices.go:606
msgctxt "input"
msgid "Payment Method"
msgstr "Mètode de pagament"
#: pkg/quote.go:646
msgid "Selected quotation status is not valid."
msgstr "Heu seleccionat un estat de pressupost que no és vàlid."
#: pkg/quote.go:647 pkg/invoices.go:643
msgid "Selected customer is not valid."
msgstr "Heu seleccionat un client que no és vàlid."
#: pkg/quote.go:648
msgid "Quotation date can not be empty."
msgstr "No podeu deixar la data del pressupost en blanc."
#: pkg/quote.go:649
msgid "Quotation date must be a valid date."
msgstr "La data del pressupost ha de ser vàlida."
#: pkg/quote.go:651 pkg/invoices.go:647
msgid "Selected payment method is not valid."
msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
#: pkg/quote.go:783 pkg/quote.go:788 pkg/invoices.go:814 pkg/invoices.go:819
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/quote.go:821 pkg/invoices.go:852
msgctxt "input"
msgid "Quantity"
msgstr "Quantitat"
#: pkg/quote.go:830 pkg/invoices.go:861
msgctxt "input"
msgid "Discount (%)"
msgstr "Descompte (%)"
#: pkg/quote.go:884
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:887 pkg/invoices.go:918
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:893 pkg/invoices.go:924
msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc."
#: pkg/quote.go:894 pkg/invoices.go:925
msgid "Quantity must be a number greater than zero."
msgstr "La quantitat ha de ser un número major a zero."
#: pkg/quote.go:896 pkg/invoices.go:927
msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc."
#: pkg/quote.go:897 pkg/invoices.go:928
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
#: pkg/profile.go:25
msgctxt "language option"
msgid "Automatic"
@ -815,39 +1047,16 @@ msgstr "No podeu deixar limport en blanc."
msgid "Amount must be a number greater than zero."
msgstr "Limport ha de ser un número major a zero."
#: pkg/expenses.go:340 pkg/invoices.go:162 pkg/invoices.go:584
msgctxt "input"
msgid "Customer"
msgstr "Client"
#: pkg/expenses.go:341 pkg/invoices.go:163
msgid "All customers"
msgstr "Tots els clients"
#: pkg/expenses.go:346 pkg/invoices.go:174
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/expenses.go:351 pkg/invoices.go:179
msgctxt "input"
msgid "From Date"
msgstr "A partir de la data"
#: pkg/expenses.go:356 pkg/invoices.go:184
msgctxt "input"
msgid "To Date"
msgstr "Fins la data"
#: pkg/invoices.go:168 pkg/invoices.go:578
msgctxt "input"
msgid "Invoice Status"
msgstr "Estat de la factura"
#: pkg/invoices.go:169
msgid "All status"
msgstr "Tots els estats"
#: pkg/invoices.go:426
msgid "Select a customer to bill."
msgstr "Escolliu un client a facturar."
@ -856,75 +1065,18 @@ msgstr "Escolliu un client a facturar."
msgid "invoices.zip"
msgstr "factures.zip"
#: pkg/invoices.go:533 pkg/invoices.go:1090 pkg/invoices.go:1098
msgid "Invalid action"
msgstr "Acció invàlida."
#: pkg/invoices.go:596
msgctxt "input"
msgid "Notes"
msgstr "Notes"
#: pkg/invoices.go:606
msgctxt "input"
msgid "Payment Method"
msgstr "Mètode de pagament"
#: pkg/invoices.go:642
msgid "Selected invoice status is not valid."
msgstr "Heu seleccionat un estat de factura que no és vàlid."
#: pkg/invoices.go:643
msgid "Selected customer is not valid."
msgstr "Heu seleccionat un client que no és vàlid."
#: pkg/invoices.go:644
msgid "Invoice date can not be empty."
msgstr "No podeu deixar la data de la factura en blanc."
#: pkg/invoices.go:647
msgid "Selected payment method is not valid."
msgstr "Heu seleccionat un mètode de pagament que no és vàlid."
#: pkg/invoices.go:806 pkg/invoices.go:811
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/invoices.go:844
msgctxt "input"
msgid "Quantity"
msgstr "Quantitat"
#: pkg/invoices.go:853
msgctxt "input"
msgid "Discount (%)"
msgstr "Descompte (%)"
#: pkg/invoices.go:907
#: pkg/invoices.go:915
msgid "Invoice product ID must be a number greater than zero."
msgstr "LID del producte de factura ha de ser un número major a zero."
#: pkg/invoices.go:910
msgid "Product ID must be a positive number or zero."
msgstr "LID del producte ha de ser un número positiu o zero."
#: pkg/invoices.go:916
msgid "Quantity can not be empty."
msgstr "No podeu deixar la quantitat en blanc."
#: pkg/invoices.go:917
msgid "Quantity must be a number greater than zero."
msgstr "La quantitat ha de ser un número major a zero."
#: pkg/invoices.go:919
msgid "Discount can not be empty."
msgstr "No podeu deixar el descompte en blanc."
#: pkg/invoices.go:920
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descompte ha de ser un percentatge entre 0 i 100."
#: pkg/contacts.go:238
msgctxt "input"
msgid "Business name"

470
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-05-29 00:02+0200\n"
"POT-Creation-Date: 2023-06-07 16:05+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"
@ -25,12 +25,15 @@ msgstr "Añadir productos a la factura"
#: web/template/invoices/products.gohtml:9 web/template/invoices/new.gohtml:9
#: web/template/invoices/index.gohtml:9 web/template/invoices/view.gohtml:9
#: web/template/invoices/edit.gohtml:9 web/template/contacts/new.gohtml:9
#: web/template/contacts/index.gohtml:9 web/template/contacts/edit.gohtml:10
#: web/template/profile.gohtml:9 web/template/expenses/new.gohtml:10
#: web/template/expenses/index.gohtml:10 web/template/expenses/edit.gohtml:10
#: web/template/tax-details.gohtml:9 web/template/products/new.gohtml:9
#: web/template/products/index.gohtml:9 web/template/products/edit.gohtml:10
#: web/template/invoices/edit.gohtml:9 web/template/quotes/products.gohtml:9
#: web/template/quotes/new.gohtml:9 web/template/quotes/index.gohtml:9
#: web/template/quotes/view.gohtml:9 web/template/quotes/edit.gohtml:9
#: web/template/contacts/new.gohtml:9 web/template/contacts/index.gohtml:9
#: web/template/contacts/edit.gohtml:10 web/template/profile.gohtml:9
#: web/template/expenses/new.gohtml:10 web/template/expenses/index.gohtml:10
#: web/template/expenses/edit.gohtml:10 web/template/tax-details.gohtml:9
#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:9
#: web/template/products/edit.gohtml:10
msgctxt "title"
msgid "Home"
msgstr "Inicio"
@ -49,60 +52,70 @@ msgid "New Invoice"
msgstr "Nueva factura"
#: web/template/invoices/products.gohtml:48
#: web/template/quotes/products.gohtml:48
msgctxt "product"
msgid "All"
msgstr "Todos"
#: web/template/invoices/products.gohtml:49
#: web/template/products/index.gohtml:40
#: web/template/quotes/products.gohtml:49 web/template/products/index.gohtml:40
msgctxt "title"
msgid "Name"
msgstr "Nombre"
#: web/template/invoices/products.gohtml:50
#: web/template/invoices/view.gohtml:62 web/template/products/index.gohtml:42
#: web/template/invoices/view.gohtml:62 web/template/quotes/products.gohtml:50
#: web/template/quotes/view.gohtml:62 web/template/products/index.gohtml:42
msgctxt "title"
msgid "Price"
msgstr "Precio"
#: web/template/invoices/products.gohtml:64
#: web/template/products/index.gohtml:82
#: web/template/quotes/products.gohtml:64 web/template/products/index.gohtml:82
msgid "No products added yet."
msgstr "No hay productos."
#: web/template/invoices/products.gohtml:72 web/template/invoices/new.gohtml:83
#: web/template/invoices/edit.gohtml:84
#: web/template/invoices/edit.gohtml:84 web/template/quotes/products.gohtml:72
#: web/template/quotes/new.gohtml:84 web/template/quotes/edit.gohtml:85
msgctxt "action"
msgid "Add products"
msgstr "Añadir productos"
#: web/template/invoices/new.gohtml:27 web/template/invoices/edit.gohtml:27
#: web/template/quotes/new.gohtml:27 web/template/quotes/edit.gohtml:27
msgid "Product “%s” removed"
msgstr "Se ha borrado el producto «%s»"
#: web/template/invoices/new.gohtml:31 web/template/invoices/edit.gohtml:31
#: web/template/quotes/new.gohtml:31 web/template/quotes/edit.gohtml:31
msgctxt "action"
msgid "Undo"
msgstr "Deshacer"
#: web/template/invoices/new.gohtml:60 web/template/invoices/view.gohtml:67
#: web/template/invoices/edit.gohtml:61
#: web/template/invoices/edit.gohtml:61 web/template/quotes/new.gohtml:61
#: web/template/quotes/view.gohtml:67 web/template/quotes/edit.gohtml:62
msgctxt "title"
msgid "Subtotal"
msgstr "Subtotal"
#: web/template/invoices/new.gohtml:70 web/template/invoices/view.gohtml:71
#: web/template/invoices/view.gohtml:111 web/template/invoices/edit.gohtml:71
#: web/template/quotes/new.gohtml:71 web/template/quotes/view.gohtml:71
#: web/template/quotes/view.gohtml:111 web/template/quotes/edit.gohtml:72
msgctxt "title"
msgid "Total"
msgstr "Total"
#: web/template/invoices/new.gohtml:87 web/template/invoices/edit.gohtml:88
#: web/template/quotes/new.gohtml:88 web/template/quotes/edit.gohtml:89
msgctxt "action"
msgid "Update"
msgstr "Actualizar"
#: web/template/invoices/new.gohtml:90 web/template/invoices/edit.gohtml:91
#: web/template/quotes/new.gohtml:91 web/template/quotes/edit.gohtml:92
#: web/template/contacts/new.gohtml:39 web/template/contacts/edit.gohtml:43
#: web/template/expenses/new.gohtml:33 web/template/expenses/edit.gohtml:38
#: web/template/products/new.gohtml:30 web/template/products/edit.gohtml:36
@ -121,8 +134,8 @@ msgid "New invoice"
msgstr "Nueva factura"
#: web/template/invoices/index.gohtml:43 web/template/dashboard.gohtml:23
#: web/template/contacts/index.gohtml:34 web/template/expenses/index.gohtml:36
#: web/template/products/index.gohtml:34
#: web/template/quotes/index.gohtml:43 web/template/contacts/index.gohtml:34
#: web/template/expenses/index.gohtml:36 web/template/products/index.gohtml:34
msgctxt "action"
msgid "Filter"
msgstr "Filtrar"
@ -133,6 +146,7 @@ msgid "All"
msgstr "Todas"
#: web/template/invoices/index.gohtml:50 web/template/invoices/view.gohtml:34
#: web/template/quotes/index.gohtml:50 web/template/quotes/view.gohtml:34
msgctxt "title"
msgid "Date"
msgstr "Fecha"
@ -140,36 +154,41 @@ msgstr "Fecha"
#: web/template/invoices/index.gohtml:51
msgctxt "title"
msgid "Invoice Num."
msgstr "Nº factura"
msgstr "N.º factura"
#: web/template/invoices/index.gohtml:52 web/template/contacts/index.gohtml:40
#: web/template/invoices/index.gohtml:52 web/template/quotes/index.gohtml:52
#: web/template/contacts/index.gohtml:40
msgctxt "title"
msgid "Customer"
msgstr "Cliente"
#: web/template/invoices/index.gohtml:53
#: web/template/invoices/index.gohtml:53 web/template/quotes/index.gohtml:53
msgctxt "title"
msgid "Status"
msgstr "Estado"
#: web/template/invoices/index.gohtml:54 web/template/contacts/index.gohtml:43
#: web/template/expenses/index.gohtml:46 web/template/products/index.gohtml:41
#: web/template/invoices/index.gohtml:54 web/template/quotes/index.gohtml:54
#: web/template/contacts/index.gohtml:43 web/template/expenses/index.gohtml:46
#: web/template/products/index.gohtml:41
msgctxt "title"
msgid "Tags"
msgstr "Etiquetes"
#: web/template/invoices/index.gohtml:55 web/template/expenses/index.gohtml:47
#: web/template/invoices/index.gohtml:55 web/template/quotes/index.gohtml:55
#: web/template/expenses/index.gohtml:47
msgctxt "title"
msgid "Amount"
msgstr "Importe"
#: web/template/invoices/index.gohtml:56 web/template/expenses/index.gohtml:48
#: web/template/invoices/index.gohtml:56 web/template/quotes/index.gohtml:56
#: web/template/expenses/index.gohtml:48
msgctxt "title"
msgid "Download"
msgstr "Descargar"
#: web/template/invoices/index.gohtml:57 web/template/contacts/index.gohtml:44
#: web/template/expenses/index.gohtml:49 web/template/products/index.gohtml:43
#: web/template/invoices/index.gohtml:57 web/template/quotes/index.gohtml:57
#: web/template/contacts/index.gohtml:44 web/template/expenses/index.gohtml:49
#: web/template/products/index.gohtml:43
msgctxt "title"
msgid "Actions"
msgstr "Acciones"
@ -180,6 +199,7 @@ msgid "Select invoice %v"
msgstr "Seleccionar factura %v"
#: web/template/invoices/index.gohtml:119 web/template/invoices/view.gohtml:19
#: web/template/quotes/index.gohtml:119 web/template/quotes/view.gohtml:19
#: web/template/contacts/index.gohtml:74 web/template/expenses/index.gohtml:88
#: web/template/products/index.gohtml:72
msgctxt "action"
@ -187,6 +207,7 @@ msgid "Edit"
msgstr "Editar"
#: web/template/invoices/index.gohtml:127 web/template/invoices/view.gohtml:16
#: web/template/quotes/index.gohtml:127 web/template/quotes/view.gohtml:16
msgctxt "action"
msgid "Duplicate"
msgstr "Duplicar"
@ -205,22 +226,22 @@ msgctxt "action"
msgid "Download invoice"
msgstr "Descargar factura"
#: web/template/invoices/view.gohtml:61
#: web/template/invoices/view.gohtml:61 web/template/quotes/view.gohtml:61
msgctxt "title"
msgid "Concept"
msgstr "Concepto"
#: web/template/invoices/view.gohtml:64
#: web/template/invoices/view.gohtml:64 web/template/quotes/view.gohtml:64
msgctxt "title"
msgid "Discount"
msgstr "Descuento"
#: web/template/invoices/view.gohtml:66
#: web/template/invoices/view.gohtml:66 web/template/quotes/view.gohtml:66
msgctxt "title"
msgid "Units"
msgstr "Unidades"
#: web/template/invoices/view.gohtml:101
#: web/template/invoices/view.gohtml:101 web/template/quotes/view.gohtml:101
msgctxt "title"
msgid "Tax Base"
msgstr "Base imponible"
@ -235,7 +256,7 @@ msgctxt "input"
msgid "(Max. %s)"
msgstr "(Máx. %s)"
#: web/template/form.gohtml:171
#: web/template/form.gohtml:194
msgctxt "action"
msgid "Filters"
msgstr "Filtrar"
@ -275,6 +296,68 @@ msgctxt "term"
msgid "Net Income"
msgstr "Ingresos netos"
#: web/template/quotes/products.gohtml:2 web/template/quotes/products.gohtml:23
msgctxt "title"
msgid "Add Products to Quotation"
msgstr "Añadir productos al presupuesto"
#: web/template/quotes/products.gohtml:10 web/template/quotes/new.gohtml:10
#: web/template/quotes/index.gohtml:2 web/template/quotes/index.gohtml:10
#: web/template/quotes/view.gohtml:10 web/template/quotes/edit.gohtml:10
msgctxt "title"
msgid "Quotations"
msgstr "Presupuestos"
#: web/template/quotes/products.gohtml:12 web/template/quotes/new.gohtml:2
#: web/template/quotes/new.gohtml:11 web/template/quotes/new.gohtml:19
msgctxt "title"
msgid "New Quotation"
msgstr "Nuevo presupuesto"
#: web/template/quotes/index.gohtml:19
msgctxt "action"
msgid "Download quotations"
msgstr "Descargar presupuestos"
#: web/template/quotes/index.gohtml:21
msgctxt "action"
msgid "New quotation"
msgstr "Nuevo presupuesto"
#: web/template/quotes/index.gohtml:49
msgctxt "quote"
msgid "All"
msgstr "Todos"
#: web/template/quotes/index.gohtml:51
msgctxt "title"
msgid "Quotation Num."
msgstr "N.º de presupuesto"
#: web/template/quotes/index.gohtml:64
msgctxt "action"
msgid "Select quotation %v"
msgstr "Seleccionar presupuesto %v"
#: web/template/quotes/index.gohtml:137
msgid "No quotations added yet."
msgstr "No hay presupuestos."
#: web/template/quotes/view.gohtml:2 web/template/quotes/view.gohtml:33
msgctxt "title"
msgid "Quotation %s"
msgstr "Estado del presupuesto"
#: web/template/quotes/view.gohtml:22
msgctxt "action"
msgid "Download quotation"
msgstr "Descargar presupuesto"
#: web/template/quotes/edit.gohtml:2 web/template/quotes/edit.gohtml:19
msgctxt "title"
msgid "Edit Quotation “%s”"
msgstr "Edición del presupuesto «%s»"
#: web/template/app.gohtml:23
msgctxt "menu"
msgid "Account"
@ -297,20 +380,25 @@ msgstr "Panel"
#: web/template/app.gohtml:47
msgctxt "nav"
msgid "Quotations"
msgstr "Presupuestos"
#: web/template/app.gohtml:48
msgctxt "nav"
msgid "Invoices"
msgstr "Facturas"
#: web/template/app.gohtml:48
#: web/template/app.gohtml:49
msgctxt "nav"
msgid "Expenses"
msgstr "Gastos"
#: web/template/app.gohtml:49
#: web/template/app.gohtml:50
msgctxt "nav"
msgid "Products"
msgstr "Productos"
#: web/template/app.gohtml:50
#: web/template/app.gohtml:51
msgctxt "nav"
msgid "Contacts"
msgstr "Contactos"
@ -382,7 +470,7 @@ msgctxt "title"
msgid "Language"
msgstr "Idioma"
#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:172
#: web/template/profile.gohtml:39 web/template/tax-details.gohtml:173
msgctxt "action"
msgid "Save changes"
msgstr "Guardar cambios"
@ -449,54 +537,54 @@ msgctxt "title"
msgid "Invoicing"
msgstr "Facturación"
#: web/template/tax-details.gohtml:53
#: web/template/tax-details.gohtml:54
msgid "Are you sure?"
msgstr "¿Estáis seguro?"
#: web/template/tax-details.gohtml:59
#: web/template/tax-details.gohtml:60
msgctxt "title"
msgid "Tax Name"
msgstr "Nombre impuesto"
#: web/template/tax-details.gohtml:60
#: web/template/tax-details.gohtml:61
msgctxt "title"
msgid "Rate (%)"
msgstr "Porcentaje"
#: web/template/tax-details.gohtml:61
#: web/template/tax-details.gohtml:62
msgctxt "title"
msgid "Class"
msgstr "Clase"
#: web/template/tax-details.gohtml:85
#: web/template/tax-details.gohtml:86
msgid "No taxes added yet."
msgstr "No hay impuestos."
#: web/template/tax-details.gohtml:91 web/template/tax-details.gohtml:152
#: web/template/tax-details.gohtml:92 web/template/tax-details.gohtml:153
msgctxt "title"
msgid "New Line"
msgstr "Nueva línea"
#: web/template/tax-details.gohtml:105
#: web/template/tax-details.gohtml:106
msgctxt "action"
msgid "Add new tax"
msgstr "Añadir nuevo impuesto"
#: web/template/tax-details.gohtml:121
#: web/template/tax-details.gohtml:122
msgctxt "title"
msgid "Payment Method"
msgstr "Método de pago"
#: web/template/tax-details.gohtml:122
#: web/template/tax-details.gohtml:123
msgctxt "title"
msgid "Instructions"
msgstr "Instrucciones"
#: web/template/tax-details.gohtml:146
#: web/template/tax-details.gohtml:147
msgid "No payment methods added yet."
msgstr "No hay métodos de pago."
#: web/template/tax-details.gohtml:164
#: web/template/tax-details.gohtml:165
msgctxt "action"
msgid "Add new payment method"
msgstr "Añadir nuevo método de pago"
@ -553,26 +641,27 @@ 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/invoices.go:816
#: pkg/products.go:164 pkg/products.go:263 pkg/quote.go:793 pkg/invoices.go:824
#: pkg/contacts.go:135
msgctxt "input"
msgid "Name"
msgstr "Nombre"
#: pkg/products.go:169 pkg/products.go:290 pkg/expenses.go:202
#: pkg/expenses.go:361 pkg/invoices.go:189 pkg/invoices.go:601
#: pkg/invoices.go:1115 pkg/contacts.go:140 pkg/contacts.go:325
#: pkg/products.go:169 pkg/products.go:290 pkg/quote.go:188 pkg/quote.go:606
#: pkg/expenses.go:202 pkg/expenses.go:361 pkg/invoices.go:189
#: pkg/invoices.go:601 pkg/invoices.go:1123 pkg/contacts.go:140
#: pkg/contacts.go:325
msgctxt "input"
msgid "Tags"
msgstr "Etiquetes"
#: pkg/products.go:173 pkg/expenses.go:365 pkg/invoices.go:193
#: pkg/products.go:173 pkg/quote.go:192 pkg/expenses.go:365 pkg/invoices.go:193
#: pkg/contacts.go:144
msgctxt "input"
msgid "Tags Condition"
msgstr "Condición de las etiquetas"
#: pkg/products.go:177 pkg/expenses.go:369 pkg/invoices.go:197
#: pkg/products.go:177 pkg/quote.go:196 pkg/expenses.go:369 pkg/invoices.go:197
#: pkg/contacts.go:148
msgctxt "tag condition"
msgid "All"
@ -583,7 +672,7 @@ msgstr "Todas"
msgid "Invoices must have all the specified labels."
msgstr "Las facturas deben tener todas las etiquetas."
#: pkg/products.go:182 pkg/expenses.go:374 pkg/invoices.go:202
#: pkg/products.go:182 pkg/quote.go:201 pkg/expenses.go:374 pkg/invoices.go:202
#: pkg/contacts.go:153
msgctxt "tag condition"
msgid "Any"
@ -592,121 +681,264 @@ msgstr "Cualquiera"
#: pkg/products.go:183 pkg/expenses.go:375 pkg/invoices.go:203
#: pkg/contacts.go:154
msgid "Invoices must have at least one of the specified labels."
msgstr "Las facturas debent tener como mínimo una de las etiquetas."
msgstr "Las facturas deben tener como mínimo una de las etiquetas."
#: pkg/products.go:269 pkg/invoices.go:830
#: pkg/products.go:269 pkg/quote.go:807 pkg/invoices.go:838
msgctxt "input"
msgid "Description"
msgstr "Descripción"
#: pkg/products.go:274 pkg/invoices.go:834
#: pkg/products.go:274 pkg/quote.go:811 pkg/invoices.go:842
msgctxt "input"
msgid "Price"
msgstr "Precio"
#: pkg/products.go:284 pkg/expenses.go:181 pkg/invoices.go:863
#: pkg/products.go:284 pkg/quote.go:840 pkg/expenses.go:181 pkg/invoices.go:871
msgctxt "input"
msgid "Taxes"
msgstr "Impuestos"
#: pkg/products.go:309 pkg/profile.go:92 pkg/invoices.go:912
#: pkg/products.go:309 pkg/quote.go:889 pkg/profile.go:92 pkg/invoices.go:920
msgid "Name can not be empty."
msgstr "No podéis dejar el nombre en blanco."
#: pkg/products.go:310 pkg/invoices.go:913
#: pkg/products.go:310 pkg/quote.go:890 pkg/invoices.go:921
msgid "Price can not be empty."
msgstr "No podéis dejar el precio en blanco."
#: pkg/products.go:311 pkg/invoices.go:914
#: pkg/products.go:311 pkg/quote.go:891 pkg/invoices.go:922
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/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:922
#: pkg/products.go:313 pkg/quote.go:899 pkg/expenses.go:227 pkg/expenses.go:232
#: pkg/invoices.go:930
msgid "Selected tax is not valid."
msgstr "Habéis escogido un impuesto que no es válido."
#: pkg/products.go:314 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:923
#: pkg/products.go:314 pkg/quote.go:900 pkg/expenses.go:228 pkg/expenses.go:233
#: pkg/invoices.go:931
msgid "You can only select a tax of each class."
msgstr "Solo podéis escoger un impuesto de cada clase."
#: pkg/company.go:98
#: pkg/company.go:100
msgctxt "input"
msgid "Currency"
msgstr "Moneda"
#: pkg/company.go:105
#: pkg/company.go:107
msgctxt "input"
msgid "Invoice number format"
msgstr "Formato del número de factura"
#: pkg/company.go:111
#: pkg/company.go:113
msgctxt "input"
msgid "Next invoice number"
msgstr "Número de presupuesto"
#: pkg/company.go:122
msgctxt "input"
msgid "Legal disclaimer"
msgstr "Nota legal"
#: pkg/company.go:129
#: pkg/company.go:141
msgid "Selected currency is not valid."
msgstr "Habéis escogido una moneda que no es válida."
#: pkg/company.go:130
#: pkg/company.go:142
msgid "Invoice number format can not be empty."
msgstr "No podéis dejar el formato del número de factura en blanco."
#: pkg/company.go:297
#: pkg/company.go:143
msgid "Next invoice number must be a number greater than zero."
msgstr "El siguiente número de factura tiene que ser un número mayor a cero."
#: pkg/company.go:350
msgctxt "input"
msgid "Tax name"
msgstr "Nombre impuesto"
#: pkg/company.go:303
#: pkg/company.go:356
msgctxt "input"
msgid "Tax Class"
msgstr "Clase de impuesto"
#: pkg/company.go:306
#: pkg/company.go:359
msgid "Select a tax class"
msgstr "Escoged una clase de impuesto"
#: pkg/company.go:310
#: pkg/company.go:363
msgctxt "input"
msgid "Rate (%)"
msgstr "Porcentaje"
#: pkg/company.go:333
#: pkg/company.go:386
msgid "Tax name can not be empty."
msgstr "No podéis dejar el nombre del impuesto en blanco."
#: pkg/company.go:334
#: pkg/company.go:387
msgid "Selected tax class is not valid."
msgstr "Habéis escogido una clase impuesto que no es válida."
#: pkg/company.go:335
#: pkg/company.go:388
msgid "Tax rate can not be empty."
msgstr "No podéis dejar el porcentaje en blanco."
#: pkg/company.go:336
#: pkg/company.go:389
msgid "Tax rate must be an integer between -99 and 99."
msgstr "El porcentaje tiene que estar entre -99 y 99."
#: pkg/company.go:399
#: pkg/company.go:452
msgctxt "input"
msgid "Payment method name"
msgstr "Nombre del método de pago"
#: pkg/company.go:405
#: pkg/company.go:458
msgctxt "input"
msgid "Instructions"
msgstr "Instrucciones"
#: pkg/company.go:423
#: pkg/company.go:476
msgid "Payment method name can not be empty."
msgstr "No podéis dejar el nombre del método de pago en blanco."
#: pkg/company.go:424
#: pkg/company.go:477
msgid "Payment instructions can not be empty."
msgstr "No podéis dejar las instrucciones de pago en blanco."
#: pkg/quote.go:161 pkg/quote.go:585 pkg/expenses.go:340 pkg/invoices.go:162
#: pkg/invoices.go:584
msgctxt "input"
msgid "Customer"
msgstr "Cliente"
#: pkg/quote.go:162 pkg/expenses.go:341 pkg/invoices.go:163
msgid "All customers"
msgstr "Todos los clientes"
#: pkg/quote.go:167 pkg/quote.go:579
msgctxt "input"
msgid "Quotation Status"
msgstr "Estado del presupuesto"
#: pkg/quote.go:168 pkg/invoices.go:169
msgid "All status"
msgstr "Todos los estados"
#: pkg/quote.go:173
msgctxt "input"
msgid "Quotation Number"
msgstr "Número de presupuesto"
#: pkg/quote.go:178 pkg/expenses.go:351 pkg/invoices.go:179
msgctxt "input"
msgid "From Date"
msgstr "A partir de la fecha"
#: pkg/quote.go:183 pkg/expenses.go:356 pkg/invoices.go:184
msgctxt "input"
msgid "To Date"
msgstr "Hasta la fecha"
#: pkg/quote.go:197
msgid "Quotations must have all the specified labels."
msgstr "Los presupuestos deben tener todas las etiquetas."
#: pkg/quote.go:202
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:451
msgid "Select a customer to quote."
msgstr "Escoged un cliente a presupuestar."
#: pkg/quote.go:527
msgid "quotations.zip"
msgstr "presupuestos.zip"
#: pkg/quote.go:533 pkg/quote.go:1055 pkg/quote.go:1063 pkg/invoices.go:533
#: pkg/invoices.go:1098 pkg/invoices.go:1106
msgid "Invalid action"
msgstr "Acción inválida."
#: pkg/quote.go:590
msgctxt "input"
msgid "Quotation Date"
msgstr "Fecha del presupuesto"
#: pkg/quote.go:596
msgctxt "input"
msgid "Terms and conditions"
msgstr "Condiciones de aceptación"
#: pkg/quote.go:601 pkg/invoices.go:596
msgctxt "input"
msgid "Notes"
msgstr "Notas"
#: pkg/quote.go:610 pkg/invoices.go:606
msgctxt "input"
msgid "Payment Method"
msgstr "Método de pago"
#: pkg/quote.go:646
msgid "Selected quotation status is not valid."
msgstr "Habéis escogido un estado de presupuesto que no es válido."
#: pkg/quote.go:647 pkg/invoices.go:643
msgid "Selected customer is not valid."
msgstr "Habéis escogido un cliente que no es válido."
#: pkg/quote.go:648
msgid "Quotation date can not be empty."
msgstr "No podéis dejar la fecha del presupuesto en blanco."
#: pkg/quote.go:649
msgid "Quotation date must be a valid date."
msgstr "La fecha de presupuesto debe ser válida."
#: pkg/quote.go:651 pkg/invoices.go:647
msgid "Selected payment method is not valid."
msgstr "Habéis escogido un método de pago que no es válido."
#: pkg/quote.go:783 pkg/quote.go:788 pkg/invoices.go:814 pkg/invoices.go:819
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/quote.go:821 pkg/invoices.go:852
msgctxt "input"
msgid "Quantity"
msgstr "Cantidad"
#: pkg/quote.go:830 pkg/invoices.go:861
msgctxt "input"
msgid "Discount (%)"
msgstr "Descuento (%)"
#: pkg/quote.go:884
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:887 pkg/invoices.go:918
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:893 pkg/invoices.go:924
msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco."
#: pkg/quote.go:894 pkg/invoices.go:925
msgid "Quantity must be a number greater than zero."
msgstr "La cantidad tiene que ser un número mayor a cero."
#: pkg/quote.go:896 pkg/invoices.go:927
msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco."
#: pkg/quote.go:897 pkg/invoices.go:928
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
#: pkg/profile.go:25
msgctxt "language option"
msgid "Automatic"
@ -815,39 +1047,16 @@ msgstr "No podéis dejar el importe en blanco."
msgid "Amount must be a number greater than zero."
msgstr "El importe tiene que ser un número mayor a cero."
#: pkg/expenses.go:340 pkg/invoices.go:162 pkg/invoices.go:584
msgctxt "input"
msgid "Customer"
msgstr "Cliente"
#: pkg/expenses.go:341 pkg/invoices.go:163
msgid "All customers"
msgstr "Todos los clientes"
#: pkg/expenses.go:346 pkg/invoices.go:174
msgctxt "input"
msgid "Invoice Number"
msgstr "Número de factura"
#: pkg/expenses.go:351 pkg/invoices.go:179
msgctxt "input"
msgid "From Date"
msgstr "A partir de la fecha"
#: pkg/expenses.go:356 pkg/invoices.go:184
msgctxt "input"
msgid "To Date"
msgstr "Hasta la fecha"
#: pkg/invoices.go:168 pkg/invoices.go:578
msgctxt "input"
msgid "Invoice Status"
msgstr "Estado de la factura"
#: pkg/invoices.go:169
msgid "All status"
msgstr "Todos los estados"
#: pkg/invoices.go:426
msgid "Select a customer to bill."
msgstr "Escoged un cliente a facturar."
@ -856,75 +1065,18 @@ msgstr "Escoged un cliente a facturar."
msgid "invoices.zip"
msgstr "facturas.zip"
#: pkg/invoices.go:533 pkg/invoices.go:1090 pkg/invoices.go:1098
msgid "Invalid action"
msgstr "Acción inválida."
#: pkg/invoices.go:596
msgctxt "input"
msgid "Notes"
msgstr "Notas"
#: pkg/invoices.go:606
msgctxt "input"
msgid "Payment Method"
msgstr "Método de pago"
#: pkg/invoices.go:642
msgid "Selected invoice status is not valid."
msgstr "Habéis escogido un estado de factura que no es válido."
#: pkg/invoices.go:643
msgid "Selected customer is not valid."
msgstr "Habéis escogido un cliente que no es válido."
#: pkg/invoices.go:644
msgid "Invoice date can not be empty."
msgstr "No podéis dejar la fecha de la factura en blanco."
#: pkg/invoices.go:647
msgid "Selected payment method is not valid."
msgstr "Habéis escogido un método de pago que no es válido."
#: pkg/invoices.go:806 pkg/invoices.go:811
msgctxt "input"
msgid "Id"
msgstr "Identificador"
#: pkg/invoices.go:844
msgctxt "input"
msgid "Quantity"
msgstr "Cantidad"
#: pkg/invoices.go:853
msgctxt "input"
msgid "Discount (%)"
msgstr "Descuento (%)"
#: pkg/invoices.go:907
#: pkg/invoices.go:915
msgid "Invoice product ID must be a number greater than zero."
msgstr "El ID de producto de factura tiene que ser un número mayor a cero."
#: pkg/invoices.go:910
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/invoices.go:916
msgid "Quantity can not be empty."
msgstr "No podéis dejar la cantidad en blanco."
#: pkg/invoices.go:917
msgid "Quantity must be a number greater than zero."
msgstr "La cantidad tiene que ser un número mayor a cero."
#: pkg/invoices.go:919
msgid "Discount can not be empty."
msgstr "No podéis dejar el descuento en blanco."
#: pkg/invoices.go:920
msgid "Discount must be a percentage between 0 and 100."
msgstr "El descuento tiene que ser un porcentaje entre 0 y 100."
#: pkg/contacts.go:238
msgctxt "input"
msgid "Business name"

View File

@ -577,28 +577,33 @@ main > nav {
/* Invoice */
.new-quote-product input,
.new-invoice-product input {
width: 100%;
}
.new-quote-product,
.new-invoice-product {
display: grid;
grid-template-columns: repeat(4, 1fr);
position: relative;
}
.new-quote-product .delete-product,
.new-invoice-product .delete-product {
position: absolute;
right: 0;
top: .75rem;
}
.new-quote-product .input:nth-of-type(5),
.new-invoice-product .input:nth-of-type(5) {
grid-column-start: 1;
grid-column-end: 4;
}
.new-quote-product textarea,
.new-invoice-product textarea {
width: 100%;
height: 100%;
@ -608,23 +613,28 @@ main > nav {
text-align: right;
}
.quote-download,
.invoice-download {
text-align: center;
}
.quote-download a,
.invoice-download a {
color: inherit;
text-decoration: none;
}
.quote-status,
.invoice-status {
position: relative;
}
.quote-status summary,
.invoice-status summary {
height: 3rem;
}
.quote-status ul,
.invoice-status ul {
position: absolute;
top: 0;
@ -635,29 +645,35 @@ main > nav {
gap: 1rem;
}
.quote-status button,
.invoice-status button {
border: 0;
min-width: 15rem;
}
[class^='quote-status-'],
[class^='invoice-status-'] {
text-align: center;
text-transform: uppercase;
cursor: pointer;
}
.quote-status-created,
.invoice-status-created {
background-color: var(--numerus--color--light-blue);
}
.quote-status-sent,
.invoice-status-sent {
background-color: var(--numerus--color--hay);
}
.quote-status-accepted,
.invoice-status-paid {
background-color: var(--numerus--color--light-green);
}
.quote-status-rejected,
.invoice-status-unpaid {
background-color: var(--numerus--color--rosy);
}
@ -670,12 +686,14 @@ main > nav {
right: 0;
}
.invoice-data, .product-data, .expenses-data {
.invoice-data, .quote-data, .product-data, .expenses-data {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.quote-data .input:last-child,
.quote-data .input:nth-child(6),
.invoice-data .input:last-child {
grid-column-start: 1;
grid-column-end: 5;

View File

@ -44,6 +44,7 @@
<nav aria-label="{{( pgettext "Main" "title" )}}" data-hx-target="main" data-hx-boost="true">
<ul>
<li><a href="{{ companyURI "/" }}">{{( pgettext "Dashboard" "nav" )}}</a></li>
<li><a href="{{ companyURI "/quotes" }}">{{( pgettext "Quotations" "nav" )}}</a></li>
<li><a href="{{ companyURI "/invoices" }}">{{( pgettext "Invoices" "nav" )}}</a></li>
<li><a href="{{ companyURI "/expenses" }}">{{( pgettext "Expenses" "nav" )}}</a></li>
<li><a href="{{ companyURI "/products" }}">{{( pgettext "Products" "nav" )}}</a></li>

View File

@ -165,6 +165,29 @@
</fieldset>
{{- end }}
{{ define "quote-product-form" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.quoteProductForm*/ -}}
<fieldset class="new-quote-product"
data-hx-select="unset"
data-hx-vals='{"index": {{ .Index }}}'
data-hx-include="[name='product.quantity.{{ .Index }}']"
>
<button type="submit" class="icon delete-product"
formnovalidate
name="action" value="remove-product.{{ .Index }}"
aria-label="{{( gettext "Delete product from quotation" )}}"
><i class="ri-delete-back-2-line"></i></button>
{{ template "hidden-field" .QuoteProductId }}
{{ template "hidden-field" .ProductId }}
{{ template "input-field" .Name }}
{{ template "input-field" .Price }}
{{ template "input-field" .Quantity }}
{{ template "input-field" .Discount }}
{{ template "input-field" .Description | addInputAttr `rows="1"`}}
{{ template "select-field" .Tax }}
</fieldset>
{{- end }}
{{ define "filters-toggle" -}}
<button id="filters-toggle" x-cloak x-data="{}"
@click="document.body.classList.toggle('filters-visible')"

View File

@ -0,0 +1,103 @@
{{ define "title" -}}
{{ printf ( pgettext "Edit Quotation “%s”" "title" ) .Number }}
{{- end }}
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editQuotationPage*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a href="{{ companyURI "/quotes"}}">{{( pgettext "Quotations" "title" )}}</a> /
<a>{{ .Number }}</a>
</p>
</nav>
{{- end }}
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editQuotationPage*/ -}}
<section id="quote-dialog-content" data-hx-target="main">
<h2>{{ printf (pgettext "Edit Quotation “%s”" "title") .Number }}</h2>
<form method="POST" action="{{ companyURI "/quotes/" }}{{ .Slug }}/edit" data-hx-boost="true">
{{ csrfToken }}
{{ with .Form -}}
{{ if .RemovedProduct -}}
<div role="alert">
{{ with .RemovedProduct -}}
<p>{{printf (gettext "Product “%s” removed") .Name}}</p>
<button type="submit"
formnovalidate
name="action" value="restore-product"
>{{( pgettext "Undo" "action" )}}</button>
{{ template "hidden-field" .QuoteProductId }}
{{ template "hidden-field" .ProductId }}
{{ template "hidden-field" .Name }}
{{ template "hidden-field" .Price }}
{{ template "hidden-field" .Quantity }}
{{ template "hidden-field" .Discount }}
{{ template "hidden-field" .Description }}
{{ template "hidden-select-field" .Tax }}
{{- end }}
</div>
{{- end }}
<div class="quote-data">
{{ template "select-field" .Customer }}
{{ template "hidden-field" .Date }}
{{ template "tags-field" .Tags }}
{{ template "select-field" .PaymentMethod }}
{{ template "select-field" .QuoteStatus }}
{{ template "input-field" .TermsAndConditions }}
{{ template "input-field" .Notes }}
</div>
{{- range $product := .Products }}
{{ template "quote-product-form" . }}
{{- end }}
{{- end }}
<table id="quote-summary">
<tbody>
<tr>
<th scope="row">{{(pgettext "Subtotal" "title")}}</th>
<td class="numeric">{{ .Subtotal | formatPrice }}</td>
</tr>
{{- range $tax := .Taxes }}
<tr>
<th scope="row">{{ index . 0 }}</th>
<td class="numeric">{{ index . 1 | formatPrice }}</td>
</tr>
{{- end }}
<tr>
<th scope="row">{{(pgettext "Total" "title")}}</th>
<td class="numeric">{{ .Total | formatPrice }}</td>
</tr>
</tbody>
</table>
<fieldset class="button-bar">
<button formnovalidate
name="action" value="select-products"
data-hx-get="{{ companyURI "/quotes/product-form" }}"
data-hx-target="#quote-summary" data-hx-swap="beforebegin"
data-hx-select="unset"
data-hx-vals="js:{index: document.querySelectorAll('.new-quote-product').length}"
type="submit">{{( pgettext "Add products" "action" )}}</button>
<button formnovalidate
id="recompute-button"
name="action" value="update"
type="submit">{{( pgettext "Update" "action" )}}</button>
<button class="primary" name="_method" value="PUT"
formaction="{{ companyURI "/quotes" }}/{{ .Slug }}"
type="submit">{{( pgettext "Save" "action" )}}</button>
</fieldset>
</form>
</section>
<script>
document.body.addEventListener('recompute', function () {
document.getElementById('recompute-button').click();
});
</script>
{{- end }}

View File

@ -0,0 +1,142 @@
{{ define "title" -}}
{{( pgettext "Quotations" "title" )}}
{{- end }}
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.QuotesIndexPage*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a>{{( pgettext "Quotations" "title" )}}</a>
</p>
<form id="batch-form" action="{{ companyURI "/quotes/batch" }}" method="post">
{{ csrfToken }}
<p>
{{ template "filters-toggle" }}
<button type="submit"
name="action" value="download"
>{{( pgettext "Download quotations" "action" )}}</button>
<a class="primary button"
href="{{ companyURI "/quotes/new" }}">{{( pgettext "New quotation" "action" )}}</a>
</p>
</form>
</nav>
{{- end }}
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.QuotesIndexPage*/ -}}
<form class="filters" method="GET" action="{{ companyURI "/quotes"}}"
data-hx-target="main" data-hx-boost="true" data-hx-trigger="change,search,submit"
aria-labelledby="filters-toggle"
>
{{ with .Filters }}
{{ template "select-field" .Customer }}
{{ template "select-field" .QuoteStatus }}
{{ template "input-field" .FromDate }}
{{ template "input-field" .ToDate }}
{{ template "input-field" .QuoteNumber }}
{{ template "tags-field" .Tags | addTagsAttr (print `data-conditions="` .TagsCondition.Name `-field"`) }}
{{ template "toggle-field" .TagsCondition }}
{{ end }}
<noscript>
<button type="submit">{{( pgettext "Filter" "action" )}}</button>
</noscript>
</form>
<table id="quote-list">
<thead>
<tr>
<th>{{( pgettext "All" "quote" )}}</th>
<th>{{( pgettext "Date" "title" )}}</th>
<th>{{( pgettext "Quotation Num." "title" )}}</th>
<th>{{( pgettext "Customer" "title" )}}</th>
<th>{{( pgettext "Status" "title" )}}</th>
<th>{{( pgettext "Tags" "title" )}}</th>
<th>{{( pgettext "Amount" "title" )}}</th>
<th>{{( pgettext "Download" "title" )}}</th>
<th>{{( pgettext "Actions" "title" )}}</th>
</tr>
</thead>
<tbody>
{{ with .Quotes }}
{{- range $quote := . }}
<tr>
{{ $title := .Number | printf (pgettext "Select quotation %v" "action") }}
<td><input type="checkbox" form="batch-form"
name="quote" value="{{ .Slug }}"
aria-label="{{ $title }}"
title="{{ $title }}"/></td>
<td>{{ .Date|formatDate }}</td>
<td><a href="{{ companyURI "/quotes/"}}{{ .Slug }}" data-hx-target="main"
data-hx-boost="true">{{ .Number }}</a></td>
<td>{{ .CustomerName }}</td>
<td>
<details class="quote-status menu">
<summary class="quote-status-{{ .Status }}">{{ .StatusLabel }}</summary>
<form action="{{companyURI "/quotes/"}}{{ .Slug }}" method="POST" data-hx-boost="true">
{{ csrfToken }}
{{ putMethod }}
<input type="hidden" name="quick" value="status">
<ul role="menu">
{{- range $status, $name := $.QuoteStatuses }}
{{- if ne $status $quote.Status }}
<li role="presentation">
<button role="menuitem" type="submit"
name="quote_status" value="{{ $status }}"
class="quote-status-{{ $status }}"
>{{ $name }}</button>
</li>
{{- end }}
{{- end }}
</ul>
</form>
</details>
</td>
<td data-hx-get="{{companyURI "/quotes/"}}{{ .Slug }}/tags/edit"
data-hx-target="this"
data-hx-swap="outerHTML"
>
{{- range $index, $tag := .Tags }}
{{- if gt $index 0 }}, {{ end -}}
{{ . }}
{{- end }}
</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
class="ri-download-line"></i></a></td>
<td class="actions">
<details class="menu">
<summary><i class="ri-more-line"></i></summary>
<ul role="menu" class="action-menu">
<li role="presentation">
<a role="menuitem" href="{{ companyURI "/quotes"}}/{{ .Slug }}/edit"
data-hx-target="main" data-hx-boost="true"
>
<i class="ri-edit-line"></i>
{{( pgettext "Edit" "action" )}}
</a>
</li>
<li role="presentation">
<a role="menuitem" href="{{ companyURI "/quotes/new"}}?duplicate={{ .Slug }}"
data-hx-target="main" data-hx-boost="true"
>
<i class="ri-file-copy-line"></i>
{{( pgettext "Duplicate" "action" )}}
</a>
</li>
</ul>
</details>
</td>
</tr>
{{- end }}
{{ else }}
<tr>
<td colspan="9">{{( gettext "No quotations added yet." )}}</td>
</tr>
{{ end }}
</tbody>
</table>
{{- end }}

View File

@ -0,0 +1,101 @@
{{ define "title" -}}
{{( pgettext "New Quotation" "title" )}}
{{- end }}
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newQuotePage*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a href="{{ companyURI "/quotes"}}">{{( pgettext "Quotations" "title" )}}</a> /
<a>{{( pgettext "New Quotation" "title" )}}</a>
</p>
</nav>
{{- end }}
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newQuotePage*/ -}}
<section id="quote-dialog-content" data-hx-target="main">
<h2>{{(pgettext "New Quotation" "title")}}</h2>
<form method="POST" action="{{ companyURI "/quotes/new" }}" data-hx-boost="true">
{{ csrfToken }}
{{ with .Form -}}
{{ if .RemovedProduct -}}
<div role="alert">
{{ with .RemovedProduct -}}
<p>{{printf (gettext "Product “%s” removed") .Name}}</p>
<button type="submit"
formnovalidate
name="action" value="restore-product"
>{{( pgettext "Undo" "action" )}}</button>
{{ template "hidden-field" .QuoteProductId }}
{{ template "hidden-field" .ProductId }}
{{ template "hidden-field" .Name }}
{{ template "hidden-field" .Price }}
{{ template "hidden-field" .Quantity }}
{{ template "hidden-field" .Discount }}
{{ template "hidden-field" .Description }}
{{ template "hidden-select-field" .Tax }}
{{- end }}
</div>
{{- end }}
<div class="quote-data">
{{ template "hidden-select-field" .QuoteStatus }}
{{ template "select-field" .Customer }}
{{ template "input-field" .Date }}
{{ template "tags-field" .Tags }}
{{ template "select-field" .PaymentMethod }}
{{ template "input-field" .TermsAndConditions }}
{{ template "input-field" .Notes }}
</div>
{{- range $product := .Products }}
{{ template "quote-product-form" . }}
{{- end }}
{{- end }}
<table id="quote-summary">
<tbody>
<tr>
<th scope="row">{{(pgettext "Subtotal" "title")}}</th>
<td class="numeric">{{ .Subtotal | formatPrice }}</td>
</tr>
{{- range $tax := .Taxes }}
<tr>
<th scope="row">{{ index . 0 }}</th>
<td class="numeric">{{ index . 1 | formatPrice }}</td>
</tr>
{{- end }}
<tr>
<th scope="row">{{(pgettext "Total" "title")}}</th>
<td class="numeric">{{ .Total | formatPrice }}</td>
</tr>
</tbody>
</table>
<fieldset class="button-bar">
<button formnovalidate
name="action" value="select-products"
data-hx-get="{{ companyURI "/quotes/product-form" }}"
data-hx-target="#quote-summary" data-hx-swap="beforebegin"
data-hx-select="unset"
data-hx-vals="js:{index: document.querySelectorAll('.new-quote-product').length}"
type="submit">{{( pgettext "Add products" "action" )}}</button>
<button formnovalidate
id="recompute-button"
name="action" value="update"
type="submit">{{( pgettext "Update" "action" )}}</button>
<button class="primary" name="action" value="add"
formaction="{{ companyURI "/quotes" }}"
type="submit">{{( pgettext "Save" "action" )}}</button>
</fieldset>
</form>
</section>
<script>
document.body.addEventListener('recompute', function () {
document.getElementById('recompute-button').click();
});
</script>
{{- end }}

View File

@ -0,0 +1,4 @@
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.quoteProductForm*/ -}}
{{ template "quote-product-form" . }}
{{- end }}

View File

@ -0,0 +1,76 @@
{{ define "title" -}}
{{( pgettext "Add Products to Quotation" "title" )}}
{{- end }}
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newQuoteProductsPage*/ -}}
<nav data-hx-target="main" data-hx-boost="true">
<p>
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a href="{{ companyURI "/quotes"}}">{{( pgettext "Quotations" "title" )}}</a> /
{{ if eq .Form.Number "" }}
<a>{{( pgettext "New Quotation" "title" )}}</a>
{{ else }}
<a>{{ .Form.Number }}</a>
{{ end }}
</p>
</nav>
{{- end }}
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.newQuotationProductsPage*/ -}}
<section id="quote-dialog-content" data-hx-target="this">
<h2>{{(pgettext "Add Products to Quotation" "title")}}</h2>
<form method="POST" action="{{ .Action }}" data-hx-boost="true" data-hx-select="#quote-dialog-content">
{{ csrfToken }}
{{- with .Form }}
{{ template "hidden-select-field" .Customer }}
{{ template "hidden-field" .Date }}
{{ template "hidden-field" .Notes }}
{{ template "hidden-field" .Tags }}
{{- range $product := .Products }}
{{ template "hidden-field" .QuoteProductId }}
{{ template "hidden-field" .ProductId }}
{{ template "hidden-field" .Name }}
{{ template "hidden-field" .Description }}
{{ template "hidden-field" .Price }}
{{ template "hidden-field" .Quantity }}
{{ template "hidden-field" .Discount }}
{{ template "hidden-select-field" .Tax }}
{{- end }}
{{- end }}
<table>
<thead>
<tr>
<th>{{( pgettext "All" "product" )}}</th>
<th>{{( pgettext "Name" "title" )}}</th>
<th>{{( pgettext "Price" "title" )}}</th>
</tr>
</thead>
<tbody>
{{ with .Products }}
{{- range . }}
<tr>
<td><input type="checkbox" name="slug" id="product-{{ .Slug }}" value="{{.Slug}}"></td>
<td><label for="product-{{ .Slug }}">{{ .Name }}</label></td>
<td class="numeric">{{ .Price | formatPrice }}</td>
</tr>
{{- end }}
{{ else }}
<tr>
<td colspan="4">{{( gettext "No products added yet." )}}</td>
</tr>
{{ end }}
</tbody>
</table>
<fieldset>
<button class="primary" type="submit"
name="action" value="add-products">{{( pgettext "Add products" "action" )}}</button>
</fieldset>
</form>
</section>
{{- end }}

View File

@ -0,0 +1,126 @@
{{ define "title" -}}
{{ .Number | printf ( pgettext "Quotation %s" "title" )}}
{{- end }}
{{ define "breadcrumbs" -}}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.quote*/ -}}
<nav>
<p data-hx-target="main" data-hx-boost="true">
<a href="{{ companyURI "/" }}">{{( pgettext "Home" "title" )}}</a> /
<a href="{{ companyURI "/quotes"}}">{{( pgettext "Quotations" "title" )}}</a> /
<a>{{ .Number }}</a>
</p>
<p>
<a class="button primary"
data-hx-target="main" data-hx-boost="true"
href="{{ companyURI "/quotes/new"}}?duplicate={{ .Slug }}">{{( pgettext "Duplicate" "action" )}}</a>
<a class="button primary"
data-hx-target="main" data-hx-boost="true"
href="{{ companyURI "/quotes/"}}{{ .Slug }}/edit">{{( pgettext "Edit" "action" )}}</a>
<a class="primary button"
href="{{ companyURI "/quotes/" }}{{ .Slug }}.pdf"
download="{{ .Number}}.pdf">{{( pgettext "Download quotation" "action" )}}</a>
</p>
</nav>
{{- end }}
{{ define "content" }}
{{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.quote*/ -}}
<link rel="stylesheet" type="text/css" href="/static/invoice.css">
<article class="invoice">
<header>
<div>
<h1>{{ .Number | printf ( pgettext "Quotation %s" "title" )}}</h1>
<p class="date">{{( pgettext "Date" "title" )}} {{ .Date | formatDate }}</p>
</div>
<address class="quoter">
{{ .Quoter.Name }}<br>
{{ .Quoter.VATIN }}<br>
{{ .Quoter.Address }}<br>
{{ .Quoter.City }} ({{ .Quoter.PostalCode}}), {{ .Quoter.Province }}<br>
{{ .Quoter.Email }}<br>
{{ .Quoter.Phone }}<br>
</address>
<p class="legal">{{ .LegalDisclaimer }}</p>
</header>
<div>
<address class="quotee">
{{ .Quotee.Name }}<br>
{{ .Quotee.VATIN }}<br>
{{ .Quotee.Address }}<br>
{{ .Quotee.City }} ({{ .Quotee.PostalCode}}), {{ .Quotee.Province }}<br>
</address>
{{- $columns := 5 | add (len .TaxClasses) | add (boolToInt .HasDiscounts) -}}
<table>
<thead>
<tr>
<th>{{( pgettext "Concept" "title" )}}</th>
<th class="numeric">{{( pgettext "Price" "title" )}}</th>
{{ if .HasDiscounts -}}
<th class="numeric">{{( pgettext "Discount" "title" )}}</th>
{{ end -}}
<th class="numeric">{{( pgettext "Units" "title" )}}</th>
<th class="numeric">{{( pgettext "Subtotal" "title" )}}</th>
{{ range $class := .TaxClasses -}}
<th class="numeric">{{ . }}</th>
{{ end -}}
<th class="numeric">{{( pgettext "Total" "title" )}}</th>
</tr>
</thead>
{{ $lastIndex := len .Products | sub 1 }}
{{ range $index, $product := .Products -}}
<tbody>
{{- if .Description }}
<tr class="name">
<td colspan="{{ $columns }}">{{ .Name }}</td>
</tr>
{{ end -}}
<tr>
{{- if .Description }}
<td>{{ .Description }}</td>
{{- else }}
<td>{{ .Name }}</td>
{{- end -}}
<td class="numeric">{{ .Price | formatPrice }}</td>
{{ if $.HasDiscounts -}}
<td class="numeric">{{ $product.Discount | formatPercent }}</td>
{{ end -}}
<td class="numeric">{{ .Quantity }}</td>
<td class="numeric">{{ .Subtotal | formatPrice }}</td>
{{ range $class := $.TaxClasses -}}
<td class="numeric">{{ index $product.Taxes $class | formatPercent }}</td>
{{ end -}}
<td class="numeric">{{ .Total | formatPrice }}</td>
</tr>
{{ if (eq $index $lastIndex) }}
<tr class="tfoot separator">
<th scope="row" colspan="{{ $columns | sub 1 }}">{{( pgettext "Tax Base" "title" )}}</th>
<td class="numeric">{{ $.Subtotal | formatPrice }}</td>
</tr>
{{ range $tax := $.Taxes -}}
<tr class="tfoot">
<th scope="row" colspan="{{ $columns | sub 1 }}">{{ index . 0 }}</th>
<td class="numeric">{{ index . 1 | formatPrice }}</td>
</tr>
{{- end }}
<tr class="tfoot">
<th scope="row" colspan="{{ $columns | sub 1 }}">{{( pgettext "Total" "title" )}}</th>
<td class="numeric">{{ $.Total | formatPrice }}</td>
</tr>
{{ end }}
</tbody>
{{- end }}
</table>
{{ if .Notes -}}
<p class="notes">{{ .Notes }}</p>
{{- end }}
<p class="payment-instructions">{{ .PaymentInstructions }}</p>
</div>
</article>
{{- end}}