From 55d650bd62eea94b7a1efda9239215cfe914ef99 Mon Sep 17 00:00:00 2001 From: jordi fita mas Date: Wed, 3 May 2023 12:46:25 +0200 Subject: [PATCH] =?UTF-8?q?Add=20expenses=E2=80=99=20index=20and=20add=20f?= =?UTF-8?q?orm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/expenses.go | 239 +++++++++++++++++++++++++++++ pkg/router.go | 3 + po/ca.po | 158 ++++++++++++++----- po/es.po | 160 ++++++++++++++----- web/template/app.gohtml | 1 + web/template/expenses/edit.gohtml | 39 +++++ web/template/expenses/index.gohtml | 57 +++++++ web/template/expenses/new.gohtml | 36 +++++ 8 files changed, 620 insertions(+), 73 deletions(-) create mode 100644 pkg/expenses.go create mode 100644 web/template/expenses/edit.gohtml create mode 100644 web/template/expenses/index.gohtml create mode 100644 web/template/expenses/new.gohtml diff --git a/pkg/expenses.go b/pkg/expenses.go new file mode 100644 index 0000000..97e3179 --- /dev/null +++ b/pkg/expenses.go @@ -0,0 +1,239 @@ +package pkg + +import ( + "context" + "fmt" + "github.com/julienschmidt/httprouter" + "html/template" + "math" + "net/http" + "time" +) + +type ExpenseEntry struct { + Slug string + InvoiceDate time.Time + InvoiceNumber string + Amount string + InvoicerName string + Tags []string +} + +type expensesIndexPage struct { + Expenses []*ExpenseEntry +} + +func IndexExpenses(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + conn := getConn(r) + company := mustGetCompany(r) + page := &expensesIndexPage{ + Expenses: mustCollectExpenseEntries(r.Context(), conn, company), + } + mustRenderMainTemplate(w, r, "expenses/index.gohtml", page) +} + +func mustCollectExpenseEntries(ctx context.Context, conn *Conn, company *Company) []*ExpenseEntry { + rows, err := conn.Query(ctx, ` + select expense.slug + , invoice_date + , invoice_number + , to_price(amount, decimal_digits) + , contact.business_name + , expense.tags + from expense + join contact using (contact_id) + join currency using (currency_code) + where expense.company_id = $1 + order by invoice_date + `, company.Id) + if err != nil { + panic(err) + } + defer rows.Close() + + var entries []*ExpenseEntry + for rows.Next() { + entry := &ExpenseEntry{} + err = rows.Scan(&entry.Slug, &entry.InvoiceDate, &entry.InvoiceNumber, &entry.Amount, &entry.InvoicerName, &entry.Tags) + if err != nil { + panic(err) + } + entries = append(entries, entry) + } + if rows.Err() != nil { + panic(rows.Err()) + } + + return entries +} +func ServeExpenseForm(w http.ResponseWriter, r *http.Request, params httprouter.Params) { + locale := getLocale(r) + conn := getConn(r) + company := mustGetCompany(r) + form := newExpenseForm(r.Context(), conn, locale, company) + slug := params[0].Value + if slug == "new" { + w.WriteHeader(http.StatusOK) + form.InvoiceDate.Val = time.Now().Format("2006-01-02") + mustRenderNewExpenseForm(w, r, form) + return + } + if !form.MustFillFromDatabase(r.Context(), conn, slug) { + http.NotFound(w, r) + return + } + w.WriteHeader(http.StatusOK) + mustRenderEditExpenseForm(w, r, slug, form) +} + +func mustRenderNewExpenseForm(w http.ResponseWriter, r *http.Request, form *expenseForm) { + locale := getLocale(r) + form.Invoicer.EmptyLabel = gettext("Select a contact.", locale) + mustRenderMainTemplate(w, r, "expenses/new.gohtml", form) +} + +func mustRenderEditExpenseForm(w http.ResponseWriter, r *http.Request, slug string, form *expenseForm) { + page := &editExpensePage{ + Slug: slug, + Form: form, + } + mustRenderMainTemplate(w, r, "expenses/edit.gohtml", page) +} + +type editExpensePage struct { + Slug string + Form *expenseForm +} + +type expenseForm struct { + locale *Locale + company *Company + Invoicer *SelectField + InvoiceNumber *InputField + InvoiceDate *InputField + Tax *SelectField + Amount *InputField + Tags *TagsField +} + +func newExpenseForm(ctx context.Context, conn *Conn, locale *Locale, company *Company) *expenseForm { + return &expenseForm{ + locale: locale, + company: company, + Invoicer: &SelectField{ + Name: "invoicer", + Label: pgettext("input", "Contact", locale), + Required: true, + Options: mustGetContactOptions(ctx, conn, company), + }, + InvoiceNumber: &InputField{ + Name: "invoice_number", + Label: pgettext("input", "Invoice number", locale), + Type: "text", + }, + InvoiceDate: &InputField{ + Name: "invoice_date", + Label: pgettext("input", "Invoice Date", locale), + Required: true, + Type: "date", + }, + Tax: &SelectField{ + Name: "tax", + Label: pgettext("input", "Taxes", locale), + Multiple: true, + Options: mustGetTaxOptions(ctx, conn, company), + }, + Amount: &InputField{ + Name: "amount", + Label: pgettext("input", "Amount", locale), + Type: "number", + Required: true, + Attributes: []template.HTMLAttr{ + `min="0"`, + template.HTMLAttr(fmt.Sprintf(`step="%v"`, company.MinCents())), + }, + }, + Tags: &TagsField{ + Name: "tags", + Label: pgettext("input", "Tags", locale), + }, + } +} + +func (form *expenseForm) Parse(r *http.Request) error { + if err := r.ParseForm(); err != nil { + return err + } + form.Invoicer.FillValue(r) + form.InvoiceNumber.FillValue(r) + form.InvoiceDate.FillValue(r) + form.Tax.FillValue(r) + form.Amount.FillValue(r) + form.Tags.FillValue(r) + return nil +} + +func (form *expenseForm) Validate() bool { + validator := newFormValidator() + validator.CheckValidSelectOption(form.Invoicer, gettext("Selected contact is not valid.", form.locale)) + validator.CheckValidDate(form.InvoiceDate, gettext("Invoice date must be a valid date.", form.locale)) + validator.CheckValidSelectOption(form.Tax, gettext("Selected tax is not valid.", form.locale)) + validator.CheckAtMostOneOfEachGroup(form.Tax, gettext("You can only select a tax of each class.", form.locale)) + if validator.CheckRequiredInput(form.Amount, gettext("Amount can not be empty.", form.locale)) { + validator.CheckValidDecimal(form.Amount, form.company.MinCents(), math.MaxFloat64, gettext("Amount must be a number greater than zero.", form.locale)) + } + validator.CheckValidSelectOption(form.Tax, gettext("Selected tax is not valid.", form.locale)) + validator.CheckAtMostOneOfEachGroup(form.Tax, gettext("You can only select a tax of each class.", form.locale)) + return validator.AllOK() +} + +func (form *expenseForm) MustFillFromDatabase(ctx context.Context, conn *Conn, slug string) bool { + return !notFoundErrorOrPanic(conn.QueryRow(ctx, ` + select contact_id + , invoice_number + , invoice_date + , to_price(amount, decimal_digits) + , array_agg(tax_id) + , array_to_string(tags, ',') + from expense + left join expense_tax using (expense_id) + join currency using (currency_code) + where expense.slug = $1 + group by contact_id + , invoice_number + , invoice_date + , amount + , decimal_digits + , tags + `, slug).Scan( + form.Invoicer, + form.InvoiceNumber, + form.InvoiceDate, + form.Amount, + form.Tax, + form.Tags)) +} +func HandleAddExpense(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + conn := getConn(r) + locale := getLocale(r) + company := mustGetCompany(r) + form := newExpenseForm(r.Context(), conn, locale, company) + if err := form.Parse(r); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if err := verifyCsrfTokenValid(r); err != nil { + http.Error(w, err.Error(), http.StatusForbidden) + return + } + if !form.Validate() { + if !IsHTMxRequest(r) { + w.WriteHeader(http.StatusUnprocessableEntity) + } + mustRenderNewExpenseForm(w, r, form) + return + } + taxes := mustSliceAtoi(form.Tax.Selected) + conn.MustExec(r.Context(), "select add_expense($1, $2, $3, $4, $5, $6, $7)", company.Id, form.InvoiceDate, form.Invoicer, form.InvoiceNumber, form.Amount, taxes, form.Tags) + htmxRedirect(w, r, companyURI(company, "/expenses")) +} diff --git a/pkg/router.go b/pkg/router.go index 3b3b45c..d1bac87 100644 --- a/pkg/router.go +++ b/pkg/router.go @@ -34,6 +34,9 @@ func NewRouter(db *Db) http.Handler { companyRouter.PUT("/invoices/:slug/tags", HandleUpdateInvoiceTags) companyRouter.GET("/invoices/:slug/tags/edit", ServeEditInvoiceTags) companyRouter.GET("/search/products", HandleProductSearch) + companyRouter.GET("/expenses", IndexExpenses) + companyRouter.POST("/expenses", HandleAddExpense) + companyRouter.GET("/expenses/:slug", ServeExpenseForm) companyRouter.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { mustRenderMainTemplate(w, r, "dashboard.gohtml", nil) }) diff --git a/po/ca.po b/po/ca.po index c87bd2a..3ecfeb1 100644 --- a/po/ca.po +++ b/po/ca.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: numerus\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n" -"POT-Creation-Date: 2023-04-28 00:05+0200\n" +"POT-Creation-Date: 2023-05-03 12:40+0200\n" "PO-Revision-Date: 2023-01-18 17:08+0100\n" "Last-Translator: jordi fita mas \n" "Language-Team: Catalan \n" @@ -27,9 +27,10 @@ msgstr "Afegeix productes a la factura" #: 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/tax-details.gohtml:9 -#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:9 -#: web/template/products/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" @@ -48,7 +49,7 @@ msgid "New Invoice" msgstr "Nova factura" #: web/template/invoices/products.gohtml:48 -#: web/template/products/index.gohtml:38 +#: web/template/expenses/index.gohtml:25 web/template/products/index.gohtml:38 msgctxt "product" msgid "All" msgstr "Tots" @@ -139,7 +140,7 @@ msgctxt "title" msgid "Tags" msgstr "Etiquetes" -#: web/template/invoices/index.gohtml:52 +#: web/template/invoices/index.gohtml:52 web/template/expenses/index.gohtml:29 msgctxt "title" msgid "Amount" msgstr "Import" @@ -245,10 +246,15 @@ msgstr "Factures" #: web/template/app.gohtml:48 msgctxt "nav" +msgid "Expenses" +msgstr "Despeses" + +#: web/template/app.gohtml:49 +msgctxt "nav" msgid "Products" msgstr "Productes" -#: web/template/app.gohtml:49 +#: web/template/app.gohtml:50 msgctxt "nav" msgid "Contacts" msgstr "Contactes" @@ -335,6 +341,52 @@ msgctxt "action" msgid "Save changes" msgstr "Desa canvis" +#: web/template/expenses/new.gohtml:3 web/template/expenses/new.gohtml:12 +#: web/template/expenses/new.gohtml:20 +msgctxt "title" +msgid "New Expense" +msgstr "Nova despesa" + +#: web/template/expenses/new.gohtml:11 web/template/expenses/index.gohtml:3 +#: web/template/expenses/index.gohtml:11 web/template/expenses/edit.gohtml:11 +msgctxt "title" +msgid "Expenses" +msgstr "Despeses" + +#: web/template/expenses/new.gohtml:32 web/template/expenses/index.gohtml:15 +msgctxt "action" +msgid "New expense" +msgstr "Nova despesa" + +#: web/template/expenses/index.gohtml:26 +msgctxt "title" +msgid "Contact" +msgstr "Contacte" + +#: web/template/expenses/index.gohtml:27 +msgctxt "title" +msgid "Invoice Date" +msgstr "Data de factura" + +#: web/template/expenses/index.gohtml:28 +msgctxt "title" +msgid "Invoice Number" +msgstr "Número de factura" + +#: web/template/expenses/index.gohtml:52 +msgid "No expenses added yet." +msgstr "No hi ha cap despesa." + +#: web/template/expenses/edit.gohtml:3 web/template/expenses/edit.gohtml:20 +msgctxt "title" +msgid "Edit Expense “%s”" +msgstr "Edició de la despesa «%s»" + +#: web/template/expenses/edit.gohtml:35 +msgctxt "action" +msgid "Update expense" +msgstr "Actualitza despesa" + #: web/template/tax-details.gohtml:2 web/template/tax-details.gohtml:10 #: web/template/tax-details.gohtml:18 msgctxt "title" @@ -460,13 +512,14 @@ msgstr "No podeu deixar la contrasenya en blanc." msgid "Invalid user or password." msgstr "Nom d’usuari o contrasenya incorrectes." -#: pkg/products.go:161 pkg/products.go:260 pkg/invoices.go:771 +#: pkg/products.go:161 pkg/products.go:260 pkg/invoices.go:776 msgctxt "input" msgid "Name" msgstr "Nom" -#: pkg/products.go:166 pkg/products.go:287 pkg/invoices.go:187 -#: pkg/invoices.go:597 pkg/invoices.go:1031 pkg/contacts.go:256 +#: pkg/products.go:166 pkg/products.go:287 pkg/expenses.go:158 +#: pkg/invoices.go:187 pkg/invoices.go:597 pkg/invoices.go:1042 +#: pkg/contacts.go:256 msgctxt "input" msgid "Tags" msgstr "Etiquetes" @@ -494,38 +547,40 @@ 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:266 pkg/invoices.go:785 +#: pkg/products.go:266 pkg/invoices.go:790 msgctxt "input" msgid "Description" msgstr "Descripció" -#: pkg/products.go:271 pkg/invoices.go:789 +#: pkg/products.go:271 pkg/invoices.go:794 msgctxt "input" msgid "Price" msgstr "Preu" -#: pkg/products.go:281 pkg/invoices.go:815 +#: pkg/products.go:281 pkg/expenses.go:142 pkg/invoices.go:823 msgctxt "input" msgid "Taxes" msgstr "Imposts" -#: pkg/products.go:306 pkg/profile.go:92 pkg/invoices.go:856 +#: pkg/products.go:306 pkg/profile.go:92 pkg/invoices.go:867 msgid "Name can not be empty." msgstr "No podeu deixar el nom en blanc." -#: pkg/products.go:307 pkg/invoices.go:857 +#: pkg/products.go:307 pkg/invoices.go:868 msgid "Price can not be empty." msgstr "No podeu deixar el preu en blanc." -#: pkg/products.go:308 pkg/invoices.go:858 +#: pkg/products.go:308 pkg/invoices.go:869 msgid "Price must be a number greater than zero." msgstr "El preu ha de ser un número major a zero." -#: pkg/products.go:310 pkg/invoices.go:866 +#: pkg/products.go:310 pkg/expenses.go:180 pkg/expenses.go:185 +#: pkg/invoices.go:877 msgid "Selected tax is not valid." msgstr "Heu seleccionat un impost que no és vàlid." -#: pkg/products.go:311 pkg/invoices.go:867 +#: pkg/products.go:311 pkg/expenses.go:181 pkg/expenses.go:186 +#: pkg/invoices.go:878 msgid "You can only select a tax of each class." msgstr "Només podeu seleccionar un impost de cada classe." @@ -633,6 +688,46 @@ msgstr "La confirmació no és igual a la contrasenya." msgid "Selected language is not valid." msgstr "Heu seleccionat un idioma que no és vàlid." +#: pkg/expenses.go:91 +msgid "Select a contact." +msgstr "Escolliu un contacte." + +#: pkg/expenses.go:125 +msgctxt "input" +msgid "Contact" +msgstr "Contacte" + +#: pkg/expenses.go:131 +msgctxt "input" +msgid "Invoice number" +msgstr "Número de factura" + +#: pkg/expenses.go:136 pkg/invoices.go:586 +msgctxt "input" +msgid "Invoice Date" +msgstr "Data de factura" + +#: pkg/expenses.go:148 +msgctxt "input" +msgid "Amount" +msgstr "Import" + +#: pkg/expenses.go:178 +msgid "Selected contact is not valid." +msgstr "Heu seleccionat un contacte que no és vàlid." + +#: pkg/expenses.go:179 pkg/invoices.go:641 +msgid "Invoice date must be a valid date." +msgstr "La data de facturació ha de ser vàlida." + +#: pkg/expenses.go:182 +msgid "Amount can not be empty." +msgstr "No podeu deixar l’import en blanc." + +#: pkg/expenses.go:183 +msgid "Amount must be a number greater than zero." +msgstr "L’import ha de ser un número major a zero." + #: pkg/invoices.go:160 pkg/invoices.go:580 msgctxt "input" msgid "Customer" @@ -674,15 +769,10 @@ msgstr "Escolliu un client a facturar." msgid "invoices.zip" msgstr "factures.zip" -#: pkg/invoices.go:530 pkg/invoices.go:1017 +#: pkg/invoices.go:530 pkg/invoices.go:1028 msgid "Invalid action" msgstr "Acció invàlida." -#: pkg/invoices.go:586 -msgctxt "input" -msgid "Invoice Date" -msgstr "Data de factura" - #: pkg/invoices.go:592 msgctxt "input" msgid "Notes" @@ -705,46 +795,42 @@ msgstr "Heu seleccionat un client que no és vàlid." msgid "Invoice date can not be empty." msgstr "No podeu deixar la data de la factura en blanc." -#: pkg/invoices.go:641 -msgid "Invoice date must be a valid date." -msgstr "La data de facturació ha de ser vàlida." - #: pkg/invoices.go:643 msgid "Selected payment method is not valid." msgstr "Heu seleccionat un mètode de pagament que no és vàlid." -#: pkg/invoices.go:761 pkg/invoices.go:766 +#: pkg/invoices.go:766 pkg/invoices.go:771 msgctxt "input" msgid "Id" msgstr "Identificador" -#: pkg/invoices.go:798 +#: pkg/invoices.go:804 msgctxt "input" msgid "Quantity" msgstr "Quantitat" -#: pkg/invoices.go:806 +#: pkg/invoices.go:813 msgctxt "input" msgid "Discount (%)" msgstr "Descompte (%)" -#: pkg/invoices.go:854 +#: pkg/invoices.go:865 msgid "Product ID must be a number greater than zero." msgstr "L’ID del producte ha de ser un número major a zero." -#: pkg/invoices.go:860 +#: pkg/invoices.go:871 msgid "Quantity can not be empty." msgstr "No podeu deixar la quantitat en blanc." -#: pkg/invoices.go:861 +#: pkg/invoices.go:872 msgid "Quantity must be a number greater than zero." msgstr "La quantitat ha de ser un número major a zero." -#: pkg/invoices.go:863 +#: pkg/invoices.go:874 msgid "Discount can not be empty." msgstr "No podeu deixar el descompte en blanc." -#: pkg/invoices.go:864 +#: pkg/invoices.go:875 msgid "Discount must be a percentage between 0 and 100." msgstr "El descompte ha de ser un percentatge entre 0 i 100." diff --git a/po/es.po b/po/es.po index 18f4d35..73da364 100644 --- a/po/es.po +++ b/po/es.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: numerus\n" "Report-Msgid-Bugs-To: jordi@tandem.blog\n" -"POT-Creation-Date: 2023-04-28 00:05+0200\n" +"POT-Creation-Date: 2023-05-03 12:40+0200\n" "PO-Revision-Date: 2023-01-18 17:45+0100\n" "Last-Translator: jordi fita mas \n" "Language-Team: Spanish \n" @@ -27,9 +27,10 @@ msgstr "Añadir productos a la factura" #: 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/tax-details.gohtml:9 -#: web/template/products/new.gohtml:9 web/template/products/index.gohtml:9 -#: web/template/products/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" @@ -48,7 +49,7 @@ msgid "New Invoice" msgstr "Nueva factura" #: web/template/invoices/products.gohtml:48 -#: web/template/products/index.gohtml:38 +#: web/template/expenses/index.gohtml:25 web/template/products/index.gohtml:38 msgctxt "product" msgid "All" msgstr "Todos" @@ -139,7 +140,7 @@ msgctxt "title" msgid "Tags" msgstr "Etiquetes" -#: web/template/invoices/index.gohtml:52 +#: web/template/invoices/index.gohtml:52 web/template/expenses/index.gohtml:29 msgctxt "title" msgid "Amount" msgstr "Importe" @@ -206,7 +207,7 @@ msgstr "Base imponible" #: web/template/invoices/edit.gohtml:2 web/template/invoices/edit.gohtml:19 msgctxt "title" msgid "Edit Invoice “%s”" -msgstr "Edición del la factura «%s»" +msgstr "Edición de la factura «%s»" #: web/template/invoices/edit.gohtml:68 msgctxt "action" @@ -245,10 +246,15 @@ msgstr "Facturas" #: web/template/app.gohtml:48 msgctxt "nav" +msgid "Expenses" +msgstr "Gastos" + +#: web/template/app.gohtml:49 +msgctxt "nav" msgid "Products" msgstr "Productos" -#: web/template/app.gohtml:49 +#: web/template/app.gohtml:50 msgctxt "nav" msgid "Contacts" msgstr "Contactos" @@ -335,6 +341,52 @@ msgctxt "action" msgid "Save changes" msgstr "Guardar cambios" +#: web/template/expenses/new.gohtml:3 web/template/expenses/new.gohtml:12 +#: web/template/expenses/new.gohtml:20 +msgctxt "title" +msgid "New Expense" +msgstr "Nuevo gasto" + +#: web/template/expenses/new.gohtml:11 web/template/expenses/index.gohtml:3 +#: web/template/expenses/index.gohtml:11 web/template/expenses/edit.gohtml:11 +msgctxt "title" +msgid "Expenses" +msgstr "Gastos" + +#: web/template/expenses/new.gohtml:32 web/template/expenses/index.gohtml:15 +msgctxt "action" +msgid "New expense" +msgstr "Nuevo gasto" + +#: web/template/expenses/index.gohtml:26 +msgctxt "title" +msgid "Contact" +msgstr "Contacto" + +#: web/template/expenses/index.gohtml:27 +msgctxt "title" +msgid "Invoice Date" +msgstr "Fecha de factura" + +#: web/template/expenses/index.gohtml:28 +msgctxt "title" +msgid "Invoice Number" +msgstr "Número de factura" + +#: web/template/expenses/index.gohtml:52 +msgid "No expenses added yet." +msgstr "No hay gastos." + +#: web/template/expenses/edit.gohtml:3 web/template/expenses/edit.gohtml:20 +msgctxt "title" +msgid "Edit Expense “%s”" +msgstr "Edición del gasto «%s»" + +#: web/template/expenses/edit.gohtml:35 +msgctxt "action" +msgid "Update expense" +msgstr "Actualizar gasto" + #: web/template/tax-details.gohtml:2 web/template/tax-details.gohtml:10 #: web/template/tax-details.gohtml:18 msgctxt "title" @@ -460,13 +512,14 @@ 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:161 pkg/products.go:260 pkg/invoices.go:771 +#: pkg/products.go:161 pkg/products.go:260 pkg/invoices.go:776 msgctxt "input" msgid "Name" msgstr "Nombre" -#: pkg/products.go:166 pkg/products.go:287 pkg/invoices.go:187 -#: pkg/invoices.go:597 pkg/invoices.go:1031 pkg/contacts.go:256 +#: pkg/products.go:166 pkg/products.go:287 pkg/expenses.go:158 +#: pkg/invoices.go:187 pkg/invoices.go:597 pkg/invoices.go:1042 +#: pkg/contacts.go:256 msgctxt "input" msgid "Tags" msgstr "Etiquetes" @@ -494,38 +547,40 @@ msgstr "Cualquiera" msgid "Invoices must have at least one of the specified labels." msgstr "Las facturas debent tener como mínimo una de las etiquetas." -#: pkg/products.go:266 pkg/invoices.go:785 +#: pkg/products.go:266 pkg/invoices.go:790 msgctxt "input" msgid "Description" msgstr "Descripción" -#: pkg/products.go:271 pkg/invoices.go:789 +#: pkg/products.go:271 pkg/invoices.go:794 msgctxt "input" msgid "Price" msgstr "Precio" -#: pkg/products.go:281 pkg/invoices.go:815 +#: pkg/products.go:281 pkg/expenses.go:142 pkg/invoices.go:823 msgctxt "input" msgid "Taxes" msgstr "Impuestos" -#: pkg/products.go:306 pkg/profile.go:92 pkg/invoices.go:856 +#: pkg/products.go:306 pkg/profile.go:92 pkg/invoices.go:867 msgid "Name can not be empty." msgstr "No podéis dejar el nombre en blanco." -#: pkg/products.go:307 pkg/invoices.go:857 +#: pkg/products.go:307 pkg/invoices.go:868 msgid "Price can not be empty." msgstr "No podéis dejar el precio en blanco." -#: pkg/products.go:308 pkg/invoices.go:858 +#: pkg/products.go:308 pkg/invoices.go:869 msgid "Price must be a number greater than zero." msgstr "El precio tiene que ser un número mayor a cero." -#: pkg/products.go:310 pkg/invoices.go:866 +#: pkg/products.go:310 pkg/expenses.go:180 pkg/expenses.go:185 +#: pkg/invoices.go:877 msgid "Selected tax is not valid." msgstr "Habéis escogido un impuesto que no es válido." -#: pkg/products.go:311 pkg/invoices.go:867 +#: pkg/products.go:311 pkg/expenses.go:181 pkg/expenses.go:186 +#: pkg/invoices.go:878 msgid "You can only select a tax of each class." msgstr "Solo podéis escoger un impuesto de cada clase." @@ -633,6 +688,46 @@ msgstr "La confirmación no corresponde con la contraseña." msgid "Selected language is not valid." msgstr "Habéis escogido un idioma que no es válido." +#: pkg/expenses.go:91 +msgid "Select a contact." +msgstr "Escoged un contacto" + +#: pkg/expenses.go:125 +msgctxt "input" +msgid "Contact" +msgstr "Contacto" + +#: pkg/expenses.go:131 +msgctxt "input" +msgid "Invoice number" +msgstr "Número de factura" + +#: pkg/expenses.go:136 pkg/invoices.go:586 +msgctxt "input" +msgid "Invoice Date" +msgstr "Fecha de factura" + +#: pkg/expenses.go:148 +msgctxt "input" +msgid "Amount" +msgstr "Importe" + +#: pkg/expenses.go:178 +msgid "Selected contact is not valid." +msgstr "Habéis escogido un contacto que no es válido." + +#: pkg/expenses.go:179 pkg/invoices.go:641 +msgid "Invoice date must be a valid date." +msgstr "La fecha de factura debe ser válida." + +#: pkg/expenses.go:182 +msgid "Amount can not be empty." +msgstr "No podéis dejar el importe en blanco." + +#: pkg/expenses.go:183 +msgid "Amount must be a number greater than zero." +msgstr "El importe tiene que ser un número mayor a cero." + #: pkg/invoices.go:160 pkg/invoices.go:580 msgctxt "input" msgid "Customer" @@ -674,15 +769,10 @@ msgstr "Escoged un cliente a facturar." msgid "invoices.zip" msgstr "facturas.zip" -#: pkg/invoices.go:530 pkg/invoices.go:1017 +#: pkg/invoices.go:530 pkg/invoices.go:1028 msgid "Invalid action" msgstr "Acción inválida." -#: pkg/invoices.go:586 -msgctxt "input" -msgid "Invoice Date" -msgstr "Fecha de factura" - #: pkg/invoices.go:592 msgctxt "input" msgid "Notes" @@ -705,46 +795,42 @@ msgstr "Habéis escogido un cliente que no es válido." msgid "Invoice date can not be empty." msgstr "No podéis dejar la fecha de la factura en blanco." -#: pkg/invoices.go:641 -msgid "Invoice date must be a valid date." -msgstr "La fecha de factura debe ser válida." - #: pkg/invoices.go:643 msgid "Selected payment method is not valid." msgstr "Habéis escogido un método de pago que no es válido." -#: pkg/invoices.go:761 pkg/invoices.go:766 +#: pkg/invoices.go:766 pkg/invoices.go:771 msgctxt "input" msgid "Id" msgstr "Identificador" -#: pkg/invoices.go:798 +#: pkg/invoices.go:804 msgctxt "input" msgid "Quantity" msgstr "Cantidad" -#: pkg/invoices.go:806 +#: pkg/invoices.go:813 msgctxt "input" msgid "Discount (%)" msgstr "Descuento (%)" -#: pkg/invoices.go:854 +#: pkg/invoices.go:865 msgid "Product ID must be a number greater than zero." msgstr "El ID de producto tiene que ser un número mayor a cero." -#: pkg/invoices.go:860 +#: pkg/invoices.go:871 msgid "Quantity can not be empty." msgstr "No podéis dejar la cantidad en blanco." -#: pkg/invoices.go:861 +#: pkg/invoices.go:872 msgid "Quantity must be a number greater than zero." msgstr "La cantidad tiene que ser un número mayor a cero." -#: pkg/invoices.go:863 +#: pkg/invoices.go:874 msgid "Discount can not be empty." msgstr "No podéis dejar el descuento en blanco." -#: pkg/invoices.go:864 +#: pkg/invoices.go:875 msgid "Discount must be a percentage between 0 and 100." msgstr "El descuento tiene que ser un porcentaje entre 0 y 100." diff --git a/web/template/app.gohtml b/web/template/app.gohtml index d291d4e..f6cdf95 100644 --- a/web/template/app.gohtml +++ b/web/template/app.gohtml @@ -45,6 +45,7 @@ diff --git a/web/template/expenses/edit.gohtml b/web/template/expenses/edit.gohtml new file mode 100644 index 0000000..180f7f6 --- /dev/null +++ b/web/template/expenses/edit.gohtml @@ -0,0 +1,39 @@ +{{ define "title" -}} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editExpensePage*/ -}} + {{ printf ( pgettext "Edit Expense “%s”" "title" ) .Slug }} +{{- end }} + +{{ define "breadcrumbs" -}} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editExpensePage*/ -}} + +{{- end }} + +{{ define "content" }} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.editExpensePage*/ -}} +
+

{{ printf (pgettext "Edit Expense “%s”" "title") .Slug }}

+
+ {{ csrfToken }} + {{ putMethod }} + + {{ with .Form -}} + {{ template "select-field" .Invoicer }} + {{ template "input-field" .InvoiceNumber }} + {{ template "input-field" .InvoiceDate }} + {{ template "input-field" .Amount }} + {{ template "select-field" .Tax }} + {{ template "tags-field" .Tags }} + {{- end }} + +
+ +
+
+
+{{- end }} diff --git a/web/template/expenses/index.gohtml b/web/template/expenses/index.gohtml new file mode 100644 index 0000000..bd74cb2 --- /dev/null +++ b/web/template/expenses/index.gohtml @@ -0,0 +1,57 @@ +{{ define "title" -}} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expensesIndexPage*/ -}} + {{( pgettext "Expenses" "title" )}} +{{- end }} + +{{ define "breadcrumbs" -}} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expensesIndexPage*/ -}} + +{{- end }} + +{{ define "content" }} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expensesIndexPage*/ -}} + + + + + + + + + + + + {{ with .Expenses }} + {{- range . }} + + + + + + + + + {{- end }} + {{ else }} + + + + {{ end }} + +
{{( pgettext "All" "product" )}}{{( pgettext "Contact" "title" )}}{{( pgettext "Invoice Date" "title" )}}{{( pgettext "Invoice Number" "title" )}}{{( pgettext "Amount" "title" )}}
{{ .InvoicerName }}{{ .InvoiceDate|formatDate }}{{ .InvoiceNumber }} + {{- range $index, $tag := .Tags }} + {{- if gt $index 0 }}, {{ end -}} + {{ . }} + {{- end }} + {{ .Amount | formatPrice }}
{{( gettext "No expenses added yet." )}}
+{{- end }} diff --git a/web/template/expenses/new.gohtml b/web/template/expenses/new.gohtml new file mode 100644 index 0000000..ccb7809 --- /dev/null +++ b/web/template/expenses/new.gohtml @@ -0,0 +1,36 @@ +{{ define "title" -}} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expenseForm*/ -}} + {{( pgettext "New Expense" "title" )}} +{{- end }} + +{{ define "breadcrumbs" -}} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expenseForm*/ -}} + +{{- end }} + +{{ define "content" }} + {{- /*gotype: dev.tandem.ws/tandem/numerus/pkg.expenseForm*/ -}} +
+

{{(pgettext "New Expense" "title")}}

+
+ {{ csrfToken }} + + {{ template "select-field" .Invoicer }} + {{ template "input-field" .InvoiceNumber }} + {{ template "input-field" .InvoiceDate }} + {{ template "input-field" .Amount }} + {{ template "select-field" .Tax }} + {{ template "tags-field" .Tags }} + +
+ +
+
+
+{{- end }}